diff --git a/inc/cleantalk-settings.php b/inc/cleantalk-settings.php
index 97a638250..a8ea76ead 100644
--- a/inc/cleantalk-settings.php
+++ b/inc/cleantalk-settings.php
@@ -112,8 +112,7 @@ function apbct_settings__set_fields()
'cleantalk-spam-protect'
)
. ' '
- //HANDLE LINK
- . ''
+ . ''
. esc_html__(
'contact to our support.',
'cleantalk-spam-protect'
@@ -288,7 +287,7 @@ function apbct_settings__set_fields()
'Read more about %sspam protection for Search form%s on our blog. The "noindex" tag will be placed in the meta directive on the search results page.',
'cleantalk-spam-protect'
),
- '',
+ '',
''
)
: ''
@@ -526,9 +525,7 @@ function apbct_settings__set_fields()
. ( ! $apbct->white_label && ! $apbct->data["wl_mode_enabled"] ?
__(' Or you don`t have records about missed spam here:', 'cleantalk-spam-protect')
. ' '
- //HANDLE LINK
- . ''
+ . ''
. __('CleanTalk Dashboard', 'cleantalk-spam-protect')
. '.' : '' )
. '
'
@@ -665,7 +662,7 @@ function apbct_settings__set_fields()
),
'data__email_check_exist_post' => array(
'title' => __('Show email existence alert when filling in the field', 'cleantalk-spam-protect'),
- 'description' => __('Checks the email address and shows the result as an icon in the email field before submitting the form. Works for WooCommerce checkout form, FluentForms, Contact Form 7, Gravity Forms, Ninja Forms, WPForms, standard WordPress comment form and registration form.', 'cleantalk-spam-protect') . ' ' . '' . __('Read more', 'cleantalk-spam-protect') . '',
+ 'description' => __('Checks the email address and shows the result as an icon in the email field before submitting the form. Works for WooCommerce checkout form, FluentForms, Contact Form 7, Gravity Forms, Ninja Forms, WPForms, standard WordPress comment form and registration form.', 'cleantalk-spam-protect') . ' ' . '' . __('Read more', 'cleantalk-spam-protect') . '',
),
),
),
@@ -1125,8 +1122,7 @@ function apbct_settings__set_fields__network($fields)
'title' => __('Hoster Access key', 'cleantalk-spam-protect'),
'description' => sprintf(
__('Copy the Access key from your %sCleanTalk Profile%s', 'cleantalk-spam-protect'),
- //HANDLE LINK
- '',
+ '',
''
),
'class' => 'apbct_settings-field_wrapper--sub',
@@ -1370,8 +1366,7 @@ function apbct_settings__display()
if ( $apbct->key_is_ok && apbct_api_key__is_correct() && ! $apbct->data["wl_mode_enabled"] ) {
if ( $apbct->network_settings['multisite__work_mode'] != 2 || is_main_site() ) {
// CP button
- //HANDLE LINK
- echo ''
+ echo ''
. __('Cloud Dashboard', 'cleantalk-spam-protect')
. ''
. '';
@@ -1874,9 +1869,8 @@ function apbct_settings__field__state()
'cleantalk-spam-protect'
);
if ( ! $apbct->white_label || is_main_site() ) {
- //HANDLE LINK
echo '
'
- . '' . __(
+ . '' . __(
'Validate email for existence',
'cleantalk-spam-protect'
) . '';
@@ -2010,7 +2004,7 @@ function apbct_settings__field__apikey()
$replaces['public_offer_link'] = sprintf(
''
);
@@ -3025,6 +3019,24 @@ function apbct_settings__get__long_description()
}
$setting_id = TT::toString(Post::get('setting_id', null, 'word'));
+ $descriptions = apbct_settings__get_long_descriptions_data();
+
+ if (!empty($setting_id) && isset($descriptions[$setting_id])) {
+ die(json_encode($descriptions[$setting_id]));
+ } else {
+ die(json_encode(['error' => 'Invalid setting ID']));
+ }
+}
+
+/**
+ * Builds the long descriptions data array with all LinkConstructor links.
+ * Extracted for testability.
+ *
+ * @return array
+ */
+function apbct_settings__get_long_descriptions_data()
+{
+ global $apbct;
$link_exclusion_by_form_signs = LinkConstructor::buildCleanTalkLink(
'exclusion_by_form_signs',
@@ -3046,18 +3058,16 @@ function apbct_settings__get__long_description()
),
'data__set_cookies' => array(
'title' => __('Cookies setting', 'cleantalk-spam-protect'),
- //HANDLE LINK
'desc' => sprintf(
__('It determines what methods of using the HTTP cookies the Anti-Spam plugin for WordPress should switch to. It is necessary for the plugin to work properly. All CleanTalk cookies contain technical data. Data of the current website visitor is encrypted with the MD5 algorithm and being deleted when the browser session ends. %s', 'cleantalk-spam-protect'),
- '' . __('Learn more about suboptions', 'cleantalk-spam-protect') . ''
+ '' . __('Learn more about suboptions', 'cleantalk-spam-protect') . ''
)
),
'comments__hide_website_field' => array(
'title' => __('Hide the "Website" field', 'cleantalk-spam-protect'),
- //HANDLE LINK
'desc' => sprintf(
- __('This «Website» field is frequently used by spammers to place spam links in it. ' . esc_html__($apbct->data['wl_brandname']) . ' helps you protect your WordPress website comments by hiding this field off. %s', 'cleantalk-spam-protect'),
- '' . __('Learn more.', 'cleantalk-spam-protect') . ''
+ __('This «Website» field is frequently used by spammers to place spam links in it. ' . esc_html($apbct->data['wl_brandname']) . ' helps you protect your WordPress website comments by hiding this field off. %s', 'cleantalk-spam-protect'),
+ '' . __('Learn more.', 'cleantalk-spam-protect') . ''
)
),
'comments__the_real_person' => array(
@@ -3071,50 +3081,45 @@ function apbct_settings__get__long_description()
),
'sfw__anti_crawler' => array(
'title' => 'Anti-Crawler', // Do not to localize this phrase
- //HANDLE LINK
'desc' => sprintf(
'
' . sprintf( __('%1$s Anti-Crawler — this option is meant to block all types of bots visiting website pages that can search vulnerabilities on a website, attempt to hack a site, collect personal data, price parsing or content and images, generate 404 error pages, or aggressive website scanning bots. %2$s', 'cleantalk-spam-protect'), esc_html($apbct->data['wl_brandname']), - '' . __('Learn more.', 'cleantalk-spam-protect') . '' + '' . __('Learn more.', 'cleantalk-spam-protect') . '' ) . '
' . '' . __('By default, well-known good bots are allowed, including AI crawlers like GPTBot (ChatGPT), ClaudeBot (Claude), Google-Extended (Gemini), and Copilot. You can selectively block any of these bots in your private lists.', 'cleantalk-spam-protect') . '
' ) ), 'sfw__anti_flood' => array( 'title' => 'Anti-Flood', // Do not to localize this phrase - //HANDLE LINK 'desc' => sprintf( - __(esc_html__($apbct->data['wl_brandname']) . ' Anti-Flood — this option is meant to block aggressive bots. You can set the maximum number of website pages your visitors can click on within 1 minute. If any IP exceeds the set number it will get the ' . $apbct->data['wl_brandname'] . ' blocking screen for 30 seconds. It\'s impossible for the IP to open any website pages while the 30-second timer takes place. %s', 'cleantalk-spam-protect'), - '' . __('Learn more.', 'cleantalk-spam-protect') . '' + __(esc_html($apbct->data['wl_brandname']) . ' Anti-Flood — this option is meant to block aggressive bots. You can set the maximum number of website pages your visitors can click on within 1 minute. If any IP exceeds the set number it will get the ' . esc_html($apbct->data['wl_brandname']) . ' blocking screen for 30 seconds. It\'s impossible for the IP to open any website pages while the 30-second timer takes place. %s', 'cleantalk-spam-protect'), + '' . __('Learn more.', 'cleantalk-spam-protect') . '' ) ), 'data__pixel' => array( - 'title' => __(esc_html__($apbct->data['wl_brandname']) . ' Pixel', 'cleantalk-spam-protect'), - //HANDLE LINK + 'title' => __(esc_html($apbct->data['wl_brandname']) . ' Pixel', 'cleantalk-spam-protect'), 'desc' => sprintf( __('It is an «invisible» 1×1px image that the Anti-Spam plugin integrates to your WordPress website. And when someone visits your website the Pixel is triggered and reports this visit and some other data including true IP address. %s', 'cleantalk-spam-protect'), - '' . __('Learn more.', 'cleantalk-spam-protect') . '' + '' . __('Learn more.', 'cleantalk-spam-protect') . '' ) ), 'data__honeypot_field' => array( 'title' => __('Honeypot field', 'cleantalk-spam-protect'), - //HANDLE LINK 'desc' => sprintf( esc_html__('The option helps to block bots . The honeypot field option adds a hidden field to the form. When spambots come to a website form, they can fill out each input field. Enable this option to make the protection stronger on these forms. Learn more about supported forms %s', 'cleantalk-spam-protect'), - '' . __('here.', 'cleantalk-spam-protect') . '' + '' . __('here.', 'cleantalk-spam-protect') . '' ) ), 'sfw__enabled' => array( 'title' => 'SpamFireWall', - //HANDLE LINK 'desc' => sprintf( '' . esc_html__('SpamFireWall is a part of the anti-spam service and blocks the most spam active bots before the site pages load.', 'cleantalk-spam-protect') . '
' . '' . esc_html__('Anti-Crawler is an add-on to SFW and helps to strengthen the protection against spam bots. Disabled by default.', 'cleantalk-spam-protect') . '
' - . '' . esc_html__($apbct->data['wl_brandname'] . ' Anti-Flood is also an add-on to SFW and limits the number of pages visited per minute. Disabled by default.', 'cleantalk-spam-protect') . '
' + . '' . esc_html($apbct->data['wl_brandname']) . __(' Anti-Flood is also an add-on to SFW and limits the number of pages visited per minute. Disabled by default.', 'cleantalk-spam-protect') . '
' . '' . esc_html__('You can read more about SFW modes %s', 'cleantalk-spam-protect') . '
' . '' . esc_html__('Read out the article if you are using Varnish on your server.', 'cleantalk-spam-protect'), - '' . __('here.', 'cleantalk-spam-protect') . '' + '' . __('here.', 'cleantalk-spam-protect') . '' ) ), 'exclusions__form_signs' => array( @@ -3188,17 +3193,7 @@ function apbct_settings__get__long_description() ), ); - if (!empty($setting_id) && isset($descriptions[$setting_id])) { - if ($setting_id === 'comments__hide_website_field') { - $utm = '?utm_source=apbct_hint_' . esc_attr($setting_id) . '&utm_medium=hide_website_field_hint&utm_campaign=apbct_links'; - } else { - $utm = '?utm_source=apbct_hint_' . esc_attr($setting_id) . '&utm_medium=WordPress&utm_campaign=ABPCT_Settings'; - } - $descriptions[$setting_id]['desc'] = str_replace('{utm_mark}', $utm, $descriptions[$setting_id]['desc']); - die(json_encode($descriptions[$setting_id])); - } else { - die(json_encode(['error' => 'Invalid setting ID'])); - } + return $descriptions; } function apbct_settings__check_renew_banner() diff --git a/lib/Cleantalk/ApbctWP/LinkConstructor.php b/lib/Cleantalk/ApbctWP/LinkConstructor.php index 7f4bde96a..aa87d8168 100644 --- a/lib/Cleantalk/ApbctWP/LinkConstructor.php +++ b/lib/Cleantalk/ApbctWP/LinkConstructor.php @@ -230,6 +230,116 @@ class LinkConstructor extends \Cleantalk\Common\LinkConstructor 'utm_content' => 'avatar', 'utm_campaign' => 'apbct_links', ), + /* + * Settings long descriptions (hints) + */ + 'apbct_hint_data__set_cookies' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'apbct_hint_data__set_cookies', + 'utm_medium' => 'WordPress', + 'utm_campaign' => 'ABPCT_Settings', + ), + 'apbct_hint_comments__hide_website_field' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'apbct_hint_comments__hide_website_field', + 'utm_medium' => 'hide_website_field_hint', + ), + 'apbct_hint_sfw__anti_crawler' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'apbct_hint_sfw__anti_crawler', + 'utm_medium' => 'WordPress', + 'utm_campaign' => 'ABPCT_Settings', + ), + 'apbct_hint_sfw__anti_flood' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'apbct_hint_sfw__anti_flood', + 'utm_medium' => 'WordPress', + 'utm_campaign' => 'ABPCT_Settings', + ), + 'apbct_hint_data__pixel' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'apbct_hint_data__pixel', + 'utm_medium' => 'WordPress', + 'utm_campaign' => 'ABPCT_Settings', + ), + 'apbct_hint_data__honeypot_field' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'apbct_hint_data__honeypot_field', + 'utm_medium' => 'WordPress', + 'utm_campaign' => 'ABPCT_Settings', + ), + 'apbct_hint_sfw__enabled' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'apbct_hint_sfw__enabled', + 'utm_medium' => 'WordPress', + 'utm_campaign' => 'ABPCT_Settings', + ), + /* + * Settings page links + */ + 'settings_support_open' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'admin_panel', + 'utm_medium' => 'settings', + 'utm_content' => 'sfw_support_link', + ), + 'blog_search_form_protection' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'admin_panel', + 'utm_medium' => 'settings', + 'utm_content' => 'search_form_protection', + ), + 'settings_dashboard_link' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'wp-backend', + 'utm_medium' => 'admin-bar', + 'utm_content' => 'settings_dashboard_link', + ), + 'settings_email_existence_alert' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'admin_panel', + 'utm_medium' => 'settings', + 'utm_content' => 'email_existence_alert', + ), + 'settings_hoster_api_key' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'admin_panel', + 'utm_medium' => 'settings', + 'utm_content' => 'hoster_api_key', + ), + 'settings_cloud_dashboard_button' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'admin_panel', + 'utm_medium' => 'settings', + 'utm_content' => 'cloud_dashboard_button', + ), + 'blog_email_validation_status' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'admin_panel', + 'utm_medium' => 'settings', + 'utm_content' => 'email_validation_status', + ), + 'settings_public_offer' => array( + 'utm_id' => '', + 'utm_term' => '', + 'utm_source' => 'admin_panel', + 'utm_medium' => 'settings', + 'utm_content' => 'public_offer', + ), ); public static function buildCleanTalkLink($utm_preset, $uri = '', $get_params = array(), $domain = 'https://cleantalk.org') diff --git a/tests/ApbctWP/TestSettingsLongDescriptionLinks.php b/tests/ApbctWP/TestSettingsLongDescriptionLinks.php new file mode 100644 index 000000000..fd6b218c7 --- /dev/null +++ b/tests/ApbctWP/TestSettingsLongDescriptionLinks.php @@ -0,0 +1,643 @@ +assertStringStartsWith('https://cleantalk.org/help/set-cookies-option?', $link); + $this->assertStringContainsString('utm_source=apbct_hint_data__set_cookies', $link); + $this->assertStringContainsString('utm_medium=WordPress', $link); + $this->assertStringContainsString('utm_campaign=ABPCT_Settings', $link); + } + + public function testPresetDataSetCookiesExists() + { + $this->assertArrayHasKey('apbct_hint_data__set_cookies', LinkConstructor::$utm_presets); + $preset = LinkConstructor::$utm_presets['apbct_hint_data__set_cookies']; + $this->assertSame('apbct_hint_data__set_cookies', $preset['utm_source']); + $this->assertSame('WordPress', $preset['utm_medium']); + $this->assertSame('ABPCT_Settings', $preset['utm_campaign']); + } + + /** + * comments__hide_website_field: help/how-to-hide-website-field-in-wordpress-comments + * Original: https://cleantalk.org/help/how-to-hide-website-field-in-wordpress-comments?utm_source=apbct_hint_comments__hide_website_field&utm_medium=hide_website_field_hint&utm_campaign=apbct_links + */ + public function testLinkCommentsHideWebsiteField() + { + $link = LinkConstructor::buildCleanTalkLink( + 'apbct_hint_comments__hide_website_field', + 'help/how-to-hide-website-field-in-wordpress-comments' + ); + + $this->assertStringStartsWith('https://cleantalk.org/help/how-to-hide-website-field-in-wordpress-comments?', $link); + $this->assertStringContainsString('utm_source=apbct_hint_comments__hide_website_field', $link); + $this->assertStringContainsString('utm_medium=hide_website_field_hint', $link); + $this->assertStringContainsString('utm_campaign=apbct_links', $link); + } + + public function testPresetCommentsHideWebsiteFieldExists() + { + $this->assertArrayHasKey('apbct_hint_comments__hide_website_field', LinkConstructor::$utm_presets); + $preset = LinkConstructor::$utm_presets['apbct_hint_comments__hide_website_field']; + $this->assertSame('apbct_hint_comments__hide_website_field', $preset['utm_source']); + $this->assertSame('hide_website_field_hint', $preset['utm_medium']); + // utm_campaign not in preset — should fall back to static::$utm_campaign = 'apbct_links' + $this->assertArrayNotHasKey('utm_campaign', $preset); + } + + /** + * comments__the_real_person: the-real-person (was already using LinkConstructor) + * Original: https://cleantalk.org/the-real-person?...&utm_source=admin_side&utm_medium=trp_badge&utm_content=trp_badge_link_click&utm_campaign=apbct_links + */ + public function testLinkCommentsTheRealPerson() + { + $link = LinkConstructor::buildCleanTalkLink('trp_learn_more_link', 'the-real-person'); + + $this->assertStringStartsWith('https://cleantalk.org/the-real-person?', $link); + $this->assertStringContainsString('utm_source=admin_side', $link); + $this->assertStringContainsString('utm_medium=trp_badge', $link); + $this->assertStringContainsString('utm_content=trp_badge_link_click', $link); + $this->assertStringContainsString('utm_campaign=apbct_links', $link); + } + + /** + * sfw__anti_crawler: help/anti-flood-and-anti-crawler + #anticrawl anchor + * Original: https://cleantalk.org/help/anti-flood-and-anti-crawler?utm_source=apbct_hint_sfw__anti_crawler&utm_medium=WordPress&utm_campaign=ABPCT_Settings + * Note: #anticrawl is appended outside of buildCleanTalkLink + */ + public function testLinkSfwAntiCrawler() + { + $link = LinkConstructor::buildCleanTalkLink('apbct_hint_sfw__anti_crawler', 'help/anti-flood-and-anti-crawler'); + + $this->assertStringStartsWith('https://cleantalk.org/help/anti-flood-and-anti-crawler?', $link); + $this->assertStringContainsString('utm_source=apbct_hint_sfw__anti_crawler', $link); + $this->assertStringContainsString('utm_medium=WordPress', $link); + $this->assertStringContainsString('utm_campaign=ABPCT_Settings', $link); + + // Verify anchor can be appended + $linkWithAnchor = $link . '#anticrawl'; + $this->assertStringEndsWith('#anticrawl', $linkWithAnchor); + } + + public function testPresetSfwAntiCrawlerExists() + { + $this->assertArrayHasKey('apbct_hint_sfw__anti_crawler', LinkConstructor::$utm_presets); + $preset = LinkConstructor::$utm_presets['apbct_hint_sfw__anti_crawler']; + $this->assertSame('apbct_hint_sfw__anti_crawler', $preset['utm_source']); + $this->assertSame('WordPress', $preset['utm_medium']); + $this->assertSame('ABPCT_Settings', $preset['utm_campaign']); + } + + /** + * sfw__anti_flood: help/anti-flood-and-anti-crawler + #antiflood anchor + * Original: https://cleantalk.org/help/anti-flood-and-anti-crawler?utm_source=apbct_hint_sfw__anti_flood&utm_medium=WordPress&utm_campaign=ABPCT_Settings + */ + public function testLinkSfwAntiFlood() + { + $link = LinkConstructor::buildCleanTalkLink('apbct_hint_sfw__anti_flood', 'help/anti-flood-and-anti-crawler'); + + $this->assertStringStartsWith('https://cleantalk.org/help/anti-flood-and-anti-crawler?', $link); + $this->assertStringContainsString('utm_source=apbct_hint_sfw__anti_flood', $link); + $this->assertStringContainsString('utm_medium=WordPress', $link); + $this->assertStringContainsString('utm_campaign=ABPCT_Settings', $link); + + // Verify anchor can be appended + $linkWithAnchor = $link . '#antiflood'; + $this->assertStringEndsWith('#antiflood', $linkWithAnchor); + } + + public function testPresetSfwAntiFloodExists() + { + $this->assertArrayHasKey('apbct_hint_sfw__anti_flood', LinkConstructor::$utm_presets); + $preset = LinkConstructor::$utm_presets['apbct_hint_sfw__anti_flood']; + $this->assertSame('apbct_hint_sfw__anti_flood', $preset['utm_source']); + $this->assertSame('WordPress', $preset['utm_medium']); + $this->assertSame('ABPCT_Settings', $preset['utm_campaign']); + } + + /** + * data__pixel: introducing-cleantalk-pixel (blog.cleantalk.org domain) + * Original: https://blog.cleantalk.org/introducing-cleantalk-pixel?utm_source=apbct_hint_data__pixel&utm_medium=WordPress&utm_campaign=ABPCT_Settings + */ + public function testLinkDataPixel() + { + $link = LinkConstructor::buildCleanTalkLink( + 'apbct_hint_data__pixel', + 'introducing-cleantalk-pixel', + array(), + 'https://blog.cleantalk.org' + ); + + $this->assertStringStartsWith('https://blog.cleantalk.org/introducing-cleantalk-pixel?', $link); + $this->assertStringContainsString('utm_source=apbct_hint_data__pixel', $link); + $this->assertStringContainsString('utm_medium=WordPress', $link); + $this->assertStringContainsString('utm_campaign=ABPCT_Settings', $link); + } + + public function testPresetDataPixelExists() + { + $this->assertArrayHasKey('apbct_hint_data__pixel', LinkConstructor::$utm_presets); + $preset = LinkConstructor::$utm_presets['apbct_hint_data__pixel']; + $this->assertSame('apbct_hint_data__pixel', $preset['utm_source']); + $this->assertSame('WordPress', $preset['utm_medium']); + $this->assertSame('ABPCT_Settings', $preset['utm_campaign']); + } + + /** + * data__honeypot_field: help/honeypot-field + * Original: https://cleantalk.org/help/honeypot-field?utm_source=apbct_hint_data__honeypot_field&utm_medium=WordPress&utm_campaign=ABPCT_Settings + */ + public function testLinkDataHoneypotField() + { + $link = LinkConstructor::buildCleanTalkLink('apbct_hint_data__honeypot_field', 'help/honeypot-field'); + + $this->assertStringStartsWith('https://cleantalk.org/help/honeypot-field?', $link); + $this->assertStringContainsString('utm_source=apbct_hint_data__honeypot_field', $link); + $this->assertStringContainsString('utm_medium=WordPress', $link); + $this->assertStringContainsString('utm_campaign=ABPCT_Settings', $link); + } + + public function testPresetDataHoneypotFieldExists() + { + $this->assertArrayHasKey('apbct_hint_data__honeypot_field', LinkConstructor::$utm_presets); + $preset = LinkConstructor::$utm_presets['apbct_hint_data__honeypot_field']; + $this->assertSame('apbct_hint_data__honeypot_field', $preset['utm_source']); + $this->assertSame('WordPress', $preset['utm_medium']); + $this->assertSame('ABPCT_Settings', $preset['utm_campaign']); + } + + /** + * sfw__enabled: help/anti-flood-and-anti-crawler + * Original: https://cleantalk.org/help/anti-flood-and-anti-crawler?utm_source=apbct_hint_sfw__enabled&utm_medium=WordPress&utm_campaign=ABPCT_Settings + */ + public function testLinkSfwEnabled() + { + $link = LinkConstructor::buildCleanTalkLink('apbct_hint_sfw__enabled', 'help/anti-flood-and-anti-crawler'); + + $this->assertStringStartsWith('https://cleantalk.org/help/anti-flood-and-anti-crawler?', $link); + $this->assertStringContainsString('utm_source=apbct_hint_sfw__enabled', $link); + $this->assertStringContainsString('utm_medium=WordPress', $link); + $this->assertStringContainsString('utm_campaign=ABPCT_Settings', $link); + } + + public function testPresetSfwEnabledExists() + { + $this->assertArrayHasKey('apbct_hint_sfw__enabled', LinkConstructor::$utm_presets); + $preset = LinkConstructor::$utm_presets['apbct_hint_sfw__enabled']; + $this->assertSame('apbct_hint_sfw__enabled', $preset['utm_source']); + $this->assertSame('WordPress', $preset['utm_medium']); + $this->assertSame('ABPCT_Settings', $preset['utm_campaign']); + } + + /** + * exclusions__form_signs: help/exclusion-by-form-signs (was already using LinkConstructor) + * Original: https://cleantalk.org/help/exclusion-by-form-signs?...&utm_source=admin_panel&utm_medium=settings&utm_content=apbct_hint_exclusions__form_signs&utm_campaign=apbct_links + */ + public function testLinkExclusionsFormSigns() + { + $link = LinkConstructor::buildCleanTalkLink('exclusion_by_form_signs', 'help/exclusion-by-form-signs'); + + $this->assertStringStartsWith('https://cleantalk.org/help/exclusion-by-form-signs?', $link); + $this->assertStringContainsString('utm_source=admin_panel', $link); + $this->assertStringContainsString('utm_medium=settings', $link); + $this->assertStringContainsString('utm_content=apbct_hint_exclusions__form_signs', $link); + $this->assertStringContainsString('utm_campaign=apbct_links', $link); + } + + + + // ========================================================================= + // Structural tests: verify LinkConstructor correctly builds URLs + // ========================================================================= + + /** + * Ensure all hint presets produce URLs with correct domain + */ + public function testAllHintPresetsUseCleantalkDomain() + { + $hintPresets = array( + 'apbct_hint_data__set_cookies', + 'apbct_hint_comments__hide_website_field', + 'apbct_hint_sfw__anti_crawler', + 'apbct_hint_sfw__anti_flood', + 'apbct_hint_data__honeypot_field', + 'apbct_hint_sfw__enabled', + ); + + foreach ($hintPresets as $preset) { + $link = LinkConstructor::buildCleanTalkLink($preset, 'test-uri'); + $this->assertStringStartsWith( + 'https://cleantalk.org/test-uri?', + $link, + "Preset '$preset' should produce URL with cleantalk.org domain" + ); + } + } + + /** + * Ensure data__pixel preset works with blog domain + */ + public function testPixelPresetWithBlogDomain() + { + $link = LinkConstructor::buildCleanTalkLink( + 'apbct_hint_data__pixel', + 'test-uri', + array(), + 'https://blog.cleantalk.org' + ); + $this->assertStringStartsWith('https://blog.cleantalk.org/test-uri?', $link); + $this->assertStringNotContainsString('https://cleantalk.org', $link); + } + + /** + * Verify that all long description presets exist in LinkConstructor + */ + public function testAllLongDescriptionPresetsExist() + { + $requiredPresets = array( + 'apbct_hint_data__set_cookies', + 'apbct_hint_comments__hide_website_field', + 'apbct_hint_sfw__anti_crawler', + 'apbct_hint_sfw__anti_flood', + 'apbct_hint_data__pixel', + 'apbct_hint_data__honeypot_field', + 'apbct_hint_sfw__enabled', + 'trp_learn_more_link', + 'exclusion_by_form_signs', + ); + + foreach ($requiredPresets as $preset) { + $this->assertArrayHasKey( + $preset, + LinkConstructor::$utm_presets, + "UTM preset '$preset' must exist in LinkConstructor::\$utm_presets" + ); + } + } + + /** + * Verify that UTM campaign defaults to 'apbct_links' when not specified in preset + */ + public function testDefaultUtmCampaignFallback() + { + // comments__hide_website_field preset has no utm_campaign + $link = LinkConstructor::buildCleanTalkLink( + 'apbct_hint_comments__hide_website_field', + 'test' + ); + $this->assertStringContainsString('utm_campaign=apbct_links', $link); + } + + /** + * Verify that UTM campaign from preset overrides default + */ + public function testCustomUtmCampaignInPreset() + { + // data__set_cookies preset has utm_campaign=ABPCT_Settings + $link = LinkConstructor::buildCleanTalkLink( + 'apbct_hint_data__set_cookies', + 'test' + ); + $this->assertStringContainsString('utm_campaign=ABPCT_Settings', $link); + $this->assertStringNotContainsString('utm_campaign=apbct_links', $link); + } + + /** + * Verify invalid preset throws exception + */ + public function testInvalidPresetThrowsException() + { + $this->expectException(\Exception::class); + LinkConstructor::buildCleanTalkLink('nonexistent_preset', 'test'); + } + + /** + * Full URL exact match tests for regression detection. + * These are the canonical URLs that must not change. + */ + public function testExactUrlDataSetCookies() + { + $link = LinkConstructor::buildCleanTalkLink('apbct_hint_data__set_cookies', 'help/set-cookies-option'); + $this->assertSame( + 'https://cleantalk.org/help/set-cookies-option?utm_id=&utm_term=&utm_source=apbct_hint_data__set_cookies&utm_medium=WordPress&utm_campaign=ABPCT_Settings', + $link + ); + } + + public function testExactUrlCommentsHideWebsiteField() + { + $link = LinkConstructor::buildCleanTalkLink( + 'apbct_hint_comments__hide_website_field', + 'help/how-to-hide-website-field-in-wordpress-comments' + ); + $this->assertSame( + 'https://cleantalk.org/help/how-to-hide-website-field-in-wordpress-comments?utm_id=&utm_term=&utm_source=apbct_hint_comments__hide_website_field&utm_medium=hide_website_field_hint&utm_campaign=apbct_links', + $link + ); + } + + public function testExactUrlSfwAntiCrawler() + { + $link = LinkConstructor::buildCleanTalkLink('apbct_hint_sfw__anti_crawler', 'help/anti-flood-and-anti-crawler'); + $this->assertSame( + 'https://cleantalk.org/help/anti-flood-and-anti-crawler?utm_id=&utm_term=&utm_source=apbct_hint_sfw__anti_crawler&utm_medium=WordPress&utm_campaign=ABPCT_Settings', + $link + ); + } + + public function testExactUrlSfwAntiFlood() + { + $link = LinkConstructor::buildCleanTalkLink('apbct_hint_sfw__anti_flood', 'help/anti-flood-and-anti-crawler'); + $this->assertSame( + 'https://cleantalk.org/help/anti-flood-and-anti-crawler?utm_id=&utm_term=&utm_source=apbct_hint_sfw__anti_flood&utm_medium=WordPress&utm_campaign=ABPCT_Settings', + $link + ); + } + + public function testExactUrlDataPixel() + { + $link = LinkConstructor::buildCleanTalkLink( + 'apbct_hint_data__pixel', + 'introducing-cleantalk-pixel', + array(), + 'https://blog.cleantalk.org' + ); + $this->assertSame( + 'https://blog.cleantalk.org/introducing-cleantalk-pixel?utm_id=&utm_term=&utm_source=apbct_hint_data__pixel&utm_medium=WordPress&utm_campaign=ABPCT_Settings', + $link + ); + } + + public function testExactUrlDataHoneypotField() + { + $link = LinkConstructor::buildCleanTalkLink('apbct_hint_data__honeypot_field', 'help/honeypot-field'); + $this->assertSame( + 'https://cleantalk.org/help/honeypot-field?utm_id=&utm_term=&utm_source=apbct_hint_data__honeypot_field&utm_medium=WordPress&utm_campaign=ABPCT_Settings', + $link + ); + } + + public function testExactUrlSfwEnabled() + { + $link = LinkConstructor::buildCleanTalkLink('apbct_hint_sfw__enabled', 'help/anti-flood-and-anti-crawler'); + $this->assertSame( + 'https://cleantalk.org/help/anti-flood-and-anti-crawler?utm_id=&utm_term=&utm_source=apbct_hint_sfw__enabled&utm_medium=WordPress&utm_campaign=ABPCT_Settings', + $link + ); + } + + public function testExactUrlExclusionByFormSigns() + { + $link = LinkConstructor::buildCleanTalkLink('exclusion_by_form_signs', 'help/exclusion-by-form-signs'); + $this->assertSame( + 'https://cleantalk.org/help/exclusion-by-form-signs?utm_id=&utm_term=&utm_source=admin_panel&utm_medium=settings&utm_content=apbct_hint_exclusions__form_signs&utm_campaign=apbct_links', + $link + ); + } + + public function testExactUrlTrpLearnMoreLink() + { + $link = LinkConstructor::buildCleanTalkLink('trp_learn_more_link', 'the-real-person'); + $this->assertSame( + 'https://cleantalk.org/the-real-person?utm_id=&utm_term=&utm_source=admin_side&utm_medium=trp_badge&utm_content=trp_badge_link_click&utm_campaign=apbct_links', + $link + ); + } + + // ========================================================================= + // Tests for settings page links (second batch migration) + // ========================================================================= + + /** + * Line 116: Support link (SFW empty warning) + */ + public function testLinkSettingsSupportOpen() + { + $link = LinkConstructor::buildCleanTalkLink('settings_support_open', 'my/support/open'); + + $this->assertStringStartsWith('https://cleantalk.org/my/support/open?', $link); + $this->assertStringContainsString('utm_source=admin_panel', $link); + $this->assertStringContainsString('utm_medium=settings', $link); + $this->assertStringContainsString('utm_content=sfw_support_link', $link); + $this->assertStringContainsString('utm_campaign=apbct_links', $link); + } + + public function testExactUrlSettingsSupportOpen() + { + $link = LinkConstructor::buildCleanTalkLink('settings_support_open', 'my/support/open'); + $this->assertSame( + 'https://cleantalk.org/my/support/open?utm_id=&utm_term=&utm_source=admin_panel&utm_medium=settings&utm_content=sfw_support_link&utm_campaign=apbct_links', + $link + ); + } + + /** + * Line 291: Blog search form protection + */ + public function testLinkBlogSearchFormProtection() + { + $link = LinkConstructor::buildCleanTalkLink( + 'blog_search_form_protection', + 'how-to-protect-website-search-from-spambots', + array(), + 'https://blog.cleantalk.org' + ); + + $this->assertStringStartsWith('https://blog.cleantalk.org/how-to-protect-website-search-from-spambots?', $link); + $this->assertStringContainsString('utm_source=admin_panel', $link); + $this->assertStringContainsString('utm_medium=settings', $link); + $this->assertStringContainsString('utm_content=search_form_protection', $link); + } + + public function testExactUrlBlogSearchFormProtection() + { + $link = LinkConstructor::buildCleanTalkLink( + 'blog_search_form_protection', + 'how-to-protect-website-search-from-spambots', + array(), + 'https://blog.cleantalk.org' + ); + $this->assertSame( + 'https://blog.cleantalk.org/how-to-protect-website-search-from-spambots?utm_id=&utm_term=&utm_source=admin_panel&utm_medium=settings&utm_content=search_form_protection&utm_campaign=apbct_links', + $link + ); + } + + /** + * Line 530: Dashboard link with user_token and cp_mode + */ + public function testLinkSettingsDashboard() + { + $link = LinkConstructor::buildCleanTalkLink( + 'settings_dashboard_link', + 'my', + array('user_token' => 'test_token_123', 'cp_mode' => 'antispam') + ); + + $this->assertStringStartsWith('https://cleantalk.org/my?', $link); + $this->assertStringContainsString('user_token=test_token_123', $link); + $this->assertStringContainsString('cp_mode=antispam', $link); + $this->assertStringContainsString('utm_source=wp-backend', $link); + $this->assertStringContainsString('utm_medium=admin-bar', $link); + $this->assertStringContainsString('utm_content=settings_dashboard_link', $link); + } + + /** + * Line 668: Email existence alert help link + */ + public function testLinkSettingsEmailExistenceAlert() + { + $link = LinkConstructor::buildCleanTalkLink('settings_email_existence_alert', 'help/show-email-existence-alert'); + + $this->assertStringStartsWith('https://cleantalk.org/help/show-email-existence-alert?', $link); + $this->assertStringContainsString('utm_source=admin_panel', $link); + $this->assertStringContainsString('utm_medium=settings', $link); + $this->assertStringContainsString('utm_content=email_existence_alert', $link); + } + + public function testExactUrlSettingsEmailExistenceAlert() + { + $link = LinkConstructor::buildCleanTalkLink('settings_email_existence_alert', 'help/show-email-existence-alert'); + $this->assertSame( + 'https://cleantalk.org/help/show-email-existence-alert?utm_id=&utm_term=&utm_source=admin_panel&utm_medium=settings&utm_content=email_existence_alert&utm_campaign=apbct_links', + $link + ); + } + + /** + * Line 1129: Hoster API key profile link with #api_keys anchor + */ + public function testLinkSettingsHosterApiKey() + { + $link = LinkConstructor::buildCleanTalkLink('settings_hoster_api_key', 'my/profile'); + + $this->assertStringStartsWith('https://cleantalk.org/my/profile?', $link); + $this->assertStringContainsString('utm_source=admin_panel', $link); + $this->assertStringContainsString('utm_medium=settings', $link); + $this->assertStringContainsString('utm_content=hoster_api_key', $link); + + // Verify anchor can be appended + $linkWithAnchor = $link . '#api_keys'; + $this->assertStringEndsWith('#api_keys', $linkWithAnchor); + } + + /** + * Line 1374: Cloud Dashboard button with user_token + */ + public function testLinkSettingsCloudDashboardButton() + { + $link = LinkConstructor::buildCleanTalkLink( + 'settings_cloud_dashboard_button', + 'my', + array('user_token' => 'abc123', 'cp_mode' => 'antispam') + ); + + $this->assertStringStartsWith('https://cleantalk.org/my?', $link); + $this->assertStringContainsString('user_token=abc123', $link); + $this->assertStringContainsString('cp_mode=antispam', $link); + $this->assertStringContainsString('utm_source=admin_panel', $link); + $this->assertStringContainsString('utm_medium=settings', $link); + $this->assertStringContainsString('utm_content=cloud_dashboard_button', $link); + } + + /** + * Line 1879: Blog email validation status link + */ + public function testLinkBlogEmailValidationStatus() + { + $link = LinkConstructor::buildCleanTalkLink( + 'blog_email_validation_status', + 'real-time-email-address-existence-validation', + array(), + 'https://blog.cleantalk.org' + ); + + $this->assertStringStartsWith('https://blog.cleantalk.org/real-time-email-address-existence-validation?', $link); + $this->assertStringContainsString('utm_source=admin_panel', $link); + $this->assertStringContainsString('utm_medium=settings', $link); + $this->assertStringContainsString('utm_content=email_validation_status', $link); + } + + public function testExactUrlBlogEmailValidationStatus() + { + $link = LinkConstructor::buildCleanTalkLink( + 'blog_email_validation_status', + 'real-time-email-address-existence-validation', + array(), + 'https://blog.cleantalk.org' + ); + $this->assertSame( + 'https://blog.cleantalk.org/real-time-email-address-existence-validation?utm_id=&utm_term=&utm_source=admin_panel&utm_medium=settings&utm_content=email_validation_status&utm_campaign=apbct_links', + $link + ); + } + + /** + * Line 2013: Public offer link + */ + public function testLinkSettingsPublicOffer() + { + $link = LinkConstructor::buildCleanTalkLink('settings_public_offer', 'publicoffer'); + + $this->assertStringStartsWith('https://cleantalk.org/publicoffer?', $link); + $this->assertStringContainsString('utm_source=admin_panel', $link); + $this->assertStringContainsString('utm_medium=settings', $link); + $this->assertStringContainsString('utm_content=public_offer', $link); + } + + public function testExactUrlSettingsPublicOffer() + { + $link = LinkConstructor::buildCleanTalkLink('settings_public_offer', 'publicoffer'); + $this->assertSame( + 'https://cleantalk.org/publicoffer?utm_id=&utm_term=&utm_source=admin_panel&utm_medium=settings&utm_content=public_offer&utm_campaign=apbct_links', + $link + ); + } + + /** + * Verify all new settings page presets exist + */ + public function testAllSettingsPagePresetsExist() + { + $requiredPresets = array( + 'settings_support_open', + 'blog_search_form_protection', + 'settings_dashboard_link', + 'settings_email_existence_alert', + 'settings_hoster_api_key', + 'settings_cloud_dashboard_button', + 'blog_email_validation_status', + 'settings_public_offer', + ); + + foreach ($requiredPresets as $preset) { + $this->assertArrayHasKey( + $preset, + LinkConstructor::$utm_presets, + "UTM preset '$preset' must exist in LinkConstructor::\$utm_presets" + ); + } + } +} diff --git a/tests/Inc/TestCleantalkSettingsLinks.php b/tests/Inc/TestCleantalkSettingsLinks.php new file mode 100644 index 000000000..9fdf4bdea --- /dev/null +++ b/tests/Inc/TestCleantalkSettingsLinks.php @@ -0,0 +1,216 @@ +data['bot_detector_enabled'] = 1; + $apbct->data['wl_brandname'] = 'Anti-Spam by CleanTalk'; + $apbct->data['wl_mode_enabled'] = false; + $apbct->data['moderate'] = 1; + $apbct->data['moderate_ip'] = 0; + $apbct->data['ip_license'] = 0; + $this->apbct = $apbct; + } + + protected function tearDown(): void + { + global $apbct; + unset($apbct); + + // Reset Post singleton cache so $_POST changes are visible between tests + $instance = Post::getInstance(); + $instance->variables = array(); + + parent::tearDown(); + } + + // ========================================================================= + // apbct_settings__set_fields() — covers lines 115, 290, 530, 668 + // ========================================================================= + + /** + * Line 290 (blog_search_form_protection) - inside `! $apbct->white_label || is_main_site()` + * Line 530 (settings_dashboard_link) - inside `! $apbct->white_label && ! $apbct->data["wl_mode_enabled"]` + * Line 668 (settings_email_existence_alert) - unconditional + */ + public function testSetFieldsCoversSearchAndDashboardAndEmailLinks() + { + $fields = apbct_settings__set_fields(); + + // Line 290: forms__search_test description should contain the blog link + $this->assertArrayHasKey('forms_protection', $fields); + $search_field = $fields['forms_protection']['fields']['forms__search_test']; + $this->assertStringContainsString('blog.cleantalk.org', $search_field['description']); + $this->assertStringContainsString('utm_content=search_form_protection', $search_field['description']); + + // Line 530: data__general_postdata_test description should contain dashboard link + $this->assertArrayHasKey('data_processing', $fields); + $postdata_field = $fields['data_processing']['fields']['data__general_postdata_test']; + $this->assertStringContainsString('cleantalk.org/my', $postdata_field['description']); + $this->assertStringContainsString('utm_content=settings_dashboard_link', $postdata_field['description']); + + // Line 668: data__email_check_exist_post description should contain email alert link + $email_field = $fields['data_processing']['fields']['data__email_check_exist_post']; + $this->assertStringContainsString('help/show-email-existence-alert', $email_field['description']); + $this->assertStringContainsString('utm_content=email_existence_alert', $email_field['description']); + } + + /** + * Line 115 (settings_support_open) - inside condition: + * $apbct->api_key && is_null($apbct->fw_stats['firewall_updating_id']) + * && $apbct->settings['sfw__enabled'] && ! $apbct->stats['sfw']['entries'] + */ + public function testSetFieldsCoversSupportOpenLink() + { + global $apbct; + + // Set up conditions to hit line 115 + $apbct->storage['api_key'] = 'test_key_12345678'; + $apbct->storage['fw_stats'] = array('firewall_updating_id' => null); + $apbct->storage['settings'] = array_merge( + $apbct->default_settings, + array('sfw__enabled' => 1) + ); + $apbct->storage['stats'] = array('sfw' => array('entries' => 0)); + + $fields = apbct_settings__set_fields(); + + // The anti_crawler title should contain the support link + $anti_crawler = $fields['different']['fields']['sfw__anti_crawler']; + $this->assertStringContainsString('my/support/open', $anti_crawler['title']); + $this->assertStringContainsString('utm_content=sfw_support_link', $anti_crawler['title']); + } + + // ========================================================================= + // apbct_settings__field__state() — covers line 1873 + // ========================================================================= + + /** + * Line 1873 (blog_email_validation_status) - inside `! $apbct->white_label || is_main_site()` + */ + public function testFieldStateCoversEmailValidationLink() + { + ob_start(); + apbct_settings__field__state(); + $output = ob_get_clean(); + + $this->assertStringContainsString('blog.cleantalk.org', $output); + $this->assertStringContainsString('real-time-email-address-existence-validation', $output); + $this->assertStringContainsString('utm_content=email_validation_status', $output); + } + + // ========================================================================= + // apbct_settings__field__apikey() — covers line 2007 + // ========================================================================= + + /** + * Line 2007 (settings_public_offer) - unconditional after passing early returns. + * Needs: apbct__is_hosting_license() = false, not WPMS sub-site + */ + public function testFieldApikeyCoversPublicOfferLink() + { + global $apbct; + $apbct->data['moderate_ip'] = 0; + $apbct->data['ip_license'] = 0; + + ob_start(); + apbct_settings__field__apikey(); + $output = ob_get_clean(); + + $this->assertStringContainsString('publicoffer', $output); + $this->assertStringContainsString('utm_content=public_offer', $output); + } + + // ========================================================================= + // apbct_settings__get_long_descriptions_data() — covers lines 3045-3104 + // ========================================================================= + + /** + * Covers all LinkConstructor calls in long descriptions array. + * Lines: 3045, 3052, 3057, 3070, 3079, 3086, 3093, 3104 + * + * Calls the extracted helper function directly (no die/wp_die involved). + */ + public function testLongDescriptionCoversAllLinks() + { + $descriptions = apbct_settings__get_long_descriptions_data(); + + $this->assertIsArray($descriptions); + $this->assertArrayHasKey('data__set_cookies', $descriptions); + $this->assertStringContainsString('help/set-cookies-option', $descriptions['data__set_cookies']['desc']); + $this->assertStringContainsString('apbct_hint_data__set_cookies', $descriptions['data__set_cookies']['desc']); + } + + /** + * Test anti_crawler long description link with anchor. + */ + public function testLongDescriptionAntiCrawlerLink() + { + $descriptions = apbct_settings__get_long_descriptions_data(); + + $this->assertArrayHasKey('sfw__anti_crawler', $descriptions); + $this->assertStringContainsString('help/anti-flood-and-anti-crawler', $descriptions['sfw__anti_crawler']['desc']); + $this->assertStringContainsString('#anticrawl', $descriptions['sfw__anti_crawler']['desc']); + } + + /** + * Test sfw__enabled long description (line 3104). + */ + public function testLongDescriptionSfwEnabledLink() + { + $descriptions = apbct_settings__get_long_descriptions_data(); + + $this->assertArrayHasKey('sfw__enabled', $descriptions); + $this->assertStringContainsString('help/anti-flood-and-anti-crawler', $descriptions['sfw__enabled']['desc']); + $this->assertStringContainsString('apbct_hint_sfw__enabled', $descriptions['sfw__enabled']['desc']); + } + + /** + * Test data__pixel long description (line 3086) with blog.cleantalk.org domain. + */ + public function testLongDescriptionPixelLink() + { + $descriptions = apbct_settings__get_long_descriptions_data(); + + $this->assertArrayHasKey('data__pixel', $descriptions); + $this->assertStringContainsString('blog.cleantalk.org', $descriptions['data__pixel']['desc']); + $this->assertStringContainsString('introducing-cleantalk-pixel', $descriptions['data__pixel']['desc']); + } + + /** + * Test data__honeypot_field long description (line 3093). + */ + public function testLongDescriptionHoneypotLink() + { + $descriptions = apbct_settings__get_long_descriptions_data(); + + $this->assertArrayHasKey('data__honeypot_field', $descriptions); + $this->assertStringContainsString('help/honeypot-field', $descriptions['data__honeypot_field']['desc']); + $this->assertStringContainsString('apbct_hint_data__honeypot_field', $descriptions['data__honeypot_field']['desc']); + } +}