PluginProbe
wpForo Forum / 3.1.1
wpForo Forum v3.1.1
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 1.4.2 All 137 releases
wpforo / includes / functions-template.php

functions-template.php in wpForo Forum 3.1.1, at includes/functions-template.php

3,331 lines 141.3 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' ) ) exit;
4
5 add_action( 'wpforo_after_init_base_classes', function() {
6 if( WPF()->is_installed() ) {
7 foreach( WPF()->board->get_active_boardids() as $boardid ) {
8 register_nav_menus(
9 [
10 str_replace( '_', '-', WPF()->generate_prefix( $boardid ) ) . 'menu' => esc_html__(
11 'wpForo Menu',
12 'wpforo'
13 ) . ( $boardid ? ' #' . $boardid : '' ),
14 ]
15 );
16 }
17 }
18 } );
19
20 add_filter(
21 'wp_get_nav_menu_items',
22 function( $items ) {
23 if( ! wpforo_is_admin() ) {
24 foreach( $items as $key => $item ) {
25 if( isset( $item->url ) ) {
26 if( strpos( (string) $item->url, '%wpforo-' ) !== false ) {
27 // Extract shortcode and any query parameters (e.g., %wpforo-profile-activity?boardid=2%)
28 $url_part = trim( str_replace( [ 'https://', 'http://', '/', '%' ], '', (string) $item->url ) );
29 $query_params = [];
30 if( strpos( $url_part, '?' ) !== false ) {
31 list( $shortcode, $query_string ) = explode( '?', $url_part, 2 );
32 parse_str( $query_string, $query_params );
33 } else {
34 $shortcode = $url_part;
35 }
36
37 if( isset( WPF()->tpl->menu ) && isset( WPF()->tpl->menu[ $shortcode ] ) ) {
38 if( isset( WPF()->tpl->menu[ $shortcode ]['href'] ) ) {
39 $url = WPF()->tpl->menu[ $shortcode ]['href'];
40 // Append query parameters (like boardid) if present
41 if( ! empty( $query_params ) ) {
42 $url = add_query_arg( $query_params, $url );
43 }
44 // Auto-append current boardid on multi-board sites (except home).
45 // Skip if boardid is already present (admin-provided or built-in).
46 if(
47 $shortcode !== 'wpforo-home'
48 && is_wpforo_multiboard()
49 && ! isset( $query_params['boardid'] )
50 && strpos( (string) $url, 'boardid=' ) === false
51 ) {
52 $current_boardid = (int) WPF()->board->get_current( 'boardid' );
53 if( $current_boardid > 0 ) {
54 $url = add_query_arg( 'boardid', $current_boardid, $url );
55 }
56 }
57 $item->url = $url;
58 }
59 if( wpfval( WPF()->tpl->menu[ $shortcode ], 'is_active' ) || ( isset(
60 WPF()->tpl->menu[ $shortcode ]['attr']
61 ) && strpos(
62 (string) WPF()->tpl->menu[ $shortcode ]['attr'],
63 'wpforo-active'
64 ) !== false ) ) {
65 $item->classes[] = 'wpforo-active';
66 }
67 } else {
68 unset( $items[ $key ] );
69 }
70 }
71 }
72 }
73 }
74
75 return $items;
76 },
77 1
78 );
79
80 function wpforo_menu_nofollow_items( $item_output, $item, $depth, $args ) {
81 //if( isset($item->url) && strpos((string) $item->url, '?foro') !== FALSE ) {
82 //$item_output = str_replace('<a ', '<a rel="nofollow" ', $item_output);
83 //}
84 return $item_output;
85 }
86
87 add_filter( 'walker_nav_menu_start_el', 'wpforo_menu_nofollow_items', 1, 4 );
88
89 /**
90 * Append current boardid to member profile URLs on multi-board sites so users
91 * stay inside the board they're browsing when navigating to a profile. Applies
92 * in all contexts (frontend, emails, etc.). Skips if boardid is already set.
93 */
94 add_filter( 'wpforo_member_profile_url', 'wpforo_append_current_boardid_to_profile_url', 20, 3 );
95 function wpforo_append_current_boardid_to_profile_url( $profile_url, $user, $template ) {
96 if( empty( $profile_url ) ) return $profile_url;
97 if( ! is_wpforo_multiboard() ) return $profile_url;
98 if( strpos( (string) $profile_url, 'boardid=' ) !== false ) return $profile_url;
99 $boardid = (int) WPF()->board->get_current( 'boardid' );
100 if( $boardid <= 0 ) return $profile_url;
101
102 return add_query_arg( 'boardid', $boardid, $profile_url );
103 }
104
105 /**
106 * Detects the special anonymous email mask used by wpforo_anonymous_posting plugin.
107 * Pattern: anonymous+topic{topicid}-{number}@example.com
108 */
109 function wpforo_is_anonymous_mask_email( $email ): bool {
110 if( ! is_string( $email ) || $email === '' ) return false;
111
112 return (bool) preg_match( '/^anonymous\+topic\d+-\d+@example\.com$/', $email );
113 }
114
115 /**
116 * Convenience helper to check if a member/guest array is anonymized by the plugin.
117 */
118 function wpforo_is_anonymous_mask_member( $member ): bool {
119 if( is_array( $member ) ) {
120 $email = wpfval( $member, 'user_email' );
121
122 return wpforo_is_anonymous_mask_email( $email );
123 }
124
125 return false;
126 }
127
128 function wpforo_profile_plugin_menu( $user ) {
129 $userid = $user['userid'];
130 $menu_html = '';
131
132 if( $url = wpforo_has_shop_plugin() ) {
133 $menu_html .= '<div id="wpf-pp-shop-menu" class="wpf-pp-menu">
134 <a class="wpf-pp-menu-item" href="' . esc_url( (string) $url ) . '">
135 <i class="fas fa-shopping-cart" title="' . wpforo_phrase(
136 'Shop Account',
137 false
138 ) . '"></i> <span>' . wpforo_phrase( 'Shop Account', false ) . '</span>
139 </a>
140 </div>';
141 }
142 if( $url = wpforo_has_profile_plugin( $userid ) ) {
143 $menu_html .= '<div id="wpf-pp-site-menu" class="wpf-pp-menu">
144 <a class="wpf-pp-menu-item" href="' . esc_url( (string) $url ) . '">
145 <i class="fas fa-user" title="' . wpforo_phrase(
146 'Site Profile',
147 false
148 ) . '"></i> <span>' . wpforo_phrase( 'Site Profile', false ) . '</span>
149 </a>
150 </div>';
151 }
152
153 $menu_html = apply_filters( 'wpforo_profile_top_bar', $menu_html, $userid );
154 if( $menu_html ) echo '<div class="wpf-profile-plugin-menu">' . $menu_html . '<div class="wpf-clear"></div></div>';
155 }
156
157 //add_action( 'wpforo_template_profile_action_buttons_left', 'wpforo_profile_plugin_menu' );
158
159 function wpforo_post_edited( $post, $echo = true ) {
160 $edit_html = '';
161 if( ! empty( $post ) ) {
162 $created = wpforo_date( $post['created'], 'd/m/Y g:i a', false );
163 $modified = wpforo_date( $post['modified'], 'd/m/Y g:i a', false );
164 if( isset( $modified ) && $created != $modified ) {
165 if( $post['is_first_post'] && wpforo_setting( 'posting', 'edit_topic' ) ) {
166 $edit_html = WPF()->activity->build( 'topic', $post['topicid'], 'edit_topic' );
167 } elseif( wpforo_setting( 'posting', 'edit_post' ) ) {
168 $edit_html = WPF()->activity->build( 'post', $post['postid'], 'edit_post' );
169 }
170 $edit_html = $edit_html ? sprintf( '<div class="wpf-post-edit-wrap">%s</div>', $edit_html ) : '';
171 }
172 }
173
174 $edit_html = apply_filters( 'wpforo_post_edited', $edit_html, $post );
175
176 if( $echo ) {
177 echo $edit_html;
178 } else {
179 return $edit_html;
180 }
181 }
182
183 function wpforo_hide_title( $title, $id = 0 ) {
184 if( is_page( $id ) && in_the_loop() && is_wpforo_page() && $id === WPF()->board->get_current(
185 'pageid'
186 ) && ! wpforo_setting( 'components', 'page_title' ) ) {
187 $title = '';
188 }
189
190 return $title;
191 }
192
193 add_filter( 'the_title', 'wpforo_hide_title', 10, 2 );
194
195 function wpforo_validate_gravatar( $email ) {
196 $hashkey = md5( strtolower( trim( (string) $email ) ) );
197 $uri = 'http://www.gravatar.com/avatar/' . $hashkey . '?d=404';
198 $data = wp_cache_get( $hashkey );
199 if( false === $data ) {
200 $response = wp_remote_head( $uri );
201 if( is_wp_error( $response ) ) {
202 $data = 'not200';
203 } else {
204 $data = $response['response']['code'];
205 }
206 wp_cache_set( $hashkey, $data, $group = '', $expire = 60 * 5 );
207 }
208 if( $data == '200' ) {
209 return true;
210 } else {
211 return false;
212 }
213 }
214
215 function wpforo_member_title( $member = [], $echo = true, $before = '', $after = '', $exclude = [] ) {
216 // Hide user titles for anonymized masked emails
217 if( wpforo_is_anonymous_mask_member( $member ) ) {
218 if( $echo ) echo '';
219
220 return '';
221 }
222 $title = [];
223
224 if( empty( $member ) || ! $member['groupid'] ) return '';
225 $rating_title_ug_enabled = in_array( $member['groupid'], wpforo_setting( 'rating', 'rating_title_ug' ), true );
226 $usergroup_title_ug_enabled = in_array( $member['groupid'], wpforo_setting( 'profiles', 'title_groupids' ), true );
227
228 if( ! in_array( 'rating-title', $exclude, true ) && wpforo_setting(
229 'rating',
230 'rating_title'
231 ) && $rating_title_ug_enabled && isset( $member['rating']['title'] ) ) {
232 $title[] = '<span class="wpf-member-title wpfrt" title="' . wpforo_phrase(
233 'Rating Title',
234 false
235 ) . '">' . esc_html( $member['rating']['title'] ) . '</span>';
236 }
237 if( ! in_array( 'custom-title', $exclude, true ) && empty( $title ) && wpforo_setting(
238 'profiles',
239 'custom_title_is_on'
240 ) ) {
241 $title[] = '<span class="wpf-member-title wpfct" title="' . wpforo_phrase(
242 'User Title',
243 false
244 ) . '">' . wpforo_phrase( $member['title'], false ) . '</span>';
245 } elseif( in_array( 'custom-fields', $exclude, true ) ) {
246 $before = $after = '';
247 }
248 if( ! in_array( 'custom-fields', $exclude, true ) ) {
249 $fields = apply_filters( 'wpforo_member_info_custom_fields', [], $member );
250 if( ! empty( $fields ) ) {
251 $count = count( $fields );
252 $i = 1;
253 if( $count ) {
254 $title[] = apply_filters(
255 'wpforo_member_custom_field_separator',
256 '<span class="wpf-member-cf-sep">&nbsp;&nbsp;<i class="fas fa-bars"></i>&nbsp;</span>',
257 $member
258 );
259 }
260 foreach( $fields as $field ) {
261 $title[] = '<span class="wpf-member-title wpf-member-cf wpf-member-cf-' . $field['name'] . ' wpfct" title="' . wpforo_phrase(
262 $field['label'],
263 false
264 ) . '">' . $field['value'] . ( $count !== $i ? $after : '' ) . '</span>';
265 $i ++;
266 }
267 }
268 $before = $after = '';
269 }
270 if( ! in_array( 'usergroup', $exclude, true ) && $usergroup_title_ug_enabled ) {
271 $class = '';
272 if( $member['groupid'] === 1 ) $class = ' wpfbr-b wpfcl-b';
273 if( $member['groupid'] === 2 ) $class = ' wpfbr-5 wpfcl-5';
274 if( $member['groupid'] === 4 ) $class = ' wpfbg-2 wpfcl-3';
275 if( ! in_array( $member['groupid'], [ 1, 2, 4 ], true ) ) $class = ' wpfbr-7';
276 $title[] = '<span class="wpf-member-title wpfut wpfug-' . intval(
277 $member['groupid']
278 ) . $class . '" title="' . wpforo_phrase( 'Usergroup', false ) . '">' . esc_html(
279 $member['group_name']
280 ) . '</span>';
281 }
282 if( ! in_array( 'usergroup', $exclude, true ) ) {
283 $groupids = array_intersect(
284 $member['secondary_groupids'],
285 wpforo_setting( 'profiles', 'title_secondary_groupids' )
286 );
287 $secondary_group_names = $groupids ? WPF()->usergroup->get_secondary_group_names( $groupids ) : [];
288 if( $secondary_group_names ) {
289 $title[] = '<span class="wpf-member-title wpfut wpfsut" title="' . wpforo_phrase(
290 'Secondary Usergroup',
291 false
292 ) . '">' . esc_html( implode( ', ', $secondary_group_names ) ) . '</span>';
293 }
294 }
295 $title_html = '';
296 if( ! empty( $title ) ) {
297 $title_html = $before . implode( ' ', $title ) . $after;
298 $title_html = apply_filters( 'wpforo_member_title', $title_html, $member );
299 }
300 if( $echo ) echo $title_html;
301
302 return $title_html;
303 }
304
305 function wpforo_member_badge( $member = [], $sep = '', $type = 'full' ) {
306 // Hide badges for anonymized masked emails
307 if( wpforo_is_anonymous_mask_member( $member ) ) return;
308 $rating_badge_ug_enabled = in_array( $member['groupid'], wpforo_setting( 'rating', 'rating_badge_ug' ), true );
309 if( wpforo_setting( 'rating', 'rating' ) && $rating_badge_ug_enabled && isset( $member['rating']['level'] ) ): ?>
310 <div class="author-rating-<?php echo esc_attr( $type ) ?>"
311 style="color:<?php echo esc_attr( $member['rating']['color'] ) ?>"
312 title="<?php wpforo_phrase( 'Member Rating Badge' ) ?>">
313 <?php echo WPF()->member->rating_badge( $member['rating']['level'], $type ); ?>
314 </div><?php if( $sep ): ?><span class="author-rating-sep"><?php echo esc_html( $sep ); ?></span><?php endif; ?>
315 <?php endif;
316
317 do_action( 'wpforo_after_member_badge', $member );
318 }
319
320 function wpforo_member_nicename( $member = [], $prefix = '', $bracket = true, $wrap = true, $class = 'wpf-author-nicename', $echo = true ) {
321 // Hide @nicename for anonymized masked emails
322 if( wpforo_is_anonymous_mask_member( $member ) ) return '';
323 if( ! wpforo_setting(
324 'profiles',
325 'mention_nicknames'
326 ) || empty( $member ) || ! isset( $member['user_nicename'] ) ) {
327 return '';
328 }
329 $nicename = '';
330 if( $wrap ) {
331 $nicename .= '<div class="' . $class . '" title="' . wpforo_phrase(
332 'You can mention a person using @nicename in post content to send that person an email message. When you post a topic or reply, forum sends an email message to the user letting them know that they have been mentioned on the post.',
333 false
334 ) . '">';
335 }
336 if( $bracket ) $nicename .= '(';
337 $nicename .= $prefix . urldecode( (string) $member['user_nicename'] );
338 if( $bracket ) $nicename .= ')';
339 if( $wrap ) {
340 $nicename .= '</div>';
341 }
342 $nicename = apply_filters( 'wpforo_member_nicename', $nicename, $member );
343 if( $echo ) {
344 echo $nicename;
345 } else {
346 return $nicename;
347 }
348 }
349
350 function wpforo_get_body_classes() {
351 $classes = [
352 'wpf-' . esc_attr( wpforo_setting( 'styles', 'color_style' ) ),
353 'wpft-' . esc_attr( WPF()->current_object['template'] ),
354 ( WPF()->current_userid ? 'wpf-auth' : 'wpf-guest' ),
355 'wpfu-group-' . WPF()->current_user_groupid,
356 'wpf-theme-' . WPF()->tpl->theme,
357 'wpf-is_standalone-' . (int) WPF()->board->get_current( 'is_standalone' ),
358 'wpf-boardid-' . WPF()->board->get_current( 'boardid' ),
359 'is_wpforo_page-' . (int) is_wpforo_page(),
360 'is_wpforo_url-' . (int) is_wpforo_url(),
361 'is_wpforo_shortcode_page-' . (int) is_wpforo_shortcode_page(),
362 ];
363 if( is_wpforo_page() ) $classes[] = 'wpforo';
364
365 return apply_filters( 'wpforo_get_body_classes', $classes );
366 }
367
368 add_action( 'wpforo_wrap_class', function() {
369 echo implode( ' ', wpforo_get_body_classes() );
370 } );
371
372 add_filter( 'body_class', function( $classes ) {
373 return array_merge( $classes, wpforo_get_body_classes() );
374 } );
375
376 function wpforo_get_postmeta( $postid, $metakeys = '', $single = false ) {
377 return WPF()->postmeta->get_postmeta( $postid, $metakeys, $single );
378 }
379
380
381 ###############################################################################
382 ########################## THEME API FUNCTIONS ################################
383 ###############################################################################
384
385 function _wpforo_post( $postid, $var = 'item' ) {
386 $post = ( $var === 'item' ) ? [] : null;
387 if( ! $postid ) return $post;
388
389 if( $var === 'url' ) {
390 $post['url'] = WPF()->post->get_url( $postid );
391 } elseif( $var === 'full_url' ) {
392 $post['full_url'] = WPF()->post->get_full_url( $postid );
393 } elseif( $var === 'short_url' ) {
394 $post['short_url'] = WPF()->post->get_short_url( $postid );
395 } elseif( $var === 'is_answered' ) {
396 $post['is_answered'] = WPF()->post->is_answered( $postid );
397 } elseif( $var === 'likes_count' ) {
398 $post['likes_count'] = WPF()->reaction->get_post_reactions_count( $postid );
399 } elseif( $var === 'likers_usernames' ) {
400 $post['likers_usernames'] = WPF()->reaction->get_post_reactions_user_dnames( $postid );
401 } else {
402 $post = WPF()->post->_get_post( $postid, false );
403 if( ! empty( $post ) ) {
404 $post['url'] = WPF()->post->get_url( $post );
405 $post['full_url'] = WPF()->post->get_full_url( $post );
406 $post['short_url'] = WPF()->post->get_short_url( $post );
407 $post['is_answered'] = WPF()->post->is_answered( $postid );
408 $post['likes_count'] = WPF()->reaction->get_post_reactions_count( $postid );
409 $post['likers_usernames'] = WPF()->reaction->get_post_reactions_user_dnames( $postid );
410 }
411 }
412
413 if( $var !== 'item' ) $post = wpfkey( $post, $var ) ? $post[ $var ] : wpforo_get_postmeta( $postid, $var, true );
414
415 return apply_filters( 'wpforo_post', $post, $var );
416 }
417
418 function wpforo_post( $postid, $var = 'item', $echo = false ) {
419 $post = ( $var === 'item' ) ? [] : null;
420 if( ! $postid ) return $post;
421 $cache = WPF()->cache->on( 'post' );
422 if( $cache ) $post = WPF()->cache->get_item( $postid, 'post' );
423 if( empty( $post ) ) {
424 $post = [];
425 if( ! $cache && $var === 'url' ) {
426 $post['url'] = WPF()->post->get_url( $postid );
427 } elseif( ! $cache && $var === 'full_url' ) {
428 $post['full_url'] = WPF()->post->get_full_url( $postid );
429 } elseif( ! $cache && $var === 'short_url' ) {
430 $post['short_url'] = WPF()->post->get_short_url( $postid );
431 } elseif( ! $cache && $var === 'is_answered' ) {
432 $post['is_answered'] = WPF()->post->is_answered( $postid );
433 } elseif( ! $cache && $var === 'likes_count' ) {
434 $post['likes_count'] = WPF()->reaction->get_post_reactions_count( $postid );
435 } elseif( ! $cache && $var === 'likers_usernames' ) {
436 $post['likers_usernames'] = WPF()->reaction->get_post_reactions_user_dnames( $postid );
437 } else {
438 $post = WPF()->post->get_post( $postid );
439 if( ! empty( $post ) ) {
440 $post['url'] = WPF()->post->get_url( $post );
441 $post['full_url'] = WPF()->post->get_full_url( $post );
442 $post['short_url'] = WPF()->post->get_short_url( $post );
443 $post['is_answered'] = WPF()->post->is_answered( $postid );
444 $post['likes_count'] = WPF()->reaction->get_post_reactions_count( $postid );
445 $post['likers_usernames'] = WPF()->reaction->get_post_reactions_user_dnames( $postid );
446 if( ! empty( $post ) ) {
447 $cache_item = [ $postid => $post ];
448 WPF()->cache->create( 'item', $cache_item, 'post' );
449 }
450 }
451 }
452 }
453
454 if( $var !== 'item' ) $post = wpfkey( $post, $var ) ? $post[ $var ] : wpforo_get_postmeta( $postid, $var, true );
455
456 if( $echo && is_scalar( $post ) ) echo $post;
457
458 return apply_filters( 'wpforo_post', $post, $var );
459 }
460
461 function _wpforo_topic( $topicid, $var = 'item' ) {
462 $topic = ( $var === 'item' ) ? [] : null;
463 if( ! $topicid ) return $topic;
464 if( $var === 'url' ) {
465 $topic['url'] = WPF()->topic->_get_url( $topicid );
466 } elseif( $var === 'full_url' ) {
467 $topic['full_url'] = WPF()->topic->get_full_url( $topicid );
468 } elseif( $var === 'short_url' ) {
469 $topic['short_url'] = WPF()->topic->get_short_url( $topicid );
470 } else {
471 $topic = WPF()->topic->_get_topic( $topicid, false );
472 if( ! empty( $topic ) ) {
473 $topic['url'] = WPF()->topic->_get_url( $topic );
474 $topic['full_url'] = WPF()->topic->get_full_url( $topic );
475 $topic['short_url'] = WPF()->topic->get_short_url( $topic );
476 }
477 }
478
479 if( $var !== 'item' ) {
480 if( $var === 'body' ) {
481 $topic = wpforo_bigintval( wpfval( $topic, 'first_postid' ) ) ? _wpforo_post(
482 $topic['first_postid'],
483 'body'
484 ) : '';
485 } else {
486 $topic = wpfval( $topic, $var );
487 }
488 }
489
490 return apply_filters( 'wpforo_topic', $topic, $var );
491 }
492
493 function wpforo_topic( $topicid, $var = 'item', $echo = false ) {
494 $topic = ( $var === 'item' ) ? [] : null;
495 if( ! $topicid ) return $topic;
496 $cache = WPF()->cache->on( 'topic' );
497 if( $cache ) $topic = WPF()->cache->get_item( $topicid, 'topic' );
498
499 if( empty( $topic ) ) {
500 $topic = [];
501 if( ! $cache && $var === 'url' ) {
502 $topic['url'] = WPF()->topic->get_url( $topicid );
503 } elseif( ! $cache && $var === 'full_url' ) {
504 $topic['full_url'] = WPF()->topic->get_full_url( $topicid );
505 } elseif( ! $cache && $var === 'short_url' ) {
506 $topic['short_url'] = WPF()->topic->get_short_url( $topicid );
507 } else {
508 $topic = WPF()->topic->get_topic( $topicid );
509 if( ! empty( $topic ) ) {
510 $topic['url'] = WPF()->topic->get_url( $topic );
511 $topic['full_url'] = WPF()->topic->get_full_url( $topic );
512 $topic['short_url'] = WPF()->topic->get_short_url( $topic );
513 if( ! empty( $topic ) ) {
514 $cache_item = [ $topicid => $topic ];
515 WPF()->cache->create( 'item', $cache_item, 'topic' );
516 }
517 }
518 }
519 }
520
521 if( $var !== 'item' ) {
522 if( $var === 'body' ) {
523 $topic = wpforo_post( $topic['first_postid'], 'body' );
524 } else {
525 $topic = wpfval( $topic, $var );
526 }
527 }
528
529 if( $echo && is_scalar( $topic ) ) echo $topic;
530
531 return apply_filters( 'wpforo_topic', $topic, $var );
532 }
533
534 function _wpforo_forum( $forumid, $var = 'item' ) {
535 $forum = ( $var === 'item' ) ? [] : null;
536 if( ! $forumid ) return $forum;
537
538 $forum = WPF()->forum->_get_forum( $forumid );
539 if( ! empty( $forum ) ) {
540 if( in_array( $var, [ 'childs', 'counts' ], true ) ) {
541 $forum['childs'] = WPF()->forum->get_childs( $forumid );
542 $forum['childs'][] = $forumid;
543 if( $var === 'counts' ) $forum['counts'] = WPF()->forum->get_counts( $forum['childs'] );
544 }
545 }
546
547 if( $var !== 'item' ) $forum = wpfval( $forum, $var );
548
549 return apply_filters( 'wpforo_forum', $forum, $var );
550 }
551
552 function wpforo_forum( $forumid, $var = 'item', $echo = false ) {
553 $forum = ( $var === 'item' ) ? [] : null;
554 $cache = WPF()->cache->on( 'forum' );
555 if( ! $forumid ) return $forum;
556 if( $cache ) $forum = WPF()->cache->get_item( $forumid, 'forum' );
557
558 if( empty( $forum ) ) {
559 $forum = [];
560 if( ! $cache && in_array( $var, [ 'childs', 'counts' ], true ) ) {
561 $forum['childs'] = WPF()->forum->get_childs( $forumid );
562 $forum['childs'][] = $forumid;
563 if( $var === 'counts' ) $forum['counts'] = WPF()->forum->get_counts( $forum['childs'] );
564 } else {
565 $forum = WPF()->forum->get_forum( $forumid );
566 if( ! empty( $forum ) ) {
567 if( $cache ) {
568 $forum['childs'] = WPF()->forum->get_childs( $forum['forumid'] );
569 $forum['childs'][] = $forum['forumid'];
570 $forum['counts'] = WPF()->forum->get_counts( $forum['childs'] );
571 }
572 if( ! empty( $forum ) ) {
573 $cache_item = [ $forumid => $forum ];
574 WPF()->cache->create( 'item', $cache_item, 'forum' );
575 }
576 }
577 }
578 }
579
580 if( $var !== 'item' ) $forum = wpfval( $forum, $var );
581
582 if( $echo && is_scalar( $forum ) ) echo $forum;
583
584 return apply_filters( 'wpforo_forum', $forum, $var );
585 }
586
587 function wpforo_member( $object, $var = 'item', $echo = false ) {
588 $member = null;
589 if( ! empty( $object ) ) {
590 if( is_array( $object ) && ! wpforo_bigintval( wpfval( $object, 'userid' ) ) ) {
591 //If there is no user ID or it's 0 generate guest user array
592 $member = WPF()->member->get_guest( $object );
593 } else {
594 //If there is a userid get user array
595 $userid = ( is_array( $object ) && isset( $object['userid'] ) ) ? intval( $object['userid'] ) : intval(
596 $object
597 );
598 $member = WPF()->member->get_member( $userid );
599 // If userid exists but the user is removed from wp_users generate guest user array
600 if( empty( $member ) ) {
601 $object = is_array( $object ) ? $object : [];
602 $member = WPF()->member->get_guest( $object );
603 }
604 }
605 $member = apply_filters( 'wpforo_get_member', $member );
606
607 if( $var !== 'item' && $var ) {
608 if( $member ) {
609 if( $var === 'secondary_group_names' ) {
610 $secondary_group_names = [];
611 if( $secondary_groupids = (array) wpfval( $member, 'secondary_groupids' ) ) {
612 $secondary_group_names = array_map( function( $groupid ) {
613 $group = WPF()->usergroup->get_usergroup( $groupid );
614
615 return wpfval( $group, 'name' );
616 }, $secondary_groupids );
617 $secondary_group_names = array_filter( $secondary_group_names );
618 }
619 $member = $secondary_group_names;
620 } elseif( $var === 'group_names' ) {
621 $group_names = [];
622 if( $groupids = (array) wpfval( $member, 'groupids' ) ) {
623 $group_names = array_map( function( $groupid ) {
624 $group = WPF()->usergroup->get_usergroup( $groupid );
625
626 return wpfval( $group, 'name' );
627 }, $groupids );
628 $group_names = array_filter( $group_names );
629 }
630 $member = $group_names;
631 } else {
632 $member = wpfval( $member, $var );
633 }
634 } else {
635 $member = null;
636 }
637 }
638 }
639
640 if( $echo ) echo $member;
641
642 return apply_filters( 'wpforo_member', $member, $var );
643 }
644
645 function wpforo_current_usermeta( $key ) {
646 if( wpfkey( WPF()->current_usermeta, $key ) ) {
647 if( wpfkey( WPF()->current_usermeta[ $key ], 0 ) ) {
648 $meta = maybe_unserialize( WPF()->current_usermeta[ $key ][0] );
649
650 return $meta;
651 }
652 }
653 }
654
655 function _wpforo_tag( $tagid, $var = 'item' ) {
656 $tag = ( $var == 'item' ) ? [] : null;
657 if( ! $tagid ) return $tag;
658
659 if( $var === 'url' && wpfval( $tag, 'tag' ) ) {
660 $tag['url'] = wpforo_home_url() . '?wpfin=tag&wpfs=' . $tag['tag'];
661 } else {
662 $tag = WPF()->topic->get_tag( $tagid );
663 if( ! empty( $tag ) ) $tag['url'] = wpforo_home_url() . '?wpfin=tag&wpfs=' . $tag['tag'];
664 }
665
666 if( $var !== 'item' ) $tag = wpfval( $tag, $var );
667
668 return $tag;
669 }
670
671 function wpforo_tag( $tagid, $var = 'item', $echo = false ) {
672 $tag = ( $var == 'item' ) ? [] : null;
673 if( ! $tagid ) return $tag;
674 $cache = WPF()->cache->on( 'tag' );
675
676 if( $cache ) $tag = WPF()->cache->get_item( md5( $tagid ), 'tag' );
677
678 if( empty( $tag ) ) {
679 $tag = [];
680 if( ! $cache && $var == 'url' && wpfval( $tag, 'tag' ) ) {
681 $tag['url'] = wpforo_home_url() . '?wpfin=tag&wpfs=' . $tag['tag'];
682 } else {
683 $tag = WPF()->topic->get_tag( $tagid );
684 if( ! empty( $tag ) ) {
685 $tag['url'] = wpforo_home_url() . '?wpfin=tag&wpfs=' . $tag['tag'];
686 if( ! empty( $tag ) ) {
687 $cache_item = [ md5( $tagid ) => $tag ];
688 WPF()->cache->create( 'item', $cache_item, 'tag' );
689 }
690 }
691 }
692 }
693
694 if( $var !== 'item' ) $tag = wpfval( $tag, $var );
695
696 if( $echo && is_scalar( $tag ) ) echo $tag;
697
698 return $tag;
699 }
700
701 function wpforo_member_link( $user, $prefix = '', $size = 30, $class = '', $echo = true, $content = '', $attr = '' ) {
702 $dname = wpforo_user_dname( $user );
703 $title = $dname ? sprintf( 'title="%1$s"', esc_attr( $dname ) ) : '';
704 // Append is_guest class when userid == 0 to allow styling (e.g., disable hover effects)
705 $is_guest = (int) wpfval( $user, 'userid' ) === 0;
706 $classes = trim( (string) $class );
707 if( $is_guest ) {
708 $classes = $classes ? ( $classes . ' is_guest' ) : 'is_guest';
709 }
710 $class_attr = $classes ? sprintf( 'class="%1$s"', esc_attr( $classes ) ) : '';
711 $color = wpfval( $user, 'group_color' ) ? sprintf( 'color: %1$s', $user['group_color'] ) : '';
712 if( $content === 'avatar' ) {
713 $ret = wpforo_user_avatar( $user, $size, $attr );
714 } elseif( trim( (string) $content ) ) {
715 $ret = $content;
716 } else {
717 $ret = ( strpos( (string) $prefix, '%s' ) !== false ? sprintf(
718 wpforo_phrase( $prefix, false ),
719 esc_html( wpforo_text( $dname, $size, false ) )
720 ) : ( $prefix ? wpforo_phrase( $prefix, false ) . ' ' : '' ) . esc_html(
721 wpforo_text( $dname, $size, false )
722 ) );
723 }
724
725 if( apply_filters( 'wpforo_member_link_clickable', true, $user ) && wpfval( $user, 'userid' ) && wpfval(
726 $user,
727 'profile_url'
728 ) ) {
729 $ret = sprintf(
730 '<a href="%1$s" style="%2$s" %3$s %4$s>%5$s</a>',
731 esc_url( (string) $user['profile_url'] ),
732 $color,
733 $class_attr,
734 $title,
735 $ret
736 );
737 } else {
738 $ret = sprintf(
739 '<a style="%1$s" %2$s %3$s>%4$s</a>',
740 $color,
741 $class_attr,
742 $title,
743 $ret
744 );
745 }
746
747 if( $echo ) echo $ret;
748
749 return $ret;
750 }
751
752 add_shortcode( 'wpforo-lostpassword', function() {
753 $ob_exists = function_exists( 'ob_start' ) && function_exists( 'ob_get_clean' );
754 if( $ob_exists ) ob_start();
755 ?>
756 <p id="wpforo-title"><?php wpforo_phrase( 'Reset Password' ) ?></p>
757 <form method="POST">
758 <?php wp_nonce_field( 'lostpassword', '_wpfnonce', false ) ?>
759 <input type="hidden" name="wpfaction" value="lostpassword">
760 <div class="wpforo-login-wrap wpfbg-9">
761 <div class="wpforo-login-content">
762 <h3><?php wpforo_phrase( 'Forgot Your Password?' ) ?></h3>
763 <div class="wpforo-table wpforo-login-table">
764 <div class="wpf-tr row-0">
765 <div class="wpf-td wpfw-1 row_0-col_0" style="padding-top:10px;">
766 <div class="wpf-field wpf-field-type-text">
767 <div class="wpf-field-wrap">
768 <label for="userlogin"
769 style="display: block; text-align: center; font-size: 14px; padding-bottom: 10px;"><?php wpforo_phrase(
770 'Please Insert Your Email or Username'
771 ) ?></label>
772 <input id="userlogin" autofocus required type="text" name="user_login"
773 class="wpf-login-text"/>
774 <div style="text-align: center; font-size: 13px; padding-top: 10px; line-height: 18px;"><?php wpforo_phrase(
775 'Enter your email address or username and we\'ll send you a link you can use to pick a new password.'
776 ) ?></div>
777 </div>
778 <div class="wpf-field-cl"></div>
779 </div>
780 <div class="wpf-field wpf-field-type-text wpf-field-hook">
781 <div class="wpf-field-wrap">
782 <?php do_action( 'lostpassword_form' ) ?>
783 <div class="wpf-field-cl"></div>
784 </div>
785 <div class="wpf-field-cl"></div>
786 </div>
787 <div class="wpf-field">
788 <div class="wpf-field-wrap" style="text-align:center; width:100%;">
789 <input type="submit" value="<?php wpforo_phrase( 'Reset Password' ) ?>"/>
790 </div>
791 <div class="wpf-field-cl"></div>
792 </div>
793 <div class="wpf-field wpf-extra-field-end">
794 <div class="wpf-field-wrap" style="text-align:center; width:100%;">
795 <?php do_action( 'wpforo_lostpass_form_end' ) ?>
796 <div class="wpf-field-cl"></div>
797 </div>
798 </div>
799 <div class="wpf-cl"></div>
800 </div>
801 </div>
802 </div>
803 </div>
804 </div>
805 </form>
806 <?php
807 return ( $ob_exists ) ? trim( (string) ob_get_clean() ) : '';
808 } );
809
810 add_shortcode( 'wpforo-resetpassword', function() {
811 $ob_exists = function_exists( 'ob_start' ) && function_exists( 'ob_get_clean' );
812 if( $ob_exists ) ob_start();
813 ?>
814 <p id="wpforo-title"><?php wpforo_phrase( 'Reset Password' ) ?></p>
815
816 <form method="POST" autocomplete="off">
817 <input type="hidden" name="wpfaction" value="resetpassword">
818 <div class="wpforo-login-wrap">
819 <div class="wpforo-login-content">
820 <div class="wpforo-table wpforo-login-table">
821 <div class="wpf-tr row-0">
822 <div class="wpf-td wpfw-1 row_0-col_0" style="padding-top:10px;">
823 <div class="wpf-field wpf-field-type-text">
824 <div class="wpf-field-wrap">
825 <label for="pass1"
826 style="display: block; text-align: center; font-size: 14px; padding-bottom: 10px;"><?php wpforo_phrase(
827 'New password'
828 ) ?></label>
829 <input type="password" name="pass1" id="pass1" class="input" size="20" value=""
830 autocomplete="off" required autofocus/>
831 </div>
832 <div class="wpf-field-cl"></div>
833 </div>
834 <div class="wpf-field wpf-field-type-text">
835 <div class="wpf-field-wrap">
836 <label for="pass2"
837 style="display: block; text-align: center; font-size: 14px; padding-bottom: 10px;"><?php wpforo_phrase(
838 'Repeat new password'
839 ) ?></label>
840 <input type="password" name="pass2" id="pass2" class="input" size="20" value=""
841 autocomplete="off" required/>
842 </div>
843 <div class="wpf-field-cl"></div>
844 </div>
845 <div class="wpf-field wpf-field-type-text">
846 <div class="wpf-field-wrap">
847 <?php printf(
848 wpforo_phrase(
849 'Password length must be between %d characters and %d characters.',
850 false
851 ),
852 WPF()->member->pass_min_length,
853 WPF()->member->pass_max_length
854 ); ?>
855 </div>
856 <div class="wpf-field-cl"></div>
857 </div>
858 <div class="wpf-field">
859 <div class="wpf-field-wrap" style="text-align:center; width:100%;">
860 <input type="submit" value="<?php wpforo_phrase( 'Reset Password' ); ?>"/>
861 </div>
862 <div class="wpf-field-cl"></div>
863 </div>
864 <div class="wpf-field wpf-extra-field-end">
865 <div class="wpf-field-wrap" style="text-align:center; width:100%;">
866 <?php do_action( 'wpforo_resetpass_form_end' ) ?>
867 <div class="wpf-field-cl"></div>
868 </div>
869 </div>
870 <div class="wpf-cl"></div>
871 </div>
872 </div>
873 </div>
874 </div>
875 </div>
876 </form>
877 <?php
878 return ( $ob_exists ) ? trim( (string) ob_get_clean() ) : '';
879 } );
880
881 add_shortcode( 'wpforo-login-form', function() {
882 $ob_exists = function_exists( 'ob_start' ) && function_exists( 'ob_get_clean' );
883 if( $ob_exists ) ob_start();
884 if( $path = WPF()->tpl->get_template_path( 'login' ) ) include( $path );
885
886 return ( $ob_exists ) ? trim( (string) ob_get_clean() ) : '';
887 } );
888
889 #############################################################################################
890 /**
891 * Generates according page form fields using tpl->form_fields() function
892 *
893 * @param array $fields arguments
894 * @param boolean $echo
895 *
896 * @return string form fields HTML
897 * @since 1.4.0
898 *
899 */
900 function wpforo_fields( $fields, $echo = true ) {
901 if( empty( $fields ) ) return '';
902 $fields = apply_filters( 'wpforo_form_fields', $fields );
903 $html = WPF()->form->build( $fields );
904 if( $echo ) {
905 echo $html;
906 } else {
907 return $html;
908 }
909 }
910
911 function wpforo_user_avatar( $user, $size = 96, $attr = '', $lastmod = false ) {
912 $img = WPF()->member->get_avatar( $user, $size, $attr );
913 if( $lastmod && ( $url = wpforo_avatar_url( $img ) ) && strpos( (string) $url, '?' ) === false ) {
914 $img = str_replace( $url, $url . '?lm=' . time(), $img );
915 }
916
917 return $img;
918 }
919
920 function wpforo_signature( $member, $args = [] ) {
921
922 if( is_numeric( $member ) ) $member = wpforo_member( $member );
923
924 if( wpfkey( $member, 'rating', 'level' ) ) {
925 $min_level = apply_filters( 'wpforo_min_rating_level_for_signature', 1 );
926 if( $min_level && (int) $member['rating']['level'] < $min_level ) return '';
927 }
928
929 if( WPF()->current_userid != wpfval( $member, 'userid' ) && ! WPF()->usergroup->can( 'vms' ) ) return '';
930
931 $signature = '';
932 $default = [ 'nofollow' => 1, 'kses' => 1, 'echo' => 1 ];
933 if( empty( $args ) ) {
934 $args = $default;
935 } else {
936 $args = wpforo_parse_args( $args, $default );
937 }
938
939 if( is_array( $member ) && ! empty( $member ) ) {
940 $signature = ( isset( $member['signature'] ) ) ? $member['signature'] : '';
941 } elseif( is_string( $member ) ) {
942 $signature = $member;
943 }
944
945 $signature = stripslashes( (string) $signature );
946
947 if( ! empty( $args ) ) {
948 $kses = isset( $args['kses'] ) ? $args['kses'] : 1;
949 $nofollow = isset( $args['nofollow'] ) ? $args['nofollow'] : 1;
950 if( isset( $kses ) && $kses ) $signature = wpforo_kses( $signature, 'user_description' );
951 if( isset( $nofollow ) && $nofollow ) $signature = wpforo_nofollow_tag( $signature );
952 } else {
953 $signature = wpforo_nofollow( wpforo_kses( $signature, 'user_description' ) );
954 }
955
956 $length = apply_filters( 'wpforo_signature_length', 0 );
957 $signature = wpforo_text( $signature, $length, false, false, false, false );
958 $signature = wpautop( $signature );
959
960 if( $args['echo'] ) {
961 echo $signature;
962 } else {
963 return $signature;
964 }
965 }
966
967 function wpforo_register_fields() {
968 $fields = WPF()->member->get_register_fields();
969 do_action( 'wpforo_register_page_start', $fields );
970
971 return $fields;
972 }
973
974 function wpforo_account_fields() {
975 $fields = WPF()->member->get_account_fields();
976 do_action( 'wpforo_account_page_start', $fields );
977
978 return $fields;
979 }
980
981 function wpforo_profile_fields() {
982 $fields = WPF()->member->get_profile_fields();
983 do_action( 'wpforo_profile_page_start', $fields );
984
985 return $fields;
986 }
987
988 function wpforo_search_fields() {
989 $fields = WPF()->member->get_search_fields();
990 do_action( 'wpforo_search_page_start', $fields );
991
992 if( wpforo_setting( 'members', 'search_type' ) === 'search' ) {
993 $fields = [
994 [
995 [
996 [
997 'type' => 'search',
998 'isDefault' => 1,
999 'isRemovable' => 0,
1000 'isRequired' => 0,
1001 'isEditable' => 1,
1002 'class' => 'wpf-member-search-field',
1003 'label' => wpforo_phrase( 'Find a member', false ),
1004 'title' => wpforo_phrase( 'Find a member', false ),
1005 'placeholder' => wpforo_phrase( 'Display Name or Nicename', false ),
1006 'faIcon' => 'fas fa-search',
1007 'name' => 'wpfms',
1008 'cantBeInactive' => [ 'search' ],
1009 'can' => '',
1010 'isSearchable' => 1,
1011 ],
1012 ],
1013 ],
1014 ];
1015 }
1016
1017 return $fields;
1018 }
1019
1020 function wpforo_topic_types( $topic, $class = true, $echo = true ) {
1021 $topic_types = [];
1022 if( wpfval( $topic, 'type' ) ) $topic_types[] = 'sticky';
1023 if( wpfval( $topic, 'closed' ) ) $topic_types[] = 'closed';
1024 if( wpfval( $topic, 'private' ) ) $topic_types[] = 'private';
1025 if( wpfval( $topic, 'solved' ) ) $topic_types[] = 'solved';
1026 if( wpfval( $topic, 'status' ) ) $topic_types[] = 'unapproved';
1027 $types = $class ? ( ! empty( $topic_types ) ? trim(
1028 'wpf-tt-' . implode( ' wpf-tt-', $topic_types ),
1029 ','
1030 ) : '' ) : $topic_types;
1031 if( $echo ) echo $types;
1032
1033 return $types;
1034 }
1035
1036 function wpforo_unread( $itemid, $item, $echo = true, $postid = 0 ) {
1037 $class = '';
1038 $unread = false;
1039 $login = is_user_logged_in();
1040 $login = apply_filters( 'wpforo_unread_logging_for_guests', $login );
1041 if( $login ) {
1042 if( $item === 'forum' ) {
1043 $class = 'wpf-unread-forum';
1044 $unread = WPF()->log->unread( $itemid, 'forum' );
1045 $unread = apply_filters( 'wpforo_unread_forum', $unread, $itemid );
1046 } elseif( $item === 'topic' ) {
1047 $class = 'wpf-unread-topic';
1048 $unread = WPF()->log->unread( $itemid, 'topic' );
1049 $unread = apply_filters( 'wpforo_unread_topic', $unread, $itemid );
1050 } elseif( $item === 'post' ) {
1051 $class = 'wpf-unread-post';
1052 $unread = WPF()->log->unread( $itemid, 'post', $postid );
1053 $unread = apply_filters( 'wpforo_unread_post', $unread, $itemid, $postid );
1054 }
1055 }
1056 $class = ( $unread ) ? apply_filters( 'wpforo_unread_class', $class, $itemid, $item ) : '';
1057
1058 if( $echo ) echo $class;
1059
1060 return $class;
1061 }
1062
1063 function wpforo_unread_forum( $logid, $return = 'class', $echo = true ) {
1064 $unread = WPF()->log->unread( $logid, 'forum' );
1065 if( $return === 'class' ) {
1066 $log = $unread ? 'wpf_forum_unread' : '';
1067 } else {
1068 $log = (bool) $unread;
1069 }
1070
1071 if( $echo ) echo $log;
1072
1073 return $log;
1074 }
1075
1076 function wpforo_unread_topic( $logid, $return = 'class', $echo = true ) {
1077 $unread = WPF()->log->unread( $logid, 'topic' );
1078 if( $return === 'class' ) {
1079 $log = $unread ? 'wpf_topic_unread' : '';
1080 } else {
1081 $log = (bool) $unread;
1082 }
1083
1084 if( $echo ) echo $log;
1085
1086 return $log;
1087 }
1088
1089 if( ! function_exists( 'custom_wpforo_get_account_fields' ) ) {
1090 function custom_wpforo_get_account_fields( $fields ) {
1091 $hide = [
1092 'user_email',
1093 'user_nicename',
1094 ];
1095
1096 foreach( $fields as $row_key => $row ) {
1097 foreach( $row as $col_key => $col ) {
1098 foreach( $col as $key => $field ) {
1099 if( in_array( $field['fieldKey'], $hide ) ) {
1100 unset( $fields[ $row_key ][ $col_key ][ $key ] );
1101 }
1102 }
1103 }
1104 }
1105
1106 return $fields;
1107 }
1108 }
1109
1110 function wpforo_moderation_tools() {
1111 if( empty( WPF()->current_object['forumid'] ) || empty( WPF()->current_object['topicid'] ) ) return;
1112 ?>
1113 <div id="wpf_moderation_tools" class="wpf-tools">
1114 <?php
1115 $tabs = [];
1116 if( is_user_logged_in() && WPF()->perm->forum_can( 'mt' ) ) {
1117 $posts = (int) wpfval( WPF()->current_object, 'topic', 'posts' );
1118 $tabs[] = [
1119 'title' => wpforo_phrase( 'Move Topic', false ),
1120 'id' => 'topic_move_form',
1121 'class' => 'wpft-move',
1122 'icon' => 'far fa-share-square',
1123 ];
1124 if( $posts > 1 ) {
1125 $tabs[] = [
1126 'title' => wpforo_phrase( 'Move Reply', false ),
1127 'id' => 'reply_move_form',
1128 'class' => 'wpft-reply-move',
1129 'icon' => 'far fa-share-square',
1130 ];
1131 }
1132 $tabs[] = [
1133 'title' => wpforo_phrase( 'Merge Topics', false ),
1134 'id' => 'topic_merge_form',
1135 'class' => 'wpft-merge',
1136 'icon' => 'fas fa-code-branch',
1137 ];
1138 if( $posts > 1 ) {
1139 $tabs[] = [
1140 'title' => wpforo_phrase( 'Split Topic', false ),
1141 'id' => 'topic_split_form',
1142 'class' => 'wpft-split',
1143 'icon' => 'fas fa-cut',
1144 ];
1145 }
1146 }
1147 WPF()->tpl->topic_moderation_tabs( $tabs );
1148 ?>
1149 </div>
1150 <?php
1151 }
1152
1153 /**
1154 * Add an activity item.
1155 *
1156 * @param array $args {
1157 * An array of arguments.
1158 *
1159 * @type string $action Optional. The activity action/description, typically something like "Joe posted an update".
1160 * @type string $title Optional. The title of the activity item.
1161 * @type string $content Optional. The content of the activity item.
1162 * @type string $component The unique name of the component associated with the activity item - 'activity', etc.
1163 * @type string $type The specific activity type, used for directory filtering. 'wpforo_topic', 'wpforo_post', etc.
1164 * @type string $primary_link Optional. The URL for this item, as used in RSS feeds. Defaults to the URL for this activity item's permalink page.
1165 * @type int|bool $user_id Optional. The ID of the user associated with the activity item. May be set to false or 0 if the item is not related to any user. Default: the ID of the currently logged-in user.
1166 * @type string $date_recorded Optional. The GMT time, in Y-m-d h:i:s format, when the item was recorded. Defaults to the current time.
1167 * }
1168 * @return NULL
1169 * @since 1.4.6
1170 */
1171 function wpforo_activity( $args = [] ) {
1172 $default = [
1173 'action' => '',
1174 'title' => '',
1175 'content' => '',
1176 'component' => 'community',
1177 'type' => '',
1178 'primary_link' => '',
1179 'user_id' => '',
1180 'item_id' => '',
1181 'date_recorded' => '',
1182 ];
1183 $args = wpforo_parse_args( $args, $default );
1184
1185 //BuddyPress Member Activity
1186 if( wpforo_setting( 'buddypress', 'activity' ) && function_exists( 'wpforo_bp_activity' ) ) {
1187 wpforo_bp_activity( $args );
1188 }
1189 }
1190
1191 function wpforo_activity_delete( $args = [] ) {
1192
1193 $default = [
1194 'action' => '',
1195 'title' => '',
1196 'content' => '',
1197 'component' => 'community',
1198 'type' => '',
1199 'primary_link' => '',
1200 'user_id' => '',
1201 'item_id' => '',
1202 'date_recorded' => '',
1203 ];
1204 $args = wpforo_parse_args( $args, $default );
1205
1206 //Delete BuddyPress Member Activity
1207 if( wpforo_setting( 'buddypress', 'activity' ) && function_exists( 'wpforo_bp_activity_delete' ) ) {
1208 wpforo_bp_activity_delete( $args );
1209 }
1210 }
1211
1212 function wpforo_activity_content( $item = [] ) {
1213 $args = [];
1214 if( empty( $item ) ) return false;
1215 if( ( isset( $item['status'] ) && $item['status'] ) || ( isset( $item['private'] ) && $item['private'] ) ) return false;
1216 if( isset( $item['forumid'] ) && $item['forumid'] ) {
1217 $private_for_usergroups = [ 3, 4, 5 ];
1218 $private_for_usergroups = apply_filters( 'wpforo_activity_private_for_usergroups', $private_for_usergroups );
1219 if( ! empty( $private_for_usergroups ) && WPF()->forum->private_forum(
1220 $item['forumid'],
1221 $private_for_usergroups
1222 ) ) {
1223 return false;
1224 }
1225 }
1226
1227 if( isset( $item['first_postid'] ) && $item['first_postid'] ) {
1228 $args['item_id'] = $item['first_postid'];
1229 } elseif( isset( $item['postid'] ) && $item['postid'] ) {
1230 $args['item_id'] = $item['postid'];
1231 }
1232 $args['user_id'] = ( isset( $item['userid'] ) && $item['userid'] ) ? $item['userid'] : $args['user_id'] = WPF()->current_userid;
1233 $member = wpforo_member( $args['user_id'] );
1234 if( isset( $item['topicurl'] ) ) {
1235 $args['type'] = 'wpforo_topic';
1236 $args['content'] = ( wpfval( $item, 'body' ) ) ? $item['body'] : '';
1237 $args['primary_link'] = $item['topicurl'];
1238 if( isset( $item['title'] ) ) $args['title'] = $item['title'];
1239 if( $args['title'] ) $args['title'] = ' "' . esc_html( $args['title'] ) . '"';
1240 $args['action'] = sprintf( wpforo_phrase( '%s posted a new topic %s', false ), '', '' );
1241 } elseif( isset( $item['posturl'] ) ) {
1242 $args['type'] = 'wpforo_post';
1243 $args['content'] = ( wpfval( $item, 'body' ) ) ? $item['body'] : '';
1244 $args['primary_link'] = $item['posturl'];
1245 if( isset( $item['title'] ) ) $args['title'] = preg_replace( '|^.+?:\s*|is', '', (string) $item['title'] );
1246 if( $args['title'] ) $args['title'] = ' "' . esc_html( $args['title'] ) . '"';
1247 $args['action'] = sprintf( wpforo_phrase( '%s replied to the topic %s', false ), '', '' );
1248 }
1249 if( $args['content'] ) {
1250 $content_words = explode( ' ', $args['content'] );
1251 $content_words = count( $content_words );
1252 $content_words_cut = apply_filters( 'wpforo_activity_content_words', '40' );
1253 if( (int) $content_words_cut < (int) $content_words && $args['primary_link'] ) {
1254 $more = '... &nbsp; <a href="' . $args['primary_link'] . '">' . wpforo_phrase(
1255 'read more',
1256 false
1257 ) . '&raquo;</a>';
1258 $args['content'] = wp_trim_words( $args['content'], 40, $more );
1259 }
1260 }
1261 wpforo_activity( $args );
1262 }
1263
1264 function wpforo_activity_content_delete( $item = [] ) {
1265 $args = [];
1266 if( empty( $item ) ) return false;
1267 if( wpfval( $item, 'first_postid' ) ) {
1268 $args['item_id'] = $item['first_postid'];
1269 $args['type'] = 'wpforo_topic';
1270 } elseif( wpfval( $item, 'is_first_post' ) ) {
1271 $args['item_id'] = $item['postid'];
1272 $args['type'] = 'wpforo_topic';
1273 } elseif( wpfval( $item, 'postid' ) ) {
1274 $args['item_id'] = $item['postid'];
1275 $args['type'] = 'wpforo_post';
1276 }
1277 if( wpfval( $args, 'item_id' ) && wpfval( $args, 'type' ) ) wpforo_activity_delete( $args );
1278 }
1279
1280 function wpforo_activity_content_on_post_status_change( $post, $status = 0 ) {
1281 if( ! empty( $post ) ) {
1282 $post['status'] = $status;
1283 $post['posturl'] = WPF()->post->get_url( $post['postid'] );
1284 if( ! (int) wpfval( $post, 'is_first_post' ) ) {
1285 if( $status ) {
1286 wpforo_activity_content_delete( $post );
1287 } else {
1288 wpforo_activity_content( $post );
1289 }
1290 }
1291 }
1292 }
1293
1294 add_action( 'wpforo_post_status_update', 'wpforo_activity_content_on_post_status_change', 9, 2 );
1295
1296 function wpforo_activity_content_on_topic_status_change( $topic, $status = 0 ) {
1297 if( ! empty( $topic ) ) {
1298 $topic['status'] = $status;
1299 $topic['topicurl'] = WPF()->topic->get_url( $topic['topicid'] );
1300 if( $status ) {
1301 wpforo_activity_content_delete( $topic );
1302 } else {
1303 wpforo_activity_content( $topic );
1304 }
1305 }
1306 }
1307
1308 add_action( 'wpforo_topic_status_update', 'wpforo_activity_content_on_topic_status_change', 9, 2 );
1309
1310 function wpforo_activity_like( $item = [] ) {
1311 $args = [];
1312 if( empty( $item ) ) return;
1313 if( ( isset( $item['status'] ) && $item['status'] ) || ( isset( $item['private'] ) && $item['private'] ) ) return;
1314 if( isset( $item['forumid'] ) && $item['forumid'] ) {
1315 $private_for_usergroups = [ 3, 4, 5 ];
1316 $private_for_usergroups = apply_filters( 'wpforo_activity_private_for_usergroups', $private_for_usergroups );
1317 if( ! empty( $private_for_usergroups ) && WPF()->forum->private_forum(
1318 $item['forumid'],
1319 $private_for_usergroups
1320 ) ) {
1321 return;
1322 }
1323 }
1324 if( isset( $item['postid'] ) && $item['postid'] ) $args['item_id'] = $item['postid'];
1325 $args['user_id'] = WPF()->current_userid;
1326 $args['type'] = 'wpforo_like';
1327 $item = wpforo_post( $item['postid'] );
1328 if( isset( $item['url'] ) && $item['url'] ) $args['primary_link'] = $item['url'];
1329 if( isset( $item['title'] ) ) $args['title'] = preg_replace( '|^.+?:\s*|is', '', (string) $item['title'] );
1330 if( $args['title'] ) $args['title'] = ' "' . esc_html( $args['title'] ) . '"';
1331 $args['action'] = sprintf( wpforo_phrase( '%s liked forum post %s', false ), '', '' );
1332 wpforo_activity( $args );
1333 }
1334
1335 function wpforo_activity_like_delete( $item = [] ) {
1336 $args = [];
1337 if( empty( $item ) ) return false;
1338 if( isset( $item['postid'] ) && $item['postid'] ) {
1339 $args['item_id'] = $item['postid'];
1340 $args['type'] = 'wpforo_like';
1341 }
1342 if( $args['item_id'] && $args['type'] ) wpforo_activity_delete( $args );
1343 }
1344
1345 add_action( 'wpforo_after_add_topic', 'wpforo_activity_content', 9 );
1346 add_action( 'wpforo_after_add_post', 'wpforo_activity_content', 9 );
1347 add_action( 'wpforo_like', 'wpforo_activity_like', 9 );
1348 add_action( 'wpforo_after_delete_post', 'wpforo_activity_content_delete', 9 );
1349 add_action( 'wpforo_after_delete_post', 'wpforo_activity_like_delete', 9 );
1350
1351 function wpforo_user_field( $field = '', $userid = 0, $echo = true ) {
1352 $userid = ( ! $userid ) ? WPF()->current_userid : $userid;
1353 if( ! $field || ! $userid ) return false;
1354 $field = wpforo_member( $userid, $field );
1355 $field = apply_filters( 'wpforo_user_field', $field, $userid );
1356 if( ! is_array( $field ) && $field ) {
1357 if( $echo ) {
1358 echo $field;
1359 } else {
1360 return $field;
1361 }
1362 }
1363 }
1364
1365 /**
1366 * @param array $post
1367 * @param bool $echo
1368 *
1369 * @return string|void
1370 */
1371 function wpforo_content( $post, $echo = true ) {
1372 $content = '';
1373 if( is_array( $post ) && isset( $post['body'] ) ) {
1374 $content = apply_filters( 'wpforo_content_before', $post['body'], $post );
1375 $content = wpforo_kses( $content, 'post' );
1376 $content = apply_filters( 'wpforo_content', $content, $post );
1377 $content = wpforo_content_filter( $content, $post );
1378 $content = apply_filters( 'wpforo_content_after', $content, $post );
1379 $content .= '<br style="margin: 0; padding:0; width:0; height: 0; clear: both">';
1380 }
1381 if( ! $echo ) return $content;
1382 echo $content;
1383 }
1384
1385 function wpforo_share_toggle( $url = '', $text = '', $location = 'side', $custom = false ) {
1386 $position = in_array( wpforo_setting( 'social', 'sb_location_toggle' ), [ 'left', 'right' ] ) ? 'side' : wpforo_setting( 'social', 'sb_location_toggle' );
1387 if( ! wpforo_setting( 'social', 'sb_toggle_on' ) || ( $position !== $location && ! $custom ) ) return;
1388 $location_class = $custom ? $location : wpforo_setting( 'social', 'sb_location_toggle' );
1389 ?>
1390 <div class="wpf-sb wpf-sb-<?php echo esc_attr( $location_class ) ?> wpf-sb-<?php echo esc_attr(
1391 wpforo_setting( 'social', 'sb_toggle' )
1392 ) ?> sb-tt-<?php echo esc_attr( wpforo_setting( 'social', 'sb_toggle_type' ) ) ?>">
1393 <div class="wpf-sb-toggle"><i class="fas fa-share-alt" title="<?php wpforo_phrase( 'Share this post' ) ?>"></i>
1394 </div>
1395 <div class="wpf-sb-buttons"
1396 style="display: <?php if( wpforo_setting( 'social', 'sb_toggle_type' ) === 'collapsed' ) {
1397 echo 'none';
1398 }
1399 ?>;">
1400 <?php do_action( 'wpforo_share_toggle_before', $url, $text, $location, $custom ) ?>
1401 <?php WPF()->api->share_toggle( $url, $text ); ?>
1402 <?php do_action( 'wpforo_share_toggle_after', $url, $text, $location, $custom ) ?>
1403 </div>
1404 </div>
1405 <?php
1406 }
1407
1408 function wpforo_share_buttons( $location = 'bottom', $url = '', $custom = false ) {
1409 if( ! wpforo_setting( 'social', 'sb_on' ) || ( ! wpforo_setting(
1410 'social',
1411 'sb_location',
1412 $location
1413 ) && ! $custom ) ) {
1414 return;
1415 }
1416
1417 if( wpforo_setting( 'ai', 'assistant' ) && $location === 'top' ) return;
1418
1419 ?>
1420 <div class="wpf-sbtn wpf-sb-<?php echo esc_attr( $location ) ?> wpf-sb-style-<?php echo esc_attr(
1421 wpforo_setting( 'social', 'sb_style' )
1422 ) ?>" style="display: block">
1423 <div class="wpf-sbtn-title"><i class="fas fa-share-alt"></i> <span><?php wpforo_phrase( 'Share:' ) ?></span>
1424 </div>
1425 <div class="wpf-sbtn-wrap">
1426 <?php do_action( 'wpforo_share_buttons_before', $location, $url, $custom ) ?>
1427 <?php WPF()->api->share_buttons( $url ); ?>
1428 <?php do_action( 'wpforo_share_buttons_after', $location, $url, $custom ) ?>
1429 </div>
1430 <div class="wpf-clear"></div>
1431 </div>
1432 <?php
1433 }
1434
1435 function wpforo_page() {
1436 $page_template = ( wpfval( $_GET, 'view' ) ) ? sanitize_title( $_GET['view'] ) : false;
1437 do_action( 'wpforo_page', $page_template );
1438 }
1439
1440 function wpforo_admin_note() {
1441
1442 $display = false;
1443 $templates = WPF()->tools_misc['admin_note_pages'];
1444 $usergroups = WPF()->tools_misc['admin_note_groups'];
1445
1446 if( ! wpfval( WPF()->tools_misc, 'admin_note_pages' ) ) return false;
1447 if( ! wpfval( WPF()->tools_misc, 'admin_note_groups' ) ) return false;
1448
1449 if( ! empty( $usergroups ) ) {
1450 $found = array_filter( $usergroups, function( $groupid ) {
1451 return in_array( $groupid, WPF()->current_user_groupids );
1452 } );
1453 if( ! empty( $found ) ) {
1454 if( wpfval( WPF()->current_object, 'template' ) && in_array(
1455 WPF()->current_object['template'],
1456 $templates
1457 ) ) {
1458 $display = true;
1459 } else {
1460 $display = false;
1461 }
1462 }
1463 }
1464
1465 if( $display ) {
1466 $note = wpforo_kses( wpforo_unslashe( trim( (string) WPF()->tools_misc['admin_note'] ) ) );
1467 $note = apply_filters( 'wpforo_admin_note', $note );
1468 if( $note ) {
1469 ?>
1470 <div class="wpforo-admin-note"><?php echo wpautop( $note ) ?>
1471 <div class="wpf-clear"></div></div><?php
1472 }
1473 }
1474
1475 }
1476
1477 add_action( 'wpforo_header_hook', 'wpforo_admin_note', 1 );
1478
1479 function wpforo_topic_icon( $topic, $type = 'all', $color = true, $echo = true, $wrap = '%s' ) {
1480 $html = '';
1481 if( wpforo_is_id( $topic ) ) $topic = wpforo_topic( $topic );
1482 if( $type == 'mixed' ) {
1483 if( ! $icon = WPF()->tpl->icon( 'topic', $topic, false ) ) {
1484 $icon = WPF()->tpl->icon_base( $topic['posts'] );
1485 $icon = implode( ' ', $icon );
1486 }
1487 $html = sprintf( $wrap, '<i class="fa-1x ' . esc_attr( $icon ) . '"></i>' );
1488 } else {
1489 if( ( $type == 'all' || $type == 'base' ) && wpfkey( $topic, 'posts' ) ) {
1490 $icon = WPF()->tpl->icon_base( $topic['posts'] );
1491 $icon = implode( ' ', $icon );
1492 $html .= sprintf( $wrap, '<i class="fa-1x ' . esc_attr( $icon ) . '"></i>' );
1493 }
1494 if( $type == 'all' || $type == 'status' ) {
1495 $icon = WPF()->tpl->icon_status( $topic );
1496 if( ! empty( $icon ) ) {
1497 $html = '';
1498 foreach( $icon as $i ) {
1499 if( ! $color ) $i['color'] = '';
1500 $classes = $i['class'] . ' ' . $i['color'];
1501 $html .= sprintf(
1502 $wrap,
1503 '<i class="fa-1x ' . esc_attr( $classes ) . '" title="' . esc_attr(
1504 $i['title']
1505 ) . '"></i>'
1506 );
1507 }
1508 }
1509 }
1510 }
1511
1512 if( $echo ) echo $html; else return $html;
1513 }
1514
1515 function wpforo_topic_icons( $topic, $type = 'all' ) {
1516 $icon = [];
1517 if( wpforo_is_id( $topic ) ) $topic = wpforo_topic( $topic );
1518 if( $type == 'mixed' ) {
1519 $icon = WPF()->tpl->icon( 'topic', $topic, false );
1520 } else {
1521 if( ( $type == 'all' || $type == 'base' ) && wpfkey( $topic, 'posts' ) ) {
1522 $icon_base = WPF()->tpl->icon_base( $topic['posts'] );
1523 if( ! empty( $icon_base ) && is_array( $icon_base ) ) $icon = [ 'base' => $icon_base ];
1524 }
1525 if( $type == 'all' || $type == 'status' ) {
1526 $icon_status = WPF()->tpl->icon_status( $topic );
1527 if( ! empty( $icon_status ) && is_array( $icon_status ) ) $icon = $icon_status;
1528 }
1529 $icon = array_filter( $icon );
1530 }
1531
1532 return $icon;
1533 }
1534
1535 function wpforo_tags( $topic, $wrap = true, $type = 'medium', $count = false ) {
1536 if( wpforo_is_id( $topic ) && $topic > 0 ) {
1537 $topic = wpforo_topic( $topic );
1538 }
1539 if( wpfval( $topic, 'tags' ) ) {
1540 $tags = WPF()->topic->sanitize_tags( $topic['tags'], true );
1541 if( ! empty( $tags ) ) {
1542 if( $wrap ) {
1543 ?>
1544 <div class="wpforo-post wpforo-tags wpfcl-1">
1545 <?php if( $type != 'text' ): ?>
1546 <div class="wpf-tags-title">
1547 <i class="fas fa-tag"></i> <span class="wpf-ttt"><?php wpforo_phrase(
1548 'Topic Tags'
1549 ); ?></span>
1550 </div>
1551 <?php endif; ?>
1552 <div class="<?php if( $type != 'text' ) echo 'wpf-tags'; ?> wpf-tags-<?php echo esc_attr(
1553 $type
1554 ) ?>">
1555 <?php if( $type == 'text' ): ?>
1556 <i class="fas fa-tag"></i> <span class="wpf-ttt"><?php wpforo_phrase(
1557 'Topic Tags'
1558 ); ?>:&nbsp; <?php endif; ?></span>
1559 <?php foreach( $tags as $tag ): ?>
1560 <?php $item = wpforo_tag( $tag ) ?>
1561 <tag wpf-tooltip="<?php echo esc_attr( wpforo_phrase( 'Topic Tag', false ) ); ?>">
1562 <a href="<?php echo wpforo_home_url() . '?wpfin=tag&wpfs=' . $tag ?>">
1563 <?php if( is_rtl() ): ?>
1564 <?php if( $count && wpfval(
1565 $item,
1566 'count'
1567 ) && ! $topic['status'] ) {
1568 echo '(' . $item['count'] . ') ';
1569 } ?>
1570 <?php echo esc_html( $tag ); ?>
1571 <?php else: ?>
1572 <?php echo esc_html( $tag ); ?>
1573 <?php if( $count && wpfval(
1574 $item,
1575 'count'
1576 ) && ! $topic['status'] ) {
1577 echo ' (' . $item['count'] . ')';
1578 } ?>
1579 <?php endif; ?>
1580 </a>
1581 </tag>
1582 <?php if( $type == 'text' ) echo '<sep>,</sep> '; ?>
1583 <?php endforeach; ?>
1584 </div>
1585 <div class="wpf-clear"></div>
1586 </div>
1587 <?php
1588 } else {
1589 ?>
1590 <div class="<?php if( $type != 'text' ) echo 'wpf-tags'; ?> wpf-tags-<?php echo esc_attr( $type ) ?>">
1591 <?php foreach( $tags as $tag ): ?>
1592 <?php $item = wpforo_tag( $tag ) ?>
1593 <tag wpf-tooltip="<?php echo esc_attr( wpforo_phrase( 'Topic Tag', false ) ); ?>"><a
1594 href="<?php echo wpforo_home_url() . '?wpfin=tag&wpfs=' . $tag ?>"><?php echo esc_html(
1595 $tag
1596 ); ?><?php if( $count && wpfval(
1597 $item,
1598 'count'
1599 ) && ! $topic['status'] ) {
1600 echo ' (' . $item['count'] . ')';
1601 } ?></a></tag>
1602 <?php endforeach; ?>
1603 </div>
1604 <?php
1605 }
1606 }
1607 }
1608 }
1609
1610 function wpforo_topic_rel( $topic ) {
1611 if( ! empty( $topic ) ) {
1612 if( wpfval( $topic, 'tags' ) ) {
1613 $html = '';
1614 $args = [];
1615 $wheres = [];
1616 $tags = WPF()->topic->sanitize_tags( $topic['tags'], true );
1617 if( ! empty( $tags ) ) {
1618 foreach( $tags as $tag ) {
1619 if( $tag ) $wheres[] = "FIND_IN_SET('" . esc_sql( $tag ) . "', tags)";
1620 }
1621 if( $wheres ) $args['where'] = '(' . implode( ' OR ', $wheres ) . ')';
1622
1623 $args['order'] = 'DESC';
1624 $args['row_count'] = 5;
1625 $args['orderby'] = 'modified';
1626 $args['forumid'] = apply_filters( 'wpforo_related_topics_by_forums', $topic['forumid'] );
1627 $args['exclude'] = [ $topic['topicid'] ];
1628 $args = apply_filters( 'wpforo_related_topics_args', $args );
1629 $topics = WPF()->topic->get_topics( $args );
1630 if( ! empty( $topics ) ) {
1631 $html .= '<div class="wpf-rel-title"><i class="fas fa-clone"></i> ' . wpforo_phrase(
1632 'Related Topics',
1633 false
1634 ) . '</div><ul class="wpf-rel-topics">';
1635 foreach( $topics as $item ) {
1636 $data = wpforo_topic( $item['topicid'] );
1637 $html .= '<li>' . wpforo_topic_icon( $item, 'all', true, false ) . '
1638 <a href="' . esc_url( (string) $data['url'] ) . '" title="' . esc_attr(
1639 $item['title']
1640 ) . '">' . esc_html( $item['title'] ) . '</a>
1641 <div class="wpf-rel-date">' . wpforo_date( $item['modified'], 'ago', false ) . '</div>
1642 <div class="wpf-clear"></div>
1643 </li>';
1644 }
1645 $html .= '</ul>';
1646 echo '<div class="wpf-rel-wrap">' . $html . '</div>';
1647 } else {
1648 echo '<div class="wpf-no-rel"></div>';
1649 }
1650 }
1651 }
1652 }
1653 }
1654
1655 function wpforo_topic_navi( $topic ) {
1656 if( ! empty( $topic ) ) {
1657 if( wpfval( $topic, 'topicid' ) && wpfval( $topic, 'forumid' ) ) {
1658 $prev_html = '';
1659 $next_html = '';
1660 $navi_topics = WPF()->db->get_col(
1661 "SELECT `topicid` FROM `" . WPF()->tables->topics . "` WHERE ( `topicid` = IFNULL((SELECT min(`topicid`) FROM `" . WPF()->tables->topics . "` WHERE `topicid` > " . intval(
1662 $topic['topicid']
1663 ) . " AND `forumid` = " . intval(
1664 $topic['forumid']
1665 ) . " AND `status` = 0 AND `private` = 0),0) OR `topicid` = IFNULL((SELECT max(`topicid`) FROM `" . WPF()->tables->topics . "` WHERE `topicid` < " . intval(
1666 $topic['topicid']
1667 ) . " AND `forumid` = " . intval(
1668 $topic['forumid']
1669 ) . " AND `status` = 0 AND `private` = 0),0) ) "
1670 );
1671
1672 $forum_urls = [];
1673 $forums = array_filter( WPF()->forum->get_forums(), function( $forum ) {
1674 return WPF()->perm->forum_can( 'vf', $forum['forumid'] );
1675 } );
1676 if( ! empty( $forums ) ) {
1677 foreach( $forums as $forum ) {
1678 $forum_urls[ 'forum_' . $forum['forumid'] ] = wpforo_home_url(
1679 $forum['slug']
1680 );
1681 }
1682 }
1683 ?>
1684 <div class="wpf-navi-wrap">
1685 <?php if( ! empty( $forum_urls ) ): ?>
1686 <div class="wpf-forum-jump wpf-navi-item">
1687 <span class="wpf-forum-jump-title"><i class="fa-solid fa-folder-tree"></i> <?php wpforo_phrase(
1688 'Forum Jump:'
1689 ) ?></span>
1690 <select onchange="window.location.href = wpf_forum_urls['forum_' + this.value]">
1691 <?php WPF()->forum->tree( 'select_box', false, $topic['forumid'], true ); ?>
1692 </select>
1693 <script>var wpf_forum_json = '<?php echo wp_json_encode( $forum_urls ) ?>';
1694 var wpf_forum_urls = JSON.parse(wpf_forum_json);</script>
1695 </div>
1696 <?php else: ?>
1697 <div class="wpf-forum-jump wpf-navi-item">
1698 <?php wpforo_phrase( 'Topic Navigation' ) ?>
1699 </div>
1700 <?php endif; ?>
1701
1702 <?php if( ! empty( $navi_topics ) ) : ?>
1703 <?php
1704 $prev = ( wpfkey( $navi_topics, 0 ) ) ? $navi_topics[0] : false;
1705 $next = ( wpfkey( $navi_topics, 1 ) ) ? $navi_topics[1] : false;
1706 if( $prev && ! $next ) {
1707 if( $topic['topicid'] < $prev ) {
1708 $next = $prev;
1709 $prev = false;
1710 }
1711 }
1712 if( $prev ) {
1713 $prev_data = wpforo_topic( $prev );
1714 if( ! empty( $prev_data ) ) {
1715 $prev_html = '<a href="' . esc_url( (string) $prev_data['url'] ) . '" title="' . esc_attr(
1716 $prev_data['title']
1717 ) . '"><i class="fas fa-chevron-left"></i>&nbsp; ' . wpforo_phrase(
1718 'Previous Topic',
1719 false
1720 ) . '</a>';
1721 }
1722 }
1723 if( $next ) {
1724 $next_data = wpforo_topic( $next );
1725 if( ! empty( $next_data ) ) {
1726 $next_html = '<a href="' . esc_url( (string) $next_data['url'] ) . '" title="' . esc_attr(
1727 $next_data['title']
1728 ) . '">' . wpforo_phrase(
1729 'Next Topic',
1730 false
1731 ) . ' &nbsp;<i class="fas fa-chevron-right"></i></a>';
1732 }
1733 }
1734 ?>
1735 <?php if( $prev_html || $next_html ): ?>
1736 <div class="wpf-topic-prnx">
1737 <div class="wpf-topic-prev wpf-navi-item"><?php echo $prev_html ?></div>
1738 <div class="wpf-topic-next wpf-navi-item"><?php echo $next_html ?></div>
1739 </div>
1740 <?php endif; ?>
1741 <?php endif; ?>
1742 <div class="wpf-clear"></div>
1743 </div>
1744 <?php
1745 }
1746 }
1747 }
1748
1749 function wpforo_topic_visitors( $topic ) {
1750 if( ! empty( $topic ) ) {
1751 $html = '';
1752 $users = '';
1753 $guests = '';
1754 $users_visited = '';
1755 $visitors = WPF()->log->visitors( $topic );
1756 if( ! empty( $visitors ) ) {
1757 if( wpfval( $visitors, 'users' ) ) {
1758 if( wpfval( $visitors, 'users', 'viewing' ) && wpforo_setting(
1759 'logging',
1760 'display_topic_current_viewers'
1761 ) ) {
1762 $count = count( $visitors['users']['viewing'] );
1763 if( $count ) {
1764 if( $count > 1 ) {
1765 $users = wpforo_phrase( '%d users ( %s )', false );
1766 } elseif( $count == 1 ) {
1767 $users = wpforo_phrase( '%d user ( %s )', false );
1768 }
1769 $user_html = [];
1770 foreach( $visitors['users']['viewing'] as $user ) {
1771 if( wpfval( $user, 'userid' ) ) {
1772 $member = wpforo_member( $user['userid'] );
1773 if( wpfval( $member, 'display_name' ) ) {
1774 $user_html[] = wpforo_member_link(
1775 $member,
1776 '',
1777 30,
1778 'wpf-topic-visitor-link',
1779 false
1780 );
1781 }
1782 }
1783 }
1784 $user_html = implode( ', ', $user_html );
1785 $users = sprintf( $users, $count, $user_html );
1786 }
1787 $users = apply_filters( 'wpforo_topic_viewing_users', $users, $visitors['users']['viewing'] );
1788 }
1789 if( wpfval( $visitors, 'users', 'viewed' ) && wpforo_setting( 'logging', 'display_recent_viewers' ) ) {
1790 $users_visited = wpforo_phrase( 'Recently viewed by users: %s.', false );
1791 $track_users_link = [];
1792 foreach( $visitors['users']['viewed'] as $user ) {
1793 if( wpfval( $user, 'userid' ) ) {
1794 $member = wpforo_member( $user['userid'] );
1795 if( wpfval( $member, 'display_name' ) ) {
1796 $track_users_link[] = wpforo_member_link(
1797 $member,
1798 '',
1799 30,
1800 'wpf-topic-visitor-link',
1801 false
1802 ) . ' ' . wpforo_date( $user['time'], 'ago', false );
1803 }
1804 }
1805 }
1806 $track_users_link = implode( ', ', $track_users_link );
1807 $users_visited = sprintf( $users_visited, $track_users_link );
1808 $users_visited = '<p class="wpf-viewed-users"><i class="fas fa-walking"></i> ' . $users_visited . '</p>';
1809 $users_visited = apply_filters(
1810 'wpforo_topic_viewed_users',
1811 $users_visited,
1812 $visitors['users']['viewed']
1813 );
1814 }
1815 }
1816 if( wpfval( $visitors, 'guests' ) && wpforo_setting( 'logging', 'display_topic_current_viewers' ) ) {
1817 $count = count( $visitors['guests'] );
1818 if( $count > 1 ) {
1819 $guests = sprintf( wpforo_phrase( '%s guests', false ), $count );
1820 } elseif( $count == 1 ) {
1821 $guests = sprintf( wpforo_phrase( '%s guest', false ), $count );
1822 }
1823 }
1824 if( $users || $guests ) {
1825 $and = ( $users && $guests ) ? wpforo_phrase( 'and', false, 'lower' ) : '';
1826 $html .= '<p class="wpf-viewing-users"><i class="fas fa-male"></i> ' . sprintf(
1827 wpforo_phrase( 'Currently viewing this topic %s %s %s.', false ),
1828 $users,
1829 $and,
1830 $guests
1831 ) . '</p>';
1832 }
1833 $html .= $users_visited;
1834 $html = apply_filters( 'wpforo_topic_visitors_info', $html, $visitors );
1835 }
1836 echo $html;
1837 }
1838 }
1839
1840 function wpforo_viewing( $item, $echo = true ) {
1841 if( ! empty( $item ) && wpforo_setting( 'logging', 'display_forum_current_viewers' ) ) {
1842 $phrase = wpforo_phrase( '(%d viewing)', false, 'lower' );
1843 $visitors = WPF()->log->visitors( $item );
1844 $users = ( wpfval( $visitors, 'users', 'viewing' ) ) ? count( $visitors['users']['viewing'] ) : 0;
1845 $guests = ( wpfval( $visitors, 'guests' ) ) ? count( $visitors['guests'] ) : 0;
1846 $viewing = (int) $users + (int) $guests;
1847 if( $viewing > 0 ) {
1848 $phrase = '<span class="wpf-viewing">' . sprintf( $phrase, $viewing ) . '</span>';
1849 if( $echo ) {
1850 echo $phrase;
1851 } else {
1852 return $phrase;
1853 }
1854 }
1855 }
1856 }
1857
1858 function wpforo_topic_footer() {
1859 if( wpfval( WPF()->current_object, 'topic' ) && wpfval( WPF()->current_object, 'template' ) && WPF()->current_object['template'] == 'post' ) {
1860 $topic = WPF()->current_object['topic'];
1861 ?>
1862 <div class="wpforo-topic-footer wpfbg-9">
1863 <div class="wpf-topic-navi">
1864 <?php wpforo_topic_navi( $topic ) ?>
1865 </div>
1866 <div class="wpf-topic-rel">
1867 <?php wpforo_topic_rel( $topic ) ?>
1868 </div>
1869 <div class="wpf-tag-list">
1870 <?php wpforo_tags( $topic, true, 'text', true ) ?>
1871 </div>
1872 <div class="wpf-topic-visitors">
1873 <?php wpforo_topic_visitors( $topic ) ?>
1874 </div>
1875 </div>
1876 <?php
1877 }
1878 }
1879
1880 add_action( 'wpforo_post_list_footer', 'wpforo_topic_footer' );
1881
1882 function wpforo_thread( $topicid ) {
1883
1884 $thread = wpforo_topic( $topicid );
1885
1886 $thread['icons'] = '';
1887 $thread['icons_html'] = '';
1888 $thread['users_html'] = '';
1889 $thread['status_html'] = '';
1890 $thread['reply_html'] = '';
1891 $thread['user_avatar'] = '';
1892 $thread['last_user_avatar'] = '';
1893 $thread['forum'] = wpforo_forum( $thread['forumid'] );
1894 $thread['user'] = wpforo_member( $thread );
1895 $thread['last_post'] = ( wpfval( $thread, 'last_post' ) ) ? wpforo_post( $thread['last_post'] ) : [];
1896 $thread['last_user'] = ( wpfval( $thread, 'last_post' ) ) ? wpforo_member( $thread['last_post'] ) : [];
1897 $thread['replies'] = ( intval( $thread['posts'] ) - 1 );
1898 $thread['last_post_date'] = wpforo_date( $thread['modified'], 'ago-date', false );
1899 $thread['last_post_url'] = wpfval( $thread, 'last_post', 'url' );
1900 $thread['user_info'] = esc_attr(
1901 sprintf( wpforo_phrase( 'Created by %s', false ), wpfval( $thread['user'], 'display_name' ) )
1902 );
1903 $thread['reply_user_info'] = ( wpfval( $thread, 'last_user', 'display_name' ) ) ? esc_attr(
1904 sprintf( wpforo_phrase( 'Last reply by %s', false ), $thread['last_user']['display_name'] )
1905 ) : '';
1906 $thread['icons'] = wpforo_topic_icons( $thread, 'all' );
1907 $thread['wrap'] = ( count( $thread['icons'] ) > 3 ) ? ' style="flex-wrap: wrap;"' : '';
1908
1909 if( ! empty( $thread['icons'] ) ) {
1910 $i = 0;
1911 foreach( $thread['icons'] as $icon ) {
1912 $thread['icons_html'] .= '<span class="wpf-circle wpf-s wpfcl-3 ' . str_replace(
1913 'wpfcl-',
1914 'wpfbg-',
1915 $icon['color']
1916 ) . '" wpf-tooltip="' . esc_attr(
1917 wpforo_phrase( $icon['title'], false )
1918 ) . '" wpf-tooltip-position="top" wpf-tooltip-size="small"><i class="' . esc_attr(
1919 str_replace( '-circle', '', $icon['class'] )
1920 ) . '"></i></span>';
1921 if( $i === 0 ) {
1922 $thread['status_html'] .= '<span class="wpf-circle wpfsq wpf-s ' . $icon['color'] . '" wpf-tooltip="' . esc_attr(
1923 wpforo_phrase( $icon['title'], false )
1924 ) . '" wpf-tooltip-position="top" wpf-tooltip-size="small"><i class="' . esc_attr(
1925 str_replace( '-circle', '', $icon['class'] )
1926 ) . '"></i></span>';
1927 } else {
1928 $thread['status_html'] .= '<span class="wpf-circle wpfsq wpf-s wpfcl-3 ' . str_replace(
1929 'wpfcl-',
1930 'wpfbg-',
1931 $icon['color']
1932 ) . '" wpf-tooltip="' . esc_attr(
1933 wpforo_phrase( $icon['title'], false )
1934 ) . '" wpf-tooltip-position="top" wpf-tooltip-size="small"><i class="' . esc_attr(
1935 str_replace( '-circle', '', $icon['class'] )
1936 ) . '"></i></span>';
1937 }
1938 $i ++;
1939 }
1940 }
1941
1942 if( ! empty( $thread['user'] ) || ! empty( $thread['last_user'] ) ) {
1943 $thread['usergroup_can_va'] = WPF()->usergroup->can( 'va' );
1944 $thread['feature_avatars'] = wpforo_setting( 'profiles', 'avatars' );
1945 $thread['users_html'] .= '<div class="wpf-thread-users-avatars">';
1946 if( $thread['usergroup_can_va'] && $thread['feature_avatars'] ) {
1947 if( ! empty( $thread['user'] ) ) {
1948 $thread['user_avatar'] = wpforo_user_avatar( $thread['user'], 40 );
1949 $thread['users_html'] .= '<div class="wpf-circle wpf-m">
1950 <a href="' . esc_url(
1951 (string) $thread['url']
1952 ) . '" wpf-tooltip="' . $thread['user_info'] . '" wpf-tooltip-position="top" wpf-tooltip-size="medium">' . $thread['user_avatar'] . '</a>
1953 </div>';
1954 }
1955 if( $thread['replies'] && ! empty( $thread['last_user'] ) ) {
1956 $thread['last_user_avatar'] = wpforo_user_avatar( $thread['last_user'], 24 );
1957 $thread['users_html'] .= '<div class="wpf-circle wpf-s">
1958 <a href="' . esc_url(
1959 (string) $thread['last_post_url']
1960 ) . '" wpf-tooltip="' . $thread['reply_user_info'] . '" wpf-tooltip-position="top" wpf-tooltip-size="medium">' . $thread['last_user_avatar'] . '</a>
1961 </div>';
1962 }
1963 } else {
1964 $thread['users_html'] .= wpforo_member_link( $thread['user'], 'by', 9, '', false );
1965 }
1966 $thread['users_html'] .= '</div>';
1967
1968 $thread['author_html'] = '<div class="wpf-thread-users-avatars">';
1969 if( $thread['usergroup_can_va'] && $thread['feature_avatars'] ) {
1970 if( ! empty( $thread['user'] ) ) {
1971 $thread['user_avatar'] = wpforo_user_avatar( $thread['user'], 40 );
1972 $thread['author_html'] .= '<a href="' . esc_url(
1973 (string) $thread['url']
1974 ) . '" wpf-tooltip="' . esc_attr(
1975 wpfval( $thread['user'], 'display_name' )
1976 ) . '" wpf-tooltip-position="top" wpf-tooltip-size="medium">' . $thread['user_avatar'] . '</a>';
1977 }
1978 } else {
1979 $thread['author_html'] .= wpforo_member_link( $thread['user'], 'by', 9, '', false );
1980 }
1981
1982 if( $thread['usergroup_can_va'] && $thread['feature_avatars'] ) {
1983 if( $thread['replies'] && ! empty( $thread['last_user'] ) ) {
1984 $thread['last_user_avatar'] = wpforo_user_avatar( $thread['last_user'], 40 );
1985 $thread['reply_html'] = '<i class="fas fa-reply fa-rotate-180"></i> <a href="' . esc_url(
1986 (string) $thread['last_post_url']
1987 ) . '" wpf-tooltip="' . $thread['reply_user_info'] . '" wpf-tooltip-position="top" wpf-tooltip-size="medium">' . $thread['last_user_avatar'] . '</a>';
1988 } else {
1989 $thread['user_avatar'] = wpforo_user_avatar( $thread['user'], 40 );
1990 $thread['reply_html'] = '<i class="fas fa-feather-alt"></i> <a href="' . esc_url(
1991 (string) $thread['url']
1992 ) . '" wpf-tooltip="' . esc_attr(
1993 wpfval( $thread['user'], 'display_name' )
1994 ) . '" wpf-tooltip-position="top" wpf-tooltip-size="medium">' . $thread['user_avatar'] . '</a>';
1995 }
1996 }
1997 $thread['author_html'] .= '</div>';
1998 }
1999
2000 return $thread;
2001
2002 }
2003
2004 /**
2005 * @param string $type
2006 * @param array $buttons
2007 * @param array $forum
2008 * @param array $topic
2009 * @param array $post
2010 * @param bool $echo
2011 *
2012 * @return string
2013 */
2014 function wpforo_post_buttons( $type = 'icon-text', $buttons = [], $forum = [], $topic = [], $post = [], $echo = true ) {
2015 $buttons = WPF()->tpl->buttons( $buttons, $forum, $topic, $post, false );
2016 if( $type === 'icon' ) {
2017 $buttons = preg_replace( '#</i>[\r\n\t\s\0]*<span[^<>]*>.*?</span>#isu', '</i>', (string) $buttons );
2018 } elseif( $type === 'text' ) {
2019 $buttons = preg_replace( '#<i[^<>]*>[\r\n\t\s\0]*</i>[\r\n\t\s\0]*#isu', '', (string) $buttons );
2020 $buttons = preg_replace( '#\swpf-tooltip=[\'\"][^\'\"]+?[\'\"]#isu', '', (string) $buttons );
2021 } else {
2022 $buttons = preg_replace( '#\swpf-tooltip=[\'\"][^\'\"]+?[\'\"]#isu', '', (string) $buttons );
2023 }
2024 if( $echo ) echo $buttons;
2025
2026 return $buttons;
2027 }
2028
2029 function wpforo_thread_breadcrumb( $post = [], $parents = [] ) {
2030 $html = '';
2031 $gab = false;
2032 if( wpfval( $post, 'parentid' ) ) {
2033 $html .= '<i class="fas fa-reply fa-rotate-180"></i>';
2034 $parent = wpforo_post( $post['parentid'] );
2035 $member = wpforo_member( $parent );
2036 $parent_url = ( wpfval( $parent, 'url' ) ) ? $parent['url'] : '#post-' . $parent['parentid'];
2037 $avatar = ( wpforo_setting( 'profiles', 'avatars' ) ) ? wpforo_user_avatar( $member, 18 ) : '&nbsp;';
2038 $member_name = ( wpfval( $member, 'display_name' ) ) ? $member['display_name'] : wpforo_phrase(
2039 'Guest',
2040 false
2041 );
2042 $html .= '<div class="wpf-reply-to wpf-tree-item"><a href="' . esc_url(
2043 (string) $parent_url
2044 ) . '"><em>' . wpforo_phrase(
2045 'Reply to',
2046 false
2047 ) . '</em>' . $avatar . '<span>' . $member_name . '</span></a></div>';
2048 if( wpfval( $parents, $post['parentid'] ) ) {
2049 $topic = wpforo_topic( $post['topicid'] );
2050 $limit = apply_filters( 'wpforo_thread_breadcrumb_limit', 3 );
2051 $items = array_reverse( $parents[ $post['parentid'] ] );
2052 $last = key( array_slice( $items, - 1, 1, true ) );
2053 foreach( $items as $key => $parentid ) {
2054 if( $key < $limit || $key == $last ) {
2055 $parent = wpforo_post( $parentid );
2056 $starter = ( $topic['userid'] == $parent['userid'] ) ? true : false;
2057 $class = ( $starter ) ? ' wpf-starter' : '';
2058 if( ! empty( $parent ) ) {
2059 $member = wpforo_member( $parent );
2060 $name = ( wpfval( $member, 'display_name' ) ) ? $member['display_name'] : '';
2061 $tooltip = ( $starter ) ? ' wpf-tooltip="' . esc_attr(
2062 wpforo_phrase( 'Topic Author', false ) . ' - ' . $name
2063 ) . '" wpf-tooltip-size="medium"' : ' wpf-tooltip="' . esc_attr(
2064 wpforo_phrase( 'Reply by', false ) . ' ' . $name
2065 ) . '" wpf-tooltip-size="medium"';
2066 $parent_url = ( wpfval( $parent, 'url' ) ) ? $parent['url'] : '#post-' . $parent['parentid'];
2067 $avatar = ( wpforo_setting( 'profiles', 'avatars' ) ) ? wpforo_user_avatar(
2068 $member,
2069 18
2070 ) : '&nbsp;&nbsp;&nbsp;' . $name;
2071 if( ! $gab ) $html .= '<i class="fas fa-angle-right wpf-tree-sep"></i>';
2072 $html .= '<div class="wpf-tree-item' . $class . '" ' . $tooltip . '><a href="' . esc_url(
2073 (string) $parent_url
2074 ) . '">' . $avatar . '</a></div>';
2075 }
2076 } else {
2077 if( ! $gab ) $html .= '<i class="fas fa-ellipsis-h"></i>';
2078 $gab = true;
2079 }
2080 }
2081 }
2082 }
2083 echo $html;
2084 }
2085
2086 function wpforo_check_threads( $posts = [] ) {
2087 $post = array_shift( $posts );
2088 if( wpfkey( $post, 'root' ) ) {
2089 if( is_null( $post['root'] ) && wpfval( $post, 'topicid' ) ) {
2090 WPF()->topic->rebuild_threads( $post['topicid'] );
2091 wpforo_clean_cache( 'topic', $post['topicid'] );
2092 }
2093 }
2094 }
2095
2096 function wpforo_posts_ordering_dropdown( $orderby = null, $topicid = null ) {
2097 WPF()->tpl->posts_ordering_dropdown( $orderby, $topicid );
2098 }
2099
2100 function wpforo_template_pagenavi( $class = '', $permalink = true, $paged = null, $items_count = null, $items_per_page = null ) {
2101 WPF()->tpl->pagenavi( $paged, $items_count, $items_per_page, $permalink, $class );
2102 }
2103
2104 function wpforo_template_add_topic_button( $forumid = null ) {
2105 if( ! wpforo_is_bot() ) WPF()->tpl->add_topic_button( $forumid );
2106 }
2107
2108 function wpforo_feed_rss2_url( $echo = true, $general = false ) {
2109 WPF()->feed->rss2_url( $echo, $general );
2110 }
2111
2112 function wpforo_feed_link( $type = 'topic' ) {
2113
2114 $nofollow = apply_filters( 'wpforo_feed_link_nofollow', false );
2115 $nofollow = ( $nofollow ) ? ' rel="nofollow" ' : '';
2116
2117 if( wpforo_is_module_enabled( 'rss' ) && wpforo_setting( 'rss', 'feed' ) ): ?>
2118
2119 <?php if( $type === 'topic' && wpforo_setting( 'rss', 'feed_topic' ) ): ?>
2120 <a href="<?php WPF()->feed->rss2_url(); ?>" <?php echo $nofollow ?>
2121 title="<?php wpforo_phrase( 'Topic RSS Feed' ) ?>" target="_blank" class="wpf-button-outlined">
2122 <span class="">RSS</span> <i class="fas fa-rss wpfsx"></i>
2123 </a>
2124 <?php elseif( $type === 'forum' && wpforo_setting( 'rss', 'feed_forum' ) ): ?>
2125 <span class="wpf-feed">
2126 <a href="<?php wpforo_feed_rss2_url() ?>" <?php echo $nofollow ?> title="<?php wpforo_phrase(
2127 'Forum RSS Feed'
2128 ) ?>" target="_blank" class="wpf-button-outlined">
2129 <span style="text-transform: uppercase;">
2130 <?php wpforo_phrase( 'RSS' ) ?>
2131 </span>
2132 <i class="fas fa-rss wpfsx"></i>
2133 </a>
2134 </span>
2135 <?php elseif( $type === 'home' && wpforo_setting( 'rss', 'feed_general' ) ): ?>
2136 <sep> &nbsp;|&nbsp;</sep>
2137 <span class="wpf-feed-forums">
2138 <a href="<?php wpforo_feed_rss2_url(
2139 true,
2140 'forum'
2141 ) ?>" <?php echo $nofollow ?> title="<?php wpforo_phrase( 'Forums RSS Feed' ) ?>" target="_blank">
2142 <span><?php wpforo_phrase( 'Forums' ) ?></span> <i class="fas fa-rss wpfsx"></i>
2143 </a>
2144 </span>
2145 <sep> &nbsp;|&nbsp;</sep>
2146 <span class="wpf-feed-topics">
2147 <a href="<?php wpforo_feed_rss2_url(
2148 true,
2149 'topic'
2150 ) ?>" <?php echo $nofollow ?> title="<?php wpforo_phrase( 'Topics RSS Feed' ) ?>" target="_blank">
2151 <span><?php wpforo_phrase( 'Topics' ) ?></span> <i class="fas fa-rss wpfsx"></i>
2152 </a>
2153 </span>
2154 <?php endif; ?>
2155 <?php endif;
2156 }
2157
2158 function wpforo_template_topic_portable_form( $forumid = null ) {
2159 WPF()->tpl->topic_form_forums_selectbox( $forumid );
2160 }
2161
2162 function wpforo_notifications() {
2163 WPF()->activity->notifications();
2164 }
2165
2166 function wpforo_unread_url( $topicid = 0, $url = '', $echo = true, $force = false ) {
2167 //Only for loggedin users
2168 $enabled = ( ! $force ? wpforo_setting( 'logging', 'goto_unread' ) : true );
2169 if( WPF()->current_userid && $enabled && $topicid && $url ) {
2170 //Get read topics
2171 $topics = WPF()->log->get_read_topics();
2172 if( $last_read_id = wpfval( $topics, $topicid ) ) {
2173 //Make sure the post still located in current topic. When admin split a topic
2174 //and move the last reply to other topic, the users unread topic array isn't updated,
2175 //It's not reasonable to update usermeta of each user, so it's better to check
2176 //the post topic ID here. if the reply is split and moved to other topic use the
2177 //topic last post id.
2178 $last_read_post = wpforo_post( $last_read_id );
2179 if( $topicid == wpfval( $last_read_post, 'topicid' ) ) {
2180 $first_unread_id = 0;
2181 $jump_to_first_unread = apply_filters( 'wpforo_jump_to_first_unread', true );
2182 if( $jump_to_first_unread ) {
2183 //Get first unread postid
2184 $first_unread_id = WPF()->post->next_post( $last_read_id, $topicid );
2185 }
2186 //Decide to whether execute more SQLs and create direct URLs or not
2187 $direct_url = apply_filters( 'wpforo_build_direct_unread_post_url', false );
2188 //Create new URLs
2189 if( $first_unread_id ) {
2190 //Change URL to first unread post
2191 if( $direct_url ) {
2192 $url = wpforo_post( $first_unread_id, 'url' );
2193 } else {
2194 $url = ( strpos( (string) $url, '#' ) !== false ) ? preg_replace(
2195 '|#.+$|',
2196 '#post-' . intval( $first_unread_id ),
2197 (string) $url
2198 ) : $url . '#post-' . intval( $first_unread_id );
2199 }
2200 } else {
2201 //Change URL to last read post
2202 if( $direct_url ) {
2203 $url = wpforo_post( $last_read_id, 'url' );
2204 } else {
2205 $url = ( strpos( (string) $url, '#' ) !== false ) ? preg_replace(
2206 '|#.+$|',
2207 '#post-' . intval( $last_read_id ),
2208 (string) $url
2209 ) : $url . '#post-' . intval( $last_read_id );
2210 }
2211 }
2212 } elseif( $last_postid = wpforo_topic( $topicid, 'last_post' ) ) {
2213 $url = wpforo_post( $last_postid, 'url' );
2214 }
2215 }
2216 } else {
2217 $direct_topic_url = apply_filters( 'wpforo_direct_topic_url', true );
2218 if( $direct_topic_url ) {
2219 $url = preg_replace( '|#post-\d+|i', '', (string) $url );
2220 $url = WPF()->strip_url_paged_var( $url );
2221 }
2222 }
2223 if( ! $echo ) return esc_url( (string) $url );
2224 echo esc_url( (string) $url );
2225 }
2226
2227 function wpforo_unread_button( $topicid = 0, $url = '', $echo = true, $postid = 0 ) {
2228 $button = '';
2229 if( ! $postid && $topicid && $url && WPF()->current_userid && wpforo_setting( 'logging', 'goto_unread_button' ) ) {
2230 $unread = wpforo_unread( $topicid, 'topic', false );
2231 if( $unread ) {
2232 $button_link = apply_filters( 'wpforo_jump_to_unread_button_link', false );
2233 $button_text = str_replace( [ '{', '}' ], '', wpforo_phrase( '{new}', false ) );
2234 if( wpforo_setting( 'logging', 'goto_unread' ) ) {
2235 if( $button_link ) {
2236 $url = wpforo_unread_url( $topicid, $url, false, true );
2237 }
2238 } else {
2239 $url = wpforo_unread_url( $topicid, $url, false, true );
2240 $button_link = true;
2241 }
2242 $button = ( $button_link ) ? '<a href="' . $url . '" class="wpf-new-button" title="' . esc_attr(
2243 wpforo_phrase( 'Go to first unread post', false )
2244 ) . '">' . $button_text . '</a>' : '<span class="wpf-new-button">' . $button_text . '</span>';
2245 }
2246 } elseif( $postid && $topicid && WPF()->current_userid ) {
2247 $unread = wpforo_unread( $topicid, 'post', false, $postid );
2248 if( $unread ) {
2249 $button_text = str_replace( [ '{', '}' ], '', wpforo_phrase( '{new}', false ) );
2250 $button = '<span class="wpf-new-button">' . $button_text . '</span>';
2251 }
2252 }
2253 if( ! $echo ) return $button;
2254 echo $button;
2255 }
2256
2257
2258 /**
2259 * @param array|string $forum
2260 * @param string $before
2261 * @param string $after
2262 * @param bool $echo
2263 *
2264 * @return string
2265 */
2266 function wpforo_forum_title( $forum, $before = '', $after = '', $echo = true ) {
2267 $title = is_array( $forum ) ? (string) wpfval( $forum, 'title' ) : $forum;
2268 $title = esc_html( $title );
2269 $title = apply_filters( 'wpforo_forum_title', $title, $forum );
2270 if( $title ) $title = $before . $title . $after;
2271 if( $echo ) echo $title;
2272
2273 return $title;
2274 }
2275
2276 /**
2277 * @param array|string $forum
2278 * @param string $before
2279 * @param string $after
2280 * @param bool $echo
2281 *
2282 * @return string
2283 */
2284 function wpforo_forum_description( $forum, $before = '', $after = '', $echo = true ) {
2285 $description = is_array( $forum ) ? (string) wpfval( $forum, 'description' ) : $forum;
2286 $description = apply_filters( 'wpforo_forum_description', $description, $forum );
2287 $description = trim( $description );
2288 if( $description ) $description = $before . $description . $after;
2289 if( $echo ) echo $description;
2290
2291 return $description;
2292 }
2293
2294 function wpforo_admin_cpanel() {
2295 if( WPF()->current_object['template'] === 'forum' && wpforo_setting(
2296 'components',
2297 'admin_cp'
2298 ) && wpforo_current_user_is( 'admin' ) ) {
2299 $toggle = get_user_meta( WPF()->current_userid, 'wpf-acp-toggle', true );
2300 if( ! $toggle || $toggle === 'open' ) {
2301 $display = 'block';
2302 $title = wpforo_phrase( 'Close', false );
2303 $icon = '<i class="fas fa-minus-square"></i>';
2304 } else {
2305 $display = 'none';
2306 $title = wpforo_phrase( 'Open', false );
2307 $icon = '<i class="fas fa-plus-square"></i>';
2308 }
2309 ?>
2310 <div class="wpf-admincp">
2311 <div class="wpf-acp-header">
2312 <div class="wpf-acp-title"><i class="fas fa-cog"></i> <?php wpforo_phrase( 'Admin Control Panel' ) ?>
2313 </div>
2314 <div class="wpf-acp-toggle" wpf-tooltip="<?php echo esc_attr( $title ) ?>"><?php echo $icon ?></div>
2315 </div>
2316 <div class="wpf-acp-body" style="display: <?php echo $display ?>;">
2317 <div class="wpf-acp-content">
2318 <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) ); ?>"
2319 class="wpf-button-secondary"><i class="fas fa-plus"></i>&nbsp; <?php wpforo_phrase(
2320 'Add New Category or Forum'
2321 ) ?></a>
2322 <p class="wpf-acp-forum-info">
2323 <?php $layouts = '(<a href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-layouts/extended-layout/" target="_blank">Extended</a>,
2324 <a href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-layouts/simplified-layout/" target="_blank">Simplified</a>,
2325 <a href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-layouts/qa-layout/" target="_blank">Q&A</a>,
2326 <a href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-layouts/threaded-layout/" target="_blank">Threaded</a>)';
2327 $layout = '<a href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-layouts/" target="_blank">' . wpforo_phrase(
2328 'the layout you want',
2329 false,
2330 'lower'
2331 ) . '</a>';
2332 ?>
2333 <?php echo sprintf(
2334 wpforo_phrase(
2335 'Please note, that forums can be displayed with different layouts %1$s, just edit the top category (blue panel) and set %2$s. Child forums inherit the top category (blue panel) layout.',
2336 false
2337 ),
2338 $layouts,
2339 $layout
2340 ); ?>
2341 </p>
2342 </div>
2343 <div class="wpf-acp-footer">
2344 <a href="<?php echo admin_url(
2345 'admin.php?page=' . wpforo_prefix_slug( 'settings' ) . '&wpf_tab=styles'
2346 ); ?>" class="wpf-button-secondary"><i class="fas fa-paint-brush"></i> <?php wpforo_phrase(
2347 'Change Color Style'
2348 ) ?></a>
2349 <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'moderations' ) ); ?>"
2350 class="wpf-button-secondary"><i class="fas fa-tasks"></i> <?php wpforo_phrase(
2351 'Post Moderation'
2352 ) ?></a>
2353 <a href="<?php echo admin_url(
2354 'admin.php?page=' . wpforo_prefix_slug( 'settings' ) . '&wpf_tab=antispam'
2355 ); ?>"
2356 class="wpf-button-secondary"><i class="fas fa-shield-alt"></i> <?php wpforo_phrase(
2357 'Antispam'
2358 ) ?></a>
2359 <?php $menu = wp_get_nav_menu_object( 'wpforo-navigation' ); ?>
2360 <?php if( ! empty( $menu ) && isset( $menu->term_id ) ): ?>
2361 <a href="<?php echo admin_url( 'nav-menus.php?action=edit&menu=' . $menu->term_id ); ?>"
2362 class="wpf-button-secondary"><i class="fas fa-bars"></i> <?php wpforo_phrase(
2363 'Forum Menu'
2364 ) ?></a>
2365 <?php endif; ?>
2366 <a href="<?php echo admin_url( 'widgets.php' ); ?>" class="wpf-button-secondary"><i
2367 class="far fa-window-restore"></i> <?php wpforo_phrase( 'Forum Widgets' ) ?></a>
2368 <a href="<?php echo wp_nonce_url(
2369 wpforo_home_url( '?wpfaction=reset_all_caches' ),
2370 'wpforo_reset_cache'
2371 ) ?>" class="wpf-button-secondary" style="color: #e22d00 !important;"><i
2372 class="fas fa-times"></i> <?php wpforo_phrase( 'Delete Forum Cache' ) ?></a>
2373 </div>
2374 </div>
2375 </div>
2376 <?php
2377 }
2378 }
2379
2380 /**
2381 * show member template
2382 */
2383 function wpforo_member_template() {
2384 WPF()->tpl->member_template();
2385 }
2386
2387 /**
2388 * show member menu
2389 */
2390 function wpforo_member_tabs() {
2391 WPF()->tpl->member_menu();
2392 }
2393
2394 /**
2395 * get current object user or empty array
2396 *
2397 * @return array
2398 */
2399 function wpforo_get_current_object_user() {
2400 return WPF()->current_object['user'];
2401 }
2402
2403 /**
2404 * @param array|string $template
2405 *
2406 * @return bool
2407 */
2408 function wpforo_is_member_template( $template = null ) {
2409 if( $template = WPF()->tpl->get_template( $template ) ) {
2410 return wpfkey(
2411 WPF()->tpl->member_templates,
2412 $template['key']
2413 );
2414 }
2415
2416 return false;
2417 }
2418
2419 /**
2420 * show wpforo template
2421 *
2422 * @param array|string $template
2423 */
2424 function wpforo_template( $template = null ) {
2425 $template = apply_filters( 'wpforo_template', $template );
2426 if( ! $template ) $template = WPF()->current_object['template'] ? WPF()->current_object['template'] : '404';
2427 if( WPF()->tpl->can_view_template( $template, WPF()->current_object['user'] ) ) {
2428 if( $path = WPF()->tpl->get_template_path( $template ) ) {
2429 if( file_exists( $path ) ) include( $path );
2430 } else {
2431 WPF()->tpl->show_template( $template );
2432 }
2433 } else {
2434 WPF()->tpl->show_msg( wpforo_phrase( 'You do not have permission to view this page', false ) );
2435 }
2436 }
2437
2438 /**
2439 * @param string $html
2440 *
2441 * @return string
2442 */
2443 function wpforo_apply_ucf_shortcode( $html ) {
2444 return preg_replace_callback( '#\[wpfucf\s([^\[\]]+?)]#iu', function( $shortcode ) {
2445 if( preg_match( '#(?:^|\s)field=[\'\"]([^\'\"]+?)[\'\"]#iu', $shortcode[1], $field ) ) {
2446 if( $field_key = trim( $field[1] ) ) {
2447 $userid = 0;
2448 if( preg_match(
2449 '#(?:^|\s)id=[\'\"]([^\'\"]+?)[\'\"]#iu',
2450 $shortcode[1],
2451 $id
2452 ) ) {
2453 $userid = wpforo_bigintval( $id[1] );
2454 }
2455 if( ! $userid ) {
2456 $userid = WPF()->current_object['userid'] ? WPF()->current_object['userid'] : WPF()->current_userid;
2457 }
2458 if( $f = WPF()->member->get_field( $field_key ) ) {
2459 if( wpfval( $f, 'type' ) === 'html' && ( $html = trim( (string) wpfval( $f, 'html' ) ) ) ) {
2460 return (string) WPF()->form->field_html( $f );
2461 } elseif( WPF()->form->can_view( $f ) && WPF()->form->can_show_value( $f ) ) {
2462 $f['value'] = wpforo_member( $userid, $field_key );
2463 $f = WPF()->form->prepare_values( WPF()->form->esc_field( $f ), $userid );
2464
2465 return $f['value'];
2466 }
2467 }
2468 }
2469 }
2470
2471 return ''; // if that field has not found
2472 }, (string) $html );
2473 }
2474
2475 /**
2476 * @param array|string $template
2477 * @param array|int $member
2478 * @param string $default
2479 *
2480 * @return string
2481 */
2482 function wpforo_member_url( $template = null, $member = null, $default = '' ) {
2483 $member_url = $default;
2484 $template = WPF()->tpl->get_template( $template );
2485 if( $template && wpforo_is_member_template( $template ) ) {
2486 if( ! $member ) $member = WPF()->current_object['user'] ? WPF()->current_object['user'] : WPF()->current_user;
2487 $profile_url = WPF()->member->get_profile_url( $member, $template['key'] );
2488 if( $profile_url ) $member_url = $profile_url;
2489 }
2490
2491 return $member_url;
2492 }
2493
2494 function wpforo_mark_all_read_link() {
2495 if( ! wpforo_is_bot() ) : ?>
2496 <a class="wpf-mark-all-read"
2497 href="<?php echo wp_nonce_url( '?foro=allread', 'wpforo_mark_all_read', 'foro_n' ); ?>" rel="nofollow">
2498 <i class="fa-regular fa-circle-check"></i>
2499 <span><?php wpforo_phrase( 'Mark all read' ) ?></span>
2500 </a>
2501 <?php
2502 endif;
2503 }
2504
2505 function wpforo_login_link() {
2506 if( ! wpforo_is_bot() ) : ?>
2507 <a href="<?php echo wpforo_login_url(); ?>"><i class="fas fa-sign-in-alt"></i> <?php wpforo_phrase(
2508 'Login'
2509 ); ?></a>
2510 <?php
2511 endif;
2512 }
2513
2514 function wpforo_register_link() {
2515 if( ! wpforo_is_bot() ) : ?>
2516 <a href="<?php echo wpforo_register_url(); ?>"><i class="fas fa-user-plus"></i> <?php wpforo_phrase(
2517 'Create Account'
2518 ); ?></a>
2519 <?php
2520 endif;
2521 }
2522
2523 function wpforo_topic_form_extra( $forumid, $values ) { ?>
2524 <div class="wpf-extra-fields">
2525 <?php do_action( 'wpforo_topic_form_extra_fields_before', $forumid, $values ) ?>
2526 <div class="wpf-main-fields">
2527 <?php do_action( 'wpforo_topic_form_buttons_hook', $forumid, $values ); ?>
2528 </div>
2529 <?php do_action( 'wpforo_topic_form_extra_fields_after', $forumid, $values ) ?>
2530 </div>
2531 <?php
2532 }
2533
2534 function wpforo_reply_form_extra( $topic, $values ) { ?>
2535 <div class="wpf-extra-fields">
2536 <?php do_action( 'wpforo_reply_form_extra_fields_before', $topic, $values ) ?>
2537 <?php do_action( 'wpforo_reply_form_buttons_hook', $topic, $values ); ?>
2538 <?php do_action( 'wpforo_reply_form_extra_fields_after', $topic, $values ) ?>
2539 </div>
2540 <?php
2541 }
2542
2543 function wpforo_post_search_form( $values ) {
2544 WPF()->tpl->post_search_form( $values );
2545 }
2546
2547 function wpforo_member_search_form() {
2548 WPF()->tpl->member_search_form();
2549 }
2550
2551 function wpforo_member_buttons( $member ) {
2552 WPF()->tpl->member_buttons( $member );
2553 }
2554
2555 function wpforo_sanitize_search_body( $body, $needle, $post = [] ) {
2556 $body = wpforo_content_filter( $body, $post );
2557 $body = wpforo_text( $body, 0, false );
2558 $words = explode( ' ', trim( (string) $needle ) );
2559 if( ! empty( $words ) ) {
2560 $body_len = apply_filters( 'wpforo_search_results_body_length', 564 );
2561 $pos = mb_stripos( $body, " " . trim( $words[0] ), 0, get_option( 'blog_charset' ) );
2562 if( strlen( (string) $body ) > $body_len && $pos !== false ) {
2563 if( $pos > ( $body_len / 2 ) ) {
2564 $bef_body = "... ";
2565 $start = mb_stripos( $body, " ", ( $body_len / 2 ), get_option( 'blog_charset' ) );
2566 } else {
2567 $bef_body = "";
2568 $start = 0;
2569 }
2570 if( ( mb_strlen( (string) $body, get_option( 'blog_charset' ) ) - $start ) > $body_len ) {
2571 $aft_body = " ...";
2572 } else {
2573 $aft_body = "";
2574 }
2575 $body = $bef_body . mb_substr(
2576 (string) $body,
2577 $start,
2578 $body_len,
2579 get_option( 'blog_charset' )
2580 ) . $aft_body;
2581 }
2582 foreach( $words as $word ) {
2583 $word = trim( (string) $word );
2584 $body = preg_replace(
2585 '#(?:^|\s+)' . preg_quote( esc_html( $word ) ) . '#iu',
2586 ' <span class="wpf-sword wpfcl-b">' . esc_html( $word ) . '</span>',
2587 (string) $body
2588 );
2589 }
2590 }
2591
2592 return $body;
2593 }
2594
2595 function wpforo_topic_starter( $topic, $post, $type = 'full' ) {
2596
2597 $starter = false;
2598
2599 $topic_user = (int) wpfval( $topic, 'userid' );
2600 $post_user = (int) wpfval( $post, 'userid' );
2601 $topic_guest = wpfval( $topic, 'email' );
2602 $post_guest = wpfval( $post, 'email' );
2603
2604 if( $topic_user && $topic_user === $post_user ) {
2605 $starter = true;
2606 } elseif( $topic_guest && $topic_guest === $post_guest ) {
2607 $starter = true;
2608 }
2609
2610 if( $starter ) {
2611 if( $type === 'full' ) {
2612 echo '<span class="wpf-post-starter"><i class="fas fa-feather-alt"></i> ' . wpforo_phrase(
2613 'Topic starter',
2614 false
2615 ) . '</span>';
2616 } elseif( $type === 'icon' ) {
2617 echo '<span class="wpf-post-starter" wpf-tooltip="' . wpforo_phrase(
2618 'Topic starter',
2619 false
2620 ) . '"><i class="fas fa-feather-alt"></i></span>';
2621 } elseif( $type === 'label' ) {
2622 echo '<span class="wpf-post-starter">' . wpforo_phrase( 'Topic starter', false ) . '</span>';
2623 }
2624 }
2625 }
2626
2627 function wpforo_get_not_replied_topicids() {
2628 $sql = "SELECT `topicid`, COUNT(DISTINCT userid) AS userid_count FROM `" . WPF()->tables->posts . "` GROUP BY `topicid` HAVING userid_count = 1";
2629
2630 return (array) WPF()->db->get_col( $sql, 0 );
2631 }
2632
2633 /**
2634 * @param array $topic
2635 * @param string $url
2636 * @param string $structure
2637 * {i} - topic icon (empty, one reply, replied)
2638 * {is} - topic icon and status icons (+ sticky, closed, private...)
2639 * {t} - topic title
2640 * {tc} - topic title cut by x number of chars
2641 * {au} - link opening tag, link goes to first unread post
2642 * {at} - link opening tag, link goes to the topic url
2643 * {af} - link opening tag, link goes to the first post
2644 * {a} - link opening tag, link goes to the provided url
2645 * {/a} - link closing tag, this is required
2646 * {v} - displays number of users currently viewing the topic
2647 * {n} - displays [new] info when a new unread post has been posted in the topic
2648 *
2649 * Example {a}{t}{/a} - this is the topic title as a link going to the first post.
2650 *
2651 * @param bool $echo
2652 * @param string $class
2653 * @param int $length
2654 *
2655 *
2656 *
2657 *
2658 * @return mixed|string|void|null
2659 */
2660 function wpforo_topic_title( $topic, $url, $structure = '{i}{au}{t}{/a}{n}{v}', $echo = true, $class = '', $length = 70 ) {
2661 $html = __( 'No Title', 'wpforo' );
2662 if( $title = trim( esc_html( (string) wpfval( $topic, 'title' ) ) ) ) {
2663 $structure = apply_filters( 'wpforo_topic_title_structure', $structure, $topic );
2664 $class = ( $class ) ? ' class="' . esc_attr( $class ) . '"' : '';
2665 $a = '<a href="' . esc_url( (string) $url ) . '"' . $class . ' title="' . esc_attr( $topic['title'] ) . '">';
2666 $topic['url'] = $url;
2667
2668 // For better performance, make sure the component in demand before getting it.
2669 $title_cut = ( strpos( (string) $structure, '{tc}' ) !== false ) ? esc_html( wpforo_text( $topic['title'], $length, false ) ) : '';
2670 $icon_all = ( strpos( (string) $structure, '{i}' ) !== false ) ? wpforo_topic_icon( $topic, 'all', true, false ) : '';
2671 $icon_status = ( strpos( (string) $structure, '{is}' ) !== false ) ? wpforo_topic_icon( $topic, 'status', true, false ) : '';
2672 $viewing = ( strpos( (string) $structure, '{v}' ) !== false ) ? wpforo_viewing( $topic, false ) : '';
2673 $new = ( strpos( (string) $structure, '{n}' ) !== false ) ? wpforo_unread_button( $topic['topicid'], $url, false ) : '';
2674 $au = ( strpos( (string) $structure, '{au}' ) !== false ) ? '<a href="' . wpforo_unread_url( $topic['topicid'], $url, false ) . '" title="' . esc_attr(
2675 $topic['title']
2676 ) . '"' . $class . '>' : '';
2677 $at = ( strpos( (string) $structure, '{at}' ) !== false ) ? '<a href="' . wpforo_topic( $topic['topicid'], 'url' ) . '" title="' . esc_attr(
2678 $topic['title']
2679 ) . '"' . $class . '>' : '';
2680 $af = ( strpos( (string) $structure, '{af}' ) !== false ) ? '<a href="' . wpforo_post( $topic['first_postid'], 'url' ) . '" title="' . esc_attr(
2681 $topic['title']
2682 ) . '"' . $class . '>' : '';
2683
2684 $components = [
2685 '{a}' => $a,
2686 '{au}' => $au,
2687 '{at}' => $at,
2688 '{af}' => $af,
2689 '{/a}' => '</a>',
2690 '{i}' => $icon_all,
2691 '{is}' => $icon_status,
2692 '{t}' => $title,
2693 '{tc}' => $title_cut,
2694 '{v}' => ' ' . $viewing,
2695 '{n}' => ' ' . $new,
2696 '{p}' => '',
2697 '{pt}' => '',
2698 ];
2699 $components = apply_filters( 'wpforo_topic_title_components', $components, $topic );
2700 $html = strtr( $structure, $components );
2701 $html = apply_filters( 'wpforo_topic_title_html', $html, $topic, $structure, $components );
2702 }
2703 if( ! $echo ) return $html;
2704 echo $html;
2705 }
2706
2707 function wpforo_single_title( $type = 'post', $item = [], $before = '', $after = '', $echo = true ) {
2708 $title = '';
2709 if( $type === 'post' && wpfval( $item, 'title' ) ) {
2710 $icon_title = WPF()->tpl->icon( 'topic', $item, false, 'title' );
2711 if( $icon_title ) {
2712 $title .= '<span class="wpf-status-title">[' . esc_html( $icon_title ) . ']</span> ';
2713 }
2714 $title = apply_filters( "wpforo_single_{$type}_pre_title", $title, $item );
2715 $title .= esc_html( wpforo_text( $item['title'], 0, false ) );
2716 }
2717 $title = apply_filters( "wpforo_single_{$type}_title", $title );
2718 if( $title ) $title = $before . $title . $after;
2719 if( $echo ) echo $title;
2720
2721 return $title;
2722 }
2723
2724 function wpforo_schema( $forum, $topic, $post, $type = '' ) {
2725 $schema = '';
2726
2727 if( apply_filters( "wpforo_schema", true ) ) {
2728
2729 if( ! $type ) {
2730 $type = 'DiscussionForumPosting';
2731 if( 3 === (int) wpfval( $forum, 'layout' ) ) {
2732 $type = 'QAPage';
2733 }
2734 }
2735 if( $type === 'QAPage' && wpfval( $topic, 'topicid' ) ) {
2736 $extended = apply_filters( "wpforo_schema_qa_extended_best_answer", true );
2737 $best_answer = WPF()->post->get_best_answer( $topic['topicid'], $extended );
2738 $suggested_answers_count = apply_filters( "wpforo_schema_qa_suggested_answers_count", 3 );
2739 $suggested_answers = WPF()->post->get_suggested_answers(
2740 $topic['topicid'],
2741 $suggested_answers_count
2742 );
2743 $schema .= '
2744 <script type="application/ld+json">
2745 {
2746 "@context": "https://schema.org",
2747 "@type": "QAPage",
2748 "mainEntity": {
2749 "@type": "Question",
2750 "name": "' . esc_attr( $post['title'] ) . '",
2751 "text": "' . esc_attr( sanitize_text_field( $post['body'] ) ) . '",
2752 "answerCount": ' . intval( $topic['answers'] ) . ',
2753 "upvoteCount": ' . intval( $post['votes'] ) . ',
2754 "dateCreated": "' . gmdate( 'Y-m-d', strtotime( $post['created'] . ' GMT' ) ) . 'T' . gmdate(
2755 'H:i',
2756 strtotime( $post['created'] . ' GMT' )
2757 ) . 'Z",
2758 "author": {
2759 "@type": "Person",
2760 "name": "' . wpforo_member( $post['created'], 'display_name' ) . '"
2761 }';
2762 if( ! empty( $best_answer ) ) {
2763 $schema .= ',
2764 "acceptedAnswer": {
2765 "@type": "Answer",
2766 "text": "' . esc_attr( sanitize_text_field( $best_answer['body'] ) ) . '",
2767 "dateCreated": "' . gmdate(
2768 'Y-m-d',
2769 strtotime( $best_answer['created'] . ' GMT' )
2770 ) . 'T' . gmdate(
2771 'H:i',
2772 strtotime( $best_answer['created'] . ' GMT' )
2773 ) . 'Z",
2774 "upvoteCount": ' . intval( $best_answer['votes'] ) . ',
2775 "url": "' . wpforo_post( $best_answer['postid'], 'url' ) . '",
2776 "author": {
2777 "@type": "Person",
2778 "name": "' . wpforo_member( $best_answer['userid'], 'display_name' ) . '"
2779 }
2780 }';
2781 }
2782 if( ! empty( $suggested_answers ) ) {
2783 $schema .= ',
2784 "suggestedAnswer": [';
2785 $answers = '';
2786 foreach( $suggested_answers as $key => $suggested_answer ) {
2787 if( wpfval( $best_answer, 'postid' ) === wpfval( $suggested_answer, 'postid' ) ) {
2788 continue;
2789 }
2790 $answers .= '
2791 {
2792 "@type": "Answer",
2793 "text": "' . esc_attr( sanitize_text_field( $suggested_answer['body'] ) ) . '",
2794 "dateCreated": "' . gmdate(
2795 'Y-m-d',
2796 strtotime( $suggested_answer['created'] . ' GMT' )
2797 ) . 'T' . gmdate(
2798 'H:i',
2799 strtotime( $suggested_answer['created'] . ' GMT' )
2800 ) . 'Z",
2801 "upvoteCount": ' . intval( $suggested_answer['votes'] ) . ',
2802 "url": "' . wpforo_post( $suggested_answer['postid'], 'url' ) . '",
2803 "author": {
2804 "@type": "Person",
2805 "name": "' . wpforo_member( $suggested_answer['userid'], 'display_name' ) . '"
2806 }
2807 },';
2808 }
2809 $schema .= trim( $answers, ',' ) . '
2810 ]';
2811 }
2812 $schema .= '
2813 }
2814 }
2815 </script>';
2816 } elseif( wpfval( $topic, 'topicid' ) ) {
2817 $topic_posts = '';
2818 $paged = WPF()->current_object['paged'];
2819 $posts = WPF()->current_object['posts'];
2820 $topic_post = wpforo_post( $topic['first_postid'] );
2821 if( $paged > 1 || ( $paged === 1 && count( $posts ) > 1 ) ) {
2822 $topic_posts .= '"comment": [';
2823 foreach( $posts as $key => $post ) {
2824 if( $paged === 1 && $key == 0 ) continue;
2825 $post_text = wpforo_text( stripslashes( sanitize_text_field( $post['body'] ) ), 5000, false );
2826 $post_author = wpforo_generate_scheme_author_object( $post['userid'] );
2827 $post_images = wpforo_generate_scheme_image_object( $post['body'] );
2828 if( ! $post_text && ! $post_images ) continue;
2829 $topic_posts .= '{
2830 "@type": "Comment",
2831 "text": "' . esc_attr( $post_text ) . '",' . $post_images . '
2832 "datePublished": "' . gmdate( 'Y-m-d\TH:i:s\Z', strtotime( $post['created'] ) ) . '"' . $post_author . '
2833 },';
2834 }
2835 $topic_posts = ',' . trim( $topic_posts, ',' ) . ']';
2836 }
2837
2838 $topic_author = wpforo_generate_scheme_author_object( $topic['userid'] );
2839 $topic_images = wpforo_generate_scheme_image_object( $topic_post['body'] );
2840 $topic_text = wpforo_text( stripslashes( sanitize_text_field( $topic_post['body'] ) ), 5000, false );
2841 if( $topic_text || $topic_images ) {
2842 $schema .= '
2843 <script type="application/ld+json">
2844 {
2845 "@context": "https://schema.org",
2846 "@type": "DiscussionForumPosting",
2847 "mainEntityOfPage": "' . esc_url_raw( $topic['url'] ) . '",
2848 "headline": "' . esc_attr( $topic_post['title'] ) . '",
2849 "text": "' . esc_attr( $topic_text ) . '",' . $topic_images . '
2850 "url": "' . esc_url_raw( $topic['url'] ) . '"' . $topic_author . ',
2851 "datePublished": "' . gmdate( 'Y-m-d\TH:i:s\Z', strtotime( $topic_post['created'] ) ) . '",
2852 "interactionStatistic": {
2853 "@type": "InteractionCounter",
2854 "interactionType": "https://schema.org/LikeAction",
2855 "userInteractionCount": ' . intval( $topic['posts'] ) . '
2856 }
2857 ' . $topic_posts . '
2858 }
2859 </script>';
2860 }
2861 }
2862 }
2863
2864 return $schema;
2865 }
2866
2867 function wpforo_template_profile_board_panel() {
2868 echo WPF()->tpl->profile_board_panel();
2869 }
2870
2871 function wpforo_template_profile_activity_panel() {
2872 echo WPF()->tpl->profile_activity_panel();
2873 }
2874
2875 function wpforo_template_profile_favored_panel() {
2876 echo WPF()->tpl->profile_favored_panel();
2877 }
2878
2879 function wpforo_profile_head_attrs( $user ) {
2880 printf(
2881 'style="background-image:url(\'%1$s\')"',
2882 esc_url(
2883 trim( (string) wpfval( $user, 'cover' ) ) ? trim( (string) wpfval( $user, 'cover' ) ) . '?lm=' . time() : wpforo_setting( 'profiles', 'default_cover' )
2884 )
2885 );
2886 }
2887
2888 function wpforo_template_profile_action_buttons( $user ) {
2889 do_action( 'wpforo_template_profile_action_buttons_left', $user ); ?>
2890 <div class="wpf-grow"></div>
2891 <?php do_action( 'wpforo_template_profile_action_buttons_right', $user );
2892 echo WPF()->tpl->get_member_actions_html( $user );
2893 }
2894
2895
2896 function wpforo_get_users_count_for_topic( $topicid = 0 ) {
2897 if( ! $topicid ) $topicid = WPF()->current_object['topicid'];
2898
2899 return WPF()->post->get_users_count_for_topic( $topicid );
2900 }
2901
2902 function wpforo_get_likes_for_topic( $topicid ) {
2903 return WPF()->reaction->get_likes_for_topic( $topicid );
2904 }
2905
2906 function wpforo_display_header( $template = '' ) {
2907 if( ! $template ) $template = WPF()->current_object['template'];
2908
2909 return ! wpforo_is_member_template( $template ) || wpforo_setting( 'profiles', 'profile_header' );
2910 }
2911
2912 function wpforo_display_footer( $template = '' ) {
2913 if( ! $template ) $template = WPF()->current_object['template'];
2914
2915 return ( ( wpforo_is_member_template( $template ) && wpforo_setting(
2916 'profiles',
2917 'profile_footer'
2918 ) ) || ( ! wpforo_is_member_template( $template ) && wpforo_setting( 'components', 'footer' ) ) );
2919 }
2920
2921 function wpforo_forum_list_need_add_topic_button( $forumid ) {
2922 switch( WPF()->forum->get_layout( $forumid ) ) {
2923 case 2:
2924 $need_button = wpforo_setting( 'forums', 'layout_simplified_add_topic_button' );
2925 break;
2926 case 3:
2927 $need_button = wpforo_setting( 'forums', 'layout_qa_add_topic_button' );
2928 break;
2929 case 4:
2930 $need_button = wpforo_setting( 'forums', 'layout_threaded_add_topic_button' );
2931 break;
2932 default:
2933 $need_button = true;
2934 break;
2935 }
2936
2937 return $need_button;
2938 }
2939
2940 function wpforo_get_forum_cover_styles( $forum ) {
2941 $r = [
2942 'cover' => '',
2943 'title' => '',
2944 'info' => '',
2945 'button' => '',
2946 'blur' => '',
2947 ];
2948
2949 if( $forum['cover_url'] ) {
2950 $border_radius = ( (int) $forum['layout'] !== 4 ) ? 'border-radius: 4px;' : ( ! is_rtl() ? 'border-radius: 4px 0 0 4px;' : 'border-radius: 0 4px 4px 0;' );
2951 $r['cover'] = sprintf(
2952 'style="background-image:url(\'%1$s\'); background-position: center; background-size: cover; height:170px; padding:0;"',
2953 esc_attr( $forum['cover_url'] )
2954 );
2955 $r['blur'] = 'style="background: rgba(255, 255, 255, 0.7); color:#000"';
2956 $r['title'] = 'style="display: inline-block; line-height: 20px; ' . $border_radius . ' color:#000"';
2957 $r['info'] = 'style="padding-bottom: 1px; padding-top:3px;"';
2958 $r['button'] = 'style="padding-top:3px; padding-bottom: 2px; line-height: 26px; ' . ( ! is_rtl() ? 'border-radius: 0 4px 4px 0;' : 'border-radius: 4px 0 0 4px;' ) . '"';
2959 }
2960
2961 return $r;
2962 }
2963
2964 function wpforo_post_search_url( $field_name, $field_value ) {
2965 return wpforo_home_url(
2966 '?' . http_build_query(
2967 [
2968 'wpfs' => '',
2969 'wpfin' => 'entire-posts',
2970 'wpfd' => '0',
2971 'wpfob' => 'date',
2972 'wpfo' => 'desc',
2973 'data' => [
2974 'text_field' => '',
2975 'uniqid' => '',
2976 $field_name => $field_value,
2977 ],
2978 ]
2979 )
2980 );
2981 }
2982
2983 function wpforo_post_search_link( $field_name, $field_value ) {
2984 return sprintf(
2985 '<a href="%1$s">%2$s</a>',
2986 wpforo_post_search_url( $field_name, $field_value ),
2987 $field_value
2988 );
2989 }
2990
2991 function wpforo_get_sum_of_topics_posts( int $forumid, bool $check_permission = true ): array {
2992 return WPF()->forum->get_sum_of_topics_posts( $forumid, $check_permission );
2993 }
2994
2995 function _wpforo_main_wrap_classes(): array {
2996 $classes = [];
2997 switch( wpforo_setting( 'components', 'sidebar_location' ) ) {
2998 case 'left':
2999 $classes[] = 'wpforo-left-sidebar-layout';
3000 break;
3001 case 'right':
3002 $classes[] = 'wpforo-right-sidebar-layout';
3003 break;
3004 }
3005
3006 return apply_filters( 'wpforo_main_wrap_classes', $classes );
3007 }
3008
3009 function wpforo_main_wrap_classes(): void {
3010 echo implode( ' ', _wpforo_main_wrap_classes() );
3011 }
3012
3013 function wpforo_get_recent_page_title(): string {
3014 $view = wpfval( WPF()->current_object['args'], 'view' );
3015 switch( $view ) {
3016 case 'unread':
3017 $page_title = wpforo_phrase( 'Unread Posts', false );
3018 break;
3019 case 'no-replies':
3020 $page_title = wpforo_phrase( 'Not Replied Topics', false );
3021 break;
3022 case 'solved':
3023 $page_title = wpforo_phrase( 'Solved Topics', false );
3024 break;
3025 case 'unsolved':
3026 $page_title = wpforo_phrase( 'Unsolved Topics', false );
3027 break;
3028 case 'closed':
3029 $page_title = wpforo_phrase( 'Closed Topics', false );
3030 break;
3031 case 'opened':
3032 $page_title = wpforo_phrase( 'Open Topics', false );
3033 break;
3034 case 'sticky':
3035 $page_title = wpforo_phrase( 'Sticky Topics', false );
3036 break;
3037 case 'private':
3038 $page_title = wpforo_phrase( 'Private Topics', false );
3039 break;
3040 case 'unapproved':
3041 $page_title = wpforo_phrase( 'Unapproved Posts', false );
3042 break;
3043 default:
3044 $page_title = wpforo_phrase( 'Recent Posts', false );
3045 break;
3046 }
3047
3048 return apply_filters( 'wpforo_recent_posts_page_title', $page_title, WPF()->current_object['args'], $view );
3049 }
3050
3051 function wpforo_get_rss_feed_links(): string {
3052 if( wpforo_setting( 'rss', 'feed' ) ) {
3053 return sprintf(
3054 ' <sep> &nbsp;|&nbsp;</sep>
3055 <span class="wpf-feed-forums">
3056 <a href="%1$s" title="%2$s" target="_blank">
3057 <span>%3$s</span> <i class="fas fa-rss wpfsx"></i>
3058 </a>
3059 </span>
3060 <sep> &nbsp;|&nbsp;</sep>
3061 <span class="wpf-feed-topics">
3062 <a href="%4$s" title="%5$s" target="_blank">
3063 <span>%6$s</span> <i class="fas fa-rss wpfsx"></i>
3064 </a>
3065 </span>',
3066 WPF()->feed->rss2_url( false, 'forum' ),
3067 wpforo_phrase( 'Forums RSS Feed', false ),
3068 wpforo_phrase( 'Forums', false ),
3069 WPF()->feed->rss2_url( false, 'topic' ),
3070 wpforo_phrase( 'Topics RSS Feed', false ),
3071 wpforo_phrase( 'Topics', false )
3072 );
3073 }
3074
3075 return '';
3076 }
3077
3078 function wpforo_rss_feed_links(): void {
3079 echo wpforo_get_rss_feed_links();
3080 }
3081
3082 function wpforo_get_recent_page_filter_selectbox(): string {
3083 $filters = [
3084 'recent' => wpforo_phrase( 'Recent Posts', false ),
3085 'unread' => wpforo_phrase( 'Unread Posts', false ),
3086 'no-replies' => wpforo_phrase( 'Not Replied Topics', false ),
3087 'solved' => wpforo_phrase( 'Solved Topics', false ),
3088 'unsolved' => wpforo_phrase( 'Unsolved Topics', false ),
3089 'closed' => wpforo_phrase( 'Closed Topics', false ),
3090 'opened' => wpforo_phrase( 'Open Topics', false ),
3091 'sticky' => wpforo_phrase( 'Sticky Topics', false ),
3092 ];
3093
3094 // Add admin/moderator-specific filters
3095 if( wpforo_current_user_is( 'admin' ) || wpforo_current_user_is( 'moderator' ) ) {
3096 $filters['private'] = wpforo_phrase( 'Private Topics', false );
3097 $filters['unapproved'] = wpforo_phrase( 'Unapproved Posts', false );
3098 }
3099
3100 $options_html = '';
3101 foreach( $filters as $key => $value ) {
3102 $options_html .= sprintf(
3103 '<option value="?view=%1$s%2$s" %3$s>%4$s</option>',
3104 $key !== 'recent' ? $key : '',
3105 ( wpfval( WPF()->current_object['args'], 'prefixid' ) ? sprintf( '&prefixid=%1$d', WPF()->current_object['args']['prefixid'] ) : '' ),
3106 wpfo_check( wpfval( WPF()->current_object['args'], 'view' ), $key, 'selected', false ),
3107 $value
3108 );
3109 }
3110
3111 return sprintf( '<label><select onchange="window.location.assign(this.value)">%1$s</select></label>', $options_html );
3112 }
3113
3114 /**
3115 * Get icon HTML for activity type
3116 * Reads from WPF()->activity->actions for single source of truth
3117 *
3118 * @param string $type Activity type
3119 * @return string Icon HTML (SVG)
3120 */
3121 function wpforo_activity_icon( $type ) {
3122 $actions = WPF()->activity->actions;
3123 $icon = isset( $actions[ $type ]['icon'] ) ? $actions[ $type ]['icon'] : '';
3124 if( ! $icon ) {
3125 $icon = isset( $actions['default']['icon'] ) ? $actions['default']['icon'] : '';
3126 }
3127
3128 return apply_filters( 'wpforo_activity_icon', $icon, $type );
3129 }
3130
3131 /**
3132 * Get label for activity type
3133 *
3134 * @param string $type Activity type
3135 * @return string Translated label
3136 */
3137 function wpforo_activity_label( $type ) {
3138 $labels = [
3139 'wpforo_topic' => wpforo_phrase( 'created the topic', false ),
3140 'wpforo_post' => wpforo_phrase( 'replied to', false ),
3141 'edit_topic' => wpforo_phrase( 'edited a topic', false ),
3142 'edit_post' => wpforo_phrase( 'edited a post', false ),
3143 'new_reply' => wpforo_phrase( 'replied to', false ),
3144 'new_like' => wpforo_phrase( 'liked', false ),
3145 'new_dislike' => wpforo_phrase( 'disliked', false ),
3146 'new_up_vote' => wpforo_phrase( 'voted up', false ),
3147 'new_down_vote' => wpforo_phrase( 'voted down', false ),
3148 'new_reaction' => wpforo_phrase( 'reacted to', false ),
3149 'new_mention' => wpforo_phrase( 'mentioned in', false ),
3150 'new_favorite' => wpforo_phrase( 'favorited', false ),
3151 'topic_solved' => wpforo_phrase( 'marked as solved', false ),
3152 'new_closed' => wpforo_phrase( 'closed the topic', false ),
3153 'default' => wpforo_phrase( 'activity', false ),
3154 ];
3155
3156 $labels = apply_filters( 'wpforo_activity_labels', $labels );
3157
3158 return wpfval( $labels, $type ) ?: $labels['default'];
3159 }
3160
3161 /**
3162 * Render detailed activity info HTML based on activity type
3163 *
3164 * @param array $activity Activity data
3165 * @param array $member User who performed the activity
3166 * @return string HTML for the activity info row
3167 */
3168 function wpforo_activity_info_html( $activity, $member ) {
3169 $type = $activity['type'];
3170 $html = '';
3171
3172 // Get post and topic data if available
3173 $post = [];
3174 $topic = [];
3175 $forum = [];
3176 $post_author = [];
3177
3178 if( $activity['itemid'] ) {
3179 // For wpforo_topic, topic_solved, and topic_closed, itemid is the topicid, not postid
3180 if( $type === 'wpforo_topic' || $type === 'topic_solved' || $type === 'topic_closed' ) {
3181 $topic = WPF()->topic->get_topic( $activity['itemid'] );
3182 if( $topic && ! empty( $topic['forumid'] ) ) {
3183 $forum = WPF()->forum->get_forum( $topic['forumid'] );
3184 }
3185 } else {
3186 $post = WPF()->post->get_post( $activity['itemid'] );
3187 if( $post && ! empty( $post['topicid'] ) ) {
3188 $topic = WPF()->topic->get_topic( $post['topicid'] );
3189 if( $topic && ! empty( $topic['forumid'] ) ) {
3190 $forum = WPF()->forum->get_forum( $topic['forumid'] );
3191 }
3192 // Get post author for reaction-type activities
3193 if( ! empty( $post['userid'] ) && $post['userid'] != $activity['userid'] ) {
3194 $post_author = wpforo_member( $post['userid'] );
3195 }
3196 }
3197 }
3198 }
3199
3200 // Build topic link
3201 $topic_title = '';
3202 $topic_link = '';
3203 if( $topic ) {
3204 $topic_title = esc_html( $topic['title'] );
3205 $topic_url = $activity['permalink'] ?: WPF()->topic->get_url( $topic['topicid'] );
3206 $topic_link = '<a href="' . esc_url( $topic_url ) . '" class="wpf-activity-topic-link">' . $topic_title . '</a>';
3207 }
3208
3209 // Build forum link
3210 $forum_link = '';
3211 if( $forum ) {
3212 $forum_title = esc_html( $forum['title'] );
3213 $forum_url = WPF()->forum->get_forum_url( $forum );
3214 $forum_link = '<a href="' . esc_url( $forum_url ) . '" class="wpf-activity-forum-link">' . $forum_title . '</a>';
3215 }
3216
3217 // Build post author link for reactions
3218 $author_link = '';
3219 if( $post_author && ! empty( $post_author['display_name'] ) ) {
3220 $author_link = '<a href="' . esc_url( wpforo_member_url( $post_author ) ) . '" class="wpf-activity-author-link">' . esc_html( $post_author['display_name'] ) . '</a>';
3221 }
3222
3223 // Build HTML based on activity type
3224 switch( $type ) {
3225 case 'wpforo_topic':
3226 // {User} created the topic {Topic Title} in {Forum Title}
3227 $html = sprintf(
3228 wpforo_phrase( 'created the topic %1$s in %2$s', false ),
3229 $topic_link,
3230 $forum_link
3231 );
3232 break;
3233
3234 case 'wpforo_post':
3235 case 'new_reply':
3236 // {User} replied to the topic {Topic Title}
3237 $html = sprintf(
3238 wpforo_phrase( 'replied to the topic %s', false ),
3239 $topic_link
3240 );
3241 break;
3242
3243 case 'new_like':
3244 case 'new_dislike':
3245 case 'new_up_vote':
3246 case 'new_down_vote':
3247 case 'new_reaction':
3248 // {User} reacted to {Author}'s post in the topic {Topic Title}
3249 if( $author_link ) {
3250 $html = sprintf(
3251 wpforo_phrase( 'reacted to %1$s\'s post in the topic %2$s', false ),
3252 $author_link,
3253 $topic_link
3254 );
3255 } else {
3256 $html = sprintf(
3257 wpforo_phrase( 'reacted to a post in the topic %s', false ),
3258 $topic_link
3259 );
3260 }
3261 break;
3262
3263 case 'new_favorite':
3264 // {User} favorited {Author}'s post in the topic {Topic Title}
3265 if( $author_link ) {
3266 $html = sprintf(
3267 wpforo_phrase( 'favorited %1$s\'s post in the topic %2$s', false ),
3268 $author_link,
3269 $topic_link
3270 );
3271 } else {
3272 $html = sprintf(
3273 wpforo_phrase( 'favorited a post in the topic %s', false ),
3274 $topic_link
3275 );
3276 }
3277 break;
3278
3279 case 'new_solved':
3280 case 'topic_solved':
3281 // {User} marked as solved the topic {Topic Title}
3282 $html = sprintf(
3283 wpforo_phrase( 'marked as solved the topic %s', false ),
3284 $topic_link
3285 );
3286 break;
3287
3288 case 'new_closed':
3289 case 'topic_closed':
3290 // {User} closed the topic {Topic Title}
3291 $html = sprintf(
3292 wpforo_phrase( 'closed the topic %s', false ),
3293 $topic_link
3294 );
3295 break;
3296
3297 case 'post_answer':
3298 // {User} marked as answer a post in the topic {Topic Title}
3299 $html = sprintf(
3300 wpforo_phrase( 'marked as answer a post in the topic %s', false ),
3301 $topic_link
3302 );
3303 break;
3304
3305 case 'new_mention':
3306 // {User} mentioned you in {Topic Title}
3307 $html = sprintf(
3308 wpforo_phrase( 'mentioned you in %s', false ),
3309 $topic_link
3310 );
3311 break;
3312
3313 case 'edit_topic':
3314 case 'edit_post':
3315 // {User} edited a post in {Topic Title}
3316 $html = sprintf(
3317 wpforo_phrase( 'edited a post in %s', false ),
3318 $topic_link
3319 );
3320 break;
3321
3322 default:
3323 // Fallback to simple label
3324 $label = wpforo_activity_label( $type );
3325 $html = $label . ( $topic_link ? ' ' . $topic_link : '' );
3326 break;
3327 }
3328
3329 return apply_filters( 'wpforo_activity_info_html', $html, $activity, $member );
3330 }
3331