jonnybarnes.uk/app/Http/Controllers/Admin/ContactsController.php
Jonny Barnes e08763c526
Migrate outbound HTTP calls from Guzzle to the Http facade
Replaces every raw GuzzleHttp\Client usage (Nominatim, web.archive.org,
webmention fetch/discover/send, Bridgy syndication, IndieAuth client_id
lookup, contact avatar/h-card fetch, profile image download, and the
CloudConvert screenshot pipeline) with Illuminate\Support\Facades\Http,
and enables Http::preventStrayRequests() globally in tests so any
un-faked outbound call now fails loudly instead of silently hitting the
network.

The CloudConvert retry-until-finished middleware in AppServiceProvider
is replaced by a plain polling loop in SaveScreenshot, which also fixes
a latent bug where the old middleware decoded a response object instead
of its body. Bridgy syndication jobs keep their tries=1/no-retry
semantics unchanged to avoid duplicate publishes. Guzzle's PSR-7 helpers
(Header, UriResolver, Utils) stay in SendWebMentions since Http has no
equivalent for them.

Every affected test file's Guzzle MockHandler/HandlerStack boilerplate
is replaced with Http::fake()/Http::sequence().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8j7cJhCiYDsGUgB7obKNQ
2026-07-26 09:12:01 +01:00

153 lines
4.9 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
declare(strict_types=1);
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Contact;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Http\Client\RequestException;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Http;
use Illuminate\View\View;
class ContactsController extends Controller
{
/**
* List the currect contacts that can be edited.
*/
public function index(): View
{
$contacts = Contact::all();
return view('admin.contacts.index', compact('contacts'));
}
/**
* Display the form to add a new contact.
*/
public function create(): View
{
return view('admin.contacts.create');
}
/**
* Process the request to add a new contact.
*/
public function store(): RedirectResponse
{
$contact = new Contact;
$contact->name = request()->input('name');
$contact->nick = request()->input('nick');
$contact->homepage = request()->input('homepage');
$contact->twitter = request()->input('twitter');
$contact->facebook = request()->input('facebook');
$contact->save();
return redirect('/admin/contacts');
}
/**
* Show the form to edit an existing contact.
*/
public function edit(int $contactId): View
{
$contact = Contact::findOrFail($contactId);
return view('admin.contacts.edit', compact('contact'));
}
/**
* Process the request to edit a contact.
*
* @todo Allow saving profile pictures for people without homepages
*/
public function update(int $contactId): RedirectResponse
{
$contact = Contact::findOrFail($contactId);
$contact->name = request()->input('name');
$contact->nick = request()->input('nick');
$contact->homepage = request()->input('homepage');
$contact->twitter = request()->input('twitter');
$contact->facebook = request()->input('facebook');
$contact->save();
if (request()->hasFile('avatar') && (request()->input('homepage') != '')) {
$dir = parse_url(request()->input('homepage'), PHP_URL_HOST);
$destination = public_path().'/assets/profile-images/'.$dir;
$filesystem = new Filesystem;
if ($filesystem->isDirectory($destination) === false) {
$filesystem->makeDirectory($destination);
}
request()->file('avatar')->move($destination, 'image');
}
return redirect('/admin/contacts');
}
/**
* Process the request to delete a contact.
*/
public function destroy(int $contactId): RedirectResponse
{
$contact = Contact::findOrFail($contactId);
$contact->delete();
return redirect('/admin/contacts');
}
/**
* Download the avatar for a contact.
*
* This method attempts to find the microformat marked-up profile image
* from a given homepage and save it accordingly
*
* @return RedirectResponse|View
*/
public function getAvatar(int $contactId)
{
// Initialising
$avatarURL = null;
$avatar = null;
$contact = Contact::findOrFail($contactId);
if ($contact->homepage !== null && mb_strlen($contact->homepage) !== 0) {
try {
$response = Http::throw()->get($contact->homepage);
} catch (RequestException $e) {
return redirect('/admin/contacts/'.$contactId.'/edit')
->with('error', 'Bad resposne from contacts homepage');
}
$mf2 = \Mf2\parse($response->body(), $contact->homepage);
foreach ($mf2['items'] as $microformat) {
if (Arr::get($microformat, 'type.0') === 'h-card') {
$avatarURL = Arr::get($microformat, 'properties.photo.0.value');
break;
}
}
if ($avatarURL !== null) {
try {
$avatar = Http::throw()->get($avatarURL);
} catch (RequestException $e) {
return redirect('/admin/contacts/'.$contactId.'/edit')
->with('error', 'Unable to download avatar');
}
}
if ($avatar !== null) {
$directory = public_path().'/assets/profile-images/'.parse_url($contact->homepage, PHP_URL_HOST);
$filesystem = new Filesystem;
if ($filesystem->isDirectory($directory) === false) {
$filesystem->makeDirectory($directory);
}
$filesystem->put($directory.'/image', $avatar->body());
return view('admin.contacts.getavatarsuccess', [
'homepage' => parse_url($contact->homepage, PHP_URL_HOST),
]);
}
}
return redirect('/admin/contacts/'.$contactId.'/edit');
}
}