diff --git a/composer.json b/composer.json
index 3358a42..4dea866 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,7 @@
{
"$schema": "https://getcomposer.org/schema.json",
"name": "cleverogre/ogre-captcha",
- "version": "1.0.1",
+ "version": "1.1.0",
"title": "Ogre CAPTCHA",
"description": "Protect your site with CAPTCHA",
"author": "CleverOgre",
diff --git a/composer.lock b/composer.lock
index cc39dbd..2896b32 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "1daff9d4452a2ff8f159fea23468a225",
+ "content-hash": "49f5e6f919f33ce6a3edc2992620a64a",
"packages": [
{
"name": "cleverogre/plugin-framework",
@@ -59,7 +59,7 @@
"source": {
"type": "git",
"url": "git@git.cleverogre.com:cleverogre/plugin-update-checker.git",
- "reference": "010b7cf90b5df60d6d98256d48594ae423b951b7"
+ "reference": "b05c3dd4cf01f45544358671018dd5b5851cd079"
},
"require": {
"composer/installers": "~1.0",
@@ -69,7 +69,7 @@
"default-branch": true,
"type": "wordpress-plugin",
"extra": {
- "installer-name": "update-plugin-checker"
+ "installer-name": "plugin-update-checker"
},
"license": [
"MIT"
@@ -90,7 +90,7 @@
"theme updates",
"wordpress"
],
- "time": "2026-06-05T20:17:06+00:00"
+ "time": "2026-06-05T22:05:11+00:00"
},
{
"name": "cleverogre/updatepulse-updater",
diff --git a/inc/abstract-location.php b/inc/abstract-location.php
index e2e94c2..4a2eded 100644
--- a/inc/abstract-location.php
+++ b/inc/abstract-location.php
@@ -20,9 +20,6 @@ defined('ABSPATH') || exit;
abstract class Location {
use Singleton;
- public const ERROR_EMPTY_TOKEN = 'empty_captcha_token';
- public const ERROR_INVALID_CAPTCHA = 'invalid_captcha_result';
-
protected string $name;
protected function __construct(string $name) {
@@ -54,31 +51,22 @@ abstract class Location {
Widget::render();
}
- public function process(WP_Error $errors):void {
- if (!Settings::is_connected()) return;
-
- $token = Widget::get_token();
- if (empty($token)) {
- $errors->add(self::ERROR_EMPTY_TOKEN, Plugin::__('Error: Please complete CAPTCHA verification.'));
- return;
- }
-
- if (!API::verify($token)) {
- $errors->add(self::ERROR_INVALID_CAPTCHA, Plugin::__('Error: Your answer was incorrect - please try again.'));
- return;
- }
+ public function process(WP_Error|null $errors = null):void {
+ API::process(Widget::get_token(), $errors);
}
protected function print_errors(WP_Error $errors):void {
if (!$errors->has_errors()) return;
- ob_start();
- echo '
';
+
+ $messages = [];
foreach ($errors->get_error_codes() as $code) {
foreach ($errors->get_error_messages($code) as $message) {
- echo '- ' . wp_kses($message, ['strong']) . '
';
+ $messages[] = $message;
}
}
- echo '
';
+
+ ob_start();
+ echo '- ' . implode('
- ', $messages) . '
';
wp_die(ob_get_clean());
}
diff --git a/inc/class-api.php b/inc/class-api.php
index a39f756..cc39646 100644
--- a/inc/class-api.php
+++ b/inc/class-api.php
@@ -10,12 +10,17 @@
namespace Ogre\Captcha;
+use Ogre\Captcha as Plugin;
use Ogre\Captcha\Settings;
+use WP_Error;
defined('ABSPATH') || exit;
final class API {
+ public const ERROR_EMPTY_TOKEN = 'empty_captcha_token';
+ public const ERROR_INVALID_CAPTCHA = 'invalid_captcha_result';
+
private static function transaction(string $url, array $data):array|string|bool {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POSTFIELDS, wp_json_encode($data));
@@ -52,4 +57,17 @@ final class API {
return is_bool($result) ? $result : false;
}
+ public static function process(string $token, WP_Error|null $errors = null): WP_Error {
+ if (is_null($errors)) $errors = new WP_Error();
+ if (!Settings::is_connected()) return $errors;
+
+ if (empty($token)) {
+ $errors->add(self::ERROR_EMPTY_TOKEN, Plugin::__('Please complete CAPTCHA verification.'));
+ } else if (!API::verify($token)) {
+ $errors->add(self::ERROR_INVALID_CAPTCHA, Plugin::__('Your answer was incorrect - please try again.'));
+ }
+
+ return $errors;
+ }
+
}
diff --git a/inc/class-settings.php b/inc/class-settings.php
index 33210f7..68e99a7 100644
--- a/inc/class-settings.php
+++ b/inc/class-settings.php
@@ -44,6 +44,16 @@ final class Settings {
return $data;
}
+ private static function set(string $key, $value = null):void {
+ $data = self::get();
+ if (is_null($value) && isset($data[$key])) {
+ unset($data[$key]);
+ } else {
+ $data[$key] = $value;
+ }
+ update_option(Plugin::get_id(), $data);
+ }
+
private static function is_checked(string $name, string $value):bool {
return in_array($value, (array) self::get($name));
}
diff --git a/inc/gravityforms/class-addon.php b/inc/gravityforms/class-addon.php
new file mode 100644
index 0000000..64c0837
--- /dev/null
+++ b/inc/gravityforms/class-addon.php
@@ -0,0 +1,142 @@
+_version = Plugin::get_version();
+ $this->_slug = Plugin::get_id();
+ $this->_path = sprintf(
+ '%1$s/%1$s.php',
+ Plugin::get_id()
+ );
+ $this->_full_path = Plugin::get_file();
+ $this->_title = sprintf(
+ Plugin::__('%s Add-On'),
+ Plugin::get_title()
+ );
+ $this->_short_title = Plugin::get_title();
+ }
+
+ public function init() {
+ add_filter('gform_submit_button', [$this, 'submit_button'], 10, 2);
+ add_filter('gform_validation', [$this, 'validation'], 10, 2);
+ }
+
+ public function get_menu_icon() {
+ return 'gform-icon--lock';
+ }
+
+ public function plugin_settings_fields() {
+ return [
+ [
+ 'title' => esc_html(sprintf(
+ Plugin::__('%s Settings'),
+ Plugin::get_title()
+ )),
+ 'description' => sprintf(
+ Plugin::__('Looking to configure your CAPTCHA connection? You can find the settings on this page.'),
+ esc_url(Settings::get_url())
+ ),
+ 'fields' => [
+ [
+ 'label' => sprintf(
+ Plugin::esc_html__('Enable %s'),
+ Plugin::get_title()
+ ),
+ 'type' => 'checkbox',
+ 'name' => 'enabled',
+ 'choices' => [
+ [
+ 'label' => Plugin::esc_html__('Enable CAPTCHA on all forms by default.'),
+ 'name' => 'enabled',
+ ]
+ ]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ public function form_settings_fields($form) {
+ return [
+ [
+ 'title' => esc_html(sprintf(
+ Plugin::__('%s Settings'),
+ Plugin::get_title()
+ )),
+ 'fields' => [
+ [
+ 'type' => 'checkbox',
+ 'name' => 'disabled',
+ 'choices' => [
+ [
+ 'label' => Plugin::esc_html__('Disable CAPTCHA for this form.'),
+ 'name' => 'disabled',
+ ]
+ ]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ public function is_enabled(array|null $form = null): bool {
+ $plugin_enabled = (bool) $this->get_plugin_setting('enabled');
+ if (is_null($form) || !$plugin_enabled) return $plugin_enabled;
+
+ $form_settings = $this->get_form_settings($form);
+ $form_disabled = (bool) rgar($form_settings, 'disabled');
+ return !$form_disabled;
+ }
+
+ public function submit_button(string $button_input, array $form): string {
+ if (!Settings::is_connected() || !$this->is_enabled($form)) return $button_input;
+ ob_start();
+ Widget::render();
+ return ob_get_clean() . $button_input;
+ }
+
+ public function validation(array $validation_result, string $context): array {
+ if ($context != 'form-submit') return $validation_result;
+
+ if (!Settings::is_connected() || !$this->is_enabled(rgar($validation_result, 'form'))) return $validation_result;
+
+ $errors = API::process(Widget::get_token());
+ if ($errors->has_errors()) {
+ $validation_result['is_valid'] = false;
+ }
+
+ // TODO: Mark as spam (optional)
+ return $validation_result;
+ }
+
+}
diff --git a/inc/location/class-login-page.php b/inc/location/class-login-page.php
index 8d46ac0..6be5c9f 100644
--- a/inc/location/class-login-page.php
+++ b/inc/location/class-login-page.php
@@ -10,6 +10,7 @@
namespace Ogre\Captcha\Location;
+use Ogre\Captcha\API;
use Override;
use Ogre\Captcha\Location;
use Ogre\Captcha\Settings;
@@ -42,8 +43,8 @@ final class LoginPage extends Location {
return array_merge(
$shake_error_codes,
[
- self::ERROR_EMPTY_TOKEN,
- self::ERROR_INVALID_CAPTCHA,
+ API::ERROR_EMPTY_TOKEN,
+ API::ERROR_INVALID_CAPTCHA,
]
);
}
diff --git a/ogre-captcha.php b/ogre-captcha.php
index 0717355..fcb8f9e 100644
--- a/ogre-captcha.php
+++ b/ogre-captcha.php
@@ -6,14 +6,14 @@
* @author cleverogre
* @copyright 2026 CleverOgre
* @license GLP-3.0-or-later
- * @version 1.0.1
+ * @version 1.1.0
* @since 1.0.0
*
* @wordpress-plugin
* Plugin Name: Ogre CAPTCHA
* Plugin URI: https://git.cleverogre.com/cleverogre/ogre-captcha/
* Description: Protect your site with CAPTCHA
- * Version: 1.0.1
+ * Version: 1.1.0
* Requires at least: 6.0
* Requires PHP: 8.2
* Author: CleverOgre
@@ -33,6 +33,9 @@
namespace Ogre;
use Ogre\Captcha\Settings;
+use Ogre\Captcha\GravityForms\Addon;
+use GFAddOn;
+use Override;
defined('ABSPATH') || exit;
@@ -44,6 +47,10 @@ final class Captcha extends Plugin {
return is_plugin_active('woocommerce/woocommerce.php');
}
+ public static function is_gravityforms_active():bool {
+ return is_plugin_active('gravityforms/gravityforms.php');
+ }
+
protected function __construct() {
parent::__construct();
@@ -59,18 +66,22 @@ final class Captcha extends Plugin {
'inc/location/class-post-password-form.php',
'inc/location/class-registration-page.php',
]);
-
- add_action('plugins_loaded', [$this, 'load_woocommerce'], 9, 0);
}
+ #[Override]
protected function enable():void {
parent::enable();
add_filter('plugin_action_links_' . self::get_basename(), [$this, 'action_links']);
+ add_action('plugins_loaded', [$this, 'load_woocommerce'], 2, 0);
+ add_action('gform_loaded', [$this, 'load_gravityforms'], 5, 0);
}
+ #[Override]
protected function disable():void {
parent::disable();
remove_filter('plugin_action_links_' . self::get_basename(), [$this, 'action_links']);
+ remove_action('plugins_loaded', [$this, 'load_woocommerce'], 2);
+ remove_action('gform_loaded', [$this, 'load_gravityforms'], 5);
}
public function action_links(array $links):array {
@@ -95,6 +106,14 @@ final class Captcha extends Plugin {
]);
}
+ public function load_gravityforms():void {
+ if (!self::is_gravityforms_active() || !method_exists('GFForms', 'include_addon_framework')) return;
+
+ require_once('inc/gravityforms/class-addon.php');
+
+ GFAddOn::register(Addon::class);
+ }
+
}
Captcha::instance();
diff --git a/package.json b/package.json
index 3a2ade7..4384338 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"$schema": "https://www.schemastore.org/package.json",
"name": "cleverogre/ogre-captcha",
- "version": "1.0.1",
+ "version": "1.1.0",
"title": "Ogre CAPTCHA",
"description": "Protect your site with CAPTCHA",
"author": "CleverOgre",