Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b10a82fdd | |||
| 4b53e00498 | |||
| d04eb1a5ba | |||
| 3af451a8ff | |||
| f310c6c2c2 | |||
| 099382680f | |||
| 322a9d95bd | |||
| af10bb0e82 | |||
| 20b812e91b | |||
| 2165224454 | |||
| f8bf6e40ca | |||
| f8e5bd4e3f | |||
| 5c4d471fc0 | |||
| 1cae0e1310 | |||
| e0ef6dd34d | |||
| 47ca0903b1 | |||
| 37fb1e0cc1 | |||
| 90cbfc2c02 | |||
| 97ec73035e | |||
| c37712d2b5 | |||
| 3bcedb614b | |||
| 2826db7951 | |||
| cc135e0401 | |||
| 6ced723e4f | |||
| 8159c4a46c | |||
| 79f95fa0df | |||
| 6a6e8f3960 | |||
| 401676e7f3 | |||
| 0e63494eec | |||
| 2e7b565407 | |||
| ac7760db55 | |||
| 26d318160b | |||
| d72ad2c866 |
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @package ogre-captcha
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.2.0
|
||||
* @since 1.2.0
|
||||
*/
|
||||
|
||||
(() => {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
if (typeof window['CAP_FLOATING_DYNAMIC'] === 'undefined') return;
|
||||
|
||||
const element = document.body.querySelector(CAP_FLOATING_DYNAMIC['selector']);
|
||||
if (element === null) return;
|
||||
|
||||
for (const [name, value] of Object.entries(CAP_FLOATING_DYNAMIC['attributes'])) {
|
||||
element.setAttribute(name, value);
|
||||
}
|
||||
|
||||
// Trigger MutationObserver
|
||||
const parent = element.parentElement;
|
||||
parent.removeChild(element);
|
||||
parent.appendChild(element);
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @package ogre-captcha
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.1.2
|
||||
* @since 1.1.2
|
||||
*/
|
||||
|
||||
:root :where(cap-widget) {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://getcomposer.org/schema.json",
|
||||
"name": "cleverogre/ogre-captcha",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.6",
|
||||
"title": "Ogre CAPTCHA",
|
||||
"description": "Protect your site with CAPTCHA",
|
||||
"author": "CleverOgre",
|
||||
|
||||
Generated
+1
-1
@@ -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": "49f5e6f919f33ce6a3edc2992620a64a",
|
||||
"content-hash": "f65bf8d077abc18766530e9d520877ba",
|
||||
"packages": [
|
||||
{
|
||||
"name": "cleverogre/plugin-framework",
|
||||
|
||||
+4
-4
@@ -44,16 +44,16 @@ gulp.task(
|
||||
|
||||
gulp.task('package-copy', () => {
|
||||
return gulp.src([
|
||||
(fs.existsSync('acf') ? 'acf/**/*' : null),
|
||||
'assets/**/*',
|
||||
'inc/**/*',
|
||||
(fs.existsSync('lib') ? 'lib/**/*' : null),,
|
||||
'lib/**/*',
|
||||
'vendor/**/*',
|
||||
'LICENSE',
|
||||
`${NAME}.php`,
|
||||
'readme.txt',
|
||||
(fs.existsSync('updatepulse.json') ? 'updatepulse.json' : null),
|
||||
'updatepulse.json',
|
||||
'!vendor/**/node_modules/**/*',
|
||||
].filter(x => x), {
|
||||
], {
|
||||
base: './',
|
||||
encoding: false,
|
||||
})
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace Ogre\Captcha;
|
||||
|
||||
use Ogre\Singleton;
|
||||
use Ogre\Captcha as Plugin;
|
||||
use Ogre\Captcha\Settings;
|
||||
use Override;
|
||||
use WP_Error;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
@@ -70,4 +70,54 @@ abstract class Location {
|
||||
wp_die(ob_get_clean());
|
||||
}
|
||||
|
||||
public function header():void {
|
||||
if (!Settings::is_widget_floating()) return;
|
||||
ob_start();
|
||||
}
|
||||
|
||||
public function footer():void {
|
||||
if (!Settings::is_widget_floating()) return;
|
||||
$html = ob_get_clean();
|
||||
$html = Widget::set_button_floating($html);
|
||||
echo $html;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class LoginLocation extends Location {
|
||||
|
||||
protected string $action;
|
||||
|
||||
protected function __construct(string $name, string $action) {
|
||||
$this->action = $action;
|
||||
parent::__construct($name);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function activate(): void
|
||||
{
|
||||
add_action('login_head', [$this, 'header']);
|
||||
add_action('login_footer', [$this, 'footer']);
|
||||
}
|
||||
|
||||
private function get_action():string {
|
||||
return isset($_REQUEST['action']) && is_string($_REQUEST['action']) ? $_REQUEST['action'] : 'login';
|
||||
}
|
||||
|
||||
protected function is_action():bool {
|
||||
return $this->get_action() == $this->action;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function header():void {
|
||||
if (!$this->is_action()) return;
|
||||
parent::header();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function footer():void {
|
||||
if (!$this->is_action()) return;
|
||||
parent::footer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
/**
|
||||
* @package ogre-captcha
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.2.3
|
||||
* @since 1.2.1
|
||||
*/
|
||||
|
||||
namespace Ogre\Captcha\CLI;
|
||||
|
||||
use Ogre\Captcha as Plugin;
|
||||
use Ogre\Captcha\GravityForms\Addon as GFAddon;
|
||||
use Ogre\Captcha\Settings;
|
||||
use WP_CLI;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
final class CLI {
|
||||
|
||||
/**
|
||||
* Set Cap API instance url.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* <value>
|
||||
* : The url of the Cap instance.
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp ogre-captcha instance_url https://captcha.cleverogre.com
|
||||
*
|
||||
* @when after_wp_load
|
||||
*/
|
||||
public function instance_url(array $args, array $assoc_args):void {
|
||||
list($value) = $args;
|
||||
if (Settings::set_instance_url($value)) {
|
||||
WP_CLI::success(Plugin::__('Successfully updated api instance url'));
|
||||
} else {
|
||||
WP_CLI::error(Plugin::__('Invalid url'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Cap API site key.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* <value>
|
||||
* : The value of the site key. Typically 10 characters.
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp ogre-captcha site_key ##########
|
||||
*
|
||||
* @when after_wp_load
|
||||
*/
|
||||
public function site_key(array $args, array $assoc_args):void {
|
||||
list($value) = $args;
|
||||
if (Settings::set_site_key($value)) {
|
||||
WP_CLI::success(Plugin::__('Successfully updated api site key'));
|
||||
} else {
|
||||
WP_CLI::error(Plugin::__('Invalid site key'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Cap API secret key.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* <value>
|
||||
* : The value of the secret key. Typically 43 characters.
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp ogre-captcha secret_key sk-########################################
|
||||
*
|
||||
* @when after_wp_load
|
||||
*/
|
||||
public function secret_key(array $args, array $assoc_args):void {
|
||||
list($value) = $args;
|
||||
if (Settings::set_secret_key($value)) {
|
||||
WP_CLI::success(Plugin::__('Successfully updated api secret key'));
|
||||
} else {
|
||||
WP_CLI::error(Plugin::__('Invalid secret key'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable (or disable) CAPTCHA on the designated forms.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* [<locations>...]
|
||||
* : The form locations to alter. The following values are accepted. "login_page", "registration_page", "lost_password_page", "login_form", "comment_form", "post_password_form", "wc_login", "wc_lost_password", "wc_register", "wc_checkout"
|
||||
*
|
||||
* [--all]
|
||||
* : Whether or not to include all possible form locations.
|
||||
*
|
||||
* [--disable]
|
||||
* : Whether or not to disable the designated form locations rather than enable.
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp ogre-captcha form --all
|
||||
*
|
||||
* @when after_wp_load
|
||||
*/
|
||||
public function form(array $args, array $assoc_args):void {
|
||||
$locations = !empty($args) ? (array) array_values($args)[0] : [];
|
||||
$all = $assoc_args['all'] ?? false;
|
||||
$disable = $assoc_args['disable'] ?? false;
|
||||
|
||||
if (empty($locations) && !$all) {
|
||||
WP_CLI::error('Please specify one or more form locations, or use --all.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!!$all) $locations = Settings::ALL_LOCATIONS;
|
||||
|
||||
$locations = array_filter($locations, fn ($location) => !Settings::set_form_location($location, !$disable));
|
||||
|
||||
if (empty($locations)) {
|
||||
WP_CLI::success(Plugin::__('Successfully updated all form locations'));
|
||||
} else {
|
||||
WP_CLI::error(
|
||||
Plugin::__('Failed to alter form locations: %s'),
|
||||
implode(', ', $locations)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Cap widget type.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* <value>
|
||||
* : The value of the widget type. Either "standard" or "floating".
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp ogre-captcha widget_type floating
|
||||
*
|
||||
* @when after_wp_load
|
||||
*/
|
||||
public function widget_type(array $args, array $assoc_args):void {
|
||||
list($value) = $args;
|
||||
if (Settings::set_widget_type($value)) {
|
||||
WP_CLI::success(Plugin::__('Successfully updated widget type'));
|
||||
} else {
|
||||
WP_CLI::error(Plugin::__('Invalid widget type'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable CAPTCHA on Gravity Forms.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* <value>
|
||||
* : Whether or not to enable CAPTCHA on all forms by default.
|
||||
* ---
|
||||
* options:
|
||||
* - enable
|
||||
* - disable
|
||||
* ---
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp ogre-captcha gravityforms enable
|
||||
*
|
||||
* @when gform_loaded
|
||||
*/
|
||||
public function gravityforms(array $args, array $assoc_args):void {
|
||||
list($value) = $args;
|
||||
$value = $value == 'enable';
|
||||
|
||||
if (!Plugin::is_gravityforms_active()) {
|
||||
WP_CLI::error(Plugin::__('Gravity Forms not active'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GFAddon::get_instance()?->set_enabled($value)) {
|
||||
WP_CLI::success(Plugin::__('Successfully updated Gravity Forms integration'));
|
||||
} else {
|
||||
WP_CLI::error(Plugin::__('Unable to update setting'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WP_CLI::add_command(Plugin::get_id(), CLI::class);
|
||||
+149
-9
@@ -4,7 +4,7 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.5
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
@@ -27,11 +27,45 @@ final class Settings {
|
||||
public const LOCATION_LOGIN_FORM = 'login_form';
|
||||
public const LOCATION_COMMENT_FORM = 'comment_form';
|
||||
public const LOCATION_POST_PASSWORD_FORM = 'post_password_form';
|
||||
public const WP_LOCATIONS = [
|
||||
self::LOCATION_LOGIN_PAGE,
|
||||
self::LOCATION_REGISTRATION_PAGE,
|
||||
self::LOCATION_LOST_PASSWORD_FORM,
|
||||
self::LOCATION_LOGIN_FORM,
|
||||
self::LOCATION_COMMENT_FORM,
|
||||
self::LOCATION_POST_PASSWORD_FORM,
|
||||
];
|
||||
|
||||
public const LOCATION_WC_LOGIN = 'wc_login';
|
||||
public const LOCATION_WC_LOST_PASSWORD = 'wc_lost_password';
|
||||
public const LOCATION_WC_REGISTER = 'wc_register';
|
||||
public const LOCATION_WC_CHECKOUT = 'wc_checkout';
|
||||
public const WC_LOCATIONS = [
|
||||
self::LOCATION_WC_LOGIN,
|
||||
self::LOCATION_WC_LOST_PASSWORD,
|
||||
self::LOCATION_WC_REGISTER,
|
||||
self::LOCATION_WC_CHECKOUT,
|
||||
];
|
||||
|
||||
public const ALL_LOCATIONS = [
|
||||
self::LOCATION_LOGIN_PAGE,
|
||||
self::LOCATION_REGISTRATION_PAGE,
|
||||
self::LOCATION_LOST_PASSWORD_FORM,
|
||||
self::LOCATION_LOGIN_FORM,
|
||||
self::LOCATION_COMMENT_FORM,
|
||||
self::LOCATION_POST_PASSWORD_FORM,
|
||||
self::LOCATION_WC_LOGIN,
|
||||
self::LOCATION_WC_LOST_PASSWORD,
|
||||
self::LOCATION_WC_REGISTER,
|
||||
self::LOCATION_WC_CHECKOUT,
|
||||
];
|
||||
|
||||
public const WIDGET_TYPE_STANDARD = 'standard';
|
||||
public const WIDGET_TYPE_FLOATING = 'floating';
|
||||
public const WIDGET_TYPES = [
|
||||
self::WIDGET_TYPE_STANDARD,
|
||||
self::WIDGET_TYPE_FLOATING,
|
||||
];
|
||||
|
||||
protected function __construct() {
|
||||
add_action('admin_menu', [$this, 'menu']);
|
||||
@@ -44,6 +78,10 @@ final class Settings {
|
||||
return $data;
|
||||
}
|
||||
|
||||
private static function has(string $key):bool {
|
||||
return array_key_exists($key, self::get());
|
||||
}
|
||||
|
||||
private static function set(string $key, $value = null):void {
|
||||
$data = self::get();
|
||||
if (is_null($value) && isset($data[$key])) {
|
||||
@@ -54,7 +92,8 @@ final class Settings {
|
||||
update_option(Plugin::get_id(), $data);
|
||||
}
|
||||
|
||||
private static function is_checked(string $name, string $value):bool {
|
||||
private static function is_checked(string $name, string $value, string|null $default = null):bool {
|
||||
if (!is_null($default) && !self::has($name)) return $value == $default;
|
||||
return in_array($value, (array) self::get($name));
|
||||
}
|
||||
|
||||
@@ -72,14 +111,30 @@ final class Settings {
|
||||
return $url;
|
||||
}
|
||||
|
||||
public static function set_instance_url(string $value):bool {
|
||||
if (!wp_http_validate_url($value)) return false;
|
||||
self::set('instance_url', $value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function get_site_key():string {
|
||||
return (string) self::get('site_key');
|
||||
}
|
||||
|
||||
public static function set_site_key(string $value):bool {
|
||||
self::set('site_key', $value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function get_secret_key():string {
|
||||
return (string) self::get('secret_key');
|
||||
}
|
||||
|
||||
public static function set_secret_key(string $value):bool {
|
||||
self::set('secret_key', $value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function is_connected():bool {
|
||||
return !empty(self::get_site_key()) && !empty(self::get_secret_key());
|
||||
}
|
||||
@@ -99,6 +154,26 @@ final class Settings {
|
||||
return in_array($name, self::get_form_locations());
|
||||
}
|
||||
|
||||
public static function set_form_location(string $name, bool $enabled):bool {
|
||||
if (in_array($name, self::WP_LOCATIONS)) {
|
||||
$key = 'form_locations';
|
||||
} else if (in_array($name, self::WC_LOCATIONS)) {
|
||||
$key = 'wc_form_locations';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = (array) self::get($key);
|
||||
if (!!$enabled && !in_array($name, $value)) {
|
||||
$value[] = $name;
|
||||
} else if (!$enabled && in_array($name, $value)) {
|
||||
unset($value[array_search($name, $value)]);
|
||||
}
|
||||
self::set($key, $value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function is_login_page_enabled():bool {
|
||||
return self::is_form_location_enabled(self::LOCATION_LOGIN_PAGE);
|
||||
}
|
||||
@@ -139,6 +214,23 @@ final class Settings {
|
||||
return self::is_form_location_enabled(self::LOCATION_WC_CHECKOUT);
|
||||
}
|
||||
|
||||
public static function get_widget_type():string {
|
||||
$value = self::get('widget_type');
|
||||
if (is_array($value) && !empty($value)) $value = array_values($value)[0];
|
||||
if (!is_string($value)) $value = '';
|
||||
return in_array($value, self::WIDGET_TYPES) ? $value : self::WIDGET_TYPE_STANDARD;
|
||||
}
|
||||
|
||||
public static function is_widget_floating():bool {
|
||||
return self::get_widget_type() === self::WIDGET_TYPE_FLOATING;
|
||||
}
|
||||
|
||||
public static function set_widget_type(string $value):bool {
|
||||
if (!in_array($value, self::WIDGET_TYPES)) return false;
|
||||
self::set('widget_type', $value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function menu() {
|
||||
if (!current_user_can(self::CAPABILITY)) return;
|
||||
add_options_page(
|
||||
@@ -186,7 +278,9 @@ final class Settings {
|
||||
args: [
|
||||
'name' => 'site_key',
|
||||
'description' => Plugin::__('Provide your Cap site key.'),
|
||||
'type' => 'password',
|
||||
'attributes' => [
|
||||
'autocomplete' => 'off',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
@@ -199,16 +293,19 @@ final class Settings {
|
||||
'name' => 'secret_key',
|
||||
'description' => Plugin::__('Provide your Cap secret key.'),
|
||||
'type' => 'password',
|
||||
'attributes' => [
|
||||
'autocomplete' => 'off',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
add_settings_section(
|
||||
'forms',
|
||||
Plugin::__('CAPTCHA Forms'),
|
||||
Plugin::__('Forms'),
|
||||
[$this, 'section'],
|
||||
Plugin::get_id(),
|
||||
[
|
||||
'description' => Plugin::__('Define which forms you would like to secure with CAPTCHA.'),
|
||||
'description' => Plugin::__('Define which forms you would like to secure with Cap.'),
|
||||
]
|
||||
);
|
||||
|
||||
@@ -228,6 +325,7 @@ final class Settings {
|
||||
self::LOCATION_COMMENT_FORM => Plugin::__('Comment form'),
|
||||
self::LOCATION_POST_PASSWORD_FORM => Plugin::__('Password-protected posts and pages'),
|
||||
],
|
||||
'select_all' => true,
|
||||
'description' => Plugin::__('Custom login form requires login page to also be enabled.'),
|
||||
]
|
||||
);
|
||||
@@ -247,9 +345,40 @@ final class Settings {
|
||||
self::LOCATION_WC_REGISTER => Plugin::__('Register form'),
|
||||
self::LOCATION_WC_CHECKOUT => Plugin::__('Checkout form'),
|
||||
],
|
||||
'select_all' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
add_settings_section(
|
||||
'widget',
|
||||
Plugin::__('Widget'),
|
||||
[$this, 'section'],
|
||||
Plugin::get_id(),
|
||||
[
|
||||
'description' => Plugin::__('Change how the Cap widget is displayed on the frontend.'),
|
||||
]
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
Plugin::get_id() . '_widget_type',
|
||||
Plugin::__('Widget type'),
|
||||
[$this, 'checkbox_field'],
|
||||
Plugin::get_id(),
|
||||
'widget',
|
||||
[
|
||||
'type' => 'radio',
|
||||
'name' => 'widget_type',
|
||||
'options' => [
|
||||
self::WIDGET_TYPE_STANDARD => Plugin::__('Standard (default)'),
|
||||
self::WIDGET_TYPE_FLOATING => Plugin::__('Floating'),
|
||||
],
|
||||
'default' => self::WIDGET_TYPE_STANDARD,
|
||||
'description' => Plugin::__('Not sure which type you want to use? <a href="https://trycap.dev/guide/demo.html" target="_blank">View the demo</a> to try them out.'),
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function page() {
|
||||
@@ -332,13 +461,24 @@ final class Settings {
|
||||
public function checkbox_field(array $args):void {
|
||||
if (empty($args['name'] ?? '') || empty($args['options'] ?? [])) return;
|
||||
|
||||
echo implode('<br>', array_map(
|
||||
fn (string $name, string $label):string => sprintf(
|
||||
'<label><input type="checkbox" name="%s[%s][]" value="%s" %s> %s</label>',
|
||||
if (!!($args['select_all'] ?? false) && ($args['type'] ?? 'checkbox') == 'checkbox') {
|
||||
printf(
|
||||
'<label><input type="checkbox" onClick="document.getElementsByName(\'%s[%s][]\').forEach(element => { element.checked = this.checked });"> %s</label><br>',
|
||||
esc_attr(Plugin::get_id()),
|
||||
esc_attr($args['name']),
|
||||
Plugin::esc_html__('Select All')
|
||||
);
|
||||
}
|
||||
|
||||
echo implode('<br>', array_map(
|
||||
fn (string $name, string $label):string => sprintf(
|
||||
'<label><input type="%s" name="%s[%s]%s" value="%s" %s> %s</label>',
|
||||
esc_attr($args['type'] ?? 'checkbox'),
|
||||
esc_attr(Plugin::get_id()),
|
||||
esc_attr($args['name']),
|
||||
(($args['type'] ?? 'checkbox') ? '[]' : ''),
|
||||
esc_attr($name),
|
||||
checked(self::is_checked($args['name'], $name), display: false),
|
||||
checked(self::is_checked($args['name'], $name, $args['default'] ?? null), display: false),
|
||||
esc_html($label)
|
||||
),
|
||||
array_keys($args['options']),
|
||||
|
||||
+175
-8
@@ -4,19 +4,23 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace Ogre\Captcha;
|
||||
|
||||
use Ogre\Captcha as Plugin;
|
||||
use Ogre\Captcha\Settings;
|
||||
use WP_HTML_Tag_Processor;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
final class Widget {
|
||||
|
||||
private const SCRIPT_HANDLE = 'cap';
|
||||
private const HANDLE = 'cap';
|
||||
private const FLOATING_HANDLE = 'cap-floating';
|
||||
private const DYNAMIC_FLOATING_HANDLE = 'cap-floating-dynamic';
|
||||
private const TOKEN_NAME = 'cap-token';
|
||||
|
||||
private static function get_script_url():string {
|
||||
@@ -24,21 +28,184 @@ final class Widget {
|
||||
return Settings::get_instance_url('assets/widget.js');
|
||||
}
|
||||
|
||||
public static function enqueue_script():void {
|
||||
if (!Settings::is_connected() || wp_script_is(self::SCRIPT_HANDLE)) return;
|
||||
wp_enqueue_script(self::SCRIPT_HANDLE, self::get_script_url());
|
||||
private static function get_floating_script_url():string {
|
||||
if (!Settings::is_connected()) return '';
|
||||
return Settings::get_instance_url('assets/floating.js');
|
||||
}
|
||||
|
||||
public static function render(string $id = 'cap'):void {
|
||||
public static function enqueue_assets(bool $floating = false):void {
|
||||
if (!Settings::is_connected()) return;
|
||||
self::enqueue_script();
|
||||
|
||||
if (!wp_script_is(self::HANDLE)) {
|
||||
wp_enqueue_script(
|
||||
self::HANDLE,
|
||||
self::get_script_url(),
|
||||
args: [
|
||||
'in_footer' => true
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ($floating && !wp_script_is(self::FLOATING_HANDLE)) {
|
||||
wp_enqueue_script(
|
||||
self::FLOATING_HANDLE,
|
||||
self::get_floating_script_url(),
|
||||
[
|
||||
self::HANDLE
|
||||
],
|
||||
args: [
|
||||
'in_footer' => true
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (!wp_style_is(self::HANDLE)) {
|
||||
wp_enqueue_style(
|
||||
self::HANDLE,
|
||||
Plugin::get_url('assets/widget.css')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static function render(array $args = []):void {
|
||||
if (!Settings::is_connected()) return;
|
||||
|
||||
$args = wp_parse_args(
|
||||
$args,
|
||||
[
|
||||
'id' => 'cap',
|
||||
'floating' => Settings::is_widget_floating(),
|
||||
]
|
||||
);
|
||||
|
||||
self::enqueue_assets(!!$args['floating']);
|
||||
printf(
|
||||
'<cap-widget id="%s" required data-cap-api-endpoint="%s"></cap-widget>',
|
||||
esc_attr($id),
|
||||
esc_attr(!empty($args['id']) ? strval($args['id']) : 'cap'),
|
||||
esc_url(Settings::get_instance_url(Settings::get_site_key() . '/'))
|
||||
);
|
||||
}
|
||||
|
||||
public static function get_button_floating_attributes(array $args = []):array {
|
||||
$args = wp_parse_args(
|
||||
$args,
|
||||
[
|
||||
'id' => 'cap',
|
||||
'position' => 'bottom',
|
||||
'offset' => null,
|
||||
]
|
||||
);
|
||||
|
||||
$attributes = [
|
||||
'data-cap-floating' => "#{$args['id']}",
|
||||
'data-cap-floating-position' => $args['position'],
|
||||
];
|
||||
|
||||
if (!is_null($args['offset'])) {
|
||||
$attributes['data-cap-floating-offset'] = $args['offset'];
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
private static function parse_button_floating_args(array $args):array {
|
||||
return wp_parse_args(
|
||||
$args,
|
||||
[
|
||||
'id' => 'cap',
|
||||
'position' => 'bottom',
|
||||
'offset' => null,
|
||||
'tag_name' => null,
|
||||
'class_name' => null,
|
||||
'type_name' => 'submit',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private static function get_button_floating_selector(array $args):string {
|
||||
$selector = '';
|
||||
|
||||
if (!is_null($args['tag_name']) && !empty($args['tag_name'])) {
|
||||
$selector = $args['tag_name'];
|
||||
} else {
|
||||
$selector = ':where(input, button)';
|
||||
}
|
||||
|
||||
if (!is_null($args['class_name']) && !empty($args['class_name'])) {
|
||||
$selector = sprintf(
|
||||
'.',
|
||||
sanitize_html_class($args['class_name'])
|
||||
);
|
||||
}
|
||||
|
||||
if (!is_null($args['type_name']) && !empty($args['type_name'])) {
|
||||
$selector .= sprintf(
|
||||
'[type="%s"]',
|
||||
esc_attr($args['type_name'])
|
||||
);
|
||||
}
|
||||
|
||||
return $selector;
|
||||
}
|
||||
|
||||
public static function set_button_floating(string $html, array $args = []):string {
|
||||
if (empty($html)) return $html;
|
||||
|
||||
$args = self::parse_button_floating_args($args);
|
||||
$attributes = self::get_button_floating_attributes($args);
|
||||
|
||||
$queries = ['input', 'button'];
|
||||
if (!is_null($args['tag_name']) || !is_null($args['class_name'])) {
|
||||
$queries = [[
|
||||
'tag_name' => $args['tag_name'],
|
||||
'class_name' => $args['class_name'],
|
||||
]];
|
||||
}
|
||||
|
||||
$tags = new WP_HTML_Tag_Processor($html);
|
||||
$tags->next_tag();
|
||||
$tags->set_bookmark('top');
|
||||
foreach ($queries as $query) {
|
||||
$tags->seek('top');
|
||||
$found = false;
|
||||
while ($tags->next_tag($query)) {
|
||||
if (!is_null($args['type_name']) && !empty($args['type_name']) && $tags->get_attribute('type') !== $args['type_name']) continue;
|
||||
$found = true;
|
||||
foreach ($attributes as $name => $value) {
|
||||
$tags->set_attribute($name, $value);
|
||||
}
|
||||
}
|
||||
if ($found) break;
|
||||
}
|
||||
return $tags->get_updated_html();
|
||||
}
|
||||
|
||||
public static function add_dynamic_floating_attributes(array $args = []):void {
|
||||
if (wp_script_is(self::DYNAMIC_FLOATING_HANDLE)) return;
|
||||
|
||||
$args = self::parse_button_floating_args($args);
|
||||
|
||||
wp_enqueue_script(
|
||||
self::DYNAMIC_FLOATING_HANDLE,
|
||||
Plugin::get_url('assets/floating.js'),
|
||||
[
|
||||
self::FLOATING_HANDLE
|
||||
],
|
||||
args: [
|
||||
'in_footer' => true
|
||||
]
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
self::DYNAMIC_FLOATING_HANDLE,
|
||||
'CAP_FLOATING_DYNAMIC',
|
||||
[
|
||||
'selector' => self::get_button_floating_selector($args),
|
||||
'attributes' => self::get_button_floating_attributes($args),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public static function get_token():string {
|
||||
return isset($_POST[self::TOKEN_NAME]) ? (string) $_POST[self::TOKEN_NAME] : '';
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.1.1
|
||||
* @version 1.2.1
|
||||
* @since 1.1.0
|
||||
*/
|
||||
|
||||
@@ -12,11 +12,13 @@ namespace Ogre\Captcha\GravityForms;
|
||||
|
||||
use Ogre\Singleton;
|
||||
use Ogre\Captcha as Plugin;
|
||||
use GFForms;
|
||||
use GFAddOn;
|
||||
use Ogre\Captcha\API;
|
||||
use Ogre\Captcha\Settings;
|
||||
use Ogre\Captcha\Widget;
|
||||
use GFForms;
|
||||
use GFAddOn;
|
||||
use GFFormDisplay;
|
||||
use GFCommon;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -114,7 +116,7 @@ final class Addon extends GFAddOn {
|
||||
}
|
||||
|
||||
public function is_enabled(array|null $form = null): bool {
|
||||
$plugin_enabled = (bool) $this->get_plugin_setting('enabled');
|
||||
$plugin_enabled = $this->get_plugin_setting('enabled') === '1';
|
||||
if (is_null($form) || !$plugin_enabled) return $plugin_enabled;
|
||||
|
||||
$form_settings = $this->get_form_settings($form);
|
||||
@@ -122,17 +124,30 @@ final class Addon extends GFAddOn {
|
||||
return !$form_disabled;
|
||||
}
|
||||
|
||||
public function set_enabled(bool $value): bool {
|
||||
$settings = (array) $this->get_plugin_settings();
|
||||
$settings['enabled'] = !!$value ? '1' : '0';
|
||||
$this->update_plugin_settings($settings);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function submit_button(string $button_input, array $form): string {
|
||||
if (!Settings::is_connected() || !$this->is_enabled($form)) return $button_input;
|
||||
if ((is_admin() && !defined('DOING_AJAX')) || GFCommon::is_form_editor()) return $button_input;
|
||||
|
||||
ob_start();
|
||||
Widget::render();
|
||||
return ob_get_clean() . $button_input;
|
||||
$button_input = ob_get_clean() . $button_input;
|
||||
if (Settings::is_widget_floating()) $button_input = Widget::set_button_floating($button_input);
|
||||
return $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;
|
||||
$form = $validation_result['form'];
|
||||
if (!GFFormDisplay::is_last_page($form) || rgpost('action') == 'heartbeat') return $validation_result;
|
||||
if (!Settings::is_connected() || !$this->is_enabled($form)) return $validation_result;
|
||||
|
||||
$errors = API::process(Widget::get_token());
|
||||
if ($errors->has_errors()) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Ogre\Captcha\Location;
|
||||
use Override;
|
||||
use Ogre\Captcha\Location;
|
||||
use Ogre\Captcha\Settings;
|
||||
use Ogre\Captcha\Widget;
|
||||
use WP_Error;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
@@ -27,8 +28,16 @@ final class CommentForm extends Location {
|
||||
public function activate(): void
|
||||
{
|
||||
add_action('comment_form_after_fields', [$this, 'render'], 1);
|
||||
add_filter('comment_form_submit_button', [$this, 'submit_button'], 10, 2);
|
||||
add_filter('preprocess_comment', [$this, 'process_form']);
|
||||
}
|
||||
|
||||
public function submit_button(string $submit_button, array $args):string {
|
||||
if (Settings::is_widget_floating()) {
|
||||
$submit_button = Widget::set_button_floating($submit_button);
|
||||
}
|
||||
return $submit_button;
|
||||
}
|
||||
|
||||
public function process_form(array $commentdata):array {
|
||||
// Don't process CAPTCHA for comment replies inside admin menu
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Ogre\Captcha\Location;
|
||||
use Override;
|
||||
use Ogre\Captcha\Location;
|
||||
use Ogre\Captcha\Settings;
|
||||
use Ogre\Captcha\Widget;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -39,6 +40,11 @@ final class LoginForm extends Location {
|
||||
ob_start();
|
||||
$this->render();
|
||||
$content .= ob_get_clean();
|
||||
|
||||
if (Settings::is_widget_floating()) {
|
||||
Widget::add_dynamic_floating_attributes();
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
@@ -12,17 +12,17 @@ namespace Ogre\Captcha\Location;
|
||||
|
||||
use Ogre\Captcha\API;
|
||||
use Override;
|
||||
use Ogre\Captcha\Location;
|
||||
use Ogre\Captcha\LoginLocation;
|
||||
use Ogre\Captcha\Settings;
|
||||
use WP_Error;
|
||||
use WP_User;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
final class LoginPage extends Location {
|
||||
final class LoginPage extends LoginLocation {
|
||||
|
||||
protected function __construct() {
|
||||
parent::__construct(Settings::LOCATION_LOGIN_PAGE);
|
||||
parent::__construct(Settings::LOCATION_LOGIN_PAGE, 'login');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
@@ -34,6 +34,7 @@ final class LoginPage extends Location {
|
||||
#[Override]
|
||||
public function activate(): void
|
||||
{
|
||||
parent::activate();
|
||||
add_action('login_form', [$this, 'render']);
|
||||
add_filter('shake_error_codes', [$this, 'shake_error_codes']);
|
||||
add_filter('authenticate', [$this, 'process_form'], 20, 1);
|
||||
|
||||
@@ -4,24 +4,23 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.6
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace Ogre\Captcha\Location;
|
||||
|
||||
use Override;
|
||||
use Ogre\Captcha\Location;
|
||||
use Ogre\Captcha\LoginLocation;
|
||||
use Ogre\Captcha\Settings;
|
||||
use WP_Error;
|
||||
use WP_User;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
final class LostPasswordForm extends Location {
|
||||
final class LostPasswordForm extends LoginLocation {
|
||||
|
||||
protected function __construct() {
|
||||
parent::__construct(Settings::LOCATION_LOST_PASSWORD_FORM);
|
||||
parent::__construct(Settings::LOCATION_LOST_PASSWORD_FORM, 'lostpassword');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
@@ -33,11 +32,12 @@ final class LostPasswordForm extends Location {
|
||||
#[Override]
|
||||
public function activate(): void
|
||||
{
|
||||
parent::activate();
|
||||
add_action('lostpassword_form', [$this, 'render']);
|
||||
add_action('lostpassword_post', [$this, 'process_form']);
|
||||
add_action('lostpassword_post', [$this, 'process_form'], 10, 1);
|
||||
}
|
||||
|
||||
public function process_form(WP_Error $errors, WP_User|bool $user_data):void {
|
||||
public function process_form(WP_Error $errors):void {
|
||||
$this->process($errors);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Ogre\Captcha\Location;
|
||||
use Override;
|
||||
use Ogre\Captcha\Location;
|
||||
use Ogre\Captcha\Settings;
|
||||
use Ogre\Captcha\Widget;
|
||||
use WP_Error;
|
||||
use WP_Post;
|
||||
|
||||
@@ -27,14 +28,19 @@ final class PostPasswordForm extends Location {
|
||||
#[Override]
|
||||
public function activate(): void
|
||||
{
|
||||
add_filter('the_password_form', [$this, 'render_form']);
|
||||
add_filter('the_password_form', [$this, 'render_form'], 10, 3);
|
||||
add_action('login_form_postpass', [$this, 'process_form']);
|
||||
}
|
||||
|
||||
public function render_form(string $output, WP_Post $post, string $invalid_password):string {
|
||||
ob_start();
|
||||
$this->render();
|
||||
$output .= ob_get_clean();
|
||||
$output = str_replace('</form>', ob_get_clean() . '</form>', $output);
|
||||
|
||||
if (Settings::is_widget_floating()) {
|
||||
$output = Widget::set_button_floating($output);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,23 +4,23 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace Ogre\Captcha\Location;
|
||||
|
||||
use Override;
|
||||
use Ogre\Captcha\Location;
|
||||
use Ogre\Captcha\LoginLocation;
|
||||
use Ogre\Captcha\Settings;
|
||||
use WP_Error;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
final class RegistrationPage extends Location {
|
||||
final class RegistrationPage extends LoginLocation {
|
||||
|
||||
protected function __construct() {
|
||||
parent::__construct(Settings::LOCATION_REGISTRATION_PAGE);
|
||||
parent::__construct(Settings::LOCATION_REGISTRATION_PAGE, 'register');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Ogre\Captcha\Location\WooCommerce;
|
||||
use Override;
|
||||
use Ogre\Captcha\Location;
|
||||
use Ogre\Captcha\Settings;
|
||||
use Ogre\Captcha\Widget;
|
||||
use WP_Error;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
@@ -33,9 +34,17 @@ final class Checkout extends Location {
|
||||
public function activate(): void
|
||||
{
|
||||
add_action('woocommerce_after_checkout_billing_form', [$this, 'render']);
|
||||
add_filter('woocommerce_order_button_html', [$this, 'submit_button'], 10, 1);
|
||||
add_action('woocommerce_after_checkout_validation', [$this, 'process_form'], 10, 2);
|
||||
}
|
||||
|
||||
public function submit_button(string $html):string {
|
||||
if (Settings::is_widget_floating()) {
|
||||
$html = Widget::set_button_floating($html);
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function process_form(array $data, WP_Error $errors) {
|
||||
$this->process($errors);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,9 @@ final class Login extends Location {
|
||||
#[Override]
|
||||
public function activate(): void
|
||||
{
|
||||
add_action('woocommerce_login_form_start', [$this, 'header']);
|
||||
add_action('woocommerce_login_form', [$this, 'render']);
|
||||
add_action('woocommerce_login_form_end', [$this, 'footer']);
|
||||
if (isset($_POST['woocommerce-login-nonce'])) {
|
||||
add_filter('woocommerce_process_login_errors', [$this, 'process_form'], 10, 3);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
@@ -33,7 +33,9 @@ final class LostPassword extends Location {
|
||||
#[Override]
|
||||
public function activate(): void
|
||||
{
|
||||
add_action('woocommerce_before_lost_password_form', [$this, 'header']);
|
||||
add_action('woocommerce_lostpassword_form', [$this, 'render']);
|
||||
add_action('woocommerce_after_lost_password_form', [$this, 'footer']);
|
||||
if (isset($_POST['woocommerce-lost-password-nonce'])) {
|
||||
add_action('lostpassword_post', [$this, 'process_form']);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.0.0
|
||||
* @version 1.2.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,9 @@ final class Register extends Location {
|
||||
#[Override]
|
||||
public function activate(): void
|
||||
{
|
||||
add_action('woocommerce_register_form_start', [$this, 'header']);
|
||||
add_action('woocommerce_register_form', [$this, 'render']);
|
||||
add_action('woocommerce_register_form_end', [$this, 'footer']);
|
||||
if (isset($_POST['woocommerce-register-nonce'])) {
|
||||
add_filter('woocommerce_process_registration_errors', [$this, 'process_form'], 10, 4);
|
||||
}
|
||||
|
||||
+80
-72
@@ -6,14 +6,14 @@
|
||||
* @author cleverogre
|
||||
* @copyright 2026 CleverOgre
|
||||
* @license GLP-3.0-or-later
|
||||
* @version 1.1.1
|
||||
* @version 1.2.6
|
||||
* @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.1.1
|
||||
* Version: 1.2.6
|
||||
* Requires at least: 6.0
|
||||
* Requires PHP: 8.2
|
||||
* Author: CleverOgre
|
||||
@@ -27,7 +27,7 @@
|
||||
* Icon2x: https://plugins.cleverogre.com/wp-content/uploads/ogre-captcha-icon-256x256.png
|
||||
* BannerLow: https://plugins.cleverogre.com/wp-content/uploads/ogre-captcha-banner-772x250.png
|
||||
* BannerHigh: https://plugins.cleverogre.com/wp-content/uploads/ogre-captcha-banner-1544x500.png
|
||||
* Requires License: yes
|
||||
* Require License: yes
|
||||
*/
|
||||
|
||||
namespace Ogre;
|
||||
@@ -41,79 +41,87 @@ defined('ABSPATH') || exit;
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
final class Captcha extends Plugin {
|
||||
if (!class_exists('Ogre\Captcha')) {
|
||||
|
||||
public static function is_woocommerce_active():bool {
|
||||
return is_plugin_active('woocommerce/woocommerce.php');
|
||||
}
|
||||
final class Captcha extends Plugin {
|
||||
|
||||
public static function is_gravityforms_active():bool {
|
||||
return is_plugin_active('gravityforms/gravityforms.php');
|
||||
}
|
||||
|
||||
protected function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->add_files([
|
||||
'inc/class-settings.php',
|
||||
'inc/class-api.php',
|
||||
'inc/class-widget.php',
|
||||
'inc/abstract-location.php',
|
||||
'inc/location/class-comment-form.php',
|
||||
'inc/location/class-login-form.php',
|
||||
'inc/location/class-login-page.php',
|
||||
'inc/location/class-lost-password-form.php',
|
||||
'inc/location/class-post-password-form.php',
|
||||
'inc/location/class-registration-page.php',
|
||||
]);
|
||||
}
|
||||
|
||||
#[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 {
|
||||
if (current_user_can('manage_options')) {
|
||||
array_unshift($links, sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
esc_url(Settings::get_url()),
|
||||
esc_html(self::__('Settings'))
|
||||
));
|
||||
public static function is_woocommerce_active():bool {
|
||||
return is_plugin_active('woocommerce/woocommerce.php');
|
||||
}
|
||||
return $links;
|
||||
|
||||
public static function is_gravityforms_active():bool {
|
||||
return is_plugin_active('gravityforms/gravityforms.php');
|
||||
}
|
||||
|
||||
protected function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->add_files([
|
||||
'inc/class-settings.php',
|
||||
'inc/class-api.php',
|
||||
'inc/class-widget.php',
|
||||
'inc/abstract-location.php',
|
||||
'inc/location/class-comment-form.php',
|
||||
'inc/location/class-login-form.php',
|
||||
'inc/location/class-login-page.php',
|
||||
'inc/location/class-lost-password-form.php',
|
||||
'inc/location/class-post-password-form.php',
|
||||
'inc/location/class-registration-page.php',
|
||||
]);
|
||||
|
||||
if (class_exists('WP_CLI')) {
|
||||
$this->add_file('inc/class-cli.php');
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
if (current_user_can('manage_options')) {
|
||||
array_unshift($links, sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
esc_url(Settings::get_url()),
|
||||
esc_html(self::__('Settings'))
|
||||
));
|
||||
}
|
||||
return $links;
|
||||
}
|
||||
|
||||
public function load_woocommerce():void {
|
||||
if (!self::is_woocommerce_active()) return;
|
||||
|
||||
$this->add_files([
|
||||
'inc/location/woocommerce/class-checkout.php',
|
||||
'inc/location/woocommerce/class-login.php',
|
||||
'inc/location/woocommerce/class-lost-password.php',
|
||||
'inc/location/woocommerce/class-register.php',
|
||||
]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function load_woocommerce():void {
|
||||
if (!self::is_woocommerce_active()) return;
|
||||
|
||||
$this->add_files([
|
||||
'inc/location/woocommerce/class-checkout.php',
|
||||
'inc/location/woocommerce/class-login.php',
|
||||
'inc/location/woocommerce/class-lost-password.php',
|
||||
'inc/location/woocommerce/class-register.php',
|
||||
]);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
Captcha::instance();
|
||||
|
||||
Generated
+69
-69
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cleverogre/ogre-captcha",
|
||||
"version": "1.0.0",
|
||||
"version": "1.2.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cleverogre/ogre-captcha",
|
||||
"version": "1.0.0",
|
||||
"version": "1.2.6",
|
||||
"license": "GPL-3.0+",
|
||||
"devDependencies": {
|
||||
"gulp": "^5.0.0",
|
||||
@@ -58,13 +58,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
|
||||
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
|
||||
"version": "26.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz",
|
||||
"integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": ">=7.24.0 <7.24.7"
|
||||
"undici-types": "~8.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/vinyl": {
|
||||
@@ -370,11 +370,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/bare-events": {
|
||||
"version": "2.9.1",
|
||||
@@ -485,14 +488,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
|
||||
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
@@ -1756,6 +1761,37 @@
|
||||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/glob/node_modules/brace-expansion": {
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
|
||||
"integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/minimatch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/global-modules": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
|
||||
@@ -2351,9 +2387,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/gulp/node_modules/yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
||||
"version": "16.2.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz",
|
||||
"integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3196,16 +3232,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"version": "10.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
|
||||
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
"brace-expansion": "^5.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/mixin-deep": {
|
||||
@@ -3247,45 +3286,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/multimatch/node_modules/balanced-match": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/multimatch/node_modules/brace-expansion": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/multimatch/node_modules/minimatch": {
|
||||
"version": "10.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
|
||||
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/mute-stdout": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz",
|
||||
@@ -4477,9 +4477,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/streamx": {
|
||||
"version": "2.26.0",
|
||||
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.26.0.tgz",
|
||||
"integrity": "sha512-VvNG1K72Po/xwJzxZFnZ++Tbrv4lwSptsbkFuzXCJAYZvCK5nnxsvXU6ajqkv7chyiI1Y0YXq2Jh8Iy8Y7NF/A==",
|
||||
"version": "2.28.0",
|
||||
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz",
|
||||
"integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4781,9 +4781,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
|
||||
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
|
||||
"integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://www.schemastore.org/package.json",
|
||||
"name": "cleverogre/ogre-captcha",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.6",
|
||||
"title": "Ogre CAPTCHA",
|
||||
"description": "Protect your site with CAPTCHA",
|
||||
"author": "CleverOgre",
|
||||
|
||||
+35
-1
@@ -3,7 +3,7 @@ Contributors: ogrecooper, cleverogre
|
||||
Tested up to: 7.0
|
||||
Requires at least: 6.0
|
||||
Requires PHP: 8.2
|
||||
Version: 1.0.1
|
||||
Version: 1.2.6
|
||||
License: GPLv3 or later
|
||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
Copyright: CleverOgre
|
||||
@@ -28,6 +28,40 @@ If you don't know what plugin you have downloaded, please contact [CleverOgre](t
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 1.2.6 - 2026-07-15 =
|
||||
* Fix bug with lost password reset when using AIOWPS
|
||||
|
||||
= 1.2.5 - 2026-07-01 =
|
||||
* Improve API key input attributes
|
||||
|
||||
= 1.2.4 - 2026-06-18 =
|
||||
* Minor bug fixes
|
||||
|
||||
= 1.2.3 - 2026-06-12 =
|
||||
* Fix optional argument in CLI
|
||||
|
||||
= 1.2.2 - 2026-06-11 =
|
||||
* Prevent main class loading bug
|
||||
* Fix WP CLI associative arguments
|
||||
|
||||
= 1.2.1 - 2026-06-09 =
|
||||
* Added WP CLI commands to modify settings
|
||||
|
||||
= 1.2.0 - 2026-06-09 =
|
||||
* Add floating widget mode support
|
||||
* Add simple select all toggle for form locations settings
|
||||
|
||||
= 1.1.2 - 2026-06-08 =
|
||||
* Prevent captcha in Gravity Forms editor
|
||||
* Add default widget styling
|
||||
* Fix validation on multi-page forms
|
||||
|
||||
= 1.1.1 - 2026-06-08 =
|
||||
* Fixed Gravity Forms addon initialization
|
||||
|
||||
= 1.1.0 - 2026-06-05 =
|
||||
* Added Gravity Forms support
|
||||
|
||||
= 1.0.1 - 2026-06-05 =
|
||||
* Update to use UpdatePulse server
|
||||
|
||||
|
||||
Reference in New Issue
Block a user