Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .distignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
/.yarn
/build
/coverage
/docs/tasks
/node_modules
/src/js
/tests
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ assets/js/apps/
assets/js/**/*.min.js
assets/js/**/*.min.js.map
build/
CLAUDE-SECURITY-*/
coverage/
docs/tasks/
node_modules/
vendor/

Expand Down
1 change: 1 addition & 0 deletions assets/images/logo/metform.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions assets/js/command-palette.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Register hCaptcha commands in the WordPress Command Palette.
*/
const commandPalette = function() {
const config = window.HCaptchaCommandPaletteObject || {};
const commands = Array.isArray( config.commands ) ? config.commands : [];
const commandsStore = window.wp?.commands?.store;
const dispatch = window.wp?.data?.dispatch;

if ( ! commands.length || ! commandsStore || ! dispatch ) {
return;
}

const { registerCommand } = dispatch( commandsStore );

if ( typeof registerCommand !== 'function' ) {
return;
}

commands.forEach( ( command ) => {
if ( ! command.name || ! command.label || ! command.url ) {
return;
}

registerCommand( {
name: command.name,
label: command.label,
searchLabel: command.searchLabel || command.label,
category: command.category || 'view',
keywords: Array.isArray( command.keywords ) ? command.keywords : [],
callback: ( { close } = {} ) => {
if ( typeof close === 'function' ) {
close();
}

commandPalette.navigate( command.url );
},
} );
} );
};

commandPalette.navigate = function( url ) {
window.location.href = url;
};

window.hCaptchaCommandPalette = commandPalette;
document.addEventListener( 'DOMContentLoaded', commandPalette );
56 changes: 56 additions & 0 deletions assets/js/hcaptcha-metform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* global jQuery */

import { helper } from './hcaptcha-helper.js';

const hCaptchaMetForm = window.hCaptchaMetForm || ( function( window, $ ) {
const nonceName = 'hcaptcha_metform_nonce';
const restRoute = '/metform/v1/entries/insert/';

const app = {
init() {
helper.installFetchEvents();
$( document ).on( 'metform/after_form_load.hcaptchaMetForm', '.mf-form-wrapper', app.bindEvents );
$( document ).on( 'metform/before_submit.hcaptchaMetForm', '.mf-form-wrapper', app.beforeSubmit );
window.addEventListener( 'hCaptchaFetch:complete', app.fetchComplete );
app.bindEvents();
},

bindEvents() {
$( '.hcaptcha-metform-placeholder[data-hcaptcha-html]' ).each( function() {
const placeholder = $( this );
const html = window.decodeURIComponent( placeholder.attr( 'data-hcaptcha-html' ) );

placeholder.html( html ).removeAttr( 'data-hcaptcha-html' );
} );

if ( typeof window.hCaptchaBindEvents === 'function' ) {
window.hCaptchaBindEvents();
}
},

beforeSubmit( event, formData ) {
if ( ! formData || typeof formData !== 'object' ) {
return;
}

Object.assign( formData, helper.getHCaptchaData( event.currentTarget, nonceName ) );
},

fetchComplete( event ) {
const resource = event?.detail?.args?.[ 0 ];
const url = typeof resource === 'string' ? resource : ( resource?.url || '' );

if ( ! url.includes( restRoute ) ) {
return;
}

app.bindEvents();
},
};

return app;
}( window, jQuery ) );

window.hCaptchaMetForm = hCaptchaMetForm;

