Merge pull request #215 from jonnybarnes/develop

MTM: Get main branch up to date
This commit is contained in:
Jonny Barnes 2020-11-08 11:14:29 +00:00 • committed by GitHub
commit 650d54292e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
551 changed files with 20605 additions and 21912 deletions

View file

@ -56,9 +56,13 @@ PIWIK=false
PIWIK_ID=1 PIWIK_ID=1
PIWIK_URL=https://analytics.jmb.lv/piwik.php PIWIK_URL=https://analytics.jmb.lv/piwik.php
FATHOM_ID=
APP_TIMEZONE=UTC APP_TIMEZONE=UTC
APP_LANG=en APP_LANG=en
APP_LOG=daily APP_LOG=daily
SECURE_SESSION_COOKIE=true SECURE_SESSION_COOKIE=true
SLACK_WEBHOOK_URL= LOG_SLACK_WEBHOOK_URL=
FONT_LINK=

66
.env.github Normal file
View file

@ -0,0 +1,66 @@
APP_NAME=Laravel
APP_ENV=testing
APP_KEY=SomeRandomString # Leave this
APP_DEBUG=false
APP_LOG_LEVEL=warning
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=jbuktest
DB_USERNAME=postgres
DB_PASSWORD=postgres
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
AWS_S3_KEY=your-key
AWS_S3_SECRET=your-secret
AWS_S3_REGION=region
AWS_S3_BUCKET=your-bucket
AWS_S3_URL=https://xxxxxxx.s3-region.amazonaws.com
APP_URL=https://example.com # This one is necessary
APP_LONGURL=example.com
APP_SHORTURL=examp.le
ADMIN_USER=admin # pick something better, this is used for `/admin`
ADMIN_PASS=password
DISPLAY_NAME="Joe Bloggs" # This is used for example in the header and titles
TWITTER_CONSUMER_KEY=
TWITTER_CONSUMER_SECRET=
TWITTER_ACCESS_TOKEN=
TWITTER_ACCESS_TOKEN_SECRET=
SCOUT_DRIVER=pgsql
PIWIK=false
FATHOM_ID=
APP_TIMEZONE=UTC
APP_LANG=en
APP_LOG=daily
SECURE_SESSION_COOKIE=true
LOG_SLACK_WEBHOOK_URL=
FONT_LINK=

View file

@ -1,7 +1,7 @@
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://jonnybarnes.localhost:8000 APP_URL=http://jonnybarnes.localhost
APP_LONGURL=jonnybarnes.localhost APP_LONGURL=jonnybarnes.localhost
APP_SHORTURL=jmb.localhost APP_SHORTURL=jmb.localhost

56
.github/workflows/run-tests.yml vendored Normal file
View file

@ -0,0 +1,56 @@
name: Run Tests
on:
pull_request:
jobs:
phpunit:
runs-on: ubuntu-20.04
name: PHPUnit test suite
services:
postgres:
image: postgres:12
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: jbuktest
ports:
- 5432:5432
steps:
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-${{ hashFiles('**/package.json') }}
- name: Install npm dependencies
run: npm install
- name: Install ImageMagick
run: sudo apt install imagemagick
- name: Setup PHP with pecl extension
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
tools: pecl, phpcs
extensions: imagick
- name: Copy .env
run: php -r "file_exists('.env') || copy('.env.github', '.env');"
- name: Install dependencies
run: composer install -q --no-ansi --no-interaction --no-progress
- name: Generate key
run: php artisan key:generate
- name: Setup directory permissions
run: chmod -R 777 storage bootstrap/cache
- name: Setup test database
run: |
php artisan migrate
php artisan db:seed
- name: Execute tests (Unit and Feature tests) via PHPUnit
run: vendor/bin/phpunit
- name: Run phpcs
run: phpcs
- name: Check for security vulnerabilities
run: php vendor/bin/security-checker security:check

8
.gitignore vendored
View file

