| @@ -18,30 +18,8 @@ | ||
| 18 | 18 | return $default; |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | 21 | /** |
| 22 | - * Render the guest login form used on topic/reply/forum prompts. | |
| 23 | - * | |
| 24 | - * Uses the Shortcodes setting when set; otherwise falls back to [forumax_login_form] | |
| 25 | - * so Sign In / Create Account tabs always appear (when registration is allowed). | |
| 26 | - * | |
| 27 | - * @return void | |
| 28 | - */ | |
| 29 | -function forumax_render_topic_login_form() { | |
| 30 | - $shortcode = ''; | |
| 31 | - | |
| 32 | - if ( function_exists( 'forumax_get_opt' ) ) { | |
| 33 | - $shortcode = trim( (string) forumax_get_opt( 'topic_login_shortcode', '[forumax_login_form]' ) ); | |
| 34 | - } | |
| 35 | - | |
| 36 | - if ( '' === $shortcode ) { | |
| 37 | - $shortcode = '[forumax_login_form]'; | |
| 38 | - } | |
| 39 | - | |
| 40 | - echo do_shortcode( $shortcode ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 41 | -} | |
| 42 | - | |
| 43 | -/** | |
| 44 | 22 | * Check if a plugin has been installed for specific number of days |
| 45 | 23 | * |
| 46 | 24 | * @param string $plugin_path The plugin path (e.g. 'woocommerce/woocommerce.php') |
| 47 | 25 | * @param int $days Number of days to check against |
| @@ -46,9 +24,9 @@ | ||
| 46 | 24 | * @param string $plugin_path The plugin path (e.g. 'woocommerce/woocommerce.php') |
| 47 | 25 | * @param int $days Number of days to check against |
| 48 | 26 | * @return bool True if plugin is installed for specified days, false otherwise |
| 49 | 27 | */ |
| 50 | -function forumax_is_plugin_installed_for_days( $days, $plugin_slug = 'bbpc' ) { | |
| 28 | +function forumax_is_plugin_installed_for_days( $days, $plugin_slug='eazydocs' ) { | |
| 51 | 29 | // Get the installation timestamp of the plugin |
| 52 | 30 | $installed_time = get_option( $plugin_slug . '_installed' ); |
| 53 | 31 | |
| 54 | 32 | // Ensure it's a valid timestamp |
| @@ -72,157 +50,11 @@ | ||
| 72 | 50 | function forumax_is_forum_page() { |
| 73 | 51 | if ( in_array( 'bbpress', get_body_class(), true ) ) { |
| 74 | 52 | return true; |
| 75 | 53 | } |
| 76 | - return false; | |
| 77 | 54 | } |
| 78 | 55 | |
| 79 | 56 | /** |
| 80 | - * Add a stable body class for all Forumax/bbPress related pages. | |
| 81 | - * | |
| 82 | - * This helps themes (e.g. Hello Biz) and custom CSS target Forumax pages | |
| 83 | - * reliably, even when the page uses shortcodes/blocks instead of bbPress | |
| 84 | - * native endpoints. | |
| 85 | - * | |
| 86 | - * @param array $classes Existing body classes. | |
| 87 | - * @return array | |
| 88 | - */ | |
| 89 | -function forumax_add_forumax_body_class( $classes ) { | |
| 90 | - if ( is_admin() ) { | |
| 91 | - return $classes; | |
| 92 | - } | |
| 93 | - | |
| 94 | - $should_add = false; | |
| 95 | - | |
| 96 | - // Native bbPress pages (forums, topics, replies, search, user profiles, etc.). | |
| 97 | - if ( function_exists( 'is_bbpress' ) && is_bbpress() ) { | |
| 98 | - $should_add = true; | |
| 99 | - } | |
| 100 | - | |
| 101 | - // Some installs load forum content via shortcodes/blocks on normal pages. | |
| 102 | - if ( ! $should_add ) { | |
| 103 | - $post = get_post(); | |
| 104 | - if ( $post instanceof WP_Post ) { | |
| 105 | - $shortcodes = array( | |
| 106 | - // bbPress shortcodes. | |
| 107 | - 'bbp-forum-index', | |
| 108 | - 'bbp-forum-form', | |
| 109 | - 'bbp-single-forum', | |
| 110 | - 'bbp-topic-index', | |
| 111 | - 'bbp-topic-form', | |
| 112 | - 'bbp-single-topic', | |
| 113 | - 'bbp-reply-form', | |
| 114 | - 'bbp-single-reply', | |
| 115 | - 'bbp-single-view', | |
| 116 | - 'bbp-search-form', | |
| 117 | - 'bbp-search', | |
| 118 | - 'bbp-login', | |
| 119 | - 'bbp-register', | |
| 120 | - 'bbp-lost-pass', | |
| 121 | - | |
| 122 | - // Forumax shortcodes. | |
| 123 | - 'forumax_login_form', | |
| 124 | - 'forumax_chat', | |
| 125 | - ); | |
| 126 | - | |
| 127 | - foreach ( $shortcodes as $shortcode ) { | |
| 128 | - if ( has_shortcode( $post->post_content, $shortcode ) ) { | |
| 129 | - $should_add = true; | |
| 130 | - break; | |
| 131 | - } | |
| 132 | - } | |
| 133 | - | |
| 134 | - // Also check for bbPress/Forumax blocks. | |
| 135 | - if ( ! $should_add && function_exists( 'has_block' ) ) { | |
| 136 | - $blocks = array( | |
| 137 | - 'bbpress/forum-index', | |
| 138 | - 'bbpress/topic-index', | |
| 139 | - 'forumax/forums', | |
| 140 | - ); | |
| 141 | - | |
| 142 | - foreach ( $blocks as $block ) { | |
| 143 | - if ( has_block( $block, $post ) ) { | |
| 144 | - $should_add = true; | |
| 145 | - break; | |
| 146 | - } | |
| 147 | - } | |
| 148 | - } | |
| 149 | - } | |
| 150 | - } | |
| 151 | - | |
| 152 | - if ( $should_add && ! in_array( 'forumax-body', $classes, true ) ) { | |
| 153 | - $classes[] = 'forumax-body'; | |
| 154 | - } | |
| 155 | - | |
| 156 | - return $classes; | |
| 157 | -} | |
| 158 | -add_filter( 'body_class', 'forumax_add_forumax_body_class', 20 ); | |
| 159 | - | |
| 160 | -/** | |
| 161 | - * Get hashed IP address for anonymous user identification. | |
| 162 | - * | |
| 163 | - * This function creates a one-way hash of the user's IP address, | |
| 164 | - * making it impossible to recover the original IP while still | |
| 165 | - * allowing duplicate detection (same IP = same hash). | |
| 166 | - * | |
| 167 | - * GDPR Compliant: Real IP addresses are never stored in the database. | |
| 168 | - * | |
| 169 | - * @since 2.3.1 | |
| 170 | - * | |
| 171 | - * @return string Hashed IP address (64 characters) | |
| 172 | - */ | |
| 173 | -function forumax_get_hashed_ip() { | |
| 174 | - $ip = ''; | |
| 175 | - | |
| 176 | - // Get the real IP address (handles proxies and load balancers) | |
| 177 | - if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) { | |
| 178 | - $ip = $_SERVER['HTTP_CLIENT_IP']; | |
| 179 | - } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { | |
| 180 | - // Can contain multiple IPs, get the first one | |
| 181 | - $ip_list = explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] ); | |
| 182 | - $ip = trim( $ip_list[0] ); | |
| 183 | - } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) { | |
| 184 | - $ip = $_SERVER['REMOTE_ADDR']; | |
| 185 | - } | |
| 186 | - | |
| 187 | - // Sanitize the IP | |
| 188 | - $ip = filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : 'unknown'; | |
| 189 | - | |
| 190 | - // Use WordPress salt for extra security (unique per site) | |
| 191 | - $salt = defined( 'NONCE_SALT' ) ? NONCE_SALT : 'forumax_default_salt_key'; | |
| 192 | - | |
| 193 | - // Create one-way hash - cannot be reversed to get original IP | |
| 194 | - return hash( 'sha256', $ip . $salt ); | |
| 195 | -} | |
| 196 | - | |
| 197 | -/** | |
| 198 | - * Get raw IP address for backward compatibility check only. | |
| 199 | - * | |
| 200 | - * WARNING: This function is used ONLY to check for legacy vote entries. | |
| 201 | - * The raw IP is NEVER stored - it's immediately compared against legacy | |
| 202 | - * data and then discarded. New votes always use forumax_get_hashed_ip(). | |
| 203 | - * | |
| 204 | - * @since 2.3.1 | |
| 205 | - * @access private | |
| 206 | - * | |
| 207 | - * @return string|false Raw IP address or false if invalid | |
| 208 | - */ | |
| 209 | -function forumax_get_raw_ip() { | |
| 210 | - $ip = ''; | |
| 211 | - | |
| 212 | - if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) { | |
| 213 | - $ip = $_SERVER['HTTP_CLIENT_IP']; | |
| 214 | - } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { | |
| 215 | - $ip_list = explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] ); | |
| 216 | - $ip = trim( $ip_list[0] ); | |
| 217 | - } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) { | |
| 218 | - $ip = $_SERVER['REMOTE_ADDR']; | |
| 219 | - } | |
| 220 | - | |
| 221 | - return filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : false; | |
| 222 | -} | |
| 223 | - | |
| 224 | -/** | |
| 225 | 57 | * Turn on the WordPress visual editor for bbPress |
| 226 | 58 | * |
| 227 | 59 | * @param array $args |
| 228 | 60 | * @return array |
| @@ -227,63 +59,14 @@ | ||
| 227 | 59 | * @param array $args |
| 228 | 60 | * @return array |
| 229 | 61 | */ |
| 230 | 62 | function forumax_bbp_enable_visual_editor( $args = [] ) { |
| 231 | - $args['tinymce'] = true; | |
| 232 | - $args['default_editor'] = 'tinymce'; | |
| 63 | + $args['tinymce'] = true; | |
| 233 | 64 | return $args; |
| 234 | 65 | } |
| 235 | 66 | add_filter( 'bbp_after_get_the_content_parse_args', 'forumax_bbp_enable_visual_editor' ); |
| 236 | 67 | |
| 237 | 68 | /** |
| 238 | - * Sanitize topic and reply content to prevent raw HTML display. | |
| 239 | - * | |
| 240 | - * When topics/replies are submitted from the frontend, bbPress runs | |
| 241 | - * `bbp_encode_bad` which entity-encodes HTML tags (e.g. `<p>` becomes | |
| 242 | - * `<p>`). This causes HTML tags to display as visible plain text | |
| 243 | - * instead of being rendered by the browser. | |
| 244 | - * | |
| 245 | - * This filter detects entity-encoded HTML, decodes it back to real HTML, | |
| 246 | - * and sanitizes the output through `wp_kses_post()` to allow only safe | |
| 247 | - * post-level HTML tags (p, strong, em, a, br, etc.) while stripping | |
| 248 | - * dangerous elements like script or iframe. | |
| 249 | - * | |
| 250 | - * @since 2.2.2 | |
| 251 | - * | |
| 252 | - * @param string $content The topic or reply content. | |
| 253 | - * @return string Sanitized content with proper HTML rendering. | |
| 254 | - */ | |
| 255 | -function forumax_sanitize_block_content( $content ) { | |
| 256 | - // Bail early if content is empty. | |
| 257 | - if ( empty( $content ) ) { | |
| 258 | - return $content; | |
| 259 | - } | |
| 260 | - | |
| 261 | - // Check if content contains entity-encoded HTML tags (e.g. <p>). | |
| 262 | - if ( strpos( $content, '<' ) !== false ) { | |
| 263 | - // Decode entity-encoded HTML back to real HTML tags. | |
| 264 | - $content = html_entity_decode( $content, ENT_QUOTES, 'UTF-8' ); | |
| 265 | - | |
| 266 | - // Sanitize with wp_kses_post — allows safe HTML (p, strong, a, br, etc.) | |
| 267 | - // but strips dangerous tags (script, iframe, etc.). | |
| 268 | - $content = wp_kses_post( $content ); | |
| 269 | - } | |
| 270 | - | |
| 271 | - // Strip inline style attributes from all HTML tags. | |
| 272 | - $content = preg_replace( '/\s+style="[^"]*"/i', '', $content ); | |
| 273 | - | |
| 274 | - // Strip block-editor class attributes (e.g. wp-block-paragraph). | |
| 275 | - $content = preg_replace( '/\s+class="wp-block-[^"]*"/i', '', $content ); | |
| 276 | - | |
| 277 | - // Clean up any remaining empty class attributes. | |
| 278 | - $content = preg_replace( '/\s+class=""/i', '', $content ); | |
| 279 | - | |
| 280 | - return $content; | |
| 281 | -} | |
| 282 | -add_filter( 'bbp_get_topic_content', 'forumax_sanitize_block_content', 4 ); | |
| 283 | -add_filter( 'bbp_get_reply_content', 'forumax_sanitize_block_content', 4 ); | |
| 284 | - | |
| 285 | -/** | |
| 286 | 69 | * Check if the pro plugin and plan is active |
| 287 | 70 | * |
| 288 | 71 | * @return bool|void |
| 289 | 72 | */ |
| @@ -327,16 +110,8 @@ | ||
| 327 | 110 | } elseif ( 'dashboard' === $admin ) { |
| 328 | 111 | if ( admin_url( 'admin.php?page=forumax' ) === $current_url ) { |
| 329 | 112 | return true; |
| 330 | 113 | } |
| 331 | - } elseif ( 'setup' === $admin ) { | |
| 332 | - if ( admin_url( 'admin.php?page=forumax-setup' ) === $current_url ) { | |
| 333 | - return true; | |
| 334 | - } | |
| 335 | - } elseif ( 'analytics' === $admin ) { | |
| 336 | - if ( admin_url( 'admin.php?page=forumax-analytics' ) === $current_url ) { | |
| 337 | - return true; | |
| 338 | - } | |
| 339 | 114 | } |
| 340 | 115 | |
| 341 | 116 | return false; |
| 342 | 117 | } |
| @@ -633,9 +408,9 @@ | ||
| 633 | 408 | } |
| 634 | 409 | add_filter( 'user_has_cap', 'forumax_grant_bbpress_caps_to_admins', 10, 3 ); |
| 635 | 410 | |
| 636 | 411 | /** |
| 637 | - * Mutual Deactivation of old and new plugin paths | |
| 412 | + * Mutual Deactivation of Forumax and bbp-core plugins | |
| 638 | 413 | */ |
| 639 | 414 | add_action( 'activated_plugin', function( $plugin ) { |
| 640 | 415 | if ( ! function_exists( 'is_plugin_active' ) ) { |
| 641 | 416 | require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| @@ -640,33 +415,31 @@ | ||
| 640 | 415 | if ( ! function_exists( 'is_plugin_active' ) ) { |
| 641 | 416 | require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 642 | 417 | } |
| 643 | 418 | |
| 644 | - $free_plugins_new = [ 'forumax/forumax.php' ]; | |
| 645 | - $free_plugins_old = [ 'bbp-core/bbp-core.php', 'bbp-core/forumax.php' ]; | |
| 646 | - | |
| 647 | - $pro_plugins_new = [ 'forumax-pro/forumax.php' ]; | |
| 648 | - $pro_plugins_old = [ 'bbp-core-pro/bbp-core.php', 'forumax-premium/bbp-core.php', 'forumax-premium/forumax.php' ]; | |
| 419 | + // All Forumax plugins | |
| 420 | + $forumax_plugins = [ | |
| 421 | + 'forumax/forumax.php', | |
| 422 | + 'forumax-pro/forumax.php', | |
| 423 | + ]; | |
| 649 | 424 | |
| 650 | - // If activating NEW free, deactivate OLD free | |
| 651 | - if ( in_array( $plugin, $free_plugins_new, true ) ) { | |
| 652 | - deactivate_plugins( $free_plugins_old ); | |
| 425 | + // All bbp-core plugins | |
| 426 | + $bbp_core_plugins = [ | |
| 427 | + 'bbp-core/bbp-core.php', | |
| 428 | + 'bbp-core-pro/bbp-core.php', | |
| 429 | + ]; | |
| 430 | + | |
| 431 | + // If activating Forumax (free or pro) → deactivate bbp-core (all versions) | |
| 432 | + if ( in_array( $plugin, $forumax_plugins, true ) ) { | |
| 433 | + deactivate_plugins( $bbp_core_plugins ); | |
| 653 | 434 | } |
| 654 | 435 | |
| 655 | - // If activating OLD free, deactivate NEW free | |
| 656 | - if ( in_array( $plugin, $free_plugins_old, true ) ) { | |
| 657 | - deactivate_plugins( $free_plugins_new ); | |
| 658 | - } | |
| 659 | - | |
| 660 | - // If activating NEW pro, deactivate OLD pro | |
| 661 | - if ( in_array( $plugin, $pro_plugins_new, true ) ) { | |
| 662 | - deactivate_plugins( $pro_plugins_old ); | |
| 663 | - } | |
| 664 | 436 | |
| 665 | - // If activating OLD pro, deactivate NEW pro | |
| 666 | - if ( in_array( $plugin, $pro_plugins_old, true ) ) { | |
| 667 | - deactivate_plugins( $pro_plugins_new ); | |
| 437 | + // If activating bbp-core (free or pro) → deactivate Forumax (all versions) | |
| 438 | + if ( in_array( $plugin, $bbp_core_plugins, true ) ) { | |
| 439 | + deactivate_plugins( $forumax_plugins ); | |
| 668 | 440 | } |
| 441 | + | |
| 669 | 442 | } ); |
| 670 | 443 | |
| 671 | 444 | /** |
| 672 | 445 | * Register Forum Sidebar widget area |
| @@ -858,9 +631,9 @@ | ||
| 858 | 631 | * @return int The number of unanswered topics. |
| 859 | 632 | */ |
| 860 | 633 | function forumax_get_unanswered_topics_count() { |
| 861 | 634 | $cache_key = 'frmx_unanswered_topics_count'; |
| 862 | - $count = get_transient( $cache_key ); | |
| 635 | + $count = wp_cache_get( $cache_key, 'forumax' ); | |
| 863 | 636 | |
| 864 | 637 | if ( false !== $count ) { |
| 865 | 638 | return $count; |
| 866 | 639 | } |
| @@ -873,9 +646,9 @@ | ||
| 873 | 646 | AND p.post_status = 'publish' |
| 874 | 647 | AND (pm.meta_value IS NULL OR pm.meta_value = '0')" |
| 875 | 648 | ); |
| 876 | 649 | |
| 877 | - set_transient( $cache_key, $count, HOUR_IN_SECONDS ); | |
| 650 | + wp_cache_set( $cache_key, $count, 'forumax', HOUR_IN_SECONDS ); | |
| 878 | 651 | return absint( $count ); |
| 879 | 652 | } |
| 880 | 653 | |
| 881 | 654 | /** |
| @@ -911,9 +684,9 @@ | ||
| 911 | 684 | /** |
| 912 | 685 | * Invalidate unanswered topics cache. |
| 913 | 686 | */ |
| 914 | 687 | function forumax_invalidate_unanswered_topics_cache() { |
| 915 | - delete_transient( 'frmx_unanswered_topics_count' ); | |
| 688 | + wp_cache_delete( 'frmx_unanswered_topics_count', 'forumax' ); | |
| 916 | 689 | } |
| 917 | 690 | add_action( 'bbp_new_reply', 'forumax_invalidate_unanswered_topics_cache' ); |
| 918 | 691 | add_action( 'bbp_deleted_reply', 'forumax_invalidate_unanswered_topics_cache' ); |
| 919 | 692 | add_action( 'bbp_trash_reply', 'forumax_invalidate_unanswered_topics_cache' ); |
| @@ -925,47 +698,4 @@ | ||
| 925 | 698 | add_action( 'bbp_trash_topic', 'forumax_invalidate_unanswered_topics_cache' ); |
| 926 | 699 | add_action( 'bbp_untrash_topic', 'forumax_invalidate_unanswered_topics_cache' ); |
| 927 | 700 | add_action( 'bbp_spam_topic', 'forumax_invalidate_unanswered_topics_cache' ); |
| 928 | 701 | add_action( 'bbp_unspam_topic', 'forumax_invalidate_unanswered_topics_cache' ); |
| 929 | - | |
| 930 | -/** | |
| 931 | - * Filter topics by unanswered status in admin dashboard. | |
| 932 | - * | |
| 933 | - * @param WP_Query $query The WP_Query instance (modified in place). | |
| 934 | - */ | |
| 935 | -function forumax_filter_unanswered_topics_admin( $query ) { | |
| 936 | - if ( ! is_admin() || ! $query->is_main_query() ) { | |
| 937 | - return; | |
| 938 | - } | |
| 939 | - | |
| 940 | - if ( 'topic' !== $query->get( 'post_type' ) ) { | |
| 941 | - return; | |
| 942 | - } | |
| 943 | - | |
| 944 | - $forumax_filter = ''; | |
| 945 | - if ( isset( $_GET['forumax_filter'] ) ) { | |
| 946 | - $forumax_filter = sanitize_key( wp_unslash( $_GET['forumax_filter'] ) ); | |
| 947 | - } | |
| 948 | - | |
| 949 | - if ( 'unanswered' === $forumax_filter ) { | |
| 950 | - $meta_query = $query->get( 'meta_query' ); | |
| 951 | - if ( ! is_array( $meta_query ) ) { | |
| 952 | - $meta_query = []; | |
| 953 | - } | |
| 954 | - | |
| 955 | - $meta_query[] = [ | |
| 956 | - 'relation' => 'OR', | |
| 957 | - [ | |
| 958 | - 'key' => '_bbp_reply_count', | |
| 959 | - 'value' => '0', | |
| 960 | - 'compare' => '=', | |
| 961 | - ], | |
| 962 | - [ | |
| 963 | - 'key' => '_bbp_reply_count', | |
| 964 | - 'compare' => 'NOT EXISTS', | |
| 965 | - ], | |
| 966 | - ]; | |
| 967 | - | |
| 968 | - $query->set( 'meta_query', $meta_query ); | |
| 969 | - } | |
| 970 | -} | |
| 971 | -add_action( 'pre_get_posts', 'forumax_filter_unanswered_topics_admin' ); | |