hCaptchaMetForm.init();
41 changes: 37 additions & 4 deletions assets/js/hcaptcha-woocommerce-paypal-payments.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ const app = {
const requestBody = app.getJsonBody( requestConfig.body );

if (
app.isCheckoutContext( requestBody.context ) &&
app.usesCheckoutCaptcha( requestBody.context ) &&
! requestBody[ 'h-captcha-response' ]
) {
await app.executeCheckoutCaptcha();
Expand Down Expand Up @@ -280,7 +280,7 @@ const app = {
const requestConfig = { ...config };
const body = app.getJsonBody( requestConfig.body );

if ( app.isCheckoutContext( body.context ) ) {
if ( app.usesCheckoutCaptcha( body.context ) ) {
Object.assign( body, app.getCheckoutCaptchaData() );
} else {
Object.assign(
Expand All @@ -307,6 +307,12 @@ const app = {
return [ 'checkout', 'checkout-block' ].includes( context );
},

usesCheckoutCaptcha( context ) {
const checkoutRoot = app.getCheckoutCaptchaRoot();

return app.isCheckoutContext( context ) && app.hasCheckoutCaptcha( checkoutRoot );
},

async executeCaptchaBeforePayPal( wrapper ) {
if ( wrapper && ! app.isCheckoutButtonWrapper( wrapper ) ) {
await app.executePayPalCaptcha( wrapper );
Expand Down Expand Up @@ -507,6 +513,8 @@ const app = {
},

moveBlockCaptcha() {
app.moveClassicCartCaptcha();

const buttonContainers = document.querySelectorAll(
'.wc-block-components-express-payment__event-buttons',
);
Expand All @@ -519,8 +527,33 @@ const app = {
continue;
}

if ( buttonContainer.nextElementSibling !== captcha ) {
buttonContainer.after( captcha );
if ( buttonContainer.previousElementSibling !== captcha ) {
buttonContainer.before( captcha );
}

captcha.style.removeProperty( 'display' );
}
},

moveClassicCartCaptcha() {
const checkoutContainers = document.querySelectorAll(
'.wc-proceed-to-checkout',
);

for ( const checkoutContainer of checkoutContainers ) {
const checkoutButton = checkoutContainer.querySelector(
'.checkout-button',
);
const captcha = checkoutContainer.querySelector(
'.hcaptcha-woocommerce-paypal-payments',
);

if ( ! checkoutButton || ! captcha ) {
continue;
}

if ( checkoutButton.nextElementSibling !== captcha ) {
checkoutButton.after( captcha );
}

captcha.style.removeProperty( 'display' );
Expand Down
14 changes: 14 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
= 5.2.0 =
* Added MetForm integration.
* Added MetForm migration to the Migration Wizard.
* Added an option to delay the hCaptcha API until the user interacts with a protected form.
* Added hCaptcha settings shortcuts to the WordPress Command Palette for quick navigation to plugin options.
* Added validation of hCaptcha keys before making requests to fix the missing keys' scenario.
* Added support for custom login URLs in Perfmatters 2.5.8 and later.
* Fixed unbounded growth of the auto-verification form registry transient by limiting its size with LRU eviction.
* Fixed hCaptcha placement above express payment buttons on WooCommerce cart pages.
* Fixed hCaptcha signature verification for nested Kadence Advanced Forms and pages with multiple Advanced Forms.
* Fixed hCaptcha signature verification for Divi Comment Forms when the WordPress Comment Form integration is disabled.
* Fixed fatal errors on Gravity Forms submissions containing Multi Select fields.
* Fixed compatibility with Maintenance 4.32.

= 5.1.0 =
* Added version switching to the What's New popup.
* Added a Help button on hCaptcha admin pages to generate support reports for GitHub or WordPress.org, with optional System Info included.
Expand Down
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,18 @@
},
"require-dev": {
"ext-fileinfo": "*",
"10up/wp_mock": "^1.1.0",
"10up/wp_mock": "^1.1.1",
"antecedent/patchwork": "^2.2.3",
"behat/gherkin": "<=v4.12.0",
"codeception/codeception": "4.2.2 - 5.3.5",
"codeception/module-db": "1.2.0 - 3.2.2",
"codeception/module-webdriver": "1.4.1 - 3.2.2",
"lucatume/function-mocker": "^2.0.0",
"lucatume/wp-browser": "3.7.19 - 4.5.15",
"lucatume/wp-browser": "3.7.19 - 4.7.1",
"squizlabs/php_codesniffer": "^3.13.5",
"phpcompatibility/php-compatibility": "^9.3.5",
"phpcompatibility/phpcompatibility-wp": "^2.1.8",
"wp-coding-standards/wpcs": "^3.3.0"
"wp-coding-standards/wpcs": "^3.4.1"
},
"autoload": {
"psr-4": {
Expand Down
6 changes: 3 additions & 3 deletions hcaptcha.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Plugin Name: hCaptcha for WP
* Plugin URI: https://www.hcaptcha.com/
* Description: hCaptcha keeps out bots and spam while putting privacy first. It is a drop-in replacement for reCAPTCHA.
* Version: 5.1.0
* Version: 5.2.0
* Requires at least: 6.0
* Requires PHP: 7.4
* Author: hCaptcha
Expand All @@ -21,7 +21,7 @@
* Domain Path: /languages/
*
* WC requires at least: 3.0
* WC tested up to: 10.9
* WC tested up to: 11.0
*/

// phpcs:ignore Generic.Commenting.DocComment.MissingShort
Expand All @@ -39,7 +39,7 @@
/**
* Plugin version.
*/
const HCAPTCHA_VERSION = '5.1.0';
const HCAPTCHA_VERSION = '5.2.0';

/**
* Path to the plugin dir.
Expand Down
39 changes: 22 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,52 +35,57 @@
}
},
"engines": {
"node": ">=24.15.0",
"npm": ">=11.12.1"
"node": ">=24.18.1",
"npm": ">=12.0.2"
},
"packageManager": "yarn@4.17.0",
"packageManager": "yarn@4.18.0",
"dependencies": {
"@wordpress/hooks": "^4.49.0"
"@wordpress/hooks": "^4.52.0"
},
"devDependencies": {
"@babel/core": "^7.29.7",
"@babel/preset-env": "^7.29.7",
"@inrupt/jest-jsdom-polyfills": "^4.1.4",
"@wordpress/eslint-plugin": "^25.5.0",
"@inrupt/jest-jsdom-polyfills": "^4.1.5",
"@wordpress/eslint-plugin": "^25.8.0",
"babel-loader": "^10.1.1",
"css-loader": "^7.1.4",
"css-minimizer-webpack-plugin": "^8.0.0",
"eslint": "^10.6.0",
"eslint": "^10.8.0",
"glob": "^13.0.6",
"globals": "^17.7.0",
"globals": "^17.8.0",
"jest": "^30.4.2",
"jest-environment-jsdom": "^30.4.1",
"jest-fetch-mock": "^3.0.3",
"jest-fetch-mock": "^3.2.0",
"jquery": "^3.7.1",
"mini-css-extract-plugin": "^2.10.2",
"prettier": "^3.8.5",
"prettier": "^3.9.6",
"terser-webpack-plugin": "^5.6.1",
"typescript": "^6.0.3",
"webpack": "^5.108.1",
"webpack-cli": "^7.1.0",
"webpack": "^5.109.2",
"webpack-cli": "^7.2.2",
"webpack-remove-empty-scripts": "^1.1.1"
},
"resolutions": {
"@babel/plugin-transform-modules-systemjs": "^7.29.7",
"brace-expansion@npm:^1.1.7": "^1.1.16",
"brace-expansion@npm:^2.0.2": "^2.1.2",
"brace-expansion@npm:^5.0.5": "^5.0.8",
"cross-spawn": "^7.0.6",
"fast-uri": "^3.1.2",
"fast-uri": "^3.1.5",
"flatted": "^3.4.2",
"js-yaml": "^4.3.0",
"micromatch": "^4.0.8",
"minimatch@npm:^3": "^3.1.5",
"minimatch@npm:9.0.3": "^9.0.9",
"minimatch@npm:^10": "^10.2.5",
"minimatch@npm:^10.2.2": "^10.2.6",
"minimatch@npm:^10.2.4": "^10.2.6",
"picomatch@npm:^2": "^2.3.14",
"picomatch@npm:^4": "^4.0.5",
"postcss": "^8.5.25",
"qs": "^6.15.3",
"serialize-javascript": "^7.0.6",
"svgo": "^4.0.1",
"tar": "^7.5.17",
"undici": "^7.28.0"
"svgo": "^4.0.2",
"tar": "^7.5.22",
"undici": "^7.29.0"
}
}
Loading