diff --git a/Lib/WeDevs_Settings_API.php b/Lib/WeDevs_Settings_API.php
index 7a5cd9ad5..9f3098aa8 100644
--- a/Lib/WeDevs_Settings_API.php
+++ b/Lib/WeDevs_Settings_API.php
@@ -140,6 +140,7 @@ function admin_init() {
'max' => isset( $option['max'] ) ? $option['max'] : '',
'step' => isset( $option['step'] ) ? $option['step'] : '',
'is_pro_preview' => ! empty( $option['is_pro_preview'] ) ? $option['is_pro_preview'] : false,
+ 'depends_on' => ! empty( $option['depends_on'] ) ? $option['depends_on'] : '',
);
add_settings_field( $section . '[' . $option['name'] . ']', $option['label'], (isset($option['callback']) ? $option['callback'] : array($this, 'callback_' . $type )), $section, $section, $args );
@@ -173,14 +174,17 @@ public function get_field_description( $args ) {
* @param array $args settings field args
*/
function callback_text( $args ) {
-
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
$type = isset( $args['type'] ) ? $args['type'] : 'text';
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
$disabled = ! empty( $args['is_pro_preview'] ) && $args['is_pro_preview'] ? 'disabled' : '';
+ $depends_on = ! empty( $args['depends_on'] ) ? $args['depends_on'] : '';
- $html = sprintf( '', $type, $size, $args['section'], $args['id'], $value, $placeholder, $disabled );
+ $html = sprintf(
+ '',
+ $type, $size, $args['section'], $args['id'], $value, $placeholder, $disabled, $depends_on
+ );
$html .= $this->get_field_description( $args );
if ( ! empty( $args['is_pro_preview'] ) && $args['is_pro_preview'] ) {
@@ -460,6 +464,38 @@ function callback_color( $args ) {
echo $html;
}
+ /**
+ * Displays a toggle field for a settings field
+ *
+ * @param array $args settings field args
+ */
+ public function callback_toggle( $args ) {
+ $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
+ $disabled = ! empty( $args['is_pro_preview'] ) && $args['is_pro_preview'] ? 'disabled' : '';
+ $name = $args['section'] . '[' . $args['id'] . ']';
+ ?>
+
+
+
+
diff --git a/admin/form-builder/assets/js/components/form-cloudflare_turnstile/index.js b/admin/form-builder/assets/js/components/form-cloudflare_turnstile/index.js
new file mode 100644
index 000000000..1de5a8a4b
--- /dev/null
+++ b/admin/form-builder/assets/js/components/form-cloudflare_turnstile/index.js
@@ -0,0 +1,36 @@
+/**
+ * Field template: Cloudflare Turnstile
+ */
+Vue.component('form-cloudflare_turnstile', {
+ template: '#tmpl-wpuf-form-cloudflare_turnstile',
+
+ mixins: [
+ wpuf_mixins.form_field_mixin
+ ],
+
+ computed: {
+ has_turnstile_api_keys: function () {
+ return wpuf_form_builder.turnstile_site && wpuf_form_builder.turnstile_secret;
+ },
+
+ no_api_keys_msg: function () {
+ return wpuf_form_builder.field_settings.turnstile.validator.msg;
+ },
+
+ turnstile_image: function () {
+ var base_url = wpuf_form_builder.asset_url + '/images/cloudflare-placeholder-';
+
+ if (this.field.turnstile_theme === 'dark') {
+ base_url += 'dark';
+ } else {
+ base_url += 'light';
+ }
+
+ if (this.field.turnstile_size === 'compact') {
+ base_url += '-compact';
+ }
+
+ return base_url + '.png';
+ }
+ }
+});
diff --git a/admin/form-builder/assets/js/components/form-cloudflare_turnstile/template.php b/admin/form-builder/assets/js/components/form-cloudflare_turnstile/template.php
new file mode 100644
index 000000000..6eb749161
--- /dev/null
+++ b/admin/form-builder/assets/js/components/form-cloudflare_turnstile/template.php
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/admin/form-builder/assets/js/mixins/global.js b/admin/form-builder/assets/js/mixins/global.js
index 60ecffd03..0482a6834 100644
--- a/admin/form-builder/assets/js/mixins/global.js
+++ b/admin/form-builder/assets/js/mixins/global.js
@@ -44,6 +44,10 @@ Vue.mixin({
return (wpuf_form_builder.recaptcha_site && wpuf_form_builder.recaptcha_secret) ? true : false;
},
+ has_turnstile_api_keys: function () {
+ return wpuf_form_builder.turnstile_site && wpuf_form_builder.turnstile_secret;
+ },
+
containsField: function(field_name) {
var self = this,
i = 0;
diff --git a/assets/css/admin.css b/assets/css/admin.css
index bfd70f2d3..27f72c7db 100644
--- a/assets/css/admin.css
+++ b/assets/css/admin.css
@@ -944,6 +944,50 @@ span.pro-icon:hover .wpuf-pro-field-tooltip {
.wrap h2.with-headway-icon #HW_frame_cont {
top: 78px !important;
}
+.wpuf-toggle-switch {
+ position: relative;
+ display: inline-block;
+ width: 50px;
+ height: 26px;
+}
+.wpuf-toggle-switch input {
+ display: none;
+}
+.wpuf-toggle-switch input:checked + .slider {
+ background-color: #0073aa;
+}
+.wpuf-toggle-switch input:focus + .slider {
+ box-shadow: 0 0 1px #2196F3;
+}
+.wpuf-toggle-switch input:checked + .slider:before {
+ transform: translateX(26px);
+}
+.wpuf-toggle-switch .slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #ccc;
+ transition: .2s;
+}
+.wpuf-toggle-switch .slider.round {
+ border-radius: 34px;
+}
+.wpuf-toggle-switch .slider.round:before {
+ border-radius: 50%;
+}
+.wpuf-toggle-switch .slider::before {
+ position: absolute;
+ content: "";
+ height: 18px;
+ width: 18px;
+ left: 3px;
+ bottom: 4px;
+ background-color: white;
+ transition: .2s;
+}
@media only screen and (max-width: 1399px) {
a.wpuf-button.button-upgrade-to-pro {
padding: 10px;
diff --git a/assets/css/admin/wpuf-module.css b/assets/css/admin/wpuf-module.css
index 95c0f032f..3485758af 100644
--- a/assets/css/admin/wpuf-module.css
+++ b/assets/css/admin/wpuf-module.css
@@ -35,65 +35,6 @@
line-height: 1.6em;
}
-.wpuf-toggle-switch {
- position: relative;
- display: inline-block;
- width: 50px;
- height: 26px;
-}
-
-.wpuf-toggle-switch input {
- display: none;
-}
-
-.wpuf-toggle-switch .slider {
- position: absolute;
- cursor: pointer;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: #ccc;
- -webkit-transition: .4s;
- transition: .4s;
-}
-
-.wpuf-toggle-switch .slider:before {
- position: absolute;
- content: "";
- height: 18px;
- width: 18px;
- left: 3px;
- bottom: 4px;
- background-color: white;
- -webkit-transition: .4s;
- transition: .4s;
-}
-
-.wpuf-toggle-switch input:checked + .slider {
- background-color: #0073aa;
-}
-
-.wpuf-toggle-switch input:focus + .slider {
- -webkit-box-shadow: 0 0 1px #2196F3;
- box-shadow: 0 0 1px #2196F3;
-}
-
-.wpuf-toggle-switch input:checked + .slider:before {
- -webkit-transform: translateX(26px);
- -ms-transform: translateX(26px);
- transform: translateX(26px);
-}
-
-/* Rounded sliders */
-.slider.round {
- border-radius: 34px;
-}
-
-.slider.round:before {
- border-radius: 50%;
-}
-
.wpuf-modules .plugin-card {
border: 1px solid #e5e5e5;
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
diff --git a/assets/css/frontend-forms.css b/assets/css/frontend-forms.css
index 0ff853676..39fb30fe9 100644
--- a/assets/css/frontend-forms.css
+++ b/assets/css/frontend-forms.css
@@ -84,7 +84,7 @@ body .wpuf-error {
background-color: #f2dede;
color: #a94442;
border: 1px solid #ebccd1;
- margin: 10px 10px 20px;
+ margin: 10px 0 20px 0;
padding: 10px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
diff --git a/assets/images/cloudflare-placeholder-dark-compact.png b/assets/images/cloudflare-placeholder-dark-compact.png
new file mode 100644
index 000000000..ed8eae746
Binary files /dev/null and b/assets/images/cloudflare-placeholder-dark-compact.png differ
diff --git a/assets/images/cloudflare-placeholder-dark.png b/assets/images/cloudflare-placeholder-dark.png
new file mode 100644
index 000000000..417250f0e
Binary files /dev/null and b/assets/images/cloudflare-placeholder-dark.png differ
diff --git a/assets/images/cloudflare-placeholder-light-compact.png b/assets/images/cloudflare-placeholder-light-compact.png
new file mode 100644
index 000000000..be7012d1c
Binary files /dev/null and b/assets/images/cloudflare-placeholder-light-compact.png differ
diff --git a/assets/images/cloudflare-placeholder-light.png b/assets/images/cloudflare-placeholder-light.png
new file mode 100644
index 000000000..b63cd087c
Binary files /dev/null and b/assets/images/cloudflare-placeholder-light.png differ
diff --git a/assets/js-templates/form-components.php b/assets/js-templates/form-components.php
index ca68db439..93414ac83 100644
--- a/assets/js-templates/form-components.php
+++ b/assets/js-templates/form-components.php
@@ -438,6 +438,21 @@ class="option-chooser-radio"
+
+
+
+ 'has_turnstile_api_keys',
+ 'button_class' => 'button-faded',
+ 'msg_title' => __( 'Site key and Secret key', 'wp-user-frontend' ),
+ 'msg' => sprintf(
+ // translators: %s: settings url
+ __( 'You need to set Site key and Secret key in Settings in order to use "Cloudflare Turnstile" field. Click here to get the these key.', 'wp-user-frontend' ),
+ admin_url( 'admin.php?page=wpuf-settings' ),
+ 'https://developers.cloudflare.com/turnstile/'
+ ),
+ ];
+ }
+
+ /**
+ * Get field options setting
+ *
+ * @since WPUF_SINCE
+ *
+ * @return array
+ */
+ public function get_options_settings() {
+ $settings = [
+ [
+ 'name' => 'label',
+ 'title' => __( 'Title', 'wp-user-frontend' ),
+ 'type' => 'text',
+ 'section' => 'basic',
+ 'priority' => 10,
+ 'help_text' => __( 'Title of the section', 'wp-user-frontend' ),
+ ],
+ [
+ 'name' => 'turnstile_theme',
+ 'title' => 'Turnstile Theme',
+ 'type' => 'radio',
+ 'options' => [
+ 'light' => __( 'Light', 'wp-user-frontend' ),
+ 'dark' => __( 'Dark', 'wp-user-frontend' ),
+ ],
+ 'default' => 'light',
+ 'section' => 'basic',
+ 'priority' => 12,
+ 'help_text' => __( 'Select turnstile theme', 'wp-user-frontend' ),
+ ],
+ [
+ 'name' => 'turnstile_size',
+ 'title' => 'Turnstile Size',
+ 'type' => 'radio',
+ 'options' => [
+ 'normal' => __( 'Normal [Width: 300px, Height: 65px]', 'wp-user-frontend' ),
+ 'flexible' => __( 'Flexible [Width: 100% (min: 300px), Height: 65px]', 'wp-user-frontend' ),
+ 'compact' => __( 'Compact [Width: 150px, Height: 140px]', 'wp-user-frontend' ),
+ ],
+ 'default' => 'normal',
+ 'section' => 'basic',
+ 'priority' => 13,
+ 'help_text' => __( 'Select turnstile size', 'wp-user-frontend' ),
+ ],
+ [
+ 'name' => 'turnstile_type',
+ 'title' => 'Turnstile type',
+ 'type' => 'radio',
+ 'options' => [
+ 'managed' => __( 'Managed (recommended)', 'wp-user-frontend' ),
+ 'non_interactive' => __( 'Non-Interactive', 'wp-user-frontend' ),
+ 'invisible' => __( 'Invisible', 'wp-user-frontend' ),
+ ],
+ 'default' => 'managed',
+ 'section' => 'advanced',
+ 'priority' => 11,
+ 'help_text' => __( 'Select turnstile type', 'wp-user-frontend' ),
+ ],
+ ];
+
+ return $settings;
+ }
+
+ /**
+ * Get the field props
+ *
+ * @since WPUF_SINCE
+ *
+ * @return array
+ */
+ public function get_field_props() {
+
+ $props = [
+ 'input_type' => 'turnstile',
+ 'template' => $this->get_type(),
+ 'label' => '',
+ 'turnstile_type' => 'managed',
+ 'turnstile_theme' => 'light',
+ 'turnstile_size' => 'normal',
+ 'is_meta' => 'yes',
+ 'id' => 0,
+ 'wpuf_cond' => null,
+ ];
+
+ return $props;
+ }
+}
diff --git a/includes/Free/Simple_Login.php b/includes/Free/Simple_Login.php
index 200e7f638..2afac5da4 100644
--- a/includes/Free/Simple_Login.php
+++ b/includes/Free/Simple_Login.php
@@ -19,6 +19,13 @@ class Simple_Login {
private $messages = [];
+ /**
+ * Cloudflare Turnstile messages
+ *
+ * @var array
+ */
+ private $cf_messages = [];
+
private static $_instance;
public function __construct() {
@@ -339,7 +346,7 @@ public function login_form() {
$reset = isset( $getdata['reset'] ) ? sanitize_text_field( $getdata['reset'] ) : '';
if ( false === $login_page ) {
- return;
+ return '';
}
ob_start();
@@ -394,6 +401,11 @@ public function login_form() {
default:
$loggedout = isset( $getdata['loggedout'] ) ? sanitize_text_field( $getdata['loggedout'] ) : '';
+ $enable_turnstile = wpuf_get_option( 'enable_turnstile', 'wpuf_general', 'off' );
+
+ if ( 'on' === $enable_turnstile ) {
+ wp_enqueue_script( 'wpuf-turnstile' );
+ }
if ( $loggedout === 'true' ) {
$this->messages[] = __( 'You are now logged out.', 'wp-user-frontend' );
@@ -410,6 +422,52 @@ public function login_form() {
return ob_get_clean();
}
+ /**
+ * Verify if cloudflare turnstile request is successful
+ *
+ * @since WPUF_SINCE
+ *
+ * @return bool
+ */
+ private function verify_cloudflare_turnstile_on_login() {
+ $nonce = isset( $_POST['wpuf-login-nonce'] ) ? sanitize_key( wp_unslash( $_POST['wpuf-login-nonce'] ) ) : '';
+
+ if ( isset( $nonce ) && ! wp_verify_nonce( $nonce, 'wpuf_login_action' ) ) {
+ return false;
+ }
+
+ $secret = wpuf_get_option( 'turnstile_secret_key', 'wpuf_general', '' );
+
+ if ( empty( $secret ) ) {
+ return false;
+ }
+
+ $remote_addr = ! empty( $_SERVER['REMOTE_ADDR'] ) ? sanitize_url(
+ wp_unslash( $_SERVER['REMOTE_ADDR'] )
+ ) : '';
+
+ $cf_url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
+ $token = ! empty( $_POST['cf-turnstile-response'] ) ? sanitize_text_field( wp_unslash( $_POST['cf-turnstile-response'] ) ) : '';
+
+ // Request data
+ $data = [
+ 'secret' => $secret,
+ 'response' => $token,
+ 'remoteip' => $remote_addr,
+ ];
+
+ $response = wp_remote_post( $cf_url, [ 'body' => $data ] );
+ $body = json_decode( wp_remote_retrieve_body( $response ), true );
+
+ if ( ! empty( $body['success'] ) ) {
+ return true;
+ } else {
+ $this->cf_messages[] = ! empty( $body['error-codes'] ) ? $body['error-codes'] : '';
+
+ return false;
+ }
+ }
+
/**
* Remove selected cookie to have consistency with the login nonce.
* fixes WooCommerce Stripe Gateway plugin conflict
@@ -447,10 +505,24 @@ public function process_login() {
return;
}
- $log = isset( $_POST['log'] ) ? esc_attr( wp_unslash( $_POST['log'] ) ) : '';
- $pwd = isset( $_POST['pwd'] ) ? trim( $_POST['pwd'] ) : '';
+ $log = isset( $_POST['log'] ) ? sanitize_text_field( wp_unslash( $_POST['log'] ) ) : '';
+ $pwd = isset( $_POST['pwd'] ) ? sanitize_text_field( ( wp_unslash( $_POST['pwd'] ) ) ) : '';
// $g_recaptcha_response = isset( $_POST['g-recaptcha-response'] ) ? sanitize_text_field( wp_unslash( $_POST['g-recaptcha-response'] ) ) : '';
+ if ( ! $this->verify_cloudflare_turnstile_on_login() ) {
+ $errors = ! empty( $this->cf_messages[0] ) ? $this->cf_messages[0] : '';
+ $errors = implode( ', ', $errors );
+ $this->login_errors[] =
+ sprintf(
+ // translators: %1$s and %2$s are strong tags, %3$s is the error message
+ __( '%1$sError%2$s: Cloudflare Turnstile verification failed. Reasons: [%3$s]', 'wp-user-frontend' ),
+ '',
+ '',
+ $errors
+ );
+ '' . __( 'Error', 'wp-user-frontend' ) . ': ' . __( 'Cloudflare Turnstile verification failed. Reasons: [', 'wp-user-frontend' );
+ }
+
$validation_error = new WP_Error();
$validation_error = apply_filters( 'wpuf_process_login_errors', $validation_error, $log, $pwd );
diff --git a/includes/functions/settings-options.php b/includes/functions/settings-options.php
index 16d75bb82..315862b35 100644
--- a/includes/functions/settings-options.php
+++ b/includes/functions/settings-options.php
@@ -159,6 +159,29 @@ function wpuf_settings_fields() {
'desc' => __( 'Register here to get reCaptcha Site and Secret keys.',
'wp-user-frontend' ),
],
+ [
+ 'name' => 'enable_turnstile',
+ 'label' => __( 'Enable Turnstile', 'wp-user-frontend' ),
+ 'type' => 'toggle',
+ 'default' => 'off',
+ ],
+ [
+ 'name' => 'turnstile_site_key',
+ 'label' => __( 'Turnstile Site Key', 'wp-user-frontend' ),
+ 'depends_on' => 'enable_turnstile',
+ ],
+ [
+ 'name' => 'turnstile_secret_key',
+ 'label' => __( 'Turnstile Secret Key', 'wp-user-frontend' ),
+ 'depends_on' => 'enable_turnstile',
+ 'desc' => sprintf(
+ // translators: %s is a link
+ __(
+ 'Register here to get Turnstile Site and Secret keys.',
+ 'wp-user-frontend'
+ ), esc_url( 'https://developers.cloudflare.com/turnstile/' )
+ ),
+ ],
[
'name' => 'custom_css',
'label' => __( 'Custom CSS codes', 'wp-user-frontend' ),
@@ -431,6 +454,17 @@ function wpuf_settings_fields() {
'type' => 'checkbox',
'default' => 'off',
],
+ [
+ 'name' => 'login_form_turnstile',
+ 'label' => __( 'Turnstile in Login Form', 'wp-user-frontend' ),
+ 'desc' => __(
+ 'If enabled, users have to verify Cloudflare Turnstile in login page. Also, make sure that Turnstile is configured properly from General Options',
+ 'wp-user-frontend'
+ ),
+ 'type' => 'toggle',
+ 'default' => 'off',
+ 'depends_on' => 'enable_turnstile',
+ ],
] ),
'wpuf_payment' => apply_filters( 'wpuf_options_payment', [
[
diff --git a/templates/login-form.php b/templates/login-form.php
index c23aaacb1..2b12921ea 100644
--- a/templates/login-form.php
+++ b/templates/login-form.php
@@ -30,8 +30,13 @@
-
-
+
+