diff --git a/app/Actions/Settings/SettingPopulateDefaultData.php b/app/Actions/Settings/SettingPopulateDefaultData.php new file mode 100644 index 000000000..e556ace45 --- /dev/null +++ b/app/Actions/Settings/SettingPopulateDefaultData.php @@ -0,0 +1,15 @@ +form->fill(setting()->all()); + } + + public function form(Form $form): Form + { + return $form + ->schema([ + Section::make() + ->schema([ + Checkbox::make('allow_registration') + ->label('Allow Registration') + ->helperText('Allow public to register on the site.'), + Checkbox::make('must_verify_email') + ->label('Must Verify Email') + ->helperText('Require users to verify their email address before they can log in.'), + ]) + ->columns(1), + Actions::make([ + Action::make('save') + ->label('Save') + ->successNotificationTitle('Saved!') + ->failureNotificationTitle('Data could not be saved.') + ->action(function (Action $action) { + $formData = $this->form->getState(); + try { + SettingUpdateAction::run($formData); + $action->sendSuccessNotification(); + } catch (\Throwable $th) { + $action->failure(); + // $action->sendFailureNotification(); + } + }), + ])->alignLeft(), + ]) + ->statePath('formData'); + } + + public function render() + { + return view('administration.livewire.access-setting'); + } +} diff --git a/app/Administration/Livewire/DateAndTimeSetting.php b/app/Administration/Livewire/DateAndTimeSetting.php new file mode 100644 index 000000000..d61efd7d3 --- /dev/null +++ b/app/Administration/Livewire/DateAndTimeSetting.php @@ -0,0 +1,104 @@ +form->fill(setting()->all()); + } + + public function form(Form $form): Form + { + $now = now()->hours(16); + + return $form + ->statePath('formData') + ->schema([ + Section::make('Date and Time Formats') + ->description(new HtmlString(<<<'HTML' + Please select the desired format for dates and times. You may also enter a custom format using + special format characters. + HTML)) + ->schema([ + Radio::make('format.date') + ->options(fn () => collect([ + 'F j, Y', + 'F j Y', + 'j F Y', + 'Y F j', + ])->mapWithKeys(fn ($format) => [$format => $now->format($format)])), + Radio::make('format.time') + ->options(fn () => collect([ + 'h:i A', + 'g:ia', + 'H:i', + ])->mapWithKeys(fn ($format) => [$format => $now->format($format)])), + ]), + Actions::make([ + Action::make('save') + ->successNotificationTitle('Saved!') + ->action(function (Action $action) { + $formData = $this->form->getState(); + try { + SettingUpdateAction::run($formData); + + $action->sendSuccessNotification(); + } catch (\Throwable $th) { + $action->sendFailureNotification(); + } + }), + ])->alignLeft(), + ]); + } + + public function render() + { + return view('administration.livewire.access-setting'); + } +} diff --git a/app/Administration/Pages/SiteSettings.php b/app/Administration/Pages/SiteSettings.php index f6ce309db..b88583449 100644 --- a/app/Administration/Pages/SiteSettings.php +++ b/app/Administration/Pages/SiteSettings.php @@ -4,6 +4,8 @@ use App\Actions\Settings\SettingUpdateAction; use App\Actions\Site\SiteUpdateAction; +use App\Administration\Livewire\AccessSetting; +use App\Administration\Livewire\DateAndTimeSetting; use App\Administration\Livewire\EmailSetting; use App\Infolists\Components\BladeEntry; use App\Infolists\Components\LivewireEntry; @@ -32,19 +34,12 @@ class SiteSettings extends Page implements HasForms, HasInfolists protected static string $view = 'administration.pages.site-settings'; - public array $systemFormData = []; - public array $informationFormData = []; public array $appearanceFormData = []; public function mount() { - $this->systemForm->fill([ - 'format' => setting('format'), - 'privacy_statement' => setting('privacy_statement'), - ]); - $this->informationForm->fill([ 'meta' => app()->getSite()->getAllMeta()->toArray(), ]); @@ -74,8 +69,25 @@ public function infolist(Infolist $infolist): Infolist ]), Tabs\Tab::make('System') ->schema([ - BladeEntry::make('general') - ->blade('{{ $this->systemForm }}'), + // BladeEntry::make('general') + // ->blade('{{ $this->systemForm }}'), + VerticalTabs\Tabs::make() + ->tabs([ + VerticalTabs\Tab::make('Access Options') + ->icon('heroicon-o-information-circle') + ->schema([ + LivewireEntry::make('access_setting') + ->livewire(AccessSetting::class) + ->lazy(), + ]), + VerticalTabs\Tab::make('Date & Time') + ->icon('heroicon-o-clock') + ->schema([ + LivewireEntry::make('date_and_time') + ->livewire(DateAndTimeSetting::class) + ->lazy(), + ]), + ]), ]), Tabs\Tab::make('E-Mail') ->schema([ @@ -92,7 +104,6 @@ protected function getForms(): array { return [ 'informationForm', - 'systemForm', 'appearanceForm', ]; } @@ -142,49 +153,4 @@ public function informationForm(Form $form): Form ->columns(2), ]); } - - public function systemForm(Form $form): Form - { - $now = now()->hours(16); - - return $form - ->statePath('systemFormData') - ->schema([ - Section::make('Date and Time Formats') - ->description(new HtmlString(<<<'HTML' - Please select the desired format for dates and times. You may also enter a custom format using - special format characters. - HTML)) - ->schema([ - Radio::make('format.date') - ->options(fn () => collect([ - 'F j, Y', - 'F j Y', - 'j F Y', - 'Y F j', - ])->mapWithKeys(fn ($format) => [$format => $now->format($format)])), - Radio::make('format.time') - ->options(fn () => collect([ - 'h:i A', - 'g:ia', - 'H:i', - ])->mapWithKeys(fn ($format) => [$format => $now->format($format)])), - ]) - ->aside(), - Actions::make([ - Action::make('save') - ->successNotificationTitle('Saved!') - ->action(function (Action $action) { - try { - SettingUpdateAction::run($this->systemForm->getState()); - - $action->sendSuccessNotification(); - } catch (\Throwable $th) { - $action->sendFailureNotification(); - } - }), - ])->alignRight(), - ]); - } } diff --git a/app/Http/Middleware/MustVerifyEmail.php b/app/Http/Middleware/MustVerifyEmail.php new file mode 100644 index 000000000..81a1b76c9 --- /dev/null +++ b/app/Http/Middleware/MustVerifyEmail.php @@ -0,0 +1,33 @@ +user()){ + return redirect()->route('livewirePageGroup.website.pages.login'); + } + + if(!$request->user()->hasVerifiedEmail()){ + return redirect()->route('livewirePageGroup.website.pages.email-verification'); + } + + + return $next($request); + } +} diff --git a/app/Http/Middleware/Panel/PanelAuthenticate.php b/app/Http/Middleware/Panel/PanelAuthenticate.php index 51f29fcaa..7d554ff74 100644 --- a/app/Http/Middleware/Panel/PanelAuthenticate.php +++ b/app/Http/Middleware/Panel/PanelAuthenticate.php @@ -6,6 +6,7 @@ class PanelAuthenticate extends Authenticate { + protected function redirectTo($request): ?string { return route('livewirePageGroup.website.pages.login'); diff --git a/app/Models/User.php b/app/Models/User.php index c41860297..6431597d6 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,7 +2,6 @@ namespace App\Models; -// use Illuminate\Contracts\Auth\MustVerifyEmail; use App\Mail\Templates\VerifyUserEmail; use App\Models\Enums\ConferenceStatus; diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index b6a82b1a8..1588a7c7a 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,15 +2,16 @@ namespace App\Providers; +use Illuminate\Support\Str; use App\Managers\BlockManager; use App\Managers\MetaTagManager; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\URL; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Storage; use Illuminate\Support\ServiceProvider; +use Illuminate\Database\Eloquent\Relations\Relation; class AppServiceProvider extends ServiceProvider { @@ -35,6 +36,21 @@ public function boot(): void { $this->setupModel(); $this->setupStorage(); + $this->extendStr(); + } + + + protected function extendStr(){ + Str::macro('maskEmail', function ($email) { + $mail_parts = explode("@", $email); + $domain_parts = explode('.', $mail_parts[1]); + + $mail_parts[0] = Str::mask($mail_parts[0], '*', 2, strlen($mail_parts[0])); // show first 2 letters and last 1 letter + $domain_parts[0] = Str::mask($domain_parts[0], '*', 2, strlen($domain_parts[0])); // same here + $mail_parts[1] = implode('.', $domain_parts); + + return implode("@", $mail_parts); + }); } protected function setupModel() diff --git a/app/Providers/Filament/PanelProvider.php b/app/Providers/Filament/PanelProvider.php index 8518d2329..1d9a9b118 100644 --- a/app/Providers/Filament/PanelProvider.php +++ b/app/Providers/Filament/PanelProvider.php @@ -2,6 +2,7 @@ namespace App\Providers\Filament; +use App\Http\Middleware\MustVerifyEmail; use App\Http\Middleware\Panel\PanelAuthenticate; use App\Http\Middleware\Panel\TenantConference; use App\Models\Conference; @@ -143,6 +144,7 @@ public static function getAuthMiddleware(): array { return [ PanelAuthenticate::class, + MustVerifyEmail::class ]; } diff --git a/app/Website/Pages/EmailVerification.php b/app/Website/Pages/EmailVerification.php new file mode 100644 index 000000000..bc93d12bd --- /dev/null +++ b/app/Website/Pages/EmailVerification.php @@ -0,0 +1,56 @@ +route('filament.panel.tenant'); + } + + if(!auth()->check()){ + return redirect()->route('livewirePageGroup.website.pages.login'); + } + + if(auth()->user()->hasVerifiedEmail()){ + return redirect()->route('filament.panel.tenant'); + } + } + + public function getBreadcrumbs() : array + { + return []; + } + + + public function sendEmailVerificationLink() + { + if(auth()->user()->hasVerifiedEmail()){ + return redirect()->route('filament.panel.tenant'); + } + + try { + $this->rateLimit(1); + } catch (TooManyRequestsException $exception) { + $this->addError('email', __('email.verification.throttle', [ + 'seconds' => $exception->secondsUntilAvailable, + ])); + + return null; + } + + auth()->user()->sendEmailVerificationNotification(); + + session()->flash('success', true); + } +} diff --git a/app/Website/Pages/Register.php b/app/Website/Pages/Register.php index e9c2aaee0..9f44b3774 100644 --- a/app/Website/Pages/Register.php +++ b/app/Website/Pages/Register.php @@ -44,6 +44,9 @@ class Register extends Page public function mount() { + if (Filament::auth()->check()) { + $this->redirect(Filament::getUrl(), navigate: false); + } } public function getBreadcrumbs(): array diff --git a/config/services.php b/config/services.php index 0ace530e8..a84bcaaa9 100644 --- a/config/services.php +++ b/config/services.php @@ -31,4 +31,9 @@ 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), ], + 'recaptcha' => [ + 'key' => env('RECAPTCHA_SITE_KEY'), + 'secret' => env('RECAPTCHA_SECRET_KEY'), + ], + ]; diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 1d87b91d1..b67ed85a6 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -25,7 +25,6 @@ public function definition(): array 'email_verified_at' => now(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), - 'country' => 'id', ]; } diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index 302a22e94..3dac2b7b8 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -17,7 +17,6 @@ public function up(): void $table->string('family_name')->nullable(); $table->string('public_name')->nullable(); $table->string('email')->unique(); - $table->string('country')->nullable(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); diff --git a/helpers/helpers.php b/helpers/helpers.php index 0bd394a5e..d3d669a01 100644 --- a/helpers/helpers.php +++ b/helpers/helpers.php @@ -41,4 +41,4 @@ function get_navigation_link(?string $type, string $default = '#'): string default => $default, }; } -} +} \ No newline at end of file diff --git a/lang/en/auth.php b/lang/en/auth.php index 6598e2c06..26c80b83f 100644 --- a/lang/en/auth.php +++ b/lang/en/auth.php @@ -16,5 +16,4 @@ 'failed' => 'These credentials do not match our records.', 'password' => 'The provided password is incorrect.', 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', - ]; diff --git a/lang/en/email.php b/lang/en/email.php new file mode 100644 index 000000000..f96b8fa3c --- /dev/null +++ b/lang/en/email.php @@ -0,0 +1,8 @@ + [ + 'throttle' => 'Please try again in :seconds seconds.' + ] + +]; \ No newline at end of file diff --git a/public/build/manifest.json b/public/build/manifest.json index 95cdd1eaf..21793c26c 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -14,7 +14,7 @@ "src": "resources/panel/js/panel.js" }, "resources/website/css/website.css": { - "file": "assets/website-245dc049.css", + "file": "assets/website-dc2f230e.css", "isEntry": true, "src": "resources/website/css/website.css" }, diff --git a/resources/views/administration/livewire/access-setting.blade.php b/resources/views/administration/livewire/access-setting.blade.php new file mode 100644 index 000000000..4bd270eee --- /dev/null +++ b/resources/views/administration/livewire/access-setting.blade.php @@ -0,0 +1,3 @@ +
+ {{ $this->form }} +
diff --git a/resources/views/website/pages/email-verification.blade.php b/resources/views/website/pages/email-verification.blade.php new file mode 100644 index 000000000..98e93ee9c --- /dev/null +++ b/resources/views/website/pages/email-verification.blade.php @@ -0,0 +1,25 @@ + +
+

Verify your email

+ @if (session('success')) +
+ + Email verification link sent successfully. +
+ @endif + @error('email') +
+ + {{ $message }} +
+ @enderror +

Almost there! We've sent a verification email to {{ Str::maskEmail(auth()->user()->email) }}.

+

You need to verify your email address to log into Leconfe.

+
+ +
+
+
diff --git a/resources/views/website/pages/register.blade.php b/resources/views/website/pages/register.blade.php index a607c37c4..1a6b34506 100644 --- a/resources/views/website/pages/register.blade.php +++ b/resources/views/website/pages/register.blade.php @@ -2,7 +2,7 @@

{{ $this->getTitle() }}

- + @if(setting('allow_registration'))
@@ -92,7 +92,7 @@
@@ -113,5 +113,8 @@
+ @else +

This conference is currently closing user registrations

+ @endif
- + \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index da7d2b03a..a817b15e2 100644 --- a/routes/web.php +++ b/routes/web.php @@ -30,18 +30,8 @@ return $storage->download($path); })->where('path', '.*')->name('local.temp'); -Route::get('/email/verify', function () { - return view('auth.verify-email'); -})->middleware('auth')->name('verification.notice'); - Route::get('/email/verify/{id}/{hash}', function (EmailVerificationRequest $request) { $request->fulfill(); return redirect()->route('filament.panel.tenant'); -})->middleware(['auth', 'signed'])->name('verification.verify'); - -Route::post('/email/verification-notification', function (Request $request) { - $request->user()->sendEmailVerificationNotification(); - - return back()->with('message', 'Verification link sent!'); -})->middleware(['auth', 'throttle:6,1'])->name('verification.send'); +})->middleware(['auth', 'signed'])->name('verification.verify'); \ No newline at end of file diff --git a/stubs/livewire-page-group/PageView.stub b/stubs/livewire-page-group/PageView.stub index 8a19293e7..e0e19b49e 100644 --- a/stubs/livewire-page-group/PageView.stub +++ b/stubs/livewire-page-group/PageView.stub @@ -3,4 +3,4 @@

{{$this->getTitle()}}

- \ No newline at end of file + \ No newline at end of file