[ Index ] |
PHP Cross Reference of BuddyPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * BuddyPress Activity Functions. 4 * 5 * Functions for the Activity Streams component. 6 * 7 * @package BuddyPress 8 * @subpackage ActivityFunctions 9 * @since 1.5.0 10 */ 11 12 // Exit if accessed directly. 13 defined( 'ABSPATH' ) || exit; 14 15 /** 16 * Check whether the $bp global lists an activity directory page. 17 * 18 * @since 1.5.0 19 * 20 * @return bool True if activity directory page is found, otherwise false. 21 */ 22 function bp_activity_has_directory() { 23 return isset( buddypress()->pages->activity->id ) && buddypress()->pages->activity->id; 24 } 25 26 /** 27 * Are mentions enabled or disabled? 28 * 29 * The Mentions feature does a number of things, all of which will be turned 30 * off if you disable mentions: 31 * - Detecting and auto-linking @username in all BP/WP content. 32 * - Sending BP notifications and emails to users when they are mentioned 33 * using the @username syntax. 34 * - The Public Message button on user profiles. 35 * 36 * Mentions are enabled by default. To disable, put the following line in 37 * bp-custom.php or your theme's functions.php file: 38 * 39 * add_filter( 'bp_activity_do_mentions', '__return_false' ); 40 * 41 * @since 1.8.0 42 * 43 * @return bool $retval True to enable mentions, false to disable. 44 */ 45 function bp_activity_do_mentions() { 46 47 /** 48 * Filters whether or not mentions are enabled. 49 * 50 * @since 1.8.0 51 * 52 * @param bool $enabled True to enable mentions, false to disable. 53 */ 54 return (bool) apply_filters( 'bp_activity_do_mentions', true ); 55 } 56 57 /** 58 * Should BuddyPress load the mentions scripts and related assets, including results to prime the 59 * mentions suggestions? 60 * 61 * @since 2.1.0 62 * 63 * @return bool True if mentions scripts should be loaded. 64 */ 65 function bp_activity_maybe_load_mentions_scripts() { 66 $mentions_enabled = bp_activity_do_mentions() && bp_is_user_active(); 67 $load_mentions = $mentions_enabled && ( bp_is_activity_component() || is_admin() ); 68 69 /** 70 * Filters whether or not BuddyPress should load mentions scripts and assets. 71 * 72 * @since 2.1.0 73 * 74 * @param bool $load_mentions True to load mentions assets, false otherwise. 75 * @param bool $mentions_enabled True if mentions are enabled. 76 */ 77 return (bool) apply_filters( 'bp_activity_maybe_load_mentions_scripts', $load_mentions, $mentions_enabled ); 78 } 79 80 /** 81 * Locate usernames in an activity content string, as designated by an @ sign. 82 * 83 * @since 1.5.0 84 * 85 * @param string $content The content of the activity, usually found in 86 * $activity->content. 87 * @return array|bool Associative array with user ID as key and username as 88 * value. Boolean false if no mentions found. 89 */ 90 function bp_activity_find_mentions( $content ) { 91 92 $pattern = '/[@]+([A-Za-z0-9-_\.@]+)\b/'; 93 preg_match_all( $pattern, $content, $usernames ); 94 95 // Make sure there's only one instance of each username. 96 $usernames = array_unique( $usernames[1] ); 97 98 // Bail if no usernames. 99 if ( empty( $usernames ) ) { 100 return false; 101 } 102 103 $mentioned_users = array(); 104 105 // We've found some mentions! Check to see if users exist. 106 foreach( (array) array_values( $usernames ) as $username ) { 107 $user_id = bp_activity_get_userid_from_mentionname( $username ); 108 109 // The user ID exists, so let's add it to our array. 110 if ( ! empty( $user_id ) ) { 111 $mentioned_users[ $user_id ] = $username; 112 } 113 } 114 115 if ( empty( $mentioned_users ) ) { 116 return false; 117 } 118 119 /** 120 * Filters the mentioned users. 121 * 122 * @since 2.5.0 123 * 124 * @param array $mentioned_users Associative array with user IDs as keys and usernames as values. 125 */ 126 return apply_filters( 'bp_activity_mentioned_users', $mentioned_users ); 127 } 128 129 /** 130 * Reset a user's unread mentions list and count. 131 * 132 * @since 1.5.0 133 * 134 * @param int $user_id The id of the user whose unread mentions are being reset. 135 */ 136 function bp_activity_clear_new_mentions( $user_id ) { 137 bp_delete_user_meta( $user_id, 'bp_new_mention_count' ); 138 bp_delete_user_meta( $user_id, 'bp_new_mentions' ); 139 140 /** 141 * Fires once mentions has been reset for a given user. 142 * 143 * @since 2.5.0 144 * 145 * @param int $user_id The id of the user whose unread mentions are being reset. 146 */ 147 do_action( 'bp_activity_clear_new_mentions', $user_id ); 148 } 149 150 /** 151 * Adjusts mention count for mentioned users in activity items. 152 * 153 * This function is useful if you only have the activity ID handy and you 154 * haven't parsed an activity item for @mentions yet. 155 * 156 * Currently, only used in {@link bp_activity_delete()}. 157 * 158 * @since 1.5.0 159 * 160 * @param int $activity_id The unique id for the activity item. 161 * @param string $action Can be 'delete' or 'add'. Defaults to 'add'. 162 * @return bool 163 */ 164 function bp_activity_adjust_mention_count( $activity_id = 0, $action = 'add' ) { 165 166 // Bail if no activity ID passed. 167 if ( empty( $activity_id ) ) { 168 return false; 169 } 170 171 // Get activity object. 172 $activity = new BP_Activity_Activity( $activity_id ); 173 174 // Try to find mentions. 175 $usernames = bp_activity_find_mentions( strip_tags( $activity->content ) ); 176 177 // Still empty? Stop now. 178 if ( empty( $usernames ) ) { 179 return false; 180 } 181 182 // Increment mention count foreach mentioned user. 183 foreach( (array) array_keys( $usernames ) as $user_id ) { 184 bp_activity_update_mention_count_for_user( $user_id, $activity_id, $action ); 185 } 186 } 187 188 /** 189 * Update the mention count for a given user. 190 * 191 * This function should be used when you've already parsed your activity item 192 * for @mentions. 193 * 194 * @since 1.7.0 195 * 196 * @param int $user_id The user ID. 197 * @param int $activity_id The unique ID for the activity item. 198 * @param string $action 'delete' or 'add'. Default: 'add'. 199 * @return bool 200 */ 201 function bp_activity_update_mention_count_for_user( $user_id, $activity_id, $action = 'add' ) { 202 203 if ( empty( $user_id ) || empty( $activity_id ) ) { 204 return false; 205 } 206 207 // Adjust the mention list and count for the member. 208 $new_mention_count = (int) bp_get_user_meta( $user_id, 'bp_new_mention_count', true ); 209 $new_mentions = bp_get_user_meta( $user_id, 'bp_new_mentions', true ); 210 211 // Make sure new mentions is an array. 212 if ( empty( $new_mentions ) ) { 213 $new_mentions = array(); 214 } 215 216 switch ( $action ) { 217 case 'delete' : 218 $key = array_search( $activity_id, $new_mentions ); 219 220 if ( $key !== false ) { 221 unset( $new_mentions[$key] ); 222 } 223 224 break; 225 226 case 'add' : 227 default : 228 if ( !in_array( $activity_id, $new_mentions ) ) { 229 $new_mentions[] = (int) $activity_id; 230 } 231 232 break; 233 } 234 235 // Get an updated mention count. 236 $new_mention_count = count( $new_mentions ); 237 238 // Resave the user_meta. 239 bp_update_user_meta( $user_id, 'bp_new_mention_count', $new_mention_count ); 240 bp_update_user_meta( $user_id, 'bp_new_mentions', $new_mentions ); 241 242 return true; 243 } 244 245 /** 246 * Determine a user's "mentionname", the name used for that user in @-mentions. 247 * 248 * @since 1.9.0 249 * 250 * @param int|string $user_id ID of the user to get @-mention name for. 251 * @return string $mentionname User name appropriate for @-mentions. 252 */ 253 function bp_activity_get_user_mentionname( $user_id ) { 254 $mentionname = ''; 255 256 $userdata = bp_core_get_core_userdata( $user_id ); 257 258 if ( $userdata ) { 259 if ( bp_is_username_compatibility_mode() ) { 260 $mentionname = str_replace( ' ', '-', $userdata->user_login ); 261 } else { 262 $mentionname = $userdata->user_nicename; 263 } 264 } 265 266 return $mentionname; 267 } 268 269 /** 270 * Get a user ID from a "mentionname", the name used for a user in @-mentions. 271 * 272 * @since 1.9.0 273 * 274 * @param string $mentionname Username of user in @-mentions. 275 * @return int|bool ID of the user, if one is found. Otherwise false. 276 */ 277 function bp_activity_get_userid_from_mentionname( $mentionname ) { 278 $user_id = false; 279 280 /* 281 * In username compatibility mode, hyphens are ambiguous between 282 * actual hyphens and converted spaces. 283 * 284 * @todo There is the potential for username clashes between 'foo bar' 285 * and 'foo-bar' in compatibility mode. Come up with a system for 286 * unique mentionnames. 287 */ 288 if ( bp_is_username_compatibility_mode() ) { 289 // First, try the raw username. 290 $userdata = get_user_by( 'login', $mentionname ); 291 292 // Doing a direct query to use proper regex. Necessary to 293 // account for hyphens + spaces in the same user_login. 294 if ( empty( $userdata ) || ! is_a( $userdata, 'WP_User' ) ) { 295 global $wpdb; 296 $regex = esc_sql( str_replace( '-', '[ \-]', $mentionname ) ); 297 $user_id = $wpdb->get_var( "SELECT ID FROM {$wpdb->users} WHERE user_login REGEXP '{$regex}'" ); 298 } else { 299 $user_id = $userdata->ID; 300 } 301 302 // When username compatibility mode is disabled, the mentionname is 303 // the same as the nicename. 304 } else { 305 $user_id = bp_core_get_userid_from_nicename( $mentionname ); 306 } 307 308 309 return $user_id; 310 } 311 312 /** Actions ******************************************************************/ 313 314 /** 315 * Register an activity 'type' and its action description/callback. 316 * 317 * Activity actions are strings used to describe items in the activity stream, 318 * such as 'Joe became a registered member' or 'Bill and Susie are now 319 * friends'. Each activity type (such as 'new_member' or 'friendship_created') 320 * used by a component should be registered using this function. 321 * 322 * While it's possible to post items to the activity stream whose types are 323 * not registered using bp_activity_set_action(), it is not recommended; 324 * unregistered types will not be displayed properly in the activity admin 325 * panel, and dynamic action generation (which is essential for multilingual 326 * sites, etc) will not work. 327 * 328 * @since 1.1.0 329 * 330 * @param string $component_id The unique string ID of the component. 331 * @param string $type The action type. 332 * @param string $description The action description. 333 * @param callable|bool $format_callback Callback for formatting the action string. 334 * @param string|bool $label String to describe this action in the activity stream filter dropdown. 335 * @param array $context Optional. Activity stream contexts where the filter should appear. 336 * Values: 'activity', 'member', 'member_groups', 'group'. 337 * @param int $position Optional. The position of the action when listed in dropdowns. 338 * @return bool False if any param is empty, otherwise true. 339 */ 340 function bp_activity_set_action( $component_id, $type, $description, $format_callback = false, $label = false, $context = array(), $position = 0 ) { 341 $bp = buddypress(); 342 343 // Return false if any of the above values are not set. 344 if ( empty( $component_id ) || empty( $type ) || empty( $description ) ) { 345 return false; 346 } 347 348 // Set activity action. 349 if ( ! isset( $bp->activity->actions ) || ! is_object( $bp->activity->actions ) ) { 350 $bp->activity->actions = new stdClass; 351 } 352 353 // Verify callback. 354 if ( ! is_callable( $format_callback ) ) { 355 $format_callback = ''; 356 } 357 358 if ( ! isset( $bp->activity->actions->{$component_id} ) || ! is_object( $bp->activity->actions->{$component_id} ) ) { 359 $bp->activity->actions->{$component_id} = new stdClass; 360 } 361 362 /** 363 * Filters the action type being set for the current activity item. 364 * 365 * @since 1.1.0 366 * 367 * @param array $array Array of arguments for action type being set. 368 * @param string $component_id ID of the current component being set. 369 * @param string $type Action type being set. 370 * @param string $description Action description for action being set. 371 * @param callable $format_callback Callback for formatting the action string. 372 * @param string $label String to describe this action in the activity stream filter dropdown. 373 * @param array $context Activity stream contexts where the filter should appear. 'activity', 'member', 374 * 'member_groups', 'group'. 375 */ 376 $bp->activity->actions->{$component_id}->{$type} = apply_filters( 'bp_activity_set_action', array( 377 'key' => $type, 378 'value' => $description, 379 'format_callback' => $format_callback, 380 'label' => $label, 381 'context' => $context, 382 'position' => $position, 383 ), $component_id, $type, $description, $format_callback, $label, $context ); 384 385 // Sort the actions of the affected component. 386 $action_array = (array) $bp->activity->actions->{$component_id}; 387 $action_array = bp_sort_by_key( $action_array, 'position', 'num' ); 388 389 // Restore keys. 390 $bp->activity->actions->{$component_id} = new stdClass; 391 foreach ( $action_array as $key_ordered ) { 392 $bp->activity->actions->{$component_id}->{$key_ordered['key']} = $key_ordered; 393 } 394 395 return true; 396 } 397 398 /** 399 * Set tracking arguments for a given post type. 400 * 401 * @since 2.2.0 402 * 403 * @global $wp_post_types 404 * 405 * @param string $post_type The name of the post type, as registered with WordPress. Eg 'post' or 'page'. 406 * @param array $args { 407 * An associative array of tracking parameters. All items are optional. 408 * @type string $bp_activity_admin_filter String to use in the Dashboard > Activity dropdown. 409 * @type string $bp_activity_front_filter String to use in the front-end dropdown. 410 * @type string $bp_activity_new_post String format to use for generating the activity action. Should be a 411 * translatable string where %1$s is replaced by a user link and %2$s is 412 * the URL of the newly created post. 413 * @type string $bp_activity_new_post_ms String format to use for generating the activity action on Multisite. 414 * Should be a translatable string where %1$s is replaced by a user link, 415 * %2$s is the URL of the newly created post, and %3$s is a link to 416 * the site. 417 * @type string $component_id ID of the BuddyPress component to associate the activity item. 418 * @type string $action_id Value for the 'type' param of the new activity item. 419 * @type callable $format_callback Callback for formatting the activity action string. 420 * Default: 'bp_activity_format_activity_action_custom_post_type_post'. 421 * @type array $contexts The directory contexts in which the filter will show. 422 * Default: array( 'activity' ). 423 * @type array $position Position of the item in filter dropdowns. 424 * @type string $singular Singular, translatable name of the post type item. If no value is 425 * provided, it's pulled from the 'singular_name' of the post type. 426 * @type bool $activity_comment Whether to allow comments on the activity items. Defaults to true if 427 * the post type does not natively support comments, otherwise false. 428 * } 429 * @return bool 430 */ 431 function bp_activity_set_post_type_tracking_args( $post_type = '', $args = array() ) { 432 global $wp_post_types; 433 434 if ( empty( $wp_post_types[ $post_type ] ) || ! post_type_supports( $post_type, 'buddypress-activity' ) || ! is_array( $args ) ) { 435 return false; 436 } 437 438 $activity_labels = array( 439 /* Post labels */ 440 'bp_activity_admin_filter', 441 'bp_activity_front_filter', 442 'bp_activity_new_post', 443 'bp_activity_new_post_ms', 444 /* Comment labels */ 445 'bp_activity_comments_admin_filter', 446 'bp_activity_comments_front_filter', 447 'bp_activity_new_comment', 448 'bp_activity_new_comment_ms' 449 ); 450 451 // Labels are loaded into the post type object. 452 foreach ( $activity_labels as $label_type ) { 453 if ( ! empty( $args[ $label_type ] ) ) { 454 $wp_post_types[ $post_type ]->labels->{$label_type} = $args[ $label_type ]; 455 unset( $args[ $label_type ] ); 456 } 457 } 458 459 // If there are any additional args, put them in the bp_activity attribute of the post type. 460 if ( ! empty( $args ) ) { 461 $wp_post_types[ $post_type ]->bp_activity = $args; 462 } 463 } 464 465 /** 466 * Get tracking arguments for a specific post type. 467 * 468 * @since 2.2.0 469 * @since 2.5.0 Add post type comments tracking args 470 * 471 * @param string $post_type Name of the post type. 472 * @return object The tracking arguments of the post type. 473 */ 474 function bp_activity_get_post_type_tracking_args( $post_type ) { 475 if ( ! post_type_supports( $post_type, 'buddypress-activity' ) ) { 476 return false; 477 } 478 479 $post_type_object = get_post_type_object( $post_type ); 480 $post_type_support_comments = post_type_supports( $post_type, 'comments' ); 481 482 $post_type_activity = array( 483 'component_id' => buddypress()->activity->id, 484 'action_id' => 'new_' . $post_type, 485 'format_callback' => 'bp_activity_format_activity_action_custom_post_type_post', 486 'front_filter' => $post_type_object->labels->name, 487 'contexts' => array( 'activity' ), 488 'position' => 0, 489 'singular' => strtolower( $post_type_object->labels->singular_name ), 490 'activity_comment' => ! $post_type_support_comments, 491 'comment_action_id' => false, 492 'comment_format_callback' => 'bp_activity_format_activity_action_custom_post_type_comment', 493 ); 494 495 if ( ! empty( $post_type_object->bp_activity ) ) { 496 $post_type_activity = bp_parse_args( 497 (array) $post_type_object->bp_activity, 498 $post_type_activity, 499 $post_type . '_tracking_args' 500 ); 501 } 502 503 $post_type_activity = (object) $post_type_activity; 504 505 // Try to get the admin filter from the post type labels. 506 if ( ! empty( $post_type_object->labels->bp_activity_admin_filter ) ) { 507 $post_type_activity->admin_filter = $post_type_object->labels->bp_activity_admin_filter; 508 509 // Fall back to a generic name. 510 } else { 511 $post_type_activity->admin_filter = _x( 'New item published', 'Post Type generic activity post admin filter', 'buddypress' ); 512 } 513 514 // Check for the front filter in the post type labels. 515 if ( ! empty( $post_type_object->labels->bp_activity_front_filter ) ) { 516 $post_type_activity->front_filter = $post_type_object->labels->bp_activity_front_filter; 517 } 518 519 // Try to get the action for new post type action on non-multisite installations. 520 if ( ! empty( $post_type_object->labels->bp_activity_new_post ) ) { 521 $post_type_activity->new_post_type_action = $post_type_object->labels->bp_activity_new_post; 522 } 523 524 // Try to get the action for new post type action on multisite installations. 525 if ( ! empty( $post_type_object->labels->bp_activity_new_post_ms ) ) { 526 $post_type_activity->new_post_type_action_ms = $post_type_object->labels->bp_activity_new_post_ms; 527 } 528 529 // If the post type supports comments and has a comment action id, build the comments tracking args. 530 if ( $post_type_support_comments && ! empty( $post_type_activity->comment_action_id ) ) { 531 // Init a new container for the activity type for comments. 532 $post_type_activity->comments_tracking = new stdClass(); 533 534 // Build the activity type for comments. 535 $post_type_activity->comments_tracking->component_id = $post_type_activity->component_id; 536 $post_type_activity->comments_tracking->action_id = $post_type_activity->comment_action_id; 537 538 // Try to get the comments admin filter from the post type labels. 539 if ( ! empty( $post_type_object->labels->bp_activity_comments_admin_filter ) ) { 540 $post_type_activity->comments_tracking->admin_filter = $post_type_object->labels->bp_activity_comments_admin_filter; 541 542 // Fall back to a generic name. 543 } else { 544 $post_type_activity->comments_tracking->admin_filter = _x( 'New item comment posted', 'Post Type generic comments activity admin filter', 'buddypress' ); 545 } 546 547 $post_type_activity->comments_tracking->format_callback = $post_type_activity->comment_format_callback; 548 549 // Check for the comments front filter in the post type labels. 550 if ( ! empty( $post_type_object->labels->bp_activity_comments_front_filter ) ) { 551 $post_type_activity->comments_tracking->front_filter = $post_type_object->labels->bp_activity_comments_front_filter; 552 553 // Fall back to a generic name. 554 } else { 555 $post_type_activity->comments_tracking->front_filter = _x( 'Item comments', 'Post Type generic comments activity front filter', 'buddypress' ); 556 } 557 558 $post_type_activity->comments_tracking->contexts = $post_type_activity->contexts; 559 $post_type_activity->comments_tracking->position = (int) $post_type_activity->position + 1; 560 561 // Try to get the action for new post type comment action on non-multisite installations. 562 if ( ! empty( $post_type_object->labels->bp_activity_new_comment ) ) { 563 $post_type_activity->comments_tracking->new_post_type_comment_action = $post_type_object->labels->bp_activity_new_comment; 564 } 565 566 // Try to get the action for new post type comment action on multisite installations. 567 if ( ! empty( $post_type_object->labels->bp_activity_new_comment_ms ) ) { 568 $post_type_activity->comments_tracking->new_post_type_comment_action_ms = $post_type_object->labels->bp_activity_new_comment_ms; 569 } 570 } 571 572 // Finally make sure we'll be able to find the post type this activity type is associated to. 573 $post_type_activity->post_type = $post_type; 574 575 /** 576 * Filters tracking arguments for a specific post type. 577 * 578 * @since 2.2.0 579 * 580 * @param object $post_type_activity The tracking arguments of the post type. 581 * @param string $post_type Name of the post type. 582 */ 583 return apply_filters( 'bp_activity_get_post_type_tracking_args', $post_type_activity, $post_type ); 584 } 585 586 /** 587 * Get tracking arguments for all post types. 588 * 589 * @since 2.2.0 590 * @since 2.5.0 Include post type comments tracking args if needed 591 * 592 * @return array List of post types with their tracking arguments. 593 */ 594 function bp_activity_get_post_types_tracking_args() { 595 // Fetch all public post types. 596 $post_types = get_post_types( array( 'public' => true ), 'names' ); 597 598 $post_types_tracking_args = array(); 599 600 foreach ( $post_types as $post_type ) { 601 $track_post_type = bp_activity_get_post_type_tracking_args( $post_type ); 602 603 if ( ! empty( $track_post_type ) ) { 604 // Set the post type comments tracking args. 605 if ( ! empty( $track_post_type->comments_tracking->action_id ) ) { 606 // Used to check support for comment tracking by activity type (new_post_type_comment). 607 $track_post_type->comments_tracking->comments_tracking = true; 608 609 // Used to be able to find the post type this activity type is associated to. 610 $track_post_type->comments_tracking->post_type = $post_type; 611 612 $post_types_tracking_args[ $track_post_type->comments_tracking->action_id ] = $track_post_type->comments_tracking; 613 614 // Used to check support for comment tracking by activity type (new_post_type). 615 $track_post_type->comments_tracking = true; 616 } 617 618 $post_types_tracking_args[ $track_post_type->action_id ] = $track_post_type; 619 } 620 621 } 622 623 /** 624 * Filters tracking arguments for all post types. 625 * 626 * @since 2.2.0 627 * 628 * @param array $post_types_tracking_args Array of post types with 629 * their tracking arguments. 630 */ 631 return apply_filters( 'bp_activity_get_post_types_tracking_args', $post_types_tracking_args ); 632 } 633 634 /** 635 * Gets the list of activity types name supporting the requested feature. 636 * 637 * This function is still a WIP, please don't use it into your plugins or themes. 638 * 639 * @since 10.0.0 640 * 641 * @access private 642 * @todo `bp_activity_set_action()` should be improved to include a supports 643 * argument or best we should create a `bp_register_activity_type()` function 644 * to mimic the way WordPress registers post types. For now we'll use a non 645 * extendable workaround. 646 * 647 * @param string $feature The feature activity types should support. 648 * @return array The list of activity types name supporting the requested feature. 649 */ 650 function _bp_activity_get_types_by_support( $feature = 'generated-content' ) { 651 $activity_types = array(); 652 653 if ( 'generated-content' === $feature ) { 654 $activity_types = array( 'new_member', 'new_avatar' ); 655 656 if ( bp_is_active( 'friends' ) ) { 657 array_push( $activity_types, 'friendship_created' ); 658 } 659 660 if ( bp_is_active( 'groups' ) ) { 661 array_push( $activity_types, 'created_group', 'joined_group' ); 662 } 663 664 if ( bp_is_active( 'xprofile' ) ) { 665 array_push( $activity_types, 'updated_profile' ); 666 } 667 } 668 669 $filter_key = str_replace( '-', '_', $feature ); 670 671 /** 672 * Use this filter to add/remove activity types supporting the requested feature. 673 * 674 * The dynamic portion of the filter is the name of the requested feature where hyphens are 675 * replaced by underscores. Eg. use `bp_activity_get_types_supporting_generated_content` to 676 * edit the list of activities supporting the `generated-content` feature. 677 * 678 * @since 10.0.0 679 * 680 * @param array $activity_types The list of activity types name supporting the requested feature. 681 */ 682 return apply_filters( "bp_activity_get_types_supporting_{$filter_key}", $activity_types ); 683 } 684 685 /** 686 * Check if the *Post Type* activity supports a specific feature. 687 * 688 * @since 2.5.0 689 * 690 * @param string $activity_type The activity type to check. 691 * @param string $feature The feature to check. Currently supports: 692 * 'post-type-comment-tracking', 'post-type-comment-reply' & 'comment-reply'. 693 * See inline doc for more info. 694 * @return bool 695 */ 696 function bp_activity_type_supports( $activity_type = '', $feature = '' ) { 697 $retval = false; 698 699 $bp = buddypress(); 700 701 switch ( $feature ) { 702 /** 703 * Does this activity type support comment tracking? 704 * 705 * eg. 'new_blog_post' and 'new_blog_comment' will both return true. 706 */ 707 case 'post-type-comment-tracking' : 708 // Set the activity track global if not set yet. 709 if ( empty( $bp->activity->track ) ) { 710 $bp->activity->track = bp_activity_get_post_types_tracking_args(); 711 } 712 713 if ( ! empty( $bp->activity->track[ $activity_type ]->comments_tracking ) ) { 714 $retval = true; 715 } 716 break; 717 718 /** 719 * Is this a parent activity type that support post comments? 720 * 721 * eg. 'new_blog_post' will return true; 'new_blog_comment' will return false. 722 */ 723 case 'post-type-comment-reply' : 724 // Set the activity track global if not set yet. 725 if ( empty( $bp->activity->track ) ) { 726 $bp->activity->track = bp_activity_get_post_types_tracking_args(); 727 } 728 729 if ( ! empty( $bp->activity->track[ $activity_type ]->comments_tracking ) && ! empty( $bp->activity->track[ $activity_type ]->comment_action_id ) ) { 730 $retval = true; 731 } 732 break; 733 734 /** 735 * Does this activity type support comment & reply? 736 */ 737 case 'comment-reply' : 738 // Set the activity track global if not set yet. 739 if ( empty( $bp->activity->track ) ) { 740 $bp->activity->track = bp_activity_get_post_types_tracking_args(); 741 } 742 743 // Post Type activities 744 if ( ! empty( $bp->activity->track[ $activity_type ] ) ) { 745 if ( isset( $bp->activity->track[ $activity_type ]->activity_comment ) ) { 746 $retval = $bp->activity->track[ $activity_type ]->activity_comment; 747 } 748 749 // Eventually override with comment synchronization feature. 750 if ( isset( $bp->activity->track[ $activity_type ]->comments_tracking ) ) { 751 $retval = $bp->activity->track[ $activity_type ]->comments_tracking && ! bp_disable_blogforum_comments(); 752 } 753 754 // Retired Forums component 755 } elseif ( 'new_forum_topic' === $activity_type || 'new_forum_post' === $activity_type ) { 756 $retval = ! bp_disable_blogforum_comments(); 757 758 // By Default, all other activity types are supporting comments. 759 } else { 760 $retval = true; 761 } 762 break; 763 764 /** 765 * Does this activity type support `generated-content`? 766 */ 767 case 'generated-content' : 768 $activity_types = _bp_activity_get_types_by_support( 'generated-content' ); 769 770 $retval = in_array( $activity_type, $activity_types, true ); 771 break; 772 } 773 774 return $retval; 775 } 776 777 /** 778 * Get a specific tracking argument for a given activity type 779 * 780 * @since 2.5.0 781 * 782 * @param string $activity_type the activity type. 783 * @param string $arg the key of the tracking argument. 784 * @return mixed the value of the tracking arg, false if not found. 785 */ 786 function bp_activity_post_type_get_tracking_arg( $activity_type, $arg = '' ) { 787 if ( empty( $activity_type ) || empty( $arg ) ) { 788 return false; 789 } 790 791 $bp = buddypress(); 792 793 // Set the activity track global if not set yet. 794 if ( empty( $bp->activity->track ) ) { 795 $bp->activity->track = bp_activity_get_post_types_tracking_args(); 796 } 797 798 if ( isset( $bp->activity->track[ $activity_type ]->{$arg} ) ) { 799 return $bp->activity->track[ $activity_type ]->{$arg}; 800 } else { 801 return false; 802 } 803 } 804 805 /** 806 * Get all components' activity actions, sorted by their position attribute. 807 * 808 * @since 2.2.0 809 * 810 * @return object Actions ordered by their position. 811 */ 812 function bp_activity_get_actions() { 813 $bp = buddypress(); 814 815 // Set the activity track global if not set yet. 816 if ( empty( $bp->activity->track ) ) { 817 $bp->activity->track = bp_activity_get_post_types_tracking_args(); 818 } 819 820 // Create the actions for the post types, if they haven't already been created. 821 if ( ! empty( $bp->activity->track ) ) { 822 foreach ( $bp->activity->track as $post_type ) { 823 if ( isset( $bp->activity->actions->{$post_type->component_id}->{$post_type->action_id} ) ) { 824 continue; 825 } 826 827 bp_activity_set_action( 828 $post_type->component_id, 829 $post_type->action_id, 830 $post_type->admin_filter, 831 $post_type->format_callback, 832 $post_type->front_filter, 833 $post_type->contexts, 834 $post_type->position 835 ); 836 } 837 } 838 839 return $bp->activity->actions; 840 } 841 842 /** 843 * Retrieve the current action from a component and key. 844 * 845 * @since 1.1.0 846 * 847 * @param string $component_id The unique string ID of the component. 848 * @param string $key The action key. 849 * @return string|bool Action value if found, otherwise false. 850 */ 851 function bp_activity_get_action( $component_id, $key ) { 852 853 // Return false if any of the above values are not set. 854 if ( empty( $component_id ) || empty( $key ) ) { 855 return false; 856 } 857 858 $actions = bp_activity_get_actions(); 859 $retval = false; 860 861 if ( isset( $actions->{$component_id}->{$key} ) ) { 862 $retval = $actions->{$component_id}->{$key}; 863 } 864 865 /** 866 * Filters the current action by component and key. 867 * 868 * @since 1.1.0 869 * 870 * @param string|bool $retval The action key. 871 * @param string $component_id The unique string ID of the component. 872 * @param string $key The action key. 873 */ 874 return apply_filters( 'bp_activity_get_action', $retval, $component_id, $key ); 875 } 876 877 /** 878 * Fetch details of all registered activity types. 879 * 880 * @since 1.7.0 881 * 882 * @return array array( type => description ), ... 883 */ 884 function bp_activity_get_types() { 885 $actions = array(); 886 887 // Walk through the registered actions, and build an array of actions/values. 888 foreach ( bp_activity_get_actions() as $action ) { 889 $action = array_values( (array) $action ); 890 891 for ( $i = 0, $i_count = count( $action ); $i < $i_count; $i++ ) { 892 $actions[ $action[$i]['key'] ] = $action[$i]['value']; 893 } 894 } 895 896 // This was a mis-named activity type from before BP 1.6. 897 unset( $actions['friends_register_activity_action'] ); 898 899 /** 900 * Filters the available activity types. 901 * 902 * @since 1.7.0 903 * 904 * @param array $actions Array of registered activity types. 905 */ 906 return apply_filters( 'bp_activity_get_types', $actions ); 907 } 908 909 /** 910 * Returns the list of available BuddyPress activity types. 911 * 912 * @since 9.0.0 913 * 914 * @return array An array of activity type labels keyed by type names. 915 */ 916 function bp_activity_get_types_list() { 917 $actions_object = bp_activity_get_actions(); 918 $actions_array = get_object_vars( $actions_object ); 919 920 $types = array(); 921 foreach ( $actions_array as $component => $actions ) { 922 $new_types = wp_list_pluck( $actions, 'label', 'key' ); 923 924 if ( $types ) { 925 // Makes sure activity types are unique. 926 $new_types = array_diff_key( $new_types, $types ); 927 928 if ( 'friends' === $component ) { 929 $new_types = array_diff_key( 930 array( 931 'friendship_accepted' => false, 932 'friendship_created' => false, 933 'friends_register_activity_action' => false, 934 ), 935 $new_types 936 ); 937 938 $new_types['friendship_accepted,friendship_created'] = __( 'Friendships', 'buddypress' ); 939 } 940 } 941 942 $types = array_merge( $types, $new_types ); 943 } 944 945 /** 946 * Filter here to edit the activity types list. 947 * 948 * @since 9.0.0 949 * 950 * @param array $types An array of activity type labels keyed by type names. 951 */ 952 return apply_filters( 'bp_activity_get_types_list', $types ); 953 } 954 955 /** 956 * Gets the current activity context. 957 * 958 * The "context" is the current view type, corresponding roughly to the 959 * current component. Use this context to determine which activity actions 960 * should be permitted in the filter dropdown. 961 * 962 * @since 2.8.0 963 * 964 * @return string Activity context. 'member', 'member_groups', 'group', 'activity'. 965 */ 966 function bp_activity_get_current_context() { 967 // On member pages, default to 'member', unless this is a user's Groups activity. 968 if ( bp_is_user() ) { 969 if ( bp_is_active( 'groups' ) && bp_is_current_action( bp_get_groups_slug() ) ) { 970 $context = 'member_groups'; 971 } else { 972 $context = 'member'; 973 } 974 975 // On individual group pages, default to 'group'. 976 } elseif ( bp_is_active( 'groups' ) && bp_is_group() ) { 977 $context = 'group'; 978 979 // 'activity' everywhere else. 980 } else { 981 $context = 'activity'; 982 } 983 984 return $context; 985 } 986 987 /** 988 * Gets a flat list of activity actions compatible with a given context. 989 * 990 * @since 2.8.0 991 * 992 * @param string $context Optional. Name of the context. Defaults to the current context. 993 * @return array 994 */ 995 function bp_activity_get_actions_for_context( $context = '' ) { 996 if ( ! $context ) { 997 $context = bp_activity_get_current_context(); 998 } 999 1000 $actions = array(); 1001 foreach ( bp_activity_get_actions() as $component_actions ) { 1002 foreach ( $component_actions as $component_action ) { 1003 if ( in_array( $context, (array) $component_action['context'], true ) ) { 1004 $actions[] = $component_action; 1005 } 1006 } 1007 } 1008 1009 return $actions; 1010 } 1011 1012 /** Favorites ****************************************************************/ 1013 1014 /** 1015 * Get a users favorite activity stream items. 1016 * 1017 * @since 1.2.0 1018 * 1019 * @param int $user_id ID of the user whose favorites are being queried. 1020 * @return array IDs of the user's favorite activity items. 1021 */ 1022 function bp_activity_get_user_favorites( $user_id = 0 ) { 1023 1024 // Fallback to logged in user if no user_id is passed. 1025 if ( empty( $user_id ) ) { 1026 $user_id = bp_displayed_user_id(); 1027 } 1028 1029 // Get favorites for user. 1030 $favs = bp_get_user_meta( $user_id, 'bp_favorite_activities', true ); 1031 1032 /** 1033 * Filters the favorited activity items for a specified user. 1034 * 1035 * @since 1.2.0 1036 * 1037 * @param array $favs Array of user's favorited activity items. 1038 */ 1039 return apply_filters( 'bp_activity_get_user_favorites', $favs ); 1040 } 1041 1042 /** 1043 * Add an activity stream item as a favorite for a user. 1044 * 1045 * @since 1.2.0 1046 * 1047 * @param int $activity_id ID of the activity item being favorited. 1048 * @param int $user_id ID of the user favoriting the activity item. 1049 * @return bool True on success, false on failure. 1050 */ 1051 function bp_activity_add_user_favorite( $activity_id, $user_id = 0 ) { 1052 1053 // Fallback to logged in user if no user_id is passed. 1054 if ( empty( $user_id ) ) { 1055 $user_id = bp_loggedin_user_id(); 1056 } 1057 1058 $my_favs = bp_get_user_meta( $user_id, 'bp_favorite_activities', true ); 1059 if ( empty( $my_favs ) || ! is_array( $my_favs ) ) { 1060 $my_favs = array(); 1061 } 1062 1063 // Bail if the user has already favorited this activity item. 1064 if ( in_array( $activity_id, $my_favs ) ) { 1065 return false; 1066 } 1067 1068 // Add to user's favorites. 1069 $my_favs[] = $activity_id; 1070 1071 // Update the total number of users who have favorited this activity. 1072 $fav_count = bp_activity_get_meta( $activity_id, 'favorite_count' ); 1073 $fav_count = !empty( $fav_count ) ? (int) $fav_count + 1 : 1; 1074 1075 // Update user meta. 1076 bp_update_user_meta( $user_id, 'bp_favorite_activities', $my_favs ); 1077 1078 // Update activity meta counts. 1079 if ( bp_activity_update_meta( $activity_id, 'favorite_count', $fav_count ) ) { 1080 1081 /** 1082 * Fires if bp_activity_update_meta() for favorite_count is successful and before returning a true value for success. 1083 * 1084 * @since 1.2.1 1085 * 1086 * @param int $activity_id ID of the activity item being favorited. 1087 * @param int $user_id ID of the user doing the favoriting. 1088 */ 1089 do_action( 'bp_activity_add_user_favorite', $activity_id, $user_id ); 1090 1091 // Success. 1092 return true; 1093 1094 // Saving meta was unsuccessful for an unknown reason. 1095 } else { 1096 1097 /** 1098 * Fires if bp_activity_update_meta() for favorite_count is unsuccessful and before returning a false value for failure. 1099 * 1100 * @since 1.5.0 1101 * 1102 * @param int $activity_id ID of the activity item being favorited. 1103 * @param int $user_id ID of the user doing the favoriting. 1104 */ 1105 do_action( 'bp_activity_add_user_favorite_fail', $activity_id, $user_id ); 1106 1107 return false; 1108 } 1109 } 1110 1111 /** 1112 * Remove an activity stream item as a favorite for a user. 1113 * 1114 * @since 1.2.0 1115 * 1116 * @param int $activity_id ID of the activity item being unfavorited. 1117 * @param int $user_id ID of the user unfavoriting the activity item. 1118 * @return bool True on success, false on failure. 1119 */ 1120 function bp_activity_remove_user_favorite( $activity_id, $user_id = 0 ) { 1121 1122 // Fallback to logged in user if no user_id is passed. 1123 if ( empty( $user_id ) ) { 1124 $user_id = bp_loggedin_user_id(); 1125 } 1126 1127 $my_favs = bp_get_user_meta( $user_id, 'bp_favorite_activities', true ); 1128 $my_favs = array_flip( (array) $my_favs ); 1129 1130 // Bail if the user has not previously favorited the item. 1131 if ( ! isset( $my_favs[ $activity_id ] ) ) { 1132 return false; 1133 } 1134 1135 // Remove the fav from the user's favs. 1136 unset( $my_favs[$activity_id] ); 1137 $my_favs = array_unique( array_flip( $my_favs ) ); 1138 1139 // Update the total number of users who have favorited this activity. 1140 $fav_count = bp_activity_get_meta( $activity_id, 'favorite_count' ); 1141 if ( ! empty( $fav_count ) ) { 1142 1143 // Deduct from total favorites. 1144 if ( bp_activity_update_meta( $activity_id, 'favorite_count', (int) $fav_count - 1 ) ) { 1145 1146 // Update users favorites. 1147 if ( bp_update_user_meta( $user_id, 'bp_favorite_activities', $my_favs ) ) { 1148 1149 /** 1150 * Fires if bp_update_user_meta() is successful and before returning a true value for success. 1151 * 1152 * @since 1.2.1 1153 * 1154 * @param int $activity_id ID of the activity item being unfavorited. 1155 * @param int $user_id ID of the user doing the unfavoriting. 1156 */ 1157 do_action( 'bp_activity_remove_user_favorite', $activity_id, $user_id ); 1158 1159 // Success. 1160 return true; 1161 1162 // Error updating. 1163 } else { 1164 return false; 1165 } 1166 1167 // Error updating favorite count. 1168 } else { 1169 return false; 1170 } 1171 1172 // Error getting favorite count. 1173 } else { 1174 return false; 1175 } 1176 } 1177 1178 /** 1179 * Check whether an activity item exists with a given content string. 1180 * 1181 * @since 1.1.0 1182 * 1183 * @param string $content The content to filter by. 1184 * @return int|null The ID of the located activity item. Null if none is found. 1185 */ 1186 function bp_activity_check_exists_by_content( $content ) { 1187 1188 /** 1189 * Filters the results of the check for whether an activity item exists by specified content. 1190 * 1191 * @since 1.1.0 1192 * 1193 * @param BP_Activity_Activity $value ID of the activity if found, else null. 1194 */ 1195 return apply_filters( 'bp_activity_check_exists_by_content', BP_Activity_Activity::check_exists_by_content( $content ) ); 1196 } 1197 1198 /** 1199 * Retrieve the last time activity was updated. 1200 * 1201 * @since 1.0.0 1202 * 1203 * @return string Date last updated. 1204 */ 1205 function bp_activity_get_last_updated() { 1206 1207 /** 1208 * Filters the value for the last updated time for an activity item. 1209 * 1210 * @since 1.1.0 1211 * 1212 * @param BP_Activity_Activity $last_updated Date last updated. 1213 */ 1214 return apply_filters( 'bp_activity_get_last_updated', BP_Activity_Activity::get_last_updated() ); 1215 } 1216 1217 /** 1218 * Retrieve the number of favorite activity stream items a user has. 1219 * 1220 * @since 1.2.0 1221 * 1222 * @param int $user_id ID of the user whose favorite count is being requested. 1223 * @return int Total favorite count for the user. 1224 */ 1225 function bp_activity_total_favorites_for_user( $user_id = 0 ) { 1226 1227 // Fallback on displayed user, and then logged in user. 1228 if ( empty( $user_id ) ) { 1229 $user_id = ( bp_displayed_user_id() ) ? bp_displayed_user_id() : bp_loggedin_user_id(); 1230 } 1231 1232 return BP_Activity_Activity::total_favorite_count( $user_id ); 1233 } 1234 1235 /** Meta *********************************************************************/ 1236 1237 /** 1238 * Delete a meta entry from the DB for an activity stream item. 1239 * 1240 * @since 1.2.0 1241 * 1242 * @global object $wpdb WordPress database access object. 1243 * 1244 * @param int $activity_id ID of the activity item whose metadata is being deleted. 1245 * @param string $meta_key Optional. The key of the metadata being deleted. If 1246 * omitted, all metadata associated with the activity 1247 * item will be deleted. 1248 * @param string $meta_value Optional. If present, the metadata will only be 1249 * deleted if the meta_value matches this parameter. 1250 * @param bool $delete_all Optional. If true, delete matching metadata entries 1251 * for all objects, ignoring the specified object_id. Otherwise, 1252 * only delete matching metadata entries for the specified 1253 * activity item. Default: false. 1254 * @return bool True on success, false on failure. 1255 */ 1256 function bp_activity_delete_meta( $activity_id, $meta_key = '', $meta_value = '', $delete_all = false ) { 1257 1258 // Legacy - if no meta_key is passed, delete all for the item. 1259 if ( empty( $meta_key ) ) { 1260 $all_meta = bp_activity_get_meta( $activity_id ); 1261 $keys = ! empty( $all_meta ) ? array_keys( $all_meta ) : array(); 1262 1263 // With no meta_key, ignore $delete_all. 1264 $delete_all = false; 1265 } else { 1266 $keys = array( $meta_key ); 1267 } 1268 1269 $retval = true; 1270 1271 add_filter( 'query', 'bp_filter_metaid_column_name' ); 1272 foreach ( $keys as $key ) { 1273 $retval = delete_metadata( 'activity', $activity_id, $key, $meta_value, $delete_all ); 1274 } 1275 remove_filter( 'query', 'bp_filter_metaid_column_name' ); 1276 1277 return $retval; 1278 } 1279 1280 /** 1281 * Get metadata for a given activity item. 1282 * 1283 * @since 1.2.0 1284 * 1285 * @param int $activity_id ID of the activity item whose metadata is being requested. 1286 * @param string $meta_key Optional. If present, only the metadata matching 1287 * that meta key will be returned. Otherwise, all metadata for the 1288 * activity item will be fetched. 1289 * @param bool $single Optional. If true, return only the first value of the 1290 * specified meta_key. This parameter has no effect if meta_key is not 1291 * specified. Default: true. 1292 * @return mixed The meta value(s) being requested. 1293 */ 1294 function bp_activity_get_meta( $activity_id = 0, $meta_key = '', $single = true ) { 1295 add_filter( 'query', 'bp_filter_metaid_column_name' ); 1296 $retval = get_metadata( 'activity', $activity_id, $meta_key, $single ); 1297 remove_filter( 'query', 'bp_filter_metaid_column_name' ); 1298 1299 /** 1300 * Filters the metadata for a specified activity item. 1301 * 1302 * @since 1.5.0 1303 * 1304 * @param mixed $retval The meta values for the activity item. 1305 * @param int $activity_id ID of the activity item. 1306 * @param string $meta_key Meta key for the value being requested. 1307 * @param bool $single Whether to return one matched meta key row or all. 1308 */ 1309 return apply_filters( 'bp_activity_get_meta', $retval, $activity_id, $meta_key, $single ); 1310 } 1311 1312 /** 1313 * Update a piece of activity meta. 1314 * 1315 * @since 1.2.0 1316 * 1317 * @param int $activity_id ID of the activity item whose metadata is being updated. 1318 * @param string $meta_key Key of the metadata being updated. 1319 * @param mixed $meta_value Value to be set. 1320 * @param mixed $prev_value Optional. If specified, only update existing metadata entries 1321 * with the specified value. Otherwise, update all entries. 1322 * @return bool|int Returns false on failure. On successful update of existing 1323 * metadata, returns true. On successful creation of new metadata, 1324 * returns the integer ID of the new metadata row. 1325 */ 1326 function bp_activity_update_meta( $activity_id, $meta_key, $meta_value, $prev_value = '' ) { 1327 add_filter( 'query', 'bp_filter_metaid_column_name' ); 1328 $retval = update_metadata( 'activity', $activity_id, $meta_key, $meta_value, $prev_value ); 1329 remove_filter( 'query', 'bp_filter_metaid_column_name' ); 1330 1331 return $retval; 1332 } 1333 1334 /** 1335 * Add a piece of activity metadata. 1336 * 1337 * @since 2.0.0 1338 * 1339 * @param int $activity_id ID of the activity item. 1340 * @param string $meta_key Metadata key. 1341 * @param mixed $meta_value Metadata value. 1342 * @param bool $unique Optional. Whether to enforce a single metadata value for the 1343 * given key. If true, and the object already has a value for 1344 * the key, no change will be made. Default: false. 1345 * @return int|bool The meta ID on successful update, false on failure. 1346 */ 1347 function bp_activity_add_meta( $activity_id, $meta_key, $meta_value, $unique = false ) { 1348 add_filter( 'query', 'bp_filter_metaid_column_name' ); 1349 $retval = add_metadata( 'activity', $activity_id, $meta_key, $meta_value, $unique ); 1350 remove_filter( 'query', 'bp_filter_metaid_column_name' ); 1351 1352 return $retval; 1353 } 1354 1355 /** Clean up *****************************************************************/ 1356 1357 /** 1358 * Completely remove a user's activity data. 1359 * 1360 * @since 1.5.0 1361 * 1362 * @param int $user_id ID of the user whose activity is being deleted. 1363 * @return bool 1364 */ 1365 function bp_activity_remove_all_user_data( $user_id = 0 ) { 1366 if ( empty( $user_id ) ) { 1367 return false; 1368 } 1369 1370 // Clear the user's activity from the sitewide stream and clear their activity tables. 1371 bp_activity_delete( array( 'user_id' => $user_id ) ); 1372 1373 // Remove any usermeta. 1374 bp_delete_user_meta( $user_id, 'bp_latest_update' ); 1375 bp_delete_user_meta( $user_id, 'bp_favorite_activities' ); 1376 1377 // Execute additional code 1378 do_action( 'bp_activity_remove_data', $user_id ); // Deprecated! Do not use! 1379 1380 /** 1381 * Fires after the removal of all of a user's activity data. 1382 * 1383 * @since 1.5.0 1384 * 1385 * @param int $user_id ID of the user being deleted. 1386 */ 1387 do_action( 'bp_activity_remove_all_user_data', $user_id ); 1388 } 1389 add_action( 'wpmu_delete_user', 'bp_activity_remove_all_user_data' ); 1390 1391 /** 1392 * Deletes user activity data on the 'delete_user' hook. 1393 * 1394 * @since 6.0.0 1395 * 1396 * @param int $user_id The ID of the deleted user. 1397 */ 1398 function bp_activity_remove_all_user_data_on_delete_user( $user_id ) { 1399 if ( ! bp_remove_user_data_on_delete_user_hook( 'activity', $user_id ) ) { 1400 return; 1401 } 1402 1403 bp_activity_remove_all_user_data( $user_id ); 1404 } 1405 add_action( 'delete_user', 'bp_activity_remove_all_user_data_on_delete_user' ); 1406 1407 /** 1408 * Mark all of the user's activity as spam. 1409 * 1410 * @since 1.6.0 1411 * 1412 * @global object $wpdb WordPress database access object. 1413 * 1414 * @param int $user_id ID of the user whose activity is being spammed. 1415 * @return bool 1416 */ 1417 function bp_activity_spam_all_user_data( $user_id = 0 ) { 1418 global $wpdb; 1419 1420 // Do not delete user data unless a logged in user says so. 1421 if ( empty( $user_id ) || ! is_user_logged_in() ) { 1422 return false; 1423 } 1424 1425 // Get all the user's activities. 1426 $activities = bp_activity_get( array( 1427 'display_comments' => 'stream', 1428 'filter' => array( 'user_id' => $user_id ), 1429 'show_hidden' => true 1430 ) ); 1431 1432 $bp = buddypress(); 1433 1434 // Mark each as spam. 1435 foreach ( (array) $activities['activities'] as $activity ) { 1436 1437 // Create an activity object. 1438 $activity_obj = new BP_Activity_Activity; 1439 foreach ( $activity as $k => $v ) { 1440 $activity_obj->$k = $v; 1441 } 1442 1443 // Mark as spam. 1444 bp_activity_mark_as_spam( $activity_obj ); 1445 1446 /* 1447 * If Akismet is present, update the activity history meta. 1448 * 1449 * This is usually taken care of when BP_Activity_Activity::save() happens, but 1450 * as we're going to be updating all the activity statuses directly, for efficiency, 1451 * we need to update manually. 1452 */ 1453 if ( ! empty( $bp->activity->akismet ) ) { 1454 $bp->activity->akismet->update_activity_spam_meta( $activity_obj ); 1455 } 1456 1457 // Tidy up. 1458 unset( $activity_obj ); 1459 } 1460 1461 // Mark all of this user's activities as spam. 1462 $wpdb->query( $wpdb->prepare( "UPDATE {$bp->activity->table_name} SET is_spam = 1 WHERE user_id = %d", $user_id ) ); 1463 1464 /** 1465 * Fires after all activity data from a user has been marked as spam. 1466 * 1467 * @since 1.6.0 1468 * 1469 * @param int $user_id ID of the user whose activity is being marked as spam. 1470 * @param array $activities Array of activity items being marked as spam. 1471 */ 1472 do_action( 'bp_activity_spam_all_user_data', $user_id, $activities['activities'] ); 1473 } 1474 add_action( 'bp_make_spam_user', 'bp_activity_spam_all_user_data' ); 1475 1476 /** 1477 * Mark all of the user's activity as ham (not spam). 1478 * 1479 * @since 1.6.0 1480 * 1481 * @global object $wpdb WordPress database access object. 1482 * 1483 * @param int $user_id ID of the user whose activity is being hammed. 1484 * @return bool 1485 */ 1486 function bp_activity_ham_all_user_data( $user_id = 0 ) { 1487 global $wpdb; 1488 1489 // Do not delete user data unless a logged in user says so. 1490 if ( empty( $user_id ) || ! is_user_logged_in() ) { 1491 return false; 1492 } 1493 1494 // Get all the user's activities. 1495 $activities = bp_activity_get( array( 1496 'display_comments' => 'stream', 1497 'filter' => array( 'user_id' => $user_id ), 1498 'show_hidden' => true, 1499 'spam' => 'all' 1500 ) ); 1501 1502 $bp = buddypress(); 1503 1504 // Mark each as not spam. 1505 foreach ( (array) $activities['activities'] as $activity ) { 1506 1507 // Create an activity object. 1508 $activity_obj = new BP_Activity_Activity; 1509 foreach ( $activity as $k => $v ) { 1510 $activity_obj->$k = $v; 1511 } 1512 1513 // Mark as not spam. 1514 bp_activity_mark_as_ham( $activity_obj ); 1515 1516 /* 1517 * If Akismet is present, update the activity history meta. 1518 * 1519 * This is usually taken care of when BP_Activity_Activity::save() happens, but 1520 * as we're going to be updating all the activity statuses directly, for efficiency, 1521 * we need to update manually. 1522 */ 1523 if ( ! empty( $bp->activity->akismet ) ) { 1524 $bp->activity->akismet->update_activity_ham_meta( $activity_obj ); 1525 } 1526 1527 // Tidy up. 1528 unset( $activity_obj ); 1529 } 1530 1531 // Mark all of this user's activities as not spam. 1532 $wpdb->query( $wpdb->prepare( "UPDATE {$bp->activity->table_name} SET is_spam = 0 WHERE user_id = %d", $user_id ) ); 1533 1534 /** 1535 * Fires after all activity data from a user has been marked as ham. 1536 * 1537 * @since 1.6.0 1538 * 1539 * @param int $user_id ID of the user whose activity is being marked as ham. 1540 * @param array $activities Array of activity items being marked as ham. 1541 */ 1542 do_action( 'bp_activity_ham_all_user_data', $user_id, $activities['activities'] ); 1543 } 1544 add_action( 'bp_make_ham_user', 'bp_activity_ham_all_user_data' ); 1545 1546 /** 1547 * Allow core components and dependent plugins to register activity actions. 1548 * 1549 * @since 1.2.0 1550 */ 1551 function bp_register_activity_actions() { 1552 /** 1553 * Fires on bp_init to allow core components and dependent plugins to register activity actions. 1554 * 1555 * @since 1.2.0 1556 */ 1557 do_action( 'bp_register_activity_actions' ); 1558 } 1559 add_action( 'bp_init', 'bp_register_activity_actions', 8 ); 1560 1561 /** 1562 * Register the activity stream actions for updates. 1563 * 1564 * @since 1.6.0 1565 */ 1566 function bp_activity_register_activity_actions() { 1567 $bp = buddypress(); 1568 1569 bp_activity_set_action( 1570 $bp->activity->id, 1571 'activity_update', 1572 __( 'Posted a status update', 'buddypress' ), 1573 'bp_activity_format_activity_action_activity_update', 1574 __( 'Updates', 'buddypress' ), 1575 array( 'activity', 'group', 'member', 'member_groups' ) 1576 ); 1577 1578 bp_activity_set_action( 1579 $bp->activity->id, 1580 'activity_comment', 1581 __( 'Replied to a status update', 'buddypress' ), 1582 'bp_activity_format_activity_action_activity_comment', 1583 __( 'Activity Comments', 'buddypress' ) 1584 ); 1585 1586 /** 1587 * Fires at the end of the activity actions registration. 1588 * 1589 * Allows plugin authors to add their own activity actions alongside the core actions. 1590 * 1591 * @since 1.6.0 1592 */ 1593 do_action( 'bp_activity_register_activity_actions' ); 1594 1595 // Backpat. Don't use this. 1596 do_action( 'updates_register_activity_actions' ); 1597 } 1598 add_action( 'bp_register_activity_actions', 'bp_activity_register_activity_actions' ); 1599 1600 /** 1601 * Generate an activity action string for an activity item. 1602 * 1603 * @since 2.0.0 1604 * 1605 * @param object $activity Activity data object. 1606 * @return string|bool Returns false if no callback is found, otherwise returns 1607 * the formatted action string. 1608 */ 1609 function bp_activity_generate_action_string( $activity ) { 1610 1611 // Check for valid input. 1612 if ( empty( $activity->component ) || empty( $activity->type ) ) { 1613 return false; 1614 } 1615 1616 // Check for registered format callback. 1617 $actions = bp_activity_get_actions(); 1618 if ( empty( $actions->{$activity->component}->{$activity->type}['format_callback'] ) ) { 1619 return false; 1620 } 1621 1622 // We apply the format_callback as a filter. 1623 add_filter( 'bp_activity_generate_action_string', $actions->{$activity->component}->{$activity->type}['format_callback'], 10, 2 ); 1624 1625 /** 1626 * Filters the string for the activity action being returned. 1627 * 1628 * @since 2.0.0 1629 * 1630 * @param BP_Activity_Activity $action Action string being requested. 1631 * @param BP_Activity_Activity $activity Activity item object. 1632 */ 1633 $action = apply_filters( 'bp_activity_generate_action_string', $activity->action, $activity ); 1634 1635 // Remove the filter for future activity items. 1636 remove_filter( 'bp_activity_generate_action_string', $actions->{$activity->component}->{$activity->type}['format_callback'], 10 ); 1637 1638 return $action; 1639 } 1640 1641 /** 1642 * Format 'activity_update' activity actions. 1643 * 1644 * @since 2.0.0 1645 * 1646 * @param string $action Static activity action. 1647 * @param object $activity Activity data object. 1648 * @return string $action 1649 */ 1650 function bp_activity_format_activity_action_activity_update( $action, $activity ) { 1651 $action = sprintf( 1652 /* translators: %s: the activity author user link */ 1653 esc_html__( '%s posted an update', 'buddypress' ), 1654 bp_core_get_userlink( $activity->user_id ) 1655 ); 1656 1657 /** 1658 * Filters the formatted activity action update string. 1659 * 1660 * @since 1.2.0 1661 * 1662 * @param string $action Activity action string value. 1663 * @param BP_Activity_Activity $activity Activity item object. 1664 */ 1665 return apply_filters( 'bp_activity_new_update_action', $action, $activity ); 1666 } 1667 1668 /** 1669 * Format 'activity_comment' activity actions. 1670 * 1671 * @since 2.0.0 1672 * 1673 * @param string $action Static activity action. 1674 * @param object $activity Activity data object. 1675 * @return string $action 1676 */ 1677 function bp_activity_format_activity_action_activity_comment( $action, $activity ) { 1678 $action = sprintf( 1679 /* translators: %s: the activity author user link */ 1680 esc_html__( '%s posted a new activity comment', 'buddypress' ), 1681 bp_core_get_userlink( $activity->user_id ) 1682 ); 1683 1684 /** 1685 * Filters the formatted activity action comment string. 1686 * 1687 * @since 1.2.0 1688 * 1689 * @param string $action Activity action string value. 1690 * @param BP_Activity_Activity $activity Activity item object. 1691 */ 1692 return apply_filters( 'bp_activity_comment_action', $action, $activity ); 1693 } 1694 1695 /** 1696 * Format activity action strings for custom post types. 1697 * 1698 * @since 2.2.0 1699 * 1700 * @param string $action Static activity action. 1701 * @param object $activity Activity data object. 1702 * @return string $action 1703 */ 1704 function bp_activity_format_activity_action_custom_post_type_post( $action, $activity ) { 1705 $bp = buddypress(); 1706 1707 // Fetch all the tracked post types once. 1708 if ( empty( $bp->activity->track ) ) { 1709 $bp->activity->track = bp_activity_get_post_types_tracking_args(); 1710 } 1711 1712 if ( empty( $activity->type ) || empty( $bp->activity->track[ $activity->type ] ) ) { 1713 return $action; 1714 } 1715 1716 $user_link = bp_core_get_userlink( $activity->user_id ); 1717 $blog_url = get_home_url( $activity->item_id ); 1718 1719 if ( empty( $activity->post_url ) ) { 1720 $post_url = add_query_arg( 'p', $activity->secondary_item_id, trailingslashit( $blog_url ) ); 1721 } else { 1722 $post_url = $activity->post_url; 1723 } 1724 1725 $post_link = '<a href="' . esc_url( $post_url ) . '">' . esc_html_x( 'item', 'Default text for the post type name', 'buddypress' ) . '</a>'; 1726 1727 if ( is_multisite() ) { 1728 $blog_link = '<a href="' . esc_url( $blog_url ) . '">' . esc_html( get_blog_option( $activity->item_id, 'blogname' ) ) . '</a>'; 1729 1730 if ( ! empty( $bp->activity->track[ $activity->type ]->new_post_type_action_ms ) ) { 1731 $action = sprintf( $bp->activity->track[ $activity->type ]->new_post_type_action_ms, $user_link, esc_url( $post_url ), $blog_link ); 1732 } else { 1733 /* translators: 1: the activity author user link. 2: the post link. 3: the blog link. */ 1734 $action = sprintf( esc_html_x( '%1$s wrote a new %2$s, on the site %3$s', 'Activity Custom Post Type post action', 'buddypress' ), $user_link, $post_link, $blog_link ); 1735 } 1736 } else { 1737 if ( ! empty( $bp->activity->track[ $activity->type ]->new_post_type_action ) ) { 1738 $action = sprintf( $bp->activity->track[ $activity->type ]->new_post_type_action, $user_link, $post_url ); 1739 } else { 1740 /* translators: 1: the activity author user link. 2: the post link. */ 1741 $action = sprintf( esc_html_x( '%1$s wrote a new %2$s', 'Activity Custom Post Type post action', 'buddypress' ), $user_link, $post_link ); 1742 } 1743 } 1744 1745 /** 1746 * Filters the formatted custom post type activity post action string. 1747 * 1748 * @since 2.2.0 1749 * 1750 * @param string $action Activity action string value. 1751 * @param BP_Activity_Activity $activity Activity item object. 1752 */ 1753 return apply_filters( 'bp_activity_custom_post_type_post_action', $action, $activity ); 1754 } 1755 1756 /** 1757 * Format activity action strings for custom post types comments. 1758 * 1759 * @since 2.5.0 1760 * 1761 * @param string $action Static activity action. 1762 * @param object $activity Activity data object. 1763 * 1764 * @return string 1765 */ 1766 function bp_activity_format_activity_action_custom_post_type_comment( $action, $activity ) { 1767 $bp = buddypress(); 1768 1769 // Fetch all the tracked post types once. 1770 if ( empty( $bp->activity->track ) ) { 1771 $bp->activity->track = bp_activity_get_post_types_tracking_args(); 1772 } 1773 1774 if ( empty( $activity->type ) || empty( $bp->activity->track[ $activity->type ] ) ) { 1775 return $action; 1776 } 1777 1778 $user_link = bp_core_get_userlink( $activity->user_id ); 1779 $post_link = '<a href="' . esc_url( $activity->primary_link ) . '">' . esc_html_x( 'item', 'Default text for the post type name', 'buddypress' ) . '</a>'; 1780 1781 if ( is_multisite() ) { 1782 $blog_link = '<a href="' . esc_url( get_home_url( $activity->item_id ) ) . '">' . get_blog_option( $activity->item_id, 'blogname' ) . '</a>'; 1783 1784 if ( ! empty( $bp->activity->track[ $activity->type ]->new_post_type_comment_action_ms ) ) { 1785 $action = sprintf( $bp->activity->track[ $activity->type ]->new_post_type_comment_action_ms, $user_link, $activity->primary_link, $blog_link ); 1786 } else { 1787 /* translators: 1: the activity author user link. 2: the post link. 3: the blog link. */ 1788 $action = sprintf( esc_html_x( '%1$s commented on the %2$s, on the site %3$s', 'Activity Custom Post Type comment action', 'buddypress' ), $user_link, $post_link, $blog_link ); 1789 } 1790 } else { 1791 if ( ! empty( $bp->activity->track[ $activity->type ]->new_post_type_comment_action ) ) { 1792 $action = sprintf( $bp->activity->track[ $activity->type ]->new_post_type_comment_action, $user_link, $activity->primary_link ); 1793 } else { 1794 /* translators: 1: the activity author user link. 2: the post link. */ 1795 $action = sprintf( esc_html_x( '%1$s commented on the %2$s', 'Activity Custom Post Type post comment action', 'buddypress' ), $user_link, $post_link ); 1796 } 1797 } 1798 1799 /** 1800 * Filters the formatted custom post type activity comment action string. 1801 * 1802 * @since 2.5.0 1803 * 1804 * @param string $action Activity action string value. 1805 * @param BP_Activity_Activity $activity Activity item object. 1806 */ 1807 return apply_filters( 'bp_activity_custom_post_type_comment_action', $action, $activity ); 1808 } 1809 1810 /* 1811 * Business functions are where all the magic happens in BuddyPress. They will 1812 * handle the actual saving or manipulation of information. Usually they will 1813 * hand off to a database class for data access, then return 1814 * true or false on success or failure. 1815 */ 1816 1817 /** 1818 * Retrieve an activity or activities. 1819 * 1820 * The bp_activity_get() function shares all arguments with BP_Activity_Activity::get(). 1821 * The following is a list of bp_activity_get() parameters that have different 1822 * default values from BP_Activity_Activity::get() (value in parentheses is 1823 * the default for the bp_activity_get()). 1824 * - 'per_page' (false) 1825 * 1826 * @since 1.2.0 1827 * @since 2.4.0 Introduced the `$fields` parameter. 1828 * 1829 * @see BP_Activity_Activity::get() For more information on accepted arguments 1830 * and the format of the returned value. 1831 * 1832 * @param array|string $args See BP_Activity_Activity::get() for description. 1833 * @return array $activity See BP_Activity_Activity::get() for description. 1834 */ 1835 function bp_activity_get( $args = '' ) { 1836 1837 $r = bp_parse_args( 1838 $args, 1839 array( 1840 'max' => false, // Maximum number of results to return. 1841 'fields' => 'all', 1842 'page' => 1, // Page 1 without a per_page will result in no pagination. 1843 'per_page' => false, // results per page. 1844 'sort' => 'DESC', // sort ASC or DESC. 1845 'display_comments' => false, // False for no comments. 'stream' for within stream display, 'threaded' for below each activity item. 1846 'search_terms' => false, // Pass search terms as a string. 1847 'meta_query' => false, // Filter by activity meta. See WP_Meta_Query for format. 1848 'date_query' => false, // Filter by date. See first parameter of WP_Date_Query for format. 1849 'filter_query' => false, 1850 'show_hidden' => false, // Show activity items that are hidden site-wide? 1851 'exclude' => false, // Comma-separated list of activity IDs to exclude. 1852 'in' => false, // Comma-separated list or array of activity IDs to which you want to limit the query. 1853 'spam' => 'ham_only', // 'ham_only' (default), 'spam_only' or 'all'. 1854 'update_meta_cache' => true, 1855 'count_total' => false, 1856 'count_total_only' => false, 1857 'scope' => false, 1858 1859 /** 1860 * Pass filters as an array -- all filter items can be multiple values comma separated: 1861 * array( 1862 * 'user_id' => false, // User ID to filter on. 1863 * 'object' => false, // Object to filter on e.g. groups, profile, status, friends. 1864 * 'action' => false, // Action to filter on e.g. activity_update, profile_updated. 1865 * 'primary_id' => false, // Object ID to filter on e.g. a group_id or blog_id etc. 1866 * 'secondary_id' => false, // Secondary object ID to filter on e.g. a post_id. 1867 * ); 1868 */ 1869 'filter' => array() 1870 ), 1871 'activity_get' 1872 ); 1873 1874 $activity = BP_Activity_Activity::get( 1875 array( 1876 'page' => $r['page'], 1877 'per_page' => $r['per_page'], 1878 'max' => $r['max'], 1879 'sort' => $r['sort'], 1880 'search_terms' => $r['search_terms'], 1881 'meta_query' => $r['meta_query'], 1882 'date_query' => $r['date_query'], 1883 'filter_query' => $r['filter_query'], 1884 'filter' => $r['filter'], 1885 'scope' => $r['scope'], 1886 'display_comments' => $r['display_comments'], 1887 'show_hidden' => $r['show_hidden'], 1888 'exclude' => $r['exclude'], 1889 'in' => $r['in'], 1890 'spam' => $r['spam'], 1891 'update_meta_cache' => $r['update_meta_cache'], 1892 'count_total' => $r['count_total'], 1893 'count_total_only' => $r['count_total_only'], 1894 'fields' => $r['fields'], 1895 ) 1896 ); 1897 1898 /** 1899 * Filters the requested activity item(s). 1900 * 1901 * @since 1.2.0 1902 * 1903 * @param BP_Activity_Activity $activity Requested activity object. 1904 * @param array $r Arguments used for the activity query. 1905 */ 1906 return apply_filters_ref_array( 'bp_activity_get', array( &$activity, &$r ) ); 1907 } 1908 1909 /** 1910 * Fetch specific activity items. 1911 * 1912 * @since 1.2.0 1913 * 1914 * @see BP_Activity_Activity::get() For more information on accepted arguments. 1915 * 1916 * @param array|string $args { 1917 * An array of arguments. 1918 * All arguments and defaults are shared with BP_Activity_Activity::get(), 1919 * except for the following: 1920 * @type string|int|array Single activity ID, comma-separated list of IDs, 1921 * or array of IDs. 1922 * } 1923 * @return array See BP_Activity_Activity::get() for description. 1924 */ 1925 function bp_activity_get_specific( $args = '' ) { 1926 1927 $r = bp_parse_args( 1928 $args, 1929 array( 1930 'activity_ids' => false, // A single activity_id or array of IDs. 1931 'display_comments' => false, // True or false to display threaded comments for these specific activity items. 1932 'max' => false, // Maximum number of results to return. 1933 'page' => 1, // Page 1 without a per_page will result in no pagination. 1934 'per_page' => false, // Results per page. 1935 'show_hidden' => true, // When fetching specific items, show all. 1936 'sort' => 'DESC', // Sort ASC or DESC. 1937 'spam' => 'ham_only', // Retrieve items marked as spam. 1938 'update_meta_cache' => true, 1939 ), 1940 'activity_get_specific' 1941 ); 1942 1943 $get_args = array( 1944 'display_comments' => $r['display_comments'], 1945 'in' => $r['activity_ids'], 1946 'max' => $r['max'], 1947 'page' => $r['page'], 1948 'per_page' => $r['per_page'], 1949 'show_hidden' => $r['show_hidden'], 1950 'sort' => $r['sort'], 1951 'spam' => $r['spam'], 1952 'update_meta_cache' => $r['update_meta_cache'], 1953 ); 1954 1955 $activity = BP_Activity_Activity::get( $get_args ); 1956 1957 /** 1958 * Filters the requested specific activity item. 1959 * 1960 * @since 1.2.0 1961 * 1962 * @param array $activity The array returned has two keys: total and activity. 1963 * @param array $args Original passed in arguments. 1964 * @param array $get_args Constructed arguments used with request. 1965 */ 1966 return apply_filters( 'bp_activity_get_specific', $activity, $args, $get_args ); 1967 } 1968 1969 /** 1970 * Add an activity item. 1971 * 1972 * @since 1.1.0 1973 * @since 2.6.0 Added 'error_type' parameter to $args. 1974 * 1975 * @param array|string $args { 1976 * An array of arguments. 1977 * @type int|bool $id Pass an activity ID to update an existing item, or 1978 * false to create a new item. Default: false. 1979 * @type string $action Optional. The activity action/description, typically 1980 * something like "Joe posted an update". Values passed to this param 1981 * will be stored in the database and used as a fallback for when the 1982 * activity item's format_callback cannot be found (eg, when the 1983 * component is disabled). As long as you have registered a 1984 * format_callback for your $type, it is unnecessary to include this 1985 * argument - BP will generate it automatically. 1986 * See {@link bp_activity_set_action()}. 1987 * @type string $content Optional. The content of the activity item. 1988 * @type string $component The unique name of the component associated with 1989 * the activity item - 'groups', 'profile', etc. 1990 * @type string $type The specific activity type, used for directory 1991 * filtering. 'new_blog_post', 'activity_update', etc. 1992 * @type string $primary_link Optional. The URL for this item, as used in 1993 * RSS feeds. Defaults to the URL for this activity 1994 * item's permalink page. 1995 * @type int|bool $user_id Optional. The ID of the user associated with the activity 1996 * item. May be set to false or 0 if the item is not related 1997 * to any user. Default: the ID of the currently logged-in user. 1998 * @type int $item_id Optional. The ID of the associated item. 1999 * @type int $secondary_item_id Optional. The ID of a secondary associated item. 2000 * @type string $date_recorded Optional. The GMT time, in Y-m-d h:i:s format, when 2001 * the item was recorded. Defaults to the current time. 2002 * @type bool $hide_sitewide Should the item be hidden on sitewide streams? 2003 * Default: false. 2004 * @type bool $is_spam Should the item be marked as spam? Default: false. 2005 * @type string $error_type Optional. Error type. Either 'bool' or 'wp_error'. Default: 'bool'. 2006 * } 2007 * @return WP_Error|bool|int The ID of the activity on success. False on error. 2008 */ 2009 function bp_activity_add( $args = '' ) { 2010 2011 $r = bp_parse_args( 2012 $args, 2013 array( 2014 'id' => false, // Pass an existing activity ID to update an existing entry. 2015 'action' => '', // The activity action - e.g. "Jon Doe posted an update". 2016 'content' => '', // Optional: The content of the activity item e.g. "BuddyPress is awesome guys!". 2017 'component' => false, // The name/ID of the component e.g. groups, profile, mycomponent. 2018 'type' => false, // The activity type e.g. activity_update, profile_updated. 2019 'primary_link' => '', // Optional: The primary URL for this item in RSS feeds (defaults to activity permalink). 2020 'user_id' => bp_loggedin_user_id(), // Optional: The user to record the activity for, can be false if this activity is not for a user. 2021 'item_id' => false, // Optional: The ID of the specific item being recorded, e.g. a blog_id. 2022 'secondary_item_id' => false, // Optional: A second ID used to further filter e.g. a comment_id. 2023 'recorded_time' => bp_core_current_time(), // The GMT time that this activity was recorded. 2024 'hide_sitewide' => false, // Should this be hidden on the sitewide activity stream? 2025 'is_spam' => false, // Is this activity item to be marked as spam? 2026 'error_type' => 'bool', 2027 ), 2028 'activity_add' 2029 ); 2030 2031 // Make sure we are backwards compatible. 2032 if ( empty( $r['component'] ) && ! empty( $r['component_name'] ) ) { 2033 $r['component'] = $r['component_name']; 2034 } 2035 2036 if ( empty( $r['type'] ) && ! empty( $r['component_action'] ) ) { 2037 $r['type'] = $r['component_action']; 2038 } 2039 2040 // Setup activity to be added. 2041 $activity = new BP_Activity_Activity( $r['id'] ); 2042 $activity->user_id = $r['user_id']; 2043 $activity->component = $r['component']; 2044 $activity->type = $r['type']; 2045 $activity->content = $r['content']; 2046 $activity->primary_link = $r['primary_link']; 2047 $activity->item_id = $r['item_id']; 2048 $activity->secondary_item_id = $r['secondary_item_id']; 2049 $activity->date_recorded = $r['recorded_time']; 2050 $activity->hide_sitewide = $r['hide_sitewide']; 2051 $activity->is_spam = $r['is_spam']; 2052 $activity->error_type = $r['error_type']; 2053 $activity->action = ! empty( $r['action'] ) 2054 ? $r['action'] 2055 : bp_activity_generate_action_string( $activity ); 2056 2057 $save = $activity->save(); 2058 2059 if ( 'wp_error' === $r['error_type'] && is_wp_error( $save ) ) { 2060 return $save; 2061 } elseif ( 'bool' === $r['error_type'] && false === $save ) { 2062 return false; 2063 } 2064 2065 // If this is an activity comment, rebuild the tree. 2066 if ( 'activity_comment' === $activity->type ) { 2067 // Also clear the comment cache for the parent activity ID. 2068 wp_cache_delete( $activity->item_id, 'bp_activity_comments' ); 2069 2070 BP_Activity_Activity::rebuild_activity_comment_tree( $activity->item_id ); 2071 } 2072 2073 wp_cache_delete( 'bp_activity_sitewide_front', 'bp' ); 2074 2075 /** 2076 * Fires at the end of the execution of adding a new activity item, before returning the new activity item ID. 2077 * 2078 * @since 1.1.0 2079 * @since 4.0.0 Added the `$activity_id` parameter. 2080 * 2081 * @param array $r Array of parsed arguments for the activity item being added. 2082 * @param int $activity_id The id of the activity item being added. 2083 */ 2084 do_action( 'bp_activity_add', $r, $activity->id ); 2085 2086 return $activity->id; 2087 } 2088 2089 /** 2090 * Post an activity update. 2091 * 2092 * @since 1.2.0 2093 * 2094 * @param array|string $args { 2095 * An array of arguments. 2096 * @type string $content The content of the activity update. 2097 * @type int $user_id Optional. Defaults to the logged-in user. 2098 * @type string $error_type Optional. Error type to return. Either 'bool' or 'wp_error'. Defaults to 2099 * 'bool' for boolean. 'wp_error' will return a WP_Error object. 2100 * } 2101 * @return int|bool|WP_Error $activity_id The activity id on success. On failure, either boolean false or WP_Error 2102 * object depending on the 'error_type' $args parameter. 2103 */ 2104 function bp_activity_post_update( $args = '' ) { 2105 2106 $r = bp_parse_args( 2107 $args, 2108 array( 2109 'content' => false, 2110 'user_id' => bp_loggedin_user_id(), 2111 'error_type' => 'bool', 2112 ) 2113 ); 2114 2115 if ( empty( $r['content'] ) || ! strlen( trim( $r['content'] ) ) ) { 2116 if ( 'wp_error' === $r['error_type'] ) { 2117 return new WP_Error( 'bp_activity_missing_content', __( 'Please enter some content to post.', 'buddypress' ) ); 2118 } 2119 2120 return false; 2121 } 2122 2123 if ( bp_is_user_inactive( $r['user_id'] ) ) { 2124 if ( 'wp_error' === $r['error_type'] ) { 2125 return new WP_Error( 'bp_activity_inactive_user', __( 'User account has not yet been activated.', 'buddypress' ) ); 2126 } 2127 2128 return false; 2129 } 2130 2131 // Record this on the user's profile. 2132 $activity_content = $r['content']; 2133 $primary_link = bp_core_get_userlink( $r['user_id'], false, true ); 2134 2135 /** 2136 * Filters the new activity content for current activity item. 2137 * 2138 * @since 1.2.0 2139 * 2140 * @param string $activity_content Activity content posted by user. 2141 */ 2142 $add_content = apply_filters( 'bp_activity_new_update_content', $activity_content ); 2143 2144 /** 2145 * Filters the activity primary link for current activity item. 2146 * 2147 * @since 1.2.0 2148 * 2149 * @param string $primary_link Link to the profile for the user who posted the activity. 2150 */ 2151 $add_primary_link = apply_filters( 'bp_activity_new_update_primary_link', $primary_link ); 2152 2153 // Now write the values. 2154 $activity_id = bp_activity_add( array( 2155 'user_id' => $r['user_id'], 2156 'content' => $add_content, 2157 'primary_link' => $add_primary_link, 2158 'component' => buddypress()->activity->id, 2159 'type' => 'activity_update', 2160 'error_type' => $r['error_type'] 2161 ) ); 2162 2163 // Bail on failure. 2164 if ( false === $activity_id || is_wp_error( $activity_id ) ) { 2165 return $activity_id; 2166 } 2167 2168 /** 2169 * Filters the latest update content for the activity item. 2170 * 2171 * @since 1.6.0 2172 * 2173 * @param string $r Content of the activity update. 2174 * @param string $activity_content Content of the activity update. 2175 */ 2176 $activity_content = apply_filters( 'bp_activity_latest_update_content', $r['content'], $activity_content ); 2177 2178 // Add this update to the "latest update" usermeta so it can be fetched anywhere. 2179 bp_update_user_meta( bp_loggedin_user_id(), 'bp_latest_update', array( 2180 'id' => $activity_id, 2181 'content' => $activity_content 2182 ) ); 2183 2184 /** 2185 * Fires at the end of an activity post update, before returning the updated activity item ID. 2186 * 2187 * @since 1.2.0 2188 * 2189 * @param string $content Content of the activity post update. 2190 * @param int $user_id ID of the user posting the activity update. 2191 * @param int $activity_id ID of the activity item being updated. 2192 */ 2193 do_action( 'bp_activity_posted_update', $r['content'], $r['user_id'], $activity_id ); 2194 2195 return $activity_id; 2196 } 2197 2198 /** 2199 * Create an activity item for a newly published post type post. 2200 * 2201 * @since 2.2.0 2202 * 2203 * @param int $post_id ID of the new post. 2204 * @param WP_Post|null $post Post object. 2205 * @param int $user_id ID of the post author. 2206 * @return null|WP_Error|bool|int The ID of the activity on success. False on error. 2207 */ 2208 function bp_activity_post_type_publish( $post_id = 0, $post = null, $user_id = 0 ) { 2209 2210 if ( ! is_a( $post, 'WP_Post' ) ) { 2211 return; 2212 } 2213 2214 // Get the post type tracking args. 2215 $activity_post_object = bp_activity_get_post_type_tracking_args( $post->post_type ); 2216 2217 if ( 'publish' != $post->post_status || ! empty( $post->post_password ) || empty( $activity_post_object->action_id ) ) { 2218 return; 2219 } 2220 2221 if ( empty( $post_id ) ) { 2222 $post_id = $post->ID; 2223 } 2224 2225 $blog_id = get_current_blog_id(); 2226 2227 if ( empty( $user_id ) ) { 2228 $user_id = (int) $post->post_author; 2229 } 2230 2231 // Bail if an activity item already exists for this post. 2232 $existing = bp_activity_get( array( 2233 'filter' => array( 2234 'action' => $activity_post_object->action_id, 2235 'primary_id' => $blog_id, 2236 'secondary_id' => $post_id, 2237 ) 2238 ) ); 2239 2240 if ( ! empty( $existing['activities'] ) ) { 2241 return; 2242 } 2243 2244 /** 2245 * Filters whether or not to post the activity. 2246 * 2247 * This is a variable filter, dependent on the post type, 2248 * that lets components or plugins bail early if needed. 2249 * 2250 * @since 2.2.0 2251 * 2252 * @param bool $value Whether or not to continue. 2253 * @param int $blog_id ID of the current site. 2254 * @param int $post_id ID of the current post being published. 2255 * @param int $user_id ID of the current user or post author. 2256 */ 2257 if ( false === apply_filters( "bp_activity_{$post->post_type}_pre_publish", true, $blog_id, $post_id, $user_id ) ) { 2258 return; 2259 } 2260 2261 // Record this in activity streams. 2262 $blog_url = get_home_url( $blog_id ); 2263 $post_url = add_query_arg( 2264 'p', 2265 $post_id, 2266 trailingslashit( $blog_url ) 2267 ); 2268 2269 // Backward compatibility filters for the 'blogs' component. 2270 if ( 'blogs' == $activity_post_object->component_id ) { 2271 $activity_content = apply_filters( 'bp_blogs_activity_new_post_content', $post->post_content, $post, $post_url, $post->post_type ); 2272 $activity_primary_link = apply_filters( 'bp_blogs_activity_new_post_primary_link', $post_url, $post_id, $post->post_type ); 2273 } else { 2274 $activity_content = $post->post_content; 2275 $activity_primary_link = $post_url; 2276 } 2277 2278 $activity_args = array( 2279 'user_id' => $user_id, 2280 'content' => $activity_content, 2281 'primary_link' => $activity_primary_link, 2282 'component' => $activity_post_object->component_id, 2283 'type' => $activity_post_object->action_id, 2284 'item_id' => $blog_id, 2285 'secondary_item_id' => $post_id, 2286 'recorded_time' => $post->post_date_gmt, 2287 ); 2288 2289 if ( ! empty( $activity_args['content'] ) ) { 2290 // Create the excerpt. 2291 $activity_summary = bp_activity_create_summary( $activity_args['content'], $activity_args ); 2292 2293 // Backward compatibility filter for blog posts. 2294 if ( 'blogs' == $activity_post_object->component_id ) { 2295 $activity_args['content'] = apply_filters( 'bp_blogs_record_activity_content', $activity_summary, $activity_args['content'], $activity_args, $post->post_type ); 2296 } else { 2297 $activity_args['content'] = $activity_summary; 2298 } 2299 } 2300 2301 // Set up the action by using the format functions. 2302 $action_args = array_merge( $activity_args, array( 2303 'post_title' => $post->post_title, 2304 'post_url' => $post_url, 2305 ) ); 2306 2307 $activity_args['action'] = call_user_func_array( $activity_post_object->format_callback, array( '', (object) $action_args ) ); 2308 2309 // Make sure the action is set. 2310 if ( empty( $activity_args['action'] ) ) { 2311 return; 2312 } else { 2313 // Backward compatibility filter for the blogs component. 2314 if ( 'blogs' == $activity_post_object->component_id ) { 2315 $activity_args['action'] = apply_filters( 'bp_blogs_record_activity_action', $activity_args['action'] ); 2316 } 2317 } 2318 2319 $activity_id = bp_activity_add( $activity_args ); 2320 2321 /** 2322 * Fires after the publishing of an activity item for a newly published post type post. 2323 * 2324 * @since 2.2.0 2325 * 2326 * @param int $activity_id ID of the newly published activity item. 2327 * @param WP_Post $post Post object. 2328 * @param array $activity_args Array of activity arguments. 2329 */ 2330 do_action( 'bp_activity_post_type_published', $activity_id, $post, $activity_args ); 2331 2332 return $activity_id; 2333 } 2334 2335 /** 2336 * Update the activity item for a custom post type entry. 2337 * 2338 * @since 2.2.0 2339 * 2340 * @param WP_Post|null $post Post item. 2341 * @return null|WP_Error|bool True on success, false on failure. 2342 */ 2343 function bp_activity_post_type_update( $post = null ) { 2344 2345 if ( ! is_a( $post, 'WP_Post' ) ) { 2346 return; 2347 } 2348 2349 // Get the post type tracking args. 2350 $activity_post_object = bp_activity_get_post_type_tracking_args( $post->post_type ); 2351 2352 if ( empty( $activity_post_object->action_id ) ) { 2353 return; 2354 } 2355 2356 $activity_id = bp_activity_get_activity_id( array( 2357 'component' => $activity_post_object->component_id, 2358 'item_id' => get_current_blog_id(), 2359 'secondary_item_id' => $post->ID, 2360 'type' => $activity_post_object->action_id, 2361 ) ); 2362 2363 // Activity ID doesn't exist, so stop! 2364 if ( empty( $activity_id ) ) { 2365 return; 2366 } 2367 2368 // Delete the activity if the post was updated with a password. 2369 if ( ! empty( $post->post_password ) ) { 2370 bp_activity_delete( array( 'id' => $activity_id ) ); 2371 } 2372 2373 // Update the activity entry. 2374 $activity = new BP_Activity_Activity( $activity_id ); 2375 2376 // Check if the Post author has changed. 2377 if ( (int) $post->post_author !== (int) $activity->user_id ) { 2378 $activity->user_id = (int) $post->post_author; 2379 } 2380 2381 if ( ! empty( $post->post_content ) ) { 2382 $activity_summary = bp_activity_create_summary( $post->post_content, (array) $activity ); 2383 2384 // Backward compatibility filter for the blogs component. 2385 if ( 'blogs' == $activity_post_object->component_id ) { 2386 $activity->content = apply_filters( 'bp_blogs_record_activity_content', $activity_summary, $post->post_content, (array) $activity, $post->post_type ); 2387 } else { 2388 $activity->content = $activity_summary; 2389 } 2390 } 2391 2392 // Save the updated activity. 2393 $updated = $activity->save(); 2394 2395 /** 2396 * Fires after the updating of an activity item for a custom post type entry. 2397 * 2398 * @since 2.2.0 2399 * @since 2.5.0 Add the post type tracking args parameter 2400 * 2401 * @param WP_Post $post Post object. 2402 * @param BP_Activity_Activity $activity Activity object. 2403 * @param object $activity_post_object The post type tracking args object. 2404 */ 2405 do_action( 'bp_activity_post_type_updated', $post, $activity, $activity_post_object ); 2406 2407 return $updated; 2408 } 2409 2410 /** 2411 * Unpublish an activity for the custom post type. 2412 * 2413 * @since 2.2.0 2414 * 2415 * @param int $post_id ID of the post being unpublished. 2416 * @param WP_Post|null $post Post object. 2417 * @return bool True on success, false on failure. 2418 */ 2419 function bp_activity_post_type_unpublish( $post_id = 0, $post = null ) { 2420 2421 if ( ! is_a( $post, 'WP_Post' ) ) { 2422 return; 2423 } 2424 2425 // Get the post type tracking args. 2426 $activity_post_object = bp_activity_get_post_type_tracking_args( $post->post_type ); 2427 2428 if ( empty( $activity_post_object->action_id ) ) { 2429 return; 2430 } 2431 2432 if ( empty( $post_id ) ) { 2433 $post_id = $post->ID; 2434 } 2435 2436 $delete_activity_args = array( 2437 'item_id' => get_current_blog_id(), 2438 'secondary_item_id' => $post_id, 2439 'component' => $activity_post_object->component_id, 2440 'type' => $activity_post_object->action_id, 2441 'user_id' => false, 2442 ); 2443 2444 $deleted = bp_activity_delete_by_item_id( $delete_activity_args ); 2445 2446 /** 2447 * Fires after the unpublishing for the custom post type. 2448 * 2449 * @since 2.2.0 2450 * 2451 * @param array $delete_activity_args Array of arguments for activity deletion. 2452 * @param WP_Post $post Post object. 2453 * @param bool $activity Whether or not the activity was successfully deleted. 2454 */ 2455 do_action( 'bp_activity_post_type_unpublished', $delete_activity_args, $post, $deleted ); 2456 2457 return $deleted; 2458 } 2459 2460 /** 2461 * Create an activity item for a newly posted post type comment. 2462 * 2463 * @since 2.5.0 2464 * 2465 * @param int $comment_id ID of the comment. 2466 * @param bool $is_approved Whether the comment is approved or not. 2467 * @param object|null $activity_post_object The post type tracking args object. 2468 * @return null|WP_Error|bool|int The ID of the activity on success. False on error. 2469 */ 2470 function bp_activity_post_type_comment( $comment_id = 0, $is_approved = true, $activity_post_object = null ) { 2471 // Get the users comment. 2472 $post_type_comment = get_comment( $comment_id ); 2473 2474 // Don't record activity if the comment hasn't been approved. 2475 if ( empty( $is_approved ) ) { 2476 return false; 2477 } 2478 2479 // Don't record activity if no email address has been included. 2480 if ( empty( $post_type_comment->comment_author_email ) ) { 2481 return false; 2482 } 2483 2484 // Don't record activity if the comment has already been marked as spam. 2485 if ( 'spam' === $is_approved ) { 2486 return false; 2487 } 2488 2489 // Get the user by the comment author email. 2490 $user = get_user_by( 'email', $post_type_comment->comment_author_email ); 2491 2492 // If user isn't registered, don't record activity. 2493 if ( empty( $user ) ) { 2494 return false; 2495 } 2496 2497 // Get the user_id. 2498 $user_id = (int) $user->ID; 2499 2500 // Get blog and post data. 2501 $blog_id = get_current_blog_id(); 2502 2503 // Get the post. 2504 $post_type_comment->post = get_post( $post_type_comment->comment_post_ID ); 2505 2506 if ( ! is_a( $post_type_comment->post, 'WP_Post' ) ) { 2507 return false; 2508 } 2509 2510 /** 2511 * Filters whether to publish activities about the comment regarding the post status 2512 * 2513 * @since 2.5.0 2514 * 2515 * @param bool true to bail, false otherwise. 2516 */ 2517 $is_post_status_not_allowed = (bool) apply_filters( 'bp_activity_post_type_is_post_status_allowed', 'publish' !== $post_type_comment->post->post_status || ! empty( $post_type_comment->post->post_password ) ); 2518 2519 // If this is a password protected post, or not a public post don't record the comment. 2520 if ( $is_post_status_not_allowed ) { 2521 return false; 2522 } 2523 2524 // Set post type. 2525 $post_type = $post_type_comment->post->post_type; 2526 2527 if ( empty( $activity_post_object ) ) { 2528 // Get the post type tracking args. 2529 $activity_post_object = bp_activity_get_post_type_tracking_args( $post_type ); 2530 2531 // Bail if the activity type does not exist. 2532 if ( empty( $activity_post_object->comments_tracking->action_id ) ) { 2533 return false; 2534 } 2535 } 2536 2537 // Set the $activity_comment_object. 2538 $activity_comment_object = $activity_post_object->comments_tracking; 2539 2540 /** 2541 * Filters whether or not to post the activity about the comment. 2542 * 2543 * This is a variable filter, dependent on the post type, 2544 * that lets components or plugins bail early if needed. 2545 * 2546 * @since 2.5.0 2547 * 2548 * @param bool $value Whether or not to continue. 2549 * @param int $blog_id ID of the current site. 2550 * @param int $post_id ID of the current post being commented. 2551 * @param int $user_id ID of the current user. 2552 * @param int $comment_id ID of the current comment being posted. 2553 */ 2554 if ( false === apply_filters( "bp_activity_{$post_type}_pre_comment", true, $blog_id, $post_type_comment->post->ID, $user_id, $comment_id ) ) { 2555 return false; 2556 } 2557 2558 // Is this an update ? 2559 $activity_id = bp_activity_get_activity_id( array( 2560 'user_id' => $user_id, 2561 'component' => $activity_comment_object->component_id, 2562 'type' => $activity_comment_object->action_id, 2563 'item_id' => $blog_id, 2564 'secondary_item_id' => $comment_id, 2565 ) ); 2566 2567 // Record this in activity streams. 2568 $comment_link = get_comment_link( $post_type_comment->comment_ID ); 2569 2570 // Backward compatibility filters for the 'blogs' component. 2571 if ( 'blogs' == $activity_comment_object->component_id ) { 2572 $activity_content = apply_filters_ref_array( 'bp_blogs_activity_new_comment_content', array( $post_type_comment->comment_content, &$post_type_comment, $comment_link ) ); 2573 $activity_primary_link = apply_filters_ref_array( 'bp_blogs_activity_new_comment_primary_link', array( $comment_link, &$post_type_comment ) ); 2574 } else { 2575 $activity_content = $post_type_comment->comment_content; 2576 $activity_primary_link = $comment_link; 2577 } 2578 2579 $activity_args = array( 2580 'id' => $activity_id, 2581 'user_id' => $user_id, 2582 'content' => $activity_content, 2583 'primary_link' => $activity_primary_link, 2584 'component' => $activity_comment_object->component_id, 2585 'recorded_time' => $post_type_comment->comment_date_gmt, 2586 ); 2587 2588 if ( bp_disable_blogforum_comments() ) { 2589 $blog_url = get_home_url( $blog_id ); 2590 $post_url = add_query_arg( 2591 'p', 2592 $post_type_comment->post->ID, 2593 trailingslashit( $blog_url ) 2594 ); 2595 2596 $activity_args['type'] = $activity_comment_object->action_id; 2597 $activity_args['item_id'] = $blog_id; 2598 $activity_args['secondary_item_id'] = $post_type_comment->comment_ID; 2599 2600 if ( ! empty( $activity_args['content'] ) ) { 2601 // Create the excerpt. 2602 $activity_summary = bp_activity_create_summary( $activity_args['content'], $activity_args ); 2603 2604 // Backward compatibility filter for blog comments. 2605 if ( 'blogs' == $activity_post_object->component_id ) { 2606 $activity_args['content'] = apply_filters( 'bp_blogs_record_activity_content', $activity_summary, $activity_args['content'], $activity_args, $post_type ); 2607 } else { 2608 $activity_args['content'] = $activity_summary; 2609 } 2610 } 2611 2612 // Set up the action by using the format functions. 2613 $action_args = array_merge( $activity_args, array( 2614 'post_title' => $post_type_comment->post->post_title, 2615 'post_url' => $post_url, 2616 'blog_url' => $blog_url, 2617 'blog_name' => get_blog_option( $blog_id, 'blogname' ), 2618 ) ); 2619 2620 $activity_args['action'] = call_user_func_array( $activity_comment_object->format_callback, array( '', (object) $action_args ) ); 2621 2622 // Make sure the action is set. 2623 if ( empty( $activity_args['action'] ) ) { 2624 return; 2625 } else { 2626 // Backward compatibility filter for the blogs component. 2627 if ( 'blogs' === $activity_post_object->component_id ) { 2628 $activity_args['action'] = apply_filters( 'bp_blogs_record_activity_action', $activity_args['action'] ); 2629 } 2630 } 2631 2632 $activity_id = bp_activity_add( $activity_args ); 2633 } 2634 2635 /** 2636 * Fires after the publishing of an activity item for a newly published post type post. 2637 * 2638 * @since 2.5.0 2639 * 2640 * @param int $activity_id ID of the newly published activity item. 2641 * @param WP_Comment $post_type_comment Comment object. 2642 * @param array $activity_args Array of activity arguments. 2643 * @param object $activity_post_object the post type tracking args object. 2644 */ 2645 do_action_ref_array( 'bp_activity_post_type_comment', array( &$activity_id, $post_type_comment, $activity_args, $activity_post_object ) ); 2646 2647 return $activity_id; 2648 } 2649 add_action( 'comment_post', 'bp_activity_post_type_comment', 10, 2 ); 2650 add_action( 'edit_comment', 'bp_activity_post_type_comment', 10 ); 2651 2652 /** 2653 * Remove an activity item when a comment about a post type is deleted. 2654 * 2655 * @since 2.5.0 2656 * 2657 * @param int $comment_id ID of the comment. 2658 * @param object|null $activity_post_object The post type tracking args object. 2659 * @return bool True on success. False on error. 2660 */ 2661 function bp_activity_post_type_remove_comment( $comment_id = 0, $activity_post_object = null ) { 2662 if ( empty( $activity_post_object ) ) { 2663 $comment = get_comment( $comment_id ); 2664 if ( ! $comment ) { 2665 return; 2666 } 2667 2668 $post_type = get_post_type( $comment->comment_post_ID ); 2669 if ( ! $post_type ) { 2670 return; 2671 } 2672 2673 // Get the post type tracking args. 2674 $activity_post_object = bp_activity_get_post_type_tracking_args( $post_type ); 2675 2676 // Bail if the activity type does not exist. 2677 if ( empty( $activity_post_object->comments_tracking->action_id ) ) { 2678 return false; 2679 } 2680 } 2681 2682 // Set the $activity_comment_object. 2683 $activity_comment_object = $activity_post_object->comments_tracking; 2684 2685 if ( empty( $activity_comment_object->action_id ) ) { 2686 return false; 2687 } 2688 2689 $deleted = false; 2690 2691 if ( bp_disable_blogforum_comments() ) { 2692 $deleted = bp_activity_delete_by_item_id( array( 2693 'item_id' => get_current_blog_id(), 2694 'secondary_item_id' => $comment_id, 2695 'component' => $activity_comment_object->component_id, 2696 'type' => $activity_comment_object->action_id, 2697 'user_id' => false, 2698 ) ); 2699 } 2700 2701 /** 2702 * Fires after the custom post type comment activity was removed. 2703 * 2704 * @since 2.5.0 2705 * 2706 * @param bool $deleted True if the activity was deleted false otherwise 2707 * @param WP_Comment $comment Comment object. 2708 * @param object $activity_post_object The post type tracking args object. 2709 * @param string $value The post type comment activity type. 2710 */ 2711 do_action( 'bp_activity_post_type_remove_comment', $deleted, $comment_id, $activity_post_object, $activity_comment_object->action_id ); 2712 2713 return $deleted; 2714 } 2715 add_action( 'delete_comment', 'bp_activity_post_type_remove_comment', 10, 1 ); 2716 2717 /** 2718 * Add an activity comment. 2719 * 2720 * @since 1.2.0 2721 * @since 2.5.0 Add a new possible parameter $skip_notification for the array of arguments. 2722 * Add the $primary_link parameter for the array of arguments. 2723 * @since 2.6.0 Added 'error_type' parameter to $args. 2724 * 2725 * @param array|string $args { 2726 * An array of arguments. 2727 * @type int $id Optional. Pass an ID to update an existing comment. 2728 * @type string $content The content of the comment. 2729 * @type int $user_id Optional. The ID of the user making the comment. 2730 * Defaults to the ID of the logged-in user. 2731 * @type int $activity_id The ID of the "root" activity item, ie the oldest 2732 * ancestor of the comment. 2733 * @type int $parent_id Optional. The ID of the parent activity item, ie the item to 2734 * which the comment is an immediate reply. If not provided, 2735 * this value defaults to the $activity_id. 2736 * @type string $primary_link Optional. the primary link for the comment. 2737 * Defaults to an empty string. 2738 * @type bool $skip_notification Optional. false to send a comment notification, false otherwise. 2739 * Defaults to false. 2740 * @type string $error_type Optional. Error type. Either 'bool' or 'wp_error'. Default: 'bool'. 2741 * } 2742 * @return WP_Error|bool|int The ID of the comment on success, otherwise false. 2743 */ 2744 function bp_activity_new_comment( $args = '' ) { 2745 $bp = buddypress(); 2746 $r = bp_parse_args( 2747 $args, 2748 array( 2749 'id' => false, 2750 'content' => false, 2751 'user_id' => bp_loggedin_user_id(), 2752 'activity_id' => false, // ID of the root activity item. 2753 'parent_id' => false, // ID of a parent comment (optional). 2754 'primary_link' => '', 2755 'skip_notification' => false, 2756 'error_type' => 'bool', 2757 ) 2758 ); 2759 2760 // Error type is boolean; need to initialize some variables for backpat. 2761 if ( 'bool' === $r['error_type'] ) { 2762 if ( empty( $bp->activity->errors ) ) { 2763 $bp->activity->errors = array(); 2764 } 2765 } 2766 2767 // Default error message. 2768 $feedback = __( 'There was an error posting your reply. Please try again.', 'buddypress' ); 2769 2770 // Bail if missing necessary data. 2771 if ( empty( $r['content'] ) || empty( $r['user_id'] ) || empty( $r['activity_id'] ) ) { 2772 $error = new WP_Error( 'missing_data', $feedback ); 2773 2774 if ( 'wp_error' === $r['error_type'] ) { 2775 return $error; 2776 2777 // Backpat. 2778 } else { 2779 $bp->activity->errors['new_comment'] = $error; 2780 return false; 2781 } 2782 } 2783 2784 // Maybe set current activity ID as the parent. 2785 if ( empty( $r['parent_id'] ) ) { 2786 $r['parent_id'] = $r['activity_id']; 2787 } 2788 2789 $activity_id = $r['activity_id']; 2790 2791 // Get the parent activity. 2792 $activity = new BP_Activity_Activity( $activity_id ); 2793 2794 // Bail if the parent activity does not exist. 2795 if ( empty( $activity->date_recorded ) ) { 2796 $error = new WP_Error( 'missing_activity', __( 'The item you were replying to no longer exists.', 'buddypress' ) ); 2797 2798 if ( 'wp_error' === $r['error_type'] ) { 2799 return $error; 2800 2801 // Backpat. 2802 } else { 2803 $bp->activity->errors['new_comment'] = $error; 2804 return false; 2805 } 2806 2807 } 2808 2809 // Check to see if the parent activity is hidden, and if so, hide this comment publicly. 2810 $is_hidden = $activity->hide_sitewide ? 1 : 0; 2811 2812 /** 2813 * Filters the content of a new comment. 2814 * 2815 * @since 1.2.0 2816 * @since 3.0.0 Added $context parameter to disambiguate from bp_get_activity_comment_content(). 2817 * 2818 * @param string $r Content for the newly posted comment. 2819 * @param string $context This filter's context ("new"). 2820 */ 2821 $comment_content = apply_filters( 'bp_activity_comment_content', $r['content'], 'new' ); 2822 2823 // Insert the activity comment. 2824 $comment_id = bp_activity_add( array( 2825 'id' => $r['id'], 2826 'content' => $comment_content, 2827 'component' => $bp->activity->id, 2828 'type' => 'activity_comment', 2829 'primary_link' => $r['primary_link'], 2830 'user_id' => $r['user_id'], 2831 'item_id' => $activity_id, 2832 'secondary_item_id' => $r['parent_id'], 2833 'hide_sitewide' => $is_hidden, 2834 'error_type' => $r['error_type'] 2835 ) ); 2836 2837 // Bail on failure. 2838 if ( false === $comment_id || is_wp_error( $comment_id ) ) { 2839 return $comment_id; 2840 } 2841 2842 // Comment caches are stored only with the top-level item. 2843 wp_cache_delete( $activity_id, 'bp_activity_comments' ); 2844 2845 // Walk the tree to clear caches for all parent items. 2846 $clear_id = $r['parent_id']; 2847 while ( $clear_id != $activity_id ) { 2848 $clear_object = new BP_Activity_Activity( $clear_id ); 2849 wp_cache_delete( $clear_id, 'bp_activity' ); 2850 $clear_id = intval( $clear_object->secondary_item_id ); 2851 } 2852 wp_cache_delete( $activity_id, 'bp_activity' ); 2853 2854 if ( empty( $r[ 'skip_notification' ] ) ) { 2855 /** 2856 * Fires near the end of an activity comment posting, before the returning of the comment ID. 2857 * Sends a notification to the user @see bp_activity_new_comment_notification_helper(). 2858 * 2859 * @since 1.2.0 2860 * 2861 * @param int $comment_id ID of the newly posted activity comment. 2862 * @param array $r Array of parsed comment arguments. 2863 * @param BP_Activity_Activity $activity Activity item being commented on. 2864 */ 2865 do_action( 'bp_activity_comment_posted', $comment_id, $r, $activity ); 2866 } else { 2867 /** 2868 * Fires near the end of an activity comment posting, before the returning of the comment ID. 2869 * without sending a notification to the user 2870 * 2871 * @since 2.5.0 2872 * 2873 * @param int $comment_id ID of the newly posted activity comment. 2874 * @param array $r Array of parsed comment arguments. 2875 * @param BP_Activity_Activity $activity Activity item being commented on. 2876 */ 2877 do_action( 'bp_activity_comment_posted_notification_skipped', $comment_id, $r, $activity ); 2878 } 2879 2880 if ( empty( $comment_id ) ) { 2881 $error = new WP_Error( 'comment_failed', $feedback ); 2882 2883 if ( 'wp_error' === $r['error_type'] ) { 2884 return $error; 2885 2886 // Backpat. 2887 } else { 2888 $bp->activity->errors['new_comment'] = $error; 2889 } 2890 } 2891 2892 return $comment_id; 2893 } 2894 2895 /** 2896 * Fetch the activity_id for an existing activity entry in the DB. 2897 * 2898 * @since 1.2.0 2899 * 2900 * @see BP_Activity_Activity::get() For more information on accepted arguments. 2901 * 2902 * @param array|string $args See BP_Activity_Activity::get() for description. 2903 * @return int $activity_id The ID of the activity item found. 2904 */ 2905 function bp_activity_get_activity_id( $args = '' ) { 2906 2907 $r = bp_parse_args( 2908 $args, 2909 array( 2910 'user_id' => false, 2911 'component' => false, 2912 'type' => false, 2913 'item_id' => false, 2914 'secondary_item_id' => false, 2915 'action' => false, 2916 'content' => false, 2917 'date_recorded' => false, 2918 ) 2919 ); 2920 2921 /** 2922 * Filters the activity ID being requested. 2923 * 2924 * @since 1.2.0 2925 * @since 2.5.0 Added the `$r` and `$args` parameters. 2926 * 2927 * @param BP_Activity_Activity $value ID returned by BP_Activity_Activity get_id() method with provided arguments. 2928 * @param array $r Parsed function arguments. 2929 * @param array $args Arguments passed to the function. 2930 */ 2931 return apply_filters( 'bp_activity_get_activity_id', BP_Activity_Activity::get_id( $r ), $r, $args ); 2932 } 2933 2934 /** 2935 * Delete activity item(s). 2936 * 2937 * If you're looking to hook into one action that provides the ID(s) of 2938 * the activity/activities deleted, then use: 2939 * 2940 * add_action( 'bp_activity_deleted_activities', 'my_function' ); 2941 * 2942 * The action passes one parameter that is a single activity ID or an 2943 * array of activity IDs depending on the number deleted. 2944 * 2945 * If you are deleting an activity comment please use bp_activity_delete_comment(); 2946 * 2947 * @since 1.0.0 2948 * 2949 * @see BP_Activity_Activity::get() For more information on accepted arguments. 2950 * 2951 * @param array|string $args To delete specific activity items, use 2952 * $args = array( 'id' => $ids ); Otherwise, to use 2953 * filters for item deletion, the argument format is 2954 * the same as BP_Activity_Activity::get(). 2955 * See that method for a description. 2956 * @return bool True on success, false on failure. 2957 */ 2958 function bp_activity_delete( $args = '' ) { 2959 2960 // Pass one or more the of following variables to delete by those variables. 2961 $args = bp_parse_args( 2962 $args, 2963 array( 2964 'id' => false, 2965 'action' => false, 2966 'content' => false, 2967 'component' => false, 2968 'type' => false, 2969 'primary_link' => false, 2970 'user_id' => false, 2971 'item_id' => false, 2972 'secondary_item_id' => false, 2973 'date_recorded' => false, 2974 'hide_sitewide' => false, 2975 ) 2976 ); 2977 2978 /** 2979 * Fires before an activity item proceeds to be deleted. 2980 * 2981 * @since 1.5.0 2982 * 2983 * @param array $args Array of arguments to be used with the activity deletion. 2984 */ 2985 do_action( 'bp_before_activity_delete', $args ); 2986 2987 // Adjust the new mention count of any mentioned member. 2988 bp_activity_adjust_mention_count( $args['id'], 'delete' ); 2989 2990 $activity_ids_deleted = BP_Activity_Activity::delete( $args ); 2991 if ( empty( $activity_ids_deleted ) ) { 2992 return false; 2993 } 2994 2995 // Check if the user's latest update has been deleted. 2996 $user_id = empty( $args['user_id'] ) 2997 ? bp_loggedin_user_id() 2998 : $args['user_id']; 2999 3000 $latest_update = bp_get_user_meta( $user_id, 'bp_latest_update', true ); 3001 if ( !empty( $latest_update ) ) { 3002 if ( in_array( (int) $latest_update['id'], (array) $activity_ids_deleted ) ) { 3003 bp_delete_user_meta( $user_id, 'bp_latest_update' ); 3004 } 3005 } 3006 3007 /** 3008 * Fires after the activity item has been deleted. 3009 * 3010 * @since 1.0.0 3011 * 3012 * @param array $args Array of arguments used with the activity deletion. 3013 */ 3014 do_action( 'bp_activity_delete', $args ); 3015 3016 /** 3017 * Fires after the activity item has been deleted. 3018 * 3019 * @since 1.2.0 3020 * 3021 * @param array $activity_ids_deleted Array of affected activity item IDs. 3022 */ 3023 do_action( 'bp_activity_deleted_activities', $activity_ids_deleted ); 3024 3025 wp_cache_delete( 'bp_activity_sitewide_front', 'bp' ); 3026 3027 return true; 3028 } 3029 3030 /** 3031 * Delete an activity item by activity id. 3032 * 3033 * You should use bp_activity_delete() instead. 3034 * 3035 * @since 1.1.0 3036 * @deprecated 1.2.0 3037 * 3038 * @param array|string $args See BP_Activity_Activity::get for a 3039 * description of accepted arguments. 3040 * @return bool True on success, false on failure. 3041 */ 3042 function bp_activity_delete_by_item_id( $args = '' ) { 3043 3044 $r = bp_parse_args( 3045 $args, 3046 array( 3047 'item_id' => false, 3048 'component' => false, 3049 'type' => false, 3050 'user_id' => false, 3051 'secondary_item_id' => false, 3052 ) 3053 ); 3054 3055 return bp_activity_delete( $r ); 3056 } 3057 3058 /** 3059 * Delete an activity item by activity id. 3060 * 3061 * @since 1.1.0 3062 * 3063 * 3064 * @param int $activity_id ID of the activity item to be deleted. 3065 * @return bool True on success, false on failure. 3066 */ 3067 function bp_activity_delete_by_activity_id( $activity_id ) { 3068 return bp_activity_delete( array( 'id' => $activity_id ) ); 3069 } 3070 3071 /** 3072 * Delete an activity item by its content. 3073 * 3074 * You should use bp_activity_delete() instead. 3075 * 3076 * @since 1.1.0 3077 * @deprecated 1.2.0 3078 * 3079 * 3080 * @param int $user_id The user id. 3081 * @param string $content The activity id. 3082 * @param string $component The activity component. 3083 * @param string $type The activity type. 3084 * @return bool True on success, false on failure. 3085 */ 3086 function bp_activity_delete_by_content( $user_id, $content, $component, $type ) { 3087 return bp_activity_delete( array( 3088 'user_id' => $user_id, 3089 'content' => $content, 3090 'component' => $component, 3091 'type' => $type 3092 ) ); 3093 } 3094 3095 /** 3096 * Delete a user's activity for a component. 3097 * 3098 * You should use bp_activity_delete() instead. 3099 * 3100 * @since 1.1.0 3101 * @deprecated 1.2.0 3102 * 3103 * 3104 * @param int $user_id The user id. 3105 * @param string $component The activity component. 3106 * @return bool True on success, false on failure. 3107 */ 3108 function bp_activity_delete_for_user_by_component( $user_id, $component ) { 3109 return bp_activity_delete( array( 3110 'user_id' => $user_id, 3111 'component' => $component 3112 ) ); 3113 } 3114 3115 /** 3116 * Delete an activity comment. 3117 * 3118 * @since 1.2.0 3119 * 3120 * @todo Why is an activity id required? We could look this up. 3121 * @todo Why do we encourage users to call this function directly? We could just 3122 * as easily examine the activity type in bp_activity_delete() and then 3123 * call this function with the proper arguments if necessary. 3124 * 3125 * @param int $activity_id The ID of the "root" activity, ie the comment's 3126 * oldest ancestor. 3127 * @param int $comment_id The ID of the comment to be deleted. 3128 * @return bool True on success, false on failure. 3129 */ 3130 function bp_activity_delete_comment( $activity_id, $comment_id ) { 3131 $deleted = false; 3132 3133 /** 3134 * Filters whether BuddyPress should delete an activity comment or not. 3135 * 3136 * You may want to hook into this filter if you want to override this function and 3137 * handle the deletion of child comments differently. Make sure you return false. 3138 * 3139 * @since 1.2.0 3140 * @since 2.5.0 Add the deleted parameter (passed by reference) 3141 * 3142 * @param bool $value Whether BuddyPress should continue or not. 3143 * @param int $activity_id ID of the root activity item being deleted. 3144 * @param int $comment_id ID of the comment being deleted. 3145 * @param bool $deleted Whether the activity comment has been deleted or not. 3146 */ 3147 if ( ! apply_filters_ref_array( 'bp_activity_delete_comment_pre', array( true, $activity_id, $comment_id, &$deleted ) ) ) { 3148 return $deleted; 3149 } 3150 3151 // Check if comment still exists. 3152 $comment = new BP_Activity_Activity( $comment_id ); 3153 if ( empty( $comment->id ) ) { 3154 return false; 3155 } 3156 3157 // Delete any children of this comment. 3158 bp_activity_delete_children( $activity_id, $comment_id ); 3159 3160 // Delete the actual comment. 3161 if ( ! bp_activity_delete( array( 'id' => $comment_id, 'type' => 'activity_comment' ) ) ) { 3162 return false; 3163 } else { 3164 $deleted = true; 3165 } 3166 3167 // Purge comment cache for the root activity update. 3168 wp_cache_delete( $activity_id, 'bp_activity_comments' ); 3169 3170 // Recalculate the comment tree. 3171 BP_Activity_Activity::rebuild_activity_comment_tree( $activity_id ); 3172 3173 /** 3174 * Fires at the end of the deletion of an activity comment, before returning success. 3175 * 3176 * @since 1.2.0 3177 * 3178 * @param int $activity_id ID of the activity that has had a comment deleted from. 3179 * @param int $comment_id ID of the comment that was deleted. 3180 */ 3181 do_action( 'bp_activity_delete_comment', $activity_id, $comment_id ); 3182 3183 return $deleted; 3184 } 3185 3186 /** 3187 * Delete an activity comment's children. 3188 * 3189 * @since 1.2.0 3190 * 3191 * 3192 * @param int $activity_id The ID of the "root" activity, ie the 3193 * comment's oldest ancestor. 3194 * @param int $comment_id The ID of the comment to be deleted. 3195 */ 3196 function bp_activity_delete_children( $activity_id, $comment_id ) { 3197 // Check if comment still exists. 3198 $comment = new BP_Activity_Activity( $comment_id ); 3199 if ( empty( $comment->id ) ) { 3200 return; 3201 } 3202 3203 // Get activity children to delete. 3204 $children = BP_Activity_Activity::get_child_comments( $comment_id ); 3205 3206 // Recursively delete all children of this comment. 3207 if ( ! empty( $children ) ) { 3208 foreach( (array) $children as $child ) { 3209 bp_activity_delete_children( $activity_id, $child->id ); 3210 } 3211 } 3212 3213 // Delete the comment itself. 3214 bp_activity_delete( array( 3215 'secondary_item_id' => $comment_id, 3216 'type' => 'activity_comment', 3217 'item_id' => $activity_id 3218 ) ); 3219 } 3220 3221 /** 3222 * Get the permalink for a single activity item. 3223 * 3224 * When only the $activity_id param is passed, BP has to instantiate a new 3225 * BP_Activity_Activity object. To save yourself some processing overhead, 3226 * be sure to pass the full $activity_obj parameter as well, if you already 3227 * have it available. 3228 * 3229 * @since 1.2.0 3230 * 3231 * @param int $activity_id The unique id of the activity object. 3232 * @param object|bool $activity_obj Optional. The activity object. 3233 * @return string $link Permalink for the activity item. 3234 */ 3235 function bp_activity_get_permalink( $activity_id, $activity_obj = false ) { 3236 $bp = buddypress(); 3237 3238 if ( empty( $activity_obj ) ) { 3239 $activity_obj = new BP_Activity_Activity( $activity_id ); 3240 } 3241 3242 if ( isset( $activity_obj->current_comment ) ) { 3243 $activity_obj = $activity_obj->current_comment; 3244 } 3245 3246 $use_primary_links = array( 3247 'new_blog_post', 3248 'new_blog_comment', 3249 'new_forum_topic', 3250 'new_forum_post', 3251 ); 3252 3253 if ( ! empty( $bp->activity->track ) ) { 3254 $use_primary_links = array_merge( $use_primary_links, array_keys( $bp->activity->track ) ); 3255 } 3256 3257 if ( false !== array_search( $activity_obj->type, $use_primary_links ) ) { 3258 $link = $activity_obj->primary_link; 3259 } else { 3260 if ( 'activity_comment' == $activity_obj->type ) { 3261 $link = bp_get_root_domain() . '/' . bp_get_activity_root_slug() . '/p/' . $activity_obj->item_id . '/#acomment-' . $activity_obj->id; 3262 } else { 3263 $link = bp_get_root_domain() . '/' . bp_get_activity_root_slug() . '/p/' . $activity_obj->id . '/'; 3264 } 3265 } 3266 3267 /** 3268 * Filters the activity permalink for the specified activity item. 3269 * 3270 * @since 1.2.0 3271 * 3272 * @param array $array Array holding activity permalink and activity item object. 3273 */ 3274 return apply_filters_ref_array( 'bp_activity_get_permalink', array( $link, &$activity_obj ) ); 3275 } 3276 3277 /** 3278 * Can a user see a particular activity item? 3279 * 3280 * @since 3.0.0 3281 * 3282 * @param BP_Activity_Activity $activity Activity object. 3283 * @param integer $user_id User ID. 3284 * @return boolean True on success, false on failure. 3285 */ 3286 function bp_activity_user_can_read( $activity, $user_id = 0 ) { 3287 $retval = true; 3288 3289 // Fallback. 3290 if ( empty( $user_id ) ) { 3291 $user_id = bp_loggedin_user_id(); 3292 } 3293 3294 // If activity is from a group, do extra cap checks. 3295 if ( bp_is_active( 'groups' ) && buddypress()->groups->id === $activity->component ) { 3296 // Check to see if the user has access to the activity's parent group. 3297 $group = groups_get_group( $activity->item_id ); 3298 if ( $group ) { 3299 // For logged-in user, we can check against the 'user_has_access' prop. 3300 if ( bp_loggedin_user_id() === $user_id ) { 3301 $retval = $group->user_has_access; 3302 3303 // Manually check status. 3304 } elseif ( 'private' === $group->status || 'hidden' === $group->status ) { 3305 // Only group members that are not banned can view. 3306 if ( ! groups_is_user_member( $user_id, $activity->item_id ) || groups_is_user_banned( $user_id, $activity->item_id ) ) { 3307 $retval = false; 3308 } 3309 } 3310 } 3311 } 3312 3313 // Spammed items are not visible to the public. 3314 if ( $activity->is_spam ) { 3315 $retval = false; 3316 } 3317 3318 // Site moderators can view anything. 3319 if ( bp_current_user_can( 'bp_moderate' ) ) { 3320 $retval = true; 3321 } 3322 3323 /** 3324 * Filters whether the current user has access to an activity item. 3325 * 3326 * @since 3.0.0 3327 * 3328 * @param bool $retval Return value. 3329 * @param int $user_id Current user ID. 3330 * @param BP_Activity_Activity $activity Activity object. 3331 */ 3332 return apply_filters( 'bp_activity_user_can_read', $retval, $user_id, $activity ); 3333 } 3334 3335 /** 3336 * Hide a user's activity. 3337 * 3338 * @since 1.2.0 3339 * 3340 * @param int $user_id The ID of the user whose activity is being hidden. 3341 * @return bool True on success, false on failure. 3342 */ 3343 function bp_activity_hide_user_activity( $user_id ) { 3344 return BP_Activity_Activity::hide_all_for_user( $user_id ); 3345 } 3346 3347 /** 3348 * Take content, remove images, and replace them with a single thumbnail image. 3349 * 3350 * The format of items in the activity stream is such that we do not want to 3351 * allow an arbitrary number of arbitrarily large images to be rendered. 3352 * However, the activity stream is built to elegantly display a single 3353 * thumbnail corresponding to the activity comment. This function looks 3354 * through the content, grabs the first image and converts it to a thumbnail, 3355 * and removes the rest of the images from the string. 3356 * 3357 * As of BuddyPress 2.3, this function is no longer in use. 3358 * 3359 * @since 1.2.0 3360 * 3361 * @param string $content The content of the activity item. 3362 * @param string|bool $link Optional. The unescaped URL that the image should link 3363 * to. If absent, the image will not be a link. 3364 * @param array|bool $args Optional. The args passed to the activity 3365 * creation function (eg bp_blogs_record_activity()). 3366 * @return string $content The content with images stripped and replaced with a 3367 * single thumb. 3368 */ 3369 function bp_activity_thumbnail_content_images( $content, $link = false, $args = false ) { 3370 3371 preg_match_all( '/<img[^>]*>/Ui', $content, $matches ); 3372 3373 // Remove <img> tags. Also remove caption shortcodes and caption text if present. 3374 $content = preg_replace('|(\[caption(.*?)\])?<img[^>]*>([^\[\[]*\[\/caption\])?|', '', $content ); 3375 3376 if ( !empty( $matches ) && !empty( $matches[0] ) ) { 3377 3378 // Get the SRC value. 3379 preg_match( '/<img.*?(src\=[\'|"]{0,1}.*?[\'|"]{0,1})[\s|>]{1}/i', $matches[0][0], $src ); 3380 3381 // Get the width and height. 3382 preg_match( '/<img.*?(height\=[\'|"]{0,1}.*?[\'|"]{0,1})[\s|>]{1}/i', $matches[0][0], $height ); 3383 preg_match( '/<img.*?(width\=[\'|"]{0,1}.*?[\'|"]{0,1})[\s|>]{1}/i', $matches[0][0], $width ); 3384 3385 if ( ! empty( $src ) ) { 3386 $src = substr( substr( str_replace( 'src=', '', $src[1] ), 0, -1 ), 1 ); 3387 3388 if ( isset( $width[1] ) ) { 3389 $width = substr( substr( str_replace( 'width=', '', $width[1] ), 0, -1 ), 1 ); 3390 } 3391 3392 if ( isset( $height[1] ) ) { 3393 $height = substr( substr( str_replace( 'height=', '', $height[1] ), 0, -1 ), 1 ); 3394 } 3395 3396 if ( empty( $width ) || empty( $height ) ) { 3397 $width = 100; 3398 $height = 100; 3399 } 3400 3401 $ratio = (int) $width / (int) $height; 3402 $new_height = (int) $height >= 100 ? 100 : $height; 3403 $new_width = $new_height * $ratio; 3404 $image = '<img src="' . esc_url( $src ) . '" width="' . absint( $new_width ) . '" height="' . absint( $new_height ) . '" alt="' . __( 'Thumbnail', 'buddypress' ) . '" class="align-left thumbnail" />'; 3405 3406 if ( !empty( $link ) ) { 3407 $image = '<a href="' . esc_url( $link ) . '">' . $image . '</a>'; 3408 } 3409 3410 $content = $image . $content; 3411 } 3412 } 3413 3414 /** 3415 * Filters the activity content that had a thumbnail replace images. 3416 * 3417 * @since 1.2.0 3418 * 3419 * @param string $content Activity content that had images replaced in. 3420 * @param array $matches Array of all image tags found in the posted content. 3421 * @param array $args Arguments passed into function creating the activity update. 3422 */ 3423 return apply_filters( 'bp_activity_thumbnail_content_images', $content, $matches, $args ); 3424 } 3425 3426 /** 3427 * Gets the excerpt length for activity items. 3428 * 3429 * @since 2.8.0 3430 * 3431 * @return int Character length for activity excerpts. 3432 */ 3433 function bp_activity_get_excerpt_length() { 3434 /** 3435 * Filters the excerpt length for the activity excerpt. 3436 * 3437 * @since 1.5.0 3438 * 3439 * @param int Character length for activity excerpts. 3440 */ 3441 return (int) apply_filters( 'bp_activity_excerpt_length', 358 ); 3442 } 3443 3444 /** 3445 * Create a rich summary of an activity item for the activity stream. 3446 * 3447 * More than just a simple excerpt, the summary could contain oEmbeds and other types of media. 3448 * Currently, it's only used for blog post items, but it will probably be used for all types of 3449 * activity in the future. 3450 * 3451 * @since 2.3.0 3452 * 3453 * @param string $content The content of the activity item. 3454 * @param array $activity The data passed to bp_activity_add() or the values 3455 * from an Activity obj. 3456 * @return string $summary 3457 */ 3458 function bp_activity_create_summary( $content, $activity ) { 3459 $args = array( 3460 'width' => isset( $GLOBALS['content_width'] ) ? (int) $GLOBALS['content_width'] : 'medium', 3461 ); 3462 3463 // Get the WP_Post object if this activity type is a blog post. 3464 if ( $activity['type'] === 'new_blog_post' ) { 3465 $content = get_post( $activity['secondary_item_id'] ); 3466 } 3467 3468 /** 3469 * Filter the class name of the media extractor when creating an Activity summary. 3470 * 3471 * Use this filter to change the media extractor used to extract media info for the activity item. 3472 * 3473 * @since 2.3.0 3474 * 3475 * @param string $extractor Class name. 3476 * @param string $content The content of the activity item. 3477 * @param array $activity The data passed to bp_activity_add() or the values from an Activity obj. 3478 */ 3479 $extractor = apply_filters( 'bp_activity_create_summary_extractor_class', 'BP_Media_Extractor', $content, $activity ); 3480 $extractor = new $extractor; 3481 3482 /** 3483 * Filter the arguments passed to the media extractor when creating an Activity summary. 3484 * 3485 * @since 2.3.0 3486 * 3487 * @param array $args Array of bespoke data for the media extractor. 3488 * @param string $content The content of the activity item. 3489 * @param array $activity The data passed to bp_activity_add() or the values from an Activity obj. 3490 * @param BP_Media_Extractor $extractor The media extractor object. 3491 */ 3492 $args = apply_filters( 'bp_activity_create_summary_extractor_args', $args, $content, $activity, $extractor ); 3493 3494 3495 // Extract media information from the $content. 3496 $media = $extractor->extract( $content, BP_Media_Extractor::ALL, $args ); 3497 3498 // If we converted $content to an object earlier, flip it back to a string. 3499 if ( is_a( $content, 'WP_Post' ) ) { 3500 $content = $content->post_content; 3501 } 3502 3503 $para_count = substr_count( strtolower( wpautop( $content ) ), '<p>' ); 3504 $has_audio = ! empty( $media['has']['audio'] ) && $media['has']['audio']; 3505 $has_videos = ! empty( $media['has']['videos'] ) && $media['has']['videos']; 3506 $has_feat_image = ! empty( $media['has']['featured_images'] ) && $media['has']['featured_images']; 3507 $has_galleries = ! empty( $media['has']['galleries'] ) && $media['has']['galleries']; 3508 $has_images = ! empty( $media['has']['images'] ) && $media['has']['images']; 3509 $has_embeds = false; 3510 3511 // Embeds must be subtracted from the paragraph count. 3512 if ( ! empty( $media['has']['embeds'] ) ) { 3513 $has_embeds = $media['has']['embeds'] > 0; 3514 $para_count -= $media['has']['embeds']; 3515 } 3516 3517 $extracted_media = array(); 3518 $use_media_type = ''; 3519 $image_source = ''; 3520 3521 // If it's a short article and there's an embed/audio/video, use it. 3522 if ( $para_count <= 3 ) { 3523 if ( $has_embeds ) { 3524 $use_media_type = 'embeds'; 3525 } elseif ( $has_audio ) { 3526 $use_media_type = 'audio'; 3527 } elseif ( $has_videos ) { 3528 $use_media_type = 'videos'; 3529 } 3530 } 3531 3532 // If not, or in any other situation, try to use an image. 3533 if ( ! $use_media_type && $has_images ) { 3534 $use_media_type = 'images'; 3535 $image_source = 'html'; 3536 3537 // Featured Image > Galleries > inline <img>. 3538 if ( $has_feat_image ) { 3539 $image_source = 'featured_images'; 3540 3541 } elseif ( $has_galleries ) { 3542 $image_source = 'galleries'; 3543 } 3544 } 3545 3546 // Extract an item from the $media results. 3547 if ( $use_media_type ) { 3548 if ( $use_media_type === 'images' ) { 3549 $extracted_media = wp_list_filter( $media[ $use_media_type ], array( 'source' => $image_source ) ); 3550 $extracted_media = array_shift( $extracted_media ); 3551 } else { 3552 $extracted_media = array_shift( $media[ $use_media_type ] ); 3553 } 3554 3555 /** 3556 * Filter the results of the media extractor when creating an Activity summary. 3557 * 3558 * @since 2.3.0 3559 * 3560 * @param array $extracted_media Extracted media item. See {@link BP_Media_Extractor::extract()} for format. 3561 * @param string $content Content of the activity item. 3562 * @param array $activity The data passed to bp_activity_add() or the values from an Activity obj. 3563 * @param array $media All results from the media extraction. 3564 * See {@link BP_Media_Extractor::extract()} for format. 3565 * @param string $use_media_type The kind of media item that was preferentially extracted. 3566 * @param string $image_source If $use_media_type was "images", the preferential source of the image. 3567 * Otherwise empty. 3568 */ 3569 $extracted_media = apply_filters( 3570 'bp_activity_create_summary_extractor_result', 3571 $extracted_media, 3572 $content, 3573 $activity, 3574 $media, 3575 $use_media_type, 3576 $image_source 3577 ); 3578 } 3579 3580 // Generate a text excerpt for this activity item (and remove any oEmbeds URLs). 3581 $summary = bp_create_excerpt( html_entity_decode( $content ), 225, array( 3582 'html' => false, 3583 'filter_shortcodes' => true, 3584 'strip_tags' => true, 3585 'remove_links' => true 3586 ) ); 3587 3588 if ( $use_media_type === 'embeds' ) { 3589 $summary .= PHP_EOL . PHP_EOL . $extracted_media['url']; 3590 } elseif ( $use_media_type === 'images' ) { 3591 $extracted_media_url = isset( $extracted_media['url'] ) ? $extracted_media['url'] : ''; 3592 $summary .= sprintf( ' <img src="%s">', esc_url( $extracted_media_url ) ); 3593 } elseif ( in_array( $use_media_type, array( 'audio', 'videos' ), true ) ) { 3594 $summary .= PHP_EOL . PHP_EOL . $extracted_media['original']; // Full shortcode. 3595 } 3596 3597 /** 3598 * Filters the newly-generated summary for the activity item. 3599 * 3600 * @since 2.3.0 3601 * 3602 * @param string $summary Activity summary HTML. 3603 * @param string $content Content of the activity item. 3604 * @param array $activity The data passed to bp_activity_add() or the values from an Activity obj. 3605 * @param array $extracted_media Media item extracted. See {@link BP_Media_Extractor::extract()} for format. 3606 */ 3607 return apply_filters( 'bp_activity_create_summary', $summary, $content, $activity, $extracted_media ); 3608 } 3609 3610 /** 3611 * Fetch whether the current user is allowed to mark items as spam. 3612 * 3613 * @since 1.6.0 3614 * 3615 * @return bool True if user is allowed to mark activity items as spam. 3616 */ 3617 function bp_activity_user_can_mark_spam() { 3618 3619 /** 3620 * Filters whether the current user should be able to mark items as spam. 3621 * 3622 * @since 1.6.0 3623 * 3624 * @param bool $moderate Whether or not the current user has bp_moderate capability. 3625 */ 3626 return apply_filters( 'bp_activity_user_can_mark_spam', bp_current_user_can( 'bp_moderate' ) ); 3627 } 3628 3629 /** 3630 * Mark an activity item as spam. 3631 * 3632 * @since 1.6.0 3633 * 3634 * @todo We should probably save $source to activity meta. 3635 * 3636 * @param BP_Activity_Activity $activity The activity item to be spammed. 3637 * @param string $source Optional. Default is "by_a_person" (ie, a person has 3638 * manually marked the activity as spam). BP core also 3639 * accepts 'by_akismet'. 3640 */ 3641 function bp_activity_mark_as_spam( &$activity, $source = 'by_a_person' ) { 3642 $bp = buddypress(); 3643 3644 $activity->is_spam = 1; 3645 3646 // Clear the activity stream first page cache. 3647 wp_cache_delete( 'bp_activity_sitewide_front', 'bp' ); 3648 3649 if ( 'activity_comment' === $activity->type ) { 3650 $activity_id = $activity->item_id; 3651 } else { 3652 $activity_id = $activity->id; 3653 } 3654 3655 // Clear the activity comment cache for this activity item. 3656 wp_cache_delete( $activity_id, 'bp_activity_comments' ); 3657 3658 // If Akismet is active, and this was a manual spam/ham request, stop Akismet checking the activity. 3659 if ( 'by_a_person' == $source && !empty( $bp->activity->akismet ) ) { 3660 remove_action( 'bp_activity_before_save', array( $bp->activity->akismet, 'check_activity' ), 4 ); 3661 3662 // Build data package for Akismet. 3663 $activity_data = BP_Akismet::build_akismet_data_package( $activity ); 3664 3665 // Tell Akismet this is spam. 3666 $activity_data = $bp->activity->akismet->send_akismet_request( $activity_data, 'submit', 'spam' ); 3667 3668 // Update meta. 3669 add_action( 'bp_activity_after_save', array( $bp->activity->akismet, 'update_activity_spam_meta' ), 1, 1 ); 3670 } 3671 3672 /** 3673 * Fires at the end of the process to mark an activity item as spam. 3674 * 3675 * @since 1.6.0 3676 * 3677 * @param BP_Activity_Activity $activity Activity item being marked as spam. 3678 * @param string $source Source of determination of spam status. For example 3679 * "by_a_person" or "by_akismet". 3680 */ 3681 do_action( 'bp_activity_mark_as_spam', $activity, $source ); 3682 } 3683 3684 /** 3685 * Mark an activity item as ham. 3686 * 3687 * @since 1.6.0 3688 * 3689 * @param BP_Activity_Activity $activity The activity item to be hammed. Passed by reference. 3690 * @param string $source Optional. Default is "by_a_person" (ie, a person has 3691 * manually marked the activity as spam). BP core also accepts 3692 * 'by_akismet'. 3693 */ 3694 function bp_activity_mark_as_ham( &$activity, $source = 'by_a_person' ) { 3695 $bp = buddypress(); 3696 3697 $activity->is_spam = 0; 3698 3699 // Clear the activity stream first page cache. 3700 wp_cache_delete( 'bp_activity_sitewide_front', 'bp' ); 3701 3702 if ( 'activity_comment' === $activity->type ) { 3703 $activity_id = $activity->item_id; 3704 } else { 3705 $activity_id = $activity->id; 3706 } 3707 3708 // Clear the activity comment cache for this activity item. 3709 wp_cache_delete( $activity_id, 'bp_activity_comments' ); 3710 3711 // If Akismet is active, and this was a manual spam/ham request, stop Akismet checking the activity. 3712 if ( 'by_a_person' == $source && !empty( $bp->activity->akismet ) ) { 3713 remove_action( 'bp_activity_before_save', array( $bp->activity->akismet, 'check_activity' ), 4 ); 3714 3715 // Build data package for Akismet. 3716 $activity_data = BP_Akismet::build_akismet_data_package( $activity ); 3717 3718 // Tell Akismet this is spam. 3719 $activity_data = $bp->activity->akismet->send_akismet_request( $activity_data, 'submit', 'ham' ); 3720 3721 // Update meta. 3722 add_action( 'bp_activity_after_save', array( $bp->activity->akismet, 'update_activity_ham_meta' ), 1, 1 ); 3723 } 3724 3725 /** 3726 * Fires at the end of the process to mark an activity item as ham. 3727 * 3728 * @since 1.6.0 3729 * 3730 * @param BP_Activity_Activity $activity Activity item being marked as ham. 3731 * @param string $source Source of determination of ham status. For example 3732 * "by_a_person" or "by_akismet". 3733 */ 3734 do_action( 'bp_activity_mark_as_ham', $activity, $source ); 3735 } 3736 3737 /* Emails *********************************************************************/ 3738 3739 /** 3740 * Send email and BP notifications when a user is mentioned in an update. 3741 * 3742 * @since 1.2.0 3743 * 3744 * @param int $activity_id The ID of the activity update. 3745 * @param int $receiver_user_id The ID of the user who is receiving the update. 3746 */ 3747 function bp_activity_at_message_notification( $activity_id, $receiver_user_id ) { 3748 $notifications = BP_Core_Notification::get_all_for_user( $receiver_user_id, 'all' ); 3749 3750 // Don't leave multiple notifications for the same activity item. 3751 foreach( $notifications as $notification ) { 3752 if ( $activity_id == $notification->item_id ) { 3753 return; 3754 } 3755 } 3756 3757 $activity = new BP_Activity_Activity( $activity_id ); 3758 $email_type = 'activity-at-message'; 3759 $group_name = ''; 3760 $message_link = bp_activity_get_permalink( $activity_id ); 3761 $poster_name = bp_core_get_user_displayname( $activity->user_id ); 3762 3763 remove_filter( 'bp_get_activity_content_body', 'convert_smilies' ); 3764 remove_filter( 'bp_get_activity_content_body', 'wpautop' ); 3765 remove_filter( 'bp_get_activity_content_body', 'bp_activity_truncate_entry', 5 ); 3766 3767 /** This filter is documented in bp-activity/bp-activity-template.php */ 3768 $content = apply_filters_ref_array( 'bp_get_activity_content_body', array( $activity->content, &$activity ) ); 3769 3770 add_filter( 'bp_get_activity_content_body', 'convert_smilies' ); 3771 add_filter( 'bp_get_activity_content_body', 'wpautop' ); 3772 add_filter( 'bp_get_activity_content_body', 'bp_activity_truncate_entry', 5 ); 3773 3774 // Now email the user with the contents of the message (if they have enabled email notifications). 3775 if ( 'no' != bp_get_user_meta( $receiver_user_id, 'notification_activity_new_mention', true ) ) { 3776 if ( bp_is_active( 'groups' ) && bp_is_group() ) { 3777 $email_type = 'groups-at-message'; 3778 $group_name = bp_get_current_group_name(); 3779 } 3780 3781 $unsubscribe_args = array( 3782 'user_id' => $receiver_user_id, 3783 'notification_type' => $email_type, 3784 ); 3785 3786 $args = array( 3787 'tokens' => array( 3788 'activity' => $activity, 3789 'usermessage' => wp_strip_all_tags( $content ), 3790 'group.name' => $group_name, 3791 'mentioned.url' => $message_link, 3792 'poster.name' => $poster_name, 3793 'receiver-user.id' => $receiver_user_id, 3794 'unsubscribe' => esc_url( bp_email_get_unsubscribe_link( $unsubscribe_args ) ), 3795 ), 3796 ); 3797 3798 bp_send_email( $email_type, $receiver_user_id, $args ); 3799 } 3800 3801 /** 3802 * Fires after the sending of an @mention email notification. 3803 * 3804 * @since 1.5.0 3805 * @since 2.5.0 $subject, $message, $content arguments unset and deprecated. 3806 * 3807 * @param BP_Activity_Activity $activity Activity Item object. 3808 * @param string $deprecated Removed in 2.5; now an empty string. 3809 * @param string $deprecated Removed in 2.5; now an empty string. 3810 * @param string $deprecated Removed in 2.5; now an empty string. 3811 * @param int $receiver_user_id The ID of the user who is receiving the update. 3812 */ 3813 do_action( 'bp_activity_sent_mention_email', $activity, '', '', '', $receiver_user_id ); 3814 } 3815 3816 /** 3817 * Send email and BP notifications when an activity item receives a comment. 3818 * 3819 * @since 1.2.0 3820 * @since 2.5.0 Updated to use new email APIs. 3821 * 3822 * @param int $comment_id The comment id. 3823 * @param int $commenter_id The ID of the user who posted the comment. 3824 * @param array $params {@link bp_activity_new_comment()}. 3825 */ 3826 function bp_activity_new_comment_notification( $comment_id = 0, $commenter_id = 0, $params = array() ) { 3827 $original_activity = new BP_Activity_Activity( $params['activity_id'] ); 3828 $poster_name = bp_core_get_user_displayname( $commenter_id ); 3829 $thread_link = bp_activity_get_permalink( $params['activity_id'] ); 3830 3831 remove_filter( 'bp_get_activity_content_body', 'convert_smilies' ); 3832 remove_filter( 'bp_get_activity_content_body', 'wpautop' ); 3833 remove_filter( 'bp_get_activity_content_body', 'bp_activity_truncate_entry', 5 ); 3834 3835 /** This filter is documented in bp-activity/bp-activity-template.php */ 3836 $content = apply_filters_ref_array( 'bp_get_activity_content_body', array( $params['content'], &$original_activity ) ); 3837 3838 add_filter( 'bp_get_activity_content_body', 'convert_smilies' ); 3839 add_filter( 'bp_get_activity_content_body', 'wpautop' ); 3840 add_filter( 'bp_get_activity_content_body', 'bp_activity_truncate_entry', 5 ); 3841 3842 if ( $original_activity->user_id != $commenter_id ) { 3843 3844 // Send an email if the user hasn't opted-out. 3845 if ( 'no' != bp_get_user_meta( $original_activity->user_id, 'notification_activity_new_reply', true ) ) { 3846 3847 $unsubscribe_args = array( 3848 'user_id' => $original_activity->user_id, 3849 'notification_type' => 'activity-comment', 3850 ); 3851 3852 $args = array( 3853 'tokens' => array( 3854 'comment.id' => $comment_id, 3855 'commenter.id' => $commenter_id, 3856 'usermessage' => wp_strip_all_tags( $content ), 3857 'original_activity.user_id' => $original_activity->user_id, 3858 'poster.name' => $poster_name, 3859 'thread.url' => esc_url( $thread_link ), 3860 'unsubscribe' => esc_url( bp_email_get_unsubscribe_link( $unsubscribe_args ) ), 3861 ), 3862 ); 3863 3864 bp_send_email( 'activity-comment', $original_activity->user_id, $args ); 3865 } 3866 3867 /** 3868 * Fires at the point that notifications should be sent for activity comments. 3869 * 3870 * @since 2.6.0 3871 * 3872 * @param BP_Activity_Activity $original_activity The original activity. 3873 * @param int $comment_id ID for the newly received comment. 3874 * @param int $commenter_id ID of the user who made the comment. 3875 * @param array $params Arguments used with the original activity comment. 3876 */ 3877 do_action( 'bp_activity_sent_reply_to_update_notification', $original_activity, $comment_id, $commenter_id, $params ); 3878 } 3879 3880 3881 /* 3882 * If this is a reply to another comment, send an email notification to the 3883 * author of the immediate parent comment. 3884 */ 3885 if ( empty( $params['parent_id'] ) || ( $params['activity_id'] == $params['parent_id'] ) ) { 3886 return; 3887 } 3888 3889 $parent_comment = new BP_Activity_Activity( $params['parent_id'] ); 3890 3891 if ( $parent_comment->user_id != $commenter_id && $original_activity->user_id != $parent_comment->user_id ) { 3892 3893 // Send an email if the user hasn't opted-out. 3894 if ( 'no' != bp_get_user_meta( $parent_comment->user_id, 'notification_activity_new_reply', true ) ) { 3895 3896 $unsubscribe_args = array( 3897 'user_id' => $parent_comment->user_id, 3898 'notification_type' => 'activity-comment-author', 3899 ); 3900 3901 $args = array( 3902 'tokens' => array( 3903 'comment.id' => $comment_id, 3904 'commenter.id' => $commenter_id, 3905 'usermessage' => wp_strip_all_tags( $content ), 3906 'parent-comment-user.id' => $parent_comment->user_id, 3907 'poster.name' => $poster_name, 3908 'thread.url' => esc_url( $thread_link ), 3909 'unsubscribe' => esc_url( bp_email_get_unsubscribe_link( $unsubscribe_args ) ), 3910 ), 3911 ); 3912 3913 bp_send_email( 'activity-comment-author', $parent_comment->user_id, $args ); 3914 } 3915 3916 /** 3917 * Fires at the point that notifications should be sent for comments on activity replies. 3918 * 3919 * @since 2.6.0 3920 * 3921 * @param BP_Activity_Activity $parent_comment The parent activity. 3922 * @param int $comment_id ID for the newly received comment. 3923 * @param int $commenter_id ID of the user who made the comment. 3924 * @param array $params Arguments used with the original activity comment. 3925 */ 3926 do_action( 'bp_activity_sent_reply_to_reply_notification', $parent_comment, $comment_id, $commenter_id, $params ); 3927 } 3928 } 3929 3930 /** 3931 * Helper method to map action arguments to function parameters. 3932 * 3933 * @since 1.9.0 3934 * 3935 * @param int $comment_id ID of the comment being notified about. 3936 * @param array $params Parameters to use with notification. 3937 */ 3938 function bp_activity_new_comment_notification_helper( $comment_id, $params ) { 3939 bp_activity_new_comment_notification( $comment_id, $params['user_id'], $params ); 3940 } 3941 add_action( 'bp_activity_comment_posted', 'bp_activity_new_comment_notification_helper', 10, 2 ); 3942 3943 /** Embeds *******************************************************************/ 3944 3945 /** 3946 * Set up activity oEmbed cache during the activity loop. 3947 * 3948 * During an activity loop, this function sets up the hooks necessary to grab 3949 * each item's embeds from the cache, or put them in the cache if they are 3950 * not there yet. 3951 * 3952 * This does not cover recursive activity comments, as they do not use a real loop. 3953 * For that, see {@link bp_activity_comment_embed()}. 3954 * 3955 * @since 1.5.0 3956 * 3957 * @see BP_Embed 3958 * @see bp_embed_activity_cache() 3959 * @see bp_embed_activity_save_cache() 3960 * 3961 */ 3962 function bp_activity_embed() { 3963 add_filter( 'embed_post_id', 'bp_get_activity_id' ); 3964 add_filter( 'oembed_dataparse', 'bp_activity_oembed_dataparse', 10, 2 ); 3965 add_filter( 'bp_embed_get_cache', 'bp_embed_activity_cache', 10, 3 ); 3966 add_action( 'bp_embed_update_cache', 'bp_embed_activity_save_cache', 10, 3 ); 3967 } 3968 add_action( 'activity_loop_start', 'bp_activity_embed' ); 3969 3970 /** 3971 * Cache full oEmbed response from oEmbed. 3972 * 3973 * @since 2.6.0 3974 * 3975 * @param string $retval Current oEmbed result. 3976 * @param object $data Full oEmbed response. 3977 * @param string $url URL used for the oEmbed request. 3978 * @return string 3979 */ 3980 function bp_activity_oembed_dataparse( $retval, $data ) { 3981 buddypress()->activity->oembed_response = $data; 3982 3983 return $retval; 3984 } 3985 3986 /** 3987 * Set up activity oEmbed cache while recursing through activity comments. 3988 * 3989 * While crawling through an activity comment tree 3990 * ({@link bp_activity_recurse_comments}), this function sets up the hooks 3991 * necessary to grab each comment's embeds from the cache, or put them in 3992 * the cache if they are not there yet. 3993 * 3994 * @since 1.5.0 3995 * 3996 * @see BP_Embed 3997 * @see bp_embed_activity_cache() 3998 * @see bp_embed_activity_save_cache() 3999 * 4000 */ 4001 function bp_activity_comment_embed() { 4002 add_filter( 'embed_post_id', 'bp_get_activity_comment_id' ); 4003 add_filter( 'bp_embed_get_cache', 'bp_embed_activity_cache', 10, 3 ); 4004 add_action( 'bp_embed_update_cache', 'bp_embed_activity_save_cache', 10, 3 ); 4005 } 4006 add_action( 'bp_before_activity_comment', 'bp_activity_comment_embed' ); 4007 4008 /** 4009 * When a user clicks on a "Read More" item, make sure embeds are correctly parsed and shown for the expanded content. 4010 * 4011 * @since 1.5.0 4012 * 4013 * @see BP_Embed 4014 * 4015 * @param object $activity The activity that is being expanded. 4016 */ 4017 function bp_dtheme_embed_read_more( $activity ) { 4018 buddypress()->activity->read_more_id = $activity->id; 4019 4020 add_filter( 'embed_post_id', function() { return buddypress()->activity->read_more_id; } ); 4021 add_filter( 'bp_embed_get_cache', 'bp_embed_activity_cache', 10, 3 ); 4022 add_action( 'bp_embed_update_cache', 'bp_embed_activity_save_cache', 10, 3 ); 4023 } 4024 add_action( 'bp_dtheme_get_single_activity_content', 'bp_dtheme_embed_read_more' ); 4025 add_action( 'bp_legacy_theme_get_single_activity_content', 'bp_dtheme_embed_read_more' ); 4026 4027 /** 4028 * Clean up 'embed_post_id' filter after comment recursion. 4029 * 4030 * This filter must be removed so that the non-comment filters take over again 4031 * once the comments are done being processed. 4032 * 4033 * @since 1.5.0 4034 * 4035 * @see bp_activity_comment_embed() 4036 */ 4037 function bp_activity_comment_embed_after_recurse() { 4038 remove_filter( 'embed_post_id', 'bp_get_activity_comment_id' ); 4039 } 4040 add_action( 'bp_after_activity_comment', 'bp_activity_comment_embed_after_recurse' ); 4041 4042 /** 4043 * Fetch an activity item's cached embeds. 4044 * 4045 * Used during {@link BP_Embed::parse_oembed()} via {@link bp_activity_embed()}. 4046 * 4047 * @since 1.5.0 4048 * 4049 * @see BP_Embed::parse_oembed() 4050 * 4051 * @param string $cache An empty string passed by BP_Embed::parse_oembed() for 4052 * functions like this one to filter. 4053 * @param int $id The ID of the activity item. 4054 * @param string $cachekey The cache key generated in BP_Embed::parse_oembed(). 4055 * @return mixed The cached embeds for this activity item. 4056 */ 4057 function bp_embed_activity_cache( $cache, $id, $cachekey ) { 4058 return bp_activity_get_meta( $id, $cachekey ); 4059 } 4060 4061 /** 4062 * Set an activity item's embed cache. 4063 * 4064 * Used during {@link BP_Embed::parse_oembed()} via {@link bp_activity_embed()}. 4065 * 4066 * @since 1.5.0 4067 * 4068 * @see BP_Embed::parse_oembed() 4069 * 4070 * @param string $cache An empty string passed by BP_Embed::parse_oembed() for 4071 * functions like this one to filter. 4072 * @param string $cachekey The cache key generated in BP_Embed::parse_oembed(). 4073 * @param int $id The ID of the activity item. 4074 */ 4075 function bp_embed_activity_save_cache( $cache, $cachekey, $id ) { 4076 bp_activity_update_meta( $id, $cachekey, $cache ); 4077 4078 // Cache full oEmbed response. 4079 if ( true === isset( buddypress()->activity->oembed_response ) ) { 4080 $cachekey = str_replace( '_oembed', '_oembed_response', $cachekey ); 4081 bp_activity_update_meta( $id, $cachekey, buddypress()->activity->oembed_response ); 4082 } 4083 } 4084 4085 /** 4086 * Should we use Heartbeat to refresh activities? 4087 * 4088 * @since 2.0.0 4089 * 4090 * @return bool True if activity heartbeat is enabled, otherwise false. 4091 */ 4092 function bp_activity_do_heartbeat() { 4093 $retval = false; 4094 4095 if ( bp_is_activity_heartbeat_active() && ( bp_is_activity_directory() || bp_is_group_activity() ) ) { 4096 $retval = true; 4097 } 4098 4099 /** 4100 * Filters whether the heartbeat feature in the activity stream should be active. 4101 * 4102 * @since 2.8.0 4103 * 4104 * @param bool $retval Whether or not activity heartbeat is active. 4105 */ 4106 return (bool) apply_filters( 'bp_activity_do_heartbeat', $retval ); 4107 } 4108 4109 /** 4110 * Detect a change in post type status, and initiate an activity update if necessary. 4111 * 4112 * @since 2.2.0 4113 * 4114 * @todo Support untrashing better. 4115 * 4116 * @param string $new_status New status for the post. 4117 * @param string $old_status Old status for the post. 4118 * @param object $post Post data. 4119 */ 4120 function bp_activity_catch_transition_post_type_status( $new_status, $old_status, $post ) { 4121 if ( ! post_type_supports( $post->post_type, 'buddypress-activity' ) ) { 4122 return; 4123 } 4124 4125 // This is an edit. 4126 if ( $new_status === $old_status ) { 4127 // An edit of an existing post should update the existing activity item. 4128 if ( $new_status == 'publish' ) { 4129 $edit = bp_activity_post_type_update( $post ); 4130 4131 // Post was never recorded into activity stream, so record it now! 4132 if ( null === $edit ) { 4133 bp_activity_post_type_publish( $post->ID, $post ); 4134 } 4135 4136 // Allow plugins to eventually deal with other post statuses. 4137 } else { 4138 /** 4139 * Fires when editing the post and the new status is not 'publish'. 4140 * 4141 * This is a variable filter that is dependent on the post type 4142 * being untrashed. 4143 * 4144 * @since 2.5.0 4145 * 4146 * @param WP_Post $post Post data. 4147 * @param string $new_status New status for the post. 4148 * @param string $old_status Old status for the post. 4149 */ 4150 do_action( 'bp_activity_post_type_edit_' . $post->post_type, $post, $new_status, $old_status ); 4151 } 4152 4153 return; 4154 } 4155 4156 // Publishing a previously unpublished post. 4157 if ( 'publish' === $new_status ) { 4158 // Untrashing the post type - nothing here yet. 4159 if ( 'trash' == $old_status ) { 4160 4161 /** 4162 * Fires if untrashing post in a post type. 4163 * 4164 * This is a variable filter that is dependent on the post type 4165 * being untrashed. 4166 * 4167 * @since 2.2.0 4168 * 4169 * @param WP_Post $post Post data. 4170 */ 4171 do_action( 'bp_activity_post_type_untrash_' . $post->post_type, $post ); 4172 } else { 4173 // Record the post. 4174 bp_activity_post_type_publish( $post->ID, $post ); 4175 } 4176 4177 // Unpublishing a previously published post. 4178 } elseif ( 'publish' === $old_status ) { 4179 // Some form of pending status - only remove the activity entry. 4180 bp_activity_post_type_unpublish( $post->ID, $post ); 4181 4182 // For any other cases, allow plugins to eventually deal with it. 4183 } else { 4184 /** 4185 * Fires when the old and the new post status are not 'publish'. 4186 * 4187 * This is a variable filter that is dependent on the post type 4188 * being untrashed. 4189 * 4190 * @since 2.5.0 4191 * 4192 * @param WP_Post $post Post data. 4193 * @param string $new_status New status for the post. 4194 * @param string $old_status Old status for the post. 4195 */ 4196 do_action( 'bp_activity_post_type_transition_status_' . $post->post_type, $post, $new_status, $old_status ); 4197 } 4198 } 4199 add_action( 'transition_post_status', 'bp_activity_catch_transition_post_type_status', 10, 3 ); 4200 4201 /** 4202 * When a post type comment status transition occurs, update the relevant activity's status. 4203 * 4204 * @since 2.5.0 4205 * 4206 * @param string $new_status New comment status. 4207 * @param string $old_status Previous comment status. 4208 * @param WP_Comment $comment Comment data. 4209 */ 4210 function bp_activity_transition_post_type_comment_status( $new_status, $old_status, $comment ) { 4211 $post_type = get_post_type( $comment->comment_post_ID ); 4212 if ( ! $post_type ) { 4213 return; 4214 } 4215 4216 // Get the post type tracking args. 4217 $activity_post_object = bp_activity_get_post_type_tracking_args( $post_type ); 4218 4219 // Bail if the activity type does not exist. 4220 if ( empty( $activity_post_object->comments_tracking->action_id ) ) { 4221 return false; 4222 4223 // Set the $activity_comment_object. 4224 } else { 4225 $activity_comment_object = $activity_post_object->comments_tracking; 4226 } 4227 4228 // Init an empty activity ID. 4229 $activity_id = 0; 4230 4231 /** 4232 * Activity currently doesn't have any concept of a trash, or an unapproved/approved state. 4233 * 4234 * If a blog comment transitions to a "delete" or "hold" status, delete the activity item. 4235 * If a blog comment transitions to trashed, or spammed, mark the activity as spam. 4236 * If a blog comment transitions to approved (and the activity exists), mark the activity as ham. 4237 * If a blog comment transitions to unapproved (and the activity exists), mark the activity as spam. 4238 * Otherwise, record the comment into the activity stream. 4239 */ 4240 4241 // This clause handles delete/hold. 4242 if ( in_array( $new_status, array( 'delete', 'hold' ) ) ) { 4243 return bp_activity_post_type_remove_comment( $comment->comment_ID, $activity_post_object ); 4244 4245 // These clauses handle trash, spam, and un-spams. 4246 } elseif ( in_array( $new_status, array( 'trash', 'spam', 'unapproved' ) ) ) { 4247 $action = 'spam_activity'; 4248 } elseif ( 'approved' == $new_status ) { 4249 $action = 'ham_activity'; 4250 } 4251 4252 // Get the activity. 4253 if ( bp_disable_blogforum_comments() ) { 4254 $activity_id = bp_activity_get_activity_id( array( 4255 'component' => $activity_comment_object->component_id, 4256 'item_id' => get_current_blog_id(), 4257 'secondary_item_id' => $comment->comment_ID, 4258 'type' => $activity_comment_object->action_id, 4259 ) ); 4260 } else { 4261 $activity_id = get_comment_meta( $comment->comment_ID, 'bp_activity_comment_id', true ); 4262 } 4263 4264 /** 4265 * Leave a chance to plugins to manage activity comments differently. 4266 * 4267 * @since 2.5.0 4268 * 4269 * @param bool $value True to override BuddyPress management. 4270 * @param string $post_type The post type name. 4271 * @param int $activity_id The post type activity (0 if not found). 4272 * @param string $new_status The new status of the post type comment. 4273 * @param string $old_status The old status of the post type comment. 4274 * @param WP_Comment $comment Comment data. 4275 */ 4276 if ( true === apply_filters( 'bp_activity_pre_transition_post_type_comment_status', false, $post_type, $activity_id, $new_status, $old_status, $comment ) ) { 4277 return false; 4278 } 4279 4280 // Check activity item exists. 4281 if ( empty( $activity_id ) ) { 4282 // If no activity exists, but the comment has been approved, record it into the activity table. 4283 if ( 'approved' == $new_status ) { 4284 return bp_activity_post_type_comment( $comment->comment_ID, true, $activity_post_object ); 4285 } 4286 4287 return; 4288 } 4289 4290 // Create an activity object. 4291 $activity = new BP_Activity_Activity( $activity_id ); 4292 if ( empty( $activity->component ) ) { 4293 return; 4294 } 4295 4296 // Spam/ham the activity if it's not already in that state. 4297 if ( 'spam_activity' === $action && ! $activity->is_spam ) { 4298 bp_activity_mark_as_spam( $activity ); 4299 } elseif ( 'ham_activity' == $action) { 4300 bp_activity_mark_as_ham( $activity ); 4301 } 4302 4303 // Add "new_post_type_comment" to the allowed activity types, so that the activity's Akismet history is generated. 4304 $post_type_comment_action = $activity_comment_object->action_id; 4305 $comment_akismet_history = function ( $activity_types ) use ( $post_type_comment_action ) { 4306 $activity_types[] = $post_type_comment_action; 4307 4308 return $activity_types; 4309 }; 4310 add_filter( 'bp_akismet_get_activity_types', $comment_akismet_history ); 4311 4312 // Make sure the activity change won't edit the comment if sync is on. 4313 remove_action( 'bp_activity_before_save', 'bp_blogs_sync_activity_edit_to_post_comment', 20 ); 4314 4315 // Save the updated activity. 4316 $activity->save(); 4317 4318 // Restore the action. 4319 add_action( 'bp_activity_before_save', 'bp_blogs_sync_activity_edit_to_post_comment', 20 ); 4320 4321 // Remove the dynamic permitting of the "new_blog_comment" activity type so we don't break anything. 4322 remove_filter( 'bp_akismet_get_activity_types', $comment_akismet_history ); 4323 } 4324 add_action( 'transition_comment_status', 'bp_activity_transition_post_type_comment_status', 10, 3 ); 4325 4326 /** 4327 * Finds and exports personal data associated with an email address from the Activity tables. 4328 * 4329 * @since 4.0.0 4330 * 4331 * @param string $email_address The user's email address. 4332 * @param int $page Batch number. 4333 * @return array An array of personal data. 4334 */ 4335 function bp_activity_personal_data_exporter( $email_address, $page ) { 4336 $number = 50; 4337 4338 $email_address = trim( $email_address ); 4339 4340 $data_to_export = array(); 4341 4342 $user = get_user_by( 'email', $email_address ); 4343 4344 if ( ! $user ) { 4345 return array( 4346 'data' => array(), 4347 'done' => true, 4348 ); 4349 } 4350 4351 $activities = bp_activity_get( array( 4352 'display_comments' => 'stream', 4353 'per_page' => $number, 4354 'page' => $page, 4355 'show_hidden' => true, 4356 'filter' => array( 4357 'user_id' => $user->ID, 4358 ), 4359 ) ); 4360 4361 $user_data_to_export = array(); 4362 $activity_actions = bp_activity_get_actions(); 4363 4364 foreach ( $activities['activities'] as $activity ) { 4365 if ( ! empty( $activity_actions->{$activity->component}->{$activity->type}['format_callback'] ) ) { 4366 $description = call_user_func( $activity_actions->{$activity->component}->{$activity->type}['format_callback'], '', $activity ); 4367 } elseif ( ! empty( $activity->action ) ) { 4368 $description = $activity->action; 4369 } else { 4370 $description = $activity->type; 4371 } 4372 4373 $item_data = array( 4374 array( 4375 'name' => __( 'Activity Date', 'buddypress' ), 4376 'value' => $activity->date_recorded, 4377 ), 4378 array( 4379 'name' => __( 'Activity Description', 'buddypress' ), 4380 'value' => $description, 4381 ), 4382 array( 4383 'name' => __( 'Activity URL', 'buddypress' ), 4384 'value' => bp_activity_get_permalink( $activity->id, $activity ), 4385 ), 4386 ); 4387 4388 if ( ! empty( $activity->content ) ) { 4389 $item_data[] = array( 4390 'name' => __( 'Activity Content', 'buddypress' ), 4391 'value' => $activity->content, 4392 ); 4393 } 4394 4395 /** 4396 * Filters the data associated with an activity item when assembled for a WP personal data export. 4397 * 4398 * Plugins that register activity types whose `action` string doesn't adequately 4399 * describe the activity item for the purposes of data export may filter the activity 4400 * item data here. 4401 * 4402 * @since 4.0.0 4403 * 4404 * @param array $item_data Array of data describing the activity item. 4405 * @param BP_Activity_Activity $activity Activity item. 4406 */ 4407 $item_data = apply_filters( 'bp_activity_personal_data_export_item_data', $item_data, $activity ); 4408 4409 $data_to_export[] = array( 4410 'group_id' => 'bp_activity', 4411 'group_label' => __( 'Activity', 'buddypress' ), 4412 'item_id' => "bp-activity-{$activity->id}", 4413 'data' => $item_data, 4414 ); 4415 } 4416 4417 // Tell core if we have more items to process. 4418 $done = count( $activities['activities'] ) < $number; 4419 4420 return array( 4421 'data' => $data_to_export, 4422 'done' => $done, 4423 ); 4424 } 4425 4426 /** 4427 * Checks whether an activity feed is enabled. 4428 * 4429 * @since 8.0.0 4430 * 4431 * @param string $feed_id The feed identifier. Possible values are: 4432 * 'sitewide', 'personal', 'friends', 'mygroups', 'mentions', 'favorites'. 4433 */ 4434 function bp_activity_is_feed_enable( $feed_id = '' ) { 4435 /** 4436 * Filters if BuddyPress should consider feeds enabled. If disabled, it will return early. 4437 * 4438 * @since 1.8.0 4439 * @since 8.0.0 Adds the `$feed_id` parameter. 4440 * 4441 * @param bool $value Defaults to true aka feeds are enabled. 4442 * @param string $feed_id The feed identifier. 4443 */ 4444 return (bool) apply_filters( 'bp_activity_enable_feeds', true, $feed_id ); 4445 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Dec 5 01:00:58 2024 | Cross-referenced by PHPXref 0.7.1 |