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


Generated: Tue Aug 4 01:01:34 2020 Cross-referenced by PHPXref 0.7.1