diff --git a/inc/cleantalk-common.php b/inc/cleantalk-common.php
index 54cf6d4ea..6797dd6c2 100644
--- a/inc/cleantalk-common.php
+++ b/inc/cleantalk-common.php
@@ -475,6 +475,146 @@ function apbct_exclusions_check__url()
return false;
}
+/**
+ * Split Protection mode (Lite) pages list into patterns.
+ *
+ * @param string $urls_setting
+ *
+ * @return string[]
+ */
+function apbct_protection_mode__parse_url_patterns($urls_setting)
+{
+ if ( $urls_setting === '' ) {
+ return array();
+ }
+
+ if ( strpos($urls_setting, "\r\n") !== false ) {
+ $patterns = explode("\r\n", $urls_setting);
+ } elseif ( strpos($urls_setting, "\n") !== false ) {
+ $patterns = explode("\n", $urls_setting);
+ } else {
+ $patterns = explode(',', $urls_setting);
+ }
+
+ $result = array();
+ foreach ( $patterns as $pattern ) {
+ $pattern = trim($pattern);
+ if ( $pattern !== '' ) {
+ $result[] = $pattern;
+ }
+ }
+
+ return $result;
+}
+
+/**
+ * Build a regexp from a user pattern with a safe delimiter.
+ *
+ * @param string $pattern
+ *
+ * @return string
+ */
+function apbct_protection_mode__build_regexp($pattern)
+{
+ $delimiter = '#';
+
+ return $delimiter . str_replace($delimiter, '\\' . $delimiter, $pattern) . $delimiter;
+}
+
+/**
+ * Whether the pattern compiles as a regular expression (same delimiter as runtime).
+ *
+ * @param string $pattern
+ *
+ * @return bool
+ */
+function apbct_protection_mode__is_regexp_compilable($pattern)
+{
+ return @preg_match(apbct_protection_mode__build_regexp($pattern), '') !== false;
+}
+
+/**
+ * Whether the pattern looks like an intentional regular expression.
+ *
+ * @param string $pattern
+ *
+ * @return bool
+ */
+function apbct_protection_mode__looks_like_regexp($pattern)
+{
+ return (bool) preg_match('/[.*+?\[\](){}|^$\\\\]/', $pattern);
+}
+
+/**
+ * Return the first invalid regexp-like pattern, or null if all are ok.
+ *
+ * @param string $urls_setting
+ *
+ * @return string|null
+ */
+function apbct_protection_mode__get_invalid_regexp_pattern($urls_setting)
+{
+ foreach ( apbct_protection_mode__parse_url_patterns($urls_setting) as $pattern ) {
+ if ( apbct_protection_mode__looks_like_regexp($pattern) && ! apbct_protection_mode__is_regexp_compilable($pattern) ) {
+ return $pattern;
+ }
+ }
+
+ return null;
+}
+
+/**
+ * Match a single Protection mode pattern against a URL haystack.
+ * Substring first; regexp only when the pattern looks like a regular expression.
+ *
+ * @param string $pattern
+ * @param string $url_haystack
+ *
+ * @return bool
+ */
+function apbct_protection_mode__pattern_matches($pattern, $url_haystack)
+{
+ if ( stripos($url_haystack, $pattern) !== false ) {
+ return true;
+ }
+
+ if ( ! apbct_protection_mode__looks_like_regexp($pattern) ) {
+ return false;
+ }
+
+ return @preg_match(apbct_protection_mode__build_regexp($pattern), $url_haystack) === 1;
+}
+
+/**
+ * Whether public JS/CSS assets are allowed on the current page.
+ * Full mode — always. Lite mode — only if REQUEST_URI matches listed pages (substring or regexp).
+ *
+ * @return bool
+ */
+function apbct_is_assets_allowed_on_current_page()
+{
+ global $apbct;
+
+ // Full mode (0) or unset
+ if ( empty($apbct->settings['data__protection_mode']) ) {
+ return true;
+ }
+
+ if ( empty($apbct->settings['data__protection_mode__urls']) ) {
+ return false;
+ }
+
+ $url_haystack = TT::toString(Server::getString('REQUEST_URI'));
+
+ foreach ( apbct_protection_mode__parse_url_patterns($apbct->settings['data__protection_mode__urls']) as $pattern ) {
+ if ( apbct_protection_mode__pattern_matches($pattern, $url_haystack) ) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
/**
* Check POST array for the exclusion form signs. Listen for array keys or for value in case if key is "action".
* @param array $form_data The POST array or another filtered array of form data.
diff --git a/inc/cleantalk-public.php b/inc/cleantalk-public.php
index 007f6983e..6c024c0a4 100644
--- a/inc/cleantalk-public.php
+++ b/inc/cleantalk-public.php
@@ -40,7 +40,7 @@ function apbct_init()
}
// Localize data
- if ( ! apbct_exclusions_check__url() ) {
+ if ( ! apbct_exclusions_check__url() && apbct_is_assets_allowed_on_current_page() ) {
if (defined('CLEANTALK_PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER') && CLEANTALK_PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER) {
add_action('wp_footer', array(LocalizeHandler::class, 'handle'), 1);
add_action('login_footer', array(LocalizeHandler::class, 'handle'), 1);
@@ -1138,6 +1138,10 @@ function apbct_login__scripts()
{
global $apbct;
+ if ( ! apbct_is_assets_allowed_on_current_page() ) {
+ return;
+ }
+
apbct_enqueue_and_localize_public_scripts();
$apbct->public_script_loaded = true;
@@ -1205,7 +1209,7 @@ function ct_enqueue_scripts_public($_hook)
{
global $current_user, $apbct;
- if ( apbct_exclusions_check__url() || apbct_is_amp_request() ) {
+ if ( apbct_exclusions_check__url() || apbct_is_amp_request() || ! apbct_is_assets_allowed_on_current_page() ) {
return;
}
@@ -1232,7 +1236,7 @@ function ct_enqueue_styles_public()
{
global $apbct, $current_user;
- if ( apbct_exclusions_check__url() ) {
+ if ( apbct_exclusions_check__url() || ! apbct_is_assets_allowed_on_current_page() ) {
return;
}
diff --git a/inc/cleantalk-settings.php b/inc/cleantalk-settings.php
index 8aacc9a3a..112bb1256 100644
--- a/inc/cleantalk-settings.php
+++ b/inc/cleantalk-settings.php
@@ -481,6 +481,28 @@ function apbct_settings__set_fields()
'title' => __('Data Processing', 'cleantalk-spam-protect'),
'section' => 'hidden_section',
'fields' => array(
+ 'data__protection_mode' => array(
+ 'title' => __('Protection mode', 'cleantalk-spam-protect'),
+ 'description' => __(
+ 'Full — load Anti-Spam scripts and styles on all pages. Lite — load them only on the pages listed below. Regular expressions are allowed.',
+ 'cleantalk-spam-protect'
+ ),
+ 'options' => array(
+ array('val' => 0, 'label' => __('Full', 'cleantalk-spam-protect'), 'childrens_enable' => 0,),
+ array('val' => 1, 'label' => __('Lite', 'cleantalk-spam-protect'), 'childrens_enable' => 1,),
+ ),
+ 'childrens' => array('data__protection_mode__urls'),
+ ),
+ 'data__protection_mode__urls' => array(
+ 'type' => 'textarea',
+ 'title' => __('Pages to protect (Lite mode)', 'cleantalk-spam-protect'),
+ 'description' => __(
+ 'List pages where Anti-Spam assets should be loaded. One value per line or comma-separated. Plain URL parts and regular expressions are allowed. Example: /contact, /checkout, /wp-login\\.php',
+ 'cleantalk-spam-protect'
+ ),
+ 'parent' => 'data__protection_mode',
+ 'class' => 'apbct_settings-field_wrapper--sub',
+ ),
'data__protect_logged_in' => array(
'title' => __("Protect logged in Users", 'cleantalk-spam-protect'),
'description' => __(
@@ -1795,6 +1817,7 @@ function apbct_settings__error__output($return = false)
'settings_validate' => 'Validate Settings',
'exclusions_urls' => 'URL Exclusions',
'exclusions_fields' => 'Field Exclusions',
+ 'protection_mode_urls' => 'Pages to protect (Lite mode)',
// Unknown
'unknown' => __('Unknown error type: ', 'cleantalk-spam-protect'),
@@ -2369,6 +2392,7 @@ function apbct_settings__validate($incoming_settings)
'data__email_decoder_obfuscation_mode',
'data__email_decoder_obfuscation_custom_text',
'data__email_decoder_buffer',
+ 'data__protection_mode__urls',
);
$incoming_settings = apbct_settings__keep_settings_state_values(
$incoming_settings,
@@ -2448,6 +2472,40 @@ function apbct_settings__validate($incoming_settings)
} // Make HTML code inactive
}
+ // Sanitize / validate Protection mode pages (Lite) — plain URL parts and regexps both allowed
+ $raw_protection_mode_urls = isset($incoming_settings['data__protection_mode__urls'])
+ ? $incoming_settings['data__protection_mode__urls']
+ : '';
+ $result = apbct_settings__sanitize__exclusions($raw_protection_mode_urls, false);
+ if ( ! is_string($result) ) {
+ $incoming_settings['data__protection_mode__urls'] = '';
+ $apbct->errorAdd(
+ 'protection_mode_urls',
+ 'is not valid: "' . $raw_protection_mode_urls . '"',
+ 'settings_validate'
+ );
+ } else {
+ $incoming_settings['data__protection_mode__urls'] = $result;
+ $invalid_pattern = apbct_protection_mode__get_invalid_regexp_pattern($result);
+ if ( $invalid_pattern !== null ) {
+ $apbct->errorAdd(
+ 'protection_mode_urls',
+ 'contains invalid regular expression: "' . $invalid_pattern . '"',
+ 'settings_validate'
+ );
+ } else {
+ $apbct->errorDelete('protection_mode_urls', true, 'settings_validate');
+ }
+ }
+
+ // Lite without pages would disable assets everywhere — fall back to Full
+ if (
+ ! empty($incoming_settings['data__protection_mode']) &&
+ empty($incoming_settings['data__protection_mode__urls'])
+ ) {
+ $incoming_settings['data__protection_mode'] = 0;
+ }
+
// Validate Exclusions
// URLs
$is_exclusions_url_like = apbct_settings__sanitize__exclusions(
diff --git a/js/cleantalk-admin-settings-page.min.js b/js/cleantalk-admin-settings-page.min.js
index 023526a67..a19a5cb8c 100644
--- a/js/cleantalk-admin-settings-page.min.js
+++ b/js/cleantalk-admin-settings-page.min.js
@@ -1,2 +1 @@
-function handleAnchorDetection(t){"none"===document.querySelector("#apbct_settings__advanced_settings").style.display&&apbctExceptedShowHide("apbct_settings__advanced_settings"),scrollToAnchor("#"+t)}function scrollToAnchor(t){t=document.querySelector(t);t&&t.scrollIntoView({block:"end"})}function apbctManageEmailEncoderCustomTextField(){var t=document.querySelector("#apbct_setting_data__email_decoder_obfuscation_custom_text");let e;null!==t&&(e=void 0!==t.parentElement?t.parentElement:null),document.querySelectorAll(".apbct_setting---data__email_decoder_obfuscation_mode").forEach(t=>{e&&t.checked&&"replace"!==t.value&&e.classList.add("hidden"),t.addEventListener("click",t=>{void 0!==e&&("replace"===t.target.value?e.classList.remove("hidden"):e.classList.add("hidden"))})})}function apbctBannerCheck(){let c=setInterval(function(){apbct_admin_sendAJAX({action:"apbct_settings__check_renew_banner"},{callback:function(t,e,n,a){t.close_renew_banner&&(jQuery("#cleantalk_notice_renew").length&&jQuery("#cleantalk_notice_renew").hide("slow"),jQuery("#cleantalk_notice_trial").length&&jQuery("#cleantalk_notice_trial").hide("slow"),clearInterval(c))}})},9e5)}function apbctGetElems(a){for(let t=0,e=(a=a.split(",")).length,n;t "+t.desc+""+t.title+"
").append("
"+t.data+"
").insertAfter(jQuery(c))}})}}),jQuery(document).on("click","#apbct_settings_templates_export_button",function(){jQuery("#apbct-ajax-result").remove();var t=jQuery("option:selected",jQuery("#apbct_settings_templates_export")),e=jQuery("#apbct_settings_templates_export_name");let n={};if(e.css("border-color","inherit"),void 0===t.data("id"))console.log('Attribute "data-id" not set for the option.');else{if("new_template"===t.data("id")){var a=e.val();if(""===a)return void e.css("border-color","red");n={template_name:a}}else n={template_id:t.data("id")};let c=this;apbct_admin_sendAJAX({action:"settings_templates_export",data:n},{timeout:25e3,button:c,spinner:jQuery("#apbct_settings_templates_export_button .apbct_preloader_button"),notJson:!0,callback:function(t,e,n,a){t.success?(jQuery(""+t.data+"
").insertAfter(jQuery(c)),jQuery("#apbct_settings_templates_export_button .apbct_success").show(300),setTimeout(function(){jQuery("#apbct_settings_templates_export_button .apbct_success").hide(300)},2e3),document.addEventListener("cleantalkModalClosed",function(t){document.location.reload()}),setTimeout(function(){cleantalkModal.close()},2e3)):jQuery(""+t.data+"
").insertAfter(jQuery(c))}})}}),jQuery(document).on("click","#apbct_settings_templates_reset_button",function(){let c=this;apbct_admin_sendAJAX({action:"settings_templates_reset"},{timeout:25e3,button:c,spinner:jQuery("#apbct_settings_templates_reset_button .apbct_preloader_button"),notJson:!0,callback:function(t,e,n,a){t.success?(jQuery(""+t.data+"
").insertAfter(jQuery(c)),jQuery("#apbct_settings_templates_reset_button .apbct_success").show(300),setTimeout(function(){jQuery("#apbct_settings_templates_reset_button .apbct_success").hide(300)},2e3),document.addEventListener("cleantalkModalClosed",function(t){document.location.reload()}),setTimeout(function(){cleantalkModal.close()},2e3)):jQuery(""+t.data+"
").insertAfter(jQuery(c))}})}),jQuery("#apbct_button__sync").on("click",function(){apbct_admin_sendAJAX({action:"apbct_sync"},{timeout:25e3,button:document.getElementById("apbct_button__sync"),spinner:jQuery("#apbct_button__sync .apbct_preloader_button"),callback:function(t,e,n,a){jQuery("#apbct_button__sync .apbct_success").show(300),setTimeout(function(){jQuery("#apbct_button__sync .apbct_success").hide(300)},2e3),t.reload&&(ctSettingsPage.key_changed?(jQuery(".key_changed_sync").hide(300),jQuery(".key_changed_success").show(300),setTimeout(function(){document.location.reload()},3e3)):document.location.reload())}})}),ctSettingsPage.key_changed&&jQuery("#apbct_button__sync").click(),jQuery(document).on("click",".apbct_settings-long_description---show",function(){apbctSettingsShowDescription(self=jQuery(this),self.attr("setting"))}),(jQuery("#cleantalk_notice_renew").length||jQuery("#cleantalk_notice_trial").length)&&apbctBannerCheck(),jQuery(document).on("change","#apbct_settings_templates_export",function(){"new_template"===jQuery("option:selected",this).data("id")?jQuery(this).parent().parent().find("#apbct_settings_templates_export_name").show():jQuery(this).parent().parent().find("#apbct_settings_templates_export_name").hide()}),apbctSaveButtonPosition();let t;window.addEventListener("scroll",function(){clearTimeout(t),t=setTimeout(function(){apbctSaveButtonPosition()},50),apbctNavigationMenuPosition()}),jQuery("#ct_adv_showhide a").on("click",apbctSaveButtonPosition),jQuery("#apbct-change-account-email").on("click",function(t){t.preventDefault();var t=jQuery(this),e=jQuery("#apbct-account-email"),n=e.text();t.toggleClass("active"),t.hasClass("active")?(t.text(t.data("save-text")),e.attr("contenteditable","true"),e.on("keydown",function(t){"Enter"===t.code&&t.preventDefault()}),e.on("input",function(t){"insertParagraph"===t.inputType&&t.preventDefault()})):(apbct_admin_sendAJAX({action:"apbct_update_account_email",accountEmail:n},{timeout:5e3,callback:function(t,e,n,a){void 0!==t.error&&jQuery("#apbct-account-email").css("border-color","red")}}),e.attr("contenteditable","false"),t.text(t.data("default-text")))}),jQuery("#apbct_setting_apikey").on("input",function(){var t=jQuery(this).val(),e=(jQuery("#apbct_settings__key_line__save_settings").off("click"),""!==t&&null===t.match(/^[a-z\d]{8,30}\s*$/));jQuery("#apbct_settings__key_is_bad").hide(),jQuery("#apbct_showApiKey").hide(),jQuery("#apbct_settings__account_name_ob").hide(),""===t?(jQuery("#apbct_button__key_line__save_changes_wrapper").hide(),jQuery("#apbct_button__get_key_auto").length&&jQuery("#apbct_button__get_key_auto__wrapper").show()):(jQuery("#apbct_button__key_line__save_changes_wrapper").show(),jQuery("#apbct_button__get_key_auto__wrapper").hide(),e&&jQuery("#apbct_settings__key_line__save_settings").on("click",function(t){t.preventDefault(),jQuery("#apbct_settings__key_is_bad").show(),apbctHighlightElement("apbct_setting_apikey",3)}))}),jQuery("#apbct_setting_apikey").val()&&ctSettingsPage.key_is_ok&&jQuery("#apbct_button__get_key_auto__wrapper").hide(),ctSettingsPage.key_is_ok||ctSettingsPage.ip_license||jQuery('button.cleantalk_link[value="save_changes"]').on("click",function(t){t.preventDefault(),jQuery("#sync_required_notice").length||jQuery(""+t.desc+"
"),jQuery(document).on("click",c))}},n)}function apbctNavigationMenuPosition(){var t,e,n=document.querySelector("#apbct_hidden_section_nav ul"),a=document.querySelector("#apbct_settings__button_section");n&&a&&(t=window.scrollY,e=window.innerWidth,1e3"+t.data+"
").insertAfter(jQuery(c))}})}}),jQuery(document).on("click","#apbct_settings_templates_export_button",function(){jQuery("#apbct-ajax-result").remove();var t=jQuery("option:selected",jQuery("#apbct_settings_templates_export")),e=jQuery("#apbct_settings_templates_export_name");let n={};if(e.css("border-color","inherit"),void 0===t.data("id"))console.log('Attribute "data-id" not set for the option.');else{if("new_template"===t.data("id")){var a=e.val();if(""===a)return void e.css("border-color","red");n={template_name:a}}else n={template_id:t.data("id")};let c=this;apbct_admin_sendAJAX({action:"settings_templates_export",data:n},{timeout:25e3,button:c,spinner:jQuery("#apbct_settings_templates_export_button .apbct_preloader_button"),notJson:!0,callback:function(t,e,n,a){t.success?(jQuery(""+t.data+"
").insertAfter(jQuery(c)),jQuery("#apbct_settings_templates_export_button .apbct_success").show(300),setTimeout(function(){jQuery("#apbct_settings_templates_export_button .apbct_success").hide(300)},2e3),document.addEventListener("cleantalkModalClosed",function(t){document.location.reload()}),setTimeout(function(){cleantalkModal.close()},2e3)):jQuery(""+t.data+"
").insertAfter(jQuery(c))}})}}),jQuery(document).on("click","#apbct_settings_templates_reset_button",function(){let c=this;apbct_admin_sendAJAX({action:"settings_templates_reset"},{timeout:25e3,button:c,spinner:jQuery("#apbct_settings_templates_reset_button .apbct_preloader_button"),notJson:!0,callback:function(t,e,n,a){t.success?(jQuery(""+t.data+"
").insertAfter(jQuery(c)),jQuery("#apbct_settings_templates_reset_button .apbct_success").show(300),setTimeout(function(){jQuery("#apbct_settings_templates_reset_button .apbct_success").hide(300)},2e3),document.addEventListener("cleantalkModalClosed",function(t){document.location.reload()}),setTimeout(function(){cleantalkModal.close()},2e3)):jQuery(""+t.data+"
").insertAfter(jQuery(c))}})}),jQuery("#apbct_button__sync").on("click",function(){apbct_admin_sendAJAX({action:"apbct_sync"},{timeout:25e3,button:document.getElementById("apbct_button__sync"),spinner:jQuery("#apbct_button__sync .apbct_preloader_button"),callback:function(t,e,n,a){jQuery("#apbct_button__sync .apbct_success").show(300),setTimeout(function(){jQuery("#apbct_button__sync .apbct_success").hide(300)},2e3),t.reload&&(ctSettingsPage.key_changed?(jQuery(".key_changed_sync").hide(300),jQuery(".key_changed_success").show(300),setTimeout(function(){document.location.reload()},3e3)):document.location.reload())}})}),ctSettingsPage.key_changed&&jQuery("#apbct_button__sync").click(),jQuery(document).on("click",".apbct_settings-long_description---show",function(){apbctSettingsShowDescription(self=jQuery(this),self.attr("setting"))}),(jQuery("#cleantalk_notice_renew").length||jQuery("#cleantalk_notice_trial").length)&&apbctBannerCheck(),jQuery(document).on("change","#apbct_settings_templates_export",function(){"new_template"===jQuery("option:selected",this).data("id")?jQuery(this).parent().parent().find("#apbct_settings_templates_export_name").show():jQuery(this).parent().parent().find("#apbct_settings_templates_export_name").hide()}),apbctSaveButtonPosition();let t;window.addEventListener("scroll",function(){clearTimeout(t),t=setTimeout(function(){apbctSaveButtonPosition()},50),apbctNavigationMenuPosition()}),jQuery("#ct_adv_showhide a").on("click",apbctSaveButtonPosition),jQuery("#apbct-change-account-email").on("click",function(t){t.preventDefault();var t=jQuery(this),e=jQuery("#apbct-account-email"),n=e.text();t.toggleClass("active"),t.hasClass("active")?(t.text(t.data("save-text")),e.attr("contenteditable","true"),e.on("keydown",function(t){"Enter"===t.code&&t.preventDefault()}),e.on("input",function(t){"insertParagraph"===t.inputType&&t.preventDefault()})):(apbct_admin_sendAJAX({action:"apbct_update_account_email",accountEmail:n},{timeout:5e3,callback:function(t,e,n,a){void 0!==t.error&&jQuery("#apbct-account-email").css("border-color","red")}}),e.attr("contenteditable","false"),t.text(t.data("default-text")))}),jQuery("#apbct_setting_apikey").on("input",function(){var t=jQuery(this).val(),e=(jQuery("#apbct_settings__key_line__save_settings").off("click"),""!==t&&null===t.match(/^[a-z\d]{8,30}\s*$/));jQuery("#apbct_settings__key_is_bad").hide(),jQuery("#apbct_showApiKey").hide(),jQuery("#apbct_settings__account_name_ob").hide(),""===t?(jQuery("#apbct_button__key_line__save_changes_wrapper").hide(),jQuery("#apbct_button__get_key_auto").length&&jQuery("#apbct_button__get_key_auto__wrapper").show()):(jQuery("#apbct_button__key_line__save_changes_wrapper").show(),jQuery("#apbct_button__get_key_auto__wrapper").hide(),e&&jQuery("#apbct_settings__key_line__save_settings").on("click",function(t){t.preventDefault(),jQuery("#apbct_settings__key_is_bad").show(),apbctHighlightElement("apbct_setting_apikey",3)}))}),jQuery("#apbct_setting_apikey").val()&&ctSettingsPage.key_is_ok&&jQuery("#apbct_button__get_key_auto__wrapper").hide(),ctSettingsPage.key_is_ok||ctSettingsPage.ip_license||jQuery('button.cleantalk_link[value="save_changes"]').on("click",function(t){t.preventDefault(),jQuery("#sync_required_notice").length||jQuery("