PluginProbe
bbPress / trunk
bbPress vtrunk
2.6.19 2.6.18 2.6.17 trunk 2.0 2.0-beta-1 2.0-beta-2b 2.0-beta-3 2.0-beta-3b 2.0-rc-2 2.0-rc-3 2.0-rc-4 2.0-rc-5 2.0.1 2.0.2 2.0.3 2.1 2.1-beta-1 2.1-rc1 2.1-rc2 2.1-rc3 2.1-rc4 2.1.1 2.1.2 2.1.3 All 74 releases
bbpress / includes / extend / buddypress / activity.php

activity.php in bbPress trunk, at includes/extend/buddypress/activity.php

918 lines 26.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * bbPress BuddyPress Activity Class.
5 *
6 * @package bbPress
7 * @subpackage BuddyPress
8 */
9
10 // Exit if accessed directly
11 defined( 'ABSPATH' ) || exit;
12
13 if ( ! class_exists( 'BBP_BuddyPress_Activity' ) ) :
14 /**
15 * Loads BuddyPress Activity extension.
16 *
17 * @since 2.2.0 bbPress (r4394)
18 *
19 * @package bbPress
20 * @subpackage BuddyPress
21 */
22 class BBP_BuddyPress_Activity {
23
24 /** Variables *************************************************************/
25
26 /**
27 * The name of the BuddyPress component, used in activity streams.
28 *
29 * @var string
30 */
31 private $component = '';
32
33 /**
34 * Forum Create Activity Action.
35 *
36 * @var string
37 */
38 private $forum_create = '';
39
40 /**
41 * Topic Create Activity Action.
42 *
43 * @var string
44 */
45 private $topic_create = '';
46
47 /**
48 * Topic Close Activity Action.
49 *
50 * @var string
51 */
52 private $topic_close = '';
53
54 /**
55 * Topic Edit Activity Action.
56 *
57 * @var string
58 */
59 private $topic_edit = '';
60
61 /**
62 * Topic Open Activity Action.
63 *
64 * @var string
65 */
66 private $topic_open = '';
67
68 /**
69 * Reply Create Activity Action.
70 *
71 * @var string
72 */
73 private $reply_create = '';
74
75 /**
76 * Reply Edit Activity Action.
77 *
78 * @var string
79 */
80 private $reply_edit = '';
81
82 /** Setup Methods *********************************************************/
83
84 /**
85 * The bbPress BuddyPress Activity loader.
86 *
87 * @since 2.2.0 bbPress (r4394)
88 */
89 public function __construct() {
90 $this->setup_globals();
91 $this->setup_actions();
92 $this->setup_filters();
93 $this->fully_loaded();
94 }
95
96 /**
97 * Extension variables.
98 *
99 * @since 2.2.0 bbPress (r4394)
100 *
101 * @access private
102 */
103 private function setup_globals() {
104
105 // The name of the BuddyPress component, used in activity streams
106 $this->component = 'bbpress';
107
108 // Forums
109 $this->forum_create = 'bbp_forum_create';
110
111 // Topics
112 $this->topic_create = 'bbp_topic_create';
113 $this->topic_edit = 'bbp_topic_edit';
114 $this->topic_close = 'bbp_topic_close';
115 $this->topic_open = 'bbp_topic_open';
116
117 // Replies
118 $this->reply_create = 'bbp_reply_create';
119 $this->reply_edit = 'bbp_reply_edit';
120 }
121
122 /**
123 * Setup the actions.
124 *
125 * @since 2.2.0 bbPress (r4394)
126 *
127 * @access private
128 */
129 private function setup_actions() {
130
131 // Register the activity stream actions
132 add_action( 'bp_register_activity_actions', array( $this, 'register_activity_actions' ) );
133
134 // Hook into topic and reply creation
135 add_action( 'bbp_new_topic', array( $this, 'topic_create' ), 10, 4 );
136 add_action( 'bbp_new_reply', array( $this, 'reply_create' ), 10, 5 );
137
138 // Hook into topic and reply status changes
139 add_action( 'edit_post', array( $this, 'topic_update' ), 10, 2 );
140 add_action( 'edit_post', array( $this, 'reply_update' ), 10, 2 );
141
142 // Hook into topic and reply deletion
143 add_action( 'bbp_delete_topic', array( $this, 'topic_delete' ), 10, 1 );
144 add_action( 'bbp_delete_reply', array( $this, 'reply_delete' ), 10, 1 );
145 }
146
147 /**
148 * Setup the filters.
149 *
150 * @since 2.2.0 bbPress (r4394)
151 *
152 * @access private
153 */
154 private function setup_filters() {
155
156 // Obey BuddyPress commenting rules
157 add_filter( 'bp_activity_can_comment', array( $this, 'activity_can_comment' ) );
158
159 // Link directly to the topic or reply
160 add_filter( 'bp_activity_get_permalink', array( $this, 'activity_get_permalink' ), 10, 2 );
161
162 // Check current forum visibility when activity is read, including older entries.
163 add_filter( 'bp_activity_get_where_conditions', array( $this, 'activity_get_where_conditions' ), 10, 2 );
164 add_filter( 'bp_activity_get', array( $this, 'activity_get' ), 10, 2 );
165 add_filter( 'bp_activity_user_can_read', array( $this, 'activity_user_can_read' ), 10, 3 );
166 }
167
168 /**
169 * Allow the variables, actions, and filters to be modified by third party
170 * plugins and themes.
171 *
172 * @since 2.2.0 bbPress (r4394)
173 */
174 private function fully_loaded() {
175 do_action_ref_array( 'bbp_buddypress_activity_loaded', array( $this ) );
176 }
177
178 /** Methods ***************************************************************/
179
180 /**
181 * Identify BuddyPress's REST single-item query, which checks access afterward.
182 *
183 * @param array $args BuddyPress activity query arguments.
184 * @return bool Whether this is a single-item lookup.
185 */
186 private function is_single_activity_lookup( $args ) {
187 return ! empty( $args['show_hidden'] )
188 && ! empty( $args['display_comments'] )
189 && empty( $args['per_page'] )
190 && ! empty( $args['in'] )
191 && 1 === count( wp_parse_id_list( $args['in'] ) );
192 }
193
194 /**
195 * Check whether an activity query can contain bbPress topic or reply activity.
196 *
197 * @param array $args BuddyPress activity query arguments.
198 * @return bool Whether bbPress visibility conditions may be needed.
199 */
200 private function query_can_include_forum_activity( $args ) {
201 if ( empty( $args['filter'] ) || ! is_array( $args['filter'] ) ) {
202 return true;
203 }
204
205 $filter = $args['filter'];
206
207 if ( ! empty( $filter['action'] ) ) {
208 $actions = wp_parse_list( $filter['action'] );
209 if ( ! array_intersect( $actions, array( $this->topic_create, $this->reply_create ) ) ) {
210 return false;
211 }
212 }
213
214 if ( ! empty( $filter['object'] ) ) {
215 $objects = wp_parse_list( $filter['object'] );
216 $group_component = bp_is_active( 'groups' ) ? buddypress()->groups->id : 'groups';
217 if ( ! array_intersect( $objects, array( $this->component, $group_component ) ) ) {
218 return false;
219 }
220 }
221
222 return true;
223 }
224
225 /**
226 * Get password-protected bbPress posts that the current visitor cannot read.
227 *
228 * @return array[] Post IDs keyed by post type.
229 */
230 private function get_password_restricted_post_ids() {
231 $post_types = bbp_get_post_types();
232 $post_ids = array_fill_keys( $post_types, array() );
233 $protected = get_posts(
234 array(
235 'has_password' => true,
236 'no_found_rows' => true,
237 'numberposts' => -1,
238 'orderby' => 'none',
239 'post_status' => 'any',
240 'post_type' => $post_types,
241 'suppress_filters' => true,
242 'update_post_meta_cache' => false,
243 'update_post_term_cache' => false,
244 )
245 );
246
247 foreach ( $protected as $post ) {
248 if ( post_password_required( $post ) ) {
249 $post_ids[ $post->post_type ][] = (int) $post->ID;
250 }
251 }
252
253 // A password on a forum also protects every descendant forum.
254 $forum_type = bbp_get_forum_post_type();
255 $parents = $post_ids[ $forum_type ];
256 $seen = array_fill_keys( $parents, true );
257 $index = 0;
258 while ( isset( $parents[ $index ] ) ) {
259 $parent_id = $parents[ $index ];
260 ++$index;
261
262 foreach ( bbp_forum_query_subforum_ids( $parent_id ) as $forum_id ) {
263 $forum_id = (int) $forum_id;
264 if ( ! isset( $seen[ $forum_id ] ) ) {
265 $seen[ $forum_id ] = true;
266 $post_ids[ $forum_type ][] = $forum_id;
267 $parents[] = $forum_id;
268 }
269 }
270 }
271
272 return $post_ids;
273 }
274
275 /**
276 * Check the single-item query shape when used through bp_activity_get().
277 *
278 * BuddyPress REST retrieves single items through bp_activity_get_specific(),
279 * which performs its own can-read check. Other callers using the same query
280 * shape must not inherit that query's visibility exception.
281 *
282 * @param array $result BuddyPress activity query result.
283 * @param array $args BuddyPress activity query arguments.
284 * @return array Filtered activity query result.
285 */
286 public function activity_get( $result, $args ) {
287 if ( ! $this->is_single_activity_lookup( $args ) || empty( $result['activities'] ) ) {
288 return $result;
289 }
290
291 foreach ( $result['activities'] as $key => $activity ) {
292 if ( ! bp_activity_user_can_read( $activity, get_current_user_id() ) ) {
293 unset( $result['activities'][ $key ] );
294 }
295 }
296
297 $result['activities'] = array_values( $result['activities'] );
298 $result['total'] = count( $result['activities'] );
299
300 return $result;
301 }
302
303 /**
304 * Exclude activity whose topic or reply is now in a restricted forum.
305 *
306 * Activity can outlive a forum visibility or parent change, so the stored
307 * hide_sitewide value alone cannot protect previously public entries.
308 *
309 * @param array $where_conditions BuddyPress activity query conditions.
310 * @param array $args BuddyPress activity query arguments.
311 * @return array Filtered conditions.
312 */
313 public function activity_get_where_conditions( $where_conditions, $args ) {
314 global $wpdb;
315
316 // The REST single-item lookup needs the object for BuddyPress's can-read check.
317 if ( $this->is_single_activity_lookup( $args ) ) {
318 return $where_conditions;
319 }
320
321 if ( ! $this->query_can_include_forum_activity( $args ) ) {
322 return $where_conditions;
323 }
324
325 $forum_ids = array();
326 foreach ( wp_parse_id_list( bbp_get_excluded_forum_ids() ) as $forum_id ) {
327 if ( bbp_is_forum_restricted_for_user( $forum_id, get_current_user_id() ) ) {
328 $forum_ids[] = $forum_id;
329 }
330 }
331
332 $password_ids = $this->get_password_restricted_post_ids();
333 $forum_ids = wp_parse_id_list( array_merge( $forum_ids, $password_ids[ bbp_get_forum_post_type() ] ) );
334 $topic_ids = wp_parse_id_list( $password_ids[ bbp_get_topic_post_type() ] );
335 $reply_ids = wp_parse_id_list( $password_ids[ bbp_get_reply_post_type() ] );
336
337 if ( empty( $forum_ids ) && empty( $topic_ids ) && empty( $reply_ids ) ) {
338 return $where_conditions;
339 }
340
341 $group_component = bp_is_active( 'groups' ) ? buddypress()->groups->id : 'groups';
342
343 // Group activity stores the post ID in secondary_item_id instead.
344 $topic_restrictions = array();
345 $topic_values = array( $this->topic_create, $this->component, $group_component, $group_component );
346 if ( ! empty( $forum_ids ) ) {
347 $topic_restrictions[] = 'bbp_topic.post_parent IN (' . implode( ', ', array_fill( 0, count( $forum_ids ), '%d' ) ) . ')';
348 $topic_values = array_merge( $topic_values, $forum_ids );
349 }
350 if ( ! empty( $topic_ids ) ) {
351 $topic_restrictions[] = 'bbp_topic.ID IN (' . implode( ', ', array_fill( 0, count( $topic_ids ), '%d' ) ) . ')';
352 $topic_values = array_merge( $topic_values, $topic_ids );
353 }
354
355 if ( ! empty( $topic_restrictions ) ) {
356 $topic_sql = "
357 NOT (
358 a.type = %s
359 AND (
360 a.component = %s
361 OR a.component = %s
362 )
363 AND EXISTS (
364 SELECT 1
365 FROM {$wpdb->posts} AS bbp_topic
366 WHERE bbp_topic.ID = CASE
367 WHEN a.component = %s THEN a.secondary_item_id
368 ELSE a.item_id
369 END
370 AND (" . implode( ' OR ', $topic_restrictions ) . ')
371 )
372 )';
373 $where_conditions['bbpress_topic_visibility'] = $wpdb->prepare( $topic_sql, $topic_values ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- SQL contains only table names and generated integer placeholders.
374 }
375
376 // Reply activity points to the reply; follow its topic to the forum.
377 $reply_restrictions = array();
378 $reply_values = array( $this->reply_create, $this->component, $group_component, $group_component );
379 if ( ! empty( $forum_ids ) ) {
380 $reply_restrictions[] = 'bbp_reply_topic.post_parent IN (' . implode( ', ', array_fill( 0, count( $forum_ids ), '%d' ) ) . ')';
381 $reply_values = array_merge( $reply_values, $forum_ids );
382 }
383 if ( ! empty( $topic_ids ) ) {
384 $reply_restrictions[] = 'bbp_reply_topic.ID IN (' . implode( ', ', array_fill( 0, count( $topic_ids ), '%d' ) ) . ')';
385 $reply_values = array_merge( $reply_values, $topic_ids );
386 }
387 if ( ! empty( $reply_ids ) ) {
388 $reply_restrictions[] = 'bbp_reply.ID IN (' . implode( ', ', array_fill( 0, count( $reply_ids ), '%d' ) ) . ')';
389 $reply_values = array_merge( $reply_values, $reply_ids );
390 }
391
392 if ( ! empty( $reply_restrictions ) ) {
393 $reply_sql = "
394 NOT (
395 a.type = %s
396 AND (
397 a.component = %s
398 OR a.component = %s
399 )
400 AND EXISTS (
401 SELECT 1
402 FROM {$wpdb->posts} AS bbp_reply
403 INNER JOIN {$wpdb->posts} AS bbp_reply_topic
404 ON bbp_reply_topic.ID = bbp_reply.post_parent
405 WHERE bbp_reply.ID = CASE
406 WHEN a.component = %s THEN a.secondary_item_id
407 ELSE a.item_id
408 END
409 AND (" . implode( ' OR ', $reply_restrictions ) . ')
410 )
411 )';
412 $where_conditions['bbpress_reply_visibility'] = $wpdb->prepare( $reply_sql, $reply_values ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- SQL contains only table names and generated integer placeholders.
413 }
414
415 return $where_conditions;
416 }
417
418 /**
419 * Check the current forum before allowing direct access to activity.
420 *
421 * @param bool $can_read Whether BuddyPress permits access.
422 * @param int $user_id User requesting the activity.
423 * @param BP_Activity_Activity $activity Activity being requested.
424 * @return bool Whether the user may read the activity.
425 */
426 public function activity_user_can_read( $can_read, $user_id, $activity ) {
427 $group_component = bp_is_active( 'groups' ) ? buddypress()->groups->id : 'groups';
428 if ( ! $can_read || ! in_array( $activity->component, array( $this->component, $group_component ), true ) ) {
429 return $can_read;
430 }
431
432 $post_id = ( $group_component === $activity->component )
433 ? $activity->secondary_item_id
434 : $activity->item_id;
435
436 if ( $this->topic_create === $activity->type ) {
437 $forum_id = bbp_get_topic_forum_id( $post_id );
438 } elseif ( $this->reply_create === $activity->type ) {
439 $forum_id = bbp_get_reply_forum_id( $post_id );
440 } else {
441 return $can_read;
442 }
443
444 return ! empty( $forum_id )
445 && ! bbp_is_forum_restricted_for_user( $forum_id, $user_id )
446 && ! bbp_get_password_required_id( $post_id );
447 }
448
449 /**
450 * Register our activity actions with BuddyPress.
451 *
452 * @since 2.2.0 bbPress (r4394)
453 */
454 public function register_activity_actions() {
455
456 // Sitewide topic
457 bp_activity_set_action(
458 $this->component,
459 $this->topic_create,
460 esc_html__( 'New forum topic', 'bbpress' ),
461 'bbp_format_activity_action_new_topic',
462 esc_html__( 'Topics', 'bbpress' ),
463 array( 'activity', 'member', 'member_groups', 'group' )
464 );
465
466 // Sitewide reply
467 bp_activity_set_action(
468 $this->component,
469 $this->reply_create,
470 esc_html__( 'New forum reply', 'bbpress' ),
471 'bbp_format_activity_action_new_reply',
472 esc_html__( 'Replies', 'bbpress' ),
473 array( 'activity', 'member', 'member_groups', 'group' )
474 );
475 }
476
477 /**
478 * Wrapper for recoding bbPress actions to the BuddyPress activity stream.
479 *
480 * @since 2.2.0 bbPress (r4394)
481 *
482 * @param array $args Array of arguments for bp_activity_add().
483 *
484 * @return int Activity ID if successful, false if not.
485 */
486 private function record_activity( $args = array() ) {
487
488 // Default activity args
489 $activity = bbp_parse_args(
490 $args,
491 array(
492 'id' => null,
493 'user_id' => bbp_get_current_user_id(),
494 'type' => '',
495 'action' => '',
496 'item_id' => '',
497 'secondary_item_id' => '',
498 'content' => '',
499 'primary_link' => '',
500 'component' => $this->component,
501 'recorded_time' => bp_core_current_time(),
502 'hide_sitewide' => false
503 ),
504 'record_activity'
505 );
506
507 // Add the activity
508 return bp_activity_add( $activity );
509 }
510
511 /**
512 * Wrapper for deleting bbPress actions from BuddyPress activity stream.
513 *
514 * @since 2.2.0 bbPress (r4394)
515 *
516 * @param array $args Array of arguments for bp_activity_add().
517 *
518 * @return int Activity ID if successful, false if not.
519 */
520 public function delete_activity( $args = array() ) {
521
522 // Default activity args
523 $activity = bbp_parse_args(
524 $args,
525 array(
526 'item_id' => false,
527 'component' => $this->component,
528 'type' => false,
529 'user_id' => false,
530 'secondary_item_id' => false
531 ),
532 'delete_activity'
533 );
534
535 // Delete the activity
536 bp_activity_delete_by_item_id( $activity );
537 }
538
539 /**
540 * Check for an existing activity stream entry for a given post_id.
541 *
542 * @param int $post_id ID of the topic or reply.
543 * @return int if an activity id is verified, false if not.
544 */
545 private static function get_activity_id( $post_id = 0 ) {
546
547 // Try to get the activity ID of the post
548 $activity_id = (int) get_post_meta( $post_id, '_bbp_activity_id', true );
549
550 // Bail if no activity ID is in post meta
551 if ( empty( $activity_id ) ) {
552 return null;
553 }
554
555 // Get the activity stream item, bail if it doesn't exist
556 $existing = new BP_Activity_Activity( $activity_id );
557 if ( empty( $existing->component ) ) {
558 return null;
559 }
560
561 // Return the activity ID since we've verified the connection
562 return $activity_id;
563 }
564
565 /**
566 * Maybe disable activity stream comments on select actions.
567 *
568 * @since 2.2.0 bbPress (r4394)
569 *
570 * @global BP_Activity_Template $activities_template
571 * @param boolean $can_comment
572 * @return boolean
573 */
574 public function activity_can_comment( $can_comment = true ) {
575 global $activities_template;
576
577 // Already forced off, so comply
578 if ( false === $can_comment ) {
579 return $can_comment;
580 }
581
582 // Check if blog & forum activity stream commenting is off
583 if ( ! empty( $activities_template->disable_blogforum_replies ) ) {
584
585 // Get the current action name
586 $action_name = bp_get_activity_action_name();
587
588 // Setup the array of possibly disabled actions
589 $disabled_actions = array(
590 $this->topic_create,
591 $this->reply_create
592 );
593
594 // Check if this activity stream action is disabled
595 if ( in_array( $action_name, $disabled_actions, true ) ) {
596 $can_comment = false;
597 }
598 }
599
600 return $can_comment;
601 }
602
603 /**
604 * Maybe link directly to topics and replies in activity stream entries.
605 *
606 * @since 2.2.0 bbPress (r4394)
607 *
608 * @param string $link
609 * @param mixed $activity_object
610 * @return string The link to the activity stream item.
611 */
612 public function activity_get_permalink( $link = '', $activity_object = false ) {
613
614 // Setup the array of actions to link directly to
615 $disabled_actions = array(
616 $this->topic_create,
617 $this->reply_create
618 );
619
620 // Check if this activity stream action is directly linked
621 if ( in_array( $activity_object->type, $disabled_actions, true ) ) {
622 $link = $activity_object->primary_link;
623 }
624
625 return $link;
626 }
627
628 /** Topics ****************************************************************/
629
630 /**
631 * Record an activity stream entry when a topic is created or updated.
632 *
633 * @since 2.2.0 bbPress (r4394)
634 *
635 * @param int $topic_id
636 * @param int $forum_id
637 * @param array $anonymous_data
638 * @param int $topic_author_id
639 * @return Bail early if topic is by anonymous user.
640 */
641 public function topic_create( $topic_id = 0, $forum_id = 0, $anonymous_data = array(), $topic_author_id = 0 ) {
642
643 // Bail early if topic is by anonymous user
644 if ( ! empty( $anonymous_data ) ) {
645 return;
646 }
647
648 // Bail if site is private
649 if ( ! bbp_is_site_public() ) {
650 return;
651 }
652
653 // Validate activity data
654 $user_id = bbp_get_user_id( $topic_author_id );
655 $topic_id = bbp_get_topic_id( $topic_id );
656 $forum_id = bbp_get_forum_id( $forum_id );
657
658 // Bail if user is not active
659 if ( bbp_is_user_inactive( $user_id ) ) {
660 return;
661 }
662
663 // Bail if topic is not published
664 if ( ! bbp_is_topic_published( $topic_id ) ) {
665 return;
666 }
667
668 // User link for topic author
669 $user_link = bbp_get_user_profile_link( $user_id );
670
671 // Topic
672 $topic_permalink = bbp_get_topic_permalink( $topic_id );
673 $topic_title = get_post_field( 'post_title', $topic_id, 'raw' );
674 $topic_content = get_post_field( 'post_content', $topic_id, 'raw' );
675 $topic_link = '<a href="' . $topic_permalink . '">' . $topic_title . '</a>';
676
677 // Forum
678 $forum_permalink = bbp_get_forum_permalink( $forum_id );
679 $forum_title = get_post_field( 'post_title', $forum_id, 'raw' );
680 $forum_link = '<a href="' . $forum_permalink . '">' . $forum_title . '</a>';
681
682 // Activity action & text
683 $activity_text = sprintf(
684 /* translators: 1: User linked profile, 2: Topic linked title, 3: Forum linked title */
685 esc_html__( '%1$s started the topic %2$s in the forum %3$s', 'bbpress' ),
686 $user_link,
687 $topic_link,
688 $forum_link
689 );
690 $activity_action = apply_filters( 'bbp_activity_topic_create', $activity_text, $user_id, $topic_id, $forum_id );
691 $activity_content = apply_filters( 'bbp_activity_topic_create_excerpt', $topic_content );
692
693 // Compile and record the activity stream results
694 $activity_id = $this->record_activity(
695 array(
696 'id' => $this->get_activity_id( $topic_id ),
697 'user_id' => $user_id,
698 'action' => $activity_action,
699 'content' => $activity_content,
700 'primary_link' => $topic_permalink,
701 'type' => $this->topic_create,
702 'item_id' => $topic_id,
703 'secondary_item_id' => $forum_id,
704 'recorded_time' => get_post_time( 'Y-m-d H:i:s', true, $topic_id ),
705 'hide_sitewide' => ! bbp_is_forum_public( $forum_id )
706 )
707 );
708
709 // Add the activity entry ID as a meta value to the topic
710 if ( ! empty( $activity_id ) ) {
711 update_post_meta( $topic_id, '_bbp_activity_id', $activity_id );
712 }
713 }
714
715 /**
716 * Delete the activity stream entry when a topic is spammed, trashed, or deleted.
717 *
718 * @param int $topic_id
719 */
720 public function topic_delete( $topic_id = 0 ) {
721
722 // Get activity ID, bail if it doesn't exist
723 $activity_id = $this->get_activity_id( $topic_id );
724 if ( ! empty( $activity_id ) ) {
725 return bp_activity_delete( array( 'id' => $activity_id ) );
726 }
727
728 return false;
729 }
730
731 /**
732 * Update the activity stream entry when a topic status changes.
733 *
734 * @param int $topic_id
735 * @param obj $post
736 * @return Bail early if not a topic, or topic is by anonymous user.
737 */
738 public function topic_update( $topic_id = 0, $post = null ) {
739
740 // Bail early if not a topic
741 if ( get_post_type( $post ) !== bbp_get_topic_post_type() ) {
742 return;
743 }
744
745 // Bail early if revisions are off
746 if ( ! bbp_allow_revisions() || ! post_type_supports( bbp_get_topic_post_type(), 'revisions' ) ) {
747 return;
748 }
749
750 $topic_id = bbp_get_topic_id( $topic_id );
751
752 // Bail early if topic is by anonymous user
753 if ( bbp_is_topic_anonymous( $topic_id ) ) {
754 return;
755 }
756
757 // Action based on new status
758 if ( bbp_is_topic_public( $post->ID ) ) {
759
760 // Validate topic data
761 $forum_id = bbp_get_topic_forum_id( $topic_id );
762 $topic_author_id = bbp_get_topic_author_id( $topic_id );
763
764 $this->topic_create( $topic_id, $forum_id, array(), $topic_author_id );
765 } else {
766 $this->topic_delete( $topic_id );
767 }
768 }
769
770 /** Replies ***************************************************************/
771
772 /**
773 * Record an activity stream entry when a reply is created.
774 *
775 * @since 2.2.0 bbPress (r4394)
776 *
777 * @param int $topic_id
778 * @param int $forum_id
779 * @param array $anonymous_data
780 * @param int $topic_author_id
781 * @return Bail early if topic is by anonymous user.
782 */
783 public function reply_create( $reply_id = 0, $topic_id = 0, $forum_id = 0, $anonymous_data = array(), $reply_author_id = 0 ) {
784
785 // Do not log activity of anonymous users
786 if ( ! empty( $anonymous_data ) ) {
787 return;
788 }
789
790 // Bail if site is private
791 if ( ! bbp_is_site_public() ) {
792 return;
793 }
794
795 // Validate activity data
796 $user_id = bbp_get_user_id( $reply_author_id );
797 $reply_id = bbp_get_reply_id( $reply_id );
798 $topic_id = bbp_get_topic_id( $topic_id );
799 $forum_id = bbp_get_forum_id( $forum_id );
800
801 // Bail if user is not active
802 if ( bbp_is_user_inactive( $user_id ) ) {
803 return;
804 }
805
806 // Bail if reply is not published
807 if ( ! bbp_is_reply_published( $reply_id ) ) {
808 return;
809 }
810
811 // Setup links for activity stream
812 $user_link = bbp_get_user_profile_link( $user_id );
813
814 // Reply
815 $reply_url = bbp_get_reply_url( $reply_id );
816 $reply_content = get_post_field( 'post_content', $reply_id, 'raw' );
817
818 // Topic
819 $topic_permalink = bbp_get_topic_permalink( $topic_id );
820 $topic_title = get_post_field( 'post_title', $topic_id, 'raw' );
821 $topic_link = '<a href="' . $topic_permalink . '">' . $topic_title . '</a>';
822
823 // Forum
824 $forum_permalink = bbp_get_forum_permalink( $forum_id );
825 $forum_title = get_post_field( 'post_title', $forum_id, 'raw' );
826 $forum_link = '<a href="' . $forum_permalink . '">' . $forum_title . '</a>';
827
828 // Activity action & text
829 $activity_text = sprintf(
830 /* translators: 1: User linked profile, 2: Topic linked title, 3: Forum linked title */
831 esc_html__( '%1$s replied to the topic %2$s in the forum %3$s', 'bbpress' ),
832 $user_link,
833 $topic_link,
834 $forum_link
835 );
836 $activity_action = apply_filters( 'bbp_activity_reply_create', $activity_text, $user_id, $reply_id, $topic_id );
837 $activity_content = apply_filters( 'bbp_activity_reply_create_excerpt', $reply_content );
838
839 // Compile and record the activity stream results
840 $activity_id = $this->record_activity(
841 array(
842 'id' => $this->get_activity_id( $reply_id ),
843 'user_id' => $user_id,
844 'action' => $activity_action,
845 'content' => $activity_content,
846 'primary_link' => $reply_url,
847 'type' => $this->reply_create,
848 'item_id' => $reply_id,
849 'secondary_item_id' => $topic_id,
850 'recorded_time' => get_post_time( 'Y-m-d H:i:s', true, $reply_id ),
851 'hide_sitewide' => ! bbp_is_forum_public( $forum_id )
852 )
853 );
854
855 // Add the activity entry ID as a meta value to the reply
856 if ( ! empty( $activity_id ) ) {
857 update_post_meta( $reply_id, '_bbp_activity_id', $activity_id );
858 }
859 }
860
861 /**
862 * Delete the activity stream entry when a reply is spammed, trashed, or deleted.
863 *
864 * @param int $reply_id
865 */
866 public function reply_delete( $reply_id ) {
867
868 // Get activity ID, bail if it doesn't exist
869 $activity_id = $this->get_activity_id( $reply_id );
870 if ( ! empty( $activity_id ) ) {
871 return bp_activity_delete( array( 'id' => $activity_id ) );
872 }
873
874 return false;
875 }
876
877 /**
878 * Update the activity stream entry when a reply status changes.
879 *
880 * @param int $reply_id
881 * @param obj $post
882 * @return Bail early if not a reply, or reply is by anonymous user.
883 */
884 public function reply_update( $reply_id, $post ) {
885
886 // Bail early if not a reply
887 if ( get_post_type( $post ) !== bbp_get_reply_post_type() ) {
888 return;
889 }
890
891 // Bail early if revisions are off
892 if ( ! bbp_allow_revisions() || ! post_type_supports( bbp_get_reply_post_type(), 'revisions' ) ) {
893 return;
894 }
895
896 $reply_id = bbp_get_reply_id( $reply_id );
897
898 // Bail early if reply is by anonymous user
899 if ( bbp_is_reply_anonymous( $reply_id ) ) {
900 return;
901 }
902
903 // Action based on new status
904 if ( bbp_get_public_status_id() === $post->post_status ) {
905
906 // Validate reply data
907 $topic_id = bbp_get_reply_topic_id( $reply_id );
908 $forum_id = bbp_get_reply_forum_id( $reply_id );
909 $reply_author_id = bbp_get_reply_author_id( $reply_id );
910
911 $this->reply_create( $reply_id, $topic_id, $forum_id, array(), $reply_author_id );
912 } else {
913 $this->reply_delete( $reply_id );
914 }
915 }
916 }
917 endif;
918