@ -1,6 +1,4 @@
/node_modules /node_modules
/public/hot
/public/storage
/storage/*.key /storage/*.key
/vendor /vendor
.env .env
@ -10,7 +8,13 @@ Homestead.yaml
npm-debug.log npm-debug.log
yarn-error.log yarn-error.log
/.idea /.idea
/lsp
.phpstorm.meta.php
_ide_helper.php
# Custom paths in /public # Custom paths in /public
/public/coverage /public/coverage
/public/hot
/public/storage
/public/fonts
/public/files /public/files
/public/keybase.txt /public/keybase.txt

View file

@ -2,7 +2,6 @@ preset: laravel
disabled: disabled:
- concat_without_spaces - concat_without_spaces
- simplified_null_return
- single_import_per_statement - single_import_per_statement
finder: finder:

View file

@ -1,5 +1,5 @@
{ {
"extends": "stylelint-config-standard", "extends": ["stylelint-config-standard", "stylelint-a11y/recommended"],
"rules": { "rules": {
"indentation": 4 "indentation": 4
} }

View file

@ -1,70 +0,0 @@
language: php
sudo: false
dist: trusty
cache:
- apt
addons:
hosts:
- jmb.localhost
- jonnybarnes.localhost
postgresql: "9.6"
apt:
packages:
- nginx-full
- realpath
- postgresql-9.6-postgis-2.3
- imagemagick
#- google-chrome-stable
artifacts:
s3_region: "eu-west-1"
paths:
- $(ls tests/Browser/screenshots/*.png | tr "\n" ":")
- $(ls tests/Browser/console/*.log | tr "\n" ":")
- $(ls storage/logs/*.log | tr "\n" ":")
- $(ls /tmp/*.log | tr "\n" ":")
services:
- postgresql
env:
global:
- setup=basic
php:
- 7.2
- 7.3
before_install:
- printf "\n" | pecl install imagick
- cp .env.travis .env
- echo 'error_log = "/tmp/php.error.log"' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- psql -U travis -c 'create database travis_ci_test'
- psql -U travis -d travis_ci_test -c 'create extension postgis'
- travis_retry composer self-update --preview
- curl -OL https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar
install:
- if [[ $setup = 'basic' ]]; then travis_retry composer install --no-interaction --prefer-dist; fi
- if [[ $setup = 'stable' ]]; then travis_retry composer update --no-interaction --prefer-dist --prefer-stable; fi
- if [[ $setup = 'lowest' ]]; then travis_retry composer update --no-interaction --prefer-dist --prefer-lowest --prefer-stable; fi
- travis/install-nginx.sh
- . $HOME/.nvm/nvm.sh
- nvm install stable
- nvm use stable
- npm i puppeteer
before_script:
- php artisan key:generate
- php artisan migrate
- php artisan db:seed
#- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9515 http://localhost:8000 &
#- sleep 5
script:
- php vendor/bin/phpunit
- php phpcs.phar
#- php artisan dusk
- php vendor/bin/security-checker security:check

View file

@ -0,0 +1,84 @@
<?php
namespace App\Console\Commands;
use App\Models\Place;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class MigratePlaceDataFromPostgis extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'places:migratefrompostgis';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Copy Postgis data to normal latitude longitude fields';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$locationColumn = DB::selectOne(DB::raw("
SELECT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'places'
AND column_name = 'location'
)
"));
if (! $locationColumn->exists) {
$this->info('There is no Postgis location data in the table. Exiting.');
return 0;
}
$latitudeColumn = DB::selectOne(DB::raw("
SELECT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'places'
AND column_name = 'latitude'
)
"));
if (! $latitudeColumn->exists) {
$this->error('Latitude and longitude columns have not been created yet');
return 1;
}
$places = Place::all();
$places->each(function ($place) {
$this->info('Extracting Postgis data for place: ' . $place->name);
$place->latitude = $place->location->getLat();
$place->longitude = $place->location->getLng();
$place->save();
});
return 0;
}
}

View file

@ -4,9 +4,9 @@ declare(strict_types=1);
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Jobs\DownloadWebMention;
use App\Models\WebMention; use App\Models\WebMention;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use App\Jobs\DownloadWebMention;
class ReDownloadWebMentions extends Command class ReDownloadWebMentions extends Command
{ {

View file

@ -27,6 +27,7 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule) protected function schedule(Schedule $schedule)
{ {
$schedule->command('horizon:snapshot')->everyFiveMinutes(); $schedule->command('horizon:snapshot')->everyFiveMinutes();
$schedule->command('telescope:prune --hours=48')->daily();
} }
/** /**

View file

@ -2,11 +2,16 @@
namespace App\Exceptions; namespace App\Exceptions;
use App;
use Exception; use Exception;
use Illuminate\Support\Facades\Route; use GuzzleHttp\Client;
use Illuminate\Session\TokenMismatchException; use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Session\TokenMismatchException;
use Illuminate\Support\Facades\Route;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Throwable;
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
@ -19,7 +24,8 @@ class Handler extends ExceptionHandler
* @var array * @var array
*/ */
protected $dontReport = [ protected $dontReport = [
\Symfony\Component\HttpKernel\Exception\HttpException::class, NotFoundHttpException::class,
ModelNotFoundException::class,
]; ];
/** /**
@ -37,53 +43,61 @@ class Handler extends ExceptionHandler
* *
* This is a great spot to send exceptions to Sentry, Bugsnag, etc. * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
* *
* @param \Exception $exception * @param Throwable $throwable
* @return void * @return void
* @throws Exception
* @throws Throwable
*/ */
public function report(Exception $exception) public function report(Throwable $throwable)
{ {
$guzzle = new \GuzzleHttp\Client([ parent::report($throwable);
if ($this->shouldReport($throwable)) {
$guzzle = new Client([
'headers' => [ 'headers' => [
'Content-Type' => 'application/json', 'Content-Type' => 'application/json',
], ],
]); ]);
$exceptionName = get_class($throwable) ?? 'Unknown Exception';
$title = $exceptionName . ': ' . $throwable->getMessage();
$guzzle->post( $guzzle->post(
env('SLACK_WEBHOOK_URL'), config('logging.slack'),
[ [
'body' => json_encode([ 'body' => json_encode([
'attachments' => [[ 'attachments' => [[
'fallback' => 'There was an exception.', 'fallback' => 'There was an exception.',
'pretext' => 'There was an exception.', 'pretext' => 'There was an exception.',
'color' => '#d00000', 'color' => '#d00000',
'author_name' => App::environment(), 'author_name' => app()->environment(),
'author_link' => config('app.url'), 'author_link' => config('app.url'),
'fields' => [[ 'fields' => [[
'title' => get_class($exception) ?? 'Unkown Exception', 'title' => $title,
'value' => $exception->getMessage() ?? '', 'value' => request()->method() . ' ' . request()->fullUrl(),
]], ]],
'ts' => time(), 'ts' => time(),
]], ]],
]), ]),
] ]
); );
}
parent::report($exception);
} }
/** /**
* Render an exception into an HTTP response. * Render an exception into an HTTP response.
* *
* @param \Illuminate\Http\Request $request * @param Request $request
* @param \Exception $exception * @param Throwable $throwable
* @return \Illuminate\Http\Response * @return Response
* @throws Throwable
*/ */
public function render($request, Exception $exception) public function render($request, Throwable $throwable)
{ {
if ($exception instanceof TokenMismatchException) { if ($throwable instanceof TokenMismatchException) {
Route::getRoutes()->match($request); Route::getRoutes()->match($request);
} }
return parent::render($request, $exception); return parent::render($request, $throwable);
} }
} }

