diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index b5d6720..480a667 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -15,7 +15,7 @@ jobs:
os: [ubuntu-latest, windows-latest]
php: [8.2, 8.1]
laravel: [10.*]
- stability: [prefer-lowest, prefer-stable]
+ stability: [prefer-stable]
include:
- laravel: 10.*
testbench: 8.*
diff --git a/README.md b/README.md
index 7d5ff56..0971990 100644
--- a/README.md
+++ b/README.md
@@ -11,90 +11,116 @@
-__Filament Turnstile__, is a plugin to help you implement the Cloudflare turnstile.
+**Filament Turnstile** is an essential plugin designed to seamlessly integrate Cloudflare's turnstile into your applications.
-This plugin uses [Laravel Turnstile](https://github.com/coderflexx/laravel-turnstile) Behind the scene, you can head to the page __README__ to learn more.
+This plugin uses [Laravel Turnstile](https://github.com/coderflexx/laravel-turnstile) under the hood. For detailed information, explore the [Laravel Turnstile README](https://github.com/coderflexx/laravel-turnstile).
## Installation
-You can install the package via composer:
-
+Install the package via Composer:
```bash
composer require coderflex/filament-turnstile
```
+For users still on **Filament V2**, install the package using:
+
+```bash
+composer require coderflex/filament-turnstil "^1.0"
+```
## Turnstile Keys
-To be able to use __Cloudflare Turnstile__, you need to get the `SiteKey`, and the `SecretKey` from your [Cloudflare dashboard](https://developers.cloudflare.com/turnstile/get-started/#get-a-sitekey-and-secret-key)
+To utilize **Cloudflare Turnstile**, obtain your `SiteKey` and `SecretKey` from your Cloudflare Dashboard.
-After Generating the __keys__, use `TURNSTILE_SITE_KEY`, and `TURNSTILE_SECRET_KEY` in your `.env` file
+Refer to the [documentation](https://developers.cloudflare.com/turnstile/get-started/#get-a-sitekey-and-secret-key) for detailed instructions.
-```.env
-TURNSTILE_SITE_KEY=2x00000000000000000000AB
-TURNSTILE_SECRET_KEY=2x0000000000000000000000000000000AA
+After generating the **keys**, include them in your `.env` file using the following format:
+
+```env
+TURNSTILE_SITE_KEY=1x00000000000000000000AA
+TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA
```
-If you want to test the widget, you can use the [Dummy site keys and secret keys](https://developers.cloudflare.com/turnstile/reference/testing/) that Cloudflare provides.
+For testing purposes, you can use [Dummy site keys and secret keys](https://developers.cloudflare.com/turnstile/reference/testing/) provided by Cloudflare.
## Usage
-The usage of this plugin, is really straight - forward. In your form, use the following code:
+Utilizing this plugin is incredibly straightforward. In your form, incorporate the following code:
```php
-...
use Coderflex\FilamentTurnstile\Forms\Components\Turnstile;
-...
- Turnstile::make('captcha')
- ->theme('auto')
- ->language('fr')
- ->size('normal'),
+Turnstile::make('captcha')
+ ->theme('auto') // accepts light, dark, auto
+ ->language('en-US') // see below
+ ->size('normal'), // accepts normal, compact
```
-The `Turnstile` field, has few options to use. You can learn more about them in [the Cloudflare configuration section](https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#configurations)
+For a list of supported languages, refer to the [supported languages section](https://developers.cloudflare.com/turnstile/reference/supported-languages/).
+
+The `Turnstile` field offers various options; you can learn more about them in [the Cloudflare configuration section](https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#configurations).
+
-## Real Life Example:
-In order to use __Turnstile__ captcha with the `Login` page in filament, use the following steps:
+## Real-Life Example:
-Create a new `App/Filament/Pages/Login.php` class
+To implement the **Turnstile** captcha with the `Login` page in Filament, follow these steps:
+
+Create a new `App/Filament/Pages/Auth/Login.php` class:
```php
-
+ */
+ protected function getForms(): array
{
- return array_merge(
- parent::getFormSchema(),
- [
- Turnstile::make('cf-captcha')
- ->theme('auto')
- ->language('en-US')
- ->size('normal'),
- ]
- );
+ return [
+ 'form' => $this->form(
+ $this->makeForm()
+ ->schema([
+ $this->getEmailFormComponent(),
+ $this->getPasswordFormComponent(),
+ $this->getRememberFormComponent(),
+ Turnstile::make('captcha')
+ ->label('Captcha')
+ ->theme('auto'),
+ ])
+ ->statePath('data'),
+ ),
+ ];
}
}
```
-Then override the `Login` class in the `filament.php` config file.
+Then, override the `login()` method in your `PanelProvider` (e.g., `AdminPanelProvider`):
```php
- return [
- ....
- 'auth' => [
- 'guard' => env('FILAMENT_AUTH_GUARD', 'web'),
- 'pages' => [
- 'login' => \App\Filament\Pages\Login::class,
- ],
- ],
- ...
- ]
+namespace App\Providers\Filament;
+
+use App\Filament\Auth\Login;
+use Filament\Panel;
+use Filament\PanelProvider;
+
+class AdminPanelProvider extends PanelProvider
+{
+ public function panel(Panel $panel): Panel
+ {
+ return $panel
+ ->default()
+ ->id('admin')
+ ->path('admin')
+ ->login(Login::class); // override the login page class.
+ ...
+ }
+}
```
## Testing
diff --git a/art/login_screen.png b/art/login_screen.png
index cf58fa5..d984f30 100644
Binary files a/art/login_screen.png and b/art/login_screen.png differ
diff --git a/composer.json b/composer.json
index 9f73980..fe087f4 100644
--- a/composer.json
+++ b/composer.json
@@ -26,7 +26,7 @@
"spatie/laravel-package-tools": "^1.14.0"
},
"require-dev": {
- "filament/filament": "^2.17",
+ "filament/filament": "^3.0",
"laravel/pint": "^1.0",
"nunomaduro/collision": "^7.9",
"nunomaduro/larastan": "^2.0.1",
diff --git a/resources/views/components/turnstile.blade.php b/resources/views/components/turnstile.blade.php
index e359b12..278133b 100644
--- a/resources/views/components/turnstile.blade.php
+++ b/resources/views/components/turnstile.blade.php
@@ -1,48 +1,44 @@
-
-
+
+
-
-
-
-
- @push('scripts')
-
-
+ })()"
+ >
+
+
+
+
+ @push('scripts')
+
@endpush
\ No newline at end of file
diff --git a/src/FilamentTurnstileServiceProvider.php b/src/FilamentTurnstileServiceProvider.php
index cfe6ce4..02fa4a1 100644
--- a/src/FilamentTurnstileServiceProvider.php
+++ b/src/FilamentTurnstileServiceProvider.php
@@ -2,10 +2,10 @@
namespace Coderflex\FilamentTurnstile;
-use Filament\PluginServiceProvider;
use Spatie\LaravelPackageTools\Package;
+use Spatie\LaravelPackageTools\PackageServiceProvider;
-class FilamentTurnstileServiceProvider extends PluginServiceProvider
+class FilamentTurnstileServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
diff --git a/src/Forms/Components/Turnstile.php b/src/Forms/Components/Turnstile.php
index 309d11b..bd27458 100644
--- a/src/Forms/Components/Turnstile.php
+++ b/src/Forms/Components/Turnstile.php
@@ -7,6 +7,8 @@
class Turnstile extends Field
{
+ protected string $viewIdentifier = 'turnstile';
+
protected string $view = 'turnstile::components.turnstile';
protected string $theme = 'auto';
@@ -21,7 +23,9 @@ protected function setUp(): void
$this->label('');
- $this->rules(['required', new TurnstileCheck()]);
+ $this->required();
+
+ $this->rule(new TurnstileCheck());
$this->dehydrated(false);
}
@@ -47,17 +51,26 @@ public function language(string $language): static
return $this;
}
- public function getTheme(): string
+ /**
+ * @return string
+ */
+ public function getTheme()
{
return $this->evaluate($this->theme);
}
- public function getSize(): string
+ /**
+ * @return string
+ */
+ public function getSize()
{
return $this->evaluate($this->size);
}
- public function getLanguage(): string
+ /**
+ * @return string
+ */
+ public function getLanguage()
{
return $this->evaluate($this->language);
}
diff --git a/tests/Database/Factories/UserFactory.php b/tests/Database/Factories/UserFactory.php
new file mode 100644
index 0000000..9e4518a
--- /dev/null
+++ b/tests/Database/Factories/UserFactory.php
@@ -0,0 +1,10 @@
+id();
+ $table->string('name');
+ $table->string('email');
+ $table->text('content');
+ $table->timestamps();
+ });
+ }
+};
diff --git a/tests/Database/Migrations/create_users_table.php b/tests/Database/Migrations/create_users_table.php
new file mode 100644
index 0000000..57f0097
--- /dev/null
+++ b/tests/Database/Migrations/create_users_table.php
@@ -0,0 +1,21 @@
+id();
+ $table->string('name');
+ $table->string('email')->unique();
+ $table->timestamp('email_verified_at')->nullable();
+ $table->string('password');
+ $table->rememberToken();
+ $table->timestamps();
+ });
+ }
+};
diff --git a/tests/Fixtures/ContactUs.php b/tests/Fixtures/ContactUs.php
new file mode 100644
index 0000000..3081880
--- /dev/null
+++ b/tests/Fixtures/ContactUs.php
@@ -0,0 +1,58 @@
+form->fill();
+ }
+
+ public function form(Form $form): Form
+ {
+ return $form;
+ }
+
+ protected function getForms(): array
+ {
+ return [
+ 'form' => $this->form(
+ $this->makeForm()
+ ->schema([
+ Forms\Components\TextInput::make('name')
+ ->label('Name')
+ ->required(),
+ Forms\Components\TextInput::make('email')
+ ->label('Email')
+ ->required(),
+ Forms\Components\TextInput::make('content')
+ ->label('Content')
+ ->required(),
+ Turnstile::make('cf-captcha')
+ ->theme('auto'),
+ ])
+ )
+ ->statePath('data')
+ ->model(Contact::class),
+ ];
+ }
+
+ public function send()
+ {
+ Contact::create($this->form->getState());
+ }
+
+ public function render()
+ {
+ return 'fixtures.contact-us';
+ }
+}
diff --git a/tests/Fixtures/Login.php b/tests/Fixtures/Login.php
deleted file mode 100644
index ae2d1b8..0000000
--- a/tests/Fixtures/Login.php
+++ /dev/null
@@ -1,19 +0,0 @@
-theme('auto'),
- ]
- );
- }
-}
diff --git a/tests/Models/Contact.php b/tests/Models/Contact.php
new file mode 100644
index 0000000..c45a7d5
--- /dev/null
+++ b/tests/Models/Contact.php
@@ -0,0 +1,10 @@
+ 'Coderflex\\FilamentTurnstile\\Tests\\Database\\Factories\\'.class_basename($modelName).'Factory'
+ );
+
config()->set('app.key', '6rE9Nz59bGRbeMATftriyQjrpF7DcOQm');
- $this->registerLivewireComponents();
+ $this->setCurrentFilamentPanel();
}
protected function getPackageProviders($app)
{
return [
+ ActionsServiceProvider::class,
+ BladeCaptureDirectiveServiceProvider::class,
+ BladeHeroiconsServiceProvider::class,
+ BladeIconsServiceProvider::class,
FilamentServiceProvider::class,
FormsServiceProvider::class,
+ InfolistsServiceProvider::class,
LivewireServiceProvider::class,
+ NotificationsServiceProvider::class,
SupportServiceProvider::class,
+ TablesServiceProvider::class,
+ WidgetsServiceProvider::class,
FilamentTurnstileServiceProvider::class,
+ TurnstilePanelProvider::class,
];
}
public function getEnvironmentSetUp($app)
{
- //
+ config()->set('database.default', 'testing');
+
+ $app['config']->set('view.paths', [
+ ...$app['config']->get('view.paths'),
+ __DIR__.'/resources/views',
+ ]);
+
+ $migrations = [
+ include __DIR__.'/Database/Migrations/create_users_table.php',
+ include __DIR__.'/Database/Migrations/create_contacts_table.php',
+ ];
+
+ collect($migrations)->each(
+ fn ($migration) => $migration->up()
+ );
}
- protected function registerLivewireComponents(): void
+ protected function setCurrentFilamentPanel(): void
{
- Livewire::component('login', Login::class);
+ Filament::setCurrentPanel(
+ Filament::getPanel('turnstile')
+ );
}
}
diff --git a/tests/TurnstilePanelProvider.php b/tests/TurnstilePanelProvider.php
new file mode 100644
index 0000000..aa42894
--- /dev/null
+++ b/tests/TurnstilePanelProvider.php
@@ -0,0 +1,43 @@
+id('turnstile')
+ ->login()
+ ->registration()
+ ->resources([])
+ ->pages([])
+ ->middleware([
+ EncryptCookies::class,
+ AddQueuedCookiesToResponse::class,
+ StartSession::class,
+ AuthenticateSession::class,
+ ShareErrorsFromSession::class,
+ VerifyCsrfToken::class,
+ SubstituteBindings::class,
+ DisableBladeIconComponents::class,
+ DispatchServingFilamentEvent::class,
+ ])
+ ->authMiddleware([
+ Authenticate::class,
+ ]);
+ }
+}
diff --git a/tests/TurnstileTest.php b/tests/TurnstileTest.php
index 2394942..50d5c8a 100644
--- a/tests/TurnstileTest.php
+++ b/tests/TurnstileTest.php
@@ -1,9 +1,96 @@
assertFormFieldExists('cf-captcha');
+it('can render contact page', function () {
+ livewire(ContactUs::class)
+ ->assertSuccessful();
+});
+
+test('contact page has captcha field', function () {
+ livewire(ContactUs::class)
+ ->assertFormFieldExists('cf-captcha', 'form');
+});
+
+it('can return success response', function () {
+ /**
+ * Setting Turnstile keys to always pass the request
+ *
+ * @link https://developers.cloudflare.com/turnstile/reference/testing/#dummy-sitekeys-and-secret-keys
+ */
+ Config::set('turnstile', [
+ 'turnstile_site_key' => '1x00000000000000000000AA',
+ 'turnstile_secret_key' => '1x0000000000000000000000000000000AA',
+ ]);
+
+ $response = LaravelTurnstile::validate('XXXX.DUMMY.TOKEN.XXXX');
+
+ expect($response['success'])->toBeTrue();
+});
+
+it('can does not return success response', function () {
+ /**
+ * Setting Turnstile keys to always block the request
+ *
+ * @link https://developers.cloudflare.com/turnstile/reference/testing/#dummy-sitekeys-and-secret-keys
+ */
+ Config::set('turnstile', [
+ 'turnstile_site_key' => '2x00000000000000000000AB',
+ 'turnstile_secret_key' => '2x0000000000000000000000000000000AA',
+ ]);
+
+ $response = LaravelTurnstile::validate('XXXX.DUMMY.TOKEN.XXXX');
+
+ expect($response['success'])->toBeFalse();
+});
+
+it('can send a message', function () {
+ /**
+ * In this context, Alpine.js didn't function as expected due to the need to pass the `cf-captcha` field.
+ * The value of the mentioned key is dynamically determined by the response from Cloudflare (CF) in the UI.
+ * For more information, refer to the Cloudflare Turnstile documentation:
+ *
+ * @link https://developers.cloudflare.com/turnstile/
+ */
+ Config::set('turnstile', [
+ 'turnstile_site_key' => '1x00000000000000000000AA',
+ 'turnstile_secret_key' => '1x0000000000000000000000000000000AA',
+ ]);
+
+ livewire(ContactUs::class)
+ ->fillForm([
+ 'name' => 'John Doe',
+ 'email' => 'john@example.com',
+ 'content' => 'This is a simple message',
+ 'cf-captcha' => 'XXXX.DUMMY.TOKEN.XXXX',
+ ])
+ ->call('send')
+ ->assertHasNoFormErrors();
+
+ expect(Contact::get())
+ ->toHaveCount(1);
+});
+
+it('cannot send a message', function () {
+ Config::set('turnstile', [
+ 'turnstile_site_key' => '2x00000000000000000000AB',
+ 'turnstile_secret_key' => '2x0000000000000000000000000000000AA',
+ ]);
+
+ livewire(ContactUs::class)
+ ->fillForm([
+ 'name' => 'John Doe',
+ 'email' => 'john@example.com',
+ 'content' => 'This is a simple message',
+ ])
+ ->call('send')
+ ->assertHasFormErrors(['cf-captcha']);
+
+ expect(Contact::get())
+ ->toHaveCount(0);
});
diff --git a/tests/resources/views/fixtures/contact-us.blade.php b/tests/resources/views/fixtures/contact-us.blade.php
new file mode 100644
index 0000000..fccf563
--- /dev/null
+++ b/tests/resources/views/fixtures/contact-us.blade.php
@@ -0,0 +1,7 @@
+