diff --git a/app/Http/Controllers/BlogController.php b/app/Http/Controllers/BlogController.php index f7b249d..f52539a 100644 --- a/app/Http/Controllers/BlogController.php +++ b/app/Http/Controllers/BlogController.php @@ -25,7 +25,7 @@ class BlogController extends Controller $blog->save(); return ['messageStatus' => 'success', 'errorMessage' => '']; } catch (\Illuminate\Database\QueryException $e) { - return ['messageStatus' => 'danger', 'errorMessage' => $e->getMessage()]; + return ['messageStatus' => 'danger', 'errorMessage' => __("controller_messages.BlogController.error_occurred", ["error" => $e->getMessage()])]; } } abort(404); @@ -43,7 +43,7 @@ class BlogController extends Controller $blog->save(); return ['messageStatus' => 'success', 'errorMessage' => '']; } catch (\Illuminate\Database\QueryException $e) { - return ['messageStatus' => 'failure', 'errorMessage' => $e->getMessage()]; + return ['messageStatus' => 'failure', 'errorMessage' => __("controller_messages.BlogController.error_occurred", ["error" => $e->getMessage()])]; } } abort(404); @@ -58,7 +58,7 @@ class BlogController extends Controller } return ['messageStatus' => 'success', 'errorMessage' => '']; } catch(\Exception $e){ - return ['messageStatus' => 'failure', 'errorMessage' => $e->getMessage()]; + return ['messageStatus' => 'failure', 'errorMessage' => __("controller_messages.BlogController.error_occurred", ["error" => $e->getMessage()])]; } } abort(404); diff --git a/app/Http/Controllers/LoginController.php b/app/Http/Controllers/LoginController.php index fb5b0ca..14e3a2e 100644 --- a/app/Http/Controllers/LoginController.php +++ b/app/Http/Controllers/LoginController.php @@ -16,7 +16,7 @@ class LoginController extends Controller // messageStatus success|failure|not permitted - $json = ["messageStatus" => "failure", "errorMessage" => "idk"]; + $json = ["messageStatus" => "failure", "errorMessage" => __("controller_messages.LoginController.unknown_error")]; $mail = $request->input('mail'); $username = $request->input('username'); @@ -24,14 +24,14 @@ class LoginController extends Controller $token = $request->input('token'); if(preg_match('/@c3gov\.de$/', $mail) != 1){ - return ["messageStatus" => "not permitted", "errorMessage" => ""]; + return ["messageStatus" => "not permitted", "errorMessage" => __("controller_messages.LoginController.not_permitted")]; } $rt = RegisterToken::where('token', $token)->first(); if($rt) { $u = User::whereEmail($mail)->first(); if($u) { - return ["messageStatus" => "failure", "errorMessage" => "Diese elektronische Postadresse wird bereits verwendet."]; + return ["messageStatus" => "failure", "errorMessage" => __("controller_messages.LoginController.email_in_use")]; } try { $u = new User(); @@ -43,10 +43,10 @@ class LoginController extends Controller $rt->delete(); } catch (\Exception $e) { - return ["messageStatus" => "failure", "errorMessage" => $e->getMessage()]; + return ["messageStatus" => "failure", "errorMessage" => __("controller_messages.LoginController.unknown_error_with_details", ["error" => $e->getMessage()])]; } } else { - return ["messageStatus" => "failure", "errorMessage" => "Machen Sie die Musik aus."]; + return ["messageStatus" => "failure", "errorMessage" => __("controller_messages.LoginController.invalid_token")]; } return ["messageStatus" => "success", "errorMessage" => ""]; @@ -64,10 +64,10 @@ class LoginController extends Controller Auth::login($u); return ["messageStatus" => "success", "errorMessage" => ""]; } else { - return ["messageStatus" => "failure", "errorMessage" => "Falsch. Einfach nur falsch."]; + return ["messageStatus" => "failure", "errorMessage" => __("controller_messages.LoginController.wrong_credentials")]; } } else { - return ["messageStatus" => "failure", "errorMessage" => "Falsch. Einfach nur falsch."]; + return ["messageStatus" => "failure", "errorMessage" => __("controller_messages.LoginController.wrong_credentials")]; } } diff --git a/app/Http/Controllers/PreApplicationController.php b/app/Http/Controllers/PreApplicationController.php new file mode 100644 index 0000000..b409ee8 --- /dev/null +++ b/app/Http/Controllers/PreApplicationController.php @@ -0,0 +1,86 @@ +input('event'); + + $ce = ChaosEvents::where('id', $event)->where('to_date', '>=', now())->first(); + if($ce){ + + $passport_type = $request->input('passport_type'); + if(!in_array($passport_type, ['d', 'k'])){ + return ["messageStatus" => "failure", "errorMessage" => __("controller_messages.PreApplicationController.wrong_passport_type")]; + } + + $first_name = $request->input('first_name'); + $last_name = $request->input('last_name'); + $location = $request->input('location'); + + $email = $request->input('mail'); + + if(!filter_var($email, FILTER_VALIDATE_EMAIL)){ + return ["messageStatus" => "failure", "errorMessage" => __("controller_messages.PreApplicationController.invalid_email_format")]; + } + + switch($passport_type){ + case 'd': + $max_characters = 17; + break; + case 'k': + $max_characters = 25; + break; + default: + $max_characters = 0; + } + + $fn = preg_match('/^[a-zA-Z0-9\s]{1,' . $max_characters . '}$/', $first_name); + $ln = preg_match('/^[a-zA-Z0-9\s]{,' . $max_characters . '}$/', $last_name); + $loc = preg_match('/^[a-zA-Z0-9\s]{,' . $max_characters . '}$/', $location); + + //Log::debug('fn: ' . var_export($fn, true) . ' ln: ' . var_export($ln, true) . ' loc: ' . var_export($loc, true) . ''); + + if($fn && $ln && $loc){ + + $free_reference_number = false; + while(!$free_reference_number){ + $reference_number = $ce->shortname . '-' . strtoupper($passport_type) . '-' . Random::generate(4, "0-9") . '-' . Random::generate(4, "0-9") . '-' . Random::generate(4, "0-9"); + $free_reference_number = !PreApplications::where('reference_number', $reference_number)->exists(); + } + + try{ + $pa = new PreApplications(); + $pa->first_name = $first_name; + $pa->last_name = $last_name; + $pa->location = $location; + $pa->passport_type = $passport_type; + $pa->reference_number = $reference_number; + $pa->event_id = $event; + $pa->email = $email; + $pa->save(); + } catch(\Exception $e){ + return ["messageStatus" => "failure", "errorMessage" => __("controller_messages.PreApplicationController.already_submitted")]; + } + + Mail::to($email) + ->send(new PreApplicationMail($pa, App::getLocale())); + + return ["messageStatus" => "success", "errorMessage" => __('controller_messages.PreApplicationController.success')]; + } else { + return ["messageStatus" => "failure", "errorMessage" => __("controller_messages.PreApplicationController.invalid_characters")]; + } + } + return ["messageStatus" => "failure", "errorMessage" => __("controller_messages.PreApplicationController.event_not_found")]; + } +} diff --git a/app/Http/Controllers/TickerController.php b/app/Http/Controllers/TickerController.php index 259b67e..3b91546 100644 --- a/app/Http/Controllers/TickerController.php +++ b/app/Http/Controllers/TickerController.php @@ -19,7 +19,7 @@ class TickerController extends Controller } return ["messageStatus" => "success", "errorMessage" => ""]; } catch(\Exception $e){ - return ["messageStatus" => "failure", "errorMessage" => $e->getMessage()]; + return ["messageStatus" => "failure", "errorMessage" => __("controller_messages.TickerController.error_occurred", ["error" => $e->getMessage()])]; } } @@ -38,7 +38,7 @@ class TickerController extends Controller $ticker->save(); return ["messageStatus" => "success", "errorMessage" => ""]; } catch(\Exception $e){ - return ["messageStatus" => "failure", "errorMessage" => $e->getMessage()]; + return ["messageStatus" => "failure", "errorMessage" => __("controller_messages.TickerController.error_occurred", ["error" => $e->getMessage()])]; } } abort(404); diff --git a/app/Mail/PreApplicationMail.php b/app/Mail/PreApplicationMail.php new file mode 100644 index 0000000..3628247 --- /dev/null +++ b/app/Mail/PreApplicationMail.php @@ -0,0 +1,66 @@ +lang); + return new Content( + view: 'mail.preapplication', + with: ['pa' => $this->pa], + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + $lang = match (App::getLocale()) { + 'de' => 'dk', + 'en' => 'ek', + default => 'dk', + }; + return [ + Attachment::fromPath(public_path('Dokumente/c3gov_fb5' . $lang . '.pdf')) + ->withMime('application/pdf'), + ]; + } +} diff --git a/app/Models/ChaosEvents.php b/app/Models/ChaosEvents.php new file mode 100644 index 0000000..020990b --- /dev/null +++ b/app/Models/ChaosEvents.php @@ -0,0 +1,18 @@ +hasMany('App\Models\PreApplications', 'event_id'); + } + + public static function getActiveEvents(){ + return self::where('to_date', '>=', now())->where('from_date', '<=', now())->get(); + } +} diff --git a/app/Models/PreApplications.php b/app/Models/PreApplications.php new file mode 100644 index 0000000..2b8c9b0 --- /dev/null +++ b/app/Models/PreApplications.php @@ -0,0 +1,15 @@ +belongsTo('App\Models\ChaosEvents','event_id'); + } +} diff --git a/config/app.php b/config/app.php index 423eed5..6fad0ed 100644 --- a/config/app.php +++ b/config/app.php @@ -65,7 +65,7 @@ return [ | */ - 'timezone' => 'UTC', + 'timezone' => 'Europe/Berlin', /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2026_03_26_201652_create_chaos_events_table.php b/database/migrations/2026_03_26_201652_create_chaos_events_table.php new file mode 100644 index 0000000..98a4529 --- /dev/null +++ b/database/migrations/2026_03_26_201652_create_chaos_events_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('name'); + $table->string('shortname'); + $table->date('from_date'); + $table->date('to_date'); + $table->boolean('active')->default(false); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('chaos_events'); + } +}; diff --git a/database/migrations/2026_03_26_203617_create_pre_applications_table.php b/database/migrations/2026_03_26_203617_create_pre_applications_table.php new file mode 100644 index 0000000..3039070 --- /dev/null +++ b/database/migrations/2026_03_26_203617_create_pre_applications_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('reference_number')->unique(); + $table->string('email')->unique(); + $table->string('passport_type'); + $table->bigInteger('event_id')->unsigned(); + $table->foreign('event_id')->references('id')->on('chaos_events')->onDelete('cascade'); + $table->string('first_name'); + $table->string('last_name')->nullable(); + $table->string('location')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('pre_applications'); + } +}; diff --git a/lang/de/app.php b/lang/de/app.php index d19bdb8..c99b0d3 100644 --- a/lang/de/app.php +++ b/lang/de/app.php @@ -65,5 +65,21 @@ return [ 'title' => 'Kontakt', 'content' => 'Schreiben Sie uns im Fediversum
oder per elektronischer Post an: Dezernat7Zustaendigkeit@C3Gov.De' - ] + ], + 'preapplication' => [ + 'title' => 'Vorantrag auf Reisepass gem. § 5 C3GovVerwV', + 'content' => 'Bitte wählen Sie eine temporäre Bezirksregion aus, um Ihren Vorantrag auf einen Reisepass zu stellen. +
Nachdem Sie den Vorantrag abgeschickt haben, erhalten Sie eine Bestätigung via elektronischer Post.', + 'noevent' => 'Es sind derzeit keine temporäre Bezirksregionen verfügbar.', + 'passport_type' => 'Pass-Typ', + 'passport' => [ + 'standard' => 'Hacker*innen-Reisepass', + 'kid' => 'Junghackerpass', + ], + 'mail' => 'Elektronische Postadresse', + 'firstname' => 'Vorname (Nick)', + 'lastname' => 'Nachname (optional)', + 'location' => 'Hackspace / Ort (optional)', + 'apply' => 'Vorantrag abschicken' + ] ]; diff --git a/lang/de/controller_messages.php b/lang/de/controller_messages.php new file mode 100644 index 0000000..33ab48f --- /dev/null +++ b/lang/de/controller_messages.php @@ -0,0 +1,27 @@ + [ + 'not_permitted' => 'Diese Aktion ist nicht zugelassen.', + 'email_in_use' => 'Diese elektronische Postadresse wird bereits verwendet.', + 'invalid_token' => 'Machen Sie die Musik aus.', + 'wrong_credentials' => 'Falsch. Einfach nur falsch.', + 'unknown_error' => 'Ein unbekannter Fehler ist aufgetreten.', + 'unknown_error_with_details' => 'Ein unbekannter Fehler ist aufgetreten: :error', + ], + 'PreApplicationController' => [ + 'success' => 'Voranmeldung erfolgreich abgeschickt. Bitte überprüfen Sie Ihr elektronisches Postfach.', + 'already_submitted' => 'Sie haben bereits einen Vorantrag abgeschickt.', + 'wrong_passport_type' => 'Falscher Passtyp', + 'invalid_email_format' => 'Ungültiges E-Mail-Format', + 'failed_to_process' => 'Fehler beim Verarbeiten der Voranmeldung: :error', + 'invalid_characters' => 'Ungültige Zeichen im Namen oder Ort', + 'event_not_found' => 'Chaos-Event nicht gefunden', + ], + 'BlogController' => [ + 'error_occurred' => 'Ein Fehler ist beim Verarbeiten des Blogs aufgetreten: :error', + ], + 'TickerController' => [ + 'error_occurred' => 'Ein Fehler ist beim Verarbeiten des Tickers aufgetreten: :error', + ], +]; diff --git a/lang/de/mail.php b/lang/de/mail.php new file mode 100644 index 0000000..41d63ec --- /dev/null +++ b/lang/de/mail.php @@ -0,0 +1,11 @@ + [ + 'subject' => 'Ihr Vorantrag auf einen Reisepass', + 'body1' => "Hallo,

