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


Generated: Tue Nov 19 01:01:36 2019 Cross-referenced by PHPXref 0.7.1