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 -294 trunk2.2.0 View file →
@@ -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 - * `&lt;p&gt;`). 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. &lt;p&gt;).
262 - if ( strpos( $content, '&lt;' ) !== 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' );