Ihr Vorantrag wurde erfolgreich eingereicht.
Ihre Bearbeitungsnummer lautet: ", + 'body2' => "

Folgende Daten wurden übermittelt:
", + 'body3' => "

Bitte heben Sie diese E-Mail unbedingt auf.
Wenn möglich, drucken Sie das Formblatt 5, welches sich im E-Mail-Anhang befindet, aus. Tragen Sie Ihre Bearbeitungsnummer ein und bringen Sie es bei Abholung mit.", + 'signature' => "Mit freundlichen Grüßen,


Ihr C3Gov - Ihr Amt in der Bezirksregion CCC" + ] +]; diff --git a/lang/en/app.php b/lang/en/app.php index 5077923..bf1a49b 100644 --- a/lang/en/app.php +++ b/lang/en/app.php @@ -65,5 +65,21 @@ return [ 'title' => 'Contact', 'content' => 'Write us over in the fediverse
or via email to: Dezernat7Zustaendigkeit@C3Gov.De' - ] + ], + 'preapplication' => [ + 'title' => 'Pre-application for Passport according to § 5 C3GovVerwV', + 'content' => 'Please select a temporary district region to submit your pre-application for a passport. +
After you have submitted the pre-application, you will receive a confirmation by e-mail.', + 'noevent' => 'There are currently no temporary district regions available.', + 'passport_type' => 'Passport type', + 'passport' => [ + 'standard' => 'Hacker Passport', + 'kid' => 'Junghackerpass [German only]', + ], + 'mail' => 'E-mail address', + 'firstname' => 'First name (Nickname)', + 'lastname' => 'Last name (optional)', + 'location' => 'Hackspace / Location (optional)', + 'apply' => 'Submit pre-application' + ] ]; diff --git a/lang/en/controller_messages.php b/lang/en/controller_messages.php new file mode 100644 index 0000000..0834d3b --- /dev/null +++ b/lang/en/controller_messages.php @@ -0,0 +1,27 @@ + [ + 'not_permitted' => 'This action is not permitted.', + 'email_in_use' => 'This email address is already in use.', + 'invalid_token' => 'Invalid token.', + 'wrong_credentials' => 'Wrong credentials.', + 'unknown_error' => 'An unknown error occurred.', + 'unknown_error_with_details' => 'An unknown error occurred: :error', + ], + 'PreApplicationController' => [ + 'success' => 'Preapplication submitted successfully. Please check your email inbox.', + 'already_submitted' => 'You have already submitted a preapplication.', + 'wrong_passport_type' => 'Wrong passport type', + 'invalid_email_format' => 'Invalid email format', + 'failed_to_process' => 'Failed to process preapplication: :error', + 'invalid_characters' => 'Invalid characters in name or location', + 'event_not_found' => 'Chaos event not found', + ], + 'BlogController' => [ + 'error_occurred' => 'An error occurred while processing the blog: :error', + ], + 'TickerController' => [ + 'error_occurred' => 'An error occurred while processing the ticker: :error', + ], +]; diff --git a/lang/en/mail.php b/lang/en/mail.php new file mode 100644 index 0000000..3fd5c9f --- /dev/null +++ b/lang/en/mail.php @@ -0,0 +1,11 @@ + [ + 'subject' => 'Your pre-applicaction for issuing a hacker passport', + 'body1' => "Hello,

