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