PluginProbe
wpForo Forum / 3.1.6
wpForo Forum v3.1.6
3.1.6 3.1.5 3.1.4 3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.10 1.4.11 1.4.12 1.4.13 All 138 releases
wpforo / includes / functions-template.php

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

3,356 lines 143.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // Exit if accessed directly
3 if( ! defined( 'ABSPATH' ) ) 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=' . urlencode( $tag['tag'] );
661 } else {
662 $tag = WPF()->topic->get_tag( $tagid );
663 if( ! empty( $tag ) ) $tag['url'] = wpforo_home_url() . '?wpfin=tag&wpfs=' . urlencode( $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=' . urlencode( $tag['tag'] );
682 } else {
683 $tag = WPF()->topic->get_tag( $tagid );
684 if( ! empty( $tag ) ) {
685 $tag['url'] = wpforo_home_url() . '?wpfin=tag&wpfs=' . urlencode( $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 esc_url( wpforo_home_url() . '?wpfin=tag&wpfs=' . urlencode( $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 esc_url( wpforo_home_url() . '?wpfin=tag&wpfs=' . urlencode( $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_tags_sort() {
2105 if( ! wpforo_is_module_enabled( 'tags' ) ) return;
2106 $active = wpfval( WPF()->current_object, 'tag_sort' ) ?: 'count';
2107 $base_url = wpforo_home_url( wpforo_settings_get_slug( 'tags' ) );
2108 // Inline stroke SVG icons. Layout lives in theme style.css, colors in styles/matrix.css. Static, trusted markup.
2109 $icons = [
2110 'count' => '<svg viewBox="0 0 20 20" focusable="false"><path d="M3 17V11h3v6M8.5 17V7h3v10M14 17V3h3v14M2 17.5h16"/></svg>',
2111 'az' => '<svg viewBox="0 0 20 20" focusable="false"><path d="M6 3v13M3.5 13.5 6 16l2.5-2.5"/><path d="M11 5h6M11 5l5-2M11 10h6M11 15h6"/></svg>',
2112 'za' => '<svg viewBox="0 0 20 20" focusable="false"><path d="M6 17V4M3.5 6.5 6 4l2.5 2.5"/><path d="M11 5h6M11 10h6M11 15h6M11 15l5 2"/></svg>',
2113 ];
2114 $labels = [ 'count' => 'Frequency', 'az' => 'A-Z', 'za' => 'Z-A' ];
2115 ?>
2116 <div class="wpf-tags-sort" aria-label="<?php echo esc_attr( wpforo_phrase( 'Sort tags', false ) ) ?>">
2117 <span class="wpf-tags-sort__label"><?php echo esc_html( wpforo_phrase( 'Sort by', false ) ) ?>:</span>
2118 <?php foreach( $labels as $key => $label ):
2119 $url = ( $key === 'count' ) ? $base_url : add_query_arg( 'wpf_tag_sort', $key, $base_url );
2120 // Jump back to the forum wrapper on reload so the view is not stuck at the page top.
2121 $url .= '#wpforo';
2122 $cls = ( $active === $key ) ? ' is-active' : ''; ?>
2123 <a href="<?php echo esc_url( $url ) ?>" class="wpf-tags-sort__button<?php echo $cls ?>"<?php echo ( $active === $key ) ? ' aria-current="page"' : '' ?>><?php echo $icons[ $key ] ?><span><?php echo esc_html( wpforo_phrase( $label, false ) ) ?></span></a>
2124 <?php endforeach; ?>
2125 </div>
2126 <?php
2127 }
2128
2129 function wpforo_template_add_topic_button( $forumid = null ) {
2130 if( ! wpforo_is_bot() ) WPF()->tpl->add_topic_button( $forumid );
2131 }
2132
2133 function wpforo_feed_rss2_url( $echo = true, $general = false ) {
2134 WPF()->feed->rss2_url( $echo, $general );
2135 }
2136
2137 function wpforo_feed_link( $type = 'topic' ) {
2138
2139 $nofollow = apply_filters( 'wpforo_feed_link_nofollow', false );
2140 $nofollow = ( $nofollow ) ? ' rel="nofollow" ' : '';
2141
2142 if( wpforo_is_module_enabled( 'rss' ) && wpforo_setting( 'rss', 'feed' ) ): ?>
2143
2144 <?php if( $type === 'topic' && wpforo_setting( 'rss', 'feed_topic' ) ): ?>
2145 <a href="<?php WPF()->feed->rss2_url(); ?>" <?php echo $nofollow ?>
2146 title="<?php wpforo_phrase( 'Topic RSS Feed' ) ?>" target="_blank" class="wpf-button-outlined">
2147 <span class="">RSS</span> <i class="fas fa-rss wpfsx"></i>
2148 </a>
2149 <?php elseif( $type === 'forum' && wpforo_setting( 'rss', 'feed_forum' ) ): ?>
2150 <span class="wpf-feed">
2151 <a href="<?php wpforo_feed_rss2_url() ?>" <?php echo $nofollow ?> title="<?php wpforo_phrase(
2152 'Forum RSS Feed'
2153 ) ?>" target="_blank" class="wpf-button-outlined">
2154 <span style="text-transform: uppercase;">
2155 <?php wpforo_phrase( 'RSS' ) ?>
2156 </span>
2157 <i class="fas fa-rss wpfsx"></i>
2158 </a>
2159 </span>
2160 <?php elseif( $type === 'home' && wpforo_setting( 'rss', 'feed_general' ) ): ?>
2161 <sep> &nbsp;|&nbsp;</sep>
2162 <span class="wpf-feed-forums">
2163 <a href="<?php wpforo_feed_rss2_url(
2164 true,
2165 'forum'
2166 ) ?>" <?php echo $nofollow ?> title="<?php wpforo_phrase( 'Forums RSS Feed' ) ?>" target="_blank">
2167 <span><?php wpforo_phrase( 'Forums' ) ?></span> <i class="fas fa-rss wpfsx"></i>
2168 </a>
2169 </span>
2170 <sep> &nbsp;|&nbsp;</sep>
2171 <span class="wpf-feed-topics">
2172 <a href="<?php wpforo_feed_rss2_url(
2173 true,
2174 'topic'
2175 ) ?>" <?php echo $nofollow ?> title="<?php wpforo_phrase( 'Topics RSS Feed' ) ?>" target="_blank">
2176 <span><?php wpforo_phrase( 'Topics' ) ?></span> <i class="fas fa-rss wpfsx"></i>
2177 </a>
2178 </span>
2179 <?php endif; ?>
2180 <?php endif;
2181 }
2182
2183 function wpforo_template_topic_portable_form( $forumid = null ) {
2184 WPF()->tpl->topic_form_forums_selectbox( $forumid );
2185 }
2186
2187 function wpforo_notifications() {
2188 WPF()->activity->notifications();
2189 }
2190
2191 function wpforo_unread_url( $topicid = 0, $url = '', $echo = true, $force = false ) {
2192 //Only for loggedin users
2193 $enabled = ( ! $force ? wpforo_setting( 'logging', 'goto_unread' ) : true );
2194 if( WPF()->current_userid && $enabled && $topicid && $url ) {
2195 //Get read topics
2196 $topics = WPF()->log->get_read_topics();
2197 if( $last_read_id = wpfval( $topics, $topicid ) ) {
2198 //Make sure the post still located in current topic. When admin split a topic
2199 //and move the last reply to other topic, the users unread topic array isn't updated,
2200 //It's not reasonable to update usermeta of each user, so it's better to check
2201 //the post topic ID here. if the reply is split and moved to other topic use the
2202 //topic last post id.
2203 $last_read_post = wpforo_post( $last_read_id );
2204 if( $topicid == wpfval( $last_read_post, 'topicid' ) ) {
2205 $first_unread_id = 0;
2206 $jump_to_first_unread = apply_filters( 'wpforo_jump_to_first_unread', true );
2207 if( $jump_to_first_unread ) {
2208 //Get first unread postid
2209 $first_unread_id = WPF()->post->next_post( $last_read_id, $topicid );
2210 }
2211 //Decide to whether execute more SQLs and create direct URLs or not
2212 $direct_url = apply_filters( 'wpforo_build_direct_unread_post_url', false );
2213 //Create new URLs
2214 if( $first_unread_id ) {
2215 //Change URL to first unread post
2216 if( $direct_url ) {
2217 $url = wpforo_post( $first_unread_id, 'url' );
2218 } else {
2219 $url = ( strpos( (string) $url, '#' ) !== false ) ? preg_replace(
2220 '|#.+$|',
2221 '#post-' . intval( $first_unread_id ),
2222 (string) $url
2223 ) : $url . '#post-' . intval( $first_unread_id );
2224 }
2225 } else {
2226 //Change URL to last read post
2227 if( $direct_url ) {
2228 $url = wpforo_post( $last_read_id, 'url' );
2229 } else {
2230 $url = ( strpos( (string) $url, '#' ) !== false ) ? preg_replace(
2231 '|#.+$|',
2232 '#post-' . intval( $last_read_id ),
2233 (string) $url
2234 ) : $url . '#post-' . intval( $last_read_id );
2235 }
2236 }
2237 } elseif( $last_postid = wpforo_topic( $topicid, 'last_post' ) ) {
2238 $url = wpforo_post( $last_postid, 'url' );
2239 }
2240 }
2241 } else {
2242 $direct_topic_url = apply_filters( 'wpforo_direct_topic_url', true );
2243 if( $direct_topic_url ) {
2244 $url = preg_replace( '|#post-\d+|i', '', (string) $url );
2245 $url = WPF()->strip_url_paged_var( $url );
2246 }
2247 }
2248 if( ! $echo ) return esc_url( (string) $url );
2249 echo esc_url( (string) $url );
2250 }
2251
2252 function wpforo_unread_button( $topicid = 0, $url = '', $echo = true, $postid = 0 ) {
2253 $button = '';
2254 if( ! $postid && $topicid && $url && WPF()->current_userid && wpforo_setting( 'logging', 'goto_unread_button' ) ) {
2255 $unread = wpforo_unread( $topicid, 'topic', false );
2256 if( $unread ) {
2257 $button_link = apply_filters( 'wpforo_jump_to_unread_button_link', false );
2258 $button_text = str_replace( [ '{', '}' ], '', wpforo_phrase( '{new}', false ) );
2259 if( wpforo_setting( 'logging', 'goto_unread' ) ) {
2260 if( $button_link ) {
2261 $url = wpforo_unread_url( $topicid, $url, false, true );
2262 }
2263 } else {
2264 $url = wpforo_unread_url( $topicid, $url, false, true );
2265 $button_link = true;
2266 }
2267 $button = ( $button_link ) ? '<a href="' . $url . '" class="wpf-new-button" title="' . esc_attr(
2268 wpforo_phrase( 'Go to first unread post', false )
2269 ) . '">' . $button_text . '</a>' : '<span class="wpf-new-button">' . $button_text . '</span>';
2270 }
2271 } elseif( $postid && $topicid && WPF()->current_userid ) {
2272 $unread = wpforo_unread( $topicid, 'post', false, $postid );
2273 if( $unread ) {
2274 $button_text = str_replace( [ '{', '}' ], '', wpforo_phrase( '{new}', false ) );
2275 $button = '<span class="wpf-new-button">' . $button_text . '</span>';
2276 }
2277 }
2278 if( ! $echo ) return $button;
2279 echo $button;
2280 }
2281
2282
2283 /**
2284 * @param array|string $forum
2285 * @param string $before
2286 * @param string $after
2287 * @param bool $echo
2288 *
2289 * @return string
2290 */
2291 function wpforo_forum_title( $forum, $before = '', $after = '', $echo = true ) {
2292 $title = is_array( $forum ) ? (string) wpfval( $forum, 'title' ) : $forum;
2293 $title = esc_html( $title );
2294 $title = apply_filters( 'wpforo_forum_title', $title, $forum );
2295 if( $title ) $title = $before . $title . $after;
2296 if( $echo ) echo $title;
2297
2298 return $title;
2299 }
2300
2301 /**
2302 * @param array|string $forum
2303 * @param string $before
2304 * @param string $after
2305 * @param bool $echo
2306 *
2307 * @return string
2308 */
2309 function wpforo_forum_description( $forum, $before = '', $after = '', $echo = true ) {
2310 $description = is_array( $forum ) ? (string) wpfval( $forum, 'description' ) : $forum;
2311 $description = apply_filters( 'wpforo_forum_description', $description, $forum );
2312 $description = trim( $description );
2313 if( $description ) $description = $before . $description . $after;
2314 if( $echo ) echo $description;
2315
2316 return $description;
2317 }
2318
2319 function wpforo_admin_cpanel() {
2320 if( WPF()->current_object['template'] === 'forum' && wpforo_setting(
2321 'components',
2322 'admin_cp'
2323 ) && wpforo_current_user_is( 'admin' ) ) {
2324 $toggle = get_user_meta( WPF()->current_userid, 'wpf-acp-toggle', true );
2325 if( ! $toggle || $toggle === 'open' ) {
2326 $display = 'block';
2327 $title = wpforo_phrase( 'Close', false );
2328 $icon = '<i class="fas fa-minus-square"></i>';
2329 } else {
2330 $display = 'none';
2331 $title = wpforo_phrase( 'Open', false );
2332 $icon = '<i class="fas fa-plus-square"></i>';
2333 }
2334 ?>
2335 <div class="wpf-admincp">
2336 <div class="wpf-acp-header">
2337 <div class="wpf-acp-title"><i class="fas fa-cog"></i> <?php wpforo_phrase( 'Admin Control Panel' ) ?>
2338 </div>
2339 <div class="wpf-acp-toggle" wpf-tooltip="<?php echo esc_attr( $title ) ?>"><?php echo $icon ?></div>
2340 </div>
2341 <div class="wpf-acp-body" style="display: <?php echo $display ?>;">
2342 <div class="wpf-acp-content">
2343 <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) ); ?>"
2344 class="wpf-button-secondary"><i class="fas fa-plus"></i>&nbsp; <?php wpforo_phrase(
2345 'Add New Category or Forum'
2346 ) ?></a>
2347 <p class="wpf-acp-forum-info">
2348 <?php $layouts = '(<a href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-layouts/extended-layout/" target="_blank">Extended</a>,
2349 <a href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-layouts/simplified-layout/" target="_blank">Simplified</a>,
2350 <a href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-layouts/qa-layout/" target="_blank">Q&A</a>,
2351 <a href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-layouts/threaded-layout/" target="_blank">Threaded</a>)';
2352 $layout = '<a href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-layouts/" target="_blank">' . wpforo_phrase(
2353 'the layout you want',
2354 false,
2355 'lower'
2356 ) . '</a>';
2357 ?>
2358 <?php echo sprintf(
2359 wpforo_phrase(
2360 '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.',
2361 false
2362 ),
2363 $layouts,
2364 $layout
2365 ); ?>
2366 </p>
2367 </div>
2368 <div class="wpf-acp-footer">
2369 <a href="<?php echo admin_url(
2370 'admin.php?page=' . wpforo_prefix_slug( 'settings' ) . '&wpf_tab=styles'
2371 ); ?>" class="wpf-button-secondary"><i class="fas fa-paint-brush"></i> <?php wpforo_phrase(
2372 'Change Color Style'
2373 ) ?></a>
2374 <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'moderations' ) ); ?>"
2375 class="wpf-button-secondary"><i class="fas fa-tasks"></i> <?php wpforo_phrase(
2376 'Post Moderation'
2377 ) ?></a>
2378 <a href="<?php echo admin_url(
2379 'admin.php?page=' . wpforo_prefix_slug( 'settings' ) . '&wpf_tab=antispam'
2380 ); ?>"
2381 class="wpf-button-secondary"><i class="fas fa-shield-alt"></i> <?php wpforo_phrase(
2382 'Antispam'
2383 ) ?></a>
2384 <?php $menu = wp_get_nav_menu_object( 'wpforo-navigation' ); ?>
2385 <?php if( ! empty( $menu ) && isset( $menu->term_id ) ): ?>
2386 <a href="<?php echo admin_url( 'nav-menus.php?action=edit&menu=' . $menu->term_id ); ?>"
2387 class="wpf-button-secondary"><i class="fas fa-bars"></i> <?php wpforo_phrase(
2388 'Forum Menu'
2389 ) ?></a>
2390 <?php endif; ?>
2391 <a href="<?php echo admin_url( 'widgets.php' ); ?>" class="wpf-button-secondary"><i
2392 class="far fa-window-restore"></i> <?php wpforo_phrase( 'Forum Widgets' ) ?></a>
2393 <a href="<?php echo wp_nonce_url(
2394 wpforo_home_url( '?wpfaction=reset_all_caches' ),
2395 'wpforo_reset_cache'
2396 ) ?>" class="wpf-button-secondary" style="color: #e22d00 !important;"><i
2397 class="fas fa-times"></i> <?php wpforo_phrase( 'Delete Forum Cache' ) ?></a>
2398 </div>
2399 </div>
2400 </div>
2401 <?php
2402 }
2403 }
2404
2405 /**
2406 * show member template
2407 */
2408 function wpforo_member_template() {
2409 WPF()->tpl->member_template();
2410 }
2411
2412 /**
2413 * show member menu
2414 */
2415 function wpforo_member_tabs() {
2416 WPF()->tpl->member_menu();
2417 }
2418
2419 /**
2420 * get current object user or empty array
2421 *
2422 * @return array
2423 */
2424 function wpforo_get_current_object_user() {
2425 return WPF()->current_object['user'];
2426 }
2427
2428 /**
2429 * @param array|string $template
2430 *
2431 * @return bool
2432 */
2433 function wpforo_is_member_template( $template = null ) {
2434 if( $template = WPF()->tpl->get_template( $template ) ) {
2435 return wpfkey(
2436 WPF()->tpl->member_templates,
2437 $template['key']
2438 );
2439 }
2440
2441 return false;
2442 }
2443
2444 /**
2445 * show wpforo template
2446 *
2447 * @param array|string $template
2448 */
2449 function wpforo_template( $template = null ) {
2450 $template = apply_filters( 'wpforo_template', $template );
2451 if( ! $template ) $template = WPF()->current_object['template'] ? WPF()->current_object['template'] : '404';
2452 if( WPF()->tpl->can_view_template( $template, WPF()->current_object['user'] ) ) {
2453 if( $path = WPF()->tpl->get_template_path( $template ) ) {
2454 if( file_exists( $path ) ) include( $path );
2455 } else {
2456 WPF()->tpl->show_template( $template );
2457 }
2458 } else {
2459 WPF()->tpl->show_msg( wpforo_phrase( 'You do not have permission to view this page', false ) );
2460 }
2461 }
2462
2463 /**
2464 * @param string $html
2465 *
2466 * @return string
2467 */
2468 function wpforo_apply_ucf_shortcode( $html ) {
2469 return preg_replace_callback( '#\[wpfucf\s([^\[\]]+?)]#iu', function( $shortcode ) {
2470 if( preg_match( '#(?:^|\s)field=[\'\"]([^\'\"]+?)[\'\"]#iu', $shortcode[1], $field ) ) {
2471 if( $field_key = trim( $field[1] ) ) {
2472 $userid = 0;
2473 if( preg_match(
2474 '#(?:^|\s)id=[\'\"]([^\'\"]+?)[\'\"]#iu',
2475 $shortcode[1],
2476 $id
2477 ) ) {
2478 $userid = wpforo_bigintval( $id[1] );
2479 }
2480 if( ! $userid ) {
2481 $userid = WPF()->current_object['userid'] ? WPF()->current_object['userid'] : WPF()->current_userid;
2482 }
2483 if( $f = WPF()->member->get_field( $field_key ) ) {
2484 if( wpfval( $f, 'type' ) === 'html' && ( $html = trim( (string) wpfval( $f, 'html' ) ) ) ) {
2485 return (string) WPF()->form->field_html( $f );
2486 } elseif( WPF()->form->can_view( $f ) && WPF()->form->can_show_value( $f ) ) {
2487 $f['value'] = wpforo_member( $userid, $field_key );
2488 $f = WPF()->form->prepare_values( WPF()->form->esc_field( $f ), $userid );
2489
2490 return $f['value'];
2491 }
2492 }
2493 }
2494 }
2495
2496 return ''; // if that field has not found
2497 }, (string) $html );
2498 }
2499
2500 /**
2501 * @param array|string $template
2502 * @param array|int $member
2503 * @param string $default
2504 *
2505 * @return string
2506 */
2507 function wpforo_member_url( $template = null, $member = null, $default = '' ) {
2508 $member_url = $default;
2509 $template = WPF()->tpl->get_template( $template );
2510 if( $template && wpforo_is_member_template( $template ) ) {
2511 if( ! $member ) $member = WPF()->current_object['user'] ? WPF()->current_object['user'] : WPF()->current_user;
2512 $profile_url = WPF()->member->get_profile_url( $member, $template['key'] );
2513 if( $profile_url ) $member_url = $profile_url;
2514 }
2515
2516 return $member_url;
2517 }
2518
2519 function wpforo_mark_all_read_link() {
2520 if( ! wpforo_is_bot() ) : ?>
2521 <a class="wpf-mark-all-read"
2522 href="<?php echo wp_nonce_url( '?foro=allread', 'wpforo_mark_all_read', 'foro_n' ); ?>" rel="nofollow">
2523 <i class="fa-regular fa-circle-check"></i>
2524 <span><?php wpforo_phrase( 'Mark all read' ) ?></span>
2525 </a>
2526 <?php
2527 endif;
2528 }
2529
2530 function wpforo_login_link() {
2531 if( ! wpforo_is_bot() ) : ?>
2532 <a href="<?php echo wpforo_login_url(); ?>"><i class="fas fa-sign-in-alt"></i> <?php wpforo_phrase(
2533 'Login'
2534 ); ?></a>
2535 <?php
2536 endif;
2537 }
2538
2539 function wpforo_register_link() {
2540 if( ! wpforo_is_bot() ) : ?>
2541 <a href="<?php echo wpforo_register_url(); ?>"><i class="fas fa-user-plus"></i> <?php wpforo_phrase(
2542 'Create Account'
2543 ); ?></a>
2544 <?php
2545 endif;
2546 }
2547
2548 function wpforo_topic_form_extra( $forumid, $values ) { ?>
2549 <div class="wpf-extra-fields">
2550 <?php do_action( 'wpforo_topic_form_extra_fields_before', $forumid, $values ) ?>
2551 <div class="wpf-main-fields">
2552 <?php do_action( 'wpforo_topic_form_buttons_hook', $forumid, $values ); ?>
2553 </div>
2554 <?php do_action( 'wpforo_topic_form_extra_fields_after', $forumid, $values ) ?>
2555 </div>
2556 <?php
2557 }
2558
2559 function wpforo_reply_form_extra( $topic, $values ) { ?>
2560 <div class="wpf-extra-fields">
2561 <?php do_action( 'wpforo_reply_form_extra_fields_before', $topic, $values ) ?>
2562 <?php do_action( 'wpforo_reply_form_buttons_hook', $topic, $values ); ?>
2563 <?php do_action( 'wpforo_reply_form_extra_fields_after', $topic, $values ) ?>
2564 </div>
2565 <?php
2566 }
2567
2568 function wpforo_post_search_form( $values ) {
2569 WPF()->tpl->post_search_form( $values );
2570 }
2571
2572 function wpforo_member_search_form() {
2573 WPF()->tpl->member_search_form();
2574 }
2575
2576 function wpforo_member_buttons( $member ) {
2577 WPF()->tpl->member_buttons( $member );
2578 }
2579
2580 function wpforo_sanitize_search_body( $body, $needle, $post = [] ) {
2581 $body = wpforo_content_filter( $body, $post );
2582 $body = wpforo_text( $body, 0, false );
2583 $words = explode( ' ', trim( (string) $needle ) );
2584 if( ! empty( $words ) ) {
2585 $body_len = apply_filters( 'wpforo_search_results_body_length', 564 );
2586 $pos = mb_stripos( $body, " " . trim( $words[0] ), 0, get_option( 'blog_charset' ) );
2587 if( strlen( (string) $body ) > $body_len && $pos !== false ) {
2588 if( $pos > ( $body_len / 2 ) ) {
2589 $bef_body = "... ";
2590 $start = mb_stripos( $body, " ", ( $body_len / 2 ), get_option( 'blog_charset' ) );
2591 } else {
2592 $bef_body = "";
2593 $start = 0;
2594 }
2595 if( ( mb_strlen( (string) $body, get_option( 'blog_charset' ) ) - $start ) > $body_len ) {
2596 $aft_body = " ...";
2597 } else {
2598 $aft_body = "";
2599 }
2600 $body = $bef_body . mb_substr(
2601 (string) $body,
2602 $start,
2603 $body_len,
2604 get_option( 'blog_charset' )
2605 ) . $aft_body;
2606 }
2607 foreach( $words as $word ) {
2608 $word = trim( (string) $word );
2609 $body = preg_replace(
2610 '#(?:^|\s+)' . preg_quote( esc_html( $word ) ) . '#iu',
2611 ' <span class="wpf-sword wpfcl-b">' . esc_html( $word ) . '</span>',
2612 (string) $body
2613 );
2614 }
2615 }
2616
2617 return $body;
2618 }
2619
2620 function wpforo_topic_starter( $topic, $post, $type = 'full' ) {
2621
2622 $starter = false;
2623
2624 $topic_user = (int) wpfval( $topic, 'userid' );
2625 $post_user = (int) wpfval( $post, 'userid' );
2626 $topic_guest = wpfval( $topic, 'email' );
2627 $post_guest = wpfval( $post, 'email' );
2628
2629 if( $topic_user && $topic_user === $post_user ) {
2630 $starter = true;
2631 } elseif( $topic_guest && $topic_guest === $post_guest ) {
2632 $starter = true;
2633 }
2634
2635 if( $starter ) {
2636 if( $type === 'full' ) {
2637 echo '<span class="wpf-post-starter"><i class="fas fa-feather-alt"></i> ' . wpforo_phrase(
2638 'Topic starter',
2639 false
2640 ) . '</span>';
2641 } elseif( $type === 'icon' ) {
2642 echo '<span class="wpf-post-starter" wpf-tooltip="' . wpforo_phrase(
2643 'Topic starter',
2644 false
2645 ) . '"><i class="fas fa-feather-alt"></i></span>';
2646 } elseif( $type === 'label' ) {
2647 echo '<span class="wpf-post-starter">' . wpforo_phrase( 'Topic starter', false ) . '</span>';
2648 }
2649 }
2650 }
2651
2652 function wpforo_get_not_replied_topicids() {
2653 $sql = "SELECT `topicid`, COUNT(DISTINCT userid) AS userid_count FROM `" . WPF()->tables->posts . "` GROUP BY `topicid` HAVING userid_count = 1";
2654
2655 return (array) WPF()->db->get_col( $sql, 0 );
2656 }
2657
2658 /**
2659 * @param array $topic
2660 * @param string $url
2661 * @param string $structure
2662 * {i} - topic icon (empty, one reply, replied)
2663 * {is} - topic icon and status icons (+ sticky, closed, private...)
2664 * {t} - topic title
2665 * {tc} - topic title cut by x number of chars
2666 * {au} - link opening tag, link goes to first unread post
2667 * {at} - link opening tag, link goes to the topic url
2668 * {af} - link opening tag, link goes to the first post
2669 * {a} - link opening tag, link goes to the provided url
2670 * {/a} - link closing tag, this is required
2671 * {v} - displays number of users currently viewing the topic
2672 * {n} - displays [new] info when a new unread post has been posted in the topic
2673 *
2674 * Example {a}{t}{/a} - this is the topic title as a link going to the first post.
2675 *
2676 * @param bool $echo
2677 * @param string $class
2678 * @param int $length
2679 *
2680 *
2681 *
2682 *
2683 * @return mixed|string|void|null
2684 */
2685 function wpforo_topic_title( $topic, $url, $structure = '{i}{au}{t}{/a}{n}{v}', $echo = true, $class = '', $length = 70 ) {
2686 $html = __( 'No Title', 'wpforo' );
2687 if( $title = trim( esc_html( (string) wpfval( $topic, 'title' ) ) ) ) {
2688 $structure = apply_filters( 'wpforo_topic_title_structure', $structure, $topic );
2689 $class = ( $class ) ? ' class="' . esc_attr( $class ) . '"' : '';
2690 $a = '<a href="' . esc_url( (string) $url ) . '"' . $class . ' title="' . esc_attr( $topic['title'] ) . '">';
2691 $topic['url'] = $url;
2692
2693 // For better performance, make sure the component in demand before getting it.
2694 $title_cut = ( strpos( (string) $structure, '{tc}' ) !== false ) ? esc_html( wpforo_text( $topic['title'], $length, false ) ) : '';
2695 $icon_all = ( strpos( (string) $structure, '{i}' ) !== false ) ? wpforo_topic_icon( $topic, 'all', true, false ) : '';
2696 $icon_status = ( strpos( (string) $structure, '{is}' ) !== false ) ? wpforo_topic_icon( $topic, 'status', true, false ) : '';
2697 $viewing = ( strpos( (string) $structure, '{v}' ) !== false ) ? wpforo_viewing( $topic, false ) : '';
2698 $new = ( strpos( (string) $structure, '{n}' ) !== false ) ? wpforo_unread_button( $topic['topicid'], $url, false ) : '';
2699 $au = ( strpos( (string) $structure, '{au}' ) !== false ) ? '<a href="' . wpforo_unread_url( $topic['topicid'], $url, false ) . '" title="' . esc_attr(
2700 $topic['title']
2701 ) . '"' . $class . '>' : '';
2702 $at = ( strpos( (string) $structure, '{at}' ) !== false ) ? '<a href="' . wpforo_topic( $topic['topicid'], 'url' ) . '" title="' . esc_attr(
2703 $topic['title']
2704 ) . '"' . $class . '>' : '';
2705 $af = ( strpos( (string) $structure, '{af}' ) !== false ) ? '<a href="' . wpforo_post( $topic['first_postid'], 'url' ) . '" title="' . esc_attr(
2706 $topic['title']
2707 ) . '"' . $class . '>' : '';
2708
2709 $components = [
2710 '{a}' => $a,
2711 '{au}' => $au,
2712 '{at}' => $at,
2713 '{af}' => $af,
2714 '{/a}' => '</a>',
2715 '{i}' => $icon_all,
2716 '{is}' => $icon_status,
2717 '{t}' => $title,
2718 '{tc}' => $title_cut,
2719 '{v}' => ' ' . $viewing,
2720 '{n}' => ' ' . $new,
2721 '{p}' => '',
2722 '{pt}' => '',
2723 ];
2724 $components = apply_filters( 'wpforo_topic_title_components', $components, $topic );
2725 $html = strtr( $structure, $components );
2726 $html = apply_filters( 'wpforo_topic_title_html', $html, $topic, $structure, $components );
2727 }
2728 if( ! $echo ) return $html;
2729 echo $html;
2730 }
2731
2732 function wpforo_single_title( $type = 'post', $item = [], $before = '', $after = '', $echo = true ) {
2733 $title = '';
2734 if( $type === 'post' && wpfval( $item, 'title' ) ) {
2735 $icon_title = WPF()->tpl->icon( 'topic', $item, false, 'title' );
2736 if( $icon_title ) {
2737 $title .= '<span class="wpf-status-title">[' . esc_html( $icon_title ) . ']</span> ';
2738 }
2739 $title = apply_filters( "wpforo_single_{$type}_pre_title", $title, $item );
2740 $title .= esc_html( wpforo_text( $item['title'], 0, false ) );
2741 }
2742 $title = apply_filters( "wpforo_single_{$type}_title", $title );
2743 if( $title ) $title = $before . $title . $after;
2744 if( $echo ) echo $title;
2745
2746 return $title;
2747 }
2748
2749 function wpforo_schema( $forum, $topic, $post, $type = '' ) {
2750 $schema = '';
2751
2752 if( apply_filters( "wpforo_schema", true ) ) {
2753
2754 if( ! $type ) {
2755 $type = 'DiscussionForumPosting';
2756 if( 3 === (int) wpfval( $forum, 'layout' ) ) {
2757 $type = 'QAPage';
2758 }
2759 }
2760 if( $type === 'QAPage' && wpfval( $topic, 'topicid' ) ) {
2761 $extended = apply_filters( "wpforo_schema_qa_extended_best_answer", true );
2762 $best_answer = WPF()->post->get_best_answer( $topic['topicid'], $extended );
2763 $suggested_answers_count = apply_filters( "wpforo_schema_qa_suggested_answers_count", 3 );
2764 $suggested_answers = WPF()->post->get_suggested_answers(
2765 $topic['topicid'],
2766 $suggested_answers_count
2767 );
2768 $schema .= '
2769 <script type="application/ld+json">
2770 {
2771 "@context": "https://schema.org",
2772 "@type": "QAPage",
2773 "mainEntity": {
2774 "@type": "Question",
2775 "name": "' . esc_attr( $post['title'] ) . '",
2776 "text": "' . esc_attr( sanitize_text_field( $post['body'] ) ) . '",
2777 "answerCount": ' . intval( $topic['answers'] ) . ',
2778 "upvoteCount": ' . intval( $post['votes'] ) . ',
2779 "dateCreated": "' . gmdate( 'Y-m-d', strtotime( $post['created'] . ' GMT' ) ) . 'T' . gmdate(
2780 'H:i',
2781 strtotime( $post['created'] . ' GMT' )
2782 ) . 'Z",
2783 "author": {
2784 "@type": "Person",
2785 "name": "' . wpforo_member( $post['created'], 'display_name' ) . '"
2786 }';
2787 if( ! empty( $best_answer ) ) {
2788 $schema .= ',
2789 "acceptedAnswer": {
2790 "@type": "Answer",
2791 "text": "' . esc_attr( sanitize_text_field( $best_answer['body'] ) ) . '",
2792 "dateCreated": "' . gmdate(
2793 'Y-m-d',
2794 strtotime( $best_answer['created'] . ' GMT' )
2795 ) . 'T' . gmdate(
2796 'H:i',
2797 strtotime( $best_answer['created'] . ' GMT' )
2798 ) . 'Z",
2799 "upvoteCount": ' . intval( $best_answer['votes'] ) . ',
2800 "url": "' . wpforo_post( $best_answer['postid'], 'url' ) . '",
2801 "author": {
2802 "@type": "Person",
2803 "name": "' . wpforo_member( $best_answer['userid'], 'display_name' ) . '"
2804 }
2805 }';
2806 }
2807 if( ! empty( $suggested_answers ) ) {
2808 $schema .= ',
2809 "suggestedAnswer": [';
2810 $answers = '';
2811 foreach( $suggested_answers as $key => $suggested_answer ) {
2812 if( wpfval( $best_answer, 'postid' ) === wpfval( $suggested_answer, 'postid' ) ) {
2813 continue;
2814 }
2815 $answers .= '
2816 {
2817 "@type": "Answer",
2818 "text": "' . esc_attr( sanitize_text_field( $suggested_answer['body'] ) ) . '",
2819 "dateCreated": "' . gmdate(
2820 'Y-m-d',
2821 strtotime( $suggested_answer['created'] . ' GMT' )
2822 ) . 'T' . gmdate(
2823 'H:i',
2824 strtotime( $suggested_answer['created'] . ' GMT' )
2825 ) . 'Z",
2826 "upvoteCount": ' . intval( $suggested_answer['votes'] ) . ',
2827 "url": "' . wpforo_post( $suggested_answer['postid'], 'url' ) . '",
2828 "author": {
2829 "@type": "Person",
2830 "name": "' . wpforo_member( $suggested_answer['userid'], 'display_name' ) . '"
2831 }
2832 },';
2833 }
2834 $schema .= trim( $answers, ',' ) . '
2835 ]';
2836 }
2837 $schema .= '
2838 }
2839 }
2840 </script>';
2841 } elseif( wpfval( $topic, 'topicid' ) ) {
2842 $topic_posts = '';
2843 $paged = WPF()->current_object['paged'];
2844 $posts = WPF()->current_object['posts'];
2845 $topic_post = wpforo_post( $topic['first_postid'] );
2846 if( $paged > 1 || ( $paged === 1 && count( $posts ) > 1 ) ) {
2847 $topic_posts .= '"comment": [';
2848 foreach( $posts as $key => $post ) {
2849 if( $paged === 1 && $key == 0 ) continue;
2850 $post_text = wpforo_text( stripslashes( sanitize_text_field( $post['body'] ) ), 5000, false );
2851 $post_author = wpforo_generate_scheme_author_object( $post['userid'] );
2852 $post_images = wpforo_generate_scheme_image_object( $post['body'] );
2853 if( ! $post_text && ! $post_images ) continue;
2854 $topic_posts .= '{
2855 "@type": "Comment",
2856 "text": "' . esc_attr( $post_text ) . '",' . $post_images . '
2857 "datePublished": "' . gmdate( 'Y-m-d\TH:i:s\Z', strtotime( $post['created'] ) ) . '"' . $post_author . '
2858 },';
2859 }
2860 $topic_posts = ',' . trim( $topic_posts, ',' ) . ']';
2861 }
2862
2863 $topic_author = wpforo_generate_scheme_author_object( $topic['userid'] );
2864 $topic_images = wpforo_generate_scheme_image_object( $topic_post['body'] );
2865 $topic_text = wpforo_text( stripslashes( sanitize_text_field( $topic_post['body'] ) ), 5000, false );
2866 if( $topic_text || $topic_images ) {
2867 $schema .= '
2868 <script type="application/ld+json">
2869 {
2870 "@context": "https://schema.org",
2871 "@type": "DiscussionForumPosting",
2872 "mainEntityOfPage": "' . esc_url_raw( $topic['url'] ) . '",
2873 "headline": "' . esc_attr( $topic_post['title'] ) . '",
2874 "text": "' . esc_attr( $topic_text ) . '",' . $topic_images . '
2875 "url": "' . esc_url_raw( $topic['url'] ) . '"' . $topic_author . ',
2876 "datePublished": "' . gmdate( 'Y-m-d\TH:i:s\Z', strtotime( $topic_post['created'] ) ) . '",
2877 "interactionStatistic": {
2878 "@type": "InteractionCounter",
2879 "interactionType": "https://schema.org/LikeAction",
2880 "userInteractionCount": ' . intval( $topic['posts'] ) . '
2881 }
2882 ' . $topic_posts . '
2883 }
2884 </script>';
2885 }
2886 }
2887 }
2888
2889 return $schema;
2890 }
2891
2892 function wpforo_template_profile_board_panel() {
2893 echo WPF()->tpl->profile_board_panel();
2894 }
2895
2896 function wpforo_template_profile_activity_panel() {
2897 echo WPF()->tpl->profile_activity_panel();
2898 }
2899
2900 function wpforo_template_profile_favored_panel() {
2901 echo WPF()->tpl->profile_favored_panel();
2902 }
2903
2904 function wpforo_profile_head_attrs( $user ) {
2905 printf(
2906 'style="background-image:url(\'%1$s\')"',
2907 esc_url(
2908 trim( (string) wpfval( $user, 'cover' ) ) ? trim( (string) wpfval( $user, 'cover' ) ) . '?lm=' . time() : wpforo_setting( 'profiles', 'default_cover' )
2909 )
2910 );
2911 }
2912
2913 function wpforo_template_profile_action_buttons( $user ) {
2914 do_action( 'wpforo_template_profile_action_buttons_left', $user ); ?>
2915 <div class="wpf-grow"></div>
2916 <?php do_action( 'wpforo_template_profile_action_buttons_right', $user );
2917 echo WPF()->tpl->get_member_actions_html( $user );
2918 }
2919
2920
2921 function wpforo_get_users_count_for_topic( $topicid = 0 ) {
2922 if( ! $topicid ) $topicid = WPF()->current_object['topicid'];
2923
2924 return WPF()->post->get_users_count_for_topic( $topicid );
2925 }
2926
2927 function wpforo_get_likes_for_topic( $topicid ) {
2928 return WPF()->reaction->get_likes_for_topic( $topicid );
2929 }
2930
2931 function wpforo_display_header( $template = '' ) {
2932 if( ! $template ) $template = WPF()->current_object['template'];
2933
2934 return ! wpforo_is_member_template( $template ) || wpforo_setting( 'profiles', 'profile_header' );
2935 }
2936
2937 function wpforo_display_footer( $template = '' ) {
2938 if( ! $template ) $template = WPF()->current_object['template'];
2939
2940 return ( ( wpforo_is_member_template( $template ) && wpforo_setting(
2941 'profiles',
2942 'profile_footer'
2943 ) ) || ( ! wpforo_is_member_template( $template ) && wpforo_setting( 'components', 'footer' ) ) );
2944 }
2945
2946 function wpforo_forum_list_need_add_topic_button( $forumid ) {
2947 switch( WPF()->forum->get_layout( $forumid ) ) {
2948 case 2:
2949 $need_button = wpforo_setting( 'forums', 'layout_simplified_add_topic_button' );
2950 break;
2951 case 3:
2952 $need_button = wpforo_setting( 'forums', 'layout_qa_add_topic_button' );
2953 break;
2954 case 4:
2955 $need_button = wpforo_setting( 'forums', 'layout_threaded_add_topic_button' );
2956 break;
2957 default:
2958 $need_button = true;
2959 break;
2960 }
2961
2962 return $need_button;
2963 }
2964
2965 function wpforo_get_forum_cover_styles( $forum ) {
2966 $r = [
2967 'cover' => '',
2968 'title' => '',
2969 'info' => '',
2970 'button' => '',
2971 'blur' => '',
2972 ];
2973
2974 if( $forum['cover_url'] ) {
2975 $border_radius = ( (int) $forum['layout'] !== 4 ) ? 'border-radius: 4px;' : ( ! is_rtl() ? 'border-radius: 4px 0 0 4px;' : 'border-radius: 0 4px 4px 0;' );
2976 $r['cover'] = sprintf(
2977 'style="background-image:url(\'%1$s\'); background-position: center; background-size: cover; height:170px; padding:0;"',
2978 esc_attr( $forum['cover_url'] )
2979 );
2980 $r['blur'] = 'style="background: rgba(255, 255, 255, 0.7); color:#000"';
2981 $r['title'] = 'style="display: inline-block; line-height: 20px; ' . $border_radius . ' color:#000"';
2982 $r['info'] = 'style="padding-bottom: 1px; padding-top:3px;"';
2983 $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;' ) . '"';
2984 }
2985
2986 return $r;
2987 }
2988
2989 function wpforo_post_search_url( $field_name, $field_value ) {
2990 return wpforo_home_url(
2991 '?' . http_build_query(
2992 [
2993 'wpfs' => '',
2994 'wpfin' => 'entire-posts',
2995 'wpfd' => '0',
2996 'wpfob' => 'date',
2997 'wpfo' => 'desc',
2998 'data' => [
2999 'text_field' => '',
3000 'uniqid' => '',
3001 $field_name => $field_value,
3002 ],
3003 ]
3004 )
3005 );
3006 }
3007
3008 function wpforo_post_search_link( $field_name, $field_value ) {
3009 return sprintf(
3010 '<a href="%1$s">%2$s</a>',
3011 wpforo_post_search_url( $field_name, $field_value ),
3012 $field_value
3013 );
3014 }
3015
3016 function wpforo_get_sum_of_topics_posts( int $forumid, bool $check_permission = true ): array {
3017 return WPF()->forum->get_sum_of_topics_posts( $forumid, $check_permission );
3018 }
3019
3020 function _wpforo_main_wrap_classes(): array {
3021 $classes = [];
3022 switch( wpforo_setting( 'components', 'sidebar_location' ) ) {
3023 case 'left':
3024 $classes[] = 'wpforo-left-sidebar-layout';
3025 break;
3026 case 'right':
3027 $classes[] = 'wpforo-right-sidebar-layout';
3028 break;
3029 }
3030
3031 return apply_filters( 'wpforo_main_wrap_classes', $classes );
3032 }
3033
3034 function wpforo_main_wrap_classes(): void {
3035 echo implode( ' ', _wpforo_main_wrap_classes() );
3036 }
3037
3038 function wpforo_get_recent_page_title(): string {
3039 $view = wpfval( WPF()->current_object['args'], 'view' );
3040 switch( $view ) {
3041 case 'unread':
3042 $page_title = wpforo_phrase( 'Unread Posts', false );
3043 break;
3044 case 'no-replies':
3045 $page_title = wpforo_phrase( 'Not Replied Topics', false );
3046 break;
3047 case 'solved':
3048 $page_title = wpforo_phrase( 'Solved Topics', false );
3049 break;
3050 case 'unsolved':
3051 $page_title = wpforo_phrase( 'Unsolved Topics', false );
3052 break;
3053 case 'closed':
3054 $page_title = wpforo_phrase( 'Closed Topics', false );
3055 break;
3056 case 'opened':
3057 $page_title = wpforo_phrase( 'Open Topics', false );
3058 break;
3059 case 'sticky':
3060 $page_title = wpforo_phrase( 'Sticky Topics', false );
3061 break;
3062 case 'private':
3063 $page_title = wpforo_phrase( 'Private Topics', false );
3064 break;
3065 case 'unapproved':
3066 $page_title = wpforo_phrase( 'Unapproved Posts', false );
3067 break;
3068 default:
3069 $page_title = wpforo_phrase( 'Recent Posts', false );
3070 break;
3071 }
3072
3073 return apply_filters( 'wpforo_recent_posts_page_title', $page_title, WPF()->current_object['args'], $view );
3074 }
3075
3076 function wpforo_get_rss_feed_links(): string {
3077 if( wpforo_setting( 'rss', 'feed' ) ) {
3078 return sprintf(
3079 ' <sep> &nbsp;|&nbsp;</sep>
3080 <span class="wpf-feed-forums">
3081 <a href="%1$s" title="%2$s" target="_blank">
3082 <span>%3$s</span> <i class="fas fa-rss wpfsx"></i>
3083 </a>
3084 </span>
3085 <sep> &nbsp;|&nbsp;</sep>
3086 <span class="wpf-feed-topics">
3087 <a href="%4$s" title="%5$s" target="_blank">
3088 <span>%6$s</span> <i class="fas fa-rss wpfsx"></i>
3089 </a>
3090 </span>',
3091 WPF()->feed->rss2_url( false, 'forum' ),
3092 wpforo_phrase( 'Forums RSS Feed', false ),
3093 wpforo_phrase( 'Forums', false ),
3094 WPF()->feed->rss2_url( false, 'topic' ),
3095 wpforo_phrase( 'Topics RSS Feed', false ),
3096 wpforo_phrase( 'Topics', false )
3097 );
3098 }
3099
3100 return '';
3101 }
3102
3103 function wpforo_rss_feed_links(): void {
3104 echo wpforo_get_rss_feed_links();
3105 }
3106
3107 function wpforo_get_recent_page_filter_selectbox(): string {
3108 $filters = [
3109 'recent' => wpforo_phrase( 'Recent Posts', false ),
3110 'unread' => wpforo_phrase( 'Unread Posts', false ),
3111 'no-replies' => wpforo_phrase( 'Not Replied Topics', false ),
3112 'solved' => wpforo_phrase( 'Solved Topics', false ),
3113 'unsolved' => wpforo_phrase( 'Unsolved Topics', false ),
3114 'closed' => wpforo_phrase( 'Closed Topics', false ),
3115 'opened' => wpforo_phrase( 'Open Topics', false ),
3116 'sticky' => wpforo_phrase( 'Sticky Topics', false ),
3117 ];
3118
3119 // Add admin/moderator-specific filters
3120 if( wpforo_current_user_is( 'admin' ) || wpforo_current_user_is( 'moderator' ) ) {
3121 $filters['private'] = wpforo_phrase( 'Private Topics', false );
3122 $filters['unapproved'] = wpforo_phrase( 'Unapproved Posts', false );
3123 }
3124
3125 $options_html = '';
3126 foreach( $filters as $key => $value ) {
3127 $options_html .= sprintf(
3128 '<option value="?view=%1$s%2$s" %3$s>%4$s</option>',
3129 $key !== 'recent' ? $key : '',
3130 ( wpfval( WPF()->current_object['args'], 'prefixid' ) ? sprintf( '&prefixid=%1$d', WPF()->current_object['args']['prefixid'] ) : '' ),
3131 wpfo_check( wpfval( WPF()->current_object['args'], 'view' ), $key, 'selected', false ),
3132 $value
3133 );
3134 }
3135
3136 return sprintf( '<label><select onchange="window.location.assign(this.value)">%1$s</select></label>', $options_html );
3137 }
3138
3139 /**
3140 * Get icon HTML for activity type
3141 * Reads from WPF()->activity->actions for single source of truth
3142 *
3143 * @param string $type Activity type
3144 * @return string Icon HTML (SVG)
3145 */
3146 function wpforo_activity_icon( $type ) {
3147 $actions = WPF()->activity->actions;
3148 $icon = isset( $actions[ $type ]['icon'] ) ? $actions[ $type ]['icon'] : '';
3149 if( ! $icon ) {
3150 $icon = isset( $actions['default']['icon'] ) ? $actions['default']['icon'] : '';
3151 }
3152
3153 return apply_filters( 'wpforo_activity_icon', $icon, $type );
3154 }
3155
3156 /**
3157 * Get label for activity type
3158 *
3159 * @param string $type Activity type
3160 * @return string Translated label
3161 */
3162 function wpforo_activity_label( $type ) {
3163 $labels = [
3164 'wpforo_topic' => wpforo_phrase( 'created the topic', false ),
3165 'wpforo_post' => wpforo_phrase( 'replied to', false ),
3166 'edit_topic' => wpforo_phrase( 'edited a topic', false ),
3167 'edit_post' => wpforo_phrase( 'edited a post', false ),
3168 'new_reply' => wpforo_phrase( 'replied to', false ),
3169 'new_like' => wpforo_phrase( 'liked', false ),
3170 'new_dislike' => wpforo_phrase( 'disliked', false ),
3171 'new_up_vote' => wpforo_phrase( 'voted up', false ),
3172 'new_down_vote' => wpforo_phrase( 'voted down', false ),
3173 'new_reaction' => wpforo_phrase( 'reacted to', false ),
3174 'new_mention' => wpforo_phrase( 'mentioned in', false ),
3175 'new_favorite' => wpforo_phrase( 'favorited', false ),
3176 'topic_solved' => wpforo_phrase( 'marked as solved', false ),
3177 'new_closed' => wpforo_phrase( 'closed the topic', false ),
3178 'default' => wpforo_phrase( 'activity', false ),
3179 ];
3180
3181 $labels = apply_filters( 'wpforo_activity_labels', $labels );
3182
3183 return wpfval( $labels, $type ) ?: $labels['default'];
3184 }
3185
3186 /**
3187 * Render detailed activity info HTML based on activity type
3188 *
3189 * @param array $activity Activity data
3190 * @param array $member User who performed the activity
3191 * @return string HTML for the activity info row
3192 */
3193 function wpforo_activity_info_html( $activity, $member ) {
3194 $type = $activity['type'];
3195 $html = '';
3196
3197 // Get post and topic data if available
3198 $post = [];
3199 $topic = [];
3200 $forum = [];
3201 $post_author = [];
3202
3203 if( $activity['itemid'] ) {
3204 // For wpforo_topic, topic_solved, and topic_closed, itemid is the topicid, not postid
3205 if( $type === 'wpforo_topic' || $type === 'topic_solved' || $type === 'topic_closed' ) {
3206 $topic = WPF()->topic->get_topic( $activity['itemid'] );
3207 if( $topic && ! empty( $topic['forumid'] ) ) {
3208 $forum = WPF()->forum->get_forum( $topic['forumid'] );
3209 }
3210 } else {
3211 $post = WPF()->post->get_post( $activity['itemid'] );
3212 if( $post && ! empty( $post['topicid'] ) ) {
3213 $topic = WPF()->topic->get_topic( $post['topicid'] );
3214 if( $topic && ! empty( $topic['forumid'] ) ) {
3215 $forum = WPF()->forum->get_forum( $topic['forumid'] );
3216 }
3217 // Get post author for reaction-type activities
3218 if( ! empty( $post['userid'] ) && $post['userid'] != $activity['userid'] ) {
3219 $post_author = wpforo_member( $post['userid'] );
3220 }
3221 }
3222 }
3223 }
3224
3225 // Build topic link
3226 $topic_title = '';
3227 $topic_link = '';
3228 if( $topic ) {
3229 $topic_title = esc_html( $topic['title'] );
3230 $topic_url = $activity['permalink'] ?: WPF()->topic->get_url( $topic['topicid'] );
3231 $topic_link = '<a href="' . esc_url( $topic_url ) . '" class="wpf-activity-topic-link">' . $topic_title . '</a>';
3232 }
3233
3234 // Build forum link
3235 $forum_link = '';
3236 if( $forum ) {
3237 $forum_title = esc_html( $forum['title'] );
3238 $forum_url = WPF()->forum->get_forum_url( $forum );
3239 $forum_link = '<a href="' . esc_url( $forum_url ) . '" class="wpf-activity-forum-link">' . $forum_title . '</a>';
3240 }
3241
3242 // Build post author link for reactions
3243 $author_link = '';
3244 if( $post_author && ! empty( $post_author['display_name'] ) ) {
3245 $author_link = '<a href="' . esc_url( wpforo_member_url( $post_author ) ) . '" class="wpf-activity-author-link">' . esc_html( $post_author['display_name'] ) . '</a>';
3246 }
3247
3248 // Build HTML based on activity type
3249 switch( $type ) {
3250 case 'wpforo_topic':
3251 // {User} created the topic {Topic Title} in {Forum Title}
3252 $html = sprintf(
3253 wpforo_phrase( 'created the topic %1$s in %2$s', false ),
3254 $topic_link,
3255 $forum_link
3256 );
3257 break;
3258
3259 case 'wpforo_post':
3260 case 'new_reply':
3261 // {User} replied to the topic {Topic Title}
3262 $html = sprintf(
3263 wpforo_phrase( 'replied to the topic %s', false ),
3264 $topic_link
3265 );
3266 break;
3267
3268 case 'new_like':
3269 case 'new_dislike':
3270 case 'new_up_vote':
3271 case 'new_down_vote':
3272 case 'new_reaction':
3273 // {User} reacted to {Author}'s post in the topic {Topic Title}
3274 if( $author_link ) {
3275 $html = sprintf(
3276 wpforo_phrase( 'reacted to %1$s\'s post in the topic %2$s', false ),
3277 $author_link,
3278 $topic_link
3279 );
3280 } else {
3281 $html = sprintf(
3282 wpforo_phrase( 'reacted to a post in the topic %s', false ),
3283 $topic_link
3284 );
3285 }
3286 break;
3287
3288 case 'new_favorite':
3289 // {User} favorited {Author}'s post in the topic {Topic Title}
3290 if( $author_link ) {
3291 $html = sprintf(
3292 wpforo_phrase( 'favorited %1$s\'s post in the topic %2$s', false ),
3293 $author_link,
3294 $topic_link
3295 );
3296 } else {
3297 $html = sprintf(
3298 wpforo_phrase( 'favorited a post in the topic %s', false ),
3299 $topic_link
3300 );
3301 }
3302 break;
3303
3304 case 'new_solved':
3305 case 'topic_solved':
3306 // {User} marked as solved the topic {Topic Title}
3307 $html = sprintf(
3308 wpforo_phrase( 'marked as solved the topic %s', false ),
3309 $topic_link
3310 );
3311 break;
3312
3313 case 'new_closed':
3314 case 'topic_closed':
3315 // {User} closed the topic {Topic Title}
3316 $html = sprintf(
3317 wpforo_phrase( 'closed the topic %s', false ),
3318 $topic_link
3319 );
3320 break;
3321
3322 case 'post_answer':
3323 // {User} marked as answer a post in the topic {Topic Title}
3324 $html = sprintf(
3325 wpforo_phrase( 'marked as answer a post in the topic %s', false ),
3326 $topic_link
3327 );
3328 break;
3329
3330 case 'new_mention':
3331 // {User} mentioned you in {Topic Title}
3332 $html = sprintf(
3333 wpforo_phrase( 'mentioned you in %s', false ),
3334 $topic_link
3335 );
3336 break;
3337
3338 case 'edit_topic':
3339 case 'edit_post':
3340 // {User} edited a post in {Topic Title}
3341 $html = sprintf(
3342 wpforo_phrase( 'edited a post in %s', false ),
3343 $topic_link
3344 );
3345 break;
3346
3347 default:
3348 // Fallback to simple label
3349 $label = wpforo_activity_label( $type );
3350 $html = $label . ( $topic_link ? ' ' . $topic_link : '' );
3351 break;
3352 }
3353
3354 return apply_filters( 'wpforo_activity_info_html', $html, $activity, $member );
3355 }
3356