Your preliminary application has been successfully submitted.
Your reference number is:", + 'body2' => "

The following data has been submitted:
", + 'body3' => "

Please keep this email for your records.
If possible, print out form 5, which is attached to the email. Fill in your reference number and bring it with you when you pick up your hacker passport.", + 'signature' => "Sincerely,


Your C3Gov – Your office in the CCC district region" + ] +]; diff --git a/public/Dokumente b/public/Dokumente index 8a14d91..95ab7f9 160000 --- a/public/Dokumente +++ b/public/Dokumente @@ -1 +1 @@ -Subproject commit 8a14d9148446a2763f4f9813e33af43e38612515 +Subproject commit 95ab7f9b4ed2908af3dc9a0eaeb6293061752218 diff --git a/public/js/preapplication/preapplication.js b/public/js/preapplication/preapplication.js new file mode 100644 index 0000000..dd8bf50 --- /dev/null +++ b/public/js/preapplication/preapplication.js @@ -0,0 +1,43 @@ +$(function() { + $('select[autocomplete="off"]').each(function() { + let selectedValue = $(this).find('option[selected]').val(); + if (selectedValue) { + $(this).val(selectedValue); + } + }); +}); + +function vorantragAbschicken(){ + + let data = {}; + data.event = $('#eventSelect').val(); + data.passport_type = $('#passport_type').val(); + data.mail = $('#mail').val(); + data.first_name = $('#firstname').val(); + data.last_name = $('#lastname').val(); + data.location = $('#location').val(); + + console.log(data); + + $.ajax({ + type: "POST", + url: "/vorbeantragen/absenden", + data: data, + dataType: "json", + success:function(r){ + if(r.messageStatus === "success"){ + $('#mail').val(""); + $('#firstname').val(""); + $('#lastname').val(""); + $('#location').val(""); + alert(r.errorMessage); + } else { + alert(r.errorMessage); + } + }, + error:function(r, a, e){ + alert(e); + } + }); + +} diff --git a/resources/views/application/apply.blade.php b/resources/views/application/apply.blade.php index 0ea8b70..3ceef66 100644 --- a/resources/views/application/apply.blade.php +++ b/resources/views/application/apply.blade.php @@ -1,7 +1,46 @@ +@php use App\Models\ChaosEvents; @endphp @extends('layout.app') @section('scripts') + @endsection @section('content') +

