PluginProbe
wpForo Forum / 3.1.6
wpForo Forum v3.1.6
3.1.6 3.1.5 3.1.4 3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.10 1.4.11 1.4.12 1.4.13 All 138 releases
← All changes | includes/hooks.php +223 -142 3.1.03.1.6 View file →
@@ -16,9 +16,9 @@
16 16 'Boards',
17 17 'wpforo'
18 18 ) . '</a>';
19 19 array_unshift( $links, $settings_link );
20 -
20 +
21 21 return $links;
22 22 } );
23 23
24 24 function wpforo_notice_show() {
@@ -36,9 +36,9 @@
36 36 (array) WPF()->current_user_groupids,
37 37 (array) wpforo_setting( 'general', 'admin_bar' )
38 38 );
39 39 }
40 -
40 +
41 41 return $show_admin_bar;
42 42 } );
43 43
44 44 add_action( 'admin_notices', function() {
@@ -76,15 +76,15 @@
76 76 } );
77 77
78 78 add_filter( 'comments_open', function( $open ) {
79 79 if( is_wpforo_page() ) $open = false;
80 -
80 +
81 81 return $open;
82 82 } );
83 83
84 84 add_filter( 'comments_array', function( $comments ) {
85 85 if( is_wpforo_page() ) $comments = [];
86 -
86 +
87 87 return $comments;
88 88 }, 10, 2 );
89 89
90 90 add_action( 'wpforo_actions_end', function() {
@@ -106,24 +106,24 @@
106 106 'is_404' => false,
107 107 'is_page' => true,
108 108 'is_singular' => true,
109 109 ];
110 -
110 +
111 111 $target_post = get_post( $pageid );
112 112 if( ! is_a( $target_post, 'WP_Post' ) ) {
113 113 return;
114 114 }
115 -
115 +
116 116 $post = $target_post;
117 -
117 +
118 118 $wp_query->posts = [ $post ];
119 119 $wp_query->queried_object_id = $post->ID;
120 120 $wp_query->queried_object = $post;
121 -
121 +
122 122 foreach( $args as $key => $value ) {
123 123 $wp_query->$key = $value;
124 124 }
125 -
125 +
126 126 setup_postdata( $post );
127 127 }
128 128 }
129 129 } );
@@ -129,9 +129,9 @@
129 129 } );
130 130
131 131 add_filter( 'author_link', function( $link, $author_id ) {
132 132 if( wpforo_setting( 'profiles', 'profile' ) === 'wpforo' ) return WPF()->member->get_profile_url( $author_id );
133 -
133 +
134 134 return $link;
135 135 }, 10, 2 );
136 136
137 137 add_filter( 'get_comment_author_url', function( $link, $ID = 0, $object = null ) {
@@ -142,15 +142,15 @@
142 142 return WPF()->member->get_profile_url(
143 143 $object->user_id
144 144 );
145 145 }
146 -
146 +
147 147 return $link;
148 148 }, 10, 3 );
149 149
150 150 add_filter( 'register_url', function( $register_url ) {
151 151 if( wpforo_setting( 'authorization', 'use_our_register_url' ) ) $register_url = wpforo_register_url();
152 -
152 +
153 153 return $register_url;
154 154 } );
155 155
156 156 add_filter( 'login_url', function( $login_url ) {
@@ -162,15 +162,15 @@
162 162 'wp_auth_check_html, wp_login_url,'
163 163 ) === false ) {
164 164 $login_url = wpforo_login_url();
165 165 }
166 -
166 +
167 167 return $login_url;
168 168 } );
169 169
170 170 add_filter( 'logout_url', function( $logout_url ) {
171 171 if( wpforo_setting( 'authorization', 'use_our_login_url' ) ) $logout_url = wpforo_logout_url();
172 -
172 +
173 173 return $logout_url;
174 174 } );
175 175
176 176 add_filter( 'pre_trash_post', function( $check, $post ) {
@@ -177,9 +177,9 @@
177 177 if( in_array( $post->ID, WPF()->board->get_boards_pageids(), true ) ) {
178 178 $check = false;
179 179 WPF()->notice->add( 'DO NOT DELETE WPFORO PAGE!!!', 'error' );
180 180 }
181 -
181 +
182 182 return $check;
183 183 }, 10, 2 );
184 184
185 185 add_filter( 'wp_dropdown_pages', function( $output, $r ) {
@@ -192,9 +192,9 @@
192 192 $pageids
193 193 ) . ')[\'"][^<>]*?>[^<>]*?</option>#isu';
194 194 $output = preg_replace( $pattern, '', (string) $output );
195 195 }
196 -
196 +
197 197 return $output;
198 198 }, 10, 2 );
199 199
200 200 add_filter( 'pre_update_option', function( $value, $option, $old_value ) {
@@ -222,9 +222,9 @@
222 222 }
223 223 $value = ( $page_id && ! is_wp_error( $page_id ) ? $page_id : $old_value );
224 224 }
225 225 }
226 -
226 +
227 227 return $value;
228 228 }, 10, 3 );
229 229
230 230 function wpftpl_url( $filename ) {
@@ -234,9 +234,9 @@
234 234 $tpl_url = get_template_directory_uri() . '/wpforo/' . $filename;
235 235 }
236 236 if( ! $tpl_url ) $tpl_url = WPF()->tpl->template_url . '/' . $filename;
237 237 }
238 -
238 +
239 239 return apply_filters( 'wpforo_wpftpl_url', $tpl_url, $filename );
240 240 }
241 241
242 242 function wpftpl( $filename ) {
@@ -244,9 +244,9 @@
244 244 if( $filename ) {
245 245 $tpl = locate_template( 'wpforo/' . $filename );
246 246 if( ! $tpl ) $tpl = WPF()->tpl->template_dir . '/' . $filename;
247 247 }
248 -
248 +
249 249 return apply_filters( 'wpforo_wpftpl', $tpl, $filename );
250 250 }
251 251
252 252 add_shortcode( 'wpforo', function( $atts ) {
@@ -260,9 +260,9 @@
260 260 }
261 261 if( apply_filters( 'on_wpforo_load_remove_the_content_all_filters', false ) ) {
262 262 remove_all_filters( 'the_content' );
263 263 }
264 -
264 +
265 265 ob_start();
266 266 if( wpforo_current_user_is( 'admin' ) || ! wpforo_setting( 'board', 'under_construction' ) ) {
267 267 include( wpftpl( 'index.php' ) );
268 268 } else {
@@ -270,15 +270,15 @@
270 270 }
271 271 $output = ob_get_clean();
272 272 $output = trim( (string) $output );
273 273 if( ! $output ) $output = wpforo_hook_usage( 'the_content' );
274 -
274 +
275 275 return $output;
276 276 } );
277 277
278 278 function wpforo_hook_usage( $hook = '' ) {
279 279 global $wp_filter;
280 -
280 +
281 281 $output = '<div style="color: #990000; font-size: 16px;">Notice: a plugin conflict has been detected. wpForo forums are affected by other plugin errors.
282 282 Please deactivate all plugins, delete all caches and test again.
283 283 Then activate all plugins back one by one and find the conflict maker plugin.</div>
284 284 <pre style="display: none;">' . ( empty( $hook ) || ! isset( $wp_filter[ $hook ] ) ? 'No hook usage' : print_r(
@@ -284,9 +284,9 @@
284 284 <pre style="display: none;">' . ( empty( $hook ) || ! isset( $wp_filter[ $hook ] ) ? 'No hook usage' : print_r(
285 285 $wp_filter[ $hook ],
286 286 true
287 287 ) ) . '</pre>';
288 -
288 +
289 289 return $output;
290 290 }
291 291
292 292 add_action( 'wpforo_actions_end', 'wpforo_set_header_status' );
@@ -292,9 +292,9 @@
292 292 add_action( 'wpforo_actions_end', 'wpforo_set_header_status' );
293 293 function wpforo_set_header_status() {
294 294 if( is_wpforo_page() ) {
295 295 global $wp_query;
296 -
296 +
297 297 $status = ( WPF()->current_object['is_404'] ? 404 : 200 );
298 298 status_header( $status );
299 299 if( $status === 404 ) nocache_headers();
300 300 $wp_query->is_404 = false;
@@ -302,9 +302,9 @@
302 302 }
303 303
304 304 add_filter( 'pre_handle_404', function( $bypass ) {
305 305 if( is_wpforo_page() ) return true;
306 -
306 +
307 307 return $bypass;
308 308 } );
309 309
310 310 add_action( 'wpforo_actions_end', function() {
@@ -315,9 +315,9 @@
315 315 'index.php'
316 316 ) ) ) {
317 317 return $wpforo_template;
318 318 }
319 -
319 +
320 320 return $template;
321 321 } );
322 322 }
323 323 } );
@@ -329,9 +329,9 @@
329 329 '|',
330 330 pll_languages_list()
331 331 ) . ')/)?';
332 332 }
333 -
333 +
334 334 if( ! WPF()->board->get_current( 'is_standalone' ) ) {
335 335 $pageid = 0;
336 336 $boards = WPF()->board->get_boards( [ 'status' => true ] );
337 337 foreach( $boards as $board ) {
@@ -342,9 +342,9 @@
342 342 $pattern = $rewrite_prefix . preg_quote( $route ) . '(?:/|$).*$';
343 343 $to_url = 'index.php?lang=$matches[1]&page_id=' . $board['pageid'];
344 344 if( ! array_key_exists( $pattern, $rules ) ) $rules = array_merge( [ $pattern => $to_url ], $rules );
345 345 }
346 -
346 +
347 347 if( ! $pageid ) $pageid = wpforo_get_option( 'wpforo_pageid', 0 );
348 348 foreach( WPF()->board->routes as $route ) {
349 349 // $route = utf8_uri_encode( urldecode( (string) $route ) );
350 350 $route = urldecode( (string) $route );
@@ -354,9 +354,9 @@
354 354 $to_url = 'index.php?lang=$matches[1]&page_id=' . $pageid;
355 355 if( ! array_key_exists( $pattern, $rules ) ) $rules = array_merge( [ $pattern => $to_url ], $rules );
356 356 }
357 357 }
358 -
358 +
359 359 return $rules;
360 360 } );
361 361
362 362 add_action( 'wpforo_actions_end', function() {
@@ -369,9 +369,9 @@
369 369
370 370 function wpforo_include_once_theme_functions() {
371 371 $path = wpftpl( 'functions.php' );
372 372 if( file_exists( $path ) ) include_once( $path );
373 -
373 +
374 374 $path = wpftpl( 'functions-wp.php' );
375 375 if( file_exists( $path ) ) include_once( $path );
376 376 }
377 377
@@ -378,11 +378,11 @@
378 378 add_action( 'wpforo_after_init', 'wpforo_include_once_theme_functions' );
379 379
380 380 function wpforo_meta_title( $title ) {
381 381 $meta_title = [];
382 -
382 +
383 383 if( ! wpforo_setting( 'seo', 'seo_title' ) ) return $title;
384 -
384 +
385 385 if( is_wpforo_page() ) {
386 386 $template = WPF()->current_object['template'];
387 387 if( ! WPF()->current_object['is_404'] ) {
388 388 $paged = ( WPF()->current_object['paged'] > 1 ) ? wpforo_phrase( 'page', false ) . ' ' . WPF()->current_object['paged'] . ' ' : '';
@@ -529,9 +529,9 @@
529 529 $meta_title = wpforo_meta_title( $title );
530 530 if( is_array( $meta_title ) && ! empty( $meta_title ) ) {
531 531 $title = implode( ' &#8211; ', $meta_title );
532 532 }
533 -
533 +
534 534 return $title;
535 535 }
536 536
537 537 add_filter( 'wp_title', 'wpforo_meta_wp_title', 100 );
@@ -539,9 +539,9 @@
539 539 function wpforo_add_meta_tags() {
540 540 if( ! wpforo_setting( 'seo', 'seo_meta' ) ) {
541 541 return;
542 542 }
543 -
543 +
544 544 if( is_wpforo_page() && ! is_wpforo_shortcode_page() ) {
545 545 $title = '';
546 546 $og_img = '';
547 547 $tw_img = '';
@@ -791,22 +791,22 @@
791 791 if( ! is_user_logged_in() ) {
792 792 WPF()->notice->add( wpforo_get_login_or_register_notice_text() );
793 793 wp_send_json_error( WPF()->notice->get_notices() );
794 794 }
795 -
795 +
796 796 if( ! isset( $_POST['reportmsg'] ) || ! $_POST['reportmsg'] || ! isset( $_POST['postid'] ) || ! $_POST['postid'] ) {
797 797 WPF()->notice->add( 'Error: please insert some text to report.', 'error' );
798 798 wp_send_json_error( WPF()->notice->get_notices() );
799 799 }
800 -
800 +
801 801 $postid = intval( $_POST['postid'] );
802 802 $forumid = wpforo_post( $postid, 'forumid' );
803 -
803 +
804 804 if( ! WPF()->perm->can_report( $forumid ) ) {
805 805 WPF()->notice->add( 'You are not allowed to report.', 'error' );
806 806 wp_send_json_error( WPF()->notice->get_notices() );
807 807 }
808 -
808 +
809 809 ############### Sending Email ##################
810 810 $report_text = substr( strip_tags( (string) $_POST['reportmsg'] ), 0, 1000 );
811 811 $reporter = '<a href="' . WPF()->current_user['profile_url'] . '">' . ( WPF()->current_user['display_name'] ? WPF()->current_user['display_name'] : urldecode(
812 812 (string) WPF()->current_user['user_nicename']
@@ -812,12 +812,12 @@
812 812 (string) WPF()->current_user['user_nicename']
813 813 ) ) . '</a>';
814 814 $reportmsg = wpforo_kses( $report_text, 'email' );
815 815 $post_url = WPF()->post->get_url( $postid );
816 -
816 +
817 817 $subject = wpforo_setting( 'email', 'report_email_subject' );
818 818 $message = wpforo_setting( 'email', 'report_email_message' );
819 -
819 +
820 820 $from_tags = [ "[reporter]", "[message]", "[post_url]" ];
821 821 $to_words = [
822 822 sanitize_text_field( $reporter ),
823 823 $reportmsg,
@@ -822,16 +822,16 @@
822 822 sanitize_text_field( $reporter ),
823 823 $reportmsg,
824 824 '<a target="_blank" href="' . esc_url( (string) $post_url ) . '">' . esc_url( (string) $post_url ) . '</a>',
825 825 ];
826 -
826 +
827 827 $subject = stripslashes( strip_tags( str_replace( $from_tags, $to_words, (string) $subject ) ) );
828 828 $message = stripslashes( str_replace( $from_tags, $to_words, (string) $message ) );
829 -
829 +
830 830 $admin_emails = wpforo_setting( 'email', 'admin_emails' );
831 831 $admin_email = wpfval( $admin_emails, 0 );
832 832 $headers = wpforo_admin_mail_headers();
833 -
833 +
834 834 add_filter( 'wp_mail_content_type', 'wpforo_set_html_content_type', 999 );
835 835 if( ! @wp_mail( $admin_email, $subject, $message, $headers ) ) {
836 836 WPF()->notice->add( 'Can\'t send report email', 'error' );
837 837 wp_send_json_error( WPF()->notice->get_notices() );
@@ -836,9 +836,9 @@
836 836 WPF()->notice->add( 'Can\'t send report email', 'error' );
837 837 wp_send_json_error( WPF()->notice->get_notices() );
838 838 }
839 839 remove_filter( 'wp_mail_content_type', 'wpforo_set_html_content_type' );
840 -
840 +
841 841 ############### Sending Email end ##############
842 842 do_action( 'wpforo_after_post_report', $postid, $forumid, $_POST['reportmsg'] );
843 843 WPF()->notice->add( 'Message has been sent', 'success' );
844 844 wp_send_json_success( WPF()->notice->get_notices() );
@@ -852,9 +852,9 @@
852 852 $response = [ 'notice' => WPF()->notice->get_notices() ];
853 853 if( ! $topicid = wpforo_bigintval( wpfval( $_POST, 'topicid' ) ) ) {
854 854 wp_send_json_error( $response );
855 855 }
856 -
856 +
857 857 $sql = "SELECT `forumid` FROM `" . WPF()->tables->topics . "` WHERE `topicid` = $topicid";
858 858 $forumid = WPF()->db->get_var( $sql );
859 859 if( ! WPF()->perm->forum_can( 's', $forumid ) ) {
860 860 WPF()->notice->add( 'You don\'t have permission to do this action from this forum', 'error' );
@@ -1005,8 +1005,14 @@
1005 1005 wpforo_verify_nonce( 'wpforo_post_edit' );
1006 1006 $r = [ 'html' => '' ];
1007 1007 if( $postid = wpforo_bigintval( wpfval( $_POST, 'postid' ) ) ) {
1008 1008 if( $post = WPF()->post->get_post( $postid, false ) ) {
1009 + // forum_can() is a usergroup-level check and takes no post id, so for
1010 + // guests it cannot prove ownership of THIS post. Without this gate the
1011 + // edit form (post body + a valid form nonce) is handed to any visitor.
1012 + if( ! is_user_logged_in() && ! wpforo_guest_owns_post( $postid ) ) {
1013 + wp_send_json_error( $r );
1014 + }
1009 1015 if( WPF()->perm->forum_can( 'eor', $post['forumid'] ) || WPF()->perm->forum_can(
1010 1016 'eot',
1011 1017 $post['forumid']
1012 1018 ) ) {
@@ -1063,9 +1069,9 @@
1063 1069 $resp['notice'] = WPF()->notice->get_notices();
1064 1070 wp_send_json_success( $resp );
1065 1071 }
1066 1072 }
1067 -
1073 +
1068 1074 $resp['notice'] = WPF()->notice->get_notices();
1069 1075 wp_send_json_error( $resp );
1070 1076 }
1071 1077
@@ -1081,9 +1087,9 @@
1081 1087 'filter' => 'newest',
1082 1088 'paged' => 1,
1083 1089 ];
1084 1090 $request = array_merge( $request, $_POST );
1085 -
1091 +
1086 1092 if( $forumid = intval( $request['forumid'] ) ) {
1087 1093 $items_count = 0;
1088 1094 $childs = WPF()->forum->get_childs( $forumid );
1089 1095 $childs[] = $forumid;
@@ -1093,9 +1099,9 @@
1093 1099 'forumids' => $childs,
1094 1100 'orderby' => 'type, modified',
1095 1101 'order' => 'DESC',
1096 1102 ];
1097 -
1103 +
1098 1104 switch( $request['filter'] ) {
1099 1105 case 'solved':
1100 1106 $args['solved'] = 1;
1101 1107 $args['type'] = 0;
@@ -1108,9 +1114,9 @@
1108 1114 case 'hottest':
1109 1115 $args['orderby'] = 'posts';
1110 1116 break;
1111 1117 }
1112 -
1118 +
1113 1119 $topics = WPF()->topic->get_topics( $args, $items_count );
1114 1120 if( $topics ) {
1115 1121 ob_start();
1116 1122 if( function_exists( 'wpforo_thread_forum_template' ) ) {
@@ -1126,15 +1132,15 @@
1126 1132 }
1127 1133 } else {
1128 1134 $response['no_more'] = 1;
1129 1135 }
1130 -
1136 +
1131 1137 if( $response['no_more'] ) {
1132 1138 WPF()->notice->add( 'all topics has been loaded in this list', 'success' );
1133 1139 $response['notice'] = WPF()->notice->get_notices();
1134 1140 }
1135 1141 }
1136 -
1142 +
1137 1143 if( $success ) {
1138 1144 wp_send_json_success( $response );
1139 1145 } else {
1140 1146 wp_send_json_error( $response );
@@ -1338,13 +1344,13 @@
1338 1344 if( $comments = WPF()->post->get_posts( $args ) ) {
1339 1345 ob_start();
1340 1346 include_once( wpftpl( 'layouts/3/comment.php' ) );
1341 1347 ob_clean();
1342 -
1348 +
1343 1349 foreach( $comments as $comment ) {
1344 1350 wpforo_qa_comment_template( $comment );
1345 1351 }
1346 -
1352 +
1347 1353 $response['output_html'] = ob_get_clean();
1348 1354 $response['notice'] = '';
1349 1355 wp_send_json_success( $response );
1350 1356 }
@@ -1421,9 +1427,9 @@
1421 1427 $b = sanitize_email( $b );
1422 1428 $H[] = 'BCC: ' . $b;
1423 1429 }
1424 1430 }
1425 -
1431 +
1426 1432 return $H;
1427 1433 }
1428 1434
1429 1435 function wpforo_admin_mail_headers( $from_name = '', $from_email = '', $cc = [], $bcc = [] ) {
@@ -1447,9 +1453,9 @@
1447 1453 $b = sanitize_email( $b );
1448 1454 $H[] = 'BCC: ' . $b;
1449 1455 }
1450 1456 }
1451 -
1457 +
1452 1458 return $H;
1453 1459 }
1454 1460
1455 1461 function wpforo_get_login_or_register_notice_text() {
@@ -1466,9 +1472,9 @@
1466 1472 false
1467 1473 ) . '</a>'
1468 1474 );
1469 1475 }
1470 -
1476 +
1471 1477 return apply_filters( 'wpforo_login_or_register_popup_message', $popup_html );
1472 1478 }
1473 1479
1474 1480 function wpforo_dynamic_phrases_register() {
@@ -1475,9 +1481,9 @@
1475 1481 if( ! $js = WPF()->phrase->get_wpforo_phrases_inline_js() ) {
1476 1482 return;
1477 1483 }
1478 1484 $md5_js = md5( $js );
1479 -
1485 +
1480 1486 $inline = false;
1481 1487 $dynamic_js_file = WPF()->folders['assets']['dir'] . DIRECTORY_SEPARATOR . 'phrases.js';
1482 1488 if( ! file_exists( $dynamic_js_file ) || $md5_js !== md5_file( $dynamic_js_file ) ) {
1483 1489 $result = wpforo_write_file( $dynamic_js_file, $js );
@@ -1484,9 +1490,9 @@
1484 1490 if( wpfval( $result, 'error' ) ) {
1485 1491 $inline = true;
1486 1492 }
1487 1493 }
1488 -
1494 +
1489 1495 wp_register_script(
1490 1496 'wpforo-dynamic-phrases',
1491 1497 WPF()->folders['assets']['url'] . '/phrases.js',
1492 1498 false,
@@ -1502,9 +1508,9 @@
1502 1508 if( ! $css = WPF()->tpl->generate_dynamic_css() ) {
1503 1509 return;
1504 1510 }
1505 1511 $md5_css = md5( $css );
1506 -
1512 +
1507 1513 $inline = false;
1508 1514 $dynamic_css_file = WPF()->folders['assets']['dir'] . DIRECTORY_SEPARATOR . 'colors.css';
1509 1515 if( ! file_exists( $dynamic_css_file ) || $md5_css !== md5_file( $dynamic_css_file ) ) {
1510 1516 $result = wpforo_write_file( $dynamic_css_file, $css );
@@ -1511,9 +1517,9 @@
1511 1517 if( wpfval( $result, 'error' ) ) {
1512 1518 $inline = true;
1513 1519 }
1514 1520 }
1515 -
1521 +
1516 1522 wp_register_style(
1517 1523 'wpforo-dynamic-style',
1518 1524 WPF()->folders['assets']['url'] . '/colors.css',
1519 1525 false,
@@ -1522,9 +1528,9 @@
1522 1528 if( $inline ) {
1523 1529 $css = preg_replace( '|[\r\n\t]+|', '', $css );
1524 1530 wp_add_inline_style( 'wpforo-dynamic-style', $css );
1525 1531 }
1526 -
1532 +
1527 1533 wp_enqueue_style( 'wpforo-dynamic-style' );
1528 1534 }
1529 1535
1530 1536 add_action( 'wp_enqueue_scripts', 'wpforo_dynamic_style_enqueue', 999 );
@@ -1616,9 +1622,9 @@
1616 1622 $wpforo_widgets['live_notifications_start'] = $start;
1617 1623 $wpforo_widgets['live_notifications_interval'] = $interval;
1618 1624 }
1619 1625 wp_localize_script( 'wpforo-widgets-js', 'wpforo_widgets', $wpforo_widgets );
1620 -
1626 +
1621 1627 if( ! WPF()->perm->forum_can( 'va' ) || ! WPF()->usergroup->can( 'caa' ) ) {
1622 1628 wp_add_inline_script(
1623 1629 'wpforo-frontend-js',
1624 1630 "jQuery(document).ready(function($){
@@ -1634,11 +1640,11 @@
1634 1640 });
1635 1641 })"
1636 1642 );
1637 1643 }
1638 -
1644 +
1639 1645 do_action( 'wpforo_frontend_register_scripts' );
1640 -
1646 +
1641 1647 wpforo_frontend_enqueue_scripts();
1642 1648 }
1643 1649
1644 1650 add_action( 'wp_enqueue_scripts', 'wpforo_frontend_register_scripts' );
@@ -1648,9 +1654,9 @@
1648 1654 if( $fontawesome === 'sitewide' || ( $fontawesome === 'forum' && is_wpforo_page() ) ) {
1649 1655 wp_enqueue_style( 'wpforo-font-awesome' );
1650 1656 //if( is_rtl() ) wp_enqueue_style( 'wpforo-font-awesome-rtl' );
1651 1657 }
1652 -
1658 +
1653 1659 if( is_wpforo_page() ) {
1654 1660 wp_enqueue_script( 'wpforo-dynamic-phrases' );
1655 1661 wp_enqueue_script( 'wpforo-frontend-js' );
1656 1662 // Enqueue AI features script if AI service is available and any frontend AI feature is enabled
@@ -1673,15 +1679,15 @@
1673 1679 } else {
1674 1680 wp_enqueue_style( 'wpforo-style' );
1675 1681 }
1676 1682 }
1677 -
1683 +
1678 1684 if( is_rtl() ) {
1679 1685 wp_enqueue_style( 'wpforo-widgets-rtl' );
1680 1686 } else {
1681 1687 wp_enqueue_style( 'wpforo-widgets' );
1682 1688 }
1683 -
1689 +
1684 1690 do_action( 'wpforo_frontend_enqueue_scripts' );
1685 1691 }
1686 1692
1687 1693 function wpforo_style_options( $css ) {
@@ -1699,9 +1705,9 @@
1699 1705 ) . "px!important;}\r\n#wpforo-wrap .wpforo-post .wpf-right .wpforo-post-content p {font-size: " . wpforo_setting(
1700 1706 'styles',
1701 1707 'font_size_post_content'
1702 1708 ) . "px;}";
1703 -
1709 +
1704 1710 if( 'bottom' === wpforo_setting( 'posting', 'topic_editor_toolbar_location' ) ) {
1705 1711 $css .= "\r\n
1706 1712 #wpforo #wpforo-wrap .wpf-topic-create .mce-container-body{display: flex; flex-direction: column;}
1707 1713 #wpforo #wpforo-wrap .wpf-topic-create .mce-top-part{order: 1}
@@ -1826,9 +1832,9 @@
1826 1832 'jquery-ui-draggable',
1827 1833 'jquery-ui-slider',
1828 1834 'jquery-touch-punch',
1829 1835 ], false, true );
1830 -
1836 +
1831 1837 // Register AI Features admin assets
1832 1838 wp_register_style(
1833 1839 'wpforo-ai-features',
1834 1840 WPFORO_URL . '/admin/assets/css/ai-features.css',
@@ -1903,18 +1909,18 @@
1903 1909 ) !== false ) ) {
1904 1910 wp_enqueue_style( 'wpforo-deactivation-css' );
1905 1911 wp_enqueue_script( 'wpforo-deactivation-js' );
1906 1912 }
1907 -
1913 +
1908 1914 $screen = get_current_screen();
1909 1915 if( ( 'user-edit' === $screen->id || 'profile' === $screen->id ) ) {
1910 1916 wp_enqueue_style( 'wpforo-font-awesome' );
1911 1917 }
1912 -
1918 +
1913 1919 if( $screen->id === 'widgets' ) {
1914 1920 wp_enqueue_script( 'wpforo-backend-widgets-js' );
1915 1921 }
1916 -
1922 +
1917 1923 do_action( 'wpforo_admin_enqueue_scripts' );
1918 1924 }
1919 1925
1920 1926 add_action( 'admin_enqueue_scripts', 'wpforo_admin_enqueue' );
@@ -2202,14 +2208,39 @@
2202 2208 WPF()->member->synchronize_user( $userid );
2203 2209 if( wpfval( $_POST, 'wpfreg' ) ) {
2204 2210 $data = $_POST;
2205 2211 $data['userid'] = $userid;
2206 - $data['wpfreg'] = wpforo_clear_array( $data['wpfreg'], [
2212 +
2213 + // SECURITY: allowlist wpfreg keys + force the trusted userid so an
2214 + // attacker cannot pivot the downstream update() onto another user.
2215 + $wpfreg = is_array( $data['wpfreg'] ) ? $data['wpfreg'] : [];
2216 + $data['wpfreg'] = array_intersect_key( $wpfreg, array_flip( [
2207 2217 'user_login',
2208 2218 'user_email',
2209 2219 'user_pass1',
2210 2220 'user_pass2',
2211 - ], 'key' );
2221 + 'first_name',
2222 + 'last_name',
2223 + 'user_url',
2224 + ] ) );
2225 + $data['wpfreg']['userid'] = (int) $userid;
2226 +
2227 + // SECURITY: strip reserved wp_users column names from custom-field
2228 + // input so they cannot mass-assign via Members::update()'s array_merge().
2229 + if( isset( $data['data'] ) && is_array( $data['data'] ) ) {
2230 + foreach( [
2231 + 'user_email',
2232 + 'user_login',
2233 + 'user_pass',
2234 + 'user_pass1',
2235 + 'user_pass2',
2236 + 'userid',
2237 + 'ID',
2238 + ] as $reserved ) {
2239 + unset( $data['data'][ $reserved ] );
2240 + }
2241 + }
2242 +
2212 2243 WPF()->member->update( $data, 'full', false );
2213 2244 }
2214 2245 }
2215 2246
@@ -2241,14 +2272,14 @@
2241 2272 if( wpforo_is_owner( $userid ) ) {
2242 2273 $can_change_own_rating = false;
2243 2274 }
2244 2275 }
2245 -
2276 +
2246 2277 $custom_points = ( $can_change_own_rating && wpfval(
2247 2278 $_POST,
2248 2279 'wpforo_use_member_custom_points'
2249 2280 ) ) ? (int) wpfval( $_POST, 'wpforo_member_custom_points' ) : 0;
2250 -
2281 +
2251 2282 $args = [
2252 2283 'groupid' => intval( $groupid ),
2253 2284 'about' => wpforo_kses( $_POST['description'], 'user_description' ),
2254 2285 'timezone' => ( isset( $_POST['wpforo_usertimezone'] ) ? sanitize_text_field(
@@ -2257,9 +2288,9 @@
2257 2288 'secondary_groupids' => $secondary_groupids,
2258 2289 'custom_points' => $custom_points,
2259 2290 ];
2260 2291 WPF()->member->update_profile_fields( $userid, $args, false );
2261 -
2292 +
2262 2293 if( ! wpforo_is_owner( $userid ) ) {
2263 2294 WPF()->member->inactive_to_active( $userid );
2264 2295 }
2265 2296 }
@@ -2289,9 +2320,9 @@
2289 2320 }
2290 2321 } else {
2291 2322 $user = get_user_by( 'email', $id_or_email );
2292 2323 }
2293 -
2324 +
2294 2325 if( $user && is_object( $user ) ) {
2295 2326 if( $src = WPF()->member->get_avatar_url( $user->data->ID ) ) {
2296 2327 $avatar = "<img alt='" . esc_attr( $alt ) . "' src='" . esc_url(
2297 2328 (string) $src
@@ -2299,9 +2330,9 @@
2299 2330 $size
2300 2331 ) . "' width='" . esc_attr( $size ) . "' />";
2301 2332 }
2302 2333 }
2303 -
2334 +
2304 2335 return $avatar;
2305 2336 }
2306 2337
2307 2338 add_filter( 'get_avatar', 'wpforo_avatar', 10, 5 );
@@ -2338,9 +2369,9 @@
2338 2369 $user = get_user_by( 'email', $id_or_email->comment_author_email );
2339 2370 }
2340 2371 }
2341 2372 }
2342 -
2373 +
2343 2374 if( $user && is_object( $user ) ) {
2344 2375 if( $avatar_url = WPF()->member->get_avatar_url( $user->data->ID ) ) {
2345 2376 WPF()->ram_cache->set( $key, $avatar_url );
2346 2377 $args['url'] = $avatar_url;
@@ -2347,9 +2378,9 @@
2347 2378 }
2348 2379 }
2349 2380 }
2350 2381 }
2351 -
2382 +
2352 2383 return $args;
2353 2384 }
2354 2385
2355 2386 add_filter( 'pre_get_avatar_data', 'wpforo_pre_get_avatar_data', 10, 2 );
@@ -2360,9 +2391,9 @@
2360 2391 $type = sanitize_mime_type( $_FILES[ $argname ]['type'] ); //image/png
2361 2392 $tmp_name = sanitize_text_field( $_FILES[ $argname ]['tmp_name'] ); //D:\wamp\tmp\php986B.tmp
2362 2393 $error = intval( $_FILES[ $argname ]['error'] ); //0
2363 2394 $size = intval( $_FILES[ $argname ]['size'] ); //6112
2364 -
2395 +
2365 2396 $phpFileUploadErrors = [
2366 2397 0 => 'There is no error, the file uploaded with success',
2367 2398 1 => 'The uploaded file size is too big',
2368 2399 2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
@@ -2371,19 +2402,19 @@
2371 2402 6 => 'Missing a temporary folder',
2372 2403 7 => 'Failed to write file to disk.',
2373 2404 8 => 'A PHP extension stopped the file upload.',
2374 2405 ];
2375 -
2406 +
2376 2407 if( $error ) {
2377 2408 WPF()->notice->add( $phpFileUploadErrors[ $error ], 'error' );
2378 -
2409 +
2379 2410 return '';
2380 2411 } elseif( $size > wpforo_setting( 'posting', 'max_upload_size' ) ) {
2381 2412 WPF()->notice->add( 'The uploaded file size is too big', 'error' );
2382 -
2413 +
2383 2414 return '';
2384 2415 }
2385 -
2416 +
2386 2417 if( function_exists( 'pathinfo' ) ) {
2387 2418 $ext = pathinfo( $name, PATHINFO_EXTENSION );
2388 2419 } else {
2389 2420 $ext = substr( strrchr( (string) $name, '.' ), 1 );
@@ -2395,22 +2426,22 @@
2395 2426 $allowed_types = implode( '|', $mime_types );
2396 2427 $expld = explode( '|', $allowed_types );
2397 2428 if( ! in_array( $ext, $expld ) ) {
2398 2429 WPF()->notice->add( 'File type is not allowed', 'error' );
2399 -
2430 +
2400 2431 return '';
2401 2432 }
2402 2433 if( ! WPF()->perm->can_attach_file_type( $ext ) ) {
2403 2434 WPF()->notice->add( 'You are not allowed to attach this file type', 'error' );
2404 -
2435 +
2405 2436 return '';
2406 2437 }
2407 2438 }
2408 -
2439 +
2409 2440 $attach_dir = WPF()->folders['default_attachments']['dir'];
2410 2441 $attach_url = WPF()->folders['default_attachments']['url//'];
2411 2442 if( ! is_dir( $attach_dir ) ) wp_mkdir_p( $attach_dir );
2412 -
2443 +
2413 2444 $fnm = pathinfo( $name, PATHINFO_FILENAME );
2414 2445 $fnm = str_replace( ' ', '-', $fnm );
2415 2446 while( strpos( (string) $fnm, '--' ) !== false ) {
2416 2447 $fnm = str_replace( '--', '-', $fnm );
@@ -2417,14 +2448,14 @@
2417 2448 }
2418 2449 $fnm = preg_replace( "/[^-a-zA-Z0-9_]/", "", (string) $fnm );
2419 2450 $fnm = trim( (string) $fnm, "-" );
2420 2451 $fnm_empty = ! $fnm;
2421 -
2452 +
2422 2453 $file_name = $fnm . "." . $ext;
2423 -
2454 +
2424 2455 $attach_fname = time() . ( ! $fnm_empty ? '-' : '' ) . $file_name;
2425 2456 $attach_path = $attach_dir . DIRECTORY_SEPARATOR . $attach_fname;
2426 -
2457 +
2427 2458 if( is_dir( $attach_dir ) && move_uploaded_file( $tmp_name, $attach_path ) ) {
2428 2459 $attach_id = wpforo_insert_to_media_library( $attach_path, $fnm );
2429 2460 if( $return === 'html' ) {
2430 2461 return "\r\n" . '<div id="wpfa-' . $attach_id . '" class="wpforo-attached-file"><a class="wpforo-default-attachment" href="' . esc_url(
@@ -2444,9 +2475,9 @@
2444 2475 } else {
2445 2476 WPF()->notice->add( 'Can\'t upload file', 'error' );
2446 2477 }
2447 2478 }
2448 -
2479 +
2449 2480 return '';
2450 2481 }
2451 2482
2452 2483 function wpforo_add_default_attachment( $args ) {
@@ -2457,9 +2488,9 @@
2457 2488 $args['has_attach'] = 1;
2458 2489 }
2459 2490 }
2460 2491 }
2461 -
2492 +
2462 2493 return $args;
2463 2494 }
2464 2495
2465 2496 function wpforo_delete_attachment( $attach_post_id ) {
@@ -2515,9 +2546,9 @@
2515 2546 $text = str_replace( $match[0], $attach_html, $text );
2516 2547 }
2517 2548 }
2518 2549 }
2519 -
2550 +
2520 2551 return $text;
2521 2552 }
2522 2553
2523 2554 function wpforo_content_enable_do_shortcode() {
@@ -2539,9 +2570,9 @@
2539 2570 '<span style="color: red; font-weight: bold;">$1</span>',
2540 2571 (string) $text
2541 2572 );
2542 2573 }
2543 -
2574 +
2544 2575 return $text;
2545 2576 } );
2546 2577
2547 2578 add_action( 'wp_footer', function() { WPF()->cache->create(); } );
@@ -2555,9 +2586,9 @@
2555 2586 } else {
2556 2587 $reset_password_url = $match[0];
2557 2588 }
2558 2589 }
2559 -
2590 +
2560 2591 if( wpforo_setting( 'email', 'overwrite_reset_password_email' ) && $reset_password_url ) {
2561 2592 $message = str_replace(
2562 2593 [ '[user_login]', '[reset_password_url]' ],
2563 2594 [ $user_login, $reset_password_url, ],
@@ -2565,15 +2596,15 @@
2565 2596 );
2566 2597 $message = _wpforo_apply_email_shortcodes( $message, [ 'user' => [ 'userid' => $user_data->ID ] ] );
2567 2598 add_filter( 'wp_mail_content_type', '__wpforo_set_html_content_type', 999 );
2568 2599 }
2569 -
2600 +
2570 2601 return $message;
2571 2602 }, 999, 4 );
2572 2603
2573 2604 function wpforo_user_field_shortcode_to_value( $shortcode, $userid = null, $implode = true, $separator = ',' ) {
2574 2605 $value = null;
2575 -
2606 +
2576 2607 if( $shortcode && ( $field = preg_replace(
2577 2608 '#^\s*\[?\s*(?:user_|owner_)?(?:fields_)?([^\[\]]+?)\s*]?\s*$#iu',
2578 2609 '$1',
2579 2610 (string) $shortcode
@@ -2588,15 +2619,15 @@
2588 2619 }
2589 2620 $value = wpforo_member( $userid, $field );
2590 2621 if( $implode && wpforo_is_array_of_scalars( $value ) ) $value = implode( $separator, $value );
2591 2622 }
2592 -
2623 +
2593 2624 return $value;
2594 2625 }
2595 2626
2596 2627 function wpforo_forum_field_shortcode_to_value( $shortcode, $forumid, $implode = true, $separator = ',' ) {
2597 2628 $value = null;
2598 -
2629 +
2599 2630 if( $forumid ) {
2600 2631 if( $shortcode && ( $field = preg_replace(
2601 2632 '#^\s*\[?\s*(?:forum_)?(?:fields_)?([^\[\]]+?)\s*]?\s*$#iu',
2602 2633 '$1',
@@ -2614,15 +2645,15 @@
2614 2645 if( $implode && wpforo_is_array_of_scalars( $value ) ) $value = implode( $separator, $value );
2615 2646 }
2616 2647 }
2617 2648 }
2618 -
2649 +
2619 2650 return $value;
2620 2651 }
2621 2652
2622 2653 function wpforo_topic_field_shortcode_to_value( $shortcode, $topicid, $implode = true, $separator = ',' ) {
2623 2654 $value = null;
2624 -
2655 +
2625 2656 if( $topicid ) {
2626 2657 if( $shortcode && ( $field = preg_replace(
2627 2658 '#^\s*\[?\s*(?:topic_)?(?:fields_)?([^\[\]]+?)\s*]?\s*$#iu',
2628 2659 '$1',
@@ -2640,15 +2671,15 @@
2640 2671 if( $implode && wpforo_is_array_of_scalars( $value ) ) $value = implode( $separator, $value );
2641 2672 }
2642 2673 }
2643 2674 }
2644 -
2675 +
2645 2676 return $value;
2646 2677 }
2647 2678
2648 2679 function wpforo_post_field_shortcode_to_value( $shortcode, $postid, $implode = true, $separator = ',' ) {
2649 2680 $value = null;
2650 -
2681 +
2651 2682 if( $postid ) {
2652 2683 if( $shortcode && ( $field = preg_replace(
2653 2684 '#^\s*\[?\s*(?:post_)?(?:fields_)?([^\[\]]+?)\s*]?\s*$#iu',
2654 2685 '$1',
@@ -2666,9 +2697,9 @@
2666 2697 if( $implode && wpforo_is_array_of_scalars( $value ) ) $value = implode( $separator, $value );
2667 2698 }
2668 2699 }
2669 2700 }
2670 -
2701 +
2671 2702 return $value;
2672 2703 }
2673 2704
2674 2705 function wpforo_new_user_notification_email_admin( $wp_new_user_notification_email_admin, $user, $blogname ) {
@@ -2696,14 +2727,14 @@
2696 2727 '#\[[^\[\]]+?]#isu',
2697 2728 function( $match ) use ( $userid ) {
2698 2729 $value = wpforo_user_field_shortcode_to_value( $match[0], $userid );
2699 2730 if( ! $value || ! ( is_string( $value ) || is_numeric( $value ) ) ) $value = '';
2700 -
2731 +
2701 2732 return $value;
2702 2733 },
2703 2734 (string) $wp_new_user_notification_email_admin['message']
2704 2735 );
2705 -
2736 +
2706 2737 return $wp_new_user_notification_email_admin;
2707 2738 }
2708 2739
2709 2740 add_filter( 'wp_new_user_notification_email_admin', 'wpforo_new_user_notification_email_admin', 999, 3 );
@@ -2721,9 +2752,9 @@
2721 2752 } else {
2722 2753 $set_password_url = $match[0];
2723 2754 }
2724 2755 }
2725 -
2756 +
2726 2757 if( wpforo_setting( 'email', 'overwrite_new_user_notification' ) && $set_password_url ) {
2727 2758 $wp_new_user_notification_email['subject'] = str_replace(
2728 2759 '[blogname]',
2729 2760 '[' . $blogname . ']',
@@ -2738,9 +2769,9 @@
2738 2769 wpforo_setting( 'email', 'wp_new_user_notification_email_message' )
2739 2770 );
2740 2771 add_filter( 'wp_mail_content_type', '__wpforo_set_html_content_type', 999 );
2741 2772 }
2742 -
2773 +
2743 2774 return $wp_new_user_notification_email;
2744 2775 }
2745 2776
2746 2777 add_filter( 'wp_new_user_notification_email', 'wpforo_new_user_notification_email', 999, 3 );
@@ -2749,18 +2780,18 @@
2749 2780 return '#(?:<\s*)?https?://\S+?wp-login\.php(?:[?&]action=rp|[?&]login=[^?&=\s]+)?[?&]key=(?<key>[^?&=\s]+)(?:[?&]action=rp|[?&]login=[^?&=\s]+)?(?:\s*>)?#isu';
2750 2781 }
2751 2782
2752 2783 function wpforo_synch_user_roles( $userid ) {
2753 -
2784 +
2754 2785 $user = get_userdata( $userid );
2755 -
2786 +
2756 2787 if( ! empty( $user ) && ! empty( $user->roles ) ) {
2757 2788 $role_usergroups = WPF()->usergroup->get_role_usergroup_relation();
2758 -
2789 +
2759 2790 if( ! empty( $role_usergroups ) ) {
2760 2791 $append_second_groups = apply_filters( 'wpforo_synch_user_roles_append_secondary_groups', false );
2761 2792 $ug_users_array = WPF()->usergroup->build_users_groupid_array( $role_usergroups, [ $user ] );
2762 -
2793 +
2763 2794 if( wpfval( $ug_users_array, 'user_prime_group' ) ) {
2764 2795 $userid_groupids = current( $ug_users_array['user_prime_group'] );
2765 2796 if( ! empty( $userid_groupids ) ) {
2766 2797 $groupid = current( $userid_groupids );
@@ -2766,9 +2797,9 @@
2766 2797 $groupid = current( $userid_groupids );
2767 2798 if( $groupid ) WPF()->member->set_groupid( $userid, $groupid );
2768 2799 }
2769 2800 }
2770 -
2801 +
2771 2802 if( wpfval( $ug_users_array, 'user_second_groups' ) ) {
2772 2803 foreach( $ug_users_array['user_second_groups'] as $userid => $secondary_usergroups ) {
2773 2804 if( ! empty( $secondary_usergroups ) ) {
2774 2805 if( $append_second_groups ) {
@@ -2782,9 +2813,9 @@
2782 2813 }
2783 2814 } elseif( ! $append_second_groups && ! wpfval( $ug_users_array, 'user_second_groups' ) ) {
2784 2815 WPF()->member->set_secondary_groupids( $userid, [] );
2785 2816 }
2786 -
2817 +
2787 2818 delete_user_meta( intval( $userid ), '_wpf_member_obj' );
2788 2819 }
2789 2820 }
2790 2821 }
@@ -2805,9 +2836,9 @@
2805 2836 'id' => 0,
2806 2837 ];
2807 2838
2808 2839 $wpforo_synch_roles_data = isset( $_POST['wpforo_synch_roles_data'] ) ? $_POST['wpforo_synch_roles_data'] : '';
2809 -
2840 +
2810 2841 if( $wpforo_synch_roles_data ) {
2811 2842 parse_str( $wpforo_synch_roles_data, $data );
2812 2843 check_ajax_referer( 'wpforo_synch_roles', 'checkthis' );
2813 2844 $limit = apply_filters( 'wpforo_synch_roles_step_limit', 20 );
@@ -2897,13 +2928,13 @@
2897 2928 wpforo_delete_option( 'synch_roles' );
2898 2929 }
2899 2930 }
2900 2931 }
2901 -
2932 +
2902 2933 if( intval( wpfval( $status, 'progress' ) ) === 100 ) {
2903 2934 WPF()->notice->add( 'Role-Usergroup synchronization is complete!', 'success' );
2904 2935 }
2905 -
2936 +
2906 2937 wp_die( wp_json_encode( $status ) );
2907 2938 }
2908 2939
2909 2940 add_action( 'wp_ajax_wpforo_synch_roles', 'wpforo_synch_roles' );
@@ -2926,27 +2957,28 @@
2926 2957 '<div class="wp-core-ui wp-ui-notification wpforo-menu-notification-counter">%1$d</div>',
2927 2958 $number
2928 2959 );
2929 2960 }
2930 -
2961 +
2931 2962 return '';
2932 2963 }
2933 2964
2934 2965 function wpforo_get_memb_attention_count(): int {
2966 + if( is_null( WPF()->member ) ) return 0;
2935 2967 return WPF()->member->get_count( [ 'p.status' => [ 'banned', 'inactive' ] ] );
2936 2968 }
2937 2969
2938 2970 function wpforo_get_admin_bar_numbers(): array {
2939 2971 $all_count = 0;
2940 - $mod_count = WPF()->post->unapproved_count();
2972 + $mod_count = is_null( WPF()->post ) ? 0 : WPF()->post->unapproved_count();
2941 2973 $all_count += $mod_count;
2942 -
2974 +
2943 2975 return compact( 'all_count', 'mod_count' );
2944 2976 }
2945 2977
2946 2978 function wpforo_admin_bar_menu( $wp_admin_bar ) {
2947 2979 $admin_bar_numbers = wpforo_get_admin_bar_numbers();
2948 -
2980 +
2949 2981 if( wpforo_current_user_is( 'admin' ) ) {
2950 2982 $args = [
2951 2983 'id' => 'new-forum',
2952 2984 'title' => '&#43;&nbsp;&nbsp;' . __( 'Add New Forum', 'wpforo' ),
@@ -2970,9 +3002,9 @@
2970 3002 'parent' => 'new-content',
2971 3003 ];
2972 3004 $wp_admin_bar->add_node( $args );
2973 3005 }
2974 -
3006 +
2975 3007 $args = [
2976 3008 'id' => 'wpforo-home',
2977 3009 'title' => __( 'Visit Forum', 'wpforo' ),
2978 3010 'href' => wpforo_home_url(),
@@ -2979,9 +3011,9 @@
2979 3011 'parent' => 'wpf-community',
2980 3012 // 'meta' => [ 'target' => '_blank' ]
2981 3013 ];
2982 3014 $wp_admin_bar->add_node( $args );
2983 -
3015 +
2984 3016 if( wpforo_current_user_is( 'admin' ) || WPF()->usergroup->can( 'mf' ) || WPF()->usergroup->can( 'ms' ) || WPF()->usergroup->can( 'vm' ) || WPF()->usergroup->can( 'mp' ) || WPF()->usergroup->can(
2985 3017 'aum'
2986 3018 ) || WPF()->usergroup->can( 'vmg' ) || WPF()->usergroup->can( 'mth' ) ) {
2987 3019 $args = [
@@ -3137,15 +3169,15 @@
3137 3169 'href' => admin_url( 'admin.php?page=wpforo-overview' ),
3138 3170 'parent' => 'new-content',
3139 3171 ];
3140 3172 $wp_admin_bar->add_node( $args );
3141 -
3173 +
3142 3174 if( $boardids = WPF()->board->get_active_boardids() ) {
3143 3175 foreach( $boardids as $boardid ) {
3144 3176 WPF()->change_board( $boardid );
3145 3177 $current = WPF()->board->get_current();
3146 3178 $bds_bar_numbers[ $boardid ] = wpforo_get_admin_bar_numbers();
3147 -
3179 +
3148 3180 $menuid = 'wpforo-new-content-' . $current['slug'];
3149 3181 $args = [
3150 3182 'id' => $menuid,
3151 3183 'title' => $current['title'],
@@ -3178,13 +3210,13 @@
3178 3210 ),
3179 3211 'parent' => $menuid,
3180 3212 ];
3181 3213 $wp_admin_bar->add_node( $args );
3182 -
3214 +
3183 3215 }
3184 3216 }
3185 3217 }
3186 -
3218 +
3187 3219 if( wpforo_current_user_is( 'admin' ) || WPF()->usergroup->can( 'mf' ) || WPF()->usergroup->can( 'ms' ) || WPF()->usergroup->can( 'vm' ) || WPF()->usergroup->can( 'mp' ) || WPF()->usergroup->can(
3188 3220 'aum'
3189 3221 ) || WPF()->usergroup->can( 'vmg' ) || WPF()->usergroup->can( 'mth' ) ) {
3190 3222 $bds_bar_all = 0;
@@ -3194,15 +3226,15 @@
3194 3226 'title' => 'wpForo' . wpforo_wp_admin_bar_red_circle_number( $bds_bar_all ),
3195 3227 'href' => admin_url( 'admin.php?page=wpforo-overview' ),
3196 3228 ];
3197 3229 $wp_admin_bar->add_node( $args );
3198 -
3199 -
3230 +
3231 +
3200 3232 if( $boardids = WPF()->board->get_active_boardids() ) {
3201 3233 foreach( $boardids as $boardid ) {
3202 3234 WPF()->change_board( $boardid );
3203 3235 $current = WPF()->board->get_current();
3204 -
3236 +
3205 3237 $menuid = 'wpforo-' . $current['slug'];
3206 3238 $args = [
3207 3239 'id' => $menuid,
3208 3240 'title' => '' . $current['title'] . ' (' . strtok(
@@ -3212,11 +3244,11 @@
3212 3244 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) ),
3213 3245 'parent' => 'wpforo',
3214 3246 ];
3215 3247 $wp_admin_bar->add_node( $args );
3216 -
3248 +
3217 3249 ## ---------------------------------------------------------------------------------- ####
3218 -
3250 +
3219 3251 $args = [
3220 3252 'id' => $menuid . '-home',
3221 3253 'title' => '' . __( 'Visit Forum', 'wpforo' ),
3222 3254 'href' => wpforo_home_url(),
@@ -3223,9 +3255,9 @@
3223 3255 'parent' => $menuid,
3224 3256 // 'meta' => [ 'target' => '_blank' ]
3225 3257 ];
3226 3258 $wp_admin_bar->add_node( $args );
3227 -
3259 +
3228 3260 if( WPF()->usergroup->can( 'mf' ) || wpforo_current_user_is( 'admin' ) ) {
3229 3261 $args = [
3230 3262 'id' => $menuid . '-forums',
3231 3263 'title' => '' . __( 'Forums', 'wpforo' ),
@@ -3290,11 +3322,11 @@
3290 3322 $wp_admin_bar->add_node( $args );
3291 3323 }
3292 3324 }
3293 3325 }
3294 -
3326 +
3295 3327 ##### -- ##### ----- $ ----- ###
3296 -
3328 +
3297 3329 if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
3298 3330 $args = [
3299 3331 'id' => 'wpforo-boards',
3300 3332 'title' => '' . __( 'Boards', 'wpforo' ),
@@ -3469,9 +3501,9 @@
3469 3501 add_action( 'wp_ajax_wpforo_notifications', 'wpforo_check_notifications' );
3470 3502
3471 3503 function wpforo_can_display_recaptcha_note() {
3472 3504 $d = wpforo_is_admin() ? 'recaptcha_backend_note' : 'recaptcha_note';
3473 -
3505 +
3474 3506 return ! WPF()->dissmissed[ $d ] && current_user_can( 'administrator' ) && ! wp_is_mobile() && wpforo_setting(
3475 3507 'authorization',
3476 3508 'user_register'
3477 3509 ) && ! wpforo_setting( 'authorization', 'register_url' ) && ! wpforo_is_recaptcha_configured();
@@ -3621,9 +3653,9 @@
3621 3653 $var = esc_html( $var );
3622 3654 } else {
3623 3655 $var = wpforo_esc_html( $var );
3624 3656 }
3625 -
3657 +
3626 3658 return $var;
3627 3659 }
3628 3660
3629 3661 add_action( 'wpforo_bottom_hook', 'wpforo_debug' );
@@ -3656,9 +3688,9 @@
3656 3688 $forumids = WPF()->forum->get_forumid_and_childids( $forumid );
3657 3689 unset( $args['forumid'] );
3658 3690 $args['forumids'] = $forumids;
3659 3691 }
3660 -
3692 +
3661 3693 return $args;
3662 3694 } );
3663 3695
3664 3696 function wpf_body_wp_encode_emoji( $post ) {
@@ -3663,9 +3695,9 @@
3663 3695
3664 3696 function wpf_body_wp_encode_emoji( $post ) {
3665 3697 if( wpfkey( $post, 'title' ) ) $post['title'] = wp_encode_emoji( $post['title'] );
3666 3698 if( wpfkey( $post, 'body' ) ) $post['body'] = wp_encode_emoji( $post['body'] );
3667 -
3699 +
3668 3700 return $post;
3669 3701 }
3670 3702
3671 3703 add_filter( 'wpforo_add_post_data_filter', 'wpf_body_wp_encode_emoji' );
@@ -3696,4 +3728,53 @@
3696 3728 wp_safe_redirect( admin_url( 'admin.php?page=wpforo-overview' ) );
3697 3729 exit;
3698 3730 }
3699 3731 }, 1 );
3732 +
3733 +// AI Pricing Promo Notice - shows to users without active AI subscription
3734 +add_action( 'admin_notices', function() {
3735 + if( ! current_user_can( 'manage_options' ) ) return;
3736 +
3737 + // Check if dismissed
3738 + $dismissed = get_option( 'wpforo_ai_pricing_promo_dismissed' );
3739 + if( $dismissed === '2026-08' ) return;
3740 +
3741 + // Check if user has active paid subscription - don't show to them
3742 + if( function_exists( 'WPF' ) && WPF()->ai_client ) {
3743 + $status = WPF()->ai_client->get_subscription_status();
3744 + $plan = WPF()->ai_client->get_subscription_plan();
3745 + $paid_plans = [ 'starter', 'professional', 'business', 'enterprise' ];
3746 + if( $status === 'active' && in_array( $plan, $paid_plans, true ) ) return;
3747 + }
3748 +
3749 + $ai_url = admin_url( 'admin.php?page=wpforo-ai' );
3750 + ?>
3751 + <div class="notice notice-info is-dismissible wpforo-ai-pricing-notice" style="border-left-color: #43a6df; padding: 15px 20px; display: flex; align-items: center;">
3752 + <svg class="wpf-ai-sparkle-icon" xmlns="http://www.w3.org/2000/svg" height="55px" viewBox="0 0 24 24" fill="none" stroke="#43a6df" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink: 0; margin-right: 20px;"><path d="M12 3l1.912 5.813a2 2 0 0 0 1.275 1.275L21 12l-5.813 1.912a2 2 0 0 0-1.275 1.275L12 21l-1.912-5.813a2 2 0 0 0-1.275-1.275L3 12l5.813-1.912a2 2 0 0 0 1.275-1.275L12 3z"></path><path d="M5 3v4"></path><path d="M3 5h4"></path><path d="M19 17v4"></path><path d="M17 19h4"></path></svg>
3753 + <div>
3754 + <p style="margin: 0 0 4px 0; font-size: 17px; font-weight: 600; color: #1d2327;">
3755 + wpForo AI Features — Free Month with 500 Credits and Subscription Starting at Just <span style="font-size: 17px;">&cuepr; <span style="color: #DA5800;">$9</span> &cuesc; !</span>
3756 + </p>
3757 + <p style="margin: 0 0 8px 0; font-size: 16px; color: #50575e;">
3758 + Unlock AI-powered smart search, spam protection, content moderation, topic summarization, topic suggestions, translation, and more...
3759 + </p>
3760 + <style>
3761 + .button.button-primary.wpforo-ai-get-started{background-color: #43a6df; border-color: #43a6df;}
3762 + .button.button-primary.wpforo-ai-get-started:hover{background-color: #178ac6; border-color: #178ac6;}
3763 + </style>
3764 + <p style="margin: 5px 0 0;">
3765 + <a href="<?php echo esc_url( $ai_url ); ?>" class="button button-primary wpforo-ai-get-started" style=" padding: 0 20px !important; min-height: 10px; height: 34px; line-height: 31px;">Get Started</a>
3766 + </p>
3767 + </div>
3768 + </div>
3769 + <script>
3770 + jQuery(document).on('click', '.wpforo-ai-pricing-notice .notice-dismiss', function() {
3771 + jQuery.post(ajaxurl, { action: 'wpforo_dismiss_ai_pricing_promo' });
3772 + });
3773 + </script>
3774 + <?php
3775 +} );
3776 +
3777 +add_action( 'wp_ajax_wpforo_dismiss_ai_pricing_promo', function() {
3778 + update_option( 'wpforo_ai_pricing_promo_dismissed', '2026-08' );
3779 + wp_die();
3780 +} );