[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

/src/bp-activity/ -> bp-activity-functions.php (source)

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


Generated: Sun Jan 16 01:00:57 2022 Cross-referenced by PHPXref 0.7.1