{{ __('app.preapplication.title') }}

+ +

{!! __('app.preapplication.content') !!}

+ + @forelse (ChaosEvents::getActiveEvents() as $event) +
+ +
+ + + +
+ + + +
+ + +
+ + +
+ + +

+ +
+ + @empty +

{{ __('app.preapplication.noevent') }}

+ @endforelse @endsection diff --git a/resources/views/layout/app.blade.php b/resources/views/layout/app.blade.php index d0023cf..a698399 100644 --- a/resources/views/layout/app.blade.php +++ b/resources/views/layout/app.blade.php @@ -26,8 +26,8 @@
- {{ __('app.language.de') }} - {{ __('app.language.en') }} + {{ __('app.language.de') }} + {{ __('app.language.en') }}

diff --git a/resources/views/mail/preapplication.blade.php b/resources/views/mail/preapplication.blade.php new file mode 100644 index 0000000..aa3a8c5 --- /dev/null +++ b/resources/views/mail/preapplication.blade.php @@ -0,0 +1,8 @@ +{!! __('mail.preapplication.body1') !!} +{{ $pa->reference_number }} +{!! __('mail.preapplication.body2') !!} +{{ __('app.preapplication.firstname') }}: {{ $pa->first_name }}
+{{ __('app.preapplication.lastname') }}: {{ $pa->last_name }}
+{{ __('app.preapplication.location') }}: {{ $pa->location }}
+{!! __('mail.preapplication.body3') !!}

