diff --git a/framework/elements/formbuilder/ajax.php b/framework/elements/formbuilder/ajax.php index 38231421..5687bd22 100644 --- a/framework/elements/formbuilder/ajax.php +++ b/framework/elements/formbuilder/ajax.php @@ -15,8 +15,8 @@ use Joomla\CMS\Factory; use Joomla\CMS\Mail\MailerFactoryInterface; -use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Language\Text; +use Joomla\Utilities\IpHelper; use Astroid\Helper; use Astroid\Framework; $mainframe = Factory::getApplication(); @@ -45,7 +45,7 @@ $mail = Factory::getContainer()->get(MailerFactoryInterface::class)->createMailer(); $message = $params->get('email_body', ''); $email_headers = $params->get('email_headers', ''); - $gcaptcha = $mainframe->input->post->get('g-recaptcha-response'); + $pluginParams = Helper::getPluginParams('captcha', 'astroidcaptcha'); foreach ($asformbuilder as $field => $value) { @@ -57,20 +57,27 @@ if (intval($params->get('enable_captcha', 0))) { $captcha_type = $pluginParams->get('captcha_type', 'default'); $invalidCaptchaMessage = Text::_('ASTROID_AJAX_ERROR_INVALID_CAPTCHA'); - if ($captcha_type == 'recaptcha' || $captcha_type == 'recaptcha_invisible') { + if ($captcha_type == 'recaptcha') { + $gcaptcha = $mainframe->input->post->get('g-recaptcha-response'); if (empty($gcaptcha) || !Helper\Captcha::verifyGoogleCaptcha($gcaptcha)) { throw new \Exception($invalidCaptchaMessage); } + } elseif ($captcha_type == 'turnstile') { + $token = $mainframe->input->post->get('cf-turnstile-response'); + if (empty($token) || !Helper\Captcha::verifyCloudFlareTurnstile($token)) { + throw new \Exception($invalidCaptchaMessage); + } } elseif (!Helper\Captcha::getCaptcha('as-formbuilder-captcha')) { throw new \Exception($invalidCaptchaMessage); } } //get sender UP - $senderip = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']; + $senderip = IpHelper::getIp(); // Subject Structure - $site_name = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : ''; - $mail_subject = $params->get('email_subject', '') . ' - ' . $site_name; - $mail_subject = preg_replace_callback('/\{\{(\S+?)\}\}/siU', function ($matches) use (&$asformbuilder, &$site_name) { + $config = Factory::getApplication()->getConfig(); + $site_name = $config->get( 'sitename', '' ); + $mail_subject = $params->get('email_subject', ''); + $mail_subject = preg_replace_callback('/\{\{(\S+?)\}\}/siU', function ($matches) use (&$asformbuilder, &$site_name) { if (isset($asformbuilder[$matches[1]])) { return $asformbuilder[$matches[1]]; } elseif ($matches[1] == 'site-name') { @@ -82,8 +89,6 @@ $mail_body = $message; $mail_body .= '

' . Text::_('ASTROID_FORMBUILDER_SENDER_IP'). ': ' . $senderip .'

'; - $config = Factory::getConfig(); - $sender = array( $config->get( 'mailfrom' ), $config->get( 'fromname' ) ); $recipient = $config->get( 'mailfrom' ); diff --git a/framework/elements/formbuilder/formbuilder.php b/framework/elements/formbuilder/formbuilder.php index cbd54b1a..abd2436f 100644 --- a/framework/elements/formbuilder/formbuilder.php +++ b/framework/elements/formbuilder/formbuilder.php @@ -14,7 +14,6 @@ defined('_JEXEC') or die; use Joomla\CMS\Factory; -use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Session\Session; use Joomla\CMS\Language\Text; use Joomla\CMS\Uri\Uri; @@ -61,16 +60,21 @@ if ($params->get('enable_captcha', 0) == 1) { $captcha_type = $pluginParams->get('captcha_type', 'default'); $captcha_attr = ' data-captcha="'.$captcha_type.'"'; - if ($captcha_type == 'recaptcha' || $captcha_type == 'recaptcha_invisible') { - + if ($captcha_type == 'recaptcha') { + $size = $pluginParams->get('g_size', 'normal'); $captcha_attr .= ' data-sitekey="'.$pluginParams->get('g_site_key', '').'"'; - if ($captcha_type == 'recaptcha_invisible') { + $captcha_attr .= ' data-size="'.$size.'"'; + if ($size == 'invisible') { $document->loadGoogleReCaptcha([], 'explicit'); $captcha_attr .= ' data-badge="'.$pluginParams->get('badge', 'bottomright').'"'; } else { $document->loadGoogleReCaptcha(); } $captcha_attr .= ' data-tabindex="'.$pluginParams->get('tabindex', '0').'"'; + } else if ($captcha_type == 'turnstile') { + $captcha_attr .= ' data-sitekey="'.$pluginParams->get('t_site_key', '').'"'; + $captcha_attr .= ' data-size="'.$pluginParams->get('t_size', 'normal').'"'; + $document->loadCloudFlareTurnstile(); } } @@ -186,8 +190,10 @@ if ($params->get('enable_captcha', 0) == 1) { $captcha_type = $pluginParams->get('captcha_type', 'default'); echo '
'; - if ($captcha_type == 'recaptcha' || $captcha_type == 'recaptcha_invisible') { + if ($captcha_type == 'recaptcha') { echo '
'; + } else if ($captcha_type == 'turnstile') { + echo '
'; } else { echo Helper\Captcha::loadCaptcha('as-formbuilder-captcha'); } @@ -197,4 +203,4 @@ echo '
'; echo ''; -Factory::getApplication()->getDocument()->getWebAssetManager()->registerAndUseScript('astroid.formbuilder', "astroid/formbuilder.min.js", ['relative' => true, 'version' => 'auto'], [], ['jquery']); \ No newline at end of file +Factory::getApplication()->getDocument()->getWebAssetManager()->registerAndUseScript('astroid.formbuilder', "astroid/formbuilder.min.js", ['relative' => true, 'version' => 'auto']); \ No newline at end of file diff --git a/framework/library/astroid/Document.php b/framework/library/astroid/Document.php index 25abeecf..b5ce777c 100644 --- a/framework/library/astroid/Document.php +++ b/framework/library/astroid/Document.php @@ -1042,7 +1042,9 @@ public function loadGoogleReCaptcha($onload = [], $render = ''): void } if (!empty($onload['url'])) { $wa->registerAndUseScript('google.recaptcha.onload', $onload['url'], [], ['defer' => true]); - $query[] = 'onload=' . $onload['function']; + if (!empty($onload['function'])) { + $query[] = 'onload=' . $onload['function']; + } $depends[] = 'google.recaptcha.onload'; } if (!empty($render)) { @@ -1053,6 +1055,29 @@ public function loadGoogleReCaptcha($onload = [], $render = ''): void .implode('&',$query), [], ['defer' => true], $depends); } + public function loadCloudFlareTurnstile($onload = [], $render = ''): void + { + $app = Factory::getApplication(); + $wa = $app->getDocument()->getWebAssetManager(); + $query = array(); + $depends = []; + if (empty($onload)) { + $onload = ['url' => '', 'function' => '']; + } + if (!empty($onload['url'])) { + $wa->registerAndUseScript('cloudflare.turnstile.onload', $onload['url'], [], ['defer' => true]); + if (!empty($onload['function'])) { + $query[] = 'onload=' . $onload['function']; + } + $depends[] = 'cloudflare.turnstile.onload'; + } + if (!empty($render)) { + $query[] = 'render=' . $render; + } + $query = !empty($query) ? '?' . implode('&',$query) : ''; + $wa->registerAndUseScript('cloudflare.turnstile', 'https://challenges.cloudflare.com/turnstile/v0/api.js'.$query, [], ['defer' => true], $depends); + } + public function moveFile(&$array, $a, $b): void { $out = array_splice($array, $a, 1); diff --git a/framework/library/astroid/Helper/Captcha.php b/framework/library/astroid/Helper/Captcha.php index 7bd3d4ea..f91a6397 100644 --- a/framework/library/astroid/Helper/Captcha.php +++ b/framework/library/astroid/Helper/Captcha.php @@ -66,4 +66,41 @@ public static function verifyGoogleCaptcha($gRecaptchaResponse, $secretKey = '', } return true; } + + public static function verifyCloudFlareTurnstile($turnstileResponse, $secretKey = '') { + $app = Factory::getApplication(); + if (empty($secretKey)) { + $pluginParams = Helper::getPluginParams('captcha', 'astroidcaptcha'); + $secretKey = $pluginParams->get('t_secret_key', ''); + } + if (empty($secretKey)) { + throw new \RuntimeException($app->getLanguage()->_('ASTROID_GOOGLE_TURNSTILE_ERROR_NO_PRIVATE_KEY')); + } + $remoteip = IpHelper::getIp(); + // Check for IP + if (empty($remoteip)) { + throw new \RuntimeException($app->getLanguage()->_('ASTROID_GOOGLE_TURNSTILE_ERROR_NO_IP')); + } + if (empty($turnstileResponse)) { + throw new \RuntimeException($app->getLanguage()->_('ASTROID_GOOGLE_RECAPTCHA_ERROR_EMPTY_SOLUTION')); + } + $response = file_get_contents("https://challenges.cloudflare.com/turnstile/v0/siteverify", false, stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'header' => 'Content-type: application/x-www-form-urlencoded', + 'content' => http_build_query([ + 'secret' => $secretKey, + 'response' => $turnstileResponse, + 'remoteip' => $remoteip + ]), + ], + ])); + + $result = json_decode($response, true); + if ($result['success']) { + return true; + } else { + return false; + } + } } \ No newline at end of file diff --git a/js/formbuilder.js b/js/formbuilder.js index 015c820c..16c3769b 100644 --- a/js/formbuilder.js +++ b/js/formbuilder.js @@ -1,8 +1,10 @@ class FormBuilder { constructor(el) { this.el = el; - if (this.el.dataset.captcha === 'recaptcha' || this.el.dataset.captcha === 'recaptcha_invisible') { + if (this.el.dataset.captcha === 'recaptcha') { this.initReCaptcha(); + } else if (this.el.dataset.captcha === 'turnstile') { + this.initTurnstile(); } this.el.querySelector('.as-form-builer-submit').addEventListener('click', this.onSubmit.bind(this)); } @@ -15,11 +17,11 @@ class FormBuilder { let config = { 'sitekey': this.el.dataset.sitekey, 'tabindex': this.el.dataset.tabindex, + 'size': this.el.dataset.size, 'theme': color } - if (this.el.dataset.captcha === 'recaptcha_invisible') { + if (this.el.dataset.size === 'invisible') { config['badge'] = this.el.dataset.badge; - config['size'] = 'invisible'; config['callback'] = this.onCallAjax.bind(this); } grecaptcha.ready(() => { @@ -27,12 +29,25 @@ class FormBuilder { }); } + initTurnstile() { + let color = 'light'; + if (typeof ASTROID_COLOR_MODE !== 'undefined') { + color = ASTROID_COLOR_MODE; + } + let config = { + 'sitekey': this.el.dataset.sitekey, + 'size': this.el.dataset.size, + 'theme': color + } + turnstile.render(this.el.querySelector('.cloudflare-turnstile'), config); + } + onSubmit() { if (!this.el.checkValidity()) { this.el.classList.add('was-validated'); return; } - if (this.el.dataset.captcha === 'recaptcha_invisible') { + if (this.el.dataset.captcha === 'recaptcha' && this.el.dataset.size === 'invisible') { grecaptcha.execute(); } else { this.onCallAjax(); @@ -40,41 +55,45 @@ class FormBuilder { } onCallAjax(token) { - var request = {}, - $this = jQuery(this.el), - data = $this.serializeArray(); + let form = this.el; + let formData = new FormData(form); let id = Date.now() * 1000 + Math.random() * 1000; - id = id.toString(16).replace(/\./g, "").padEnd(14, "0")+Math.trunc(Math.random() * 100000000); - for (let i = 0; i < data.length; i++) { - request[data[i]['name']] = data[i]['value']; - } - request[$this.find('.token').attr('name')] = 1; - jQuery.ajax({ - type : 'POST', - url : $this.attr('action')+'&t='+id, - data : request, - beforeSend: function(){ - $this.find('.as-formbuilder-status').empty(); - $this.find('.as-form-builer-submit').attr('disabled', 'disabled'); - }, - success: function (response) { + id = id.toString(16).replace(/\./g, "").padEnd(14, "0") + Math.trunc(Math.random() * 100000000); + + var xhr = new XMLHttpRequest(); + xhr.open('POST', form.action + '&t=' + id, true); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + var response = JSON.parse(xhr.responseText); + var statusElement = form.querySelector('.as-formbuilder-status'); + statusElement.innerHTML = ''; + form.querySelector('.as-form-builer-submit').disabled = false; + form.classList.remove('was-validated'); + if (response.status === 'success') { - $this.find('.as-formbuilder-status').append(''); - $this.trigger("reset"); - $this.find('.google-recaptcha').each(function(){ - grecaptcha.reset(this); - }) + statusElement.innerHTML = ''; + form.reset(); + form.querySelectorAll('.google-recaptcha').forEach(function(el) { + grecaptcha.reset(el); + }); + form.querySelectorAll('.cloudflare-turnstile').forEach(function(el) { + turnstile.reset(el); + }); } else { - $this.find('.as-formbuilder-status').append(''); + statusElement.innerHTML = ''; } - $this.find('.as-form-builer-submit').removeAttr('disabled'); - $this.removeClass('was-validated'); } - }); + }; + + xhr.send(new URLSearchParams(formData).toString()); + form.querySelector('.as-formbuilder-status').innerHTML = ''; + form.querySelector('.as-form-builer-submit').disabled = true; } } -jQuery(function($) { - $('.as-form-builder').each(function() { - new FormBuilder(this); +document.addEventListener('DOMContentLoaded', function() { + document.querySelectorAll('.as-form-builder').forEach(function(el) { + new FormBuilder(el); }); }); \ No newline at end of file diff --git a/js/formbuilder.min.js b/js/formbuilder.min.js index 2280a386..2ce7aa5f 100644 --- a/js/formbuilder.min.js +++ b/js/formbuilder.min.js @@ -1 +1 @@ -class FormBuilder{constructor(el){this.el=el;if(this.el.dataset.captcha==="recaptcha"||this.el.dataset.captcha==="recaptcha_invisible"){this.initReCaptcha()}this.el.querySelector(".as-form-builer-submit").addEventListener("click",this.onSubmit.bind(this))}initReCaptcha(){let color="light";if(typeof ASTROID_COLOR_MODE!=="undefined"){color=ASTROID_COLOR_MODE}let config={sitekey:this.el.dataset.sitekey,tabindex:this.el.dataset.tabindex,theme:color};if(this.el.dataset.captcha==="recaptcha_invisible"){config["badge"]=this.el.dataset.badge;config["size"]="invisible";config["callback"]=this.onCallAjax.bind(this)}grecaptcha.ready(()=>{grecaptcha.render(this.el.querySelector(".google-recaptcha"),config)})}onSubmit(){if(!this.el.checkValidity()){this.el.classList.add("was-validated");return}if(this.el.dataset.captcha==="recaptcha_invisible"){grecaptcha.execute()}else{this.onCallAjax()}}onCallAjax(token){var request={},$this=jQuery(this.el),data=$this.serializeArray();let id=Date.now()*1e3+Math.random()*1e3;id=id.toString(16).replace(/\./g,"").padEnd(14,"0")+Math.trunc(Math.random()*1e8);for(let i=0;i'+response.message+"
");$this.trigger("reset");$this.find(".google-recaptcha").each(function(){grecaptcha.reset(this)})}else{$this.find(".as-formbuilder-status").append('")}$this.find(".as-form-builer-submit").removeAttr("disabled");$this.removeClass("was-validated")}})}}jQuery(function($){$(".as-form-builder").each(function(){new FormBuilder(this)})}); \ No newline at end of file +class FormBuilder{constructor(el){this.el=el;if(this.el.dataset.captcha==="recaptcha"){this.initReCaptcha()}else if(this.el.dataset.captcha==="turnstile"){this.initTurnstile()}this.el.querySelector(".as-form-builer-submit").addEventListener("click",this.onSubmit.bind(this))}initReCaptcha(){let color="light";if(typeof ASTROID_COLOR_MODE!=="undefined"){color=ASTROID_COLOR_MODE}let config={sitekey:this.el.dataset.sitekey,tabindex:this.el.dataset.tabindex,size:this.el.dataset.size,theme:color};if(this.el.dataset.size==="invisible"){config["badge"]=this.el.dataset.badge;config["callback"]=this.onCallAjax.bind(this)}grecaptcha.ready(()=>{grecaptcha.render(this.el.querySelector(".google-recaptcha"),config)})}initTurnstile(){let color="light";if(typeof ASTROID_COLOR_MODE!=="undefined"){color=ASTROID_COLOR_MODE}let config={sitekey:this.el.dataset.sitekey,size:this.el.dataset.size,theme:color};turnstile.render(this.el.querySelector(".cloudflare-turnstile"),config)}onSubmit(){if(!this.el.checkValidity()){this.el.classList.add("was-validated");return}if(this.el.dataset.captcha==="recaptcha"&&this.el.dataset.size==="invisible"){grecaptcha.execute()}else{this.onCallAjax()}}onCallAjax(token){let form=this.el;let formData=new FormData(form);let id=Date.now()*1e3+Math.random()*1e3;id=id.toString(16).replace(/\./g,"").padEnd(14,"0")+Math.trunc(Math.random()*1e8);var xhr=new XMLHttpRequest;xhr.open("POST",form.action+"&t="+id,true);xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");xhr.onreadystatechange=function(){if(xhr.readyState===4){var response=JSON.parse(xhr.responseText);var statusElement=form.querySelector(".as-formbuilder-status");statusElement.innerHTML="";form.querySelector(".as-form-builer-submit").disabled=false;form.classList.remove("was-validated");if(response.status==="success"){statusElement.innerHTML='";form.reset();form.querySelectorAll(".google-recaptcha").forEach(function(el){grecaptcha.reset(el)});form.querySelectorAll(".cloudflare-turnstile").forEach(function(el){turnstile.reset(el)})}else{statusElement.innerHTML='"}}};xhr.send(new URLSearchParams(formData).toString());form.querySelector(".as-formbuilder-status").innerHTML="";form.querySelector(".as-form-builer-submit").disabled=true}}document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll(".as-form-builder").forEach(function(el){new FormBuilder(el)})}); \ No newline at end of file diff --git a/language/en-GB/en-GB.astroid.ini b/language/en-GB/en-GB.astroid.ini index 21c552d9..9118ac2a 100644 --- a/language/en-GB/en-GB.astroid.ini +++ b/language/en-GB/en-GB.astroid.ini @@ -1193,6 +1193,7 @@ ASTROID_GOOGLE_SITE_KEY="Site Key" ASTROID_GOOGLE_SITE_KEY_DESC="Use this site key in the HTML code your site serves to users." ASTROID_GOOGLE_SECRET_KEY="Secret Key" ASTROID_GOOGLE_SECRET_KEY_DESC="Use this secret key for communication between your site and reCAPTCHA." +ASTROID_CAPTCHA_SIZE_LABEL="Size" ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_BADGE_BOTTOMLEFT="Bottom left" ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_BADGE_BOTTOMRIGHT="Bottom right" ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_BADGE_DESC="Positioning of the reCAPTCHA badge." @@ -1204,8 +1205,11 @@ ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_ERROR_CALLBACK_DESC="(Optional) JavaScript ca ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_ERROR_CALLBACK_LABEL="Error Callback" ASTROID_GOOGLE_RECAPTCHA_ERROR_EMPTY_SOLUTION="Empty solution not allowed." ASTROID_GOOGLE_RECAPTCHA_ERROR_NO_IP="For security reasons, you must pass the remote IP address to reCAPTCHA." +ASTROID_GOOGLE_TURNSTILE_ERROR_NO_IP="For security reasons, you must pass the remote IP address to Turnstile." ASTROID_GOOGLE_RECAPTCHA_ERROR_NO_PRIVATE_KEY="reCAPTCHA plugin needs a secret key to be set in its parameters. Please contact a site administrator." +ASTROID_GOOGLE_TURNSTILE_ERROR_NO_PRIVATE_KEY="Turnstile Captcha needs a secret key to be set in its parameters. Please contact a site administrator." ASTROID_GOOGLE_RECAPTCHA_ERROR_NO_PUBLIC_KEY="reCAPTCHA plugin needs a site key to be set in its parameters. Please contact a site administrator." +ASTROID_GOOGLE_TURNSTILE_ERROR_NO_PUBLIC_KEY="Turnstile Captcha needs a site key to be set in its parameters. Please contact a site administrator." ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_EXPIRED_CALLBACK_DESC="(Optional) JavaScript callback, executed when the reCAPTCHA expired." ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_EXPIRED_CALLBACK_LABEL="Expired Callback" ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_PRIVACY_CAPABILITY_IP_ADDRESS="The Invisible reCAPTCHA plugin integrates with Google's reCAPTCHA system as a spam protection service. As part of this service, the IP address of the user answering the captcha challenge is transmitted to Google." @@ -1213,6 +1217,7 @@ ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_PRIVATE_KEY_DESC="Used in the communication b ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_PUBLIC_KEY_DESC="Used in the JavaScript code that is served to your users." ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_TABINDEX_DESC="The tabindex of the challenge." ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_TABINDEX_LABEL="Tabindex" +ASTROID_TURNSTILE_NOTE="Cloudflare Turnstile" ; Widgets ASTROID_ELEMENT_CATEGORY_SYSTEM="System" diff --git a/plugins/astroidcaptcha/astroidcaptcha.xml b/plugins/astroidcaptcha/astroidcaptcha.xml index 6c56589d..7984a98a 100644 --- a/plugins/astroidcaptcha/astroidcaptcha.xml +++ b/plugins/astroidcaptcha/astroidcaptcha.xml @@ -21,26 +21,22 @@ - + - - - - + + + + + + + + + - - - - + + + + + + + + + + + + diff --git a/plugins/astroidcaptcha/src/Extension/AstroidCaptcha.php b/plugins/astroidcaptcha/src/Extension/AstroidCaptcha.php index ab12e461..fc97ca26 100644 --- a/plugins/astroidcaptcha/src/Extension/AstroidCaptcha.php +++ b/plugins/astroidcaptcha/src/Extension/AstroidCaptcha.php @@ -29,11 +29,11 @@ public function onInit($id = null) { $app = $this->getApplication(); $captcha_type = $this->params->get('captcha_type', 'default'); - if ($captcha_type == 'recaptcha' || $captcha_type == 'recaptcha_invisible') { + if ($captcha_type == 'recaptcha') { if ($this->params->get('g_site_key', '') === '') { throw new \RuntimeException($app->getLanguage()->_('ASTROID_GOOGLE_RECAPTCHA_ERROR_NO_PUBLIC_KEY')); } - if ($captcha_type == 'recaptcha_invisible') { + if ($this->params->get('g_size', 'normal') == 'invisible') { $onload = ['url' => 'astroid/recaptcha_invisible.min.js', 'function' => 'AstroidinitReCaptchaInvisible']; $render = 'explicit'; } else { @@ -41,23 +41,29 @@ public function onInit($id = null) $render = ''; } Framework::getDocument()->loadGoogleReCaptcha($onload, $render); + } else if ($captcha_type == 'turnstile') { + if ($this->params->get('t_site_key', '') === '') { + throw new \RuntimeException($app->getLanguage()->_('ASTROID_GOOGLE_TURNSTILE_ERROR_NO_PUBLIC_KEY')); + } + Framework::getDocument()->loadCloudFlareTurnstile(); } return true; } - public function onDisplay($name = null, $id = 'astroid-recaptcha', $class = '') + public function onDisplay($name = null, $id = 'astroid-captcha', $class = '') { $captcha_type = $this->params->get('captcha_type', 'default'); $html = '
'; - if ($captcha_type == 'recaptcha' || $captcha_type == 'recaptcha_invisible') { + if ($captcha_type == 'recaptcha') { $dom = new \DOMDocument('1.0', 'UTF-8'); $ele = $dom->createElement('div'); $ele->setAttribute('id', $id); $ele->setAttribute('class', ((trim($class) == '') ? 'g-recaptcha' : ($class . ' g-recaptcha'))); $ele->setAttribute('data-sitekey', $this->params->get('g_site_key', '')); - if ($captcha_type == 'recaptcha_invisible') { + $size = $this->params->get('g_size', 'normal'); + if ($size == 'invisible') { $ele->setAttribute('data-badge', $this->params->get('badge', 'bottomright')); - $ele->setAttribute('data-size', 'invisible'); } + $ele->setAttribute('data-size', $size); $ele->setAttribute('data-tabindex', $this->params->get('tabindex', '0')); $callback = $this->params->get('callback', ''); if ($callback != '') { @@ -73,6 +79,27 @@ public function onDisplay($name = null, $id = 'astroid-recaptcha', $class = '') } $dom->appendChild($ele); $html .= $dom->saveHTML($ele); + } else if ($captcha_type == 'turnstile') { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $ele = $dom->createElement('div'); + $ele->setAttribute('id', $id); + $ele->setAttribute('class', ((trim($class) == '') ? 'cf-turnstile' : ($class . ' cf-turnstile'))); + $ele->setAttribute('data-sitekey', $this->params->get('t_site_key', '')); + $ele->setAttribute('data-size', $this->params->get('t_size', 'normal')); + $callback = $this->params->get('t_callback', ''); + if ($callback != '') { + $ele->setAttribute('data-callback', $callback); + } + $expired_callback = $this->params->get('t_expired_callback', ''); + if ($expired_callback != '') { + $ele->setAttribute('data-expired-callback', $expired_callback); + } + $error_callback = $this->params->get('t_error_callback', ''); + if ($error_callback != '') { + $ele->setAttribute('data-error-callback', $error_callback); + } + $dom->appendChild($ele); + $html .= $dom->saveHTML($ele); } else { $html .= Helper\Captcha::loadCaptcha('as-joomla-captcha'); } @@ -82,11 +109,16 @@ public function onDisplay($name = null, $id = 'astroid-recaptcha', $class = '') public function onCheckAnswer($code = null) { $captcha_type = $this->params->get('captcha_type', 'default'); - if ($captcha_type == 'recaptcha' || $captcha_type == 'recaptcha_invisible') { + if ($captcha_type == 'recaptcha') { $input = $this->getApplication()->getInput(); $privatekey = $this->params->get('g_secret_key', ''); $gcaptcha = $input->post->get('g-recaptcha-response', '', 'string'); return Helper\Captcha::verifyGoogleCaptcha($gcaptcha, $privatekey, $this->requestMethod); + } elseif ($captcha_type == 'turnstile') { + $input = $this->getApplication()->getInput(); + $privatekey = $this->params->get('t_secret_key', ''); + $token = $input->post->get('cf-turnstile-response', '', 'string'); + return Helper\Captcha::verifyCloudFlareTurnstile($token, $privatekey); } elseif (!Helper\Captcha::getCaptcha('as-joomla-captcha')) { throw new \RuntimeException(Text::_('ASTROID_AJAX_ERROR_INVALID_CAPTCHA')); } else {