Merge branch 'release/0.14'
This commit is contained in:
commit
f8cb9eb4fb
181 changed files with 5829 additions and 2253 deletions
|
@ -1,9 +1,9 @@
|
||||||
APP_ENV=testing
|
APP_ENV=testing
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_KEY=base64:6DJhvZLVjE6dD4Cqrteh+6Z5vZlG+v/soCKcDHLOAH0=
|
APP_KEY=base64:6DJhvZLVjE6dD4Cqrteh+6Z5vZlG+v/soCKcDHLOAH0=
|
||||||
APP_URL=http://localhost:8000
|
APP_URL=http://jonnybarnes.localhost:8000
|
||||||
APP_LONGURL=localhost
|
APP_LONGURL=jonnybarnes.localhost
|
||||||
APP_SHORTURL=local
|
APP_SHORTURL=jmb.localhost
|
||||||
|
|
||||||
DB_CONNECTION=travis
|
DB_CONNECTION=travis
|
||||||
|
|
||||||
|
|
20
.travis.yml
20
.travis.yml
|
@ -7,12 +7,13 @@ cache:
|
||||||
- apt
|
- apt
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
|
hosts:
|
||||||
|
- jmb.localhost
|
||||||
|
- jonnybarnes.localhost
|
||||||
postgresql: "9.6"
|
postgresql: "9.6"
|
||||||
apt:
|
apt:
|
||||||
sources:
|
|
||||||
- sourceline: 'deb http://ppa.launchpad.net/nginx/development/ubuntu trusty main'
|
|
||||||
packages:
|
packages:
|
||||||
- nginx
|
- nginx-full
|
||||||
- realpath
|
- realpath
|
||||||
- postgresql-9.6-postgis-2.3
|
- postgresql-9.6-postgis-2.3
|
||||||
- imagemagick
|
- imagemagick
|
||||||
|
@ -36,10 +37,6 @@ php:
|
||||||
- 7.1
|
- 7.1
|
||||||
- 7.2
|
- 7.2
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- php: 7.2
|
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- printf "\n" | pecl install imagick
|
- printf "\n" | pecl install imagick
|
||||||
- cp .env.travis .env
|
- cp .env.travis .env
|
||||||
|
@ -47,6 +44,7 @@ before_install:
|
||||||
- psql -U travis -c 'create database travis_ci_test'
|
- psql -U travis -c 'create database travis_ci_test'
|
||||||
- psql -U travis -d travis_ci_test -c 'create extension postgis'
|
- psql -U travis -d travis_ci_test -c 'create extension postgis'
|
||||||
- travis_retry composer self-update --preview
|
- travis_retry composer self-update --preview
|
||||||
|
- pear install pear/PHP_CodeSniffer && phpenv rehash
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- if [[ $setup = 'basic' ]]; then travis_retry composer install --no-interaction --prefer-dist; fi
|
- if [[ $setup = 'basic' ]]; then travis_retry composer install --no-interaction --prefer-dist; fi
|
||||||
|
@ -66,6 +64,10 @@ before_script:
|
||||||
#- sleep 5
|
#- sleep 5
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- php vendor/bin/phpunit --coverage-text
|
- php vendor/bin/phpunit --coverage-clover build/logs/clover.xml
|
||||||
|
- phpcs
|
||||||
#- php artisan dusk
|
#- php artisan dusk
|
||||||
- php vendor/bin/security-checker security:check ./composer.lock --end-point=http://security.sensiolabs.org/check_lock
|
- php vendor/bin/security-checker security:check --end-point=http://security.sensiolabs.org/check_lock
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- travis_retry php vendor/bin/coveralls
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\WebMention;
|
use App\Models\WebMention;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\FileSystem\FileSystem;
|
use Illuminate\FileSystem\FileSystem;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\WebMention;
|
use App\Models\WebMention;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use App\Jobs\DownloadWebMention;
|
use App\Jobs\DownloadWebMention;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,9 @@ namespace App\Console\Commands;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use SensioLabs\Security\SecurityChecker;
|
use SensioLabs\Security\SecurityChecker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
class SecurityCheck extends Command
|
class SecurityCheck extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,6 +7,9 @@ use Illuminate\Support\Facades\Route;
|
||||||
use Illuminate\Session\TokenMismatchException;
|
use Illuminate\Session\TokenMismatchException;
|
||||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
class Handler extends ExceptionHandler
|
class Handler extends ExceptionHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Exceptions;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
|
|
||||||
class InternetArchiveErrorSavingException extends Exception
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
7
app/Exceptions/InternetArchiveException.php
Normal file
7
app/Exceptions/InternetArchiveException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
class InternetArchiveException extends \Exception
|
||||||
|
{
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ namespace App\Exceptions;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
class RemoteContentNotFound extends Exception
|
class RemoteContentNotFoundException extends Exception
|
||||||
{
|
{
|
||||||
//used when guzzle can’t find the remote content
|
//used when guzzle can’t find the remote content
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Article;
|
use App\Models\Article;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\MicropubClient;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\MicropubClient;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
class ClientsController extends Controller
|
class ClientsController extends Controller
|
||||||
|
@ -86,9 +86,9 @@ class ClientsController extends Controller
|
||||||
* @param string The client id
|
* @param string The client id
|
||||||
* @return redirect
|
* @return redirect
|
||||||
*/
|
*/
|
||||||
public function destroy($articleId)
|
public function destroy($clientId)
|
||||||
{
|
{
|
||||||
MicropubClient::where('id', $articleId)->delete();
|
MicropubClient::where('id', $clientId)->delete();
|
||||||
|
|
||||||
return redirect('/admin/clients');
|
return redirect('/admin/clients');
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Contact;
|
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
|
use App\Models\Contact;
|
||||||
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;
|
||||||
|
@ -83,16 +83,14 @@ class ContactsController extends Controller
|
||||||
$contact->facebook = $request->input('facebook');
|
$contact->facebook = $request->input('facebook');
|
||||||
$contact->save();
|
$contact->save();
|
||||||
|
|
||||||
if ($request->hasFile('avatar')) {
|
if ($request->hasFile('avatar') && ($request->input('homepage') != '')) {
|
||||||
if ($request->input('homepage') != '') {
|
$dir = parse_url($request->input('homepage'), PHP_URL_HOST);
|
||||||
$dir = parse_url($request->input('homepage'))['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');
|
||||||
|
@ -123,37 +121,47 @@ class ContactsController extends Controller
|
||||||
*/
|
*/
|
||||||
public function getAvatar($contactId)
|
public function getAvatar($contactId)
|
||||||
{
|
{
|
||||||
|
// Initialising
|
||||||
|
$avatarURL = null;
|
||||||
|
$avatar = null;
|
||||||
$contact = Contact::findOrFail($contactId);
|
$contact = Contact::findOrFail($contactId);
|
||||||
$homepage = $contact->homepage;
|
if (mb_strlen($contact->homepage !== null) !== 0) {
|
||||||
if (($homepage !== null) && ($homepage !== '')) {
|
$client = resolve(Client::class);
|
||||||
$client = new Client();
|
|
||||||
try {
|
try {
|
||||||
$response = $client->get($homepage);
|
$response = $client->get($contact->homepage);
|
||||||
$html = (string) $response->getBody();
|
|
||||||
$mf2 = \Mf2\parse($html, $homepage);
|
|
||||||
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
|
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
|
||||||
return "Bad Response from $homepage";
|
return redirect('/admin/contacts/' . $contactId . '/edit')
|
||||||
|
->with('error', 'Bad resposne from contact’s homepage');
|
||||||
}
|
}
|
||||||
$avatarURL = null; // Initialising
|
$mf2 = \Mf2\parse((string) $response->getBody(), $contact->homepage);
|
||||||
foreach ($mf2['items'] as $microformat) {
|
foreach ($mf2['items'] as $microformat) {
|
||||||
if ($microformat['type'][0] == 'h-card') {
|
if (array_get($microformat, 'type.0') == 'h-card') {
|
||||||
$avatarURL = $microformat['properties']['photo'][0];
|
$avatarURL = array_get($microformat, 'properties.photo.0');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
if ($avatarURL !== null) {
|
||||||
$avatar = $client->get($avatarURL);
|
try {
|
||||||
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
|
$avatar = $client->get($avatarURL);
|
||||||
return "Unable to get $avatarURL";
|
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
|
||||||
|
return redirect('/admin/contacts/' . $contactId . '/edit')
|
||||||
|
->with('error', 'Unable to download avatar');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$directory = public_path() . '/assets/profile-images/' . parse_url($homepage)['host'];
|
if ($avatar !== null) {
|
||||||
$filesystem = new Filesystem();
|
$directory = public_path() . '/assets/profile-images/' . parse_url($contact->homepage, PHP_URL_HOST);
|
||||||
if ($filesystem->isDirectory($directory) === false) {
|
$filesystem = new Filesystem();
|
||||||
$filesystem->makeDirectory($directory);
|
if ($filesystem->isDirectory($directory) === false) {
|
||||||
}
|
$filesystem->makeDirectory($directory);
|
||||||
$filesystem->put($directory . '/image', $avatar->getBody());
|
}
|
||||||
|
$filesystem->put($directory . '/image', $avatar->getBody());
|
||||||
|
|
||||||
return view('admin.contacts.getavatarsuccess', ['homepage' => parse_url($homepage)['host']]);
|
return view('admin.contacts.getavatarsuccess', [
|
||||||
|
'homepage' => parse_url($contact->homepage, PHP_URL_HOST),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return redirect('/admin/contacts/' . $contactId . '/edit');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,13 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Note;
|
use App\Models\Note;
|
||||||
use Validator;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Jobs\SendWebMentions;
|
use App\Jobs\SendWebMentions;
|
||||||
use App\Services\NoteService;
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
class NotesController extends Controller
|
class NotesController extends Controller
|
||||||
{
|
{
|
||||||
protected $noteService;
|
|
||||||
|
|
||||||
public function __construct(NoteService $noteService)
|
|
||||||
{
|
|
||||||
$this->noteService = $noteService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List the notes that can be edited.
|
* List the notes that can be edited.
|
||||||
*
|
*
|
||||||
|
@ -51,30 +42,10 @@ class NotesController extends Controller
|
||||||
*/
|
*/
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$validator = Validator::make(
|
Note::create([
|
||||||
$request->all(),
|
'in-reply-to' => $request->input('in-reply-to'),
|
||||||
['photo' => 'photosize'],
|
'note' => $request->input('content'),
|
||||||
['photosize' => 'At least one uploaded file exceeds size limit of 5MB']
|
]);
|
||||||
);
|
|
||||||
if ($validator->fails()) {
|
|
||||||
return redirect('/admin/notes/create')
|
|
||||||
->withErrors($validator)
|
|
||||||
->withInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = [];
|
|
||||||
$data['content'] = $request->input('content');
|
|
||||||
$data['in-reply-to'] = $request->input('in-reply-to');
|
|
||||||
$data['location'] = $request->input('location');
|
|
||||||
$data['syndicate'] = [];
|
|
||||||
if ($request->input('twitter')) {
|
|
||||||
$data['syndicate'][] = 'twitter';
|
|
||||||
}
|
|
||||||
if ($request->input('facebook')) {
|
|
||||||
$data['syndicate'][] = 'facebook';
|
|
||||||
}
|
|
||||||
|
|
||||||
$note = $this->noteService->createNote($data);
|
|
||||||
|
|
||||||
return redirect('/admin/notes');
|
return redirect('/admin/notes');
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Place;
|
use App\Models\Place;
|
||||||
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;
|
||||||
|
@ -47,11 +47,7 @@ class PlacesController extends Controller
|
||||||
*/
|
*/
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$data = [];
|
$data = $request->only(['name', 'description', 'latitude', 'longitude']);
|
||||||
$data['name'] = $request->name;
|
|
||||||
$data['description'] = $request->description;
|
|
||||||
$data['latitude'] = $request->latitude;
|
|
||||||
$data['longitude'] = $request->longitude;
|
|
||||||
$place = $this->placeService->createPlace($data);
|
$place = $this->placeService->createPlace($data);
|
||||||
|
|
||||||
return redirect('/admin/places');
|
return redirect('/admin/places');
|
||||||
|
@ -67,14 +63,7 @@ class PlacesController extends Controller
|
||||||
{
|
{
|
||||||
$place = Place::findOrFail($placeId);
|
$place = Place::findOrFail($placeId);
|
||||||
|
|
||||||
return view('admin.places.edit', [
|
return view('admin.places.edit', compact('place'));
|
||||||
'id' => $placeId,
|
|
||||||
'name' => $place->name,
|
|
||||||
'description' => $place->description,
|
|
||||||
'latitude' => $place->latitude,
|
|
||||||
'longitude' => $place->longitude,
|
|
||||||
'icon' => $place->icon ?? 'marker',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Article;
|
use App\Models\Article;
|
||||||
use Jonnybarnes\IndieWeb\Numbers;
|
use Jonnybarnes\IndieWeb\Numbers;
|
||||||
|
|
||||||
class ArticlesController extends Controller
|
class ArticlesController extends Controller
|
||||||
|
@ -15,7 +15,7 @@ class ArticlesController extends Controller
|
||||||
public function index($year = null, $month = null)
|
public function index($year = null, $month = null)
|
||||||
{
|
{
|
||||||
$articles = Article::where('published', '1')
|
$articles = Article::where('published', '1')
|
||||||
->date($year, $month)
|
->date((int) $year, (int) $month)
|
||||||
->orderBy('updated_at', 'desc')
|
->orderBy('updated_at', 'desc')
|
||||||
->simplePaginate(5);
|
->simplePaginate(5);
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class ArticlesController extends Controller
|
||||||
{
|
{
|
||||||
$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) {
|
||||||
throw new \Exception;
|
return redirect('/blog/' . $article->updated_at->year . '/' . $article->updated_at->month .'/' . $slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('articles.show', compact('article'));
|
return view('articles.show', compact('article'));
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
|
||||||
|
|
||||||
class ForgotPasswordController extends Controller
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Password Reset Controller
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| This controller is responsible for handling password reset emails and
|
|
||||||
| includes a trait which assists in sending these notifications from
|
|
||||||
| your application to your users. Feel free to explore this trait.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
use SendsPasswordResetEmails;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new controller instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('guest');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
|
||||||
|
|
||||||
class LoginController extends Controller
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Login Controller
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| This controller handles authenticating users for the application and
|
|
||||||
| redirecting them to your home screen. The controller uses a trait
|
|
||||||
| to conveniently provide its functionality to your applications.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
use AuthenticatesUsers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Where to redirect users after login.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $redirectTo = '/home';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new controller instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('guest')->except('logout');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
|
||||||
|
|
||||||
use App\User;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use Illuminate\Support\Facades\Validator;
|
|
||||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
|
||||||
|
|
||||||
class RegisterController extends Controller
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Register Controller
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| This controller handles the registration of new users as well as their
|
|
||||||
| validation and creation. By default this controller uses a trait to
|
|
||||||
| provide this functionality without requiring any additional code.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
use RegistersUsers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Where to redirect users after registration.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $redirectTo = '/home';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new controller instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('guest');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a validator for an incoming registration request.
|
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @return \Illuminate\Contracts\Validation\Validator
|
|
||||||
*/
|
|
||||||
protected function validator(array $data)
|
|
||||||
{
|
|
||||||
return Validator::make($data, [
|
|
||||||
'name' => 'required|string|max:255',
|
|
||||||
'email' => 'required|string|email|max:255|unique:users',
|
|
||||||
'password' => 'required|string|min:6|confirmed',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new user instance after a valid registration.
|
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @return \App\User
|
|
||||||
*/
|
|
||||||
protected function create(array $data)
|
|
||||||
{
|
|
||||||
return User::create([
|
|
||||||
'name' => $data['name'],
|
|
||||||
'email' => $data['email'],
|
|
||||||
'password' => bcrypt($data['password']),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
|
||||||
|
|
||||||
class ResetPasswordController extends Controller
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Password Reset Controller
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| This controller is responsible for handling password reset requests
|
|
||||||
| and uses a simple trait to include this behavior. You're free to
|
|
||||||
| explore this trait and override any methods you wish to tweak.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
use ResetsPasswords;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Where to redirect users after resetting their password.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $redirectTo = '/home';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new controller instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('guest');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Bookmark;
|
use App\Models\Bookmark;
|
||||||
|
|
||||||
class BookmarksController extends Controller
|
class BookmarksController extends Controller
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Contact;
|
use App\Models\Contact;
|
||||||
use Illuminate\Filesystem\Filesystem;
|
use Illuminate\Filesystem\Filesystem;
|
||||||
|
|
||||||
class ContactsController extends Controller
|
class ContactsController extends Controller
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Note;
|
use App\Models\{Article, Note};
|
||||||
use App\Article;
|
|
||||||
|
|
||||||
class FeedsController extends Controller
|
class FeedsController extends Controller
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Like;
|
use App\Models\Like;
|
||||||
|
|
||||||
class LikesController extends Controller
|
class LikesController extends Controller
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,320 +2,96 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Storage;
|
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use Ramsey\Uuid\Uuid;
|
use Ramsey\Uuid\Uuid;
|
||||||
use App\Jobs\ProcessImage;
|
use App\Jobs\ProcessMedia;
|
||||||
use App\Services\LikeService;
|
use App\Services\TokenService;
|
||||||
use App\Services\BookmarkService;
|
use Illuminate\Http\UploadedFile;
|
||||||
use Monolog\Handler\StreamHandler;
|
use Monolog\Handler\StreamHandler;
|
||||||
use App\{Like, Media, Note, Place};
|
|
||||||
use Intervention\Image\ImageManager;
|
use Intervention\Image\ImageManager;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Http\{Request, Response};
|
use Illuminate\Http\{Request, Response};
|
||||||
use App\Exceptions\InvalidTokenException;
|
use App\Exceptions\InvalidTokenException;
|
||||||
|
use App\Models\{Like, Media, Note, Place};
|
||||||
use Phaza\LaravelPostgis\Geometries\Point;
|
use Phaza\LaravelPostgis\Geometries\Point;
|
||||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
use Intervention\Image\Exception\NotReadableException;
|
||||||
use Ramsey\Uuid\Exception\UnsatisfiedDependencyException;
|
use App\Services\Micropub\{HCardService, HEntryService, UpdateService};
|
||||||
use App\Services\{NoteService, PlaceService, TokenService};
|
|
||||||
|
|
||||||
class MicropubController extends Controller
|
class MicropubController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* The Token service container.
|
|
||||||
*/
|
|
||||||
protected $tokenService;
|
protected $tokenService;
|
||||||
|
protected $hentryService;
|
||||||
|
protected $hcardService;
|
||||||
|
protected $updateService;
|
||||||
|
|
||||||
/**
|
|
||||||
* The Note service container.
|
|
||||||
*/
|
|
||||||
protected $noteService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Place service container.
|
|
||||||
*/
|
|
||||||
protected $placeService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inject the dependencies.
|
|
||||||
*/
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
TokenService $tokenService,
|
TokenService $tokenService,
|
||||||
NoteService $noteService,
|
HEntryService $hentryService,
|
||||||
PlaceService $placeService
|
HCardService $hcardService,
|
||||||
|
UpdateService $updateService
|
||||||
) {
|
) {
|
||||||
$this->tokenService = $tokenService;
|
$this->tokenService = $tokenService;
|
||||||
$this->noteService = $noteService;
|
$this->hentryService = $hentryService;
|
||||||
$this->placeService = $placeService;
|
$this->hcardService = $hcardService;
|
||||||
|
$this->updateService = $updateService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request request
|
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function post(Request $request)
|
public function post()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$tokenData = $this->tokenService->validateToken($request->bearerToken());
|
$tokenData = $this->tokenService->validateToken(request()->bearerToken());
|
||||||
} catch (InvalidTokenException $e) {
|
} catch (InvalidTokenException $e) {
|
||||||
return response()->json([
|
return $this->invalidTokenResponse();
|
||||||
'response' => 'error',
|
|
||||||
'error' => 'invalid_token',
|
|
||||||
'error_description' => 'The provided token did not pass validation',
|
|
||||||
], 400);
|
|
||||||
}
|
}
|
||||||
// Log the request
|
|
||||||
$logger = new Logger('micropub');
|
|
||||||
$logger->pushHandler(new StreamHandler(storage_path('logs/micropub.log')), Logger::DEBUG);
|
|
||||||
$logger->debug('MicropubLog', $request->all());
|
|
||||||
if ($tokenData->hasClaim('scope')) {
|
|
||||||
if (($request->input('h') == 'entry') || ($request->input('type.0') == 'h-entry')) {
|
|
||||||
if (stristr($tokenData->getClaim('scope'), 'create') === false) {
|
|
||||||
return $this->returnInsufficientScopeResponse();
|
|
||||||
}
|
|
||||||
if ($request->has('properties.like-of') || $request->has('like-of')) {
|
|
||||||
$like = (new LikeService())->createLike($request);
|
|
||||||
|
|
||||||
return response()->json([
|
if ($tokenData->hasClaim('scope') === false) {
|
||||||
'response' => 'created',
|
return $this->tokenHasNoScopeResponse();
|
||||||
'location' => config('app.url') . "/likes/$like->id",
|
}
|
||||||
], 201)->header('Location', config('app.url') . "/likes/$like->id");
|
|
||||||
}
|
|
||||||
if ($request->has('properties.bookmark-of') || $request->has('bookmark-of')) {
|
|
||||||
$bookmark = (new BookmarkService())->createBookmark($request);
|
|
||||||
|
|
||||||
return response()->json([
|
$this->logMicropubRequest(request()->all());
|
||||||
'response' => 'created',
|
|
||||||
'location' => config('app.url') . "/bookmarks/$bookmark->id",
|
|
||||||
], 201)->header('Location', config('app.url') . "/bookmarks/$bookmark->id");
|
|
||||||
}
|
|
||||||
$data = [];
|
|
||||||
$data['client-id'] = $tokenData->getClaim('client_id');
|
|
||||||
if ($request->header('Content-Type') == 'application/json') {
|
|
||||||
if (is_string($request->input('properties.content.0'))) {
|
|
||||||
$data['content'] = $request->input('properties.content.0'); //plaintext content
|
|
||||||
}
|
|
||||||
if (is_array($request->input('properties.content.0'))
|
|
||||||
&& array_key_exists('html', $request->input('properties.content.0'))
|
|
||||||
) {
|
|
||||||
$data['content'] = $request->input('properties.content.0.html');
|
|
||||||
}
|
|
||||||
$data['in-reply-to'] = $request->input('properties.in-reply-to.0');
|
|
||||||
// check location is geo: string
|
|
||||||
if (is_string($request->input('properties.location.0'))) {
|
|
||||||
$data['location'] = $request->input('properties.location.0');
|
|
||||||
}
|
|
||||||
// check location is h-card
|
|
||||||
if (is_array($request->input('properties.location.0'))) {
|
|
||||||
if ($request->input('properties.location.0.type.0' === 'h-card')) {
|
|
||||||
try {
|
|
||||||
$place = $this->placeService->createPlaceFromCheckin(
|
|
||||||
$request->input('properties.location.0')
|
|
||||||
);
|
|
||||||
$data['checkin'] = $place->longurl;
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$data['published'] = $request->input('properties.published.0');
|
|
||||||
//create checkin place
|
|
||||||
if (array_key_exists('checkin', $request->input('properties'))) {
|
|
||||||
$data['swarm-url'] = $request->input('properties.syndication.0');
|
|
||||||
try {
|
|
||||||
$place = $this->placeService->createPlaceFromCheckin(
|
|
||||||
$request->input('properties.checkin.0')
|
|
||||||
);
|
|
||||||
$data['checkin'] = $place->longurl;
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$data['checkin'] = null;
|
|
||||||
$data['swarm-url'] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$data['content'] = $request->input('content');
|
|
||||||
$data['in-reply-to'] = $request->input('in-reply-to');
|
|
||||||
$data['location'] = $request->input('location');
|
|
||||||
$data['published'] = $request->input('published');
|
|
||||||
}
|
|
||||||
$data['syndicate'] = [];
|
|
||||||
$targets = array_pluck(config('syndication.targets'), 'uid', 'service.name');
|
|
||||||
$mpSyndicateTo = null;
|
|
||||||
if ($request->has('mp-syndicate-to')) {
|
|
||||||
$mpSyndicateTo = $request->input('mp-syndicate-to');
|
|
||||||
}
|
|
||||||
if ($request->has('properties.mp-syndicate-to')) {
|
|
||||||
$mpSyndicateTo = $request->input('properties.mp-syndicate-to');
|
|
||||||
}
|
|
||||||
if (is_string($mpSyndicateTo)) {
|
|
||||||
$service = array_search($mpSyndicateTo, $targets);
|
|
||||||
if ($service == 'Twitter') {
|
|
||||||
$data['syndicate'][] = 'twitter';
|
|
||||||
}
|
|
||||||
if ($service == 'Facebook') {
|
|
||||||
$data['syndicate'][] = 'facebook';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (is_array($mpSyndicateTo)) {
|
|
||||||
foreach ($mpSyndicateTo as $uid) {
|
|
||||||
$service = array_search($uid, $targets);
|
|
||||||
if ($service == 'Twitter') {
|
|
||||||
$data['syndicate'][] = 'twitter';
|
|
||||||
}
|
|
||||||
if ($service == 'Facebook') {
|
|
||||||
$data['syndicate'][] = 'facebook';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$data['photo'] = [];
|
|
||||||
$photos = null;
|
|
||||||
if ($request->has('photo')) {
|
|
||||||
$photos = $request->input('photo');
|
|
||||||
}
|
|
||||||
if ($request->has('properties.photo')) {
|
|
||||||
$photos = $request->input('properties.photo');
|
|
||||||
}
|
|
||||||
if ($photos !== null) {
|
|
||||||
foreach ($photos as $photo) {
|
|
||||||
if (is_string($photo)) {
|
|
||||||
//only supporting media URLs for now
|
|
||||||
$data['photo'][] = $photo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (starts_with($request->input('properties.syndication.0'), 'https://www.instagram.com')) {
|
|
||||||
$data['instagram-url'] = $request->input('properties.syndication.0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$note = $this->noteService->createNote($data);
|
|
||||||
} catch (\Exception $exception) {
|
|
||||||
return response()->json(['error' => true], 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json([
|
if ((request()->input('h') == 'entry') || (request()->input('type.0') == 'h-entry')) {
|
||||||
'response' => 'created',
|
if (stristr($tokenData->getClaim('scope'), 'create') === false) {
|
||||||
'location' => $note->longurl,
|
return $this->insufficientScopeResponse();
|
||||||
], 201)->header('Location', $note->longurl);
|
|
||||||
}
|
}
|
||||||
if ($request->input('h') == 'card' || $request->input('type')[0] == 'h-card') {
|
$location = $this->hentryService->process(request()->all(), $this->getCLientId());
|
||||||
if (stristr($tokenData->getClaim('scope'), 'create') === false) {
|
|
||||||
return $this->returnInsufficientScopeResponse();
|
|
||||||
}
|
|
||||||
$data = [];
|
|
||||||
if ($request->header('Content-Type') == 'application/json') {
|
|
||||||
$data['name'] = $request->input('properties.name');
|
|
||||||
$data['description'] = $request->input('properties.description') ?? null;
|
|
||||||
if ($request->has('properties.geo')) {
|
|
||||||
$data['geo'] = $request->input('properties.geo');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$data['name'] = $request->input('name');
|
|
||||||
$data['description'] = $request->input('description');
|
|
||||||
if ($request->has('geo')) {
|
|
||||||
$data['geo'] = $request->input('geo');
|
|
||||||
}
|
|
||||||
if ($request->has('latitude')) {
|
|
||||||
$data['latitude'] = $request->input('latitude');
|
|
||||||
$data['longitude'] = $request->input('longitude');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$place = $this->placeService->createPlace($data);
|
|
||||||
} catch (\Exception $exception) {
|
|
||||||
return response()->json(['error' => true], 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'response' => 'created',
|
'response' => 'created',
|
||||||
'location' => $place->longurl,
|
'location' => $location,
|
||||||
], 201)->header('Location', $place->longurl);
|
], 201)->header('Location', $location);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request()->input('h') == 'card' || request()->input('type')[0] == 'h-card') {
|
||||||
|
if (stristr($tokenData->getClaim('scope'), 'create') === false) {
|
||||||
|
return $this->insufficientScopeResponse();
|
||||||
}
|
}
|
||||||
if ($request->input('action') == 'update') {
|
$location = $this->hcardService->process(request()->all());
|
||||||
if (stristr($tokenData->getClaim('scope'), 'update') === false) {
|
|
||||||
return $this->returnInsufficientScopeResponse();
|
|
||||||
}
|
|
||||||
$urlPath = parse_url($request->input('url'), PHP_URL_PATH);
|
|
||||||
//is it a note we are updating?
|
|
||||||
if (mb_substr($urlPath, 1, 5) === 'notes') {
|
|
||||||
try {
|
|
||||||
$note = Note::nb60(basename($urlPath))->firstOrFail();
|
|
||||||
} catch (ModelNotFoundException $exception) {
|
|
||||||
return response()->json([
|
|
||||||
'error' => 'invalid_request',
|
|
||||||
'error_description' => 'No known note with given ID',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
//got the note, are we dealing with a “replace” request?
|
|
||||||
if ($request->has('replace')) {
|
|
||||||
foreach ($request->input('replace') as $property => $value) {
|
|
||||||
if ($property == 'content') {
|
|
||||||
$note->note = $value[0];
|
|
||||||
}
|
|
||||||
if ($property == 'syndication') {
|
|
||||||
foreach ($value as $syndicationURL) {
|
|
||||||
if (starts_with($syndicationURL, 'https://www.facebook.com')) {
|
|
||||||
$note->facebook_url = $syndicationURL;
|
|
||||||
}
|
|
||||||
if (starts_with($syndicationURL, 'https://www.swarmapp.com')) {
|
|
||||||
$note->swarm_url = $syndicationURL;
|
|
||||||
}
|
|
||||||
if (starts_with($syndicationURL, 'https://twitter.com')) {
|
|
||||||
$note->tweet_id = basename(parse_url($syndicationURL, PHP_URL_PATH));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$note->save();
|
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'response' => 'updated',
|
'response' => 'created',
|
||||||
]);
|
'location' => $location,
|
||||||
}
|
], 201)->header('Location', $location);
|
||||||
//how about “add”
|
}
|
||||||
if ($request->has('add')) {
|
|
||||||
foreach ($request->input('add') as $property => $value) {
|
|
||||||
if ($property == 'syndication') {
|
|
||||||
foreach ($value as $syndicationURL) {
|
|
||||||
if (starts_with($syndicationURL, 'https://www.facebook.com')) {
|
|
||||||
$note->facebook_url = $syndicationURL;
|
|
||||||
}
|
|
||||||
if (starts_with($syndicationURL, 'https://www.swarmapp.com')) {
|
|
||||||
$note->swarm_url = $syndicationURL;
|
|
||||||
}
|
|
||||||
if (starts_with($syndicationURL, 'https://twitter.com')) {
|
|
||||||
$note->tweet_id = basename(parse_url($syndicationURL, PHP_URL_PATH));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($property == 'photo') {
|
|
||||||
foreach ($value as $photoURL) {
|
|
||||||
if (start_with($photo, 'https://')) {
|
|
||||||
$media = new Media();
|
|
||||||
$media->path = $photoURL;
|
|
||||||
$media->type = 'image';
|
|
||||||
$media->save();
|
|
||||||
$note->media()->save($media);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$note->save();
|
|
||||||
|
|
||||||
return response()->json([
|
if (request()->input('action') == 'update') {
|
||||||
'response' => 'updated',
|
if (stristr($tokenData->getClaim('scope'), 'update') === false) {
|
||||||
]);
|
return $this->insufficientScopeResponse();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this->updateService->process(request()->all());
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'response' => 'error',
|
'response' => 'error',
|
||||||
'error' => 'forbidden',
|
'error_description' => 'unsupported_request_type',
|
||||||
'error_description' => 'The token has no scopes',
|
], 500);
|
||||||
], 403);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -324,47 +100,37 @@ class MicropubController extends Controller
|
||||||
* 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.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function get(Request $request)
|
public function get()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$tokenData = $this->tokenService->validateToken($request->bearerToken());
|
$tokenData = $this->tokenService->validateToken(request()->bearerToken());
|
||||||
} catch (InvalidTokenException $e) {
|
} catch (InvalidTokenException $e) {
|
||||||
return response()->json([
|
return $this->invalidTokenResponse();
|
||||||
'response' => 'error',
|
|
||||||
'error' => 'invalid_token',
|
|
||||||
'error_description' => 'The provided token did not pass validation',
|
|
||||||
], 400);
|
|
||||||
}
|
}
|
||||||
//we have a valid token, is `syndicate-to` set?
|
|
||||||
if ($request->input('q') === 'syndicate-to') {
|
if (request()->input('q') === 'syndicate-to') {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'syndicate-to' => config('syndication.targets'),
|
'syndicate-to' => config('syndication.targets'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//nope, how about a config query?
|
if (request()->input('q') == 'config') {
|
||||||
if ($request->input('q') == 'config') {
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'syndicate-to' => config('syndication.targets'),
|
'syndicate-to' => config('syndication.targets'),
|
||||||
'media-endpoint' => route('media-endpoint'),
|
'media-endpoint' => route('media-endpoint'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//nope, how about a geo URL?
|
if (substr(request()->input('q'), 0, 4) === 'geo:') {
|
||||||
if (substr($request->input('q'), 0, 4) === 'geo:') {
|
|
||||||
preg_match_all(
|
preg_match_all(
|
||||||
'/([0-9\.\-]+)/',
|
'/([0-9\.\-]+)/',
|
||||||
$request->input('q'),
|
request()->input('q'),
|
||||||
$matches
|
$matches
|
||||||
);
|
);
|
||||||
$distance = (count($matches[0]) == 3) ? 100 * $matches[0][2] : 1000;
|
$distance = (count($matches[0]) == 3) ? 100 * $matches[0][2] : 1000;
|
||||||
$places = Place::near(new Point($matches[0][0], $matches[0][1]))->get();
|
$places = Place::near(new Point($matches[0][0], $matches[0][1]))->get();
|
||||||
foreach ($places as $place) {
|
|
||||||
$place->uri = config('app.url') . '/places/' . $place->slug;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'response' => 'places',
|
'response' => 'places',
|
||||||
|
@ -372,7 +138,7 @@ class MicropubController extends Controller
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//nope, just return the token
|
// default response is just to return the token data
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'response' => 'token',
|
'response' => 'token',
|
||||||
'token' => [
|
'token' => [
|
||||||
|
@ -386,77 +152,25 @@ class MicropubController extends Controller
|
||||||
/**
|
/**
|
||||||
* Process a media item posted to the media endpoint.
|
* Process a media item posted to the media endpoint.
|
||||||
*
|
*
|
||||||
* @param Illuminate\Http\Request $request
|
|
||||||
* @return Illuminate\Http\Response
|
* @return Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function media(Request $request)
|
public function media()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$tokenData = $this->tokenService->validateToken($request->bearerToken());
|
$tokenData = $this->tokenService->validateToken(request()->bearerToken());
|
||||||
} catch (InvalidTokenException $e) {
|
} catch (InvalidTokenException $e) {
|
||||||
return response()->json([
|
return $this->invalidTokenResponse();
|
||||||
'response' => 'error',
|
|
||||||
'error' => 'invalid_token',
|
|
||||||
'error_description' => 'The provided token did not pass validation',
|
|
||||||
], 400);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$logger = new Logger('micropub');
|
if ($tokenData->hasClaim('scope') === false) {
|
||||||
$logger->pushHandler(new StreamHandler(storage_path('logs/micropub.log')), Logger::DEBUG);
|
return $this->tokenHasNoScopeResponse();
|
||||||
$logger->debug('MicropubMediaLog', $request->all());
|
}
|
||||||
//check post scope
|
|
||||||
if ($tokenData->hasClaim('scope')) {
|
|
||||||
if (stristr($tokenData->getClaim('scope'), 'create') === false) {
|
|
||||||
return $this->returnInsufficientScopeResponse();
|
|
||||||
}
|
|
||||||
//check media valid
|
|
||||||
if ($request->hasFile('file') && $request->file('file')->isValid()) {
|
|
||||||
try {
|
|
||||||
$filename = Uuid::uuid4() . '.' . $request->file('file')->extension();
|
|
||||||
} catch (UnsatisfiedDependencyException $e) {
|
|
||||||
return response()->json([
|
|
||||||
'response' => 'error',
|
|
||||||
'error' => 'internal_server_error',
|
|
||||||
'error_description' => 'A problem occured handling your request',
|
|
||||||
], 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
$size = $request->file('file')->getClientSize();
|
if (stristr($tokenData->getClaim('scope'), 'create') === false) {
|
||||||
Storage::disk('local')->put($filename, $request->file('file')->openFile()->fread($size));
|
return $this->insufficientScopeResponse();
|
||||||
try {
|
}
|
||||||
Storage::disk('s3')->put('media/' . $filename, $request->file('file')->openFile()->fread($size));
|
|
||||||
} catch (Exception $e) { // which exception?
|
|
||||||
return response()->json([
|
|
||||||
'response' => 'error',
|
|
||||||
'error' => 'service_unavailable',
|
|
||||||
'error_description' => 'Unable to save media to S3',
|
|
||||||
], 503);
|
|
||||||
}
|
|
||||||
|
|
||||||
$manager = app()->make(ImageManager::class);
|
|
||||||
try {
|
|
||||||
$image = $manager->make($request->file('file'));
|
|
||||||
$width = $image->width();
|
|
||||||
} catch (\Intervention\Image\Exception\NotReadableException $exception) {
|
|
||||||
// not an image
|
|
||||||
$width = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$media = new Media();
|
|
||||||
$media->token = $request->bearerToken();
|
|
||||||
$media->path = 'media/' . $filename;
|
|
||||||
$media->type = $this->getFileTypeFromMimeType($request->file('file')->getMimeType());
|
|
||||||
$media->image_widths = $width;
|
|
||||||
$media->save();
|
|
||||||
|
|
||||||
dispatch(new ProcessImage($filename));
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'response' => 'created',
|
|
||||||
'location' => $media->url,
|
|
||||||
], 201)->header('Location', $media->url);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if ((request()->hasFile('file') && request()->file('file')->isValid()) === false) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'response' => 'error',
|
'response' => 'error',
|
||||||
'error' => 'invalid_request',
|
'error' => 'invalid_request',
|
||||||
|
@ -464,11 +178,32 @@ class MicropubController extends Controller
|
||||||
], 400);
|
], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->logMicropubRequest(request()->all());
|
||||||
|
|
||||||
|
$filename = $this->saveFile(request()->file('file'));
|
||||||
|
|
||||||
|
$manager = resolve(ImageManager::class);
|
||||||
|
try {
|
||||||
|
$image = $manager->make(request()->file('file'));
|
||||||
|
$width = $image->width();
|
||||||
|
} catch (NotReadableException $exception) {
|
||||||
|
// not an image
|
||||||
|
$width = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$media = Media::create([
|
||||||
|
'token' => request()->bearerToken(),
|
||||||
|
'path' => 'media/' . $filename,
|
||||||
|
'type' => $this->getFileTypeFromMimeType(request()->file('file')->getMimeType()),
|
||||||
|
'image_widths' => $width,
|
||||||
|
]);
|
||||||
|
|
||||||
|
ProcessMedia::dispatch($filename);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'response' => 'error',
|
'response' => 'created',
|
||||||
'error' => 'invalid_request',
|
'location' => $media->url,
|
||||||
'error_description' => 'The provided token has no scopes',
|
], 201)->header('Location', $media->url);
|
||||||
], 400);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -495,6 +230,7 @@ class MicropubController extends Controller
|
||||||
$videoMimeTypes = [
|
$videoMimeTypes = [
|
||||||
'video/mp4',
|
'video/mp4',
|
||||||
'video/mpeg',
|
'video/mpeg',
|
||||||
|
'video/ogg',
|
||||||
'video/quicktime',
|
'video/quicktime',
|
||||||
'video/webm',
|
'video/webm',
|
||||||
];
|
];
|
||||||
|
@ -515,7 +251,29 @@ class MicropubController extends Controller
|
||||||
return 'download';
|
return 'download';
|
||||||
}
|
}
|
||||||
|
|
||||||
private function returnInsufficientScopeResponse()
|
private function getClientId(): string
|
||||||
|
{
|
||||||
|
return resolve(TokenService::class)
|
||||||
|
->validateToken(request()->bearerToken())
|
||||||
|
->getClaim('client_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function logMicropubRequest(array $request)
|
||||||
|
{
|
||||||
|
$logger = new Logger('micropub');
|
||||||
|
$logger->pushHandler(new StreamHandler(storage_path('logs/micropub.log')), Logger::DEBUG);
|
||||||
|
$logger->debug('MicropubLog', $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function saveFile(UploadedFile $file)
|
||||||
|
{
|
||||||
|
$filename = Uuid::uuid4() . '.' . $file->extension();
|
||||||
|
Storage::disk('local')->put($filename, $file);
|
||||||
|
|
||||||
|
return $filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function insufficientScopeResponse()
|
||||||
{
|
{
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'response' => 'error',
|
'response' => 'error',
|
||||||
|
@ -523,4 +281,22 @@ class MicropubController extends Controller
|
||||||
'error_description' => 'The token’s scope does not have the necessary requirements.',
|
'error_description' => 'The token’s scope does not have the necessary requirements.',
|
||||||
], 401);
|
], 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function invalidTokenResponse()
|
||||||
|
{
|
||||||
|
return response()->json([
|
||||||
|
'response' => 'error',
|
||||||
|
'error' => 'invalid_token',
|
||||||
|
'error_description' => 'The provided token did not pass validation',
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tokenHasNoScopeResponse()
|
||||||
|
{
|
||||||
|
return response()->json([
|
||||||
|
'response' => 'error',
|
||||||
|
'error' => 'invalid_request',
|
||||||
|
'error_description' => 'The provided token has no scopes',
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Note;
|
use App\Models\Note;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Jonnybarnes\IndieWeb\Numbers;
|
use Jonnybarnes\IndieWeb\Numbers;
|
||||||
use App\Services\ActivityStreamsService;
|
use App\Services\ActivityStreamsService;
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Note;
|
|
||||||
use Imagine\Image\Box;
|
|
||||||
use Imagine\Gd\Imagine;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Filesystem\Filesystem;
|
|
||||||
|
|
||||||
class PhotosController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Image box size limit for resizing photos.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->imageResizeLimit = 800;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save an uploaded photo to the image folder.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param string The associated note’s nb60 ID
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function saveImage(Request $request, $nb60id)
|
|
||||||
{
|
|
||||||
if ($request->hasFile('photo') !== true) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$photoFilename = 'note-' . $nb60id;
|
|
||||||
$path = public_path() . '/assets/img/notes/';
|
|
||||||
$ext = $request->file('photo')->getClientOriginalExtension();
|
|
||||||
$photoFilename .= '.' . $ext;
|
|
||||||
$request->file('photo')->move($path, $photoFilename);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepare a photo for posting to twitter.
|
|
||||||
*
|
|
||||||
* @param string photo fileanme
|
|
||||||
* @return string small photo filename, or null
|
|
||||||
*/
|
|
||||||
public function makeSmallPhotoForTwitter($photoFilename)
|
|
||||||
{
|
|
||||||
$imagine = new Imagine();
|
|
||||||
$orig = $imagine->open(public_path() . '/assets/img/notes/' . $photoFilename);
|
|
||||||
$size = [$orig->getSize()->getWidth(), $orig->getSize()->getHeight()];
|
|
||||||
if ($size[0] > $this->imageResizeLimit || $size[1] > $this->imageResizeLimit) {
|
|
||||||
$filenameParts = explode('.', $photoFilename);
|
|
||||||
$preExt = count($filenameParts) - 2;
|
|
||||||
$filenameParts[$preExt] .= '-small';
|
|
||||||
$photoFilenameSmall = implode('.', $filenameParts);
|
|
||||||
$aspectRatio = $size[0] / $size[1];
|
|
||||||
$box = ($aspectRatio >= 1) ?
|
|
||||||
[$this->imageResizeLimit, (int) round($this->imageResizeLimit / $aspectRatio)]
|
|
||||||
:
|
|
||||||
[(int) round($this->imageResizeLimit * $aspectRatio), $this->imageResizeLimit];
|
|
||||||
$orig->resize(new Box($box[0], $box[1]))
|
|
||||||
->save(public_path() . '/assets/img/notes/' . $photoFilenameSmall);
|
|
||||||
|
|
||||||
return $photoFilenameSmall;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the image path for a note.
|
|
||||||
*
|
|
||||||
* @param string $nb60id
|
|
||||||
* @return string | null
|
|
||||||
*/
|
|
||||||
public function getPhotoPath($nb60id)
|
|
||||||
{
|
|
||||||
$filesystem = new Filesystem();
|
|
||||||
$photoDir = public_path() . '/assets/img/notes';
|
|
||||||
$files = $filesystem->files($photoDir);
|
|
||||||
foreach ($files as $file) {
|
|
||||||
$parts = explode('.', $file);
|
|
||||||
$name = $parts[0];
|
|
||||||
$dirs = explode('/', $name);
|
|
||||||
$actualname = last($dirs);
|
|
||||||
if ($actualname == 'note-' . $nb60id) {
|
|
||||||
$ext = $parts[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isset($ext)) {
|
|
||||||
return '/assets/img/notes/note-' . $nb60id . '.' . $ext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Place;
|
use App\Models\Place;
|
||||||
|
|
||||||
class PlacesController extends Controller
|
class PlacesController extends Controller
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Note;
|
use App\Models\Note;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class SearchController extends Controller
|
class SearchController extends Controller
|
||||||
|
@ -10,16 +10,6 @@ class SearchController extends Controller
|
||||||
public function search(Request $request)
|
public function search(Request $request)
|
||||||
{
|
{
|
||||||
$notes = Note::search($request->terms)->paginate(10);
|
$notes = Note::search($request->terms)->paginate(10);
|
||||||
foreach ($notes as $note) {
|
|
||||||
$note->iso8601_time = $note->updated_at->toISO8601String();
|
|
||||||
$note->human_time = $note->updated_at->diffForHumans();
|
|
||||||
$photoURLs = [];
|
|
||||||
$photos = $note->getMedia();
|
|
||||||
foreach ($photos as $photo) {
|
|
||||||
$photoURLs[] = $photo->getUrl();
|
|
||||||
}
|
|
||||||
$note->photoURLs = $photoURLs;
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('search', compact('notes'));
|
return view('search', compact('notes'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\ShortURL;
|
|
||||||
use Jonnybanres\IndieWeb\Numbers;
|
|
||||||
|
|
||||||
class ShortURLsController extends Controller
|
class ShortURLsController extends Controller
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -41,21 +38,11 @@ class ShortURLsController extends Controller
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Routing\RedirectResponse redirect
|
* @return \Illuminate\Routing\RedirectResponse redirect
|
||||||
*/
|
*/
|
||||||
public function googlePLus()
|
public function googlePlus()
|
||||||
{
|
{
|
||||||
return redirect('https://plus.google.com/u/0/117317270900655269082/about');
|
return redirect('https://plus.google.com/u/0/117317270900655269082/about');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Redirect from '/α' to an App.net profile.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Routing\Redirector redirect
|
|
||||||
*/
|
|
||||||
public function appNet()
|
|
||||||
{
|
|
||||||
return redirect('https://alpha.app.net/jonnybarnes');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redirect a short url of this site out to a long one based on post type.
|
* Redirect a short url of this site out to a long one based on post type.
|
||||||
* Further redirects may happen.
|
* Further redirects may happen.
|
||||||
|
@ -75,46 +62,4 @@ class ShortURLsController extends Controller
|
||||||
|
|
||||||
return redirect(config('app.url') . '/' . $type . '/' . $postId);
|
return redirect(config('app.url') . '/' . $type . '/' . $postId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Redirect a saved short URL, this is generic.
|
|
||||||
*
|
|
||||||
* @param string The short URL id
|
|
||||||
* @return \Illuminate\Routing\Redirector redirect
|
|
||||||
*/
|
|
||||||
public function redirect($shortURLId)
|
|
||||||
{
|
|
||||||
$numbers = new Numbers();
|
|
||||||
$num = $numbers->b60tonum($shortURLId);
|
|
||||||
$shorturl = ShortURL::find($num);
|
|
||||||
$redirect = $shorturl->redirect;
|
|
||||||
|
|
||||||
return redirect($redirect);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* I had an old redirect systme breifly, but cool URLs should still work.
|
|
||||||
*
|
|
||||||
* @param string URL ID
|
|
||||||
* @return \Illuminate\Routing\Redirector redirect
|
|
||||||
*/
|
|
||||||
public function oldRedirect($shortURLId)
|
|
||||||
{
|
|
||||||
$filename = base_path() . '/public/assets/old-shorturls.json';
|
|
||||||
$handle = fopen($filename, 'r');
|
|
||||||
$contents = fread($handle, filesize($filename));
|
|
||||||
$object = json_decode($contents);
|
|
||||||
|
|
||||||
foreach ($object as $key => $val) {
|
|
||||||
if ($shortURLId == $key) {
|
|
||||||
return redirect($val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'This id was never used.
|
|
||||||
Old redirects are located at
|
|
||||||
<code>
|
|
||||||
<a href="https://jonnybarnes.net/assets/old-shorturls.json">old-shorturls.json</a>
|
|
||||||
</code>.';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Note;
|
use App\Models\Note;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use App\Jobs\ProcessWebMention;
|
use App\Jobs\ProcessWebMention;
|
||||||
|
@ -36,35 +36,32 @@ class WebMentionsController extends Controller
|
||||||
$path = parse_url($request->input('target'), PHP_URL_PATH);
|
$path = parse_url($request->input('target'), PHP_URL_PATH);
|
||||||
$pathParts = explode('/', $path);
|
$pathParts = explode('/', $path);
|
||||||
|
|
||||||
switch ($pathParts[1]) {
|
if ($pathParts[1] == 'notes') {
|
||||||
case 'notes':
|
//we have a note
|
||||||
//we have a note
|
$noteId = $pathParts[2];
|
||||||
$noteId = $pathParts[2];
|
$numbers = new Numbers();
|
||||||
$numbers = new Numbers();
|
try {
|
||||||
try {
|
$note = Note::findOrFail($numbers->b60tonum($noteId));
|
||||||
$note = Note::findOrFail($numbers->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 doesn’t exist.', 400);
|
||||||
return new Response('This note doesn’t exist.', 400);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return new Response(
|
return new Response(
|
||||||
'Webmention received, it will be processed shortly',
|
'Webmention received, it will be processed shortly',
|
||||||
202
|
202
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
case 'blog':
|
|
||||||
return new Response(
|
|
||||||
'I don’t accept webmentions for blog posts yet.',
|
|
||||||
501
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return new Response(
|
|
||||||
'Invalid request',
|
|
||||||
400
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if ($pathParts[1] == 'blog') {
|
||||||
|
return new Response(
|
||||||
|
'I don’t accept webmentions for blog posts yet.',
|
||||||
|
501
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
'Invalid request',
|
||||||
|
400
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ class Kernel extends HttpKernel
|
||||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
\App\Http\Middleware\LinkHeadersMiddleware::class,
|
\App\Http\Middleware\LinkHeadersMiddleware::class,
|
||||||
//\App\Http\Middleware\DevTokenMiddleware::class,
|
|
||||||
\App\Http\Middleware\LocalhostSessionMiddleware::class,
|
\App\Http\Middleware\LocalhostSessionMiddleware::class,
|
||||||
\App\Http\Middleware\ActivityStreamLinks::class,
|
\App\Http\Middleware\ActivityStreamLinks::class,
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
|
|
||||||
class DevTokenMiddleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handle an incoming request.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param \Closure $next
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function handle($request, Closure $next)
|
|
||||||
{
|
|
||||||
if (config('app.env') !== 'production') {
|
|
||||||
session(['me' => config('app.url')]);
|
|
||||||
if (Storage::exists('dev-token')) {
|
|
||||||
session(['token' => Storage::get('dev-token')]);
|
|
||||||
} else {
|
|
||||||
$data = [
|
|
||||||
'me' => config('app.url'),
|
|
||||||
'client_id' => route('micropub-client'),
|
|
||||||
'scope' => 'post',
|
|
||||||
];
|
|
||||||
$tokenService = new \App\Services\TokenService();
|
|
||||||
session(['token' => $tokenService->getNewToken($data)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,6 +5,9 @@ namespace App\Http\Middleware;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
class RedirectIfAuthenticated
|
class RedirectIfAuthenticated
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\MicropubClient;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use App\Models\MicropubClient;
|
||||||
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;
|
||||||
|
|
|
@ -41,7 +41,7 @@ class DownloadWebMention implements ShouldQueue
|
||||||
//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 \Illuminate\FileSystem\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';
|
||||||
if ($filesystem->exists($filename)) {
|
if ($filesystem->exists($filename)) {
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Bookmark;
|
use App\Models\Bookmark;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use App\Services\BookmarkService;
|
use App\Services\BookmarkService;
|
||||||
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;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use App\Exceptions\InternetArchiveErrorSavingException;
|
use App\Exceptions\InternetArchiveException;
|
||||||
|
|
||||||
class ProcessBookmark implements ShouldQueue
|
class ProcessBookmark implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -34,12 +34,12 @@ class ProcessBookmark implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$uuid = (new BookmarkService())->saveScreenshot($this->bookmark->url);
|
$uuid = (resolve(BookmarkService::class))->saveScreenshot($this->bookmark->url);
|
||||||
$this->bookmark->screenshot = $uuid;
|
$this->bookmark->screenshot = $uuid;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$archiveLink = (new BookmarkService())->getArchiveLink($this->bookmark->url);
|
$archiveLink = (resolve(BookmarkService::class))->getArchiveLink($this->bookmark->url);
|
||||||
} catch (InternetArchiveErrorSavingException $e) {
|
} catch (InternetArchiveException $e) {
|
||||||
$archiveLink = null;
|
$archiveLink = null;
|
||||||
}
|
}
|
||||||
$this->bookmark->archive = $archiveLink;
|
$this->bookmark->archive = $archiveLink;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Like;
|
use App\Models\Like;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
@ -44,8 +44,8 @@ class ProcessLike implements ShouldQueue
|
||||||
try {
|
try {
|
||||||
$author = $authorship->findAuthor($mf2);
|
$author = $authorship->findAuthor($mf2);
|
||||||
if (is_array($author)) {
|
if (is_array($author)) {
|
||||||
$this->like->author_name = $author['name'];
|
$this->like->author_name = array_get($author, 'properties.name.0');
|
||||||
$this->like->author_url = $author['url'];
|
$this->like->author_url = array_get($author, 'properties.url.0');
|
||||||
}
|
}
|
||||||
if (is_string($author) && $author !== '') {
|
if (is_string($author) && $author !== '') {
|
||||||
$this->like->author_name = $author;
|
$this->like->author_name = $author;
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use Storage;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Intervention\Image\ImageManager;
|
use Intervention\Image\ImageManager;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Intervention\Image\Exception\NotReadableException;
|
use Intervention\Image\Exception\NotReadableException;
|
||||||
|
|
||||||
class ProcessImage implements ShouldQueue
|
class ProcessMedia implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
@ -34,6 +34,10 @@ class ProcessImage implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function handle(ImageManager $manager)
|
public function handle(ImageManager $manager)
|
||||||
{
|
{
|
||||||
|
Storage::disk('s3')->put(
|
||||||
|
'media/' . $this->filename,
|
||||||
|
storage_path('app') . '/' . $this->filename
|
||||||
|
);
|
||||||
//open file
|
//open file
|
||||||
try {
|
try {
|
||||||
$image = $manager->make(storage_path('app') . '/' . $this->filename);
|
$image = $manager->make(storage_path('app') . '/' . $this->filename);
|
|
@ -4,8 +4,8 @@ namespace App\Jobs;
|
||||||
|
|
||||||
use Mf2;
|
use Mf2;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use App\{Note, WebMention};
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use App\Models\{Note, WebMention};
|
||||||
use Jonnybarnes\WebmentionsParser\Parser;
|
use Jonnybarnes\WebmentionsParser\Parser;
|
||||||
use GuzzleHttp\Exception\RequestException;
|
use GuzzleHttp\Exception\RequestException;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
@ -41,23 +41,25 @@ class ProcessWebMention implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function handle(Parser $parser, Client $guzzle)
|
public function handle(Parser $parser, Client $guzzle)
|
||||||
{
|
{
|
||||||
$remoteContent = $this->getRemoteContent($this->source, $guzzle);
|
try {
|
||||||
if ($remoteContent === null) {
|
$response = $guzzle->request('GET', $this->source);
|
||||||
|
} catch (RequestException $e) {
|
||||||
throw new RemoteContentNotFoundException;
|
throw new RemoteContentNotFoundException;
|
||||||
}
|
}
|
||||||
$microformats = Mf2\parse($remoteContent, $this->source);
|
$this->saveRemoteContent((string) $response->getBody(), $this->source);
|
||||||
|
$microformats = Mf2\parse((string) $response->getBody(), $this->source);
|
||||||
$webmentions = WebMention::where('source', $this->source)->get();
|
$webmentions = WebMention::where('source', $this->source)->get();
|
||||||
foreach ($webmentions as $webmention) {
|
foreach ($webmentions as $webmention) {
|
||||||
//check webmention still references target
|
// check webmention still references target
|
||||||
//we try each type of mention (reply/like/repost)
|
// we try each type of mention (reply/like/repost)
|
||||||
if ($webmention->type == 'in-reply-to') {
|
if ($webmention->type == 'in-reply-to') {
|
||||||
if ($parser->checkInReplyTo($microformats, $this->note->longurl) == false) {
|
if ($parser->checkInReplyTo($microformats, $this->note->longurl) == false) {
|
||||||
//it doesn't so delete
|
// it doesn’t so delete
|
||||||
$webmention->delete();
|
$webmention->delete();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//webmenion is still a reply, so update content
|
// webmenion is still a reply, so update content
|
||||||
dispatch(new SaveProfileImage($microformats));
|
dispatch(new SaveProfileImage($microformats));
|
||||||
$webmention->mf2 = json_encode($microformats);
|
$webmention->mf2 = json_encode($microformats);
|
||||||
$webmention->save();
|
$webmention->save();
|
||||||
|
@ -66,25 +68,25 @@ class ProcessWebMention implements ShouldQueue
|
||||||
}
|
}
|
||||||
if ($webmention->type == 'like-of') {
|
if ($webmention->type == 'like-of') {
|
||||||
if ($parser->checkLikeOf($microformats, $note->longurl) == false) {
|
if ($parser->checkLikeOf($microformats, $note->longurl) == false) {
|
||||||
//it doesn't so delete
|
// it doesn’t so delete
|
||||||
$webmention->delete();
|
$webmention->delete();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} //note we don't need to do anything if it still is a like
|
} // note we don’t need to do anything if it still is a like
|
||||||
}
|
}
|
||||||
if ($webmention->type == 'repost-of') {
|
if ($webmention->type == 'repost-of') {
|
||||||
if ($parser->checkRepostOf($microformats, $note->longurl) == false) {
|
if ($parser->checkRepostOf($microformats, $note->longurl) == false) {
|
||||||
//it doesn't so delete
|
// it doesn’t so delete
|
||||||
$webmention->delete();
|
$webmention->delete();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} //again, we don't need to do anything if it still is a repost
|
} // again, we don’t need to do anything if it still is a repost
|
||||||
}
|
}
|
||||||
}//foreach
|
}// foreach
|
||||||
|
|
||||||
//no wemention in db so create new one
|
// no webmention in the db so create new one
|
||||||
$webmention = new WebMention();
|
$webmention = new WebMention();
|
||||||
$type = $parser->getMentionType($microformats); //throw error here?
|
$type = $parser->getMentionType($microformats); // throw error here?
|
||||||
dispatch(new SaveProfileImage($microformats));
|
dispatch(new SaveProfileImage($microformats));
|
||||||
$webmention->source = $this->source;
|
$webmention->source = $this->source;
|
||||||
$webmention->target = $this->note->longurl;
|
$webmention->target = $this->note->longurl;
|
||||||
|
@ -96,21 +98,23 @@ class ProcessWebMention implements ShouldQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retreive the remote content from a URL, and caches the result.
|
* Save the HTML of a webmention for future use.
|
||||||
*
|
*
|
||||||
|
* @param string $html
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param GuzzleHttp\client $guzzle
|
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
private function getRemoteContent($url, Client $guzzle)
|
private function saveRemoteContent($html, $url)
|
||||||
{
|
{
|
||||||
try {
|
$filenameFromURL = str_replace(
|
||||||
$response = $guzzle->request('GET', $url);
|
['https://', 'http://'],
|
||||||
} catch (RequestException $e) {
|
['https/', 'http/'],
|
||||||
return;
|
$url
|
||||||
|
);
|
||||||
|
if (substr($url, -1) == '/') {
|
||||||
|
$filenameFromURL .= 'index.html';
|
||||||
}
|
}
|
||||||
$html = (string) $response->getBody();
|
$path = storage_path() . '/HTML/' . $filenameFromURL;
|
||||||
$path = storage_path() . '/HTML/' . $this->createFilenameFromURL($url);
|
|
||||||
$parts = explode('/', $path);
|
$parts = explode('/', $path);
|
||||||
$name = array_pop($parts);
|
$name = array_pop($parts);
|
||||||
$dir = implode('/', $parts);
|
$dir = implode('/', $parts);
|
||||||
|
@ -118,24 +122,5 @@ class ProcessWebMention implements ShouldQueue
|
||||||
mkdir($dir, 0755, true);
|
mkdir($dir, 0755, true);
|
||||||
}
|
}
|
||||||
file_put_contents("$dir/$name", $html);
|
file_put_contents("$dir/$name", $html);
|
||||||
|
|
||||||
return $html;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a file path from a URL. This is used when caching the HTML
|
|
||||||
* response.
|
|
||||||
*
|
|
||||||
* @param string The URL
|
|
||||||
* @return string The path name
|
|
||||||
*/
|
|
||||||
private function createFilenameFromURL($url)
|
|
||||||
{
|
|
||||||
$url = str_replace(['https://', 'http://'], ['https/', 'http/'], $url);
|
|
||||||
if (substr($url, -1) == '/') {
|
|
||||||
$url = $url . 'index.html';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $url;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ class SaveProfileImage implements ShouldQueue
|
||||||
//dont save pbs.twimg.com links
|
//dont save pbs.twimg.com links
|
||||||
if (parse_url($photo, PHP_URL_HOST) != 'pbs.twimg.com'
|
if (parse_url($photo, PHP_URL_HOST) != 'pbs.twimg.com'
|
||||||
&& parse_url($photo, PHP_URL_HOST) != 'twitter.com') {
|
&& parse_url($photo, PHP_URL_HOST) != 'twitter.com') {
|
||||||
$client = new Client();
|
$client = resolve(Client::class);
|
||||||
try {
|
try {
|
||||||
$response = $client->get($photo);
|
$response = $client->get($photo);
|
||||||
$image = $response->getBody(true);
|
$image = $response->getBody(true);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Note;
|
use App\Models\Note;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
@ -29,18 +29,18 @@ class SendWebMentions implements ShouldQueue
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
*
|
*
|
||||||
* @param \GuzzleHttp\Client $guzzle
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function handle(Client $guzzle)
|
public function handle()
|
||||||
{
|
{
|
||||||
//grab the URLs
|
//grab the URLs
|
||||||
$urlsInReplyTo = explode(' ', $this->note->in_reply_to);
|
$urlsInReplyTo = explode(' ', $this->note->in_reply_to);
|
||||||
$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) {
|
||||||
$endpoint = $this->discoverWebmentionEndpoint($url, $guzzle);
|
$endpoint = $this->discoverWebmentionEndpoint($url);
|
||||||
if ($endpoint) {
|
if ($endpoint !== null) {
|
||||||
|
$guzzle = resolve(Client::class);
|
||||||
$guzzle->post($endpoint, [
|
$guzzle->post($endpoint, [
|
||||||
'form_params' => [
|
'form_params' => [
|
||||||
'source' => $this->note->longurl,
|
'source' => $this->note->longurl,
|
||||||
|
@ -55,21 +55,21 @@ 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 The URL
|
||||||
* @param \GuzzleHttp\Client $guzzle
|
|
||||||
* @return string The webmention endpoint URL
|
* @return string The webmention endpoint URL
|
||||||
*/
|
*/
|
||||||
public function discoverWebmentionEndpoint($url, $guzzle)
|
public function discoverWebmentionEndpoint($url)
|
||||||
{
|
{
|
||||||
//let’s not send webmentions to myself
|
//let’s 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')) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
if (starts_with($url, '/notes/tagged/')) {
|
if (starts_with($url, '/notes/tagged/')) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$endpoint = null;
|
$endpoint = null;
|
||||||
|
|
||||||
|
$guzzle = resolve(Client::class);
|
||||||
$response = $guzzle->get($url);
|
$response = $guzzle->get($url);
|
||||||
//check HTTP Headers for webmention endpoint
|
//check HTTP Headers for webmention endpoint
|
||||||
$links = \GuzzleHttp\Psr7\parse_header($response->getHeader('Link'));
|
$links = \GuzzleHttp\Psr7\parse_header($response->getHeader('Link'));
|
||||||
|
@ -92,8 +92,6 @@ class SendWebMentions implements ShouldQueue
|
||||||
if ($endpoint) {
|
if ($endpoint) {
|
||||||
return $this->resolveUri($endpoint, $url);
|
return $this->resolveUri($endpoint, $url);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,16 +2,17 @@
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Bookmark;
|
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
|
use App\Models\Bookmark;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
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;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
|
||||||
class SyndicateBookmarkToFacebook implements ShouldQueue
|
class SyndicateBookmarkToFacebook implements ShouldQueue
|
||||||
{
|
{
|
||||||
use InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
protected $bookmark;
|
protected $bookmark;
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,17 @@
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Bookmark;
|
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
|
use App\Models\Bookmark;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
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;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
|
||||||
class SyndicateBookmarkToTwitter implements ShouldQueue
|
class SyndicateBookmarkToTwitter implements ShouldQueue
|
||||||
{
|
{
|
||||||
use InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
protected $bookmark;
|
protected $bookmark;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Note;
|
use App\Models\Note;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Note;
|
use App\Models\Note;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Cviebrock\EloquentSluggable\Sluggable;
|
use Cviebrock\EloquentSluggable\Sluggable;
|
||||||
|
@ -17,7 +17,7 @@ class Article extends Model
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $dates = ['deleted_at'];
|
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The database table used by the model.
|
* The database table used by the model.
|
||||||
|
@ -40,16 +40,6 @@ class Article extends Model
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the relationship with webmentions.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public function webmentions()
|
|
||||||
{
|
|
||||||
return $this->morphMany('App\WebMention', 'commentable');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We shall set a blacklist of non-modifiable model attributes.
|
* We shall set a blacklist of non-modifiable model attributes.
|
||||||
*
|
*
|
||||||
|
@ -66,7 +56,7 @@ class Article extends Model
|
||||||
{
|
{
|
||||||
$markdown = new CommonMarkConverter();
|
$markdown = new CommonMarkConverter();
|
||||||
$html = $markdown->convertToHtml($this->main);
|
$html = $markdown->convertToHtml($this->main);
|
||||||
//change <pre><code>[lang] ~> <pre><code data-language="lang">
|
// changes <pre><code>[lang] ~> <pre><code data-language="lang">
|
||||||
$match = '/<pre><code>\[(.*)\]\n/';
|
$match = '/<pre><code>\[(.*)\]\n/';
|
||||||
$replace = '<pre><code class="language-$1">';
|
$replace = '<pre><code class="language-$1">';
|
||||||
$text = preg_replace($match, $replace, $html);
|
$text = preg_replace($match, $replace, $html);
|
||||||
|
@ -130,20 +120,20 @@ class Article extends Model
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Database\Eloquent\Builder
|
* @return \Illuminate\Database\Eloquent\Builder
|
||||||
*/
|
*/
|
||||||
public function scopeDate($query, $year = null, $month = null)
|
public function scopeDate($query, int $year = null, int $month = null)
|
||||||
{
|
{
|
||||||
if ($year == null) {
|
if ($year == null) {
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
$start = $year . '-01-01 00:00:00';
|
$start = $year . '-01-01 00:00:00';
|
||||||
$end = ($year + 1) . '-01-01 00:00:00';
|
$end = ($year + 1) . '-01-01 00:00:00';
|
||||||
if (($month !== null) && ($month !== '12')) {
|
if (($month !== null) && ($month !== 12)) {
|
||||||
$start = $year . '-' . $month . '-01 00:00:00';
|
$start = $year . '-' . $month . '-01 00:00:00';
|
||||||
$end = $year . '-' . ($month + 1) . '-01 00:00:00';
|
$end = $year . '-' . ($month + 1) . '-01 00:00:00';
|
||||||
}
|
}
|
||||||
if ($month === '12') {
|
if ($month === 12) {
|
||||||
$start = $year . '-12-01 00:00:00';
|
$start = $year . '-12-01 00:00:00';
|
||||||
//$end as above
|
$end = ($year + 1) . '-01-01 00:00:00';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $query->where([
|
return $query->where([
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class Bookmark extends Model
|
||||||
*/
|
*/
|
||||||
public function tags()
|
public function tags()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany('App\Tag');
|
return $this->belongsToMany('App\Models\Tag');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
use Mf2;
|
use Mf2;
|
||||||
use HTMLPurifier;
|
use HTMLPurifier;
|
||||||
|
@ -24,7 +24,7 @@ class Like extends Model
|
||||||
public function getContentAttribute($value)
|
public function getContentAttribute($value)
|
||||||
{
|
{
|
||||||
if ($value === null) {
|
if ($value === null) {
|
||||||
return $this->url;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$mf2 = Mf2\parse($value, $this->url);
|
$mf2 = Mf2\parse($value, $this->url);
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
@ -18,14 +18,14 @@ class Media extends Model
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $fillable = ['path'];
|
protected $fillable = ['token', 'path', 'type', 'image_widths'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the note that owns this media.
|
* Get the note that owns this media.
|
||||||
*/
|
*/
|
||||||
public function note()
|
public function note()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Note');
|
return $this->belongsTo('App\Models\Note');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,10 +70,10 @@ class Media extends Model
|
||||||
|
|
||||||
public function getBasename($path)
|
public function getBasename($path)
|
||||||
{
|
{
|
||||||
$filenameParts = explode('.', $path);
|
|
||||||
|
|
||||||
// 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
|
||||||
|
$filenameParts = explode('.', $path);
|
||||||
|
array_pop($filenameParts);
|
||||||
$basename = ltrim(array_reduce($filenameParts, function ($carry, $item) {
|
$basename = ltrim(array_reduce($filenameParts, function ($carry, $item) {
|
||||||
return $carry . '.' . $item;
|
return $carry . '.' . $item;
|
||||||
}, ''), '.');
|
}, ''), '.');
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
@ -27,6 +27,6 @@ class MicropubClient extends Model
|
||||||
*/
|
*/
|
||||||
public function notes()
|
public function notes()
|
||||||
{
|
{
|
||||||
return $this->hasMany('App\Note', 'client_id', 'client_url');
|
return $this->hasMany('App\Models\Note', 'client_id', 'client_url');
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
use Cache;
|
use Cache;
|
||||||
use Twitter;
|
use Twitter;
|
||||||
|
@ -69,7 +69,7 @@ class Note extends Model
|
||||||
*/
|
*/
|
||||||
public function tags()
|
public function tags()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany('App\Tag');
|
return $this->belongsToMany('App\Models\Tag');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,7 +79,7 @@ class Note extends Model
|
||||||
*/
|
*/
|
||||||
public function client()
|
public function client()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\MicropubClient', 'client_id', 'client_url');
|
return $this->belongsTo('App\Models\MicropubClient', 'client_id', 'client_url');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,7 +89,7 @@ class Note extends Model
|
||||||
*/
|
*/
|
||||||
public function webmentions()
|
public function webmentions()
|
||||||
{
|
{
|
||||||
return $this->morphMany('App\WebMention', 'commentable');
|
return $this->morphMany('App\Models\WebMention', 'commentable');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -99,7 +99,7 @@ class Note extends Model
|
||||||
*/
|
*/
|
||||||
public function place()
|
public function place()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Place');
|
return $this->belongsTo('App\Models\Place');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -109,7 +109,7 @@ class Note extends Model
|
||||||
*/
|
*/
|
||||||
public function media()
|
public function media()
|
||||||
{
|
{
|
||||||
return $this->hasMany('App\Media');
|
return $this->hasMany('App\Models\Media');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -146,9 +146,9 @@ class Note extends Model
|
||||||
$emoji = new EmojiModifier();
|
$emoji = new EmojiModifier();
|
||||||
|
|
||||||
$hcards = $this->makeHCards($value);
|
$hcards = $this->makeHCards($value);
|
||||||
$html = $this->convertMarkdown($hcards);
|
$hashtags = $this->autoLinkHashtag($hcards);
|
||||||
$hashtags = $this->autoLinkHashtag($html);
|
$html = $this->convertMarkdown($hashtags);
|
||||||
$modified = $emoji->makeEmojiAccessible($hashtags);
|
$modified = $emoji->makeEmojiAccessible($html);
|
||||||
|
|
||||||
return $modified;
|
return $modified;
|
||||||
}
|
}
|
||||||
|
@ -223,9 +223,7 @@ class Note extends Model
|
||||||
public function getLatitudeAttribute()
|
public function getLatitudeAttribute()
|
||||||
{
|
{
|
||||||
if ($this->place !== null) {
|
if ($this->place !== null) {
|
||||||
$lnglat = explode(' ', $this->place->location);
|
return $this->place->location->getLat();
|
||||||
|
|
||||||
return $lnglat[1];
|
|
||||||
}
|
}
|
||||||
if ($this->location !== null) {
|
if ($this->location !== null) {
|
||||||
$pieces = explode(':', $this->location);
|
$pieces = explode(':', $this->location);
|
||||||
|
@ -243,9 +241,7 @@ class Note extends Model
|
||||||
public function getLongitudeAttribute()
|
public function getLongitudeAttribute()
|
||||||
{
|
{
|
||||||
if ($this->place !== null) {
|
if ($this->place !== null) {
|
||||||
$lnglat = explode(' ', $this->place->location);
|
return $this->place->location->getLng();
|
||||||
|
|
||||||
return $lnglat[1];
|
|
||||||
}
|
}
|
||||||
if ($this->location !== null) {
|
if ($this->location !== null) {
|
||||||
$pieces = explode(':', $this->location);
|
$pieces = explode(':', $this->location);
|
||||||
|
@ -281,12 +277,13 @@ class Note extends Model
|
||||||
if (Cache::has($tweetId)) {
|
if (Cache::has($tweetId)) {
|
||||||
return Cache::get($tweetId);
|
return Cache::get($tweetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$oEmbed = Twitter::getOembed([
|
$oEmbed = Twitter::getOembed([
|
||||||
'id' => $tweetId,
|
'url' => $this->in_reply_to,
|
||||||
'dnt' => true,
|
'dnt' => true,
|
||||||
'align' => 'center',
|
'align' => 'center',
|
||||||
'maxwidth' => 550,
|
'maxwidth' => 512,
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return;
|
return;
|
||||||
|
@ -408,10 +405,10 @@ class Note extends Model
|
||||||
|
|
||||||
$contact = $this->contacts[$matches[1]]; // easier to read the following code
|
$contact = $this->contacts[$matches[1]]; // easier to read the following code
|
||||||
$host = parse_url($contact->homepage, PHP_URL_HOST);
|
$host = parse_url($contact->homepage, PHP_URL_HOST);
|
||||||
$contact->photo = (file_exists(public_path() . '/assets/profile-images/' . $host . '/image')) ?
|
$contact->photo = '/assets/profile-images/default-image';
|
||||||
'/assets/profile-images/' . $host . '/image'
|
if (file_exists(public_path() . '/assets/profile-images/' . $host . '/image')) {
|
||||||
:
|
$contact->photo = '/assets/profile-images/' . $host . '/image';
|
||||||
'/assets/profile-images/default-image';
|
}
|
||||||
|
|
||||||
return trim(view('templates.mini-hcard', ['contact' => $contact])->render());
|
return trim(view('templates.mini-hcard', ['contact' => $contact])->render());
|
||||||
},
|
},
|
||||||
|
@ -450,31 +447,17 @@ class Note extends Model
|
||||||
* @param string The note
|
* @param string The note
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function autoLinkHashtag($text)
|
public function autoLinkHashtag($text)
|
||||||
{
|
{
|
||||||
// $replacements = ["#tag" => "<a rel="tag" href="/tags/tag">#tag</a>]
|
return preg_replace_callback(
|
||||||
$replacements = [];
|
'/#([^\s]*)\b/',
|
||||||
$matches = [];
|
function ($matches) {
|
||||||
|
return '<a rel="tag" class="p-category" href="/notes/tagged/'
|
||||||
if (preg_match_all('/(?<=^|\s)\#([a-zA-Z0-9\-\_]+)/i', $text, $matches, PREG_PATTERN_ORDER)) {
|
. Tag::normalize($matches[1]) . '">#'
|
||||||
// Look up #tags, get Full name and URL
|
. Tag::normalize($matches[1]) . '</a>';
|
||||||
foreach ($matches[0] as $name) {
|
},
|
||||||
$name = str_replace('#', '', $name);
|
$text
|
||||||
$replacements[$name] =
|
);
|
||||||
'<a rel="tag" class="p-category" href="/notes/tagged/'
|
|
||||||
. Tag::normalize($name)
|
|
||||||
. '">#'
|
|
||||||
. $name
|
|
||||||
. '</a>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace #tags with valid microformat-enabled link
|
|
||||||
foreach ($replacements as $name => $replacement) {
|
|
||||||
$text = str_replace('#' . $name, $replacement, $text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function convertMarkdown($text)
|
private function convertMarkdown($text)
|
||||||
|
@ -536,7 +519,7 @@ class Note extends Model
|
||||||
|
|
||||||
return $address;
|
return $address;
|
||||||
}
|
}
|
||||||
$adress = '<span class="p-country-name">' . $json->address->country . '</span>';
|
$address = '<span class="p-country-name">' . $json->address->country . '</span>';
|
||||||
Cache::forever($latlng, $address);
|
Cache::forever($latlng, $address);
|
||||||
|
|
||||||
return $address;
|
return $address;
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
use DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Cviebrock\EloquentSluggable\Sluggable;
|
use Cviebrock\EloquentSluggable\Sluggable;
|
||||||
|
@ -53,7 +53,7 @@ class Place extends Model
|
||||||
*/
|
*/
|
||||||
public function notes()
|
public function notes()
|
||||||
{
|
{
|
||||||
return $this->hasMany('App\Note');
|
return $this->hasMany('App\Models\Note');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,15 +79,8 @@ class Place extends Model
|
||||||
|
|
||||||
public function scopeWhereExternalURL(Builder $query, string $url)
|
public function scopeWhereExternalURL(Builder $query, string $url)
|
||||||
{
|
{
|
||||||
$type = $this->getType($url);
|
|
||||||
if ($type === null) {
|
|
||||||
// we haven’t set a type, therefore result must be empty set
|
|
||||||
// id can’t be null, so this will return empty set
|
|
||||||
return $query->whereNull('id');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $query->where('external_urls', '@>', json_encode([
|
return $query->where('external_urls', '@>', json_encode([
|
||||||
$type => $url,
|
$this->getType($url) => $url,
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,12 +124,22 @@ class Place extends Model
|
||||||
return config('app.shorturl') . '/places/' . $this->slug;
|
return config('app.shorturl') . '/places/' . $this->slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is an alternative for `longurl`.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getUriAttribute()
|
||||||
|
{
|
||||||
|
return $this->longurl;
|
||||||
|
}
|
||||||
|
|
||||||
public function setExternalUrlsAttribute($url)
|
public function setExternalUrlsAttribute($url)
|
||||||
{
|
{
|
||||||
$type = $this->getType($url);
|
if ($url === null) {
|
||||||
if ($type === null) {
|
return;
|
||||||
throw new \Exception('Unkown external url type ' . $url);
|
|
||||||
}
|
}
|
||||||
|
$type = $this->getType($url);
|
||||||
$already = [];
|
$already = [];
|
||||||
if (array_key_exists('external_urls', $this->attributes)) {
|
if (array_key_exists('external_urls', $this->attributes)) {
|
||||||
$already = json_decode($this->attributes['external_urls'], true);
|
$already = json_decode($this->attributes['external_urls'], true);
|
||||||
|
@ -155,6 +158,6 @@ class Place extends Model
|
||||||
return 'osm';
|
return 'osm';
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return 'default';
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,18 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class Tag extends Model
|
class Tag extends Model
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* We shall set a blacklist of non-modifiable model attributes.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $guarded = ['id'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the relationship with tags.
|
* Define the relationship with tags.
|
||||||
*
|
*
|
||||||
|
@ -13,7 +20,7 @@ class Tag extends Model
|
||||||
*/
|
*/
|
||||||
public function notes()
|
public function notes()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany('App\Note');
|
return $this->belongsToMany('App\Models\Note');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,16 +28,9 @@ class Tag extends Model
|
||||||
*/
|
*/
|
||||||
public function bookmarks()
|
public function bookmarks()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany('App\Bookmark');
|
return $this->belongsToMany('App\Models\Bookmark');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* We shall set a blacklist of non-modifiable model attributes.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $guarded = ['id'];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize tags so they’re lowercase and fancy diatrics are removed.
|
* Normalize tags so they’re lowercase and fancy diatrics are removed.
|
||||||
*
|
*
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App;
|
namespace App\Models;
|
||||||
|
|
||||||
use Cache;
|
use Cache;
|
||||||
use Twitter;
|
use Twitter;
|
||||||
|
@ -19,6 +19,13 @@ class WebMention extends Model
|
||||||
*/
|
*/
|
||||||
protected $table = 'webmentions';
|
protected $table = 'webmentions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We shall set a blacklist of non-modifiable model attributes.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $guarded = ['id'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the relationship.
|
* Define the relationship.
|
||||||
*
|
*
|
||||||
|
@ -29,13 +36,6 @@ class WebMention extends Model
|
||||||
return $this->morphTo();
|
return $this->morphTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* We shall set a blacklist of non-modifiable model attributes.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $guarded = ['id'];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the author of the webmention.
|
* Get the author of the webmention.
|
||||||
*
|
*
|
||||||
|
@ -78,9 +78,9 @@ class WebMention extends Model
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the filteres HTML of a reply.
|
* Get the filtered HTML of a reply.
|
||||||
*
|
*
|
||||||
* @return strin|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function getReplyAttribute()
|
public function getReplyAttribute()
|
||||||
{
|
{
|
||||||
|
@ -108,14 +108,10 @@ class WebMention extends Model
|
||||||
if (Cache::has($url)) {
|
if (Cache::has($url)) {
|
||||||
return Cache::get($url);
|
return Cache::get($url);
|
||||||
}
|
}
|
||||||
$username = parse_url($url, PHP_URL_PATH);
|
$username = ltrim(parse_url($url, PHP_URL_PATH), '/');
|
||||||
try {
|
$info = Twitter::getUsers(['screen_name' => $username]);
|
||||||
$info = Twitter::getUsers(['screen_name' => $username]);
|
$profile_image = $info->profile_image_url_https;
|
||||||
$profile_image = $info->profile_image_url_https;
|
Cache::put($url, $profile_image, 10080); //1 week
|
||||||
Cache::put($url, $profile_image, 10080); //1 week
|
|
||||||
} catch (Exception $e) {
|
|
||||||
return $url; //not sure here
|
|
||||||
}
|
|
||||||
|
|
||||||
return $profile_image;
|
return $profile_image;
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Observers;
|
namespace App\Observers;
|
||||||
|
|
||||||
use App\{Note, Tag};
|
use App\Models\{Note, Tag};
|
||||||
|
|
||||||
class NoteObserver
|
class NoteObserver
|
||||||
{
|
{
|
||||||
|
@ -21,12 +21,10 @@ class NoteObserver
|
||||||
}
|
}
|
||||||
|
|
||||||
$tags->transform(function ($tag) {
|
$tags->transform(function ($tag) {
|
||||||
return Tag::firstOrCreate(['tag' => $tag]);
|
return Tag::firstOrCreate(['tag' => $tag])->id;
|
||||||
});
|
})->toArray();
|
||||||
|
|
||||||
$note->tags()->attach($tags->map(function ($tag) {
|
$note->tags()->attach($tags);
|
||||||
return $tag->id;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,9 +63,6 @@ class NoteObserver
|
||||||
public function getTagsFromNote($note)
|
public function getTagsFromNote($note)
|
||||||
{
|
{
|
||||||
preg_match_all('/#([^\s<>]+)\b/', $note, $tags);
|
preg_match_all('/#([^\s<>]+)\b/', $note, $tags);
|
||||||
if (array_get($tags, '1') === null) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return collect($tags[1])->map(function ($tag) {
|
return collect($tags[1])->map(function ($tag) {
|
||||||
return Tag::normalize($tag);
|
return Tag::normalize($tag);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use App\Note;
|
use App\Models\Note;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Observers\NoteObserver;
|
use App\Observers\NoteObserver;
|
||||||
use Laravel\Dusk\DuskServiceProvider;
|
use Laravel\Dusk\DuskServiceProvider;
|
||||||
|
|
|
@ -5,6 +5,9 @@ namespace App\Providers;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Illuminate\Support\Facades\Broadcast;
|
use Illuminate\Support\Facades\Broadcast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
class BroadcastServiceProvider extends ServiceProvider
|
class BroadcastServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -6,6 +6,9 @@ use Illuminate\Http\Request;
|
||||||
use Laravel\Horizon\Horizon;
|
use Laravel\Horizon\Horizon;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
class HorizonServiceProvider extends ServiceProvider
|
class HorizonServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use App\Note;
|
use App\Models\Note;
|
||||||
|
|
||||||
class ActivityStreamsService
|
class ActivityStreamsService
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,42 +4,39 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use App\Tag;
|
|
||||||
use App\Bookmark;
|
|
||||||
use Ramsey\Uuid\Uuid;
|
use Ramsey\Uuid\Uuid;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Jobs\ProcessBookmark;
|
use App\Jobs\ProcessBookmark;
|
||||||
|
use App\Models\{Bookmark, Tag};
|
||||||
use Spatie\Browsershot\Browsershot;
|
use Spatie\Browsershot\Browsershot;
|
||||||
use App\Jobs\SyndicateBookmarkToTwitter;
|
use App\Jobs\SyndicateBookmarkToTwitter;
|
||||||
use App\Jobs\SyndicateBookmarkToFacebook;
|
use App\Jobs\SyndicateBookmarkToFacebook;
|
||||||
use App\Exceptions\InternetArchiveErrorSavingException;
|
use GuzzleHttp\Exception\ClientException;
|
||||||
|
use App\Exceptions\InternetArchiveException;
|
||||||
|
|
||||||
class BookmarkService
|
class BookmarkService
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Create a new Bookmark.
|
* Create a new Bookmark.
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param array $request
|
||||||
|
* @return Bookmark $bookmark
|
||||||
*/
|
*/
|
||||||
public function createBookmark(Request $request): Bookmark
|
public function createBookmark(array $request): Bookmark
|
||||||
{
|
{
|
||||||
if ($request->header('Content-Type') == 'application/json') {
|
if (array_get($request, 'properties.bookmark-of.0')) {
|
||||||
//micropub request
|
//micropub request
|
||||||
$url = normalize_url($request->input('properties.bookmark-of.0'));
|
$url = normalize_url(array_get($request, 'properties.bookmark-of.0'));
|
||||||
$name = $request->input('properties.name.0');
|
$name = array_get($request, 'properties.name.0');
|
||||||
$content = $request->input('properties.content.0');
|
$content = array_get($request, 'properties.content.0');
|
||||||
$categories = $request->input('properties.category');
|
$categories = array_get($request, 'properties.category');
|
||||||
}
|
}
|
||||||
if (
|
if (array_get($request, 'bookmark-of')) {
|
||||||
($request->header('Content-Type') == 'application/x-www-form-urlencoded')
|
$url = normalize_url(array_get($request, 'bookmark-of'));
|
||||||
||
|
$name = array_get($request, 'name');
|
||||||
(str_contains($request->header('Content-Type'), 'multipart/form-data'))
|
$content = array_get($request, 'content');
|
||||||
) {
|
$categories = array_get($request, 'category');
|
||||||
$url = normalize_url($request->input('bookmark-of'));
|
|
||||||
$name = $request->input('name');
|
|
||||||
$content = $request->input('content');
|
|
||||||
$categories = $request->input('category');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$bookmark = Bookmark::create([
|
$bookmark = Bookmark::create([
|
||||||
|
@ -55,11 +52,11 @@ class BookmarkService
|
||||||
|
|
||||||
$targets = array_pluck(config('syndication.targets'), 'uid', 'service.name');
|
$targets = array_pluck(config('syndication.targets'), 'uid', 'service.name');
|
||||||
$mpSyndicateTo = null;
|
$mpSyndicateTo = null;
|
||||||
if ($request->has('mp-syndicate-to')) {
|
if (array_get($request, 'mp-syndicate-to')) {
|
||||||
$mpSyndicateTo = $request->input('mp-syndicate-to');
|
$mpSyndicateTo = array_get($request, 'mp-syndicate-to');
|
||||||
}
|
}
|
||||||
if ($request->has('properties.mp-syndicate-to')) {
|
if (array_get($request, 'properties.mp-syndicate-to')) {
|
||||||
$mpSyndicateTo = $request->input('properties.mp-syndicate-to');
|
$mpSyndicateTo = array_get($request, 'properties.mp-syndicate-to');
|
||||||
}
|
}
|
||||||
if (is_string($mpSyndicateTo)) {
|
if (is_string($mpSyndicateTo)) {
|
||||||
$service = array_search($mpSyndicateTo, $targets);
|
$service = array_search($mpSyndicateTo, $targets);
|
||||||
|
@ -104,20 +101,20 @@ class BookmarkService
|
||||||
|
|
||||||
public function getArchiveLink(string $url): string
|
public function getArchiveLink(string $url): string
|
||||||
{
|
{
|
||||||
$client = new Client();
|
$client = resolve(Client::class);
|
||||||
|
try {
|
||||||
$response = $client->request('GET', 'https://web.archive.org/save/' . $url);
|
$response = $client->request('GET', 'https://web.archive.org/save/' . $url);
|
||||||
|
} catch (ClientException $e) {
|
||||||
|
//throw an exception to be caught
|
||||||
|
throw new InternetArchiveException;
|
||||||
|
}
|
||||||
if ($response->hasHeader('Content-Location')) {
|
if ($response->hasHeader('Content-Location')) {
|
||||||
if (starts_with($response->getHeader('Content-Location')[0], '/web')) {
|
if (starts_with(array_get($response->getHeader('Content-Location'), 0), '/web')) {
|
||||||
return $response->getHeader('Content-Location')[0];
|
return $response->getHeader('Content-Location')[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (starts_with(array_get($response->getHeader('Content-Location'), 0), '/web')) {
|
|
||||||
return $response->getHeader('Content-Location')[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
//throw an exception to be caught
|
//throw an exception to be caught
|
||||||
throw new InternetArchiveErrorSavingException;
|
throw new InternetArchiveException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,29 +4,25 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use App\Like;
|
use App\Models\Like;
|
||||||
use App\Jobs\ProcessLike;
|
use App\Jobs\ProcessLike;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class LikeService
|
class LikeService
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Create a new Like.
|
* Create a new Like.
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param array $request
|
||||||
|
* @return Like $like
|
||||||
*/
|
*/
|
||||||
public function createLike(Request $request): Like
|
public function createLike(array $request): Like
|
||||||
{
|
{
|
||||||
if ($request->header('Content-Type') == 'application/json') {
|
if (array_get($request, 'properties.like-of.0')) {
|
||||||
//micropub request
|
//micropub request
|
||||||
$url = normalize_url($request->input('properties.like-of.0'));
|
$url = normalize_url(array_get($request, 'properties.like-of.0'));
|
||||||
}
|
}
|
||||||
if (
|
if (array_get($request, 'like-of')) {
|
||||||
($request->header('Content-Type') == 'x-www-url-formencoded')
|
$url = normalize_url(array_get($request, 'like-of'));
|
||||||
||
|
|
||||||
($request->header('Content-Type') == 'multipart/form-data')
|
|
||||||
) {
|
|
||||||
$url = normalize_url($request->input('like-of'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$like = Like::create(['url' => $url]);
|
$like = Like::create(['url' => $url]);
|
||||||
|
|
27
app/Services/Micropub/HCardService.php
Normal file
27
app/Services/Micropub/HCardService.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\Micropub;
|
||||||
|
|
||||||
|
use App\Services\PlaceService;
|
||||||
|
|
||||||
|
class HCardService
|
||||||
|
{
|
||||||
|
public function process(array $request)
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
if (array_get($request, 'properties.name')) {
|
||||||
|
$data['name'] = array_get($request, 'properties.name');
|
||||||
|
$data['description'] = array_get($request, 'properties.description');
|
||||||
|
$data['geo'] = array_get($request, 'properties.geo');
|
||||||
|
} else {
|
||||||
|
$data['name'] = array_get($request, 'name');
|
||||||
|
$data['description'] = array_get($request, 'description');
|
||||||
|
$data['geo'] = array_get($request, 'geo');
|
||||||
|
$data['latitude'] = array_get($request, 'latitude');
|
||||||
|
$data['longitude'] = array_get($request, 'longitude');
|
||||||
|
}
|
||||||
|
$place = resolve(PlaceService::class)->createPlace($data);
|
||||||
|
|
||||||
|
return $place->longurl;
|
||||||
|
}
|
||||||
|
}
|
27
app/Services/Micropub/HEntryService.php
Normal file
27
app/Services/Micropub/HEntryService.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\Micropub;
|
||||||
|
|
||||||
|
use App\Services\{BookmarkService, LikeService, NoteService};
|
||||||
|
|
||||||
|
class HEntryService
|
||||||
|
{
|
||||||
|
public function process(array $request, string $client = null)
|
||||||
|
{
|
||||||
|
if (array_get($request, 'properties.like-of') || array_get($request, 'like-of')) {
|
||||||
|
$like = resolve(LikeService::class)->createLike($request);
|
||||||
|
|
||||||
|
return $like->longurl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_get($request, 'properties.bookmark-of') || array_get($request, 'bookmark-of')) {
|
||||||
|
$bookmark = resolve(BookmarkService::class)->createBookmark($request);
|
||||||
|
|
||||||
|
return $bookmark->longurl;
|
||||||
|
}
|
||||||
|
|
||||||
|
$note = resolve(NoteService::class)->createNote($request, $client);
|
||||||
|
|
||||||
|
return $note->longurl;
|
||||||
|
}
|
||||||
|
}
|
98
app/Services/Micropub/UpdateService.php
Normal file
98
app/Services/Micropub/UpdateService.php
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\Micropub;
|
||||||
|
|
||||||
|
use App\Models\{Media, Note};
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
|
|
||||||
|
class UpdateService
|
||||||
|
{
|
||||||
|
public function process(array $request)
|
||||||
|
{
|
||||||
|
$urlPath = parse_url(array_get($request, 'url'), PHP_URL_PATH);
|
||||||
|
|
||||||
|
//is it a note we are updating?
|
||||||
|
if (mb_substr($urlPath, 1, 5) !== 'notes') {
|
||||||
|
return response()->json([
|
||||||
|
'error' => 'invalid',
|
||||||
|
'error_description' => 'This implementation currently only support the updating of notes',
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$note = Note::nb60(basename($urlPath))->firstOrFail();
|
||||||
|
} catch (ModelNotFoundException $exception) {
|
||||||
|
return response()->json([
|
||||||
|
'error' => 'invalid_request',
|
||||||
|
'error_description' => 'No known note with given ID',
|
||||||
|
], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
//got the note, are we dealing with a “replace” request?
|
||||||
|
if (array_get($request, 'replace')) {
|
||||||
|
foreach (array_get($request, 'replace') as $property => $value) {
|
||||||
|
if ($property == 'content') {
|
||||||
|
$note->note = $value[0];
|
||||||
|
}
|
||||||
|
if ($property == 'syndication') {
|
||||||
|
foreach ($value as $syndicationURL) {
|
||||||
|
if (starts_with($syndicationURL, 'https://www.facebook.com')) {
|
||||||
|
$note->facebook_url = $syndicationURL;
|
||||||
|
}
|
||||||
|
if (starts_with($syndicationURL, 'https://www.swarmapp.com')) {
|
||||||
|
$note->swarm_url = $syndicationURL;
|
||||||
|
}
|
||||||
|
if (starts_with($syndicationURL, 'https://twitter.com')) {
|
||||||
|
$note->tweet_id = basename(parse_url($syndicationURL, PHP_URL_PATH));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$note->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'response' => 'updated',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//how about “add”
|
||||||
|
if (array_get($request, 'add')) {
|
||||||
|
foreach (array_get($request, 'add') as $property => $value) {
|
||||||
|
if ($property == 'syndication') {
|
||||||
|
foreach ($value as $syndicationURL) {
|
||||||
|
if (starts_with($syndicationURL, 'https://www.facebook.com')) {
|
||||||
|
$note->facebook_url = $syndicationURL;
|
||||||
|
}
|
||||||
|
if (starts_with($syndicationURL, 'https://www.swarmapp.com')) {
|
||||||
|
$note->swarm_url = $syndicationURL;
|
||||||
|
}
|
||||||
|
if (starts_with($syndicationURL, 'https://twitter.com')) {
|
||||||
|
$note->tweet_id = basename(parse_url($syndicationURL, PHP_URL_PATH));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($property == 'photo') {
|
||||||
|
foreach ($value as $photoURL) {
|
||||||
|
if (starts_with($photoURL, 'https://')) {
|
||||||
|
$media = new Media();
|
||||||
|
$media->path = $photoURL;
|
||||||
|
$media->type = 'image';
|
||||||
|
$media->save();
|
||||||
|
$note->media()->save($media);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$note->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'response' => 'updated',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'response' => 'error',
|
||||||
|
'error_description' => 'unsupported request',
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use App\{Media, Note, Place};
|
use App\Models\{Media, Note, Place};
|
||||||
use App\Jobs\{SendWebMentions, SyndicateNoteToFacebook, SyndicateNoteToTwitter};
|
use App\Jobs\{SendWebMentions, SyndicateNoteToFacebook, SyndicateNoteToTwitter};
|
||||||
|
|
||||||
class NoteService
|
class NoteService
|
||||||
|
@ -12,91 +12,38 @@ class NoteService
|
||||||
/**
|
/**
|
||||||
* Create a new note.
|
* Create a new note.
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $request
|
||||||
|
* @param string $client
|
||||||
* @return \App\Note $note
|
* @return \App\Note $note
|
||||||
*/
|
*/
|
||||||
public function createNote(array $data): Note
|
public function createNote(array $request, string $client = null): Note
|
||||||
{
|
{
|
||||||
|
|
||||||
//check the input
|
|
||||||
if (array_key_exists('content', $data) === false) {
|
|
||||||
$data['content'] = null;
|
|
||||||
}
|
|
||||||
if (array_key_exists('in-reply-to', $data) === false) {
|
|
||||||
$data['in-reply-to'] = null;
|
|
||||||
}
|
|
||||||
if (array_key_exists('client-id', $data) === false) {
|
|
||||||
$data['client-id'] = null;
|
|
||||||
}
|
|
||||||
$note = Note::create(
|
$note = Note::create(
|
||||||
[
|
[
|
||||||
'note' => $data['content'],
|
'note' => $this->getContent($request),
|
||||||
'in_reply_to' => $data['in-reply-to'],
|
'in_reply_to' => $this->getInReplyTo($request),
|
||||||
'client_id' => $data['client-id'],
|
'client_id' => $client,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (array_key_exists('published', $data) && empty($data['published']) === false) {
|
if ($this->getPublished($request)) {
|
||||||
$carbon = carbon($data['published']);
|
$note->created_at = $note->updated_at = $this->getPublished($request);
|
||||||
$note->created_at = $note->updated_at = $carbon->toDateTimeString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('location', $data) && $data['location'] !== null && $data['location'] !== 'no-location') {
|
$note->location = $this->getLocation($request);
|
||||||
if (starts_with($data['location'], config('app.url'))) {
|
|
||||||
//uri of form http://host/places/slug, we want slug
|
if ($this->getCheckin($request)) {
|
||||||
//get the URL path, then take last part, we can hack with basename
|
$note->place()->associate($this->getCheckin($request));
|
||||||
//as path looks like file path.
|
$note->swarm_url = $this->getSwarmUrl($request);
|
||||||
$place = Place::where('slug', basename(parse_url($data['location'], PHP_URL_PATH)))->first();
|
if ($note->note === null || $note->note == '') {
|
||||||
$note->place()->associate($place);
|
$note->note = 'I’ve just checked in with Swarm';
|
||||||
}
|
|
||||||
if (substr($data['location'], 0, 4) == 'geo:') {
|
|
||||||
preg_match_all(
|
|
||||||
'/([0-9\.\-]+)/',
|
|
||||||
$data['location'],
|
|
||||||
$matches
|
|
||||||
);
|
|
||||||
$note->location = $matches[0][0] . ', ' . $matches[0][1];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('checkin', $data) && $data['checkin'] !== null) {
|
$note->instagram_url = $this->getInstagramUrl($request);
|
||||||
$place = Place::where('slug', basename(parse_url($data['checkin'], PHP_URL_PATH)))->first();
|
|
||||||
if ($place !== null) {
|
|
||||||
$note->place()->associate($place);
|
|
||||||
$note->swarm_url = $data['swarm-url'];
|
|
||||||
if ($note->note === null || $note->note == '') {
|
|
||||||
$note->note = 'I’ve just checked in with Swarm';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* drop image support for now
|
foreach ($this->getMedia($request) as $media) {
|
||||||
//add images to media library
|
$note->media()->save($media);
|
||||||
if ($request->hasFile('photo')) {
|
|
||||||
$files = $request->file('photo');
|
|
||||||
foreach ($files as $file) {
|
|
||||||
$note->addMedia($file)->toCollectionOnDisk('images', 's3');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
//add support for media uploaded as URLs
|
|
||||||
if (array_key_exists('photo', $data)) {
|
|
||||||
foreach ($data['photo'] as $photo) {
|
|
||||||
// check the media was uploaded to my endpoint, and use path
|
|
||||||
if (starts_with($photo, config('filesystems.disks.s3.url'))) {
|
|
||||||
$path = substr($photo, strlen(config('filesystems.disks.s3.url')));
|
|
||||||
$media = Media::where('path', ltrim($path, '/'))->firstOrFail();
|
|
||||||
} else {
|
|
||||||
$media = Media::firstOrNew(['path' => $photo]);
|
|
||||||
// currently assuming this is a photo from Swarm or OwnYourGram
|
|
||||||
$media->type = 'image';
|
|
||||||
$media->save();
|
|
||||||
}
|
|
||||||
$note->media()->save($media);
|
|
||||||
}
|
|
||||||
if (array_key_exists('instagram-url', $data)) {
|
|
||||||
$note->instagram_url = $data['instagram-url'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$note->save();
|
$note->save();
|
||||||
|
@ -104,13 +51,175 @@ class NoteService
|
||||||
dispatch(new SendWebMentions($note));
|
dispatch(new SendWebMentions($note));
|
||||||
|
|
||||||
//syndication targets
|
//syndication targets
|
||||||
if (in_array('twitter', $data['syndicate'])) {
|
if (count($this->getSyndicationTargets($request)) > 0) {
|
||||||
dispatch(new SyndicateNoteToTwitter($note));
|
if (in_array('twitter', $this->getSyndicationTargets($request))) {
|
||||||
}
|
dispatch(new SyndicateNoteToTwitter($note));
|
||||||
if (in_array('facebook', $data['syndicate'])) {
|
}
|
||||||
dispatch(new SyndicateNoteToFacebook($note));
|
if (in_array('facebook', $this->getSyndicationTargets($request))) {
|
||||||
|
dispatch(new SyndicateNoteToFacebook($note));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $note;
|
return $note;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getContent(array $request): ?string
|
||||||
|
{
|
||||||
|
if (array_get($request, 'properties.content.0.html')) {
|
||||||
|
return array_get($request, 'properties.content.0.html');
|
||||||
|
}
|
||||||
|
if (is_string(array_get($request, 'properties.content.0'))) {
|
||||||
|
return array_get($request, 'properties.content.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_get($request, 'content');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getInReplyTo(array $request): ?string
|
||||||
|
{
|
||||||
|
if (array_get($request, 'properties.in-reply-to.0')) {
|
||||||
|
return array_get($request, 'properties.in-reply-to.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_get($request, 'in-reply-to');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPublished(array $request): ?string
|
||||||
|
{
|
||||||
|
if (array_get($request, 'properties.published.0')) {
|
||||||
|
return carbon(array_get($request, 'properties.published.0'))
|
||||||
|
->toDateTimeString();
|
||||||
|
}
|
||||||
|
if (array_get($request, 'published')) {
|
||||||
|
return carbon(array_get($request, 'published'))->toDateTimeString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getLocation(array $request): ?string
|
||||||
|
{
|
||||||
|
$location = array_get($request, 'properties.location.0') ?? array_get($request, 'location');
|
||||||
|
if (is_string($location) && substr($location, 0, 4) == 'geo:') {
|
||||||
|
preg_match_all(
|
||||||
|
'/([0-9\.\-]+)/',
|
||||||
|
$location,
|
||||||
|
$matches
|
||||||
|
);
|
||||||
|
|
||||||
|
return $matches[0][0] . ', ' . $matches[0][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getCheckin(array $request): ?Place
|
||||||
|
{
|
||||||
|
if (array_get($request, 'properties.location.0.type.0') === 'h-card') {
|
||||||
|
try {
|
||||||
|
$place = resolve(PlaceService::class)->createPlaceFromCheckin(
|
||||||
|
array_get($request, 'properties.location.0')
|
||||||
|
);
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $place;
|
||||||
|
}
|
||||||
|
if (starts_with(array_get($request, 'properties.location.0'), config('app.url'))) {
|
||||||
|
return Place::where(
|
||||||
|
'slug',
|
||||||
|
basename(
|
||||||
|
parse_url(
|
||||||
|
array_get($request, 'properties.location.0'),
|
||||||
|
PHP_URL_PATH
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)->first();
|
||||||
|
}
|
||||||
|
if (array_get($request, 'properties.checkin')) {
|
||||||
|
try {
|
||||||
|
$place = resolve(PlaceService::class)->createPlaceFromCheckin(
|
||||||
|
array_get($request, 'properties.checkin.0')
|
||||||
|
);
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $place;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getSwarmUrl(array $request): ?string
|
||||||
|
{
|
||||||
|
if (stristr(array_get($request, 'properties.syndication.0', ''), 'swarmapp')) {
|
||||||
|
return array_get($request, 'properties.syndication.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getSyndicationTargets(array $request): array
|
||||||
|
{
|
||||||
|
$syndication = [];
|
||||||
|
$targets = array_pluck(config('syndication.targets'), 'uid', 'service.name');
|
||||||
|
$mpSyndicateTo = array_get($request, 'mp-syndicate-to') ?? array_get($request, 'properties.mp-syndicate-to');
|
||||||
|
if (is_string($mpSyndicateTo)) {
|
||||||
|
$service = array_search($mpSyndicateTo, $targets);
|
||||||
|
if ($service == 'Twitter') {
|
||||||
|
$syndication[] = 'twitter';
|
||||||
|
}
|
||||||
|
if ($service == 'Facebook') {
|
||||||
|
$syndication[] = 'facebook';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_array($mpSyndicateTo)) {
|
||||||
|
foreach ($mpSyndicateTo as $uid) {
|
||||||
|
$service = array_search($uid, $targets);
|
||||||
|
if ($service == 'Twitter') {
|
||||||
|
$syndication[] = 'twitter';
|
||||||
|
}
|
||||||
|
if ($service == 'Facebook') {
|
||||||
|
$syndication[] = 'facebook';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $syndication;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMedia(array $request): array
|
||||||
|
{
|
||||||
|
$media = [];
|
||||||
|
$photos = array_get($request, 'photo') ?? array_get($request, 'properties.photo');
|
||||||
|
|
||||||
|
if (isset($photos)) {
|
||||||
|
foreach ((array) $photos as $photo) {
|
||||||
|
// check the media was uploaded to my endpoint, and use path
|
||||||
|
if (starts_with($photo, config('filesystems.disks.s3.url'))) {
|
||||||
|
$path = substr($photo, strlen(config('filesystems.disks.s3.url')));
|
||||||
|
$media[] = Media::where('path', ltrim($path, '/'))->firstOrFail();
|
||||||
|
} else {
|
||||||
|
$newMedia = Media::firstOrNew(['path' => $photo]);
|
||||||
|
// currently assuming this is a photo from Swarm or OwnYourGram
|
||||||
|
$newMedia->type = 'image';
|
||||||
|
$newMedia->save();
|
||||||
|
$media[] = $newMedia;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $media;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getInstagramUrl(array $request): ?string
|
||||||
|
{
|
||||||
|
if (starts_with(array_get($request, 'properties.syndication.0'), 'https://www.instagram.com')) {
|
||||||
|
return array_get($request, 'properties.syndication.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use App\Place;
|
use App\Models\Place;
|
||||||
use Phaza\LaravelPostgis\Geometries\Point;
|
use Phaza\LaravelPostgis\Geometries\Point;
|
||||||
|
|
||||||
class PlaceService
|
class PlaceService
|
||||||
|
@ -19,7 +19,7 @@ class PlaceService
|
||||||
{
|
{
|
||||||
//obviously a place needs a lat/lng, but this could be sent in a geo-url
|
//obviously a place needs a lat/lng, but this could be sent in a geo-url
|
||||||
//if no geo array key, we assume the array already has lat/lng values
|
//if no geo array key, we assume the array already has lat/lng values
|
||||||
if (array_key_exists('geo', $data)) {
|
if (array_key_exists('geo', $data) && $data['geo'] !== null) {
|
||||||
preg_match_all(
|
preg_match_all(
|
||||||
'/([0-9\.\-]+)/',
|
'/([0-9\.\-]+)/',
|
||||||
$data['geo'],
|
$data['geo'],
|
||||||
|
@ -46,24 +46,24 @@ class PlaceService
|
||||||
public function createPlaceFromCheckin(array $checkin): Place
|
public function createPlaceFromCheckin(array $checkin): Place
|
||||||
{
|
{
|
||||||
//check if the place exists if from swarm
|
//check if the place exists if from swarm
|
||||||
if (array_key_exists('url', $checkin['properties'])) {
|
if (array_has($checkin, 'properties.url')) {
|
||||||
$place = Place::whereExternalURL($checkin['properties']['url'][0])->get();
|
$place = Place::whereExternalURL(array_get($checkin, 'properties.url.0'))->get();
|
||||||
if (count($place) === 1) {
|
if (count($place) === 1) {
|
||||||
return $place->first();
|
return $place->first();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (array_key_exists('name', $checkin['properties']) === false) {
|
if (array_has($checkin, 'properties.name') === false) {
|
||||||
throw new \InvalidArgumentException('Missing required name');
|
throw new \InvalidArgumentException('Missing required name');
|
||||||
}
|
}
|
||||||
if (array_key_exists('latitude', $checkin['properties']) === false) {
|
if (array_has($checkin, 'properties.latitude') === false) {
|
||||||
throw new \InvalidArgumentException('Missing required longitude/latitude');
|
throw new \InvalidArgumentException('Missing required longitude/latitude');
|
||||||
}
|
}
|
||||||
$place = new Place();
|
$place = new Place();
|
||||||
$place->name = $checkin['properties']['name'][0];
|
$place->name = array_get($checkin, 'properties.name.0');
|
||||||
$place->external_urls = $checkin['properties']['url'][0];
|
$place->external_urls = array_get($checkin, 'properties.url.0');
|
||||||
$place->location = new Point(
|
$place->location = new Point(
|
||||||
(float) $checkin['properties']['latitude'][0],
|
(float) array_get($checkin, 'properties.latitude.0'),
|
||||||
(float) $checkin['properties']['longitude'][0]
|
(float) array_get($checkin, 'properties.longitude.0')
|
||||||
);
|
);
|
||||||
$place->save();
|
$place->save();
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ class TokenService
|
||||||
* @param string The token
|
* @param string The token
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function validateToken(string $bearerToken): ?Token
|
public function validateToken(string $bearerToken): Token
|
||||||
{
|
{
|
||||||
$signer = new Sha256();
|
$signer = new Sha256();
|
||||||
try {
|
try {
|
||||||
|
@ -47,7 +47,7 @@ class TokenService
|
||||||
throw new InvalidTokenException('Token could not be parsed');
|
throw new InvalidTokenException('Token could not be parsed');
|
||||||
}
|
}
|
||||||
if (! $token->verify($signer, config('app.key'))) {
|
if (! $token->verify($signer, config('app.key'))) {
|
||||||
throw new InvalidTokenException('Token failed verification');
|
throw new InvalidTokenException('Token failed validation');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $token;
|
return $token;
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Version 0.14 (2017-12-22)
|
||||||
|
- Tests
|
||||||
|
- Refactor
|
||||||
|
- More tests, seriously, code-coverage to now above 90%
|
||||||
|
|
||||||
## Version 0.13.1 (2017-11-20)
|
## Version 0.13.1 (2017-11-20)
|
||||||
- A small fix when adding a new bookmark
|
- A small fix when adding a new bookmark
|
||||||
|
|
||||||
|
|
|
@ -35,12 +35,14 @@
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"barryvdh/laravel-debugbar": "~3.0",
|
"barryvdh/laravel-debugbar": "~3.0",
|
||||||
"bmitch/churn-php": "^0.2.0",
|
"bmitch/churn-php": "^0.2.0",
|
||||||
|
"codedungeon/phpunit-result-printer": "^0.3.0",
|
||||||
"filp/whoops": "~2.0",
|
"filp/whoops": "~2.0",
|
||||||
"fzaninotto/faker": "~1.4",
|
"fzaninotto/faker": "~1.4",
|
||||||
"jakub-onderka/php-parallel-lint": "^0.9.2",
|
"jakub-onderka/php-parallel-lint": "^0.9.2",
|
||||||
"laravel/dusk": "^2.0",
|
"laravel/dusk": "^2.0",
|
||||||
"mockery/mockery": "0.9.*",
|
"mockery/mockery": "0.9.*",
|
||||||
"nunomaduro/collision": "^1.1",
|
"nunomaduro/collision": "^1.1",
|
||||||
|
"php-coveralls/php-coveralls": "^1.0",
|
||||||
"phpunit/phpunit": "~6.0",
|
"phpunit/phpunit": "~6.0",
|
||||||
"sebastian/phpcpd": "^3.0"
|
"sebastian/phpcpd": "^3.0"
|
||||||
},
|
},
|
||||||
|
|
1052
composer.lock
generated
1052
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Bookmark;
|
||||||
use Faker\Generator as Faker;
|
use Faker\Generator as Faker;
|
||||||
|
|
||||||
$factory->define(App\Bookmark::class, function (Faker $faker) {
|
$factory->define(Bookmark::class, function (Faker $faker) {
|
||||||
return [
|
return [
|
||||||
'url' => $faker->url,
|
'url' => $faker->url,
|
||||||
'name' => $faker->sentence,
|
'name' => $faker->sentence,
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Like;
|
||||||
use Faker\Generator as Faker;
|
use Faker\Generator as Faker;
|
||||||
|
|
||||||
$factory->define(App\Like::class, function (Faker $faker) {
|
$factory->define(Like::class, function (Faker $faker) {
|
||||||
return [
|
return [
|
||||||
'url' => $faker->url,
|
'url' => $faker->url,
|
||||||
'author_name' => $faker->name,
|
'author_name' => $faker->name,
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Note;
|
||||||
use Faker\Generator as Faker;
|
use Faker\Generator as Faker;
|
||||||
|
|
||||||
$factory->define(App\Note::class, function (Faker $faker) {
|
$factory->define(Note::class, function (Faker $faker) {
|
||||||
return [
|
return [
|
||||||
'note' => $faker->paragraph,
|
'note' => $faker->paragraph,
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Tag;
|
||||||
use Faker\Generator as Faker;
|
use Faker\Generator as Faker;
|
||||||
|
|
||||||
$factory->define(App\Tag::class, function (Faker $faker) {
|
$factory->define(Tag::class, function (Faker $faker) {
|
||||||
return [
|
return [
|
||||||
'tag' => $faker->word,
|
'tag' => $faker->word,
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class UpdateModelsReferenceInWebmentionsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('webmentions', function (Blueprint $table) {
|
||||||
|
DB::statement("UPDATE webmentions SET commentable_type = 'App\\Models\\Note' WHERE commentable_type = 'App\\Note'");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('webmentions', function (Blueprint $table) {
|
||||||
|
DB::statement("UPDATE webmentions SET commentable_type = 'App\\Note' WHERE commentable_type = 'App\\Models\\Note'");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Article;
|
use App\Models\Article;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
class ArticlesTableSeeder extends Seeder
|
class ArticlesTableSeeder extends Seeder
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Models\{Bookmark, Tag};
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
class BookmarksTableSeeder extends Seeder
|
class BookmarksTableSeeder extends Seeder
|
||||||
|
@ -11,8 +12,8 @@ class BookmarksTableSeeder extends Seeder
|
||||||
*/
|
*/
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
factory(App\Bookmark::class, 10)->create()->each(function ($bookmark) {
|
factory(Bookmark::class, 10)->create()->each(function ($bookmark) {
|
||||||
$bookmark->tags()->save(factory(App\Tag::class)->make());
|
$bookmark->tags()->save(factory(Tag::class)->make());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Contact;
|
use App\Models\Contact;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
class ContactsTableSeeder extends Seeder
|
class ContactsTableSeeder extends Seeder
|
||||||
|
@ -22,7 +22,6 @@ class ContactsTableSeeder extends Seeder
|
||||||
'nick' => 'aaron',
|
'nick' => 'aaron',
|
||||||
'name' => 'Aaron Parecki',
|
'name' => 'Aaron Parecki',
|
||||||
'homepage' => 'https://aaronparecki.com',
|
'homepage' => 'https://aaronparecki.com',
|
||||||
'twitter' => 'aaronpk',
|
|
||||||
'facebook' => '123456',
|
'facebook' => '123456',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Like;
|
||||||
|
use Faker\Generator;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
class LikesTableSeeder extends Seeder
|
class LikesTableSeeder extends Seeder
|
||||||
|
@ -11,6 +13,16 @@ class LikesTableSeeder extends Seeder
|
||||||
*/
|
*/
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
factory(App\Like::class, 10)->create();
|
factory(Like::class, 10)->create();
|
||||||
|
|
||||||
|
$faker = new Generator();
|
||||||
|
$faker->addProvider(new \Faker\Provider\en_US\Person($faker));
|
||||||
|
$faker->addProvider(new \Faker\Provider\Lorem($faker));
|
||||||
|
$faker->addProvider(new \Faker\Provider\Internet($faker));
|
||||||
|
Like::create([
|
||||||
|
'url' => $faker->url,
|
||||||
|
'author_url' => $faker->url,
|
||||||
|
'author_name' => $faker->name,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
use App\Models\{Media, Note, Place};
|
||||||
|
|
||||||
class NotesTableSeeder extends Seeder
|
class NotesTableSeeder extends Seeder
|
||||||
{
|
{
|
||||||
|
@ -11,26 +12,31 @@ class NotesTableSeeder extends Seeder
|
||||||
*/
|
*/
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
factory(App\Note::class, 10)->create();
|
factory(Note::class, 10)->create();
|
||||||
sleep(1);
|
sleep(1);
|
||||||
$noteWithPlace = App\Note::create([
|
$noteTwitterReply = Note::create([
|
||||||
|
'note' => 'What does this even mean?',
|
||||||
|
'in_reply_to' => 'https://twitter.com/realDonaldTrump/status/933662564587855877',
|
||||||
|
]);
|
||||||
|
sleep(1);
|
||||||
|
$noteWithPlace = Note::create([
|
||||||
'note' => 'Having a #beer at the local. 🍺',
|
'note' => 'Having a #beer at the local. 🍺',
|
||||||
]);
|
]);
|
||||||
$noteWithPlace->tweet_id = '123456789';
|
$noteWithPlace->tweet_id = '123456789';
|
||||||
$place = App\Place::find(1);
|
$place = Place::find(1);
|
||||||
$noteWithPlace->place()->associate($place);
|
$noteWithPlace->place()->associate($place);
|
||||||
$noteWithPlace->save();
|
$noteWithPlace->save();
|
||||||
sleep(1);
|
sleep(1);
|
||||||
$noteWithContact = App\Note::create([
|
$noteWithContact = Note::create([
|
||||||
'note' => 'Hi @tantek'
|
'note' => 'Hi @tantek'
|
||||||
]);
|
]);
|
||||||
sleep(1);
|
sleep(1);
|
||||||
$noteWithContactPlusPic = App\Note::create([
|
$noteWithContactPlusPic = Note::create([
|
||||||
'note' => 'Hi @aaron',
|
'note' => 'Hi @aaron',
|
||||||
'client_id' => 'https://jbl5.dev/notes/new'
|
'client_id' => 'https://jbl5.dev/notes/new'
|
||||||
]);
|
]);
|
||||||
sleep(1);
|
sleep(1);
|
||||||
$noteWithoutContact = App\Note::create([
|
$noteWithoutContact = Note::create([
|
||||||
'note' => 'Hi @bob',
|
'note' => 'Hi @bob',
|
||||||
'client_id' => 'https://quill.p3k.io'
|
'client_id' => 'https://quill.p3k.io'
|
||||||
]);
|
]);
|
||||||
|
@ -41,13 +47,31 @@ class NotesTableSeeder extends Seeder
|
||||||
mkdir(public_path() . '/assets/profile-images/aaronparecki.com', 0755);
|
mkdir(public_path() . '/assets/profile-images/aaronparecki.com', 0755);
|
||||||
copy(base_path() . '/tests/aaron.png', public_path() . '/assets/profile-images/aaronparecki.com/image');
|
copy(base_path() . '/tests/aaron.png', public_path() . '/assets/profile-images/aaronparecki.com/image');
|
||||||
}
|
}
|
||||||
$noteWithCoords = App\Note::create([
|
$noteWithCoords = Note::create([
|
||||||
'note' => 'Note from somehwere',
|
'note' => 'Note from a town',
|
||||||
]);
|
]);
|
||||||
$noteWithCoords->location = '53.499,-2.379';
|
$noteWithCoords->location = '53.499,-2.379';
|
||||||
$noteWithCoords->save();
|
$noteWithCoords->save();
|
||||||
sleep(1);
|
sleep(1);
|
||||||
$noteSyndicated = App\Note::create([
|
$noteWithCoords2 = Note::create([
|
||||||
|
'note' => 'Note from a city',
|
||||||
|
]);
|
||||||
|
$noteWithCoords2->location = '53.9026894,-2.42250444118781';
|
||||||
|
$noteWithCoords2->save();
|
||||||
|
sleep(1);
|
||||||
|
$noteWithCoords3 = Note::create([
|
||||||
|
'note' => 'Note from a county',
|
||||||
|
]);
|
||||||
|
$noteWithCoords3->location = '57.5066357,-5.0038367';
|
||||||
|
$noteWithCoords3->save();
|
||||||
|
sleep(1);
|
||||||
|
$noteWithCoords4 = Note::create([
|
||||||
|
'note' => 'Note from a country',
|
||||||
|
]);
|
||||||
|
$noteWithCoords4->location = '63.000147,-136.002502';
|
||||||
|
$noteWithCoords4->save();
|
||||||
|
sleep(1);
|
||||||
|
$noteSyndicated = Note::create([
|
||||||
'note' => 'This note has all the syndication targets',
|
'note' => 'This note has all the syndication targets',
|
||||||
]);
|
]);
|
||||||
$noteSyndicated->tweet_id = '123456';
|
$noteSyndicated->tweet_id = '123456';
|
||||||
|
@ -56,8 +80,29 @@ class NotesTableSeeder extends Seeder
|
||||||
$noteSyndicated->instagram_url = 'https://www.instagram.com/p/aWsEd123Jh';
|
$noteSyndicated->instagram_url = 'https://www.instagram.com/p/aWsEd123Jh';
|
||||||
$noteSyndicated->save();
|
$noteSyndicated->save();
|
||||||
sleep(1);
|
sleep(1);
|
||||||
$noteWithTextLinkandEmoji = App\Note::create([
|
$noteWithTextLinkandEmoji = Note::create([
|
||||||
'note' => 'I love https://duckduckgo.com 💕' // theres a two-heart emoji at the end of this
|
'note' => 'I love https://duckduckgo.com 💕' // theres a two-heart emoji at the end of this
|
||||||
]);
|
]);
|
||||||
|
sleep(1);
|
||||||
|
$media = new Media();
|
||||||
|
$media->path = 'media/f1bc8faa-1a8f-45b8-a9b1-57282fa73f87.jpg';
|
||||||
|
$media->type = 'image';
|
||||||
|
$media->image_widths = '3648';
|
||||||
|
$media->save();
|
||||||
|
$noteWithImage = Note::create([
|
||||||
|
'note' => 'A lovely waterfall',
|
||||||
|
]);
|
||||||
|
$noteWithImage->media()->save($media);
|
||||||
|
sleep(1);
|
||||||
|
$noteFromInstagram = Note::create([
|
||||||
|
'note' => 'Lovely #wedding #weddingfavour',
|
||||||
|
]);
|
||||||
|
$noteFromInstagram->instagram_url = 'https://www.instagram.com/p/Bbo22MHhE_0';
|
||||||
|
$noteFromInstagram->save();
|
||||||
|
$mediaInstagram = new Media();
|
||||||
|
$mediaInstagram->path = 'https://scontent-lhr3-1.cdninstagram.com/t51.2885-15/e35/23734479_149605352435937_400133507076063232_n.jpg';
|
||||||
|
$mediaInstagram->type = 'image';
|
||||||
|
$mediaInstagram->save();
|
||||||
|
$noteFromInstagram->media()->save($mediaInstagram);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Place;
|
use App\Models\Place;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
use Phaza\LaravelPostgis\Geometries\Point;
|
use Phaza\LaravelPostgis\Geometries\Point;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\WebMention;
|
use App\Models\WebMention;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
class WebMentionsTableSeeder extends Seeder
|
class WebMentionsTableSeeder extends Seeder
|
||||||
|
@ -12,13 +12,21 @@ class WebMentionsTableSeeder extends Seeder
|
||||||
*/
|
*/
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
$webmention = WebMention::create([
|
$webmentionAaron = WebMention::create([
|
||||||
'source' => 'https://aaornpk.localhost/reply/1',
|
'source' => 'https://aaronpk.localhost/reply/1',
|
||||||
'target' => 'https://jonnybarnes.localhost/notes/D',
|
'target' => config('app.url') . '/notes/E',
|
||||||
'commentable_id' => '13',
|
'commentable_id' => '14',
|
||||||
'commentable_type' => 'App\Note',
|
'commentable_type' => 'App\Models\Note',
|
||||||
'type' => 'in-reply-to',
|
'type' => 'in-reply-to',
|
||||||
'mf2' => '{"rels": [], "items": [{"type": ["h-entry"], "properties": {"url": ["https://aaronpk.localhost/reply/1"], "name": ["Hi too"], "author": [{"type": ["h-card"], "value": "Aaron Parecki", "properties": {"url": ["https://aaronpk.localhost"], "name": ["Aaron Parecki"], "photo": ["https://aaronparecki.com/images/profile.jpg"]}}], "content": [{"html": "Hi too", "value": "Hi too"}], "published": ["' . date(DATE_W3C) . '"], "in-reply-to": ["https://aaronpk.loclahost/reply/1", "https://jonnybarnes.uk/notes/D"]}}]}'
|
'mf2' => '{"rels": [], "items": [{"type": ["h-entry"], "properties": {"url": ["https://aaronpk.localhost/reply/1"], "name": ["Hi too"], "author": [{"type": ["h-card"], "value": "Aaron Parecki", "properties": {"url": ["https://aaronpk.localhost"], "name": ["Aaron Parecki"], "photo": ["https://aaronparecki.com/images/profile.jpg"]}}], "content": [{"html": "Hi too", "value": "Hi too"}], "published": ["' . date(DATE_W3C) . '"], "in-reply-to": ["https://aaronpk.loclahost/reply/1", "' . config('app.url') .'/notes/E"]}}]}'
|
||||||
|
]);
|
||||||
|
$webmentionTantek = WebMention::create([
|
||||||
|
'source' => 'http://tantek.com/',
|
||||||
|
'target' => config('app.url') . '/notes/D',
|
||||||
|
'commentable_id' => '13',
|
||||||
|
'commentable_type' => 'App\Models\Note',
|
||||||
|
'type' => 'in-reply-to',
|
||||||
|
'mf2' => '{"rels": [], "items": [{"type": ["h-entry"], "properties": {"url": ["http://tantek.com/"], "name": ["KUTGW"], "author": [{"type": ["h-card"], "value": "Tantek Celik", "properties": {"url": ["http://tantek.com/"], "name": ["Tantek Celik"]}}], "content": [{"html": "kutgw", "value": "kutgw"}], "published": ["' . date(DATE_W3C) . '"], "in-reply-to": ["' . config('app.url') . '/notes/D"]}}]}'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1471
package-lock.json
generated
1471
package-lock.json
generated
File diff suppressed because it is too large
Load diff
35
package.json
35
package.json
|
@ -6,13 +6,13 @@
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"alertify.js": "^1.0.12",
|
"alertify.js": "^1.0.12",
|
||||||
"mapbox-gl": "^0.42.0",
|
"mapbox-gl": "^0.42.2",
|
||||||
"marked": "^0.3.6",
|
"marked": "^0.3.7",
|
||||||
"normalize.css": "^7.0.0"
|
"normalize.css": "^7.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ajv": "^5.3.0",
|
"ajv": "^5.5.2",
|
||||||
"autoprefixer": "^7.1.6",
|
"autoprefixer": "^7.2.3",
|
||||||
"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",
|
||||||
|
@ -21,22 +21,23 @@
|
||||||
"babel-preset-latest": "^6.16.0",
|
"babel-preset-latest": "^6.16.0",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"dotenv-webpack": "^1.5.4",
|
"dotenv-webpack": "^1.5.4",
|
||||||
"eslint": "^4.11.0",
|
"eslint": "^4.13.1",
|
||||||
"eslint-config-standard": "^10.2.1",
|
"eslint-config-standard": "^10.2.1",
|
||||||
"eslint-plugin-import": "^2.8.0",
|
"eslint-plugin-import": "^2.8.0",
|
||||||
"eslint-plugin-node": "^5.2.1",
|
"eslint-plugin-node": "^5.2.1",
|
||||||
"eslint-plugin-promise": "^3.6.0",
|
"eslint-plugin-promise": "^3.6.0",
|
||||||
"eslint-plugin-standard": "^3.0.1",
|
"eslint-plugin-standard": "^3.0.1",
|
||||||
"husky": "^0.14.3",
|
"husky": "^0.15.0-beta.16",
|
||||||
"lint-staged": "^5.0.0",
|
"lint-staged": "^6.0.0",
|
||||||
"postcss-cli": "^4.1.1",
|
"postcss-cli": "^4.1.1",
|
||||||
|
"postcss-sass": "^0.2.0",
|
||||||
"pre-commit": "^1.1.3",
|
"pre-commit": "^1.1.3",
|
||||||
"source-list-map": "^2.0.0",
|
"source-list-map": "^2.0.0",
|
||||||
"stylelint": "^8.2.0",
|
"stylelint": "^8.4.0",
|
||||||
"stylelint-config-standard": "^17.0.0",
|
"stylelint-config-standard": "^18.0.0",
|
||||||
"uglify-js": "^3.1.9",
|
"uglify-js": "^3.2.2",
|
||||||
"webpack": "^3.8.1",
|
"webpack": "^3.10.0",
|
||||||
"webpack-sources": "^1.0.2"
|
"webpack-sources": "^1.1.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"compress": "scripts/compress",
|
"compress": "scripts/compress",
|
||||||
|
@ -47,17 +48,21 @@
|
||||||
"make:css": "npm run lint:sass && npm run sass && npm run postcss",
|
"make:css": "npm run lint:sass && npm run sass && npm run postcss",
|
||||||
"make:js": "npm run lint:es6 && npm run webpack && npm run uglifyjs",
|
"make:js": "npm run lint:es6 && npm run webpack && npm run uglifyjs",
|
||||||
"postcss": "postcss public/assets/css/app.css --use autoprefixer --autoprefixer.browsers \"> 5%\" --replace --map",
|
"postcss": "postcss public/assets/css/app.css --use autoprefixer --autoprefixer.browsers \"> 5%\" --replace --map",
|
||||||
"precommit": "lint-staged",
|
|
||||||
"sass": "sassc --style compressed --sourcemap resources/assets/sass/app.scss public/assets/css/app.css",
|
"sass": "sassc --style compressed --sourcemap resources/assets/sass/app.scss public/assets/css/app.css",
|
||||||
"uglifyjs": "scripts/uglifyjs",
|
"uglifyjs": "scripts/uglifyjs",
|
||||||
"webpack": "webpack --progress --colors"
|
"webpack": "webpack --progress --colors"
|
||||||
},
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"resources/assets/es6/*.js": [
|
"./resources/assets/es6/*.js": [
|
||||||
"eslint --fix",
|
"eslint --fix",
|
||||||
"git add"
|
"git add"
|
||||||
],
|
],
|
||||||
"resources/assets/sass/**/*.scss": [
|
"*.scss": [
|
||||||
"stylelint --syntax=scss --fix",
|
"stylelint --syntax=scss --fix",
|
||||||
"git add"
|
"git add"
|
||||||
]
|
]
|
||||||
|
|
8
phpcs.xml
Normal file
8
phpcs.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<ruleset name="jonnybarnes.uk">
|
||||||
|
<description>Custom configuration for code running jonnybarnes.uk</description>
|
||||||
|
<file>./app/</file>
|
||||||
|
<rule ref="PSR2">
|
||||||
|
<exclude name="PSR2.Namespaces.UseDeclaration" />
|
||||||
|
</rule>
|
||||||
|
</ruleset>
|
|
@ -7,7 +7,8 @@
|
||||||
convertNoticesToExceptions="true"
|
convertNoticesToExceptions="true"
|
||||||
convertWarningsToExceptions="true"
|
convertWarningsToExceptions="true"
|
||||||
processIsolation="false"
|
processIsolation="false"
|
||||||
stopOnFailure="false">
|
stopOnFailure="false"
|
||||||
|
printerClass="Codedungeon\PHPUnitPrettyResultPrinter\Printer">
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="Feature">
|
<testsuite name="Feature">
|
||||||
<directory suffix="Test.php">./tests/Feature</directory>
|
<directory suffix="Test.php">./tests/Feature</directory>
|
||||||
|
|
2
public/assets/css/app.css
vendored
2
public/assets/css/app.css
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
|
@ -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/_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,yBAA0B,CAC7B,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,gBAAiB,CACpB,SAGG,cAAe,CAClB,WAIG,gBAAiB,CACpB,aCfG,iBAAkB,CACrB,qBAGG,iBAAkB,CACrB,2BAGG,WAAY,CACf,8BAGG,oBAAa,AAAb,oBAAa,AAAb,YAAa,CAChB,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,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,qDCVG,YAAa,CAChB,sDCCG,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,CC/CL,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,aCZG,oBAAqB,CACxB,KAGG,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/_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,gBAAiB,CACpB,SAGG,cAAe,CAClB,WAIG,gBAAiB,CACpB,aCfG,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,qDCVG,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"}
|
1
public/assets/frontend/mapbox-gl.css
vendored
1
public/assets/frontend/mapbox-gl.css
vendored
|
@ -223,6 +223,7 @@ a.mapboxgl-ctrl-logo {
|
||||||
border-color: #333;
|
border-color: #333;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
color: #333;
|
color: #333;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-popup {
|
.mapboxgl-popup {
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1 +1 @@
|
||||||
{"version":3,"sources":["webpack:///webpack/bootstrap b6efe62e7997f66fb20e","webpack:///colours.js"],"names":["__webpack_require__","moduleId","installedModules","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","css","document","querySelector","getAttribute","split","pop","getElementById","value","form","childNodes","addEventListener","e","preventDefault","newCss","link","parts","push","setAttribute","join","formData","FormData","fetch","method","credentials","body","catch","error","console","warn"],"mappings":"mBAIA,SAAAA,oBAAAC,UAGA,GAAAC,iBAAAD,UACA,OAAAC,iBAAAD,UAAAE,QAGA,IAAAC,OAAAF,iBAAAD,WACAI,EAAAJ,SACAK,GAAA,EACAH,YAUA,OANAI,QAAAN,UAAAO,KAAAJ,OAAAD,QAAAC,OAAAA,OAAAD,QAAAH,qBAGAI,OAAAE,GAAA,EAGAF,OAAAD,QAvBA,IAAAD,oBA4BAF,oBAAAS,EAAAF,QAGAP,oBAAAU,EAAAR,iBAGAF,oBAAAW,EAAA,SAAAR,QAAAS,KAAAC,QACAb,oBAAAc,EAAAX,QAAAS,OACAG,OAAAC,eAAAb,QAAAS,MACAK,cAAA,EACAC,YAAA,EACAC,IAAAN,UAMAb,oBAAAoB,EAAA,SAAAhB,QACA,IAAAS,OAAAT,QAAAA,OAAAiB,WACA,WAA2B,OAAAjB,OAAA,SAC3B,WAAiC,OAAAA,QAEjC,OADAJ,oBAAAW,EAAAE,OAAA,IAAAA,QACAA,QAIAb,oBAAAc,EAAA,SAAAQ,OAAAC,UAAsD,OAAAR,OAAAS,UAAAC,eAAAjB,KAAAc,OAAAC,WAGtDvB,oBAAA0B,EAAA,GAGA1B,oBAAAA,oBAAA2B,EAAA,iEC3DA,IAEIC,IAFOC,SAASC,cAAc,iBAEnBC,aAAa,QAAQC,MAAM,KAAKC,MAG/CJ,SAASK,eAAe,sBAAsBC,MAAQP,IAGtD,IAAIQ,KAAOP,SAASK,eAAe,oBACzBE,KAAKC,WAAW,GACtBC,iBAAiB,QAAS,SAAUC,GACpCA,EAAEC,iBACF,IAAIC,OAASZ,SAASK,eAAe,sBAAsBC,MACvDO,KAAOb,SAASC,cAAc,iBAE9Ba,MADMD,KAAKX,aAAa,QACZC,MAAM,KACtBW,MAAMV,MACNU,MAAMC,KAAKH,QACXC,KAAKG,aAAa,OAAQF,MAAMG,KAAK,MACrC,IAAIC,SAAW,IAAIC,SAASZ,MAC5Ba,MAAM,yBACFC,OAAQ,OACRC,YAAa,cACbC,KAAML,WACPM,MAAM,SAAUC,OACfC,QAAQC,KAAKF","file":"public/assets/js/colours.js.map","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 4);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap b6efe62e7997f66fb20e","//colours.js\n\nlet link = document.querySelector('#colourScheme');\n\nlet css = link.getAttribute('href').split('/').pop();\n\n// update selected item in colour scheme list\ndocument.getElementById('colourSchemeSelect').value = css;\n\n// fix form\nlet form = document.getElementById('colourSchemeForm');\nlet btn = form.childNodes[5];\nbtn.addEventListener('click', function (e) {\n e.preventDefault();\n let newCss = document.getElementById('colourSchemeSelect').value;\n let link = document.querySelector('#colourScheme');\n let css = link.getAttribute('href');\n let parts = css.split('/');\n parts.pop();\n parts.push(newCss);\n link.setAttribute('href', parts.join('/'));\n let formData = new FormData(form);\n fetch('/update-colour-scheme', {\n method: 'POST',\n credentials: 'same-origin',\n body: formData\n }).catch(function (error) {\n console.warn(error);\n });\n});\n\n\n\n// WEBPACK FOOTER //\n// ./colours.js"]}
|
{"version":3,"sources":["webpack:///webpack/bootstrap b56e9accee14dcede691","webpack:///colours.js"],"names":["__webpack_require__","moduleId","installedModules","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","css","document","querySelector","getAttribute","split","pop","getElementById","value","form","childNodes","addEventListener","e","preventDefault","newCss","link","parts","push","setAttribute","join","formData","FormData","fetch","method","credentials","body","catch","error","console","warn"],"mappings":"mBAIA,SAAAA,oBAAAC,UAGA,GAAAC,iBAAAD,UACA,OAAAC,iBAAAD,UAAAE,QAGA,IAAAC,OAAAF,iBAAAD,WACAI,EAAAJ,SACAK,GAAA,EACAH,YAUA,OANAI,QAAAN,UAAAO,KAAAJ,OAAAD,QAAAC,OAAAA,OAAAD,QAAAH,qBAGAI,OAAAE,GAAA,EAGAF,OAAAD,QAvBA,IAAAD,oBA4BAF,oBAAAS,EAAAF,QAGAP,oBAAAU,EAAAR,iBAGAF,oBAAAW,EAAA,SAAAR,QAAAS,KAAAC,QACAb,oBAAAc,EAAAX,QAAAS,OACAG,OAAAC,eAAAb,QAAAS,MACAK,cAAA,EACAC,YAAA,EACAC,IAAAN,UAMAb,oBAAAoB,EAAA,SAAAhB,QACA,IAAAS,OAAAT,QAAAA,OAAAiB,WACA,WAA2B,OAAAjB,OAAA,SAC3B,WAAiC,OAAAA,QAEjC,OADAJ,oBAAAW,EAAAE,OAAA,IAAAA,QACAA,QAIAb,oBAAAc,EAAA,SAAAQ,OAAAC,UAAsD,OAAAR,OAAAS,UAAAC,eAAAjB,KAAAc,OAAAC,WAGtDvB,oBAAA0B,EAAA,GAGA1B,oBAAAA,oBAAA2B,EAAA,iEC3DA,IAEIC,IAFOC,SAASC,cAAc,iBAEnBC,aAAa,QAAQC,MAAM,KAAKC,MAG/CJ,SAASK,eAAe,sBAAsBC,MAAQP,IAGtD,IAAIQ,KAAOP,SAASK,eAAe,oBACzBE,KAAKC,WAAW,GACtBC,iBAAiB,QAAS,SAAUC,GACpCA,EAAEC,iBACF,IAAIC,OAASZ,SAASK,eAAe,sBAAsBC,MACvDO,KAAOb,SAASC,cAAc,iBAE9Ba,MADMD,KAAKX,aAAa,QACZC,MAAM,KACtBW,MAAMV,MACNU,MAAMC,KAAKH,QACXC,KAAKG,aAAa,OAAQF,MAAMG,KAAK,MACrC,IAAIC,SAAW,IAAIC,SAASZ,MAC5Ba,MAAM,yBACFC,OAAQ,OACRC,YAAa,cACbC,KAAML,WACPM,MAAM,SAAUC,OACfC,QAAQC,KAAKF","file":"public/assets/js/colours.js.map","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 4);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap b56e9accee14dcede691","//colours.js\n\nlet link = document.querySelector('#colourScheme');\n\nlet css = link.getAttribute('href').split('/').pop();\n\n// update selected item in colour scheme list\ndocument.getElementById('colourSchemeSelect').value = css;\n\n// fix form\nlet form = document.getElementById('colourSchemeForm');\nlet btn = form.childNodes[5];\nbtn.addEventListener('click', function (e) {\n e.preventDefault();\n let newCss = document.getElementById('colourSchemeSelect').value;\n let link = document.querySelector('#colourScheme');\n let css = link.getAttribute('href');\n let parts = css.split('/');\n parts.pop();\n parts.push(newCss);\n link.setAttribute('href', parts.join('/'));\n let formData = new FormData(form);\n fetch('/update-colour-scheme', {\n method: 'POST',\n credentials: 'same-origin',\n body: formData\n }).catch(function (error) {\n console.warn(error);\n });\n});\n\n\n\n// WEBPACK FOOTER //\n// ./colours.js"]}
|
File diff suppressed because one or more lines are too long
2
public/assets/js/maps.js
vendored
2
public/assets/js/maps.js
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue