');
+ }
+ $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
diff --git a/js/formbuilder.min.js b/js/formbuilder.min.js
index f35366a4..2280a386 100644
--- a/js/formbuilder.min.js
+++ b/js/formbuilder.min.js
@@ -1 +1 @@
-jQuery((function(e){e(".as-form-builder").length&&e(document).on("submit",".as-form-builder",(function(t){t.preventDefault();var a={},r=e(this),s=r.serializeArray();let i=1e3*Date.now()+1e3*Math.random();i=i.toString(16).replace(/\./g,"").padEnd(14,"0")+Math.trunc(1e8*Math.random());for(let e=0;e'+e.message+""),r.trigger("reset"),r.find(".g-recaptcha").each((function(){grecaptcha.reset(this)}))):r.find(".as-formbuilder-status").append('
'+e.message+"
"),r.find(".as-form-builer-submit").removeAttr("disabled")}})}))}));
\ No newline at end of file
+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('
'+response.message+"
")}$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
diff --git a/js/recaptcha_invisible.js b/js/recaptcha_invisible.js
new file mode 100644
index 00000000..5a1c7510
--- /dev/null
+++ b/js/recaptcha_invisible.js
@@ -0,0 +1,25 @@
+((window, document) => {
+
+ window.AstroidinitReCaptchaInvisible = () => {
+ const optionKeys = ['sitekey', 'badge', 'size', 'tabindex', 'callback', 'expired-callback', 'error-callback'];
+ document.querySelectorAll('.g-recaptcha').forEach(element => {
+ let options = {};
+ if (element.dataset) {
+ options = element.dataset;
+ } else {
+ optionKeys.forEach(key => {
+ const optionKeyFq = `data-${optionKeys[key]}`;
+ if (element.hasAttribute(optionKeyFq)) {
+ options[optionKeys[key]] = element.getAttribute(optionKeyFq);
+ }
+ });
+ }
+
+ // Set the widget id of the recaptcha item
+ element.setAttribute('data-recaptcha-widget-id', window.grecaptcha.render(element, options));
+
+ // Execute the invisible reCAPTCHA
+ window.grecaptcha.execute(element.getAttribute('data-recaptcha-widget-id'));
+ });
+ };
+})(window, document);
\ No newline at end of file
diff --git a/js/recaptcha_invisible.min.js b/js/recaptcha_invisible.min.js
new file mode 100644
index 00000000..e484b0b1
--- /dev/null
+++ b/js/recaptcha_invisible.min.js
@@ -0,0 +1 @@
+((window,document)=>{window.AstroidinitReCaptchaInvisible=()=>{const optionKeys=["sitekey","badge","size","tabindex","callback","expired-callback","error-callback"];document.querySelectorAll(".g-recaptcha").forEach(element=>{let options={};if(element.dataset){options=element.dataset}else{optionKeys.forEach(key=>{const optionKeyFq=`data-${optionKeys[key]}`;if(element.hasAttribute(optionKeyFq)){options[optionKeys[key]]=element.getAttribute(optionKeyFq)}})}element.setAttribute("data-recaptcha-widget-id",window.grecaptcha.render(element,options));window.grecaptcha.execute(element.getAttribute("data-recaptcha-widget-id"))})}})(window,document);
\ 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 9d0ac77d..21c552d9 100644
--- a/language/en-GB/en-GB.astroid.ini
+++ b/language/en-GB/en-GB.astroid.ini
@@ -1188,12 +1188,31 @@ TPL_ASTROID_MINIFYJS_EXCLUDES_LABEL="Exclude JS Files"
TPL_ASTROID_MINIFYJS_EXCLUDES_DESC="Add comma separated filesnames to exclude files from JS minification. You can add filename directly or use available filename patterns: *.min.js jquery.* *bootstrap*"
; ReCaptcha
-COM_PLUGINS_ASTROID_CAPTCHA_FIELDSET_LABEL="Captcha"
ASTROID_RECAPTCHA_NOTE="Google ReCaptcha"
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_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."
+ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_BADGE_INLINE="Inline"
+ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_BADGE_LABEL="Badge"
+ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_CALLBACK_DESC="(Optional) JavaScript callback, executed after successful reCAPTCHA response."
+ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_CALLBACK_LABEL="Callback"
+ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_ERROR_CALLBACK_DESC="(Optional) JavaScript callback, executed when the reCAPTCHA encounters an error."
+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_RECAPTCHA_ERROR_NO_PRIVATE_KEY="reCAPTCHA plugin 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_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."
+ASTROID_GOOGLE_RECAPTCHA_INVISIBLE_PRIVATE_KEY_DESC="Used in the communication between your server and the reCAPTCHA server. Be sure to keep it a secret."
+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"
; Widgets
ASTROID_ELEMENT_CATEGORY_SYSTEM="System"
diff --git a/plugins/astroidcaptcha/astroidcaptcha.xml b/plugins/astroidcaptcha/astroidcaptcha.xml
new file mode 100644
index 00000000..6c56589d
--- /dev/null
+++ b/plugins/astroidcaptcha/astroidcaptcha.xml
@@ -0,0 +1,87 @@
+
+
+ Astroid Captcha
+ Astroid Framework Team
+ December 2024
+ 1.0.0
+ https://www.astroidframe.work
+ Copyright (C) 2024 TemPlaza, Inc. All rights reserved.
+ GNU General Public License version 3 or later; see LICENSE.txt
+ info@templaza.com
+ https://www.templaza.com
+ Astroid Framework Plugin Description Here
+ Joomla\Plugin\Captcha\AstroidCaptcha
+
+ services
+ src
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/astroidcaptcha/services/provider.php b/plugins/astroidcaptcha/services/provider.php
new file mode 100644
index 00000000..89f667d3
--- /dev/null
+++ b/plugins/astroidcaptcha/services/provider.php
@@ -0,0 +1,45 @@
+set(
+ PluginInterface::class,
+ function (Container $container) {
+ $dispatcher = $container->get(DispatcherInterface::class);
+ $plugin = new AstroidCaptcha(
+ $dispatcher,
+ (array) PluginHelper::getPlugin('captcha', 'astroidcaptcha'),
+ new HttpBridgePostRequestMethod()
+ );
+ $plugin->setApplication(Factory::getApplication());
+
+ return $plugin;
+ }
+ );
+ }
+};
\ No newline at end of file
diff --git a/plugins/astroidcaptcha/src/Extension/AstroidCaptcha.php b/plugins/astroidcaptcha/src/Extension/AstroidCaptcha.php
new file mode 100644
index 00000000..ab12e461
--- /dev/null
+++ b/plugins/astroidcaptcha/src/Extension/AstroidCaptcha.php
@@ -0,0 +1,96 @@
+requestMethod = $requestMethod;
+ }
+ 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 ($this->params->get('g_site_key', '') === '') {
+ throw new \RuntimeException($app->getLanguage()->_('ASTROID_GOOGLE_RECAPTCHA_ERROR_NO_PUBLIC_KEY'));
+ }
+ if ($captcha_type == 'recaptcha_invisible') {
+ $onload = ['url' => 'astroid/recaptcha_invisible.min.js', 'function' => 'AstroidinitReCaptchaInvisible'];
+ $render = 'explicit';
+ } else {
+ $onload = [];
+ $render = '';
+ }
+ Framework::getDocument()->loadGoogleReCaptcha($onload, $render);
+ }
+ return true;
+ }
+ public function onDisplay($name = null, $id = 'astroid-recaptcha', $class = '')
+ {
+ $captcha_type = $this->params->get('captcha_type', 'default');
+ $html = '