PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.2.0
Forumax – AI Powered Advanced Community Forum Plugin v2.2.0
2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 trunk 1.0.8 1.1.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 2.0.0 2.1.0 All 29 releases
← All changes | includes/functions.php +24 -272 2.4.22.2.0 View file →
@@ -24,9 +24,9 @@
24 24 * @param string $plugin_path The plugin path (e.g. 'woocommerce/woocommerce.php')
25 25 * @param int $days Number of days to check against
26 26 * @return bool True if plugin is installed for specified days, false otherwise
27 27 */
28 -function forumax_is_plugin_installed_for_days( $days, $plugin_slug = 'bbpc' ) {
28 +function forumax_is_plugin_installed_for_days( $days, $plugin_slug='eazydocs' ) {
29 29 // Get the installation timestamp of the plugin
30 30 $installed_time = get_option( $plugin_slug . '_installed' );
31 31
32 32 // Ensure it's a valid timestamp
@@ -50,157 +50,11 @@
50 50 function forumax_is_forum_page() {
51 51 if ( in_array( 'bbpress', get_body_class(), true ) ) {
52 52 return true;
53 53 }
54 - return false;
55 54 }
56 55
57 56 /**
58 - * Add a stable body class for all Forumax/bbPress related pages.
59 - *
60 - * This helps themes (e.g. Hello Biz) and custom CSS target Forumax pages
61 - * reliably, even when the page uses shortcodes/blocks instead of bbPress
62 - * native endpoints.
63 - *
64 - * @param array $classes Existing body classes.
65 - * @return array
66 - */
67 -function forumax_add_forumax_body_class( $classes ) {
68 - if ( is_admin() ) {
69 - return $classes;
70 - }
71 -
72 - $should_add = false;
73 -
74 - // Native bbPress pages (forums, topics, replies, search, user profiles, etc.).
75 - if ( function_exists( 'is_bbpress' ) && is_bbpress() ) {
76 - $should_add = true;
77 - }
78 -
79 - // Some installs load forum content via shortcodes/blocks on normal pages.
80 - if ( ! $should_add ) {
81 - $post = get_post();
82 - if ( $post instanceof WP_Post ) {
83 - $shortcodes = array(
84 - // bbPress shortcodes.
85 - 'bbp-forum-index',
86 - 'bbp-forum-form',
87 - 'bbp-single-forum',
88 - 'bbp-topic-index',
89 - 'bbp-topic-form',
90 - 'bbp-single-topic',
91 - 'bbp-reply-form',
92 - 'bbp-single-reply',
93 - 'bbp-single-view',
94 - 'bbp-search-form',
95 - 'bbp-search',
96 - 'bbp-login',
97 - 'bbp-register',
98 - 'bbp-lost-pass',
99 -
100 - // Forumax shortcodes.
101 - 'forumax_login_form',
102 - 'forumax_chat',
103 - );
104 -
105 - foreach ( $shortcodes as $shortcode ) {
106 - if ( has_shortcode( $post->post_content, $shortcode ) ) {
107 - $should_add = true;
108 - break;
109 - }
110 - }
111 -
112 - // Also check for bbPress/Forumax blocks.
113 - if ( ! $should_add && function_exists( 'has_block' ) ) {
114 - $blocks = array(
115 - 'bbpress/forum-index',
116 - 'bbpress/topic-index',
117 - 'forumax/forums',
118 - );
119 -
120 - foreach ( $blocks as $block ) {
121 - if ( has_block( $block, $post ) ) {
122 - $should_add = true;
123 - break;
124 - }
125 - }
126 - }
127 - }
128 - }
129 -
130 - if ( $should_add && ! in_array( 'forumax-body', $classes, true ) ) {
131 - $classes[] = 'forumax-body';
132 - }
133 -
134 - return $classes;
135 -}
136 -add_filter( 'body_class', 'forumax_add_forumax_body_class', 20 );
137 -
138 -/**
139 - * Get hashed IP address for anonymous user identification.
140 - *
141 - * This function creates a one-way hash of the user's IP address,
142 - * making it impossible to recover the original IP while still
143 - * allowing duplicate detection (same IP = same hash).
144 - *
145 - * GDPR Compliant: Real IP addresses are never stored in the database.
146 - *
147 - * @since 2.3.1
148 - *
149 - * @return string Hashed IP address (64 characters)
150 - */
151 -function forumax_get_hashed_ip() {
152 - $ip = '';
153 -
154 - // Get the real IP address (handles proxies and load balancers)
155 - if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
156 - $ip = $_SERVER['HTTP_CLIENT_IP'];
157 - } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
158 - // Can contain multiple IPs, get the first one
159 - $ip_list = explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] );
160 - $ip = trim( $ip_list[0] );
161 - } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
162 - $ip = $_SERVER['REMOTE_ADDR'];
163 - }
164 -
165 - // Sanitize the IP
166 - $ip = filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : 'unknown';
167 -
168 - // Use WordPress salt for extra security (unique per site)
169 - $salt = defined( 'NONCE_SALT' ) ? NONCE_SALT : 'forumax_default_salt_key';
170 -
171 - // Create one-way hash - cannot be reversed to get original IP
172 - return hash( 'sha256', $ip . $salt );
173 -}
174 -
175 -/**
176 - * Get raw IP address for backward compatibility check only.
177 - *
178 - * WARNING: This function is used ONLY to check for legacy vote entries.
179 - * The raw IP is NEVER stored - it's immediately compared against legacy
180 - * data and then discarded. New votes always use forumax_get_hashed_ip().
181 - *
182 - * @since 2.3.1
183 - * @access private
184 - *
185 - * @return string|false Raw IP address or false if invalid
186 - */
187 -function forumax_get_raw_ip() {
188 - $ip = '';
189 -
190 - if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
191 - $ip = $_SERVER['HTTP_CLIENT_IP'];
192 - } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
193 - $ip_list = explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] );
194 - $ip = trim( $ip_list[0] );
195 - } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
196 - $ip = $_SERVER['REMOTE_ADDR'];
197 - }
198 -
199 - return filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : false;
200 -}
201 -
202 -/**
203 57 * Turn on the WordPress visual editor for bbPress
204 58 *
205 59 * @param array $args
206 60 * @return array
@@ -205,63 +59,14 @@
205 59 * @param array $args
206 60 * @return array
207 61 */
208 62 function forumax_bbp_enable_visual_editor( $args = [] ) {
209 - $args['tinymce'] = true;
210 - $args['default_editor'] = 'tinymce';
63 + $args['tinymce'] = true;
211 64 return $args;
212 65 }
213 66 add_filter( 'bbp_after_get_the_content_parse_args', 'forumax_bbp_enable_visual_editor' );
214 67
215 68 /**
216 - * Sanitize topic and reply content to prevent raw HTML display.
217 - *
218 - * When topics/replies are submitted from the frontend, bbPress runs
219 - * `bbp_encode_bad` which entity-encodes HTML tags (e.g. `<p>` becomes
220 - * `&lt;p&gt;`). This causes HTML tags to display as visible plain text
221 - * instead of being rendered by the browser.
222 - *
223 - * This filter detects entity-encoded HTML, decodes it back to real HTML,
224 - * and sanitizes the output through `wp_kses_post()` to allow only safe
225 - * post-level HTML tags (p, strong, em, a, br, etc.) while stripping
226 - * dangerous elements like script or iframe.
227 - *
228 - * @since 2.2.2
229 - *
230 - * @param string $content The topic or reply content.
231 - * @return string Sanitized content with proper HTML rendering.
232 - */
233 -function forumax_sanitize_block_content( $content ) {
234 - // Bail early if content is empty.
235 - if ( empty( $content ) ) {
236 - return $content;
237 - }
238 -
239 - // Check if content contains entity-encoded HTML tags (e.g. &lt;p&gt;).
240 - if ( strpos( $content, '&lt;' ) !== false ) {
241 - // Decode entity-encoded HTML back to real HTML tags.
242 - $content = html_entity_decode( $content, ENT_QUOTES, 'UTF-8' );
243 -
244 - // Sanitize with wp_kses_post — allows safe HTML (p, strong, a, br, etc.)
245 - // but strips dangerous tags (script, iframe, etc.).
246 - $content = wp_kses_post( $content );
247 - }
248 -
249 - // Strip inline style attributes from all HTML tags.
250 - $content = preg_replace( '/\s+style="[^"]*"/i', '', $content );
251 -
252 - // Strip block-editor class attributes (e.g. wp-block-paragraph).
253 - $content = preg_replace( '/\s+class="wp-block-[^"]*"/i', '', $content );
254 -
255 - // Clean up any remaining empty class attributes.
256 - $content = preg_replace( '/\s+class=""/i', '', $content );
257 -
258 - return $content;
259 -}
260 -add_filter( 'bbp_get_topic_content', 'forumax_sanitize_block_content', 4 );
261 -add_filter( 'bbp_get_reply_content', 'forumax_sanitize_block_content', 4 );
262 -
263 -/**
264 69 * Check if the pro plugin and plan is active
265 70 *
266 71 * @return bool|void
267 72 */
@@ -305,16 +110,8 @@
305 110 } elseif ( 'dashboard' === $admin ) {
306 111 if ( admin_url( 'admin.php?page=forumax' ) === $current_url ) {
307 112 return true;
308 113 }
309 - } elseif ( 'setup' === $admin ) {
310 - if ( admin_url( 'admin.php?page=forumax-setup' ) === $current_url ) {
311 - return true;
312 - }
313 - } elseif ( 'analytics' === $admin ) {
314 - if ( admin_url( 'admin.php?page=forumax-analytics' ) === $current_url ) {
315 - return true;
316 - }
317 114 }
318 115
319 116 return false;
320 117 }
@@ -611,9 +408,9 @@
611 408 }
612 409 add_filter( 'user_has_cap', 'forumax_grant_bbpress_caps_to_admins', 10, 3 );
613 410
614 411 /**
615 - * Mutual Deactivation of old and new plugin paths
412 + * Mutual Deactivation of Forumax and bbp-core plugins
616 413 */
617 414 add_action( 'activated_plugin', function( $plugin ) {
618 415 if ( ! function_exists( 'is_plugin_active' ) ) {
619 416 require_once ABSPATH . 'wp-admin/includes/plugin.php';
@@ -618,33 +415,31 @@
618 415 if ( ! function_exists( 'is_plugin_active' ) ) {
619 416 require_once ABSPATH . 'wp-admin/includes/plugin.php';
620 417 }
621 418
622 - $free_plugins_new = [ 'forumax/forumax.php' ];
623 - $free_plugins_old = [ 'bbp-core/bbp-core.php', 'bbp-core/forumax.php' ];
624 -
625 - $pro_plugins_new = [ 'forumax-pro/forumax.php' ];
626 - $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 + ];
627 424
628 - // If activating NEW free, deactivate OLD free
629 - if ( in_array( $plugin, $free_plugins_new, true ) ) {
630 - 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 );
631 434 }
632 435
633 - // If activating OLD free, deactivate NEW free
634 - if ( in_array( $plugin, $free_plugins_old, true ) ) {
635 - deactivate_plugins( $free_plugins_new );
636 - }
637 -
638 - // If activating NEW pro, deactivate OLD pro
639 - if ( in_array( $plugin, $pro_plugins_new, true ) ) {
640 - deactivate_plugins( $pro_plugins_old );
641 - }
642 436
643 - // If activating OLD pro, deactivate NEW pro
644 - if ( in_array( $plugin, $pro_plugins_old, true ) ) {
645 - 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 );
646 440 }
441 +
647 442 } );
648 443
649 444 /**
650 445 * Register Forum Sidebar widget area
@@ -836,9 +631,9 @@
836 631 * @return int The number of unanswered topics.
837 632 */
838 633 function forumax_get_unanswered_topics_count() {
839 634 $cache_key = 'frmx_unanswered_topics_count';
840 - $count = get_transient( $cache_key );
635 + $count = wp_cache_get( $cache_key, 'forumax' );
841 636
842 637 if ( false !== $count ) {
843 638 return $count;
844 639 }
@@ -851,9 +646,9 @@
851 646 AND p.post_status = 'publish'
852 647 AND (pm.meta_value IS NULL OR pm.meta_value = '0')"
853 648 );
854 649
855 - set_transient( $cache_key, $count, HOUR_IN_SECONDS );
650 + wp_cache_set( $cache_key, $count, 'forumax', HOUR_IN_SECONDS );
856 651 return absint( $count );
857 652 }
858 653
859 654 /**
@@ -889,9 +684,9 @@
889 684 /**
890 685 * Invalidate unanswered topics cache.
891 686 */
892 687 function forumax_invalidate_unanswered_topics_cache() {
893 - delete_transient( 'frmx_unanswered_topics_count' );
688 + wp_cache_delete( 'frmx_unanswered_topics_count', 'forumax' );
894 689 }
895 690 add_action( 'bbp_new_reply', 'forumax_invalidate_unanswered_topics_cache' );
896 691 add_action( 'bbp_deleted_reply', 'forumax_invalidate_unanswered_topics_cache' );
897 692 add_action( 'bbp_trash_reply', 'forumax_invalidate_unanswered_topics_cache' );
@@ -903,47 +698,4 @@
903 698 add_action( 'bbp_trash_topic', 'forumax_invalidate_unanswered_topics_cache' );
904 699 add_action( 'bbp_untrash_topic', 'forumax_invalidate_unanswered_topics_cache' );
905 700 add_action( 'bbp_spam_topic', 'forumax_invalidate_unanswered_topics_cache' );
906 701 add_action( 'bbp_unspam_topic', 'forumax_invalidate_unanswered_topics_cache' );
907 -
908 -/**
909 - * Filter topics by unanswered status in admin dashboard.
910 - *
911 - * @param WP_Query $query The WP_Query instance (modified in place).
912 - */
913 -function forumax_filter_unanswered_topics_admin( $query ) {
914 - if ( ! is_admin() || ! $query->is_main_query() ) {
915 - return;
916 - }
917 -
918 - if ( 'topic' !== $query->get( 'post_type' ) ) {
919 - return;
920 - }
921 -
922 - $forumax_filter = '';
923 - if ( isset( $_GET['forumax_filter'] ) ) {
924 - $forumax_filter = sanitize_key( wp_unslash( $_GET['forumax_filter'] ) );
925 - }
926 -
927 - if ( 'unanswered' === $forumax_filter ) {
928 - $meta_query = $query->get( 'meta_query' );
929 - if ( ! is_array( $meta_query ) ) {
930 - $meta_query = [];
931 - }
932 -
933 - $meta_query[] = [
934 - 'relation' => 'OR',
935 - [
936 - 'key' => '_bbp_reply_count',
937 - 'value' => '0',
938 - 'compare' => '=',
939 - ],
940 - [
941 - 'key' => '_bbp_reply_count',
942 - 'compare' => 'NOT EXISTS',
943 - ],
944 - ];
945 -
946 - $query->set( 'meta_query', $meta_query );
947 - }
948 -}
949 -add_action( 'pre_get_posts', 'forumax_filter_unanswered_topics_admin' );