From ccef61352ddace1046ef3a0affd3450f5eb622ec Mon Sep 17 00:00:00 2001 From: philipp lang Date: Tue, 17 Jun 2025 00:01:07 +0200 Subject: [PATCH] Add backend for copying forms --- app/Form/Actions/FormCopyAction.php | 41 ++++++++++++ app/Form/Resources/FormResource.php | 1 + routes/web.php | 2 + tests/EndToEnd/Form/FormIndexActionTest.php | 9 +++ tests/Feature/Form/FormCopyActionTest.php | 70 +++++++++++++++++++++ tests/TestCase.php | 14 +++++ 6 files changed, 137 insertions(+) create mode 100644 app/Form/Actions/FormCopyAction.php create mode 100644 tests/Feature/Form/FormCopyActionTest.php diff --git a/app/Form/Actions/FormCopyAction.php b/app/Form/Actions/FormCopyAction.php new file mode 100644 index 00000000..ca08d3d4 --- /dev/null +++ b/app/Form/Actions/FormCopyAction.php @@ -0,0 +1,41 @@ + $attributes + */ + public function handle(Form $form): Form + { + $newForm = $form->replicate(); + $newForm->save(); + $newForm->update(['name' => $form->name.' - Kopie', 'is_active' => false]); + + foreach ($form->getRegisteredMediaCollections() as $collection) { + foreach ($form->getMedia($collection->name) as $media) { + $media->copy($newForm, $collection->name); + } + } + + ClearFrontendCacheAction::run(); + + return $form; + } + + public function asController(Form $form): JsonResponse + { + $this->handle($form); + + Succeeded::message('Veranstaltung kopiert.')->dispatch(); + return response()->json([]); + } +} diff --git a/app/Form/Resources/FormResource.php b/app/Form/Resources/FormResource.php index 9e664d45..2718f6ca 100644 --- a/app/Form/Resources/FormResource.php +++ b/app/Form/Resources/FormResource.php @@ -61,6 +61,7 @@ class FormResource extends JsonResource 'is_dirty' => route('form.is-dirty', $this->getModel()), 'frontend' => str(app(FormSettings::class)->registerUrl)->replace('{slug}', $this->slug), 'export' => route('form.export', $this->getModel()), + 'copy' => route('form.copy', $this->getModel()), ] ]; } diff --git a/routes/web.php b/routes/web.php index 7da88809..ef36e970 100644 --- a/routes/web.php +++ b/routes/web.php @@ -23,6 +23,7 @@ use App\Fileshare\Actions\FileshareStoreAction; use App\Fileshare\Actions\FileshareUpdateAction; use App\Fileshare\Actions\ListFilesAction; use App\Form\Actions\ExportAction as ActionsExportAction; +use App\Form\Actions\FormCopyAction; use App\Form\Actions\FormDestroyAction; use App\Form\Actions\FormIndexAction; use App\Group\Actions\GroupBulkstoreAction; @@ -179,6 +180,7 @@ Route::group(['middleware' => 'auth:web'], function (): void { Route::get('/participant/{participant}/fields', ParticipantFieldsAction::class)->name('participant.fields'); Route::patch('/participant/{participant}', ParticipantUpdateAction::class)->name('participant.update'); Route::post('/form/{form}/participant', ParticipantStoreAction::class)->name('form.participant.store'); + Route::post('/form/{form}/copy', FormCopyAction::class)->name('form.copy'); // ------------------------------------ fileshare ----------------------------------- Route::post('/fileshare', FileshareStoreAction::class)->name('fileshare.store'); diff --git a/tests/EndToEnd/Form/FormIndexActionTest.php b/tests/EndToEnd/Form/FormIndexActionTest.php index 99be2836..6aeaabcc 100644 --- a/tests/EndToEnd/Form/FormIndexActionTest.php +++ b/tests/EndToEnd/Form/FormIndexActionTest.php @@ -145,6 +145,15 @@ class FormIndexActionTest extends FormTestCase $this->callFilter('form.index', [])->assertInertiaPath('data.data.0.links.frontend', 'https://example.com/form/zem-2024/register'); } + public function testItDisplaysCopyUrl(): void + { + $this->withoutExceptionHandling()->login()->loginNami(); + $form = Form::factory()->create(); + + sleep(1); + $this->callFilter('form.index', [])->assertInertiaPath('data.data.0.links.copy', route('form.copy', $form)); + } + public function testItDoesntReturnInactiveForms(): void { $this->withoutExceptionHandling()->login()->loginNami(); diff --git a/tests/Feature/Form/FormCopyActionTest.php b/tests/Feature/Form/FormCopyActionTest.php new file mode 100644 index 00000000..53ebb6f5 --- /dev/null +++ b/tests/Feature/Form/FormCopyActionTest.php @@ -0,0 +1,70 @@ +fakeMessages(); + test()->setUpForm(); +}); + +dataset('media', fn () => [ + ['mailattachments'], + ['headerImage'], +]); + +it('copies a form', function () { + test()->login()->loginNami()->withoutExceptionHandling(); + $form = Form::factory()->name('Lager')->create(); + + test()->post(route('form.copy', ['form' => $form])) + ->assertOk(); + + test()->assertDatabaseCount('forms', 2); + + $newForm = Form::where('name', 'Lager - Kopie')->firstOrFail(); + test()->assertEquals( + Arr::except($form->fresh()->toArray(), ['id', 'name', 'slug', 'created_at', 'updated_at', 'is_active']), + Arr::except($newForm->fresh()->toArray(), ['id', 'name', 'slug', 'created_at', 'updated_at', 'is_active']) + ); +}); + +it('copies the forms media', function (string $collectionName) { + test()->login()->loginNami()->withoutExceptionHandling(); + $form = Form::factory()->withImage($collectionName, 'lala.jpg')->name('Lager')->create(); + + test()->post(route('form.copy', ['form' => $form]))->assertOk(); + + test()->assertDatabaseCount('forms', 2); + + $newForm = Form::where('name', 'Lager - Kopie')->firstOrFail(); + test()->assertEquals($form->getMedia($collectionName)->first()->name, $newForm->getMedia($collectionName)->first()->name); + test()->assertNotEquals($form->getMedia($collectionName)->first()->id, $newForm->getMedia($collectionName)->first()->id); + test()->assertNotEquals($form->getMedia($collectionName)->first()->getFullUrl(), $newForm->getMedia($collectionName)->first()->getFullUrl()); +})->with('media'); + +it('deactivates a copied form', function () { + test()->login()->loginNami()->withoutExceptionHandling(); + $form = Form::factory()->name('Lager')->create(['is_active' => true]); + + test()->post(route('form.copy', ['form' => $form]))->assertOk(); + + $newForm = Form::where('name', 'Lager - Kopie')->firstOrFail(); + test()->assertEquals(false, $newForm->is_active); +}); + +it('shows success message', function () { + test()->login()->loginNami()->withoutExceptionHandling(); + $form = Form::factory()->create(); + + test()->post(route('form.copy', ['form' => $form]))->assertOk(); + + test()->assertSuccessMessage('Veranstaltung kopiert.'); +}); diff --git a/tests/TestCase.php b/tests/TestCase.php index 6731b997..d2ed1b86 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,12 +3,14 @@ namespace Tests; use App\Group; +use App\Lib\Events\Succeeded; use App\Member\Member; use App\Setting\NamiSettings; use App\User; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Arr; +use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Http; use Illuminate\Testing\AssertableJsonString; use Illuminate\Testing\TestResponse; @@ -192,4 +194,16 @@ class TestCase extends BaseTestCase return $this; }); } + + public function fakeMessages() { + Event::fake([Succeeded::class]); + + return $this; + } + + public function assertSuccessMessage(string $message) { + Event::assertDispatched(Succeeded::class, fn ($event) => $event->message === $message); + + return $this; + } }