+{!! __('mail.preapplication.signature') !!} diff --git a/resources/views/ticker/edit.blade.php b/resources/views/ticker/edit.blade.php index af12bba..759f362 100644 --- a/resources/views/ticker/edit.blade.php +++ b/resources/views/ticker/edit.blade.php @@ -1,3 +1,4 @@ +@php use App\Models\TickerMessages; @endphp @extends('layout.app') @section('scripts') @@ -10,39 +11,37 @@
Hier kann der Ticker verändert werden.

Deutsch

-

+

-

+

-


+


-
+
-

+

Englisch

-

+

-

+

-


+


-
+
-

+

- - @endsection diff --git a/routes/web.php b/routes/web.php index 515f6fd..8e83e13 100644 --- a/routes/web.php +++ b/routes/web.php @@ -2,6 +2,7 @@ use App\Http\Controllers\BlogController; use App\Http\Controllers\LoginController; +use App\Http\Controllers\PreApplicationController; use App\Http\Controllers\WebsiteController; use App\Http\Controllers\TickerController; use Illuminate\Support\Facades\Route; @@ -14,7 +15,9 @@ Route::get('/kontakt', [WebsiteController::class, 'contact'])->name('contact'); Route::get('/impressum', [WebsiteController::class, 'imprint'])->name('imprint'); Route::get('/services', [WebsiteController::class, 'services'])->name('services'); Route::get('/neuigkeiten', [WebsiteController::class, 'news'])->name('news'); -Route::get('/beantragen', [WebsiteController::class, 'apply'])->name('apply'); + +Route::get('/vorbeantragen', [WebsiteController::class, 'apply'])->name('apply'); +Route::post('/vorbeantragen/absenden', [PreApplicationController::class, 'send']); Route::get('/intern/registrieren', [WebsiteController::class, 'showRegister']); Route::post('/intern/registrieren/abschicken', [LoginController::class, 'register']);