- Slim::storeCroppedImage() vertraute dem vom Client gemeldeten Typ und uebernahm die Endung aus dem Dateinamen; "shell.php" mit Typ image/png landete als .php im public-Disk. Jetzt entscheidet getimagesizefromstring() ueber die Bytes, die Endung kommt aus dem geprueften Typ. - Foto loeschen war ein GET-Link und liess sich per fremdem <img src> ausloesen (CSRF); jetzt POST-Formular mit Token. - Ohne Konto (users.account_id) bricht Upload/Loeschen mit 404 ab statt mit einem Fatal. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
86 lines
2.8 KiB
PHP
86 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Services\Slim;
|
|
use Auth;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Request;
|
|
|
|
/**
|
|
* Berater-Profilfoto (N6): Upload/Löschen über das Slim-Cropper-Muster
|
|
* ({@see UserShopController::uploadImage()}), gespeichert unter
|
|
* images/profile/ im public-Disk (user_accounts.profile_image).
|
|
*
|
|
* Der Upload ist nur mit gesetzter Einwilligungs-Checkbox möglich
|
|
* (teaminterne Anzeige in Ranglisten/Übersichten, account.profile_image_consent).
|
|
*/
|
|
class ProfileImageController extends Controller
|
|
{
|
|
public function uploadImage(): RedirectResponse
|
|
{
|
|
$user = Auth::user();
|
|
|
|
// Das Foto haengt am Konto (users.account_id). Ohne Konto gibt es nichts
|
|
// zu speichern — sonst liefe der Zugriff darunter in einen Fatal.
|
|
if (! $user->account) {
|
|
abort(404);
|
|
}
|
|
|
|
if (! Request::get('profile_image_consent')) {
|
|
\Session()->flash('alert-danger', __('account.profile_image_consent_required'));
|
|
|
|
return redirect(route('user_edit'));
|
|
}
|
|
|
|
try {
|
|
$image = Slim::getImages('images')[0];
|
|
|
|
if (isset($image['output']['data'])) {
|
|
$stored = Slim::storeCroppedImage($image, 'images/profile');
|
|
|
|
if ($stored === null) {
|
|
\Session()->flash('alert-danger', 'File is not jpg or png!');
|
|
|
|
return redirect(route('user_edit'));
|
|
}
|
|
|
|
if ($user->account->profile_image) {
|
|
\Storage::disk('public')->delete('images/profile/'.$user->account->profile_image);
|
|
}
|
|
|
|
$user->account->profile_image = $stored['filename'];
|
|
$user->account->save();
|
|
|
|
\Session()->flash('alert-success', __('msg.file_uploaded'));
|
|
|
|
return redirect(route('user_edit'));
|
|
}
|
|
\Session()->flash('alert-danger', __('msg.file_empty'));
|
|
|
|
return redirect(route('user_edit'));
|
|
} catch (\Exception $e) {
|
|
// Details ins Log, nicht ins UI — die Exception enthält Serverpfade.
|
|
\Log::warning('ProfileImageController: Upload fehlgeschlagen für User '.$user->id.': '.$e->getMessage());
|
|
\Session()->flash('alert-danger', __('msg.file_upload_failed'));
|
|
|
|
return redirect(route('user_edit'));
|
|
}
|
|
}
|
|
|
|
public function deleteImage(): RedirectResponse
|
|
{
|
|
$user = Auth::user();
|
|
|
|
if ($user->account?->profile_image) {
|
|
\Storage::disk('public')->delete('images/profile/'.$user->account->profile_image);
|
|
|
|
$user->account->profile_image = null;
|
|
$user->account->save();
|
|
|
|
\Session()->flash('alert-success', __('msg.file_deleted'));
|
|
}
|
|
|
|
return redirect(route('user_edit'));
|
|
}
|
|
}
|