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


Generated: Fri Jul 30 01:01:42 2021 Cross-referenced by PHPXref 0.7.1