2026-01-15 01:13:44 +00:00
|
|
|
<?php
|
|
|
|
|
|
refactor: settings redesign, Spanish translations, language system, strict_types
Settings pages:
- Redesign layout to match Sendkit (max-w-4xl, space-y-12, Separator sections)
- Merge Members page into Workspace settings with Table, invite Dialog, ConfirmDeleteModal
- Add workspace logo upload/delete routes and controller methods
- Translate all hardcoded strings in Workspace.vue modals
Language system:
- Drop languages table, replace language_id FK with locale string column on users
- Create config/languages.php for available languages and default locale
- Add Spanish (es) translations (13 files)
- Simplify HandleInertiaRequests, ProfileController, RegisteredUserController
Code quality:
- Add declare(strict_types=1) to all PHP files
- Fix MastodonPublisher using wrong attribute (filename -> original_filename)
- Fix HasMediaTest for new has_photo/photo_url accessors
- Fix PublishToSocialPlatformTest type error revealed by strict_types
- Remove orphaned Language model from AppServiceProvider morph map
- Update User TypeScript interface (has_photo, photo_url, locale)
- Eager load media relation on workspaces to prevent N+1
- Add 8 new tests for workspace logo upload/delete
- Update workspace settings test to assert members/invitations props
All 710 tests passing.
2026-03-30 03:20:43 +00:00
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
2026-07-17 18:40:15 +00:00
|
|
|
use App\Services\Brand\SafeHttpFetcher;
|
2026-01-15 01:13:44 +00:00
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
|
use Illuminate\Support\Str;
|
|
|
|
|
|
|
|
|
|
if (! function_exists('uploadFromUrl')) {
|
|
|
|
|
/**
|
|
|
|
|
* Download an image from URL and upload to storage.
|
|
|
|
|
*
|
|
|
|
|
* @param string|null $url The URL to download from
|
|
|
|
|
* @param string $directory The directory to store the file in
|
|
|
|
|
* @return string|null The stored file path or null on failure
|
|
|
|
|
*/
|
|
|
|
|
function uploadFromUrl(?string $url, string $directory = 'social-accounts'): ?string
|
|
|
|
|
{
|
|
|
|
|
if (! $url) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
2026-07-17 18:40:15 +00:00
|
|
|
$response = app(SafeHttpFetcher::class)->guardedRequest($url)->timeout(10)->get($url);
|
2026-01-15 01:13:44 +00:00
|
|
|
|
|
|
|
|
if (! $response->successful()) {
|
|
|
|
|
Log::warning('uploadFromUrl: Failed to download', [
|
|
|
|
|
'url' => $url,
|
|
|
|
|
'status' => $response->status(),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$contentType = $response->header('Content-Type') ?? 'image/jpeg';
|
|
|
|
|
$extension = match (true) {
|
|
|
|
|
str_contains($contentType, 'png') => 'png',
|
|
|
|
|
str_contains($contentType, 'gif') => 'gif',
|
|
|
|
|
str_contains($contentType, 'webp') => 'webp',
|
|
|
|
|
default => 'jpg',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$filename = sprintf(
|
|
|
|
|
'%s/%s.%s',
|
|
|
|
|
trim($directory, '/'),
|
|
|
|
|
Str::uuid(),
|
|
|
|
|
$extension
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Storage::put($filename, $response->body(), 'public');
|
|
|
|
|
|
|
|
|
|
return $filename;
|
2026-03-29 22:24:28 +00:00
|
|
|
} catch (Exception $e) {
|
2026-01-15 01:13:44 +00:00
|
|
|
Log::warning('uploadFromUrl: Exception', [
|
|
|
|
|
'url' => $url,
|
|
|
|
|
'error' => $e->getMessage(),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|