View file

@ -4,11 +4,11 @@ declare(strict_types=1);
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use App\Models\Article;
use Illuminate\View\View;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Article;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class ArticlesController extends Controller class ArticlesController extends Controller
{ {

View file

@ -4,11 +4,11 @@ declare(strict_types=1);
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use Illuminate\View\View;
use Illuminate\Http\Request;
use App\Models\MicropubClient;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\MicropubClient;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class ClientsController extends Controller class ClientsController extends Controller
{ {

View file

@ -4,14 +4,14 @@ declare(strict_types=1);
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use GuzzleHttp\Client;
use App\Models\Contact;
use Illuminate\View\View;
use Illuminate\Support\Arr;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Contact;
use GuzzleHttp\Client;
use Illuminate\Filesystem\Filesystem; use Illuminate\Filesystem\Filesystem;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\View\View;
class ContactsController extends Controller class ContactsController extends Controller
{ {

View file

@ -4,8 +4,8 @@ declare(strict_types=1);
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use Illuminate\View\View;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\View\View;
class HomeController extends Controller class HomeController extends Controller
{ {

View file

@ -4,11 +4,11 @@ declare(strict_types=1);
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use App\Models\Like;
use App\Jobs\ProcessLike;
use Illuminate\View\View;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Jobs\ProcessLike;
use App\Models\Like;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class LikesController extends Controller class LikesController extends Controller
{ {

View file

@ -4,12 +4,12 @@ declare(strict_types=1);
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use App\Models\Note;
use Illuminate\View\View;
use Illuminate\Http\Request;
use App\Jobs\SendWebMentions;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Jobs\SendWebMentions;
use App\Models\Note;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class NotesController extends Controller class NotesController extends Controller
{ {

View file

@ -4,17 +4,15 @@ declare(strict_types=1);
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use App\Models\Place;
use Illuminate\View\View;
use Illuminate\Http\Request;
use App\Services\PlaceService;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Place;
use App\Services\PlaceService;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Phaza\LaravelPostgis\Geometries\Point; use Illuminate\View\View;
class PlacesController extends Controller class PlacesController extends Controller
{ {
protected $placeService; protected PlaceService $placeService;
public function __construct(PlaceService $placeService) public function __construct(PlaceService $placeService)
{ {
@ -24,7 +22,7 @@ class PlacesController extends Controller
/** /**
* List the places that can be edited. * List the places that can be edited.
* *
* @return \Illuminate\View\View * @return View
*/ */
public function index(): View public function index(): View
{ {
@ -36,7 +34,7 @@ class PlacesController extends Controller
/** /**
* Show the form to make a new place. * Show the form to make a new place.
* *
* @return \Illuminate\View\View * @return View
*/ */
public function create(): View public function create(): View
{ {
@ -46,12 +44,18 @@ class PlacesController extends Controller
/** /**
* Process a request to make a new place. * Process a request to make a new place.
* *
* @return \Illuminate\Http\RedirectResponse * @return RedirectResponse
*/ */
public function store(): RedirectResponse public function store(): RedirectResponse
{ {
$data = request()->only(['name', 'description', 'latitude', 'longitude']); $this->placeService->createPlace(
$place = $this->placeService->createPlace($data); request()->only([
'name',
'description',
'latitude',
'longitude',
])
);
return redirect('/admin/places'); return redirect('/admin/places');
} }
@ -60,7 +64,7 @@ class PlacesController extends Controller
* Display the form to edit a specific place. * Display the form to edit a specific place.
* *
* @param int $placeId * @param int $placeId
* @return \Illuminate\View\View * @return View
*/ */
public function edit(int $placeId): View public function edit(int $placeId): View
{ {
@ -73,17 +77,15 @@ class PlacesController extends Controller
* Process a request to edit a place. * Process a request to edit a place.
* *
* @param int $placeId * @param int $placeId
* @return \Illuminate\Http\RedirectResponse * @return RedirectResponse
*/ */
public function update(int $placeId): RedirectResponse public function update(int $placeId): RedirectResponse
{ {
$place = Place::findOrFail($placeId); $place = Place::findOrFail($placeId);
$place->name = request()->input('name'); $place->name = request()->input('name');
$place->description = request()->input('description'); $place->description = request()->input('description');
$place->location = new Point( $place->latitude = request()->input('latitude');
(float) request()->input('latitude'), $place->longitude = request()->input('longitude');
(float) request()->input('longitude')
);
$place->icon = request()->input('icon'); $place->icon = request()->input('icon');
$place->save(); $place->save();
@ -94,12 +96,12 @@ class PlacesController extends Controller
* List the places we can merge with the current place. * List the places we can merge with the current place.
* *
* @param int $placeId * @param int $placeId
* @return \Illuminate\View\View * @return View
*/ */
public function mergeIndex(int $placeId): View public function mergeIndex(int $placeId): View
{ {
$first = Place::find($placeId); $first = Place::find($placeId);
$results = Place::near(new Point($first->latitude, $first->longitude))->get(); $results = Place::near((object) ['latitude' => $first->latitude, 'longitude' => $first->longitude])->get();
$places = []; $places = [];
foreach ($results as $place) { foreach ($results as $place) {
if ($place->slug !== $first->slug) { if ($place->slug !== $first->slug) {
@ -115,7 +117,7 @@ class PlacesController extends Controller
* *
* @param int $placeId1 * @param int $placeId1
* @param int $placeId2 * @param int $placeId2
* @return \Illuminate\View\View * @return View
*/ */
public function mergeEdit(int $placeId1, int $placeId2): View public function mergeEdit(int $placeId1, int $placeId2): View
{ {
@ -128,7 +130,7 @@ class PlacesController extends Controller
/** /**
* Process the request to merge two places. * Process the request to merge two places.
* *
* @return \Illuminate\Http\RedirectResponse * @return RedirectResponse
*/ */
public function mergeStore(): RedirectResponse public function mergeStore(): RedirectResponse
{ {

View file

@ -5,18 +5,19 @@ declare(strict_types=1);
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Article; use App\Models\Article;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View; use Illuminate\View\View;
use Jonnybarnes\IndieWeb\Numbers; use Jonnybarnes\IndieWeb\Numbers;
use Illuminate\Http\RedirectResponse;
class ArticlesController extends Controller class ArticlesController extends Controller
{ {
/** /**
* Show all articles (with pagination). * Show all articles (with pagination).
* *
* @param int $year * @param int|null $year
* @param int $month * @param int|null $month
* @return \Illuminate\View\View * @return View
*/ */
public function index(int $year = null, int $month = null): View public function index(int $year = null, int $month = null): View
{ {
@ -34,11 +35,16 @@ class ArticlesController extends Controller
* @param int $year * @param int $year
* @param int $month * @param int $month
* @param string $slug * @param string $slug
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View * @return RedirectResponse|View
*/ */
public function show(int $year, int $month, string $slug) public function show(int $year, int $month, string $slug)
{ {
try {
$article = Article::where('titleurl', $slug)->firstOrFail(); $article = Article::where('titleurl', $slug)->firstOrFail();
} catch (ModelNotFoundException $exception) {
abort(404);
}
if ($article->updated_at->year != $year || $article->updated_at->month != $month) { if ($article->updated_at->year != $year || $article->updated_at->month != $month) {
return redirect('/blog/' return redirect('/blog/'
. $article->updated_at->year . $article->updated_at->year
@ -54,12 +60,17 @@ class ArticlesController extends Controller
* and redirect to it. * and redirect to it.
* *
* @param int $idFromUrl * @param int $idFromUrl
* @return \Illuminte\Http\RedirectResponse * @return RedirectResponse
*/ */
public function onlyIdInUrl(int $idFromUrl): RedirectResponse public function onlyIdInUrl(int $idFromUrl): RedirectResponse
{ {
$realId = resolve(Numbers::class)->b60tonum($idFromUrl); $realId = resolve(Numbers::class)->b60tonum((string) $idFromUrl);
try {
$article = Article::findOrFail($realId); $article = Article::findOrFail($realId);
} catch (ModelNotFoundException $exception) {
abort(404);
}
return redirect($article->link); return redirect($article->link);
} }

Some files were not shown because too many files have changed in this diff Show more