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
wpforo / includes / hooks.php

hooks.php in wpForo Forum 3.1.6, at includes/hooks.php

3,781 lines 140.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // Exit if accessed directly
3 if( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 add_filter( 'plugin_action_links_' . WPFORO_BASENAME, function( $links ) {
8 $uninstall_url = wp_nonce_url( admin_url( 'plugins.php?wpfaction=uninstall' ), 'wpforo_uninstall' );
9 $links[] = '<a href="' . esc_url(
10 (string) $uninstall_url
11 ) . '" class="wpforo-uninstall" style="color:#a00;" onclick="return confirm(\'' . __(
12 'IMPORTANT! Uninstall is not a simple deactivation action. This action will permanently remove all forum data (forums, topics, replies, attachments...) from database. Please backup database before this action, you may need this forum data in future. If you are sure that you want to delete all forum data please confirm. If not, just cancel it, then you can deactivate this plugin, that will not remove forum data.',
13 'wpforo'
14 ) . '\')">' . __( 'Uninstall', 'wpforo' ) . '</a>';
15 $settings_link = '<a href="' . esc_url( (string) admin_url( 'admin.php?page=wpforo-boards' ) ) . '">' . __(
16 'Boards',
17 'wpforo'
18 ) . '</a>';
19 array_unshift( $links, $settings_link );
20
21 return $links;
22 } );
23
24 function wpforo_notice_show() {
25 WPF()->notice->show();
26 }
27
28 add_action( 'wp_footer', 'wpforo_notice_show' );
29
30 add_action( 'show_admin_bar', function( $show_admin_bar ) {
31 if( ! is_super_admin() && is_user_logged_in() && WPF()->wp_current_user && ! array_intersect(
32 [ 'editor', 'administrator', 'author', ],
33 (array) WPF()->wp_current_user->roles
34 ) ) {
35 $show_admin_bar = (bool) array_intersect(
36 (array) WPF()->current_user_groupids,
37 (array) wpforo_setting( 'general', 'admin_bar' )
38 );
39 }
40
41 return $show_admin_bar;
42 } );
43
44 add_action( 'admin_notices', function() {
45 if( strpos( wpforo_get_request_uri(), 'nav-menus.php' ) !== false ) {
46 $message = '';
47 foreach(
48 WPF()->tpl->menu as $key => $value
49 ) {
50 $message .= "<tr><td> " . $value['label'] . ": </td><td> /%$key%/ </td></tr>";
51 }
52 $message .= "<tr><td> " . wpforo_phrase( 'register', false ) . ": </td><td> /%wpforo-register%/ </td></tr>
53 <tr><td> " . wpforo_phrase( 'login', false ) . ": </td><td> /%wpforo-login%/ </td></tr>";
54 printf(
55 '<div class="notice notice-warning wpforo-menu-shortcodes">
56 <p class="wpforo-menu-shortcodes-head" style="cursor: pointer;">wpForo Menu Shortcodes hint <span class="dashicons dashicons-arrow-down-alt2"></span></p>
57 <div class="wpforo-menu-shortcodes-body" style="display: none;">
58 <hr/><table>%1$s</table>
59 </div>
60 </div>',
61 $message
62 );
63 ?>
64 <script>
65 jQuery(document).ready(function ($) {
66 $('body').on('click', '.wpforo-menu-shortcodes .wpforo-menu-shortcodes-head', {}, function () {
67 var $this = $(this);
68 var wrap = $this.closest('.wpforo-menu-shortcodes');
69 var body = $('.wpforo-menu-shortcodes-body', wrap);
70 body.toggle();
71 });
72 });
73 </script>
74 <?php
75 }
76 } );
77
78 add_filter( 'comments_open', function( $open ) {
79 if( is_wpforo_page() ) $open = false;
80
81 return $open;
82 } );
83
84 add_filter( 'comments_array', function( $comments ) {
85 if( is_wpforo_page() ) $comments = [];
86
87 return $comments;
88 }, 10, 2 );
89
90 add_action( 'wpforo_actions_end', function() {
91 if( is_wpforo_page() ) {
92 remove_post_type_support( 'post', 'comments' );
93 remove_post_type_support( 'page', 'comments' );
94 }
95 }, 100 );
96
97 add_action( 'wpforo_actions_end', function() {
98 if( ! WPF()->board->get_current( 'is_standalone' ) && ! is_front_page() && ! is_home() && is_wpforo_url() ) {
99 global $wp_query, $post;
100 $pageid = WPF()->board->get_current( 'pageid' );
101 if( is_a( $post, 'WP_Post' ) && $post->ID !== $pageid ) {
102 $args = [
103 'post_count' => 1,
104 'found_posts' => 1,
105 'max_num_pages' => 0,
106 'is_404' => false,
107 'is_page' => true,
108 'is_singular' => true,
109 ];
110
111 $target_post = get_post( $pageid );
112 if( ! is_a( $target_post, 'WP_Post' ) ) {
113 return;
114 }
115
116 $post = $target_post;
117
118 $wp_query->posts = [ $post ];
119 $wp_query->queried_object_id = $post->ID;
120 $wp_query->queried_object = $post;
121
122 foreach( $args as $key => $value ) {
123 $wp_query->$key = $value;
124 }
125
126 setup_postdata( $post );
127 }
128 }
129 } );
130
131 add_filter( 'author_link', function( $link, $author_id ) {
132 if( wpforo_setting( 'profiles', 'profile' ) === 'wpforo' ) return WPF()->member->get_profile_url( $author_id );
133
134 return $link;
135 }, 10, 2 );
136
137 add_filter( 'get_comment_author_url', function( $link, $ID = 0, $object = null ) {
138 if( isset( $object->user_id ) && $object->user_id && wpforo_setting(
139 'profiles',
140 'profile'
141 ) === 'wpforo' ) {
142 return WPF()->member->get_profile_url(
143 $object->user_id
144 );
145 }
146
147 return $link;
148 }, 10, 3 );
149
150 add_filter( 'register_url', function( $register_url ) {
151 if( wpforo_setting( 'authorization', 'use_our_register_url' ) ) $register_url = wpforo_register_url();
152
153 return $register_url;
154 } );
155
156 add_filter( 'login_url', function( $login_url ) {
157 if( wpforo_setting( 'authorization', 'use_our_login_url' ) && strpos(
158 (string) $_SERVER['REQUEST_URI'],
159 '/wp-json/'
160 ) !== 0 && strpos(
161 (string) wp_debug_backtrace_summary(),
162 'wp_auth_check_html, wp_login_url,'
163 ) === false ) {
164 $login_url = wpforo_login_url();
165 }
166
167 return $login_url;
168 } );
169
170 add_filter( 'logout_url', function( $logout_url ) {
171 if( wpforo_setting( 'authorization', 'use_our_login_url' ) ) $logout_url = wpforo_logout_url();
172
173 return $logout_url;
174 } );
175
176 add_filter( 'pre_trash_post', function( $check, $post ) {
177 if( in_array( $post->ID, WPF()->board->get_boards_pageids(), true ) ) {
178 $check = false;
179 WPF()->notice->add( 'DO NOT DELETE WPFORO PAGE!!!', 'error' );
180 }
181
182 return $check;
183 }, 10, 2 );
184
185 add_filter( 'wp_dropdown_pages', function( $output, $r ) {
186 $pageids = WPF()->board->get_boards_pageids();
187 if( $r['name'] === 'page_for_posts' || ( $r['name'] === 'page_on_front' && wpforo_get_shortcode_pageid(
188 $pageids
189 ) ) ) {
190 $pattern = '#[\r\n\t\s]*<option[^<>]*?value=[\'"](' . implode(
191 '|',
192 $pageids
193 ) . ')[\'"][^<>]*?>[^<>]*?</option>#isu';
194 $output = preg_replace( $pattern, '', (string) $output );
195 }
196
197 return $output;
198 }, 10, 2 );
199
200 add_filter( 'pre_update_option', function( $value, $option, $old_value ) {
201 if( $option === 'page_on_front' ) {
202 $pageids = WPF()->board->get_boards_pageids();
203 if( in_array( wpforo_bigintval( $value ), $pageids, true ) ) {
204 if( ! $page_id = wpforo_get_shortcode_pageid( $pageids ) ) {
205 $page_id = wp_insert_post(
206 [
207 'post_date' => current_time( 'mysql', 1 ),
208 'post_date_gmt' => current_time( 'mysql', 1 ),
209 'post_content' => '[wpforo]',
210 'post_title' => 'Forum page_on_front',
211 'post_status' => 'publish',
212 'comment_status' => 'close',
213 'ping_status' => 'close',
214 'post_name' => 'front-community',
215 'post_modified' => current_time( 'mysql', 1 ),
216 'post_modified_gmt' => current_time( 'mysql', 1 ),
217 'post_parent' => 0,
218 'menu_order' => 0,
219 'post_type' => 'page',
220 ]
221 );
222 }
223 $value = ( $page_id && ! is_wp_error( $page_id ) ? $page_id : $old_value );
224 }
225 }
226
227 return $value;
228 }, 10, 3 );
229
230 function wpftpl_url( $filename ) {
231 $tpl_url = '';
232 if( $filename ) {
233 if( $tpl_url = locate_template( 'wpforo/' . $filename ) ) {
234 $tpl_url = get_template_directory_uri() . '/wpforo/' . $filename;
235 }
236 if( ! $tpl_url ) $tpl_url = WPF()->tpl->template_url . '/' . $filename;
237 }
238
239 return apply_filters( 'wpforo_wpftpl_url', $tpl_url, $filename );
240 }
241
242 function wpftpl( $filename ) {
243 $tpl = '';
244 if( $filename ) {
245 $tpl = locate_template( 'wpforo/' . $filename );
246 if( ! $tpl ) $tpl = WPF()->tpl->template_dir . '/' . $filename;
247 }
248
249 return apply_filters( 'wpforo_wpftpl', $tpl, $filename );
250 }
251
252 add_shortcode( 'wpforo', function( $atts ) {
253 wpforo_include_once_theme_functions();
254 if( defined( 'REST_REQUEST' ) && REST_REQUEST ) return '';
255 if( ! is_wpforo_url() ) {
256 if( ! $atts ) $atts = [ 'item' => 'forum' ];
257 WPF()->init_current_url( $atts );
258 WPF()->init_current_object();
259 wpforo_frontend_enqueue_scripts();
260 }
261 if( apply_filters( 'on_wpforo_load_remove_the_content_all_filters', false ) ) {
262 remove_all_filters( 'the_content' );
263 }
264
265 ob_start();
266 if( wpforo_current_user_is( 'admin' ) || ! wpforo_setting( 'board', 'under_construction' ) ) {
267 include( wpftpl( 'index.php' ) );
268 } else {
269 include( wpftpl( 'uc.php' ) );
270 }
271 $output = ob_get_clean();
272 $output = trim( (string) $output );
273 if( ! $output ) $output = wpforo_hook_usage( 'the_content' );
274
275 return $output;
276 } );
277
278 function wpforo_hook_usage( $hook = '' ) {
279 global $wp_filter;
280
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 Please deactivate all plugins, delete all caches and test again.
283 Then activate all plugins back one by one and find the conflict maker plugin.</div>
284 <pre style="display: none;">' . ( empty( $hook ) || ! isset( $wp_filter[ $hook ] ) ? 'No hook usage' : print_r(
285 $wp_filter[ $hook ],
286 true
287 ) ) . '</pre>';
288
289 return $output;
290 }
291
292 add_action( 'wpforo_actions_end', 'wpforo_set_header_status' );
293 function wpforo_set_header_status() {
294 if( is_wpforo_page() ) {
295 global $wp_query;
296
297 $status = ( WPF()->current_object['is_404'] ? 404 : 200 );
298 status_header( $status );
299 if( $status === 404 ) nocache_headers();
300 $wp_query->is_404 = false;
301 }
302 }
303
304 add_filter( 'pre_handle_404', function( $bypass ) {
305 if( is_wpforo_page() ) return true;
306
307 return $bypass;
308 } );
309
310 add_action( 'wpforo_actions_end', function() {
311 if( is_wpforo_page() && WPF()->board->get_current( 'is_standalone' ) ) {
312 add_rewrite_rule( '(.*)', 'index.php?page_id=' . WPF()->board->get_current( 'pageid' ), 'top' );
313 add_filter( 'template_include', function( $template ) {
314 if( is_wpforo_page() && ! is_wpforo_shortcode_page() && ( $wpforo_template = wpftpl(
315 'index.php'
316 ) ) ) {
317 return $wpforo_template;
318 }
319
320 return $template;
321 } );
322 }
323 } );
324
325 add_filter( 'rewrite_rules_array', function( $rules ) {
326 $rewrite_prefix = '(?:([a-z]{2,3})/)?';
327 if( function_exists( 'pll_languages_list' ) ) {
328 $rewrite_prefix = '(?:(' . implode(
329 '|',
330 pll_languages_list()
331 ) . ')/)?';
332 }
333
334 if( ! WPF()->board->get_current( 'is_standalone' ) ) {
335 $pageid = 0;
336 $boards = WPF()->board->get_boards( [ 'status' => true ] );
337 foreach( $boards as $board ) {
338 if( $board['boardid'] === 0 ) $pageid = $board['pageid'];
339 $route = urldecode( (string) $board['slug'] );
340 $route = preg_replace( '#^/?index\.php/?#iu', '', $route );
341 $route = trim( (string) $route, '/' );
342 $pattern = $rewrite_prefix . preg_quote( $route ) . '(?:/|$).*$';
343 $to_url = 'index.php?lang=$matches[1]&page_id=' . $board['pageid'];
344 if( ! array_key_exists( $pattern, $rules ) ) $rules = array_merge( [ $pattern => $to_url ], $rules );
345 }
346
347 if( ! $pageid ) $pageid = wpforo_get_option( 'wpforo_pageid', 0 );
348 foreach( WPF()->board->routes as $route ) {
349 // $route = utf8_uri_encode( urldecode( (string) $route ) );
350 $route = urldecode( (string) $route );
351 $route = preg_replace( '#^/?index\.php/?#isu', '', $route );
352 $route = trim( (string) $route, '/' );
353 $pattern = $rewrite_prefix . preg_quote( $route ) . '(?:/|$).*$';
354 $to_url = 'index.php?lang=$matches[1]&page_id=' . $pageid;
355 if( ! array_key_exists( $pattern, $rules ) ) $rules = array_merge( [ $pattern => $to_url ], $rules );
356 }
357 }
358
359 return $rules;
360 } );
361
362 add_action( 'wpforo_actions_end', function() {
363 $pageid = WPF()->board->get_current( 'pageid' );
364 if( is_wpforo_url() && $pageid && WPF()->board->route && get_the_ID() !== $pageid ) {
365 wpforo_repair_main_shortcode_page();
366 }
367 } );
368
369
370 function wpforo_include_once_theme_functions() {
371 $path = wpftpl( 'functions.php' );
372 if( file_exists( $path ) ) include_once( $path );
373
374 $path = wpftpl( 'functions-wp.php' );
375 if( file_exists( $path ) ) include_once( $path );
376 }
377
378 add_action( 'wpforo_after_init', 'wpforo_include_once_theme_functions' );
379
380 function wpforo_meta_title( $title ) {
381 $meta_title = [];
382
383 if( ! wpforo_setting( 'seo', 'seo_title' ) ) return $title;
384
385 if( is_wpforo_page() ) {
386 $template = WPF()->current_object['template'];
387 if( ! WPF()->current_object['is_404'] ) {
388 $paged = ( WPF()->current_object['paged'] > 1 ) ? wpforo_phrase( 'page', false ) . ' ' . WPF()->current_object['paged'] . ' ' : '';
389 if( ! empty( WPF()->current_object['forum'] ) ) {
390 $forum = WPF()->current_object['forum'];
391 }
392 if( ! empty( WPF()->current_object['topic'] ) ) {
393 $topic = WPF()->current_object['topic'];
394 }
395 if( ! empty( WPF()->current_object['user'] ) ) {
396 $user = WPF()->current_object['user'];
397 }
398 if( isset( $topic['title'] ) && isset( $forum['title'] ) && isset(
399 WPF()->board->get_current(
400 'settings'
401 )['title']
402 ) ) {
403 $meta_title = [
404 $topic['title'],
405 $paged,
406 $forum['title'],
407 WPF()->board->get_current( 'settings' )['title'],
408 ];
409 $meta_title = apply_filters( 'wpforo_seo_topic_title', $meta_title );
410 } elseif( ! isset( $topic['title'] ) && isset( $forum['title'] ) && isset(
411 WPF()->board->get_current(
412 'settings'
413 )['title']
414 ) ) {
415 $meta_title = [ $forum['title'], $paged, WPF()->board->get_current( 'settings' )['title'] ];
416 $meta_title = apply_filters( 'wpforo_seo_forum_title', $meta_title );
417 } elseif( ! in_array( $template, [ 'forum', 'topic', 'post' ], true ) ) {
418 if( wpforo_is_member_template( $template ) ) {
419 if( isset( $user['display_name'] ) ) {
420 $meta_title = [
421 $user['display_name'],
422 wpforo_phrase( ucfirst( (string) $template ), false ),
423 WPF()->board->get_current( 'settings' )['title'],
424 $paged,
425 ];
426 } elseif( isset( WPF()->current_object['user_nicename'] ) ) {
427 $meta_title = [
428 WPF()->current_object['user_nicename'],
429 wpforo_phrase( ucfirst( (string) $template ), false ),
430 WPF()->board->get_current( 'settings' )['title'],
431 $paged,
432 ];
433 } else {
434 $meta_title = [
435 wpforo_phrase( 'Member', false ),
436 wpforo_phrase( ucfirst( (string) $template ), false ),
437 WPF()->board->get_current( 'settings' )['title'],
438 $paged,
439 ];
440 }
441 $meta_title = apply_filters( 'wpforo_seo_profile_title', $meta_title );
442 } elseif( $template === 'recent' ) {
443 $wpfpaged = ( isset( $_GET['wpfpaged'] ) && $_GET['wpfpaged'] > 1 ) ? ' - ' . wpforo_phrase(
444 'page',
445 false
446 ) . ' ' . $_GET['wpfpaged'] . ' ' : '';
447 $view = wpfval( $_GET, 'view' );
448 if( $view == 'unread' ) {
449 $main_title = wpforo_phrase( 'Unread Posts', false );
450 } elseif( $view == 'prefix' ) {
451 $main_title = wpforo_phrase( 'Topic Prefix', false );
452 } else {
453 $main_title = wpforo_phrase( 'Recent Posts', false );
454 }
455 $meta_title = [ $main_title . $wpfpaged, WPF()->board->get_current( 'settings' )['title'] ];
456 $meta_title = apply_filters( 'wpforo_seo_recent_posts_title', $meta_title );
457 } elseif( $template === 'tags' ) {
458 $wpfpaged = ( isset( $_GET['wpfpaged'] ) && $_GET['wpfpaged'] > 1 ) ? ' - ' . wpforo_phrase(
459 'page',
460 false
461 ) . ' ' . $_GET['wpfpaged'] . ' ' : '';
462 $meta_title = [
463 wpforo_phrase( 'Tags', false ) . $wpfpaged,
464 WPF()->board->get_current( 'settings' )['title'],
465 ];
466 $meta_title = apply_filters( 'wpforo_seo_tags_title', $meta_title );
467 } elseif( $template === 'search' && wpfval( $_GET, 'wpfin' ) && wpfval(
468 $_GET,
469 'wpfs'
470 ) && $_GET['wpfin'] === 'tag' ) {
471 $wpfpaged = ( isset( $_GET['wpfpaged'] ) && $_GET['wpfpaged'] > 1 ) ? ' - ' . wpforo_phrase(
472 'page',
473 false
474 ) . ' ' . $_GET['wpfpaged'] . ' ' : '';
475 $meta_title = [
476 wpforo_phrase( 'Topic Tag:', false ) . ' ' . esc_html( $_GET['wpfs'] ),
477 $wpfpaged,
478 WPF()->board->get_current( 'settings' )['title'],
479 ];
480 $meta_title = apply_filters( 'wpforo_seo_tag_title', $meta_title );
481 } elseif( $template ) {
482 $wpfpaged = ( isset( $_GET['wpfpaged'] ) && $_GET['wpfpaged'] > 1 ) ? ' - ' . wpforo_phrase(
483 'page',
484 false
485 ) . ' ' . $_GET['wpfpaged'] . ' ' : '';
486 $meta_title = [
487 wpforo_phrase( ucfirst( (string) $template ), false ) . $wpfpaged,
488 WPF()->board->get_current( 'settings' )['title'],
489 ];
490 $meta_title = apply_filters( 'wpforo_seo_template_title', $meta_title );
491 } elseif( $title ) {
492 $meta_title = ( is_array( $title ) ) ? $title : [ $title ];
493 $meta_title = apply_filters( 'wpforo_seo_x_title', $meta_title );
494 } else {
495 $meta_title = [ wpforo_phrase( 'Forum', false ), get_bloginfo( 'name' ) ];
496 $meta_title = apply_filters( 'wpforo_seo_general_title', $meta_title );
497 }
498 } elseif( isset( WPF()->board->get_current( 'settings' )['title'] ) && WPF()->board->get_current(
499 'settings'
500 )['title'] ) {
501 $meta_title = [ WPF()->board->get_current( 'settings' )['title'], get_bloginfo( 'name' ) ];
502 $meta_title = apply_filters( 'wpforo_seo_main_title', $meta_title );
503 } elseif( $title ) {
504 $meta_title = ( is_array( $title ) ) ? $title : [ $title ];
505 $meta_title = apply_filters( 'wpforo_seo_x_title', $meta_title );
506 } else {
507 $meta_title = [ wpforo_phrase( 'Forum', false ), get_bloginfo( 'name' ) ];
508 $meta_title = apply_filters( 'wpforo_seo_general_title', $meta_title );
509 }
510 } else {
511 $meta_title = [
512 wpforo_phrase( '404 - Page not found', false ),
513 WPF()->board->get_current( 'settings' )['title'],
514 ];
515 $meta_title = apply_filters( 'wpforo_seo_404_title', $meta_title );
516 }
517 }
518 if( ! empty( $meta_title ) ) {
519 return $meta_title;
520 } else {
521 return $title;
522 }
523 }
524
525 add_filter( 'document_title_parts', 'wpforo_meta_title', 100 );
526
527 function wpforo_meta_wp_title( $title ) {
528 if( ! wpforo_setting( 'seo', 'seo_title' ) ) return $title;
529 $meta_title = wpforo_meta_title( $title );
530 if( is_array( $meta_title ) && ! empty( $meta_title ) ) {
531 $title = implode( ' &#8211; ', $meta_title );
532 }
533
534 return $title;
535 }
536
537 add_filter( 'wp_title', 'wpforo_meta_wp_title', 100 );
538
539 function wpforo_add_meta_tags() {
540 if( ! wpforo_setting( 'seo', 'seo_meta' ) ) {
541 return;
542 }
543
544 if( is_wpforo_page() && ! is_wpforo_shortcode_page() ) {
545 $title = '';
546 $og_img = '';
547 $tw_img = '';
548 $schema = '';
549 $noindex = '';
550 $template = '';
551 $description = '';
552 $udata = [];
553 if( preg_match( '#\?.*$#is', (string) WPF()->canonical_url, $requests ) ) {
554 if( strpos( (string) $requests[0], 'orderby' ) !== false ) {
555 $canonical = preg_replace( '#\?.*$#is', '', (string) WPF()->canonical_url );
556 } else {
557 $canonical = wpforo_home_url( $requests[0] );
558 }
559 } else {
560 $canonical = WPF()->canonical_url;
561 }
562 $noindex_urls = wpforo_setting( 'seo', 'noindex' );
563 $image = '';
564 foreach( $noindex_urls as $noindex_url ) {
565 $noindex_url = strtok( $noindex_url, "#" );
566 if( strpos( (string) $noindex_url, '*' ) !== false ) {
567 $noindex_url = strtok( $noindex_url, "*" );
568 if( preg_match( '|^' . preg_quote( $noindex_url ) . '|is', (string) $canonical ) ) {
569 $noindex = "<meta name=\"robots\" content=\"noindex\">\r\n";
570 break;
571 }
572 } elseif( $canonical == $noindex_url ) {
573 $noindex = "<meta name=\"robots\" content=\"noindex\">\r\n";
574 break;
575 }
576 }
577 $paged = ( WPF()->current_object['paged'] > 1 ) ? wpforo_phrase( 'page', false ) . ' ' . WPF()->current_object['paged'] . ' | ' : '';
578 if( isset( WPF()->current_object['template'] ) ) {
579 $template = WPF()->current_object['template'];
580 }
581 if( ! empty( WPF()->current_object['forum'] ) ) {
582 $forum = WPF()->current_object['forum'];
583 }
584 if( ! empty( WPF()->current_object['topic'] ) ) {
585 $topic = WPF()->current_object['topic'];
586 }
587 if( ! empty( WPF()->current_object['user'] ) ) {
588 $user = WPF()->current_object['user'];
589 }
590 if( isset( WPF()->current_object ) ) {
591 if( wpfval( WPF()->current_object, 'forumid' ) && ! wpfval( WPF()->current_object, 'topicid' ) ) {
592 if( isset( $forum['title'] ) ) {
593 $title = $forum['title'];
594 }
595 if( isset( WPF()->current_object['forum_meta_desc'] ) && WPF()->current_object['forum_meta_desc'] != '' ) {
596 $description = $paged . WPF()->current_object['forum_meta_desc'];
597 } elseif( isset( WPF()->current_object['forum_desc'] ) && WPF()->current_object['forum_desc'] != '' ) {
598 $description = $paged . WPF()->current_object['forum_desc'];
599 }
600 } elseif( isset( WPF()->current_object['topicid'] ) && isset( $topic['first_postid'] ) ) {
601 $post = WPF()->post->get_post( $topic['first_postid'] );
602 $content = wpforo_content( $post, false );
603 $image = wpforo_find_image_urls( $content, true, 'og:image' );
604 if( isset( $post['title'] ) ) {
605 $title = wpforo_text( $paged . $post['title'], 60, false );
606 }
607 if( isset( $post['body'] ) ) {
608 $description = wpforo_text( $paged . $post['body'], 150, false );
609 }
610 if( isset( $post['body'] ) ) {
611 $schema = wpforo_schema( $forum, $topic, $post );
612 }
613 } elseif( wpforo_is_member_template( $template ) ) {
614 if( isset( WPF()->board->get_current( 'settings' )['title'] ) ) {
615 $title = $paged . WPF()->board->get_current( 'settings' )['title'];
616 }
617 $udata['name'] = ( isset( $user['display_name'] ) && $user['display_name'] ) ? wpforo_phrase(
618 'User',
619 false
620 ) . ': ' . $user['display_name'] : '';
621 $udata['title'] = ( isset( $user['rating']['title'] ) && $user['rating']['title'] ) ? wpforo_phrase(
622 'Title',
623 false
624 ) . ': ' . $user['rating']['title'] : '';
625 $udata['about'] = ( isset( $user['about'] ) && $user['about'] ) ? wpforo_phrase(
626 'About',
627 false
628 ) . ': ' . wpforo_text(
629 $user['about'],
630 150,
631 false
632 ) : '';
633 $description = $title . ' - ' . wpforo_phrase( 'Member Profile', false ) . ' &gt; ' . wpforo_phrase(
634 ucfirst( (string) $template ),
635 false
636 ) . ' ' . wpforo_phrase( 'Page', false ) . '. ' . implode( ', ', $udata );
637 if( ! wpforo_setting( 'seo', 'seo_profile' ) ) {
638 $noindex = "<meta name=\"robots\" content=\"noindex\">\r\n";
639 }
640 } elseif( isset( WPF()->current_object['template'] ) && WPF()->current_object['template'] === 'member' ) {
641 $wpfpaged = ( isset( $_GET['wpfpaged'] ) && $_GET['wpfpaged'] > 1 ) ? wpforo_phrase(
642 'Page',
643 false
644 ) . ' ' . $_GET['wpfpaged'] . ' | ' : '';
645 $description = $wpfpaged . wpforo_phrase( 'Forum Members List', false );
646 } elseif( isset( WPF()->current_object['template'] ) && WPF()->current_object['template'] === 'recent' ) {
647 $wpfpaged = ( isset( $_GET['wpfpaged'] ) && $_GET['wpfpaged'] > 1 ) ? wpforo_phrase(
648 'Page',
649 false
650 ) . ' ' . $_GET['wpfpaged'] . ' | ' : '';
651 $description = $wpfpaged . wpforo_phrase( 'Recent Posts', false );
652 } elseif( isset( WPF()->current_object['template'] ) && WPF()->current_object['template'] === 'tags' ) {
653 $wpfpaged = ( isset( $_GET['wpfpaged'] ) && $_GET['wpfpaged'] > 1 ) ? wpforo_phrase(
654 'Page',
655 false
656 ) . ' ' . $_GET['wpfpaged'] . ' | ' : '';
657 $description = $wpfpaged . wpforo_phrase( 'Tags', false );
658 } else {
659 if( isset( WPF()->board->get_current( 'settings' )['title'] ) ) {
660 $title = $paged . WPF()->board->get_current( 'settings' )['title'];
661 }
662 if( isset( WPF()->board->get_current( 'settings' )['desc'] ) ) {
663 $description = $paged . WPF()->board->get_current( 'settings' )['desc'];
664 }
665 if( isset( WPF()->current_object['template'] ) && ( WPF()->current_object['template'] == 'login' || WPF()->current_object['template'] == 'register' ) ) {
666 $noindex = "<meta name=\"robots\" content=\"noindex\">\r\n";
667 }
668 }
669 $description = strip_tags( str_replace( '>', '> ', $description ) );
670 $description = preg_replace( '#[\t\r\n]+#iu', ' ', $description );
671 if( $image ) {
672 $og_img = '<meta property="og:image" content="' . $image . '" />' . "\r\n";
673 $tw_img = '<meta property="twitter:image" content="' . $image . '" />' . "\r\n";
674 }
675 $meta_tags = "\r\n<!-- wpForo SEO -->\r\n" . $noindex . "<link rel=\"canonical\" href=\"" . $canonical . "\" />\r\n<meta name=\"description\" content=\"" . esc_html(
676 $description
677 ) . "\" />\r\n<meta property=\"og:title\" content=\"" . esc_html(
678 $title
679 ) . "\" />\r\n<meta property=\"og:description\" content=\"" . esc_html(
680 $description
681 ) . "\" />\r\n<meta property=\"og:url\" content=\"" . $canonical . "\" />\r\n<meta property=\"og:locale\" content=\"" . WPF(
682 )->locale . "\" />\r\n" . $og_img . "<meta property=\"og:site_name\" content=\"" . get_bloginfo(
683 'name'
684 ) . "\" />\r\n<meta property=\"og:type\" content=\"website\" />\r\n<meta name=\"twitter:description\" content=\"" . esc_html(
685 $description
686 ) . "\"/>\r\n<meta name=\"twitter:title\" content=\"" . esc_html(
687 $title
688 ) . "\" />\r\n<meta property=\"twitter:card\" content=\"summary_large_image\" />\r\n" . $tw_img . "<!-- wpForo SEO End -->\r\n\r\n";
689 $schema = "<!-- wpForo Schema -->" . $schema . "\r\n<!-- wpForo Schema End -->\r\n\r\n";
690 echo apply_filters( 'wpforo_seo_meta_tags', $meta_tags . $schema );
691 }
692 }
693 }
694
695 add_action( 'wp_head', 'wpforo_add_meta_tags', 1 );
696
697 add_action( 'wp_ajax_wpforo_answer_ajax', 'wpf_answer' );
698 function wpf_answer() {
699 wpforo_verify_nonce( 'wpforo_answer_ajax' );
700 $response = [ 'notice' => WPF()->notice->get_notices() ];
701 if( ! is_user_logged_in() ) {
702 WPF()->notice->add( wpforo_get_login_or_register_notice_text() );
703 $response['notice'] = WPF()->notice->get_notices();
704 wp_send_json_error( $response );
705 }
706 if( ! isset( $_POST['answerstatus'] ) || ! isset( $_POST['postid'] ) || ! $postid = intval( $_POST['postid'] ) ) {
707 WPF()->notice->add( 'action error', 'error' );
708 $response['notice'] = WPF()->notice->get_notices();
709 wp_send_json_error( $response );
710 }
711 if( ! $post = WPF()->post->get_post( $postid ) ) {
712 WPF()->notice->add( 'post not found', 'error' );
713 $response['notice'] = WPF()->notice->get_notices();
714 wp_send_json_error( $response );
715 }
716 if( ! $topic = WPF()->topic->get_topic( $post['topicid'] ) ) {
717 WPF()->notice->add( 'topic not found', 'error' );
718 $response['notice'] = WPF()->notice->get_notices();
719 wp_send_json_error( $response );
720 }
721 if( ! ( WPF()->perm->forum_can( 'at', $post['forumid'] ) || ( WPF()->perm->forum_can(
722 'oat',
723 $post['forumid']
724 ) && WPF()->current_userid == $topic['userid'] ) ) ) {
725 WPF()->notice->add( 'You don\'t have permission to make topic answered', 'error' );
726 $response['notice'] = WPF()->notice->get_notices();
727 wp_send_json_error( $response );
728 }
729 if( intval( $_POST['answerstatus'] ) && WPF()->topic->has_is_answer_post( $topic['topicid'] ) ) {
730 WPF()->notice->add( 'You don\'t have permission to make two best answers for one topic', 'error' );
731 $response['notice'] = WPF()->notice->get_notices();
732 wp_send_json_error( $response );
733 }
734 if( false !== WPF()->db->query(
735 "UPDATE " . WPF()->tables->posts . " SET is_answer = " . intval(
736 $_POST['answerstatus']
737 ) . " WHERE postid = " . intval( $postid )
738 ) ) {
739 wpforo_clean_cache( 'post', $postid, $post );
740 WPF()->db->query(
741 "UPDATE " . WPF()->tables->topics . " SET `solved` = " . intval(
742 $_POST['answerstatus']
743 ) . " WHERE `topicid` = " . intval( $post['topicid'] )
744 );
745 do_action( 'wpforo_answer', intval( $_POST['answerstatus'] ), $post );
746 WPF()->notice->add( 'done', 'success' );
747 $response['notice'] = WPF()->notice->get_notices();
748 wp_send_json_success( $response );
749 }
750 wp_send_json_error( $response );
751 }
752
753 add_action( 'wp_ajax_wpforo_quote_ajax', 'wpf_quote' );
754 add_action( 'wp_ajax_nopriv_wpforo_quote_ajax', 'wpf_quote' );
755 function wpf_quote() {
756 wpforo_verify_nonce( 'wpforo_quote_ajax' );
757 if( ! $current_topicid = wpforo_bigintval( WPF()->current_object['topicid'] ) ) {
758 exit();
759 }
760 $post = WPF()->db->get_row(
761 'SELECT * FROM ' . WPF()->tables->posts . ' WHERE topicid = ' . $current_topicid . ' AND postid =' . wpforo_bigintval(
762 $_POST['postid']
763 ),
764 ARRAY_A
765 );
766 if( ! ( WPF()->perm->forum_can( 'cr', $post['forumid'] ) || ( wpforo_is_owner(
767 wpfval(
768 WPF()->current_object['topic'],
769 'userid'
770 ),
771 wpfval( WPF()->current_object['topic'], 'email' )
772 ) && WPF()->perm->forum_can(
773 'ocr',
774 $post['forumid']
775 ) ) ) ) {
776 return;
777 }
778 $post = apply_filters( 'wpforo_quote_post_ajax', $post );
779 $response = sprintf(
780 '[quote data-userid="%1$d" data-postid="%2$d"]%3$s[/quote]<p></p>',
781 $post['userid'],
782 $post['postid'],
783 wpautop( $post['body'] )
784 );
785 wp_send_json_success( $response );
786 }
787
788 add_action( 'wp_ajax_wpforo_report_ajax', 'wpf_report' );
789 function wpf_report() {
790 wpforo_verify_nonce( 'wpforo_report_ajax' );
791 if( ! is_user_logged_in() ) {
792 WPF()->notice->add( wpforo_get_login_or_register_notice_text() );
793 wp_send_json_error( WPF()->notice->get_notices() );
794 }
795
796 if( ! isset( $_POST['reportmsg'] ) || ! $_POST['reportmsg'] || ! isset( $_POST['postid'] ) || ! $_POST['postid'] ) {
797 WPF()->notice->add( 'Error: please insert some text to report.', 'error' );
798 wp_send_json_error( WPF()->notice->get_notices() );
799 }
800
801 $postid = intval( $_POST['postid'] );
802 $forumid = wpforo_post( $postid, 'forumid' );
803
804 if( ! WPF()->perm->can_report( $forumid ) ) {
805 WPF()->notice->add( 'You are not allowed to report.', 'error' );
806 wp_send_json_error( WPF()->notice->get_notices() );
807 }
808
809 ############### Sending Email ##################
810 $report_text = substr( strip_tags( (string) $_POST['reportmsg'] ), 0, 1000 );
811 $reporter = '<a href="' . WPF()->current_user['profile_url'] . '">' . ( WPF()->current_user['display_name'] ? WPF()->current_user['display_name'] : urldecode(
812 (string) WPF()->current_user['user_nicename']
813 ) ) . '</a>';
814 $reportmsg = wpforo_kses( $report_text, 'email' );
815 $post_url = WPF()->post->get_url( $postid );
816
817 $subject = wpforo_setting( 'email', 'report_email_subject' );
818 $message = wpforo_setting( 'email', 'report_email_message' );
819
820 $from_tags = [ "[reporter]", "[message]", "[post_url]" ];
821 $to_words = [
822 sanitize_text_field( $reporter ),
823 $reportmsg,
824 '<a target="_blank" href="' . esc_url( (string) $post_url ) . '">' . esc_url( (string) $post_url ) . '</a>',
825 ];
826
827 $subject = stripslashes( strip_tags( str_replace( $from_tags, $to_words, (string) $subject ) ) );
828 $message = stripslashes( str_replace( $from_tags, $to_words, (string) $message ) );
829
830 $admin_emails = wpforo_setting( 'email', 'admin_emails' );
831 $admin_email = wpfval( $admin_emails, 0 );
832 $headers = wpforo_admin_mail_headers();
833
834 add_filter( 'wp_mail_content_type', 'wpforo_set_html_content_type', 999 );
835 if( ! @wp_mail( $admin_email, $subject, $message, $headers ) ) {
836 WPF()->notice->add( 'Can\'t send report email', 'error' );
837 wp_send_json_error( WPF()->notice->get_notices() );
838 }
839 remove_filter( 'wp_mail_content_type', 'wpforo_set_html_content_type' );
840
841 ############### Sending Email end ##############
842 do_action( 'wpforo_after_post_report', $postid, $forumid, $_POST['reportmsg'] );
843 WPF()->notice->add( 'Message has been sent', 'success' );
844 wp_send_json_success( WPF()->notice->get_notices() );
845 }
846
847 add_action( 'wp_ajax_wpforo_sticky_ajax', 'wpf_sticky' );
848 add_action( 'wp_ajax_nopriv_wpforo_sticky_ajax', 'wpf_sticky' );
849 function wpf_sticky() {
850 wpforo_verify_nonce( 'wpforo_sticky_ajax' );
851 WPF()->notice->add( 'wrong data', 'error' );
852 $response = [ 'notice' => WPF()->notice->get_notices() ];
853 if( ! $topicid = wpforo_bigintval( wpfval( $_POST, 'topicid' ) ) ) {
854 wp_send_json_error( $response );
855 }
856
857 $sql = "SELECT `forumid` FROM `" . WPF()->tables->topics . "` WHERE `topicid` = $topicid";
858 $forumid = WPF()->db->get_var( $sql );
859 if( ! WPF()->perm->forum_can( 's', $forumid ) ) {
860 WPF()->notice->add( 'You don\'t have permission to do this action from this forum', 'error' );
861 $response['notice'] = WPF()->notice->get_notices();
862 wp_send_json_error( $response );
863 }
864 $status = wpfval( $_POST, 'status' );
865 if( $status === 'sticky' ) {
866 $sql = "UPDATE `" . WPF()->tables->topics . "` SET `type` = 1 WHERE `topicid` = $topicid";
867 if( false !== WPF()->db->query( $sql ) ) {
868 wpforo_clean_cache( 'topic-first-post', $topicid );
869 WPF()->notice->add( 'Done!', 'success' );
870 $response['notice'] = WPF()->notice->get_notices();
871 wp_send_json_success( $response );
872 }
873 } elseif( $status === 'unsticky' ) {
874 $sql = "UPDATE `" . WPF()->tables->topics . "` SET `type` = 0 WHERE `topicid` = $topicid";
875 if( false !== WPF()->db->query( $sql ) ) {
876 wpforo_clean_cache( 'topic-first-post', $topicid );
877 WPF()->notice->add( 'Done!', 'success' );
878 $response['notice'] = WPF()->notice->get_notices();
879 wp_send_json_success( $response );
880 }
881 }
882 wp_send_json_error( $response );
883 }
884
885 add_action( 'wp_ajax_wpforo_private_ajax', 'wpf_private' );
886 function wpf_private() {
887 wpforo_verify_nonce( 'wpforo_private_ajax' );
888 if( ! WPF()->current_userid ) {
889 wp_send_json_error();
890 }
891 if( ! ( $topicid = wpforo_bigintval( wpfval( $_POST, 'topicid' ) ) ) ) {
892 wp_send_json_error();
893 }
894 if( ! ( $topic = WPF()->topic->get_topic( $topicid ) ) ) {
895 wp_send_json_error();
896 }
897 if( ! ( WPF()->perm->forum_can( 'p', $topic['forumid'] ) || ( WPF()->current_userid == $topic['userid'] && WPF()->perm->forum_can( 'op', $topic['forumid'] ) ) ) ) {
898 wp_send_json_error();
899 }
900 $status = wpfval( $_POST, 'status' );
901 if( $status === 'private' ) {
902 WPF()->topic->wprivate( $topicid, 1 );
903 wp_send_json_success();
904 } elseif( $status === 'public' ) {
905 WPF()->topic->wprivate( $topicid, 0 );
906 wp_send_json_success();
907 }
908 wp_send_json_error();
909 }
910
911 add_action( 'wp_ajax_wpforo_solved_ajax', 'wpf_solved' );
912 add_action( 'wp_ajax_nopriv_wpforo_solved_ajax', 'wpf_solved' );
913 function wpf_solved() {
914 wpforo_verify_nonce( 'wpforo_solved_ajax' );
915 if( ! WPF()->current_userid ) {
916 wp_send_json_error();
917 }
918 if( ! $postid = wpforo_bigintval( wpfval( $_POST, 'postid' ) ) ) {
919 wp_send_json_error();
920 }
921 if( $post = WPF()->post->get_post( $postid ) ) {
922 if( WPF()->perm->forum_can( 'sv', $post['forumid'] ) || ( WPF()->current_userid == $post['userid'] && WPF()->perm->forum_can( 'osv', $post['forumid'] ) ) ) {
923 $solved = ( wpfval( $_POST, 'status' ) === 'solved' ? 1 : 0 );
924 $sql = "UPDATE " . WPF()->tables->topics . " SET `solved` = %d WHERE `topicid` = %d";
925 WPF()->db->query( WPF()->db->prepare( $sql, $solved, $post['topicid'] ) );
926 wpforo_clean_cache( 'topic-first-post', $post['topicid'] );
927
928 // Fire action hook for topic solved/unsolved
929 $topic = WPF()->topic->get_topic( $post['topicid'] );
930 if( $topic ) {
931 if( $solved ) {
932 do_action( 'wpforo_topic_solved', $topic, $post );
933 } else {
934 do_action( 'wpforo_topic_unsolved', $topic, $post );
935 }
936 }
937
938 wp_send_json_success();
939 }
940 }
941 wp_send_json_error();
942 }
943
944 add_action( 'wp_ajax_wpforo_approve_ajax', 'wpf_approve' );
945 function wpf_approve() {
946 wpforo_verify_nonce( 'wpforo_approve_ajax' );
947 if( ! WPF()->current_userid ) {
948 WPF()->notice->add( wpforo_get_login_or_register_notice_text() );
949 wp_send_json_error();
950 }
951 if( ! $postid = wpforo_bigintval( wpfval( $_POST, 'postid' ) ) ) {
952 wp_send_json_error();
953 }
954 $post = WPF()->post->get_post( $postid );
955 if( ! $post || ! WPF()->perm->forum_can( 'au', $post['forumid'] ) ) {
956 wp_send_json_error();
957 }
958 $status = wpfval( $_POST, 'status' );
959 if( $status === 'approve' ) {
960 WPF()->moderation->post_approve( $postid );
961 wp_send_json_success();
962 } elseif( $status === 'unapprove' ) {
963 WPF()->moderation->post_unapprove( $postid );
964 wp_send_json_success();
965 }
966 wp_send_json_error();
967 }
968
969 add_action( 'wp_ajax_wpforo_close_ajax', 'wpf_close' );
970 function wpf_close() {
971 wpforo_verify_nonce( 'wpforo_close_ajax' );
972 if( ! WPF()->current_userid ) {
973 wp_send_json_error();
974 }
975
976 if( ! $topicid = wpforo_bigintval( wpfval( $_POST, 'topicid' ) ) ) {
977 wp_send_json_error();
978 }
979 $topic = WPF()->topic->get_topic( $topicid );
980 if( ! $topic || ! WPF()->perm->forum_can( 'cot', $topic['forumid'] ) ) {
981 wp_send_json_error();
982 }
983 $status = wpfval( $_POST, 'status' );
984 $topic = WPF()->topic->get_topic( $topicid );
985
986 if( $status === 'closed' ) {
987 WPF()->topic->open( $topicid );
988 if( $topic ) {
989 do_action( 'wpforo_topic_opened', $topic );
990 }
991 wp_send_json_success();
992 } elseif( $status === 'close' ) {
993 WPF()->topic->close( $topicid );
994 if( $topic ) {
995 do_action( 'wpforo_topic_closed', $topic );
996 }
997 wp_send_json_success();
998 }
999 wp_send_json_error();
1000 }
1001
1002 add_action( 'wp_ajax_wpforo_post_edit', 'wpforo_post_edit' );
1003 add_action( 'wp_ajax_nopriv_wpforo_post_edit', 'wpforo_post_edit' );
1004 function wpforo_post_edit() {
1005 wpforo_verify_nonce( 'wpforo_post_edit' );
1006 $r = [ 'html' => '' ];
1007 if( $postid = wpforo_bigintval( wpfval( $_POST, 'postid' ) ) ) {
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 }
1015 if( WPF()->perm->forum_can( 'eor', $post['forumid'] ) || WPF()->perm->forum_can(
1016 'eot',
1017 $post['forumid']
1018 ) ) {
1019 if( $topic = WPF()->topic->get_topic( $post['topicid'] ) ) {
1020 $postmetas = (array) WPF()->postmeta->get_postmeta( $postid, '', true );
1021 $post['body'] = htmlspecialchars( $post['body'], ENT_NOQUOTES );
1022 $values = array_merge( $post, $topic, $postmetas );
1023 $values = apply_filters( 'wpforo_edit_post_ajax', $values, $post, $topic, $postmetas );
1024 ob_start();
1025 if( intval( $post['is_first_post'] ) ) {
1026 WPF()->form->current['varname'] = 'thread';
1027 WPF()->tpl->topic_form( $post['forumid'], $values );
1028 } else {
1029 WPF()->form->current['varname'] = 'post';
1030 if( trim( (string) $post['title'] ) ) {
1031 $values['title'] = $post['title'];
1032 }
1033 WPF()->tpl->reply_form( $topic, $values );
1034 }
1035 $r['html'] = ob_get_clean();
1036 wp_send_json_success( $r );
1037 }
1038 }
1039 }
1040 }
1041 wp_send_json_error( $r );
1042 }
1043
1044 add_action( 'wp_ajax_wpforo_delete_ajax', 'wpf_delete' );
1045 function wpf_delete() {
1046 wpforo_verify_nonce( 'wpforo_delete_ajax' );
1047 $resp = [];
1048 $status = (string) wpfval( $_POST, 'status' );
1049 $postid = (int) wpfval( $_POST, 'postid' );
1050 if( $status === 'topic' ) {
1051 if( WPF()->topic->delete( $postid ) ) {
1052 $forumid = (int) wpfval( $_POST, 'forumid' );
1053 $resp = [
1054 'postid' => $postid,
1055 'location' => $forumid ? WPF()->forum->get_forum_url( $forumid ) : wpforo_home_url(),
1056 ];
1057 $resp['notice'] = WPF()->notice->get_notices();
1058 wp_send_json_success( $resp );
1059 }
1060 } elseif( $status === 'reply' ) {
1061 if( WPF()->post->delete( $postid ) ) {
1062 $root = (int) WPF()->post->get_root( $postid );
1063 $root_replies_count = (int) WPF()->post->get_root_replies_count( $root );
1064 $resp = [
1065 'postid' => $postid,
1066 'root' => $root,
1067 'root_count' => $root_replies_count,
1068 ];
1069 $resp['notice'] = WPF()->notice->get_notices();
1070 wp_send_json_success( $resp );
1071 }
1072 }
1073
1074 $resp['notice'] = WPF()->notice->get_notices();
1075 wp_send_json_error( $resp );
1076 }
1077
1078 add_action( 'wp_ajax_wpforo_layout4_loadmore', 'wpfl4_loadmore' );
1079 add_action( 'wp_ajax_nopriv_wpforo_layout4_loadmore', 'wpfl4_loadmore' );
1080 function wpfl4_loadmore() {
1081 wpforo_verify_nonce( 'wpforo_layout4_loadmore' );
1082 $success = false;
1083 WPF()->notice->add( 'wrong data', 'error' );
1084 $response = [ 'no_more' => 0, 'output_html' => '', 'notice' => WPF()->notice->get_notices() ];
1085 $request = [
1086 'forumid' => 0,
1087 'filter' => 'newest',
1088 'paged' => 1,
1089 ];
1090 $request = array_merge( $request, $_POST );
1091
1092 if( $forumid = intval( $request['forumid'] ) ) {
1093 $items_count = 0;
1094 $childs = WPF()->forum->get_childs( $forumid );
1095 $childs[] = $forumid;
1096 $args = [
1097 'offset' => ( $request['paged'] - 1 ) * wpforo_setting( 'forums', 'layout_threaded_intro_topics_count' ),
1098 'row_count' => wpforo_setting( 'forums', 'layout_threaded_intro_topics_count' ),
1099 'forumids' => $childs,
1100 'orderby' => 'type, modified',
1101 'order' => 'DESC',
1102 ];
1103
1104 switch( $request['filter'] ) {
1105 case 'solved':
1106 $args['solved'] = 1;
1107 $args['type'] = 0;
1108 break;
1109 case 'unsolved':
1110 $args['solved'] = 0;
1111 $args['type'] = 0;
1112 $args['closed'] = 0;
1113 break;
1114 case 'hottest':
1115 $args['orderby'] = 'posts';
1116 break;
1117 }
1118
1119 $topics = WPF()->topic->get_topics( $args, $items_count );
1120 if( $topics ) {
1121 ob_start();
1122 if( function_exists( 'wpforo_thread_forum_template' ) ) {
1123 foreach( $topics as $topic ) {
1124 wpforo_thread_forum_template( $topic['topicid'] );
1125 }
1126 }
1127 $response['output_html'] = ob_get_clean();
1128 $success = true;
1129 $response['notice'] = '';
1130 if( count( $topics ) < wpforo_setting( 'forums', 'layout_threaded_intro_topics_count' ) ) {
1131 $response['no_more'] = 1;
1132 }
1133 } else {
1134 $response['no_more'] = 1;
1135 }
1136
1137 if( $response['no_more'] ) {
1138 WPF()->notice->add( 'all topics has been loaded in this list', 'success' );
1139 $response['notice'] = WPF()->notice->get_notices();
1140 }
1141 }
1142
1143 if( $success ) {
1144 wp_send_json_success( $response );
1145 } else {
1146 wp_send_json_error( $response );
1147 }
1148 }
1149
1150 add_action( 'wp_ajax_wpforo_load_more_activities', 'wpforo_load_more_activities' );
1151 add_action( 'wp_ajax_nopriv_wpforo_load_more_activities', 'wpforo_load_more_activities' );
1152 function wpforo_load_more_activities() {
1153 wpforo_verify_nonce( 'wpforo_load_more_activities' );
1154
1155 $items_per_page = 20;
1156 $page = ! empty( $_POST['page'] ) ? intval( $_POST['page'] ) : 1;
1157 $activity_type = ! empty( $_POST['type'] ) ? sanitize_text_field( $_POST['type'] ) : 'all';
1158 $period = ! empty( $_POST['period'] ) ? sanitize_text_field( $_POST['period'] ) : 'week';
1159
1160 // Calculate date_from based on period
1161 $date_from = null;
1162 switch( $period ) {
1163 case 'day':
1164 $date_from = time() - ( 24 * 60 * 60 );
1165 break;
1166 case 'week':
1167 $date_from = time() - ( 7 * 24 * 60 * 60 );
1168 break;
1169 case 'month':
1170 $date_from = time() - ( 30 * 24 * 60 * 60 );
1171 break;
1172 case 'all':
1173 default:
1174 $date_from = null;
1175 break;
1176 }
1177
1178 // Get enabled activity types from settings
1179 $enabled_types = WPF()->settings->activity['enabled_types'] ?? [];
1180
1181 // Build activity query args
1182 $args = [
1183 'types_exclude' => [ 'new_reply' ], // Exclude notification-only types
1184 'orderby' => 'date',
1185 'order' => 'DESC',
1186 'offset' => ( $page - 1 ) * $items_per_page,
1187 'row_count' => $items_per_page,
1188 'date_from' => $date_from,
1189 ];
1190
1191 // Filter by activity type
1192 if( $activity_type !== 'all' ) {
1193 $type_mapping = [
1194 'topic' => [ 'wpforo_topic', 'edit_topic' ],
1195 'reply' => [ 'wpforo_post', 'edit_post' ],
1196 'reaction' => [ 'new_like', 'new_dislike', 'new_up_vote', 'new_down_vote', 'new_reaction' ],
1197 'favorite' => [ 'new_favorite' ],
1198 'solved' => [ 'topic_solved' ],
1199 'closed' => [ 'topic_closed' ],
1200 'answer' => [ 'post_answer' ],
1201 ];
1202 if( isset( $type_mapping[ $activity_type ] ) ) {
1203 // Intersect with enabled types to respect settings
1204 $args['types_include'] = array_intersect( $type_mapping[ $activity_type ], $enabled_types );
1205 }
1206 } else {
1207 // Show all enabled types
1208 if( ! empty( $enabled_types ) ) {
1209 $args['types_include'] = $enabled_types;
1210 }
1211 }
1212
1213 $items_count = 0;
1214 $activities = WPF()->activity->get_activities( $args, $items_count, true, true );
1215 $has_more = ( $page * $items_per_page ) < $items_count;
1216
1217 // Generate HTML for timeline items
1218 $html = '';
1219 if( ! empty( $activities ) ) {
1220 $index = 0;
1221 foreach( $activities as $activity ) {
1222 // For non-content actions (reactions, favorites, votes, etc.), the actor is in itemid_second
1223 $actor_actions = [ 'new_like', 'new_dislike', 'new_up_vote', 'new_down_vote', 'new_reaction', 'new_mention', 'new_favorite', 'topic_solved', 'topic_closed', 'post_answer' ];
1224 $actor_userid = in_array( $activity['type'], $actor_actions ) && ! empty( $activity['itemid_second'] )
1225 ? $activity['itemid_second']
1226 : $activity['userid'];
1227 $member = wpforo_member( $actor_userid );
1228 $icon = wpforo_activity_icon( $activity['type'] );
1229 $info_html = wpforo_activity_info_html( $activity, $member );
1230 $show_excerpt = in_array( $activity['type'], [ 'wpforo_topic', 'wpforo_post' ] );
1231 $excerpt = '';
1232 if( $show_excerpt && $activity['itemid'] ) {
1233 // For wpforo_topic, itemid is topicid - get first post via topic's first_postid
1234 if( $activity['type'] === 'wpforo_topic' ) {
1235 $topic_data = WPF()->topic->get_topic( $activity['itemid'] );
1236 if( $topic_data && ! empty( $topic_data['first_postid'] ) ) {
1237 $first_post = WPF()->post->get_post( $topic_data['first_postid'] );
1238 if( $first_post && ! empty( $first_post['body'] ) ) {
1239 $excerpt = wp_trim_words( strip_tags( $first_post['body'] ), 20 );
1240 }
1241 }
1242 } else {
1243 $post = WPF()->post->get_post( $activity['itemid'] );
1244 if( $post && ! empty( $post['body'] ) ) {
1245 $excerpt = wp_trim_words( strip_tags( $post['body'] ), 20 );
1246 }
1247 }
1248 }
1249
1250 $html .= '<div class="wpf-timeline-item wpf-timeline-' . esc_attr( $activity['type'] ) . '" data-activity-id="' . esc_attr( $activity['id'] ) . '" style="--delay: ' . ( $index * 0.1 ) . 's">';
1251 $html .= '<div class="wpf-timeline-left">';
1252 if( WPF()->usergroup->can( 'va' ) && wpforo_setting( 'profiles', 'avatars' ) ) {
1253 $html .= '<div class="wpf-timeline-avatar">' . wpforo_user_avatar( $member, 40 ) . '</div>';
1254 }
1255 $html .= '<div class="wpf-timeline-user">';
1256 ob_start();
1257 wpforo_member_link( $member );
1258 $html .= ob_get_clean();
1259 $html .= '</div>';
1260 $html .= '<div class="wpf-timeline-date">';
1261 ob_start();
1262 wpforo_date( $activity['date'], 'ago' );
1263 $html .= ob_get_clean();
1264 $html .= '</div>';
1265 $html .= '</div>';
1266 $html .= '<div class="wpf-timeline-node"><div class="wpf-timeline-icon">' . $icon . '</div></div>';
1267 $html .= '<div class="wpf-timeline-right"><div class="wpf-timeline-card">';
1268 if( WPF()->usergroup->can( 'aum' ) ) {
1269 $html .= '<button type="button" class="wpf-activity-delete" title="' . esc_attr( wpforo_phrase( 'Delete Activity', false ) ) . '"><i class="fas fa-trash-alt"></i></button>';
1270 }
1271 $html .= '<div class="wpf-timeline-activity">' . $info_html . '</div>';
1272 if( $show_excerpt && $excerpt ) {
1273 $html .= '<div class="wpf-timeline-excerpt">' . esc_html( $excerpt ) . '</div>';
1274 }
1275 $html .= '</div></div>';
1276 $html .= '</div>';
1277
1278 $index++;
1279 }
1280 }
1281
1282 wp_send_json_success( [
1283 'html' => $html,
1284 'has_more' => $has_more,
1285 ] );
1286 }
1287
1288 add_action( 'wp_ajax_wpforo_delete_activity', 'wpforo_delete_activity' );
1289 function wpforo_delete_activity() {
1290 wpforo_verify_nonce( 'wpforo_delete_activity' );
1291
1292 // Permission check
1293 if( ! WPF()->usergroup->can( 'aum' ) ) {
1294 wp_send_json_error( [ 'message' => wpforo_phrase( 'Permission denied', false ) ] );
1295 }
1296
1297 $activity_id = ! empty( $_POST['activity_id'] ) ? wpforo_bigintval( $_POST['activity_id'] ) : 0;
1298 if( ! $activity_id ) {
1299 wp_send_json_error( [ 'message' => wpforo_phrase( 'Invalid activity', false ) ] );
1300 }
1301
1302 if( WPF()->activity->delete_activity( $activity_id ) ) {
1303 wp_send_json_success( [ 'message' => wpforo_phrase( 'Activity deleted', false ) ] );
1304 } else {
1305 wp_send_json_error( [ 'message' => wpforo_phrase( 'Failed to delete activity', false ) ] );
1306 }
1307 }
1308
1309 add_action( 'wp_ajax_wpforo_topic_portable_form', 'wpforo_topic_portable_form' );
1310 add_action( 'wp_ajax_nopriv_wpforo_topic_portable_form', 'wpforo_topic_portable_form' );
1311 function wpforo_topic_portable_form() {
1312 wpforo_verify_nonce( 'wpforo_topic_portable_form' );
1313 $html = '';
1314 if( $forumid = wpfval( $_POST, 'forumid' ) ) {
1315 WPF()->form->current['varname'] = 'thread';
1316 ob_start();
1317 WPF()->tpl->topic_form( $forumid );
1318 do_action( 'wpforo_topic_portable_form_extra_html', $forumid );
1319 $html = trim( (string) ob_get_clean() );
1320 }
1321 if( $html ) {
1322 wp_send_json_success( $html );
1323 } else {
1324 wp_send_json_error();
1325 }
1326 }
1327
1328 add_action( 'wp_ajax_wpforo_qa_comment_loadrest', 'wpforo_qa_comment_loadrest' );
1329 add_action( 'wp_ajax_nopriv_wpforo_qa_comment_loadrest', 'wpforo_qa_comment_loadrest' );
1330 function wpforo_qa_comment_loadrest() {
1331 wpforo_verify_nonce( 'wpforo_qa_comment_loadrest' );
1332 WPF()->notice->add( 'wrong data', 'error' );
1333 $response = [ 'output_html' => '', 'notice' => WPF()->notice->get_notices() ];
1334 if( $parentid = wpfval( $_POST, 'parentid' ) ) {
1335 $args = [
1336 'root' => $parentid,
1337 'offset' => wpforo_setting( 'topics', 'layout_qa_comments_limit_count' ),
1338 'row_count' => PHP_INT_MAX,
1339 ];
1340 if( ! wpforo_root_exist() ) {
1341 unset( $args['root'] );
1342 $args['parentid'] = $parentid;
1343 }
1344 if( $comments = WPF()->post->get_posts( $args ) ) {
1345 ob_start();
1346 include_once( wpftpl( 'layouts/3/comment.php' ) );
1347 ob_clean();
1348
1349 foreach( $comments as $comment ) {
1350 wpforo_qa_comment_template( $comment );
1351 }
1352
1353 $response['output_html'] = ob_get_clean();
1354 $response['notice'] = '';
1355 wp_send_json_success( $response );
1356 }
1357 }
1358 wp_send_json_error( $response );
1359 }
1360
1361 add_action( 'wp_ajax_wpforo_post_url_fixer', 'wpforo_post_url_fixer' );
1362 add_action( 'wp_ajax_nopriv_wpforo_post_url_fixer', 'wpforo_post_url_fixer' );
1363 function wpforo_post_url_fixer() {
1364 wpforo_verify_nonce( 'wpforo_post_url_fixer' );
1365 if( ( $postid = wpforo_bigintval( wpfval( $_POST, 'postid' ) ) ) && ( $referer = wpfval( $_POST, 'referer' ) ) ) {
1366 if( is_wpforo_url( $referer ) ) {
1367 $diff = 100;
1368 $referer_hash = md5( $referer . $postid );
1369 $nowtime = time();
1370 if( $lasttime = (int) get_transient( 'wpforo_post_url_fixer' . $referer_hash ) ) {
1371 $diff = $nowtime - $lasttime;
1372 }
1373 if( $diff <= 8 ) {
1374 exit();
1375 }
1376 set_transient( 'wpforo_post_url_fixer' . $referer_hash, $nowtime, 1000 );
1377 if( $post = WPF()->post->get_post( $postid ) ) {
1378 echo WPF()->post->get_url( $post );
1379 }
1380 }
1381 }
1382 exit();
1383 }
1384
1385 add_action( 'wp_ajax_wpforo_update_database', 'wpforo_ajax_update_database' );
1386 function wpforo_ajax_update_database() {
1387 check_admin_referer( 'wpforo_update_database' );
1388 wpforo_set_max_execution_time();
1389 wpforo_update_db();
1390 exit();
1391 }
1392
1393 ############### Sending Email ##################
1394 function wpforo_set_html_content_type() {
1395 return apply_filters( 'wpforo_emails_content_type', 'text/html' );
1396 }
1397
1398 function __wpforo_set_html_content_type() {
1399 return 'text/html';
1400 }
1401
1402 function wpforo_mail_from_name() {
1403 return wpforo_setting( 'email', 'from_name' ) ?: get_option( 'blogname' );
1404 }
1405
1406 function wpforo_mail_from_email() {
1407 return wpforo_setting( 'email', 'from_email' ) ?: get_option( 'admin_email' );
1408 }
1409
1410 function wpforo_mail_headers( $from_name = '', $from_email = '', $cc = [], $bcc = [] ) {
1411 $H = [];
1412 if( ! $from_name ) {
1413 $from_name = wpforo_mail_from_name();
1414 }
1415 if( ! $from_email ) {
1416 $from_email = wpforo_mail_from_email();
1417 }
1418 $H[] = 'From: ' . $from_name . ' <' . $from_email . '>';
1419 if( ! empty( $cc ) ) {
1420 foreach( $cc as $c ) {
1421 $c = sanitize_email( $c );
1422 $H[] = 'CC: ' . $c;
1423 }
1424 }
1425 if( ! empty( $bcc ) ) {
1426 foreach( $bcc as $b ) {
1427 $b = sanitize_email( $b );
1428 $H[] = 'BCC: ' . $b;
1429 }
1430 }
1431
1432 return $H;
1433 }
1434
1435 function wpforo_admin_mail_headers( $from_name = '', $from_email = '', $cc = [], $bcc = [] ) {
1436 $H = [];
1437 if( ! $from_name ) {
1438 $from_name = wpforo_mail_from_name();
1439 }
1440 if( ! $from_email ) {
1441 $from_email = wpforo_mail_from_email();
1442 }
1443 $H[] = 'From: ' . $from_name . ' <' . $from_email . '>';
1444 if( empty( $cc ) ) $cc = wpforo_setting( 'email', 'admin_emails' );
1445 if( ! empty( $cc ) ) {
1446 foreach( $cc as $c ) {
1447 $c = sanitize_email( $c );
1448 $H[] = 'CC: ' . $c;
1449 }
1450 }
1451 if( ! empty( $bcc ) ) {
1452 foreach( $bcc as $b ) {
1453 $b = sanitize_email( $b );
1454 $H[] = 'BCC: ' . $b;
1455 }
1456 }
1457
1458 return $H;
1459 }
1460
1461 function wpforo_get_login_or_register_notice_text() {
1462 $popup_html = '';
1463 if( ! wpforo_is_bot() ) {
1464 $popup_html = sprintf(
1465 wpforo_phrase( 'Please %s or %s', false ),
1466 '<a href="' . wpforo_login_url() . '" rel="noindex,nofollow">' . wpforo_phrase(
1467 'Login',
1468 false
1469 ) . '</a>',
1470 '<a href="' . wpforo_register_url() . '" rel="noindex,nofollow">' . wpforo_phrase(
1471 'Register',
1472 false
1473 ) . '</a>'
1474 );
1475 }
1476
1477 return apply_filters( 'wpforo_login_or_register_popup_message', $popup_html );
1478 }
1479
1480 function wpforo_dynamic_phrases_register() {
1481 if( ! $js = WPF()->phrase->get_wpforo_phrases_inline_js() ) {
1482 return;
1483 }
1484 $md5_js = md5( $js );
1485
1486 $inline = false;
1487 $dynamic_js_file = WPF()->folders['assets']['dir'] . DIRECTORY_SEPARATOR . 'phrases.js';
1488 if( ! file_exists( $dynamic_js_file ) || $md5_js !== md5_file( $dynamic_js_file ) ) {
1489 $result = wpforo_write_file( $dynamic_js_file, $js );
1490 if( wpfval( $result, 'error' ) ) {
1491 $inline = true;
1492 }
1493 }
1494
1495 wp_register_script(
1496 'wpforo-dynamic-phrases',
1497 WPF()->folders['assets']['url'] . '/phrases.js',
1498 false,
1499 WPFORO_VERSION . '.' . $md5_js
1500 );
1501 if( $inline ) {
1502 $js = preg_replace( '|[\r\n\t]+|', '', $js );
1503 wp_add_inline_script( 'wpforo-dynamic-phrases', $js );
1504 }
1505 }
1506
1507 function wpforo_dynamic_style_enqueue() {
1508 if( ! $css = WPF()->tpl->generate_dynamic_css() ) {
1509 return;
1510 }
1511 $md5_css = md5( $css );
1512
1513 $inline = false;
1514 $dynamic_css_file = WPF()->folders['assets']['dir'] . DIRECTORY_SEPARATOR . 'colors.css';
1515 if( ! file_exists( $dynamic_css_file ) || $md5_css !== md5_file( $dynamic_css_file ) ) {
1516 $result = wpforo_write_file( $dynamic_css_file, $css );
1517 if( wpfval( $result, 'error' ) ) {
1518 $inline = true;
1519 }
1520 }
1521
1522 wp_register_style(
1523 'wpforo-dynamic-style',
1524 WPF()->folders['assets']['url'] . '/colors.css',
1525 false,
1526 WPFORO_VERSION . '.' . $md5_css
1527 );
1528 if( $inline ) {
1529 $css = preg_replace( '|[\r\n\t]+|', '', $css );
1530 wp_add_inline_style( 'wpforo-dynamic-style', $css );
1531 }
1532
1533 wp_enqueue_style( 'wpforo-dynamic-style' );
1534 }
1535
1536 add_action( 'wp_enqueue_scripts', 'wpforo_dynamic_style_enqueue', 999 );
1537
1538 function wpforo_frontend_register_scripts() {
1539 wp_register_style(
1540 'wpforo-font-awesome',
1541 WPFORO_URL . '/assets/css/font-awesome/css/fontawesome-all.min.css',
1542 false,
1543 '6.1.1'
1544 );
1545 //wp_register_style( 'wpforo-font-awesome-rtl', WPFORO_URL . '/assets/css/font-awesome/css/font-awesome-rtl.css', [ 'wpforo-font-awesome' ], WPFORO_VERSION );
1546 wpforo_dynamic_phrases_register();
1547 wp_register_script( 'wpforo-frontend-js', WPFORO_URL . '/assets/js/frontend.js', [
1548 'jquery',
1549 'wpforo-dynamic-phrases',
1550 ], WPFORO_VERSION, true );
1551 wp_localize_script( 'wpforo-frontend-js', 'wpforo', [
1552 'ajax_url' => wpforo_get_ajax_url(),
1553 'nonces' => wpforo_generate_ajax_nonces(),
1554 'settings_slugs' => WPF()->settings->slugs,
1555 'editor_settings' => WPF()->tpl->editor_buttons( 'post' ),
1556 'revision_options' => WPF()->settings->posting,
1557 'notice' => [
1558 'login_or_register' => wpforo_get_login_or_register_notice_text(),
1559 'timeouts' => WPF()->notice->get_timeouts(),
1560 ],
1561 ] );
1562 // Register AI features script if AI service is available and any frontend AI feature is enabled
1563 // ai-features.js handles: assistant widget, translation, topic summary, topic suggestions, bot reply
1564 if( isset( WPF()->ai_client ) && WPF()->ai_client->is_service_available() && (
1565 wpforo_setting( 'ai', 'assistant' ) ||
1566 wpforo_setting( 'ai', 'translation' ) ||
1567 wpforo_setting( 'ai', 'topic_summary' ) ||
1568 wpforo_setting( 'ai', 'topic_suggestions' ) ||
1569 wpforo_setting( 'ai', 'bot_reply' )
1570 ) ) {
1571 wp_register_script( 'wpforo-ai-features-js', WPFORO_URL . '/assets/js/ai-features.js', [
1572 'wpforo-frontend-js',
1573 ], WPFORO_VERSION, true );
1574 }
1575 // Register AI Chatbot script separately (can be large, loaded conditionally)
1576 if( isset( WPF()->ai_chatbot ) && WPF()->ai_chatbot->is_enabled() ) {
1577 wp_register_script( 'wpforo-ai-chatbot-js', WPFORO_URL . '/assets/js/ai-chatbot.js', [
1578 'wpforo-frontend-js',
1579 ], WPFORO_VERSION, true );
1580 wp_localize_script( 'wpforo-ai-chatbot-js', 'wpforo_ai_chatbot', [
1581 'ajax_url' => wpforo_get_ajax_url(),
1582 'nonce' => wp_create_nonce( 'wpforo_ai_chatbot' ),
1583 'welcome_message' => wpforo_setting( 'ai', 'chatbot_welcome_message' ),
1584 'use_rag' => (bool) wpforo_setting( 'ai', 'chatbot_use_rag' ),
1585 'use_local_context' => (bool) wpforo_setting( 'ai', 'chatbot_use_local_context' ),
1586 'phrases' => [
1587 'new_conversation' => wpforo_phrase( 'New Conversation', false ),
1588 'send_message' => wpforo_phrase( 'Send a message...', false ),
1589 'delete_confirm' => wpforo_phrase( 'Delete this conversation?', false ),
1590 'error_sending' => wpforo_phrase( 'Error sending message. Please try again.', false ),
1591 'typing' => wpforo_phrase( 'AI is thinking...', false ),
1592 'no_conversations' => wpforo_phrase( 'No conversations yet', false ),
1593 'start_conversation' => wpforo_phrase( 'Start a new conversation to chat with the AI assistant.', false ),
1594 ],
1595 ] );
1596 }
1597 wp_register_script( 'wpforo-ajax', WPFORO_URL . '/assets/js/ajax.js', [
1598 'suggest',
1599 'wpforo-frontend-js',
1600 ], WPFORO_VERSION, true );
1601 wp_register_style( 'wpforo-style', wpftpl_url( 'style.css' ), false, WPFORO_VERSION );
1602 wp_register_style( 'wpforo-style-rtl', wpftpl_url( 'style-rtl.css' ), false, WPFORO_VERSION );
1603 wp_register_style( 'wpforo-widgets', wpftpl_url( 'widgets.css' ), [], WPFORO_VERSION );
1604 wp_register_style( 'wpforo-widgets-rtl', wpftpl_url( 'widgets-rtl.css' ), [], WPFORO_VERSION );
1605 wp_register_script( 'wpforo-widgets-js', WPFORO_URL . '/assets/js/widgets.js', [ 'jquery' ], WPFORO_VERSION, true );
1606 $wpforo_widgets = [
1607 'ajax_url' => wpforo_get_ajax_url(),
1608 'is_live_notifications_on' => 0,
1609 'live_notifications_start' => 30000,
1610 'live_notifications_interval' => 60000,
1611 ];
1612 if( WPF()->current_userid && wpforo_setting( 'notifications', 'notifications' ) && wpforo_setting(
1613 'notifications',
1614 'notifications_live'
1615 ) ) {
1616 $start = apply_filters( 'wpforo_notifications_list', 30000 );
1617 $interval = apply_filters( 'wpforo_notifications_list', 60000 );
1618 if( $interval < 10000 ) {
1619 $interval = 10000;
1620 }
1621 $wpforo_widgets['is_live_notifications_on'] = 1;
1622 $wpforo_widgets['live_notifications_start'] = $start;
1623 $wpforo_widgets['live_notifications_interval'] = $interval;
1624 }
1625 wp_localize_script( 'wpforo-widgets-js', 'wpforo_widgets', $wpforo_widgets );
1626
1627 if( ! WPF()->perm->forum_can( 'va' ) || ! WPF()->usergroup->can( 'caa' ) ) {
1628 wp_add_inline_script(
1629 'wpforo-frontend-js',
1630 "jQuery(document).ready(function($){
1631 $('#wpforo-wrap').on('click', '.attach_cant_view', function(){
1632 wpforo_notice_show(
1633 '<p>" . addslashes(
1634 (string) ( is_user_logged_in() ? wpforo_phrase(
1635 'You are not permitted to view this attachment',
1636 false
1637 ) : wpforo_get_login_or_register_notice_text() )
1638 ) . "</p>'
1639 );
1640 });
1641 })"
1642 );
1643 }
1644
1645 do_action( 'wpforo_frontend_register_scripts' );
1646
1647 wpforo_frontend_enqueue_scripts();
1648 }
1649
1650 add_action( 'wp_enqueue_scripts', 'wpforo_frontend_register_scripts' );
1651
1652 function wpforo_frontend_enqueue_scripts() {
1653 $fontawesome = wpforo_setting( 'general', 'fontawesome' );
1654 if( $fontawesome === 'sitewide' || ( $fontawesome === 'forum' && is_wpforo_page() ) ) {
1655 wp_enqueue_style( 'wpforo-font-awesome' );
1656 //if( is_rtl() ) wp_enqueue_style( 'wpforo-font-awesome-rtl' );
1657 }
1658
1659 if( is_wpforo_page() ) {
1660 wp_enqueue_script( 'wpforo-dynamic-phrases' );
1661 wp_enqueue_script( 'wpforo-frontend-js' );
1662 // Enqueue AI features script if AI service is available and any frontend AI feature is enabled
1663 if( isset( WPF()->ai_client ) && WPF()->ai_client->is_service_available() && (
1664 wpforo_setting( 'ai', 'assistant' ) ||
1665 wpforo_setting( 'ai', 'translation' ) ||
1666 wpforo_setting( 'ai', 'topic_summary' ) ||
1667 wpforo_setting( 'ai', 'topic_suggestions' ) ||
1668 wpforo_setting( 'ai', 'bot_reply' )
1669 ) ) {
1670 wp_enqueue_script( 'wpforo-ai-features-js' );
1671 }
1672 // Enqueue AI Chatbot script if enabled and user can chat
1673 if( isset( WPF()->ai_chatbot ) && WPF()->ai_chatbot->is_enabled() && WPF()->ai_chatbot->user_can_chat() ) {
1674 wp_enqueue_script( 'wpforo-ai-chatbot-js' );
1675 }
1676 wp_enqueue_script( 'wpforo-ajax' );
1677 if( is_rtl() ) {
1678 wp_enqueue_style( 'wpforo-style-rtl' );
1679 } else {
1680 wp_enqueue_style( 'wpforo-style' );
1681 }
1682 }
1683
1684 if( is_rtl() ) {
1685 wp_enqueue_style( 'wpforo-widgets-rtl' );
1686 } else {
1687 wp_enqueue_style( 'wpforo-widgets' );
1688 }
1689
1690 do_action( 'wpforo_frontend_enqueue_scripts' );
1691 }
1692
1693 function wpforo_style_options( $css ) {
1694 $css .= "\r\n#wpforo-wrap .wpforo-forum-title{font-size: " . wpforo_setting(
1695 'styles',
1696 'font_size_forum'
1697 ) . "px!important; line-height: " . ( wpforo_setting( 'styles', 'font_size_forum' ) + 1 ) . "px!important;}";
1698 $css .= "\r\n#wpforo-wrap .wpforo-topic-title a { font-size: " . wpforo_setting(
1699 'styles',
1700 'font_size_topic'
1701 ) . "px!important; }";
1702 $css .= "\r\n#wpforo-wrap .wpforo-post .wpf-right .wpforo-post-content {font-size: " . wpforo_setting(
1703 'styles',
1704 'font_size_post_content'
1705 ) . "px!important;}\r\n#wpforo-wrap .wpforo-post .wpf-right .wpforo-post-content p {font-size: " . wpforo_setting(
1706 'styles',
1707 'font_size_post_content'
1708 ) . "px;}";
1709
1710 if( 'bottom' === wpforo_setting( 'posting', 'topic_editor_toolbar_location' ) ) {
1711 $css .= "\r\n
1712 #wpforo #wpforo-wrap .wpf-topic-create .mce-container-body{display: flex; flex-direction: column;}
1713 #wpforo #wpforo-wrap .wpf-topic-create .mce-top-part{order: 1}
1714 #wpforo #wpforo-wrap .wpf-topic-create .wpf-field-wrap > .wpf-extra-fields,
1715 #wpforo #wpforo-wrap .wpforo-portable-form-wrap .wpf-extra-fields,
1716 #wpforo #wpforo-wrap .wpforo-messages-content .wpfpm-main .wpfpm-form-wrapper .wpf-extra-fields{ margin-top: 10px !important; }";
1717 }
1718 if( 'bottom' === wpforo_setting( 'posting', 'reply_editor_toolbar_location' ) ) {
1719 $css .= "\r\n
1720 #wpforo #wpforo-wrap .wpf-post-create .mce-container-body{display: flex; flex-direction: column;}
1721 #wpforo #wpforo-wrap .wpf-post-create .mce-top-part{order: 1}
1722 #wpforo #wpforo-wrap .wpf-post-create .wpf-field-wrap > .wpf-extra-fields,
1723 #wpforo #wpforo-wrap .wpforo-portable-form-wrap .wpf-extra-fields,
1724 #wpforo #wpforo-wrap .wpforo-messages-content .wpfpm-main .wpfpm-form-wrapper .wpf-extra-fields{ margin-top: 10px !important; }";
1725 }
1726 if( wpforo_setting( 'ai', 'assistant_highlight' ) ) {
1727 $css .= "\r\n
1728 #wpforo #wpforo-wrap .wpf-ai-helper-toggle.wpf-ai-highlight{ background:__WPFCOLOR_12__; color:__WPFCOLOR_1__;}
1729 #wpforo #wpforo-wrap .wpf-ai-helper-toggle.wpf-ai-highlight .wpf-ai-sparkle-icon{ fill:__WPFCOLOR_1__;}
1730 #wpforo #wpforo-wrap .wpf-ai-helper-toggle.wpf-ai-highlight:hover{ background:__WPFCOLOR_12__; color:__WPFCOLOR_1__; opacity: 0.85;}";
1731 }
1732 if( $ccss = wpforo_setting( 'styles', 'custom_css' ) ) $css .= "\r\n" . stripslashes( (string) $ccss );
1733
1734 return $css;
1735 }
1736
1737 add_filter( 'wpforo_dynamic_css_filter', 'wpforo_style_options' );
1738
1739 function wpforo_admin_enqueue() {
1740 wp_register_style(
1741 'wpforo-font-awesome',
1742 WPFORO_URL . '/assets/css/font-awesome/css/fontawesome-all.min.css',
1743 false,
1744 '6.1.1'
1745 );
1746 wp_register_style(
1747 'wpforo-iconpicker-css',
1748 WPFORO_URL . '/admin/assets/third-party/iconpicker/fontawesome-iconpicker.min.css',
1749 [ 'wpforo-font-awesome' ],
1750 '2.0.0'
1751 );
1752 wp_register_script(
1753 'wpforo-iconpicker-js',
1754 WPFORO_URL . '/admin/assets/third-party/iconpicker/fontawesome-iconpicker.js',
1755 [ 'jquery' ],
1756 '2.0.0',
1757 true
1758 );
1759 wp_register_style( 'wpforo-admin', WPFORO_URL . '/admin/assets/css/admin.css', false, WPFORO_VERSION );
1760 wp_localize_script( 'wp-color-picker', 'wpColorPickerL10n', [
1761 'clear' => __( 'Clear' ),
1762 'defaultString' => __( 'Default' ),
1763 'pick' => __( 'Select Color' ),
1764 ] );
1765 wp_register_script(
1766 'wpforo-contenthover-addons',
1767 WPFORO_URL . '/admin/assets/js/contenthover/jquery.contenthover.min.js',
1768 [ 'jquery' ],
1769 WPFORO_VERSION,
1770 false
1771 );
1772 wp_register_script(
1773 'wpforo-backend-widgets-js',
1774 WPFORO_URL . '/admin/assets/js/widgets.js',
1775 [ 'jquery' ],
1776 WPFORO_VERSION,
1777 true
1778 );
1779 wp_register_script(
1780 'wpforo-backend-js',
1781 WPFORO_URL . '/admin/assets/js/backend.js',
1782 [ 'jquery' ],
1783 WPFORO_VERSION,
1784 false
1785 );
1786 wp_localize_script( 'wpforo-backend-js', 'wpforo_admin', [
1787 'phrases' => [
1788 'move' => __( 'Move', 'wpforo' ),
1789 'delete' => __( 'Delete', 'wpforo' ),
1790 ],
1791 ] );
1792 WPF()->settings->init_info();
1793 wp_localize_script(
1794 'wpforo-backend-js',
1795 'wpforo',
1796 [
1797 'ajax_url' => wpforo_get_ajax_url(),
1798 'nonces' => wpforo_generate_ajax_nonces(),
1799 'board' => [ 'has_more_boards' => is_wpforo_multiboard() ],
1800 'settings' => [
1801 'info' => [
1802 'core' => WPF()->settings->info->core,
1803 'addons' => WPF()->settings->info->addons,
1804 ],
1805 ],
1806 ]
1807 );
1808 wp_register_style(
1809 'wpforo-deactivation-css',
1810 WPFORO_URL . '/admin/assets/css/deactivation-dialog.css',
1811 [],
1812 WPFORO_VERSION
1813 );
1814 wp_register_script(
1815 'wpforo-deactivation-js',
1816 WPFORO_URL . '/admin/assets/js/deactivation-dialog.js',
1817 [ 'jquery' ],
1818 WPFORO_VERSION
1819 );
1820 wp_localize_script( 'wpforo-deactivation-js', 'wpforo_deactivation_obj', [
1821 'msgReasonRequired' => __( 'Please choose one reasons before sending a feedback!', 'wpforo' ),
1822 'msgReasonDescRequired' => __( 'Please provide more information', 'wpforo' ),
1823 'msgFeedbackHasEmailNoCheckbox' => __(
1824 'With the email address, please check the "I agree to receive email" checkbox to proceed.',
1825 'wpforo'
1826 ),
1827 'msgFeedbackHasCheckboxNoEmail' => __( 'Please fill your email address for feedback', 'wpforo' ),
1828 'msgFeedbackNotValidEmail' => __( 'Your email address is not valid', 'wpforo' ),
1829 'adminUrl' => get_admin_url(),
1830 ] );
1831 wp_register_script( 'wpforo-iris', admin_url( 'js/iris.min.js' ), [
1832 'jquery-ui-draggable',
1833 'jquery-ui-slider',
1834 'jquery-touch-punch',
1835 ], false, true );
1836
1837 // Register AI Features admin assets
1838 wp_register_style(
1839 'wpforo-ai-features',
1840 WPFORO_URL . '/admin/assets/css/ai-features.css',
1841 false,
1842 WPFORO_VERSION
1843 );
1844 wp_register_script(
1845 'wpforo-ai-features-js',
1846 WPFORO_URL . '/admin/assets/js/ai-features.js',
1847 [ 'jquery' ],
1848 WPFORO_VERSION,
1849 true
1850 );
1851
1852 do_action( 'wpforo_admin_register_scripts' );
1853
1854 if( ! empty( $_GET['page'] ) && strpos( (string) $_GET['page'], 'wpforo' ) === 0 ) {
1855 wp_enqueue_style( 'wpforo-admin' );
1856 wp_enqueue_script( 'wpforo-backend-js' );
1857 wp_enqueue_style( 'wpforo-iconpicker-css' );
1858 wp_enqueue_script( 'wpforo-iconpicker-js' );
1859 wp_enqueue_style( 'wpforo-font-awesome' );
1860 wp_enqueue_script( 'jquery' );
1861 wp_enqueue_script( 'jquery-ui-core' );
1862 wp_enqueue_script( 'jquery-ui-widget' );
1863 wp_enqueue_script( 'jquery-ui-mouse' );
1864 wp_enqueue_script( 'jquery-ui-position' );
1865 wp_enqueue_script( 'jquery-ui-draggable' );
1866 wp_enqueue_script( 'jquery-ui-droppable' );
1867 wp_enqueue_script( 'jquery-ui-menu' );
1868 wp_enqueue_script( 'jquery-ui-sortable' );
1869 wp_enqueue_script( 'jquery-color' );
1870 wp_enqueue_script( 'wp-lists' );
1871 if( preg_match( '#^wpforo-(?:\d+-)?forums$#iu', (string) $_GET['page'] ) ) {
1872 if( ! empty( $_GET['action'] ) ) {
1873 //Just for excluding 'nav-menu' js loading//
1874 wp_enqueue_script( 'postbox' );
1875 wp_enqueue_script( 'link' );
1876 wp_enqueue_style( 'wp-color-picker' );
1877 wp_enqueue_script( 'wp-color-picker' );
1878 wp_enqueue_script( 'wp-color-picker-script-handle' );
1879 } else {
1880 wp_enqueue_script( 'nav-menu' );
1881 }
1882 } elseif( preg_match(
1883 '#^wpforo-(?:\d+-)?settings$#iu',
1884 (string) $_GET['page']
1885 ) && ! empty( $_GET['wpf_tab'] ) && $_GET['wpf_tab'] === 'styles' ) {
1886 wp_enqueue_style( 'wp-color-picker' );
1887 wp_enqueue_script( 'wpforo-iris' );
1888 wp_enqueue_script( 'wp-color-picker' );
1889 } elseif( $_GET['page'] === wpforo_prefix_slug( 'dashboard' ) || $_GET['page'] === 'wpforo-overview' ) {
1890 wp_enqueue_script( 'postbox' );
1891 wp_enqueue_script( 'link' );
1892 } elseif( preg_match( '#^wpforo-(?:\d+-)?addons$#iu', (string) $_GET['page'] ) ) {
1893 wp_enqueue_script( 'wpforo-contenthover-addons' );
1894 } elseif( $_GET['page'] === 'wpforo-ai' ) {
1895 wp_enqueue_style( 'wpforo-ai-features' );
1896 wp_enqueue_script( 'wpforo-ai-features-js' );
1897 } elseif( preg_match(
1898 '#^wpforo-(?:\d+-)?settings$#iu',
1899 (string) $_GET['page']
1900 ) && ! empty( $_GET['wpf_tab'] ) && $_GET['wpf_tab'] === 'ai' ) {
1901 // Load AI features scripts on Settings > AI tab for bot user autocomplete
1902 wp_enqueue_style( 'wpforo-ai-features' );
1903 wp_enqueue_script( 'wpforo-ai-features-js' );
1904 }
1905 }
1906 if( ! wpforo_get_option( 'deactivation_dialog_never_show', false, false ) && ( strpos(
1907 wpforo_get_request_uri(),
1908 '/plugins.php'
1909 ) !== false ) ) {
1910 wp_enqueue_style( 'wpforo-deactivation-css' );
1911 wp_enqueue_script( 'wpforo-deactivation-js' );
1912 }
1913
1914 $screen = get_current_screen();
1915 if( ( 'user-edit' === $screen->id || 'profile' === $screen->id ) ) {
1916 wp_enqueue_style( 'wpforo-font-awesome' );
1917 }
1918
1919 if( $screen->id === 'widgets' ) {
1920 wp_enqueue_script( 'wpforo-backend-widgets-js' );
1921 }
1922
1923 do_action( 'wpforo_admin_enqueue_scripts' );
1924 }
1925
1926 add_action( 'admin_enqueue_scripts', 'wpforo_admin_enqueue' );
1927
1928 function wpforo_admin_permalink_notice() {
1929 $permalink_structure = get_option( 'permalink_structure' );
1930 if( ! $permalink_structure ) {
1931 $class = 'notice notice-warning';
1932 $message = __(
1933 'IMPORTANT: wpForo can\'t work with default permalink, please change permalink structure',
1934 'wpforo'
1935 );
1936 printf( '<div class="%1$s"><p>%2$s</p></div>', $class, $message );
1937 }
1938 }
1939
1940 add_action( 'admin_notices', 'wpforo_admin_permalink_notice' );
1941
1942 function wpforo_userform_to_wpuser_html_form( $wp_user ) {
1943 if( is_super_admin() ) {
1944 $userid = 0;
1945 $groupid = 0;
1946 $timezone = '';
1947 $secondary_groupids = [];
1948 if( is_object( $wp_user ) ) {
1949 $userid = $wp_user->ID;
1950 $user = WPF()->member->get_member( $wp_user->ID );
1951 $groupid = $user['groupid'];
1952 $timezone = sanitize_text_field( $user['timezone'] );
1953 $secondary_groupids = $user['secondary_groupids'];
1954 }
1955 ?>
1956 <style>
1957 #wpf-rating-table {
1958 width: 100%;
1959 font-size: 12px;
1960 }
1961
1962 #wpf-rating-table h4 {
1963 margin: 0;
1964 padding: 0;
1965 font-size: 14px;
1966 font-weight: bold;
1967 white-space: nowrap;
1968 }
1969
1970 #wpf-rating-table .wpf-badge-full {
1971 color: #FFFFFF;
1972 white-space: nowrap;
1973 font-size: 15px;
1974 line-height: 16px;
1975 font-weight: bold;
1976 text-align: center;
1977 display: inline-block;
1978 padding: 2px 8px;
1979 min-width: 30px;
1980 }
1981
1982 #wpf-rating-table .wpf-badge-full.wpf-badge-level-6, #wpf-rating-table .wpf-badge-full.wpf-badge-level-7, #wpf-rating-table .wpf-badge-full.wpf-badge-level-8 {
1983 font-size: 18px !important;
1984 }
1985
1986 #wpf-rating-table .wpf-badge-full.wpf-badge-level-9, #wpf-rating-table .wpf-badge-full.wpf-badge-level-10 {
1987 font-size: 22px !important;
1988 }
1989
1990 #wpf-rating-table th, #wpf-rating-table td {
1991 padding: 5px 10px;
1992 text-align: left;
1993 vertical-align: top;
1994 }
1995
1996 #wpf-rating-table tr:nth-child(odd) {
1997 background: #f5f5f5;
1998 }
1999
2000 #wpf-rating-table tr:nth-child(even) {
2001 background: #FFFFFF;
2002 }
2003
2004 #wpf-rating-table th {
2005 text-transform: uppercase;
2006 font-size: 12px;
2007 padding: 10px;
2008 }
2009
2010 .wpforo-profile-table input[type=checkbox] {
2011 width: auto !important;
2012 }
2013
2014 @media screen and (max-width: 700px) {
2015 #wpf-rating-table th, #wpf-rating-table td {
2016 display: table-cell !important;
2017 }
2018 }
2019 </style>
2020 <h2 style="margin-bottom: 30px; margin-top: 50px;"><?php _e( 'Forum Profile Fields - wpForo' ); ?></h2>
2021 <table class="form-table wpforo-profile-table"
2022 style="box-shadow: 1px 1px 6px #cccccc; background: #f7f7f7; margin-bottom: 30px; width: 97%;">
2023 <tr>
2024 <td colspan="2" style="padding: 5px;"></td>
2025 </tr>
2026 <tr class="form-field">
2027 <th scope="row" style="padding: 10px 20px 10px 20px; width: 30%;">
2028 <label for="wpforo_usergroup">
2029 <?php _e( 'Forum - Usergroup', 'wpforo' ); ?>
2030 </label>
2031 <?php if( wpforo_setting( 'authorization', 'role_synch' ) ): ?>
2032 <p class="description" style="font-weight: normal;">
2033 <?php $wpforo_synch_table = admin_url(
2034 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' )
2035 ) ?>
2036 <?php echo sprintf(
2037 __(
2038 'Forum Usergroups are synched with User Roles based on the %s. When you change this user Role the Usergroup is automatically changed according to that table.',
2039 'wpforo'
2040 ),
2041 '<a href="' . $wpforo_synch_table . '" target="_blank">Role-Usergroup synchronization table</a>'
2042 ); ?>
2043 </p>
2044 <?php endif; ?>
2045 </th>
2046 <td style="padding: 15px 20px 10px 20px; vertical-align: top;">
2047 <?php if( wpforo_setting( 'authorization', 'role_synch' ) ): ?>
2048 <select id="wpforo_usergroup" disabled="disabled">
2049 <option value="0"><?php _e( 'Synced with user role', 'wpforo' ); ?></option>
2050 <?php WPF()->usergroup->show_selectbox( $groupid ); ?>
2051 </select>
2052 <input type="hidden" name="wpforo_usergroup" value="<?php echo $groupid; ?>">
2053 &nbsp; <span
2054 style="color: green"><?php _e(
2055 'Role-Usergroup Synchronization is Turned ON!',
2056 'wpforo'
2057 ); ?></span>
2058 <br/>
2059 <p class="description"
2060 style="font-weight: normal; font-size: 13px; line-height: 18px;"><?php _e(
2061 'This user Usergroup is automatically changed according to current Role. If you want to disable Role-Usergroup synchronization and manage Usergroups and User Roles independently, please navigate to <b>Dashboard > wpForo > Settings > Login & Registration</b> admin page and disable "Role-Usergroup Synchronization" option.',
2062 'wpforo'
2063 ); ?></p>
2064 <?php else: ?>
2065 <select id="wpforo_usergroup"
2066 name="wpforo_usergroup"<?php if( wpforo_is_owner( $userid ) || ! current_user_can(
2067 'administrator'
2068 ) ) {
2069 echo ' disabled="disabled"';
2070 } ?>>
2071 <?php WPF()->usergroup->show_selectbox( $groupid ?: WPF()->usergroup->default_groupid ); ?>
2072 </select>
2073 <?php endif; ?>
2074 </td>
2075 </tr>
2076 <tr class="form-field">
2077 <th scope="row" style="padding: 10px 20px 10px 20px;">
2078 <label for="wpforo_usergroup">
2079 <?php _e( 'Forum - Secondary Usergroups', 'wpforo' ); ?>
2080 </label>
2081 </th>
2082 <td style="padding: 15px 20px 10px 20px; vertical-align: top;">
2083 <?php $usergroups = WPF()->usergroup->get_secondary_groups(); ?>
2084 <?php if( ! empty( $usergroups ) ): ?>
2085 <?php foreach( $usergroups as $usergroup ): ?>
2086 <?php if( $usergroup['groupid'] == 1 || $usergroup['groupid'] == 4 ) {
2087 continue;
2088 } //|| $usergroup['groupid'] == $groupid ?>
2089 <label style="min-width: 20%; display: inline-block; padding-bottom: 5px;">
2090 <input type="checkbox"
2091 name="wpforo_secondary_groupids[]"
2092 value="<?php echo intval( $usergroup['groupid'] ) ?>"
2093 <?php checked(
2094 in_array( (int) $usergroup['groupid'], (array) $secondary_groupids, true )
2095 ); ?>>&nbsp;
2096 <?php echo esc_html( $usergroup['name'] ); ?>
2097 </label>
2098 <?php endforeach; ?>
2099 <?php endif; ?>
2100 <input name="wpforo_secondary_groupids[]" value="0" type="hidden">
2101 </td>
2102 </tr>
2103 <tr class="form-field">
2104 <th scope="row" style="padding: 10px 20px 10px 20px;">
2105 <label for="wpforo_usertimezone"><?php _e( 'Forum - User Timezone', 'wpforo' ); ?></label>
2106 </th>
2107 <td style="padding: 15px 20px 10px 20px; vertical-align: top;">
2108 <select name="wpforo_usertimezone" id="wpforo_usertimezone">
2109 <?php echo wp_timezone_choice( $timezone ); ?>
2110 </select>
2111 </td>
2112 </tr>
2113 <?php if( ! empty( $user ) ) : ?>
2114 <tr class="form-field">
2115 <th scope="row" style="padding: 10px 20px 10px 20px;">
2116 <label><?php _e( 'User Reputation', 'wpforo' ); ?></label>
2117 <p class="description"
2118 style="font-weight: normal;"><?php _e(
2119 'By default all members get rating badges and titles based on number of posts. However, using this option you can grant lower or higher rating to certain user (this user). The default member reputation badges, titles and points can be managed in Forums > Settings > Members Tab.',
2120 'wpforo'
2121 ) ?></p>
2122 </th>
2123 <td style="padding: 15px 20px 10px 20px; vertical-align: top;">
2124 <div style="padding-bottom: 10px; margin-bottom: 10px; border-bottom: 1px dashed #cccccc;">
2125 <label><input type="radio" id="wpf-user-rating-default"
2126 name="wpforo_use_member_custom_points"
2127 value="0" <?php checked( ! $user['custom_points'] ) ?>> <?php _e(
2128 'Default Rating',
2129 'wpforo'
2130 ) ?></label>
2131 &nbsp;&nbsp;
2132 <label><input type="radio" id="wpf-user-rating-custom"
2133 name="wpforo_use_member_custom_points"
2134 value="1" <?php checked( $user['custom_points'] ) ?>> <?php _e(
2135 'Custom Rating',
2136 'wpforo'
2137 ) ?></label>
2138 </div>
2139
2140 <table id="wpf-rating-table" cellspacing="0" cellpadding="0" border="0"
2141 style="display: block; width: 100%; height: 150px; overflow-y: scroll; hidden; border: 1px solid #ccc; border-top: none; border-bottom: none; <?php if( ! $user['custom_points'] ) echo 'opacity:0.5;' ?>">
2142 <tr>
2143 <th style="text-align: center; width: 5%;">#</th>
2144 <th style="width: 30%"><?php _e( 'Rating Level', 'wpforo' ); ?></th>
2145 <th style="width: 35%"><?php _e( 'Rating Title', 'wpforo' ); ?></th>
2146 <th style="width: 30%;text-align:center;"><?php _e( 'Rating Badge', 'wpforo' ); ?></th>
2147 </tr>
2148 <?php $levels = WPF()->member->levels(); ?>
2149 <?php foreach( $levels as $level ): $points = WPF()->member->rating( $level, 'points' );
2150 $bgx = $level === $user['rating']['level'] ? 'background-color: #feff88' : ''; ?>
2151 <tr>
2152 <td style="text-align: center;<?php echo $bgx ?>"><input type="radio"
2153 name="wpforo_member_custom_points"
2154 id="wpf-user-rating-<?php echo intval(
2155 $points
2156 ) ?>"
2157 value="<?php echo intval(
2158 $points
2159 ) ?>" <?php checked(
2160 $level === $user['rating']['level']
2161 ) ?>></td>
2162 <td style="<?php echo $bgx ?>"><h4><label
2163 for="wpf-user-rating-<?php echo intval( $points ) ?>"><?php _e(
2164 'Level',
2165 'wpforo'
2166 ); ?><?php echo esc_html( $level ) ?></label>
2167 </h4></td>
2168 <td style="<?php echo $bgx ?>"><?php echo WPF()->member->rating(
2169 $level,
2170 'title'
2171 ) ?></td>
2172 <td style="text-align:center;<?php echo $bgx ?>">
2173 <div class="wpf-badge-full wpf-badge-level-<?php echo esc_attr( $level ) ?>"
2174 style="color:<?php echo WPF()->member->rating(
2175 $level,
2176 'color'
2177 ) ?>;"><?php echo WPF()->member->rating_badge( $level, 'full' ); ?></div>
2178 </td>
2179 </tr>
2180 <?php endforeach; ?>
2181 </table>
2182 </td>
2183 </tr>
2184 <?php endif; ?>
2185 <tr>
2186 <td colspan="2" style="padding: 5px;"></td>
2187 </tr>
2188 </table>
2189 <script>
2190 jQuery(document).ready(function ($) {
2191 $('#wpf-user-rating-default').change(function () {if (this.checked) {$('#wpf-rating-table').css('opacity', 0.5);}});
2192 $('#wpf-user-rating-custom').change(function () {if (this.checked) { $('#wpf-rating-table').css('opacity', 1);}});
2193 });
2194 </script>
2195 <?php
2196 }
2197 }
2198
2199 add_action( 'user_new_form', 'wpforo_userform_to_wpuser_html_form' );
2200 add_action( 'show_user_profile', 'wpforo_userform_to_wpuser_html_form' );
2201 add_action( 'edit_user_profile', 'wpforo_userform_to_wpuser_html_form' );
2202
2203 add_action( 'register_form', function() {
2204 wp_nonce_field( 'wpforo_user_register', '_wpfnonce' );
2205 }, 21 );
2206
2207 function wpforo_do_hook_user_register( $userid ) {
2208 WPF()->member->synchronize_user( $userid );
2209 if( wpfval( $_POST, 'wpfreg' ) ) {
2210 $data = $_POST;
2211 $data['userid'] = $userid;
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( [
2217 'user_login',
2218 'user_email',
2219 'user_pass1',
2220 'user_pass2',
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
2243 WPF()->member->update( $data, 'full', false );
2244 }
2245 }
2246
2247 add_action( 'user_register', 'wpforo_do_hook_user_register', 10, 1 );
2248
2249 function wpforo_do_hook_update_profile( $userid ) {
2250 if( $userid ) {
2251 if( current_user_can( 'create_users' ) || current_user_can( 'edit_user' ) ) {
2252 if( wpfval( $_POST, 'wpforo_usergroup' ) || wpfkey( $_POST, 'wpforo_usertimezone' ) ) {
2253 $member = wpforo_member( $userid );
2254 $can_change_own_rating = true;
2255 if( wpfval( $member, 'userid' ) ) {
2256 $groupid = $member['groupid'];
2257 $secondary_groupids = $member['secondary_groupids'];
2258 if( current_user_can( 'administrator' ) ) {
2259 if( wpfval( $_POST, 'wpforo_usergroup' ) ) {
2260 if( $userid != 1 && ! wpforo_is_owner( $userid ) && current_user_can( 'administrator' ) ) {
2261 $groupid = intval( $_POST['wpforo_usergroup'] );
2262 }
2263 }
2264 if( wpfval( $_POST, 'wpforo_secondary_groupids' ) ) {
2265 if( ! empty( $_POST['wpforo_secondary_groupids'] ) ) {
2266 $secondary_groupids = array_filter(
2267 array_map( 'intval', $_POST['wpforo_secondary_groupids'] )
2268 );
2269 }
2270 }
2271 } else {
2272 if( wpforo_is_owner( $userid ) ) {
2273 $can_change_own_rating = false;
2274 }
2275 }
2276
2277 $custom_points = ( $can_change_own_rating && wpfval(
2278 $_POST,
2279 'wpforo_use_member_custom_points'
2280 ) ) ? (int) wpfval( $_POST, 'wpforo_member_custom_points' ) : 0;
2281
2282 $args = [
2283 'groupid' => intval( $groupid ),
2284 'about' => wpforo_kses( $_POST['description'], 'user_description' ),
2285 'timezone' => ( isset( $_POST['wpforo_usertimezone'] ) ? sanitize_text_field(
2286 $_POST['wpforo_usertimezone']
2287 ) : '' ),
2288 'secondary_groupids' => $secondary_groupids,
2289 'custom_points' => $custom_points,
2290 ];
2291 WPF()->member->update_profile_fields( $userid, $args, false );
2292
2293 if( ! wpforo_is_owner( $userid ) ) {
2294 WPF()->member->inactive_to_active( $userid );
2295 }
2296 }
2297 }
2298 }
2299 }
2300 }
2301
2302 add_action( 'personal_options_update', 'wpforo_do_hook_update_profile' );
2303 add_action( 'edit_user_profile_update', 'wpforo_do_hook_update_profile' );
2304
2305 function wpforo_avatar( $avatar, $id_or_email, $size, $default, $alt ) {
2306 if( ! wpforo_setting( 'profiles', 'replace_avatar' ) ) {
2307 return $avatar;
2308 }
2309 $user = false;
2310 if( wpforo_is_id( $id_or_email ) ) {
2311 $id = (int) $id_or_email;
2312 $user = get_user_by( 'id', $id );
2313 } elseif( is_object( $id_or_email ) ) {
2314 if( ! empty( $id_or_email->user_id ) ) {
2315 $id = (int) $id_or_email->user_id;
2316 $user = get_user_by( 'id', $id );
2317 } elseif( ! empty( $id_or_email->ID ) ) {
2318 $id = (int) $id_or_email->ID;
2319 $user = get_user_by( 'id', $id );
2320 }
2321 } else {
2322 $user = get_user_by( 'email', $id_or_email );
2323 }
2324
2325 if( $user && is_object( $user ) ) {
2326 if( $src = WPF()->member->get_avatar_url( $user->data->ID ) ) {
2327 $avatar = "<img alt='" . esc_attr( $alt ) . "' src='" . esc_url(
2328 (string) $src
2329 ) . "' class='avatar avatar-" . esc_attr( $size ) . " photo' height='" . esc_attr(
2330 $size
2331 ) . "' width='" . esc_attr( $size ) . "' />";
2332 }
2333 }
2334
2335 return $avatar;
2336 }
2337
2338 add_filter( 'get_avatar', 'wpforo_avatar', 10, 5 );
2339
2340 function wpforo_pre_get_avatar_data( $args, $id_or_email ) {
2341 if( wpforo_setting( 'profiles', 'replace_avatar' ) ) {
2342 $key = [ 'wpforo_pre_get_avatar_data', $id_or_email ];
2343 if( WPF()->ram_cache->exists( $key ) ) {
2344 $args['url'] = WPF()->ram_cache->get( $key );
2345 } else {
2346 $user = false;
2347 if( is_object( $id_or_email ) && isset( $id_or_email->comment_ID ) ) {
2348 $id_or_email = get_comment( $id_or_email );
2349 }
2350 // Process the user identifier.
2351 if( wpforo_is_id( $id_or_email ) ) {
2352 $user = get_user_by( 'id', absint( $id_or_email ) );
2353 } elseif( is_string( $id_or_email ) ) {
2354 if( ! strpos( (string) $id_or_email, '@md5.gravatar.com' ) ) {
2355 $user = get_user_by( 'email', $id_or_email );
2356 }
2357 } elseif( $id_or_email instanceof WP_User ) {
2358 // User Object
2359 $user = $id_or_email;
2360 } elseif( $id_or_email instanceof WP_Post ) {
2361 // Post Object
2362 $user = get_user_by( 'id', (int) $id_or_email->post_author );
2363 } elseif( function_exists( 'is_avatar_comment_type' ) && $id_or_email instanceof WP_Comment ) {
2364 if( is_avatar_comment_type( get_comment_type( $id_or_email ) ) ) {
2365 if( ! empty( $id_or_email->user_id ) ) {
2366 $user = get_user_by( 'id', (int) $id_or_email->user_id );
2367 }
2368 if( ( ! $user || is_wp_error( $user ) ) && ! empty( $id_or_email->comment_author_email ) ) {
2369 $user = get_user_by( 'email', $id_or_email->comment_author_email );
2370 }
2371 }
2372 }
2373
2374 if( $user && is_object( $user ) ) {
2375 if( $avatar_url = WPF()->member->get_avatar_url( $user->data->ID ) ) {
2376 WPF()->ram_cache->set( $key, $avatar_url );
2377 $args['url'] = $avatar_url;
2378 }
2379 }
2380 }
2381 }
2382
2383 return $args;
2384 }
2385
2386 add_filter( 'pre_get_avatar_data', 'wpforo_pre_get_avatar_data', 10, 2 );
2387
2388 function wpforo_move_uploded_default_attach( $argname, $return = 'html' ) {
2389 if( ! empty( $_FILES[ $argname ] ) && ! empty( $_FILES[ $argname ]['name'] ) ) {
2390 $name = sanitize_file_name( $_FILES[ $argname ]['name'] ); //myimg.png
2391 $type = sanitize_mime_type( $_FILES[ $argname ]['type'] ); //image/png
2392 $tmp_name = sanitize_text_field( $_FILES[ $argname ]['tmp_name'] ); //D:\wamp\tmp\php986B.tmp
2393 $error = intval( $_FILES[ $argname ]['error'] ); //0
2394 $size = intval( $_FILES[ $argname ]['size'] ); //6112
2395
2396 $phpFileUploadErrors = [
2397 0 => 'There is no error, the file uploaded with success',
2398 1 => 'The uploaded file size is too big',
2399 2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
2400 3 => 'The uploaded file was only partially uploaded',
2401 4 => 'No file was uploaded',
2402 6 => 'Missing a temporary folder',
2403 7 => 'Failed to write file to disk.',
2404 8 => 'A PHP extension stopped the file upload.',
2405 ];
2406
2407 if( $error ) {
2408 WPF()->notice->add( $phpFileUploadErrors[ $error ], 'error' );
2409
2410 return '';
2411 } elseif( $size > wpforo_setting( 'posting', 'max_upload_size' ) ) {
2412 WPF()->notice->add( 'The uploaded file size is too big', 'error' );
2413
2414 return '';
2415 }
2416
2417 if( function_exists( 'pathinfo' ) ) {
2418 $ext = pathinfo( $name, PATHINFO_EXTENSION );
2419 } else {
2420 $ext = substr( strrchr( (string) $name, '.' ), 1 );
2421 }
2422 $ext = strtolower( (string) $ext );
2423 $mime_types = get_allowed_mime_types();
2424 $mime_types = array_flip( $mime_types );
2425 if( ! empty( $mime_types ) ) {
2426 $allowed_types = implode( '|', $mime_types );
2427 $expld = explode( '|', $allowed_types );
2428 if( ! in_array( $ext, $expld ) ) {
2429 WPF()->notice->add( 'File type is not allowed', 'error' );
2430
2431 return '';
2432 }
2433 if( ! WPF()->perm->can_attach_file_type( $ext ) ) {
2434 WPF()->notice->add( 'You are not allowed to attach this file type', 'error' );
2435
2436 return '';
2437 }
2438 }
2439
2440 $attach_dir = WPF()->folders['default_attachments']['dir'];
2441 $attach_url = WPF()->folders['default_attachments']['url//'];
2442 if( ! is_dir( $attach_dir ) ) wp_mkdir_p( $attach_dir );
2443
2444 $fnm = pathinfo( $name, PATHINFO_FILENAME );
2445 $fnm = str_replace( ' ', '-', $fnm );
2446 while( strpos( (string) $fnm, '--' ) !== false ) {
2447 $fnm = str_replace( '--', '-', $fnm );
2448 }
2449 $fnm = preg_replace( "/[^-a-zA-Z0-9_]/", "", (string) $fnm );
2450 $fnm = trim( (string) $fnm, "-" );
2451 $fnm_empty = ! $fnm;
2452
2453 $file_name = $fnm . "." . $ext;
2454
2455 $attach_fname = time() . ( ! $fnm_empty ? '-' : '' ) . $file_name;
2456 $attach_path = $attach_dir . DIRECTORY_SEPARATOR . $attach_fname;
2457
2458 if( is_dir( $attach_dir ) && move_uploaded_file( $tmp_name, $attach_path ) ) {
2459 $attach_id = wpforo_insert_to_media_library( $attach_path, $fnm );
2460 if( $return === 'html' ) {
2461 return "\r\n" . '<div id="wpfa-' . $attach_id . '" class="wpforo-attached-file"><a class="wpforo-default-attachment" href="' . esc_url(
2462 (string) $attach_url . '/' . $attach_fname
2463 ) . '" target="_blank" title="' . esc_attr(
2464 basename( (string) $name )
2465 ) . '"><i class="fas fa-paperclip"></i>&nbsp;' . esc_html(
2466 basename( (string) $name )
2467 ) . '</a></div>';
2468 } else {
2469 return [
2470 'fileurl' => $attach_url . '/' . $attach_fname,
2471 'filename' => basename( (string) $name ),
2472 'mediaid' => $attach_id,
2473 ];
2474 }
2475 } else {
2476 WPF()->notice->add( 'Can\'t upload file', 'error' );
2477 }
2478 }
2479
2480 return '';
2481 }
2482
2483 function wpforo_add_default_attachment( $args ) {
2484 if( ! empty( $_FILES['attachfile'] ) && ! empty( $_FILES['attachfile']['name'] ) ) {
2485 if( WPF()->perm->can_attach( wpfval( $args, 'forumid' ) ) ) {
2486 if( $default_attach = wpforo_move_uploded_default_attach( 'attachfile' ) ) {
2487 $args['body'] .= $default_attach;
2488 $args['has_attach'] = 1;
2489 }
2490 }
2491 }
2492
2493 return $args;
2494 }
2495
2496 function wpforo_delete_attachment( $attach_post_id ) {
2497 if( ! $attach_post_id ) {
2498 return;
2499 }
2500 $posts = WPF()->db->get_results(
2501 "SELECT `postid`, `body` FROM `" . WPF()->tables->posts . "` WHERE `body` LIKE '%wpfa-" . intval(
2502 $attach_post_id
2503 ) . "%'",
2504 ARRAY_A
2505 );
2506 if( ! empty( $posts ) || is_array( $posts ) ) {
2507 foreach( $posts as $post ) {
2508 $body = preg_replace(
2509 '|<div[^><]*id=[\'\"]+wpfa-' . $attach_post_id . '[\'\"]+[^><]*>.+?</div>|is',
2510 '<div class="wpforo-attached-file wpfa-deleted">' . wpforo_phrase(
2511 'Attachment removed',
2512 false
2513 ) . '</div>',
2514 (string) $post['body']
2515 );
2516 if( $body ) {
2517 WPF()->db->query(
2518 "UPDATE `" . WPF()->tables->posts . "` SET `body` = '" . esc_sql(
2519 $body
2520 ) . "' WHERE `postid` = " . intval( $post['postid'] )
2521 );
2522 }
2523 }
2524 }
2525 }
2526
2527 function wpforo_default_attachments_filter( $text ) {
2528 if( preg_match_all(
2529 '#<a[^<>]*class=[\'"]wpforo-default-attachment[\'"][^<>]*href=[\'"]([^\'"]+)[\'"][^<>]*>[\r\n\t\s\0]*(?:<i[^<>]*>[\r\n\t\s\0]*</i>[\r\n\t\s\0]*)?([^<>]*)</a>#iu',
2530 (string) $text,
2531 $matches,
2532 PREG_SET_ORDER
2533 ) ) {
2534 foreach( $matches as $match ) {
2535 $attach_html = '';
2536 if( file_exists( wpforo_fix_upload_dir( $match[1] ) ) ) {
2537 if( ! WPF()->perm->forum_can( 'va' ) || ! WPF()->usergroup->can( 'caa' ) ) {
2538 $attach_html .= '<br/><div class="wpfa-item wpfa-file"><a class="attach_cant_view" style="cursor: pointer;"><span style="color: #666;">' . wpforo_phrase(
2539 'Attachment',
2540 false
2541 ) . ' : </span> ' . urldecode( basename( $match[2] ) ) . '</a></div>';
2542 }
2543 }
2544 if( $attach_html ) {
2545 $attach_html .= '<br/>';
2546 $text = str_replace( $match[0], $attach_html, $text );
2547 }
2548 }
2549 }
2550
2551 return $text;
2552 }
2553
2554 function wpforo_content_enable_do_shortcode() {
2555 if( wpforo_setting( 'posting', 'content_do_shortcode' ) ) {
2556 add_filter( 'wpforo_content_after', 'do_shortcode', 20 );
2557 }
2558 }
2559
2560 add_action( 'wpforo_after_init', 'wpforo_content_enable_do_shortcode' );
2561
2562 add_filter( 'wpforo_content_after', function( $text ) {
2563 return wpforo_strip_shortcodes( $text, true );
2564 }, 999 );
2565
2566 add_filter( 'wpforo_body_text_filter', function( $text ) {
2567 if( apply_filters( 'wpforo_allow_replace_3asterisk', true ) ) {
2568 $text = preg_replace(
2569 '#\*{3}([^*]+?)\*{3}#',
2570 '<span style="color: red; font-weight: bold;">$1</span>',
2571 (string) $text
2572 );
2573 }
2574
2575 return $text;
2576 } );
2577
2578 add_action( 'wp_footer', function() { WPF()->cache->create(); } );
2579
2580 add_filter( 'retrieve_password_message', function( $message, $key, $user_login, $user_data ) {
2581 $reset_password_url = '';
2582 if( preg_match( wpforo_get_wprp_url_pattern(), (string) $message, $match ) ) {
2583 if( wpforo_setting( 'authorization', 'use_our_lostpassword_url' ) ) {
2584 $reset_password_url = wpforo_resetpassword_url( $key, $user_login );
2585 $message = str_replace( $match[0], $reset_password_url, $message );
2586 } else {
2587 $reset_password_url = $match[0];
2588 }
2589 }
2590
2591 if( wpforo_setting( 'email', 'overwrite_reset_password_email' ) && $reset_password_url ) {
2592 $message = str_replace(
2593 [ '[user_login]', '[reset_password_url]' ],
2594 [ $user_login, $reset_password_url, ],
2595 wpforo_setting( 'email', 'reset_password_email_message' )
2596 );
2597 $message = _wpforo_apply_email_shortcodes( $message, [ 'user' => [ 'userid' => $user_data->ID ] ] );
2598 add_filter( 'wp_mail_content_type', '__wpforo_set_html_content_type', 999 );
2599 }
2600
2601 return $message;
2602 }, 999, 4 );
2603
2604 function wpforo_user_field_shortcode_to_value( $shortcode, $userid = null, $implode = true, $separator = ',' ) {
2605 $value = null;
2606
2607 if( $shortcode && ( $field = preg_replace(
2608 '#^\s*\[?\s*(?:user_|owner_)?(?:fields_)?([^\[\]]+?)\s*]?\s*$#iu',
2609 '$1',
2610 (string) $shortcode
2611 ) ) ) {
2612 if( ! $userid ) $userid = WPF()->current_userid;
2613 if( in_array(
2614 $field,
2615 [ 'login', 'pass', 'nicename', 'email', 'url', 'registered', 'activation_key', 'status' ],
2616 true
2617 ) ) {
2618 $field = 'user_' . $field;
2619 }
2620 $value = wpforo_member( $userid, $field );
2621 if( $implode && wpforo_is_array_of_scalars( $value ) ) $value = implode( $separator, $value );
2622 }
2623
2624 return $value;
2625 }
2626
2627 function wpforo_forum_field_shortcode_to_value( $shortcode, $forumid, $implode = true, $separator = ',' ) {
2628 $value = null;
2629
2630 if( $forumid ) {
2631 if( $shortcode && ( $field = preg_replace(
2632 '#^\s*\[?\s*(?:forum_)?(?:fields_)?([^\[\]]+?)\s*]?\s*$#iu',
2633 '$1',
2634 (string) $shortcode
2635 ) ) ) {
2636 if( $field === 'link' ) {
2637 $value = sprintf(
2638 '<a target="_blank" href="%1$s">%2$s</a>',
2639 esc_url( (string) _wpforo_forum( $forumid, 'url' ) ),
2640 sanitize_text_field( _wpforo_forum( $forumid, 'title' ) )
2641 );
2642 $value = stripslashes( (string) $value );
2643 } else {
2644 $value = _wpforo_forum( $forumid, $field );
2645 if( $implode && wpforo_is_array_of_scalars( $value ) ) $value = implode( $separator, $value );
2646 }
2647 }
2648 }
2649
2650 return $value;
2651 }
2652
2653 function wpforo_topic_field_shortcode_to_value( $shortcode, $topicid, $implode = true, $separator = ',' ) {
2654 $value = null;
2655
2656 if( $topicid ) {
2657 if( $shortcode && ( $field = preg_replace(
2658 '#^\s*\[?\s*(?:topic_)?(?:fields_)?([^\[\]]+?)\s*]?\s*$#iu',
2659 '$1',
2660 (string) $shortcode
2661 ) ) ) {
2662 if( $field === 'link' ) {
2663 $value = sprintf(
2664 '<a target="_blank" href="%1$s">%2$s</a>',
2665 esc_url( (string) _wpforo_topic( $topicid, 'short_url' ) ),
2666 sanitize_text_field( _wpforo_topic( $topicid, 'title' ) )
2667 );
2668 $value = stripslashes( $value );
2669 } else {
2670 $value = _wpforo_topic( $topicid, $field );
2671 if( $implode && wpforo_is_array_of_scalars( $value ) ) $value = implode( $separator, $value );
2672 }
2673 }
2674 }
2675
2676 return $value;
2677 }
2678
2679 function wpforo_post_field_shortcode_to_value( $shortcode, $postid, $implode = true, $separator = ',' ) {
2680 $value = null;
2681
2682 if( $postid ) {
2683 if( $shortcode && ( $field = preg_replace(
2684 '#^\s*\[?\s*(?:post_)?(?:fields_)?([^\[\]]+?)\s*]?\s*$#iu',
2685 '$1',
2686 (string) $shortcode
2687 ) ) ) {
2688 if( $field === 'link' ) {
2689 $value = sprintf(
2690 '<a target="_blank" href="%1$s">%2$s</a>',
2691 esc_url( (string) _wpforo_post( $postid, 'short_url' ) ),
2692 sanitize_text_field( _wpforo_post( $postid, 'title' ) )
2693 );
2694 $value = stripslashes( $value );
2695 } else {
2696 $value = _wpforo_post( $postid, $field );
2697 if( $implode && wpforo_is_array_of_scalars( $value ) ) $value = implode( $separator, $value );
2698 }
2699 }
2700 }
2701
2702 return $value;
2703 }
2704
2705 function wpforo_new_user_notification_email_admin( $wp_new_user_notification_email_admin, $user, $blogname ) {
2706 if( wpforo_setting( 'email', 'overwrite_new_user_notification_admin' ) ) {
2707 $wp_new_user_notification_email_admin['subject'] = str_replace(
2708 '[blogname]',
2709 '[' . $blogname . ']',
2710 wpforo_setting(
2711 'email',
2712 'wp_new_user_notification_email_admin_subject'
2713 )
2714 );
2715 $wp_new_user_notification_email_admin['message'] = str_replace(
2716 '[blogname]',
2717 $blogname,
2718 wpforo_setting(
2719 'email',
2720 'wp_new_user_notification_email_admin_message'
2721 )
2722 );
2723 add_filter( 'wp_mail_content_type', '__wpforo_set_html_content_type', 999 );
2724 }
2725 $userid = $user->ID;
2726 $wp_new_user_notification_email_admin['message'] = preg_replace_callback(
2727 '#\[[^\[\]]+?]#isu',
2728 function( $match ) use ( $userid ) {
2729 $value = wpforo_user_field_shortcode_to_value( $match[0], $userid );
2730 if( ! $value || ! ( is_string( $value ) || is_numeric( $value ) ) ) $value = '';
2731
2732 return $value;
2733 },
2734 (string) $wp_new_user_notification_email_admin['message']
2735 );
2736
2737 return $wp_new_user_notification_email_admin;
2738 }
2739
2740 add_filter( 'wp_new_user_notification_email_admin', 'wpforo_new_user_notification_email_admin', 999, 3 );
2741
2742 function wpforo_new_user_notification_email( $wp_new_user_notification_email, $user, $blogname ) {
2743 $set_password_url = '';
2744 if( preg_match( wpforo_get_wprp_url_pattern(), (string) $wp_new_user_notification_email['message'], $match ) ) {
2745 if( wpforo_setting( 'authorization', 'use_our_lostpassword_url' ) ) {
2746 $set_password_url = wpforo_resetpassword_url( $match['key'], $user->user_login );
2747 $wp_new_user_notification_email['message'] = str_replace(
2748 $match[0],
2749 $set_password_url,
2750 $wp_new_user_notification_email['message']
2751 );
2752 } else {
2753 $set_password_url = $match[0];
2754 }
2755 }
2756
2757 if( wpforo_setting( 'email', 'overwrite_new_user_notification' ) && $set_password_url ) {
2758 $wp_new_user_notification_email['subject'] = str_replace(
2759 '[blogname]',
2760 '[' . $blogname . ']',
2761 wpforo_setting(
2762 'email',
2763 'wp_new_user_notification_email_subject'
2764 )
2765 );
2766 $wp_new_user_notification_email['message'] = str_replace(
2767 [ '[user_login]', '[set_password_url]' ],
2768 [ $user->user_login, $set_password_url ],
2769 wpforo_setting( 'email', 'wp_new_user_notification_email_message' )
2770 );
2771 add_filter( 'wp_mail_content_type', '__wpforo_set_html_content_type', 999 );
2772 }
2773
2774 return $wp_new_user_notification_email;
2775 }
2776
2777 add_filter( 'wp_new_user_notification_email', 'wpforo_new_user_notification_email', 999, 3 );
2778
2779 function wpforo_get_wprp_url_pattern() {
2780 return '#(?:<\s*)?https?://\S+?wp-login\.php(?:[?&]action=rp|[?&]login=[^?&=\s]+)?[?&]key=(?<key>[^?&=\s]+)(?:[?&]action=rp|[?&]login=[^?&=\s]+)?(?:\s*>)?#isu';
2781 }
2782
2783 function wpforo_synch_user_roles( $userid ) {
2784
2785 $user = get_userdata( $userid );
2786
2787 if( ! empty( $user ) && ! empty( $user->roles ) ) {
2788 $role_usergroups = WPF()->usergroup->get_role_usergroup_relation();
2789
2790 if( ! empty( $role_usergroups ) ) {
2791 $append_second_groups = apply_filters( 'wpforo_synch_user_roles_append_secondary_groups', false );
2792 $ug_users_array = WPF()->usergroup->build_users_groupid_array( $role_usergroups, [ $user ] );
2793
2794 if( wpfval( $ug_users_array, 'user_prime_group' ) ) {
2795 $userid_groupids = current( $ug_users_array['user_prime_group'] );
2796 if( ! empty( $userid_groupids ) ) {
2797 $groupid = current( $userid_groupids );
2798 if( $groupid ) WPF()->member->set_groupid( $userid, $groupid );
2799 }
2800 }
2801
2802 if( wpfval( $ug_users_array, 'user_second_groups' ) ) {
2803 foreach( $ug_users_array['user_second_groups'] as $userid => $secondary_usergroups ) {
2804 if( ! empty( $secondary_usergroups ) ) {
2805 if( $append_second_groups ) {
2806 // Merges current secondary groups with new secondary groups
2807 WPF()->member->append_secondary_groupids( $userid, $secondary_usergroups );
2808 } else {
2809 // Removes current secondary groups and sets new secondary groups
2810 WPF()->member->set_secondary_groupids( $userid, $secondary_usergroups );
2811 }
2812 }
2813 }
2814 } elseif( ! $append_second_groups && ! wpfval( $ug_users_array, 'user_second_groups' ) ) {
2815 WPF()->member->set_secondary_groupids( $userid, [] );
2816 }
2817
2818 delete_user_meta( intval( $userid ), '_wpf_member_obj' );
2819 }
2820 }
2821 }
2822
2823
2824 function wpforo_synch_roles() {
2825 if( ! current_user_can( 'manage_options' ) ) {
2826 wp_send_json_error( [ 'message' => 'Permission denied' ] );
2827 }
2828
2829 $status = [
2830 'progress' => 0,
2831 'error' => 0,
2832 'start' => 0,
2833 'step' => 1,
2834 'left' => 0,
2835 'total' => 0,
2836 'id' => 0,
2837 ];
2838
2839 $wpforo_synch_roles_data = isset( $_POST['wpforo_synch_roles_data'] ) ? $_POST['wpforo_synch_roles_data'] : '';
2840
2841 if( $wpforo_synch_roles_data ) {
2842 parse_str( $wpforo_synch_roles_data, $data );
2843 check_ajax_referer( 'wpforo_synch_roles', 'checkthis' );
2844 $limit = apply_filters( 'wpforo_synch_roles_step_limit', 20 );
2845 $append_second_groups = apply_filters( 'wpforo_synch_roles_append_secondary_groups', true );
2846 $success = false;
2847 $group_users = [];
2848 $user_prime_group = [];
2849 $user_second_groups = [];
2850 $options = wpforo_get_option( 'synch_roles' );
2851 $step = isset( $data['wpf-step'] ) ? intval( $data['wpf-step'] ) : 1;
2852 $left = isset( $data['wpf-left-users'] ) ? intval( $data['wpf-left-users'] ) : 0;
2853 $id = isset( $data['wpf-start-id'] ) ? intval( $data['wpf-start-id'] ) : 0;
2854 $start = isset( $data['wpf-start'] ) ? intval( $data['wpf-start'] ) : 0;
2855 $ug_role = isset( $data['wpf_synch_roles'] ) ? $data['wpf_synch_roles'] : [];
2856 if( ! empty( $ug_role ) ) {
2857 /////////////////////////////////////////////////////
2858 /// Update Roles of Usergroups in usergroups table
2859 WPF()->usergroup->set_ug_roles( $ug_role );
2860 /////////////////////////////////////////////////////
2861 if( ! is_array( $options ) || $left > 0 ) {
2862 $args = [ 'orderby' => 'ID', 'order' => 'ASC', 'offset' => $start, 'number' => $limit ];
2863 $users = get_users( $args );
2864 ////////////////////////////////////////////////////////////////////////////////////
2865 /// Builds associative array of Usergroup ID => Users ID array()
2866 $ug_users_array = WPF()->usergroup->build_users_groupid_array( $ug_role, $users, true );
2867 ////////////////////////////////////////////////////////////////////////////////////
2868 if( wpfval( $ug_users_array, 'group_users' ) ) {
2869 $group_users = $ug_users_array['group_users'];
2870 }
2871 if( wpfval( $ug_users_array, 'user_prime_group' ) ) {
2872 $user_prime_group = $ug_users_array['user_prime_group'];
2873 }
2874 if( wpfval( $ug_users_array, 'user_second_groups' ) ) {
2875 foreach( $ug_users_array['user_second_groups'] as $userid => $secondary_usergroups ) {
2876 if( ! empty( $secondary_usergroups ) ) {
2877 if( $append_second_groups ) {
2878 // Merges current secondary groups with new secondary groups
2879 WPF()->member->append_secondary_groupids( $userid, $secondary_usergroups );
2880 } else {
2881 // Removes current secondary groups and sets new secondary groups
2882 WPF()->member->set_secondary_groupids( $userid, $secondary_usergroups );
2883 }
2884 }
2885 }
2886 }
2887 if( ! empty( $group_users ) ) {
2888 /////////////////////////////////////////////////////////////////
2889 /// Updates users Usergroup Ids in profiles table
2890 $return = WPF()->usergroup->set_users_groupid( $group_users );
2891 /////////////////////////////////////////////////////////////////
2892 $success = ( wpfval( $return, 'success' ) ) ? $return['success'] : $success;
2893 $status['error'] = ( wpfval( $return, 'error' ) ) ? $return['error'] : $status['error'];
2894 if( $success ) {
2895 end( $users );
2896 $key = key( $users );
2897 $status['id'] = $users[ $key ]->ID;
2898 $result = count_users();
2899 $status['total'] = ( wpfval( $result, 'total_users' ) ) ? intval(
2900 $result['total_users']
2901 ) : 0;
2902 $status['start'] = $step * $limit;
2903 $status['left'] = ( $status['total'] > $status['start'] ) ? ( $status['total'] - $status['start'] ) : 0;
2904 $status['step'] = $step + 1;
2905 $progress = ( $status['total'] > $status['start'] ) ? ( $status['start'] * 100 ) / $status['total'] : 100;
2906 $status['progress'] = round( $progress );
2907 if( $progress == 100 ) {
2908 wpforo_delete_option( 'synch_roles' );
2909 } else {
2910 wpforo_update_option( 'synch_roles', $status );
2911 }
2912 }
2913 } else {
2914 $status['total'] = 0;
2915 $status['start'] = 0;
2916 $status['left'] = 0;
2917 $status['step'] = 1;
2918 $status['progress'] = 100;
2919 wpforo_delete_option( 'synch_roles' );
2920 }
2921 } else {
2922 $result = count_users();
2923 $status['total'] = ( wpfval( $result, 'total_users' ) ) ? intval( $result['total_users'] ) : 0;
2924 $status['start'] = $step * $limit;
2925 $status['left'] = 0;
2926 $status['step'] = 1;
2927 $status['progress'] = 100;
2928 wpforo_delete_option( 'synch_roles' );
2929 }
2930 }
2931 }
2932
2933 if( intval( wpfval( $status, 'progress' ) ) === 100 ) {
2934 WPF()->notice->add( 'Role-Usergroup synchronization is complete!', 'success' );
2935 }
2936
2937 wp_die( wp_json_encode( $status ) );
2938 }
2939
2940 add_action( 'wp_ajax_wpforo_synch_roles', 'wpforo_synch_roles' );
2941
2942
2943 function wpforo_update_usergroup_on_role_change( $userid, $new_role, $old_roles = [] ) {
2944 if( wpforo_setting( 'authorization', 'role_synch' ) ) {
2945 wpforo_synch_user_roles( $userid );
2946 }
2947 }
2948
2949 add_action( 'add_user_role', 'wpforo_update_usergroup_on_role_change', 99, 2 );
2950 add_action( 'set_user_role', 'wpforo_update_usergroup_on_role_change', 99, 3 );
2951
2952
2953 function wpforo_wp_admin_bar_red_circle_number( $number ) {
2954 $number = intval( $number );
2955 if( $number ) {
2956 return sprintf(
2957 '<div class="wp-core-ui wp-ui-notification wpforo-menu-notification-counter">%1$d</div>',
2958 $number
2959 );
2960 }
2961
2962 return '';
2963 }
2964
2965 function wpforo_get_memb_attention_count(): int {
2966 if( is_null( WPF()->member ) ) return 0;
2967 return WPF()->member->get_count( [ 'p.status' => [ 'banned', 'inactive' ] ] );
2968 }
2969
2970 function wpforo_get_admin_bar_numbers(): array {
2971 $all_count = 0;
2972 $mod_count = is_null( WPF()->post ) ? 0 : WPF()->post->unapproved_count();
2973 $all_count += $mod_count;
2974
2975 return compact( 'all_count', 'mod_count' );
2976 }
2977
2978 function wpforo_admin_bar_menu( $wp_admin_bar ) {
2979 $admin_bar_numbers = wpforo_get_admin_bar_numbers();
2980
2981 if( wpforo_current_user_is( 'admin' ) ) {
2982 $args = [
2983 'id' => 'new-forum',
2984 'title' => '&#43;&nbsp;&nbsp;' . __( 'Add New Forum', 'wpforo' ),
2985 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) . '&action=add' ),
2986 'parent' => 'new-content',
2987 ];
2988 $wp_admin_bar->add_node( $args );
2989 $args = [
2990 'id' => 'new-ugroup',
2991 'title' => '&#43;&nbsp;&nbsp;' . __( 'Add New User Group', 'wpforo' ),
2992 'href' => admin_url(
2993 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) . '&wpfaction=wpforo_usergroup_save_form'
2994 ),
2995 'parent' => 'new-content',
2996 ];
2997 $wp_admin_bar->add_node( $args );
2998 $args = [
2999 'id' => 'new-phrase',
3000 'title' => '&#43;&nbsp;&nbsp;' . __( 'Add New Phrase', 'wpforo' ),
3001 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'phrases' ) . '&wpfaction=phrase_add_form' ),
3002 'parent' => 'new-content',
3003 ];
3004 $wp_admin_bar->add_node( $args );
3005 }
3006
3007 $args = [
3008 'id' => 'wpforo-home',
3009 'title' => __( 'Visit Forum', 'wpforo' ),
3010 'href' => wpforo_home_url(),
3011 'parent' => 'wpf-community',
3012 // 'meta' => [ 'target' => '_blank' ]
3013 ];
3014 $wp_admin_bar->add_node( $args );
3015
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(
3017 'aum'
3018 ) || WPF()->usergroup->can( 'vmg' ) || WPF()->usergroup->can( 'mth' ) ) {
3019 $args = [
3020 'id' => 'wpf-community',
3021 'title' => __( 'Forum Dashboard', 'wpforo' ) . wpforo_wp_admin_bar_red_circle_number(
3022 $admin_bar_numbers['all_count']
3023 ),
3024 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) ),
3025 ];
3026 $wp_admin_bar->add_node( $args );
3027 }
3028 if( WPF()->usergroup->can( 'mf' ) || wpforo_current_user_is( 'admin' ) ) {
3029 $args = [
3030 'id' => 'wpf-forums',
3031 'title' => '' . __( 'Forums', 'wpforo' ),
3032 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) ),
3033 'parent' => 'wpf-community',
3034 ];
3035 $wp_admin_bar->add_node( $args );
3036 $args = [
3037 'id' => 'wpf-new-forum',
3038 'title' => '' . __( 'Add New Forum', 'wpforo' ),
3039 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) . '&action=add' ),
3040 'parent' => 'wpf-forums',
3041 ];
3042 $wp_admin_bar->add_node( $args );
3043 }
3044 if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
3045 $args = [
3046 'id' => 'wpf-settings',
3047 'title' => '' . __( 'Settings', 'wpforo' ),
3048 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'settings' ) ),
3049 'parent' => 'wpf-community',
3050 ];
3051 $wp_admin_bar->add_node( $args );
3052 }
3053 if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
3054 $args = [
3055 'id' => 'wpf-ai',
3056 'title' => '' . __( 'AI Features', 'wpforo' ),
3057 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'ai' ) ),
3058 'parent' => 'wpf-community',
3059 ];
3060 $wp_admin_bar->add_node( $args );
3061 }
3062 if( WPF()->usergroup->can( 'mt' ) || wpforo_current_user_is( 'admin' ) ) {
3063 $args = [
3064 'id' => 'wpf-tools',
3065 'title' => '' . __( 'Tools', 'wpforo' ),
3066 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) ),
3067 'parent' => 'wpf-community',
3068 ];
3069 $wp_admin_bar->add_node( $args );
3070 }
3071 if( WPF()->usergroup->can( 'aum' ) || wpforo_current_user_is( 'admin' ) ) {
3072 $args = [
3073 'id' => 'wpf-moderation',
3074 'title' => '' . __( 'Moderation', 'wpforo' ) . wpforo_wp_admin_bar_red_circle_number(
3075 $admin_bar_numbers['mod_count']
3076 ),
3077 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'moderations' ) ),
3078 'parent' => 'wpf-community',
3079 ];
3080 $wp_admin_bar->add_node( $args );
3081 }
3082 if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
3083 $args = [
3084 'id' => 'wpforo-accesses',
3085 'title' => '' . __( 'Accesses', 'wpforo' ),
3086 'href' => admin_url( 'admin.php?page=wpforo-accesses' ),
3087 'parent' => 'wpf-community',
3088 ];
3089 $wp_admin_bar->add_node( $args );
3090 $args = [
3091 'id' => 'wpforo-new-accesses',
3092 'title' => '' . __( 'Add New Forum Access', 'wpforo' ),
3093 'href' => admin_url( 'admin.php?page=wpforo-accesses&wpfaction=wpforo_access_save_form' ),
3094 'parent' => 'wpforo-accesses',
3095 ];
3096 $wp_admin_bar->add_node( $args );
3097 }
3098 if( WPF()->usergroup->can( 'vm' ) || wpforo_current_user_is( 'admin' ) ) {
3099 $args = [
3100 'id' => 'wpf-members',
3101 'title' => '' . __( 'Members', 'wpforo' ) . wpforo_wp_admin_bar_red_circle_number(
3102 wpforo_get_memb_attention_count()
3103 ),
3104 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'members' ) ),
3105 'parent' => 'wpf-community',
3106 ];
3107 $wp_admin_bar->add_node( $args );
3108 }
3109 if( WPF()->usergroup->can( 'vmg' ) || wpforo_current_user_is( 'admin' ) ) {
3110 $args = [
3111 'id' => 'wpf-usergroups',
3112 'title' => '' . __( 'Usergroups', 'wpforo' ),
3113 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) ),
3114 'parent' => 'wpf-community',
3115 ];
3116 $wp_admin_bar->add_node( $args );
3117 $args = [
3118 'id' => 'wpf-new-ugroup',
3119 'title' => '' . __( 'Add New Usergroup', 'wpforo' ),
3120 'href' => admin_url(
3121 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) . '&wpfaction=wpforo_usergroup_save_form'
3122 ),
3123 'parent' => 'wpf-usergroups',
3124 ];
3125 $wp_admin_bar->add_node( $args );
3126 }
3127 if( WPF()->usergroup->can( 'mp' ) || wpforo_current_user_is( 'admin' ) ) {
3128 $args = [
3129 'id' => 'wpf-phrases',
3130 'title' => '' . __( 'Phrases', 'wpforo' ),
3131 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'phrases' ) ),
3132 'parent' => 'wpf-community',
3133 ];
3134 $wp_admin_bar->add_node( $args );
3135 $args = [
3136 'id' => 'wpf-new-phrase',
3137 'title' => '' . __( 'Add New Phrase', 'wpforo' ),
3138 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'phrases' ) . '&wpfaction=phrase_add_form' ),
3139 'parent' => 'wpf-phrases',
3140 ];
3141 $wp_admin_bar->add_node( $args );
3142 }
3143 if( WPF()->usergroup->can( 'mth' ) || wpforo_current_user_is( 'admin' ) ) {
3144 $args = [
3145 'id' => 'wpf-themes',
3146 'title' => '' . __( 'Themes', 'wpforo' ),
3147 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'themes' ) ),
3148 'parent' => 'wpf-community',
3149 ];
3150 $wp_admin_bar->add_node( $args );
3151 }
3152 if( wpforo_current_user_is( 'admin' ) ) {
3153 $args = [
3154 'id' => 'wpf-addons',
3155 'title' => '' . __( 'Addons', 'wpforo' ),
3156 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'addons' ) ),
3157 'parent' => 'wpf-community',
3158 ];
3159 $wp_admin_bar->add_node( $args );
3160 }
3161 }
3162
3163 function wpforo_multiboard_admin_bar_menu( $wp_admin_bar ) {
3164 $bds_bar_numbers = [];
3165 if( wpforo_current_user_is( 'admin' ) ) {
3166 $args = [
3167 'id' => 'wpforo-new-content',
3168 'title' => 'wpForo',
3169 'href' => admin_url( 'admin.php?page=wpforo-overview' ),
3170 'parent' => 'new-content',
3171 ];
3172 $wp_admin_bar->add_node( $args );
3173
3174 if( $boardids = WPF()->board->get_active_boardids() ) {
3175 foreach( $boardids as $boardid ) {
3176 WPF()->change_board( $boardid );
3177 $current = WPF()->board->get_current();
3178 $bds_bar_numbers[ $boardid ] = wpforo_get_admin_bar_numbers();
3179
3180 $menuid = 'wpforo-new-content-' . $current['slug'];
3181 $args = [
3182 'id' => $menuid,
3183 'title' => $current['title'],
3184 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) ),
3185 'parent' => 'wpforo-new-content',
3186 ];
3187 $wp_admin_bar->add_node( $args );
3188 ## ---------------------------------------------------------------------------------- ####
3189 $args = [
3190 'id' => $menuid . 'new-forum',
3191 'title' => '' . __( 'Add New Forum', 'wpforo' ),
3192 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) . '&action=add' ),
3193 'parent' => $menuid,
3194 ];
3195 $wp_admin_bar->add_node( $args );
3196 $args = [
3197 'id' => $menuid . 'new-ugroup',
3198 'title' => '' . __( 'Add New User Group', 'wpforo' ),
3199 'href' => admin_url(
3200 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) . '&wpfaction=wpforo_usergroup_save_form'
3201 ),
3202 'parent' => $menuid,
3203 ];
3204 $wp_admin_bar->add_node( $args );
3205 $args = [
3206 'id' => $menuid . 'new-phrase',
3207 'title' => '' . __( 'Add New Phrase', 'wpforo' ),
3208 'href' => admin_url(
3209 'admin.php?page=' . wpforo_prefix_slug( 'phrases' ) . '&wpfaction=phrase_add_form'
3210 ),
3211 'parent' => $menuid,
3212 ];
3213 $wp_admin_bar->add_node( $args );
3214
3215 }
3216 }
3217 }
3218
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(
3220 'aum'
3221 ) || WPF()->usergroup->can( 'vmg' ) || WPF()->usergroup->can( 'mth' ) ) {
3222 $bds_bar_all = 0;
3223 foreach( $bds_bar_numbers as $x ) $bds_bar_all += $x['all_count'];
3224 $args = [
3225 'id' => 'wpforo',
3226 'title' => 'wpForo' . wpforo_wp_admin_bar_red_circle_number( $bds_bar_all ),
3227 'href' => admin_url( 'admin.php?page=wpforo-overview' ),
3228 ];
3229 $wp_admin_bar->add_node( $args );
3230
3231
3232 if( $boardids = WPF()->board->get_active_boardids() ) {
3233 foreach( $boardids as $boardid ) {
3234 WPF()->change_board( $boardid );
3235 $current = WPF()->board->get_current();
3236
3237 $menuid = 'wpforo-' . $current['slug'];
3238 $args = [
3239 'id' => $menuid,
3240 'title' => '' . $current['title'] . ' (' . strtok(
3241 $current['locale'],
3242 '_'
3243 ) . ')' . wpforo_wp_admin_bar_red_circle_number( $bds_bar_numbers[ $boardid ]['all_count'] ),
3244 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) ),
3245 'parent' => 'wpforo',
3246 ];
3247 $wp_admin_bar->add_node( $args );
3248
3249 ## ---------------------------------------------------------------------------------- ####
3250
3251 $args = [
3252 'id' => $menuid . '-home',
3253 'title' => '' . __( 'Visit Forum', 'wpforo' ),
3254 'href' => wpforo_home_url(),
3255 'parent' => $menuid,
3256 // 'meta' => [ 'target' => '_blank' ]
3257 ];
3258 $wp_admin_bar->add_node( $args );
3259
3260 if( WPF()->usergroup->can( 'mf' ) || wpforo_current_user_is( 'admin' ) ) {
3261 $args = [
3262 'id' => $menuid . '-forums',
3263 'title' => '' . __( 'Forums', 'wpforo' ),
3264 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) ),
3265 'parent' => $menuid,
3266 ];
3267 $wp_admin_bar->add_node( $args );
3268 $args = [
3269 'id' => $menuid . '-new-forum',
3270 'title' => '' . __( 'Add New Forum', 'wpforo' ),
3271 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) . '&action=add' ),
3272 'parent' => $menuid . '-forums',
3273 ];
3274 $wp_admin_bar->add_node( $args );
3275 }
3276 if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
3277 $args = [
3278 'id' => $menuid . '-settings',
3279 'title' => '' . __( 'Settings', 'wpforo' ),
3280 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'settings' ) ),
3281 'parent' => $menuid,
3282 ];
3283 $wp_admin_bar->add_node( $args );
3284 }
3285 if( WPF()->usergroup->can( 'mt' ) || wpforo_current_user_is( 'admin' ) ) {
3286 $args = [
3287 'id' => $menuid . '-tools',
3288 'title' => '' . __( 'Tools', 'wpforo' ),
3289 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) ),
3290 'parent' => $menuid,
3291 ];
3292 $wp_admin_bar->add_node( $args );
3293 }
3294 if( WPF()->usergroup->can( 'aum' ) || wpforo_current_user_is( 'admin' ) ) {
3295 $args = [
3296 'id' => $menuid . '-moderation',
3297 'title' => '' . __(
3298 'Moderation',
3299 'wpforo'
3300 ) . wpforo_wp_admin_bar_red_circle_number( $bds_bar_numbers[ $boardid ]['mod_count'] ),
3301 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'moderations' ) ),
3302 'parent' => $menuid,
3303 ];
3304 $wp_admin_bar->add_node( $args );
3305 }
3306 if( WPF()->usergroup->can( 'mp' ) || wpforo_current_user_is( 'admin' ) ) {
3307 $args = [
3308 'id' => $menuid . '-phrases',
3309 'title' => '' . __( 'Phrases', 'wpforo' ),
3310 'href' => admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'phrases' ) ),
3311 'parent' => $menuid,
3312 ];
3313 $wp_admin_bar->add_node( $args );
3314 $args = [
3315 'id' => $menuid . '-new-phrase',
3316 'title' => '' . __( 'Add New Phrase', 'wpforo' ),
3317 'href' => admin_url(
3318 'admin.php?page=' . wpforo_prefix_slug( 'phrases' ) . '&wpfaction=phrase_add_form'
3319 ),
3320 'parent' => $menuid . '-phrases',
3321 ];
3322 $wp_admin_bar->add_node( $args );
3323 }
3324 }
3325 }
3326
3327 ##### -- ##### ----- $ ----- ###
3328
3329 if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
3330 $args = [
3331 'id' => 'wpforo-boards',
3332 'title' => '' . __( 'Boards', 'wpforo' ),
3333 'href' => admin_url( 'admin.php?page=wpforo-boards' ),
3334 'parent' => 'wpforo',
3335 ];
3336 $wp_admin_bar->add_node( $args );
3337 $args = [
3338 'id' => 'wpforo-new-board',
3339 'title' => '' . __( 'Add New Board', 'wpforo' ),
3340 'href' => admin_url( 'admin.php?page=wpforo-boards&wpfaction=wpforo_board_save_form' ),
3341 'parent' => 'wpforo-boards',
3342 ];
3343 $wp_admin_bar->add_node( $args );
3344 }
3345 if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
3346 $args = [
3347 'id' => 'wpforo-accesses',
3348 'title' => '' . __( 'Accesses', 'wpforo' ),
3349 'href' => admin_url( 'admin.php?page=wpforo-accesses' ),
3350 'parent' => 'wpforo',
3351 ];
3352 $wp_admin_bar->add_node( $args );
3353 $args = [
3354 'id' => 'wpforo-new-accesses',
3355 'title' => '' . __( 'Add New Forum Access', 'wpforo' ),
3356 'href' => admin_url( 'admin.php?page=wpforo-accesses&wpfaction=wpforo_access_save_form' ),
3357 'parent' => 'wpforo-accesses',
3358 ];
3359 $wp_admin_bar->add_node( $args );
3360 }
3361 if( WPF()->usergroup->can( 'vmg' ) || wpforo_current_user_is( 'admin' ) ) {
3362 $args = [
3363 'id' => 'wpforo-usergroups',
3364 'title' => '' . __( 'Usergroups', 'wpforo' ),
3365 'href' => admin_url( 'admin.php?page=wpforo-usergroups' ),
3366 'parent' => 'wpforo',
3367 ];
3368 $wp_admin_bar->add_node( $args );
3369 $args = [
3370 'id' => 'wpforo-new-ugroup',
3371 'title' => '' . __( 'Add New Usergroup', 'wpforo' ),
3372 'href' => admin_url( 'admin.php?page=wpforo-usergroups&wpfaction=wpforo_usergroup_save_form' ),
3373 'parent' => 'wpforo-usergroups',
3374 ];
3375 $wp_admin_bar->add_node( $args );
3376 }
3377 if( WPF()->usergroup->can( 'vm' ) || wpforo_current_user_is( 'admin' ) ) {
3378 $args = [
3379 'id' => 'wpforo-members',
3380 'title' => '' . __( 'Members', 'wpforo' ) . wpforo_wp_admin_bar_red_circle_number(
3381 wpforo_get_memb_attention_count()
3382 ),
3383 'href' => admin_url( 'admin.php?page=wpforo-members' ),
3384 'parent' => 'wpforo',
3385 ];
3386 $wp_admin_bar->add_node( $args );
3387 }
3388 if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
3389 $args = [
3390 'id' => 'wpforo-base-settings',
3391 'title' => '' . __( 'Settings', 'wpforo' ),
3392 'href' => admin_url( 'admin.php?page=wpforo-base-settings' ),
3393 'parent' => 'wpforo',
3394 ];
3395 $wp_admin_bar->add_node( $args );
3396 }
3397 if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
3398 $args = [
3399 'id' => 'wpforo-ai',
3400 'title' => '' . __( 'AI Features', 'wpforo' ),
3401 'href' => admin_url( 'admin.php?page=wpforo-ai' ),
3402 'parent' => 'wpforo',
3403 ];
3404 $wp_admin_bar->add_node( $args );
3405 }
3406 if( WPF()->usergroup->can( 'mth' ) || wpforo_current_user_is( 'admin' ) ) {
3407 $args = [
3408 'id' => 'wpforo-themes',
3409 'title' => '' . __( 'Themes', 'wpforo' ),
3410 'href' => admin_url( 'admin.php?page=wpforo-themes' ),
3411 'parent' => 'wpforo',
3412 ];
3413 $wp_admin_bar->add_node( $args );
3414 }
3415 if( wpforo_current_user_is( 'admin' ) ) {
3416 $args = [
3417 'id' => 'wpforo-addons',
3418 'title' => '' . __( 'Addons', 'wpforo' ),
3419 'href' => admin_url( 'admin.php?page=wpforo-addons' ),
3420 'parent' => 'wpforo',
3421 ];
3422 $wp_admin_bar->add_node( $args );
3423 }
3424 }
3425 }
3426
3427 add_action( 'admin_bar_menu', function( $wp_admin_bar ) {
3428 if( is_wpforo_multiboard() ) {
3429 wpforo_multiboard_admin_bar_menu( $wp_admin_bar );
3430 WPF()->change_board();
3431 } else {
3432 wpforo_admin_bar_menu( $wp_admin_bar );
3433 }
3434 echo '<style>
3435 #wpadminbar .wpforo-menu-notification-counter,
3436 #wpadminbar .wpforo-menu-notification-indicator{
3437 background-color: #d63638;
3438 color: #ffffff;
3439 }
3440
3441 #wpadminbar .wpforo-menu-notification-counter{
3442 display: inline-block;
3443 min-width: 18px;
3444 height: 18px;
3445 border-radius: 50%;
3446 margin: 5px 5px 0 5px;
3447 vertical-align: top;
3448 font-size: 11px;
3449 line-height: 1.6;
3450 text-align: center;
3451 }
3452 </style>';
3453 }, 999 );
3454
3455 function wpforo_tag_search() {
3456 $s = wp_unslash( $_GET['q'] );
3457 $tag_separator = ( strpos( $s, '،' ) !== false ) ? '،' : ',';
3458 $tag_separator = apply_filters( 'wpforo_tag_separator', $tag_separator );
3459 if( false !== strpos( (string) $s, $tag_separator ) ) {
3460 $s = str_replace( $tag_separator, ',', $s );
3461 $s = explode( ',', $s );
3462 $s = $s[ count( $s ) - 1 ];
3463 }
3464 $s = trim( (string) $s );
3465 $tag_search_min_chars = apply_filters( 'wpforo_tag_search_min_chars', 2 );
3466 if( wpforo_strlen( $s ) >= $tag_search_min_chars ) {
3467 $limit = wpforo_setting( 'tags', 'suggest_limit' );
3468 $results = WPF()->db->get_col(
3469 "SELECT `tag` FROM `" . WPF()->tables->tags . "` WHERE `tag` LIKE '" . esc_sql( $s ) . "%' LIMIT " . $limit
3470 );
3471 if( ! empty( $results ) ) {
3472 echo implode( "\n", $results );
3473 }
3474 }
3475 wp_die();
3476 }
3477
3478 add_action( 'wp_ajax_wpforo_tag_search', 'wpforo_tag_search' );
3479 add_action( 'wp_ajax_nopriv_wpforo_tag_search', 'wpforo_tag_search' );
3480
3481 function wpforo_add_to_footer() {
3482 if( WPF()->current_object['load_tinymce'] ) {
3483 wp_enqueue_editor();
3484 }
3485 }
3486
3487 add_action( 'wpforo_bottom_hook', 'wpforo_add_to_footer' );
3488
3489 function wpforo_check_notifications() {
3490 $data = [ 'alerts' => 0, 'notifications' => '' ];
3491 if( is_user_logged_in() && wpforo_setting( 'notifications', 'notifications' ) ) {
3492 $data['alerts'] = count( WPF()->activity->notifications );
3493 if( wpfval( $_POST, 'getdata' ) ) {
3494 $data['notifications'] = WPF()->activity->notifications_list( false );
3495 }
3496 wp_send_json_success( $data );
3497 }
3498 wp_send_json_error( $data );
3499 }
3500
3501 add_action( 'wp_ajax_wpforo_notifications', 'wpforo_check_notifications' );
3502
3503 function wpforo_can_display_recaptcha_note() {
3504 $d = wpforo_is_admin() ? 'recaptcha_backend_note' : 'recaptcha_note';
3505
3506 return ! WPF()->dissmissed[ $d ] && current_user_can( 'administrator' ) && ! wp_is_mobile() && wpforo_setting(
3507 'authorization',
3508 'user_register'
3509 ) && ! wpforo_setting( 'authorization', 'register_url' ) && ! wpforo_is_recaptcha_configured();
3510 }
3511
3512 add_action( 'wpforo_header_hook', 'wpforo_recaptcha_note' );
3513 function wpforo_recaptcha_note() {
3514 if( wpforo_can_display_recaptcha_note() ) {
3515 ?>
3516 <div class="wpforo-rcn-wrap">
3517 <div class="wpforo-rcn-body">
3518 <span class="wpforo-rcn-head"><i
3519 class="fas fa-user-secret"></i> <?php wpforo_phrase(
3520 'Protect your forum from spam user registration!'
3521 ); ?></span>
3522 <?php printf(
3523 wpforo_phrase(
3524 'wpForo has not found any protection solution against spam user registration on the forum registration form. Please %1$s and enable the %2$s antibot protection in %3$s or install other alternative %4$s to avoid registration of spam users.',
3525 false,
3526 'native'
3527 ),
3528 '<a class="wpf-rcnl" href="' . admin_url(
3529 'admin.php?page=' . wpforo_prefix_slug(
3530 'base-settings'
3531 ) . '&wpf_tab=recaptcha#wpf-settings-tab'
3532 ) . '" target="_blank">' . wpforo_phrase( 'configure', false, 'lower' ) . '</a>',
3533 '<a class="wpf-rcngl" href="https://developers.google.com/recaptcha" target="_blank">Google reCAPTCHA</a>',
3534 '<a href="' . admin_url(
3535 'admin.php?page=' . wpforo_prefix_slug(
3536 'base-settings'
3537 ) . '&wpf_tab=recaptcha#wpf-settings-tab'
3538 ) . '">' . wpforo_phrase( 'Settings &gt; Spam Protection Tab', false ) . '</a>',
3539 '<a class="wpf-rcnwl" href="https://wordpress.org/plugins/search/spam+users/" target="_blank">' . wpforo_phrase(
3540 'WordPress plugin',
3541 false
3542 ) . '</a>'
3543 )
3544 ?>
3545 </div>
3546 <div class="wpforo-rcn-footer">
3547 <div class="wpforo-rcn-info">
3548 <?php wpforo_phrase(
3549 'This notification is only visible for the website administrators. It will be automatically disabled once some antispam solution is enabled. If you don\'t use wpForo registration form or you\'re sure, that you have an antispam solution just click the [dismiss] button.'
3550 ); ?>
3551 </div>
3552 <div class="wpforo-rcn-dismiss">
3553 <span class="wpforo-rcn-dismiss-button"
3554 wpf-tooltip="<?php wpforo_phrase( 'I got it, please dismiss this message' ) ?>"
3555 wpf-tooltip-size="long"><?php wpforo_phrase( 'Dismiss' ) ?></span>
3556 </div>
3557 </div>
3558 </div>
3559 <?php
3560 }
3561 }
3562
3563 //add_action('admin_notices', 'wpforo_admin_notice_recaptcha');
3564 function wpforo_admin_notice_recaptcha() {
3565 if( wpforo_can_display_recaptcha_note() ) {
3566 wp_enqueue_script( 'wpforo-backend-js' );
3567 $class = 'notice notice-error is-dismissible';
3568 $message = __(
3569 'IMPORTANT! The forum registration form is probably under risk of spam attacks. Please configure wpForo built-in %s antibot for registration form to avoid spam registrations. If you don\'t use the forum registration form or you are sure that your registration forms are secured, just click on (x) button to dismiss this message.',
3570 'wpforo'
3571 );
3572 $message = sprintf(
3573 $message,
3574 '<a href="' . admin_url(
3575 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=antispam#wpf-recaptcha'
3576 ) . '" target="_blank" style="text-decoration:none;">' . __(
3577 'Google reCAPTCHA',
3578 'wpforo'
3579 ) . '</a>'
3580 );
3581 printf( '<div id="wpforo-admin-notice-recaptcha" class="%1$s"><p>%2$s</p></div>', $class, $message );
3582 }
3583 }
3584
3585 function wpforo_is_recaptcha_configured() {
3586 return wpforo_setting( 'recaptcha', 'wpf_reg_form' ) && wpforo_setting( 'recaptcha', 'site_key' ) && wpforo_setting(
3587 'recaptcha',
3588 'secret_key'
3589 );
3590 }
3591
3592
3593 /*add_action( 'plugins_loaded', function() {
3594 if( ! (int) wpfval( WPF()->dissmissed, 'poll_version_is_old' ) && function_exists( 'WPF_POLL' ) ) {
3595 if( version_compare( WPFORO_VERSION, '1.7.7', '>' ) && version_compare( WPFOROPOLL_VERSION, '1.0.5', '<=' ) ) {
3596 remove_action( 'widgets_init', [ WPF_POLL(), 'init_widgets' ] );
3597 remove_action( 'wpforo_before_init', [ WPF_POLL(), 'init' ] );
3598
3599 WPF()->dissmissed['poll_version_is_old'] = 0;
3600 wpforo_update_option( 'dissmissed', WPF()->dissmissed );
3601 } else {
3602 WPF()->dissmissed['poll_version_is_old'] = 1;
3603 wpforo_update_option( 'dissmissed', WPF()->dissmissed );
3604 }
3605 }
3606 } );*/
3607
3608 add_action( 'admin_notices', function() {
3609 if( wpfkey( WPF()->dissmissed, 'poll_version_is_old' ) && ! (int) WPF()->dissmissed['poll_version_is_old'] ) {
3610 $class = 'notice notice-error';
3611 $message = '<div style="font-size: 16px; padding: 10px 0;"><strong>' . __(
3612 'wpForo Polls addon is disabled!',
3613 'wpforo'
3614 ) . '</strong><p style="font-size:15px; margin-bottom:0;">' . __(
3615 ' Your addon version is not compatible with the current version of wpForo. Please update the addon or downgrade wpForo to 1.7.7',
3616 'wpforo'
3617 ) . '</p></div>';
3618 $message .= ' <a href="' . admin_url(
3619 wp_nonce_url(
3620 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) . '&wpfaction=dissmiss_poll_version_is_old',
3621 'wpforo-dissmiss-poll-version-is-old'
3622 )
3623 ) . '">[' . __( 'dismiss', 'wpforo' ) . ']</a>';
3624 printf( '<div class="%1$s"><p>%2$s</p></div>', $class, $message );
3625 }
3626 } );
3627
3628 add_action( 'wpforo_update_option', function() {
3629 wpforo_clean_folder(
3630 WPF()->folders['cache']['dir'] . DIRECTORY_SEPARATOR . 'item' . DIRECTORY_SEPARATOR . 'option'
3631 );
3632 } );
3633
3634 add_action( 'wpforo_after_init', function() {
3635 add_action( 'wp_ajax_dismiss_wpforo_addon_note', [ WPF()->notice, 'dismissAddonNote' ] );
3636 add_action( 'admin_notices', [ WPF()->notice, 'addonNote' ] );
3637 } );
3638
3639 add_action( 'wpforo_after_init', function() {
3640 add_action( 'wp_ajax_dismiss_wpforo_cache_conflict_note', [ WPF()->notice, 'dismissCacheConflict' ] );
3641 } );
3642
3643 add_action( 'admin_enqueue_scripts', 'wpforo_cat_cover_js' );
3644
3645 function wpforo_cat_cover_js() {
3646 if( wpfval( $_GET, 'page' ) === wpforo_prefix_slug( 'forums' ) && ! did_action( 'wp_enqueue_media' ) ) {
3647 wp_enqueue_media();
3648 }
3649 }
3650
3651 function wpforo_esc_html( $var ) {
3652 if( is_scalar( $var ) ) {
3653 $var = esc_html( $var );
3654 } else {
3655 $var = wpforo_esc_html( $var );
3656 }
3657
3658 return $var;
3659 }
3660
3661 add_action( 'wpforo_bottom_hook', 'wpforo_debug' );
3662 function wpforo_debug() {
3663 if( wpforo_setting( 'general', 'debug_mode' ) ) : ?>
3664 <div id="wpforo-debug" style="display:none">
3665 <h4>Super Globals</h4>
3666 <textarea style="width:100%; height:300px;">
3667 Requests: <?php //print_r( array_map( 'wpforo_esc_html', $_REQUEST ) ); ?>
3668 Server: <?php //print_r( $_SERVER ); ?>
3669 </textarea>
3670 <h4>Options and Features</h4>
3671 <textarea style="width:100%; height:300px;">
3672 <?php echo @ 'route: ' . WPF()->board->route . "\r\n";
3673 echo @ 'use_home_url: ' . WPF()->board->get_current( 'is_standalone' ) . "\r\n";
3674 echo @ 'url: ' . wpforo_home_url() . "\r\n";
3675 echo @ 'pageid:' . WPF()->board->get_current( 'pageid' ) . "\r\n";
3676 echo @ 'default_groupid: ' . WPF()->usergroup->default_groupid . "\r\n";
3677 @print_r( WPF()->tpl->theme ) . "\r\n";
3678 ?>
3679 </textarea>
3680 </div>
3681 <?php
3682 endif;
3683 }
3684
3685 add_filter( 'wpforo_topic_list_args', function( $args ) {
3686 if( wpforo_setting( 'topics', 'include_subforums_topics' ) ) {
3687 $forumid = (int) $args['forumid'];
3688 $forumids = WPF()->forum->get_forumid_and_childids( $forumid );
3689 unset( $args['forumid'] );
3690 $args['forumids'] = $forumids;
3691 }
3692
3693 return $args;
3694 } );
3695
3696 function wpf_body_wp_encode_emoji( $post ) {
3697 if( wpfkey( $post, 'title' ) ) $post['title'] = wp_encode_emoji( $post['title'] );
3698 if( wpfkey( $post, 'body' ) ) $post['body'] = wp_encode_emoji( $post['body'] );
3699
3700 return $post;
3701 }
3702
3703 add_filter( 'wpforo_add_post_data_filter', 'wpf_body_wp_encode_emoji' );
3704 add_filter( 'wpforo_edit_post_data_filter', 'wpf_body_wp_encode_emoji' );
3705 add_filter( 'wpforo_add_topic_data_filter', 'wpf_body_wp_encode_emoji' );
3706 add_filter( 'wpforo_edit_topic_data_filter', 'wpf_body_wp_encode_emoji' );
3707
3708 // Feature Introduction Modal - AJAX dismiss handler (must be in hooks.php, not admin/index.php, because wpforo_is_admin() excludes AJAX)
3709 add_action( 'wp_ajax_wpforo_dismiss_feature_intro', function() {
3710 \wpforo\classes\FeatureIntro::ajax_dismiss_static();
3711 } );
3712
3713 // Feature Introduction Modal - redirect to wpForo Overview after update/activation
3714 add_action( 'admin_init', function() {
3715 if( wp_doing_ajax() || wp_doing_cron() ) return;
3716 if( is_network_admin() ) return;
3717 if( isset( $_GET['activate-multi'] ) ) return;
3718 if( ! current_user_can( 'manage_options' ) ) return;
3719
3720 $intro_version = \wpforo\classes\FeatureIntro::CURRENT_INTRO;
3721 $version_option = \wpforo\classes\FeatureIntro::VERSION_OPTION;
3722 $last_intro = get_option( $version_option, '' );
3723
3724 if( version_compare( $last_intro, $intro_version, '<' ) ) {
3725 update_option( $version_option, $intro_version );
3726 // Don't redirect if already on a wpForo page
3727 if( isset( $_GET['page'] ) && strpos( sanitize_text_field( $_GET['page'] ), 'wpforo' ) !== false ) return;
3728 wp_safe_redirect( admin_url( 'admin.php?page=wpforo-overview' ) );
3729 exit;
3730 }
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 } );
3781