feat(service): keyvisual/background upload + scope choice
This commit is contained in:
parent
1ce30b76e3
commit
b31f21959f
48
app/Http/Controllers/ServiceImageController.php
Normal file
48
app/Http/Controllers/ServiceImageController.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Service;
|
||||
use App\Models\Setting;
|
||||
use App\Services\FileConversionService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class ServiceImageController extends Controller
|
||||
{
|
||||
public function storeKeyVisual(Request $request, Service $service): RedirectResponse
|
||||
{
|
||||
return $this->store($request, $service, 'key_visual_filename', 'current_key_visual');
|
||||
}
|
||||
|
||||
public function storeBackground(Request $request, Service $service): RedirectResponse
|
||||
{
|
||||
return $this->store($request, $service, 'background_filename', 'current_background');
|
||||
}
|
||||
|
||||
private function store(Request $request, Service $service, string $column, string $settingKey): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'file' => ['required', 'file', 'mimes:jpg,jpeg,png', 'max:20480'],
|
||||
'scope' => ['required', Rule::in(['service', 'default'])],
|
||||
], [
|
||||
'file.required' => 'Bitte wähle eine Bilddatei aus.',
|
||||
'file.file' => 'Die hochgeladene Datei ist ungültig.',
|
||||
'file.mimes' => 'Nur Bilddateien (jpg, png) sind erlaubt.',
|
||||
'file.max' => 'Die Datei darf maximal 20 MB groß sein.',
|
||||
'scope.required' => 'Bitte wähle einen Geltungsbereich.',
|
||||
'scope.in' => 'Der gewählte Geltungsbereich ist ungültig.',
|
||||
]);
|
||||
|
||||
$result = app(FileConversionService::class)->convertImageCover($request->file('file'));
|
||||
|
||||
$service->update([$column => $result['filename']]);
|
||||
|
||||
if ($request->input('scope') === 'default') {
|
||||
Setting::set($settingKey, $result['filename']);
|
||||
}
|
||||
|
||||
return back()->with('success', 'Bild wurde gespeichert.');
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
use App\Http\Controllers\MacroAssignmentController;
|
||||
use App\Http\Controllers\MacroImportController;
|
||||
use App\Http\Controllers\ServiceController;
|
||||
use App\Http\Controllers\ServiceImageController;
|
||||
use App\Http\Controllers\ServiceMacroOverrideController;
|
||||
use App\Http\Controllers\SettingsController;
|
||||
use App\Http\Controllers\SongPdfController;
|
||||
|
|
@ -67,6 +68,8 @@
|
|||
Route::get('/services/{service}/download-bundle/{blockType}', [ServiceController::class, 'downloadBundle'])->name('services.download-bundle');
|
||||
Route::get('/services/{service}/agenda-items/{agendaItem}/download', [ServiceController::class, 'downloadAgendaItem'])->name('services.agenda-item.download');
|
||||
Route::get('/services/{service}/edit', [ServiceController::class, 'edit'])->name('services.edit');
|
||||
Route::post('/services/{service}/key-visual', [ServiceImageController::class, 'storeKeyVisual'])->name('services.key-visual.store');
|
||||
Route::post('/services/{service}/background', [ServiceImageController::class, 'storeBackground'])->name('services.background.store');
|
||||
Route::get('/songs/{song}/translate', [TranslationController::class, 'page'])->name('songs.translate');
|
||||
|
||||
Route::get('/songs', function () {
|
||||
|
|
|
|||
159
tests/Feature/ServiceImageControllerTest.php
Normal file
159
tests/Feature/ServiceImageControllerTest.php
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Service;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
beforeEach(function () {
|
||||
Storage::fake('public');
|
||||
Queue::fake();
|
||||
$this->user = User::factory()->create();
|
||||
$this->actingAs($this->user);
|
||||
$this->service = Service::factory()->create();
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Key-Visual Upload
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
test('key visual upload with service scope sets column only', function () {
|
||||
$file = makeImageUpload('keyvisual.png', 800, 600);
|
||||
|
||||
$response = $this->post(route('services.key-visual.store', $this->service), [
|
||||
'file' => $file,
|
||||
'scope' => 'service',
|
||||
]);
|
||||
|
||||
$response->assertRedirect();
|
||||
$response->assertSessionHas('success');
|
||||
|
||||
$this->service->refresh();
|
||||
expect($this->service->key_visual_filename)->not->toBeNull();
|
||||
expect($this->service->key_visual_filename)->toStartWith('slides/');
|
||||
expect($this->service->key_visual_filename)->toEndWith('.jpg');
|
||||
expect(Storage::disk('public')->exists($this->service->key_visual_filename))->toBeTrue();
|
||||
|
||||
expect(Setting::get('current_key_visual'))->toBeNull();
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Background Upload (default scope)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
test('background upload with default scope sets column and global setting', function () {
|
||||
$file = makeImageUpload('background.png', 1920, 1080);
|
||||
|
||||
$response = $this->post(route('services.background.store', $this->service), [
|
||||
'file' => $file,
|
||||
'scope' => 'default',
|
||||
]);
|
||||
|
||||
$response->assertRedirect();
|
||||
$response->assertSessionHas('success');
|
||||
|
||||
$this->service->refresh();
|
||||
expect($this->service->background_filename)->not->toBeNull();
|
||||
expect($this->service->background_filename)->toStartWith('slides/');
|
||||
expect(Storage::disk('public')->exists($this->service->background_filename))->toBeTrue();
|
||||
|
||||
expect(Setting::get('current_background'))->toBe($this->service->background_filename);
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Validation
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
test('key visual upload rejects non-image file with german error', function () {
|
||||
$file = UploadedFile::fake()->create('notes.txt', 10, 'text/plain');
|
||||
|
||||
$response = $this->postJson(route('services.key-visual.store', $this->service), [
|
||||
'file' => $file,
|
||||
'scope' => 'service',
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJsonValidationErrors(['file']);
|
||||
expect($response->json('errors.file.0'))->toContain('Bild');
|
||||
|
||||
$this->service->refresh();
|
||||
expect($this->service->key_visual_filename)->toBeNull();
|
||||
});
|
||||
|
||||
test('background upload rejects invalid scope', function () {
|
||||
$file = makeImageUpload('background.png', 800, 600);
|
||||
|
||||
$response = $this->postJson(route('services.background.store', $this->service), [
|
||||
'file' => $file,
|
||||
'scope' => 'bogus',
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJsonValidationErrors(['scope']);
|
||||
});
|
||||
|
||||
test('key visual upload does not delete previous file', function () {
|
||||
$first = makeImageUpload('first.png', 800, 600);
|
||||
$this->post(route('services.key-visual.store', $this->service), [
|
||||
'file' => $first,
|
||||
'scope' => 'service',
|
||||
]);
|
||||
$this->service->refresh();
|
||||
$oldFilename = $this->service->key_visual_filename;
|
||||
|
||||
$second = makeImageUpload('second.png', 800, 600);
|
||||
$this->post(route('services.key-visual.store', $this->service), [
|
||||
'file' => $second,
|
||||
'scope' => 'service',
|
||||
]);
|
||||
$this->service->refresh();
|
||||
|
||||
expect($this->service->key_visual_filename)->not->toBe($oldFilename);
|
||||
expect(Storage::disk('public')->exists($oldFilename))->toBeTrue();
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Auth
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
test('key visual upload requires authentication', function () {
|
||||
auth()->logout();
|
||||
$file = makeImageUpload('keyvisual.png', 800, 600);
|
||||
|
||||
$response = $this->post(route('services.key-visual.store', $this->service), [
|
||||
'file' => $file,
|
||||
'scope' => 'service',
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('login'));
|
||||
});
|
||||
|
||||
function makeImageUpload(string $name = 'test.png', int $w = 800, int $h = 600): UploadedFile
|
||||
{
|
||||
$path = tempnam(sys_get_temp_dir(), 'cts-svc-img-');
|
||||
if ($path === false) {
|
||||
throw new RuntimeException('Temp-Datei konnte nicht erstellt werden.');
|
||||
}
|
||||
|
||||
$image = imagecreatetruecolor($w, $h);
|
||||
if ($image === false) {
|
||||
throw new RuntimeException('Bild konnte nicht erstellt werden.');
|
||||
}
|
||||
|
||||
$blue = imagecolorallocate($image, 0, 0, 255);
|
||||
imagefill($image, 0, 0, $blue);
|
||||
imagepng($image, $path);
|
||||
imagedestroy($image);
|
||||
|
||||
return new UploadedFile($path, $name, 'image/png', null, true);
|
||||
}
|
||||
Loading…
Reference in a new issue