jonnybarnes.uk/app/Http/Controllers/MicropubClientController.php
Jonny Barnes 3f15291d16 Update the app to Laravel 5.5
Squashed commit of the following:

commit 070f46bbacd91855730d467cc2806183441791ae
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Sep 4 18:06:04 2017 +0100

    Now we now how the laravel IoC conatiner works, no need to be newing class dependencies

commit 57eeacdef178532a681f774f8c6738950d40c964
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Sep 4 17:59:28 2017 +0100

    Get json test working again

commit 81c3cfc9b432241d8a4df7f1e0511a50ea4f9b90
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Sep 4 14:38:10 2017 +0100

    Can’t use RefreshDatabase yet

commit 4ba5ff724d50ca86b3fa90c7bb4e71ad9e4dad79
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Sep 4 14:10:33 2017 +0100

    Initial attempt at updating to Laravel 5.5
2017-09-04 19:34:39 +01:00

550 lines
19 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Http\Controllers;
use App\IndieWebUser;
use IndieAuth\Client as IndieClient;
use GuzzleHttp\Client as GuzzleClient;
use Illuminate\Http\{Request, Response};
use GuzzleHttp\Exception\{ClientException, ServerException};
class MicropubClientController extends Controller
{
/**
* Inject the dependencies.
*/
public function __construct(
IndieClient $indieClient,
GuzzleClient $guzzleClient
) {
$this->indieClient = $indieClient;
$this->guzzleClient = $guzzleClient;
}
/**
* Display the new notes form.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
*/
public function create(Request $request)
{
//initiate varaibles
$indiewebUser = null;
$syndication = null;
$mediaEndpoint = null;
$mediaURLs = null;
$url = $request->session()->get('me');
if ($url) {
$indiewebUser = IndieWebUser::where('me', $url)->first();
}
if ($indiewebUser) {
$syndication = $this->parseSyndicationTargets($indiewebUser->syndication);
$mediaEndpoint = $indiewebUser->mediaEndpoint ?? null;
$mediaURLs = $request->session()->get('media-links');
}
return view('micropub.create', compact('url', 'syndication', 'mediaEndpoint', 'mediaURLs'));
}
/**
* Process an upload to the media endpoint.
*
* @param Illuminate\Http\Request $request
* @return Illuminate\Http\Response
*/
public function processMedia(Request $request)
{
if ($request->hasFile('files') == false) {
return back()->with('error', 'No files uploaded');
}
$user = IndieWebUser::where('me', $request->session()->get('me'))->firstOrFail();
if ($user->mediaEndpoint == null || $user->token == null) {
return back()->with('error', 'No user token or known endpoint');
}
$mediaURLs = [];
foreach ($request->file('files') as $file) {
try {
$response = $this->guzzleClient->request('POST', $user->mediaEndpoint, [
'headers' => [
'Authorization' => 'Bearer ' . $user->token,
],
'multipart' => [
[
'name' => 'file',
'contents' => fopen($file->path(), 'r'),
'filename' => $file->getClientOriginalName(),
],
],
]);
} catch (ClientException | ServerException $e) {
continue;
}
$mediaURLs[] = $response->getHeader('Location')[0];
}
$storedMediaURLs = $request->session()->get('media-links') ?? [];
$mediaURLsToSave = array_merge($storedMediaURLs, $mediaURLs);
$request->session()->put('media-links', $mediaURLsToSave);
return redirect()->route('micropub-client');
}
public function clearLinks(Request $request)
{
$request->session()->forget('media-links');
return redirect(route('micropub-client'));
}
/**
* Post the notes content to the relavent micropub API endpoint.
*
* @todo make sure this works with multiple syndication targets
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function store(Request $request)
{
$url = normalize_url($request->session()->get('me'));
$user = IndieWebUser::where('me', $url)->firstOrFail();
if ($user->token == null) {
return redirect()->route('micropub-client')->with('error', 'You havent requested a token yet');
}
$micropubEndpoint = $this->indieClient->discoverMicropubEndpoint($url);
if (! $micropubEndpoint) {
return redirect()->route('micropub-client')->with('error', 'Unable to determine micropub API endpoint');
}
$headers = [
'Authorization' => 'Bearer ' . $user->token,
];
if ($user->syntax == 'html') {
$multipart = [
[
'name' => 'h',
'contents' => 'entry',
],
[
'name' => 'content',
'contents' => $request->input('content'),
],
];
if ($request->hasFile('photo')) {
$photos = $request->file('photo');
foreach ($photos as $photo) {
$multipart[] = [
'name' => 'photo[]',
'contents' => fopen($photo->path(), 'r'),
'filename' => $photo->getClientOriginalName(),
];
}
}
if ($request->input('in-reply-to') != '') {
$multipart[] = [
'name' => 'in-reply-to',
'contents' => $request->input('in-reply-to'),
];
}
if ($request->input('mp-syndicate-to')) {
foreach ($request->input('mp-syndicate-to') as $syn) {
$multipart[] = [
'name' => 'mp-syndicate-to[]',
'contents' => $syn,
];
}
}
if ($request->input('location')) {
if ($request->input('location') !== 'no-location') {
$multipart[] = [
'name' => 'location',
'contents' => $request->input('location'),
];
}
}
if ($request->input('media')) {
foreach ($request->input('media') as $media) {
$multipart[] = [
'name' => 'photo[]',
'contents' => $media,
];
}
}
try {
$response = $this->guzzleClient->post($micropubEndpoint, [
'multipart' => $multipart,
'headers' => $headers,
]);
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
return redirect()->route('micropub-client')->with(
'error',
'There was a bad response from the micropub endpoint.'
);
}
if ($response->getStatusCode() == 201) {
$request->session()->forget('media-links');
$location = $response->getHeader('Location');
if (is_array($location)) {
return redirect($location[0]);
}
return redirect($location);
}
}
if ($user->syntax == 'json') {
$json = [];
$json['type'] = ['h-entry'];
$json['properties'] = ['content' => [$request->input('content')]];
if ($request->input('in-reply-to') != '') {
$json['properties']['in-reply-to'][] = $request->input('in-reply-to');
}
if ($request->input('mp-syndicate-to')) {
foreach ($request->input('mp-syndicate-to') as $syn) {
$json['properties']['mp-syndicate-to'][] = $syn;
}
}
if ($request->input('location')) {
if ($request->input('location') !== 'no-location') {
$json['properties']['location'][] = $request->input('location');
}
}
if ($request->input('media')) {
$json['properties']['photo'] = [];
foreach ($request->input('media') as $media) {
$json['properties']['photo'][] = $media;
}
}
try {
$response = $this->guzzleClient->post($micropubEndpoint, [
'json' => $json,
'headers' => $headers,
]);
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
return redirect()->route('micropub-client')->with(
'error',
'There was a bad response from the micropub endpoint.'
);
}
if ($response->getStatusCode() == 201) {
$request->session()->forget('media-links');
$location = $response->getHeader('Location');
if (is_array($location)) {
return redirect($location[0]);
}
return redirect($location);
}
}
return redirect()->route('micropub-client')->with('error', 'Endpoint didnt create the note.');
}
/**
* Show currently stored configuration values.
*
* @param Illuminate\Http\Request $request
* @return view
*/
public function config(Request $request)
{
//default values
$data = [
'me' => '',
'token' => 'none',
'syndication' => 'none defined',
'media-endpoint' => 'none defined',
'syntax' => 'html',
];
if ($request->session()->has('me')) {
$data['me'] = normalize_url($request->session()->get('me'));
$user = IndieWebUser::where('me', $request->session()->get('me'))->firstOrFail();
$data['token'] = $user->token ?? 'none defined';
$data['syndication'] = $user->syndication ?? 'none defined';
$data['media-endpoint'] = $user->mediaEndpoint ?? 'none defined';
$data['syntax'] = $user->syntax;
}
return view('micropub.config', compact('data'));
}
/**
* Get a new token.
*
* @param Illuminate\Http\Request $request
* @return view
*/
public function getNewToken(Request $request)
{
if ($request->session()->has('me')) {
$url = normalize_url($request->session()->get('me'));
$authozationEndpoint = $this->indieClient->discoverAuthorizationEndpoint($url);
if ($authozationEndpoint) {
$state = bin2hex(random_bytes(16));
$request->session()->put('state', $state);
$authorizationURL = $this->indieClient->buildAuthorizationURL(
$authozationEndpoint,
$url,
route('micropub-client-get-new-token-callback'), // redirect_uri
route('micropub-client'), //client_id
$state,
'create update' // scope needs to be a setting
);
return redirect($authorizationURL);
}
return redirect()->route('micropub-config')->with('error', 'Unable to find authorisation endpoint');
}
return redirect()->route('micropub-config')->with('error', 'You arent logged in');
}
/**
* The callback for getting a token.
*/
public function getNewTokenCallback(Request $request)
{
if ($request->input('state') !== $request->session()->get('state')) {
return redirect()->route('micropub-config')->with('error', 'The <code>state</code> didnt match.');
}
$tokenEndpoint = $this->indieClient->discoverTokenEndpoint(normalize_url($request->input('me')));
if ($tokenEndpoint) {
$token = $this->indieClient->getAccessToken(
$tokenEndpoint,
$request->input('code'),
$request->input('me'),
route('micropub-client-get-new-token-callback'), // redirect_uri
route('micropub-client') // client_id
);
if (array_key_exists('access_token', $token)) {
$url = normalize_url($token['me']);
$user = IndieWebUser::where('me', $url)->firstOrFail();
$user->token = $token['access_token'];
$user->save();
return redirect()->route('micropub-config');
}
return redirect()->route('micropub-config')->with('error', 'Error getting token from the endpoint');
}
return redirect()->route('micropub-config')->with('error', 'Unable to find token endpoint');
}
/**
* Query the micropub endpoint and store response.
*
* @param Illuminate\Http\Request $request
* @return redirect
*/
public function queryEndpoint(Request $request)
{
$url = normalize_url($request->session()->get('me'));
$user = IndieWebUser::where('me', $url)->firstOrFail();
$token = $user->token;
$micropubEndpoint = $this->indieClient->discoverMicropubEndpoint($url);
if ($micropubEndpoint) {
try {
$response = $this->guzzleClient->get($micropubEndpoint, [
'headers' => ['Authorization' => 'Bearer ' . $token],
'query' => 'q=config',
]);
} catch (ClientException | ServerException $e) {
return back();
}
$body = (string) $response->getBody();
$data = json_decode($body, true);
if (array_key_exists('syndicate-to', $data)) {
$user->syndication = json_encode($data['syndicate-to']);
}
if (array_key_exists('media-endpoint', $data)) {
$user->mediaEndpoint = $data['media-endpoint'];
}
$user->save();
return back();
}
}
/**
* Update the syntax setting.
*
* @param Illuminate\Http\Request $request
* @return Illuminate\Http\RedirectResponse
* @todo validate input
*/
public function updateSyntax(Request $request)
{
$user = IndieWebUser::where('me', $request->session()->get('me'))->firstOrFail();
$user->syntax = $request->syntax;
$user->save();
return redirect()->route('micropub-config');
}
/**
* Create a new place.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function newPlace(Request $request)
{
$url = normalize_url($request->session()->get('me'));
$user = IndieWebUser::where('me', $url)->firstOrFail();
if ($user->token === null) {
return response()->json([
'error' => true,
'error_description' => 'No known token',
], 400);
}
$micropubEndpoint = $this->indieClient->discoverMicropubEndpoint($url);
if (! $micropubEndpoint) {
return response()->json([
'error' => true,
'error_description' => 'Could not determine the micropub endpoint.',
], 400);
}
$formParams = [
'h' => 'card',
'name' => $request->input('place-name'),
'description' => $request->input('place-description'),
'geo' => 'geo:' . $request->input('place-latitude') . ',' . $request->input('place-longitude'),
];
$headers = [
'Authorization' => 'Bearer ' . $user->token,
];
try {
$response = $this->guzzleClient->request('POST', $micropubEndpoint, [
'form_params' => $formParams,
'headers' => $headers,
]);
} catch (ClientException $e) {
return response()->json([
'error' => true,
'error_description' => 'Unable to create the new place',
], 400);
}
$place = $response->getHeader('Location')[0];
return response()->json([
'uri' => $place,
'name' => $request->input('place-name'),
'latitude' => $request->input('place-latitude'),
'longitude' => $request->input('place-longitude'),
]);
}
/**
* Make a request to the micropub endpoint requesting any nearby places.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function nearbyPlaces(Request $request)
{
$url = normalize_url($request->session()->get('me'));
$user = IndieWebUser::where('me', $url)->firstOrFail();
if ($user->token === null) {
return response()->json([
'error' => true,
'error_description' => 'No known token',
], 400);
}
$micropubEndpoint = $this->indieClient->discoverMicropubEndpoint($url);
if (! $micropubEndpoint) {
return response()->json([
'error' => true,
'error_description' => 'No known endpoint',
], 400);
}
try {
$query = 'geo:' . $request->input('latitude') . ',' . $request->input('longitude');
if ($request->input('u') !== null) {
$query .= ';u=' . $request->input('u');
}
$response = $this->guzzleClient->get($micropubEndpoint, [
'headers' => ['Authorization' => 'Bearer ' . $user->token],
'query' => ['q' => $query],
]);
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
return response()->json([
'error' => true,
'error_description' => 'The endpoint ' . $micropubEndpoint . ' returned a non-good response',
], 400);
}
return response($response->getBody(), 200)
->header('Content-Type', 'application/json');
}
/**
* Parse the syndication targets JSON into a an array.
*
* @param string|null
* @return array|null
*/
private function parseSyndicationTargets($syndicationTargets = null)
{
if ($syndicationTargets === null || $syndicationTargets === '') {
return;
}
$syndicateTo = [];
$data = json_decode($syndicationTargets, true);
if (array_key_exists('uid', $data)) {
$syndicateTo[] = [
'target' => $data['uid'],
'name' => $data['name'],
];
}
foreach ($data as $syn) {
if (array_key_exists('uid', $syn)) {
$syndicateTo[] = [
'target' => $syn['uid'],
'name' => $syn['name'],
];
}
}
return $syndicateTo;
}
/**
* Parse the media-endpoint retrieved from querying a micropub endpoint.
*
* @param string|null
* @return string
*/
private function parseMediaEndpoint($queryResponse = null)
{
if ($queryResponse === null) {
return;
}
$data = json_decode($queryResponse, true);
if (array_key_exists('media-endpoint', $data)) {
return $data['media-endpoint'];
}
}
}