[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

/src/bp-activity/classes/ -> class-bp-activity-activity.php (source)

   1  <?php
   2  /**
   3   * BuddyPress Activity Classes
   4   *
   5   * @package BuddyPress
   6   * @subpackage Activity
   7   * @since 1.0.0
   8   */
   9  
  10  // Exit if accessed directly.
  11  defined( 'ABSPATH' ) || exit;
  12  
  13  /**
  14   * Database interaction class for the BuddyPress activity component.
  15   * Instance methods are available for creating/editing an activity,
  16   * static methods for querying activities.
  17   *
  18   * @since 1.0.0
  19   */
  20  class BP_Activity_Activity {
  21  
  22      /** Properties ************************************************************/
  23  
  24      /**
  25       * ID of the activity item.
  26       *
  27       * @since 1.0.0
  28       * @var int
  29       */
  30      var $id;
  31  
  32      /**
  33       * ID of the associated item.
  34       *
  35       * @since 1.0.0
  36       * @var int
  37       */
  38      var $item_id;
  39  
  40      /**
  41       * ID of the associated secondary item.
  42       *
  43       * @since 1.0.0
  44       * @var int
  45       */
  46      var $secondary_item_id;
  47  
  48      /**
  49       * ID of user associated with the activity item.
  50       *
  51       * @since 1.0.0
  52       * @var int
  53       */
  54      var $user_id;
  55  
  56      /**
  57       * The primary URL for the activity in RSS feeds.
  58       *
  59       * @since 1.0.0
  60       * @var string
  61       */
  62      var $primary_link;
  63  
  64      /**
  65       * BuddyPress component the activity item relates to.
  66       *
  67       * @since 1.2.0
  68       * @var string
  69       */
  70      var $component;
  71  
  72      /**
  73       * Activity type, eg 'new_blog_post'.
  74       *
  75       * @since 1.2.0
  76       * @var string
  77       */
  78      var $type;
  79  
  80      /**
  81       * Description of the activity, eg 'Alex updated his profile.'.
  82       *
  83       * @since 1.2.0
  84       * @var string
  85       */
  86      var $action;
  87  
  88      /**
  89       * The content of the activity item.
  90       *
  91       * @since 1.2.0
  92       * @var string
  93       */
  94      var $content;
  95  
  96      /**
  97       * The date the activity item was recorded, in 'Y-m-d h:i:s' format.
  98       *
  99       * @since 1.0.0
 100       * @var string
 101       */
 102      var $date_recorded;
 103  
 104      /**
 105       * Whether the item should be hidden in sitewide streams.
 106       *
 107       * @since 1.1.0
 108       * @var int
 109       */
 110      var $hide_sitewide = 0;
 111  
 112      /**
 113       * Node boundary start for activity or activity comment.
 114       *
 115       * @since 1.5.0
 116       * @var int
 117       */
 118      var $mptt_left;
 119  
 120      /**
 121       * Node boundary end for activity or activity comment.
 122       *
 123       * @since 1.5.0
 124       * @var int
 125       */
 126      var $mptt_right;
 127  
 128      /**
 129       * Whether this item is marked as spam.
 130       *
 131       * @since 1.6.0
 132       * @var int
 133       */
 134      var $is_spam;
 135  
 136      /**
 137       * Error holder.
 138       *
 139       * @since 2.6.0
 140       *
 141       * @var WP_Error
 142       */
 143      public $errors;
 144  
 145      /**
 146       * Error type to return. Either 'bool' or 'wp_error'.
 147       *
 148       * @since 2.6.0
 149       *
 150       * @var string
 151       */
 152      public $error_type = 'bool';
 153  
 154      /**
 155       * Constructor method.
 156       *
 157       * @since 1.5.0
 158       *
 159       * @param int|bool $id Optional. The ID of a specific activity item.
 160       */
 161  	public function __construct( $id = false ) {
 162          // Instantiate errors object.
 163          $this->errors = new WP_Error;
 164  
 165          if ( !empty( $id ) ) {
 166              $this->id = (int) $id;
 167              $this->populate();
 168          }
 169      }
 170  
 171      /**
 172       * Populate the object with data about the specific activity item.
 173       *
 174       * @since 1.0.0
 175       */
 176  	public function populate() {
 177          global $wpdb;
 178  
 179          $row = wp_cache_get( $this->id, 'bp_activity' );
 180  
 181          if ( false === $row ) {
 182              $bp  = buddypress();
 183              $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$bp->activity->table_name} WHERE id = %d", $this->id ) );
 184  
 185              wp_cache_set( $this->id, $row, 'bp_activity' );
 186          }
 187  
 188          if ( empty( $row ) ) {
 189              $this->id = 0;
 190              return;
 191          }
 192  
 193          $this->id                = (int) $row->id;
 194          $this->item_id           = (int) $row->item_id;
 195          $this->secondary_item_id = (int) $row->secondary_item_id;
 196          $this->user_id           = (int) $row->user_id;
 197          $this->primary_link      = $row->primary_link;
 198          $this->component         = $row->component;
 199          $this->type              = $row->type;
 200          $this->action            = $row->action;
 201          $this->content           = $row->content;
 202          $this->date_recorded     = $row->date_recorded;
 203          $this->hide_sitewide     = (int) $row->hide_sitewide;
 204          $this->mptt_left         = (int) $row->mptt_left;
 205          $this->mptt_right        = (int) $row->mptt_right;
 206          $this->is_spam           = (int) $row->is_spam;
 207  
 208          // Generate dynamic 'action' when possible.
 209          $action = bp_activity_generate_action_string( $this );
 210          if ( false !== $action ) {
 211              $this->action = $action;
 212  
 213              // If no callback is available, use the literal string from
 214              // the database row.
 215          } elseif ( ! empty( $row->action ) ) {
 216              $this->action = $row->action;
 217  
 218              // Provide a fallback to avoid PHP notices.
 219          } else {
 220              $this->action = '';
 221          }
 222      }
 223  
 224      /**
 225       * Save the activity item to the database.
 226       *
 227       * @since 1.0.0
 228       *
 229       * @return WP_Error|bool True on success.
 230       */
 231  	public function save() {
 232          global $wpdb;
 233  
 234          $bp = buddypress();
 235  
 236          $this->id                = apply_filters_ref_array( 'bp_activity_id_before_save',                array( $this->id,                &$this ) );
 237          $this->item_id           = apply_filters_ref_array( 'bp_activity_item_id_before_save',           array( $this->item_id,           &$this ) );
 238          $this->secondary_item_id = apply_filters_ref_array( 'bp_activity_secondary_item_id_before_save', array( $this->secondary_item_id, &$this ) );
 239          $this->user_id           = apply_filters_ref_array( 'bp_activity_user_id_before_save',           array( $this->user_id,           &$this ) );
 240          $this->primary_link      = apply_filters_ref_array( 'bp_activity_primary_link_before_save',      array( $this->primary_link,      &$this ) );
 241          $this->component         = apply_filters_ref_array( 'bp_activity_component_before_save',         array( $this->component,         &$this ) );
 242          $this->type              = apply_filters_ref_array( 'bp_activity_type_before_save',              array( $this->type,              &$this ) );
 243          $this->action            = apply_filters_ref_array( 'bp_activity_action_before_save',            array( $this->action,            &$this ) );
 244          $this->content           = apply_filters_ref_array( 'bp_activity_content_before_save',           array( $this->content,           &$this ) );
 245          $this->date_recorded     = apply_filters_ref_array( 'bp_activity_date_recorded_before_save',     array( $this->date_recorded,     &$this ) );
 246          $this->hide_sitewide     = apply_filters_ref_array( 'bp_activity_hide_sitewide_before_save',     array( $this->hide_sitewide,     &$this ) );
 247          $this->mptt_left         = apply_filters_ref_array( 'bp_activity_mptt_left_before_save',         array( $this->mptt_left,         &$this ) );
 248          $this->mptt_right        = apply_filters_ref_array( 'bp_activity_mptt_right_before_save',        array( $this->mptt_right,        &$this ) );
 249          $this->is_spam           = apply_filters_ref_array( 'bp_activity_is_spam_before_save',           array( $this->is_spam,           &$this ) );
 250  
 251          /**
 252           * Fires before the current activity item gets saved.
 253           *
 254           * Please use this hook to filter the properties above. Each part will be passed in.
 255           *
 256           * @since 1.0.0
 257           *
 258           * @param BP_Activity_Activity $this Current instance of the activity item being saved. Passed by reference.
 259           */
 260          do_action_ref_array( 'bp_activity_before_save', array( &$this ) );
 261  
 262          if ( 'wp_error' === $this->error_type && $this->errors->get_error_code() ) {
 263              return $this->errors;
 264          }
 265  
 266          if ( empty( $this->component ) || empty( $this->type ) ) {
 267              if ( 'bool' === $this->error_type ) {
 268                  return false;
 269              } else {
 270                  if ( empty( $this->component ) ) {
 271                      $this->errors->add( 'bp_activity_missing_component', __( 'You need to define a component parameter to insert activity.', 'buddypress' ) );
 272                  } else {
 273                      $this->errors->add( 'bp_activity_missing_type', __( 'You need to define a type parameter to insert activity.', 'buddypress' ) );
 274                  }
 275  
 276                  return $this->errors;
 277              }
 278          }
 279  
 280          /**
 281           * Use this filter to make the content of your activity required.
 282           *
 283           * @since 6.0.0
 284           *
 285           * @param bool   $value True if the content of the activity type is required.
 286           *                      False otherwise.
 287           * @param string $type  The type of the activity we are about to insert.
 288           */
 289          $type_requires_content = (bool) apply_filters( 'bp_activity_type_requires_content', $this->type === 'activity_update', $this->type );
 290          if ( $type_requires_content && ! $this->content ) {
 291              if ( 'bool' === $this->error_type ) {
 292                  return false;
 293              } else {
 294                  $this->errors->add( 'bp_activity_missing_content', __( 'Please enter some content to post.', 'buddypress' ) );
 295  
 296                  return $this->errors;
 297              }
 298          }
 299  
 300          if ( empty( $this->primary_link ) ) {
 301              $this->primary_link = bp_loggedin_user_domain();
 302          }
 303  
 304          // If we have an existing ID, update the activity item, otherwise insert it.
 305          if ( ! empty( $this->id ) ) {
 306              $q = $wpdb->prepare( "UPDATE {$bp->activity->table_name} SET user_id = %d, component = %s, type = %s, action = %s, content = %s, primary_link = %s, date_recorded = %s, item_id = %d, secondary_item_id = %d, hide_sitewide = %d, is_spam = %d WHERE id = %d", $this->user_id, $this->component, $this->type, $this->action, $this->content, $this->primary_link, $this->date_recorded, $this->item_id, $this->secondary_item_id, $this->hide_sitewide, $this->is_spam, $this->id );
 307          } else {
 308              $q = $wpdb->prepare( "INSERT INTO {$bp->activity->table_name} ( user_id, component, type, action, content, primary_link, date_recorded, item_id, secondary_item_id, hide_sitewide, is_spam ) VALUES ( %d, %s, %s, %s, %s, %s, %s, %d, %d, %d, %d )", $this->user_id, $this->component, $this->type, $this->action, $this->content, $this->primary_link, $this->date_recorded, $this->item_id, $this->secondary_item_id, $this->hide_sitewide, $this->is_spam );
 309          }
 310  
 311          if ( false === $wpdb->query( $q ) ) {
 312              return false;
 313          }
 314  
 315          // If this is a new activity item, set the $id property.
 316          if ( empty( $this->id ) ) {
 317              $this->id = $wpdb->insert_id;
 318  
 319              // If an existing activity item, prevent any changes to the content generating new @mention notifications.
 320          } else {
 321              add_filter( 'bp_activity_at_name_do_notifications', '__return_false' );
 322          }
 323  
 324          /**
 325           * Fires after an activity item has been saved to the database.
 326           *
 327           * @since 1.0.0
 328           *
 329           * @param BP_Activity_Activity $this Current instance of activity item being saved. Passed by reference.
 330           */
 331          do_action_ref_array( 'bp_activity_after_save', array( &$this ) );
 332  
 333          return true;
 334      }
 335  
 336      /** Static Methods ***************************************************/
 337  
 338      /**
 339       * Get activity items, as specified by parameters.
 340       *
 341       * @since 1.2.0
 342       * @since 2.4.0 Introduced the `$fields` parameter.
 343       * @since 2.9.0 Introduced the `$order_by` parameter.
 344       * @since 10.0.0 Introduced the `$count_total_only` parameter.
 345       *
 346       * @see BP_Activity_Activity::get_filter_sql() for a description of the
 347       *      'filter' parameter.
 348       * @see WP_Meta_Query::queries for a description of the 'meta_query'
 349       *      parameter format.
 350       *
 351       * @param array $args {
 352       *     An array of arguments. All items are optional.
 353       *     @type int          $page              Which page of results to fetch. Using page=1 without per_page will result
 354       *                                           in no pagination. Default: 1.
 355       *     @type int|bool     $per_page          Number of results per page. Default: 25.
 356       *     @type int|bool     $max               Maximum number of results to return. Default: false (unlimited).
 357       *     @type string       $fields            Activity fields to return. Pass 'ids' to get only the activity IDs.
 358       *                                           'all' returns full activity objects.
 359       *     @type string       $sort              ASC or DESC. Default: 'DESC'.
 360       *     @type string       $order_by          Column to order results by.
 361       *     @type array        $exclude           Array of activity IDs to exclude. Default: false.
 362       *     @type array        $in                Array of ids to limit query by (IN). Default: false.
 363       *     @type array        $meta_query        Array of meta_query conditions. See WP_Meta_Query::queries.
 364       *     @type array        $date_query        Array of date_query conditions. See first parameter of
 365       *                                           WP_Date_Query::__construct().
 366       *     @type array        $filter_query      Array of advanced query conditions. See BP_Activity_Query::__construct().
 367       *     @type string|array $scope             Pre-determined set of activity arguments.
 368       *     @type array        $filter            See BP_Activity_Activity::get_filter_sql().
 369       *     @type string       $search_terms      Limit results by a search term. Default: false.
 370       *     @type bool         $display_comments  Whether to include activity comments. Default: false.
 371       *     @type bool         $show_hidden       Whether to show items marked hide_sitewide. Default: false.
 372       *     @type string       $spam              Spam status. Default: 'ham_only'.
 373       *     @type bool         $update_meta_cache Whether to pre-fetch metadata for queried activity items. Default: true.
 374       *     @type string|bool  $count_total       If true, an additional DB query is run to count the total activity items
 375       *                                           for the query. Default: false.
 376       *     @type bool         $count_total_only  If true, only the DB query to count the total activity items is run.
 377       *                                           Default: false.
 378       * }
 379       * @return array The array returned has two keys:
 380       *               - 'total' is the count of located activities
 381       *               - 'activities' is an array of the located activities
 382       */
 383  	public static function get( $args = array() ) {
 384          global $wpdb;
 385  
 386          $function_args = func_get_args();
 387  
 388          // Backward compatibility with old method of passing arguments.
 389          if ( ! is_array( $args ) || count( $function_args ) > 1 ) {
 390              _deprecated_argument(
 391                  __METHOD__,
 392                  '1.6',
 393                  sprintf(
 394                      /* translators: 1: the name of the method. 2: the name of the file. */
 395                      esc_html__( 'Arguments passed to %1$s should be in an associative array. See the inline documentation at %2$s for more details.', 'buddypress' ),
 396                      __METHOD__,
 397                      __FILE__
 398                  )
 399              );
 400  
 401              $old_args_keys = array(
 402                  0 => 'max',
 403                  1 => 'page',
 404                  2 => 'per_page',
 405                  3 => 'sort',
 406                  4 => 'search_terms',
 407                  5 => 'filter',
 408                  6 => 'display_comments',
 409                  7 => 'show_hidden',
 410                  8 => 'exclude',
 411                  9 => 'in',
 412                  10 => 'spam'
 413              );
 414  
 415              $args = bp_core_parse_args_array( $old_args_keys, $function_args );
 416          }
 417  
 418          $bp = buddypress();
 419          $r  = bp_parse_args(
 420              $args,
 421              array(
 422                  'page'              => 1,               // The current page.
 423                  'per_page'          => 25,              // Activity items per page.
 424                  'max'               => false,           // Max number of items to return.
 425                  'fields'            => 'all',           // Fields to include.
 426                  'sort'              => 'DESC',          // ASC or DESC.
 427                  'order_by'          => 'date_recorded', // Column to order by.
 428                  'exclude'           => false,           // Array of ids to exclude.
 429                  'in'                => false,           // Array of ids to limit query by (IN).
 430                  'meta_query'        => false,           // Filter by activitymeta.
 431                  'date_query'        => false,           // Filter by date.
 432                  'filter_query'      => false,           // Advanced filtering - see BP_Activity_Query.
 433                  'filter'            => false,           // See self::get_filter_sql().
 434                  'scope'             => false,           // Preset activity arguments.
 435                  'search_terms'      => false,           // Terms to search by.
 436                  'display_comments'  => false,           // Whether to include activity comments.
 437                  'show_hidden'       => false,           // Show items marked hide_sitewide.
 438                  'spam'              => 'ham_only',      // Spam status.
 439                  'update_meta_cache' => true,            // Whether or not to update meta cache.
 440                  'count_total'       => false,           // Whether or not to use count_total.
 441                  'count_total_only'  => false,           // Whether to only get the total count.
 442              )
 443          );
 444  
 445          // Select conditions.
 446          $select_sql = "SELECT DISTINCT a.id";
 447  
 448          $from_sql   = " FROM {$bp->activity->table_name} a";
 449  
 450          $join_sql   = '';
 451  
 452          // Where conditions.
 453          $where_conditions = array();
 454  
 455          // Excluded types.
 456          $excluded_types = array();
 457  
 458          // Scope takes precedence.
 459          if ( ! empty( $r['scope'] ) ) {
 460              $scope_query = self::get_scope_query_sql( $r['scope'], $r );
 461  
 462              // Add our SQL conditions if matches were found.
 463              if ( ! empty( $scope_query['sql'] ) ) {
 464                  $where_conditions['scope_query_sql'] = $scope_query['sql'];
 465              }
 466  
 467              // Override some arguments if needed.
 468              if ( ! empty( $scope_query['override'] ) ) {
 469                  $r = array_replace_recursive( $r, $scope_query['override'] );
 470              }
 471  
 472              // Advanced filtering.
 473          } elseif ( ! empty( $r['filter_query'] ) ) {
 474              $filter_query = new BP_Activity_Query( $r['filter_query'] );
 475              $sql          = $filter_query->get_sql();
 476              if ( ! empty( $sql ) ) {
 477                  $where_conditions['filter_query_sql'] = $sql;
 478              }
 479          }
 480  
 481          // Regular filtering.
 482          if ( $r['filter'] && $filter_sql = BP_Activity_Activity::get_filter_sql( $r['filter'] ) ) {
 483              $where_conditions['filter_sql'] = $filter_sql;
 484          }
 485  
 486          // Spam.
 487          if ( 'ham_only' == $r['spam'] ) {
 488              $where_conditions['spam_sql'] = 'a.is_spam = 0';
 489          } elseif ( 'spam_only' == $r['spam'] ) {
 490              $where_conditions['spam_sql'] = 'a.is_spam = 1';
 491          }
 492  
 493          // Searching.
 494          if ( $r['search_terms'] ) {
 495              $search_terms_like = '%' . bp_esc_like( $r['search_terms'] ) . '%';
 496              $where_conditions['search_sql'] = $wpdb->prepare( 'a.content LIKE %s', $search_terms_like );
 497  
 498              /**
 499               * Filters whether or not to include users for search parameters.
 500               *
 501               * @since 3.0.0
 502               *
 503               * @param bool $value Whether or not to include user search. Default false.
 504               */
 505              if ( apply_filters( 'bp_activity_get_include_user_search', false ) ) {
 506                  $user_search = get_user_by( 'slug', $r['search_terms'] );
 507                  if ( false !== $user_search ) {
 508                      $user_id                         = $user_search->ID;
 509                      $where_conditions['search_sql'] .= $wpdb->prepare( ' OR a.user_id = %d', $user_id );
 510                  }
 511              }
 512          }
 513  
 514          // Sanitize 'order'.
 515          $sort = $r['sort'];
 516          if ( 'DESC' !== $sort ) {
 517              $sort = bp_esc_sql_order( $sort );
 518          }
 519  
 520          switch( $r['order_by'] ) {
 521              case 'id' :
 522              case 'user_id' :
 523              case 'component' :
 524              case 'type' :
 525              case 'action' :
 526              case 'content' :
 527              case 'primary_link' :
 528              case 'item_id' :
 529              case 'secondary_item_id' :
 530              case 'date_recorded' :
 531              case 'hide_sitewide' :
 532              case 'mptt_left' :
 533              case 'mptt_right' :
 534              case 'is_spam' :
 535                  break;
 536  
 537              default :
 538                  $r['order_by'] = 'date_recorded';
 539                  break;
 540          }
 541          $order_by = 'a.' . $r['order_by'];
 542  
 543          // Hide Hidden Items?
 544          if ( ! $r['show_hidden'] ) {
 545              $where_conditions['hidden_sql'] = "a.hide_sitewide = 0";
 546          }
 547  
 548          // Exclude specified items.
 549          if ( ! empty( $r['exclude'] ) ) {
 550              $exclude = implode( ',', wp_parse_id_list( $r['exclude'] ) );
 551              $where_conditions['exclude'] = "a.id NOT IN ({$exclude})";
 552          }
 553  
 554          // The specific ids to which you want to limit the query.
 555          if ( ! empty( $r['in'] ) ) {
 556              $in = implode( ',', wp_parse_id_list( $r['in'] ) );
 557              $where_conditions['in'] = "a.id IN ({$in})";
 558          }
 559  
 560          // Process meta_query into SQL.
 561          $meta_query_sql = self::get_meta_query_sql( $r['meta_query'] );
 562  
 563          if ( ! empty( $meta_query_sql['join'] ) ) {
 564              $join_sql .= $meta_query_sql['join'];
 565          }
 566  
 567          if ( ! empty( $meta_query_sql['where'] ) ) {
 568              $where_conditions[] = $meta_query_sql['where'];
 569          }
 570  
 571          // Process date_query into SQL.
 572          $date_query_sql = self::get_date_query_sql( $r['date_query'] );
 573  
 574          if ( ! empty( $date_query_sql ) ) {
 575              $where_conditions['date'] = $date_query_sql;
 576          }
 577  
 578          // Alter the query based on whether we want to show activity item
 579          // comments in the stream like normal comments or threaded below
 580          // the activity.
 581          if ( false === $r['display_comments'] || 'threaded' === $r['display_comments'] ) {
 582              $excluded_types[] = 'activity_comment';
 583          }
 584  
 585          // Exclude 'last_activity' items unless the 'action' filter has
 586          // been explicitly set.
 587          if ( empty( $r['filter']['object'] ) ) {
 588              $excluded_types[] = 'last_activity';
 589          }
 590  
 591          // Build the excluded type sql part.
 592          if ( ! empty( $excluded_types ) ) {
 593              $not_in = "'" . implode( "', '", esc_sql( $excluded_types ) ) . "'";
 594              $where_conditions['excluded_types'] = "a.type NOT IN ({$not_in})";
 595          }
 596  
 597          /**
 598           * Filters the MySQL WHERE conditions for the Activity items get method.
 599           *
 600           * @since 1.9.0
 601           *
 602           * @param array  $where_conditions Current conditions for MySQL WHERE statement.
 603           * @param array  $r                Parsed arguments passed into method.
 604           * @param string $select_sql       Current SELECT MySQL statement at point of execution.
 605           * @param string $from_sql         Current FROM MySQL statement at point of execution.
 606           * @param string $join_sql         Current INNER JOIN MySQL statement at point of execution.
 607           */
 608          $where_conditions = apply_filters( 'bp_activity_get_where_conditions', $where_conditions, $r, $select_sql, $from_sql, $join_sql );
 609  
 610          // Join the where conditions together.
 611          $where_sql = 'WHERE ' . join( ' AND ', $where_conditions );
 612  
 613          /**
 614           * Filter the MySQL JOIN clause for the main activity query.
 615           *
 616           * @since 2.5.0
 617           *
 618           * @param string $join_sql   JOIN clause.
 619           * @param array  $r          Method parameters.
 620           * @param string $select_sql Current SELECT MySQL statement.
 621           * @param string $from_sql   Current FROM MySQL statement.
 622           * @param string $where_sql  Current WHERE MySQL statement.
 623           */
 624          $join_sql = apply_filters( 'bp_activity_get_join_sql', $join_sql, $r, $select_sql, $from_sql, $where_sql );
 625  
 626          // Sanitize page and per_page parameters.
 627          $page     = absint( $r['page']     );
 628          $per_page = absint( $r['per_page'] );
 629  
 630          $retval = array(
 631              'activities'     => null,
 632              'total'          => null,
 633              'has_more_items' => null,
 634          );
 635  
 636          // Init the activity list.
 637          $activities     = array();
 638          $only_get_count = (bool) $r['count_total_only'];
 639  
 640          /**
 641           * Filters if BuddyPress should use legacy query structure over current structure for version 2.0+.
 642           *
 643           * It is not recommended to use the legacy structure, but allowed to if needed.
 644           *
 645           * @since 2.0.0
 646           *
 647           * @param bool                 $value Whether to use legacy structure or not.
 648           * @param BP_Activity_Activity $value Current method being called.
 649           * @param array                $r     Parsed arguments passed into method.
 650           */
 651          if ( ! $only_get_count && apply_filters( 'bp_use_legacy_activity_query', false, __METHOD__, $r ) ) {
 652  
 653              // Legacy queries joined against the user table.
 654              $select_sql = "SELECT DISTINCT a.*, u.user_email, u.user_nicename, u.user_login, u.display_name";
 655              $from_sql   = " FROM {$bp->activity->table_name} a LEFT JOIN {$wpdb->users} u ON a.user_id = u.ID";
 656  
 657              if ( ! empty( $page ) && ! empty( $per_page ) ) {
 658                  $pag_sql = $wpdb->prepare( "LIMIT %d, %d", absint( ( $page - 1 ) * $per_page ), $per_page );
 659  
 660                  /** This filter is documented in bp-activity/bp-activity-classes.php */
 661                  $activity_sql = apply_filters( 'bp_activity_get_user_join_filter', "{$select_sql} {$from_sql} {$join_sql} {$where_sql} ORDER BY a.date_recorded {$sort}, a.id {$sort} {$pag_sql}", $select_sql, $from_sql, $where_sql, $sort, $pag_sql );
 662              } else {
 663                  $pag_sql = '';
 664  
 665                  /**
 666                   * Filters the legacy MySQL query statement so plugins can alter before results are fetched.
 667                   *
 668                   * @since 1.5.0
 669                   *
 670                   * @param string $value      Concatenated MySQL statement pieces to be query results with for legacy query.
 671                   * @param string $select_sql Final SELECT MySQL statement portion for legacy query.
 672                   * @param string $from_sql   Final FROM MySQL statement portion for legacy query.
 673                   * @param string $where_sql  Final WHERE MySQL statement portion for legacy query.
 674                   * @param string $sort       Final sort direction for legacy query.
 675                   */
 676                  $activity_sql = apply_filters( 'bp_activity_get_user_join_filter', "{$select_sql} {$from_sql} {$join_sql} {$where_sql} ORDER BY a.date_recorded {$sort}, a.id {$sort}", $select_sql, $from_sql, $where_sql, $sort, $pag_sql );
 677              }
 678  
 679              /*
 680               * Queries that include 'last_activity' are cached separately,
 681               * since they are generally much less long-lived.
 682               */
 683              if ( preg_match( '/a\.type NOT IN \([^\)]*\'last_activity\'[^\)]*\)/', $activity_sql ) ) {
 684                  $cache_group = 'bp_activity';
 685              } else {
 686                  $cache_group = 'bp_activity_with_last_activity';
 687              }
 688  
 689              $activities = $wpdb->get_results( $activity_sql );
 690  
 691              // Integer casting for legacy activity query.
 692              foreach ( (array) $activities as $i => $ac ) {
 693                  $activities[ $i ]->id                = (int) $ac->id;
 694                  $activities[ $i ]->item_id           = (int) $ac->item_id;
 695                  $activities[ $i ]->secondary_item_id = (int) $ac->secondary_item_id;
 696                  $activities[ $i ]->user_id           = (int) $ac->user_id;
 697                  $activities[ $i ]->hide_sitewide     = (int) $ac->hide_sitewide;
 698                  $activities[ $i ]->mptt_left         = (int) $ac->mptt_left;
 699                  $activities[ $i ]->mptt_right        = (int) $ac->mptt_right;
 700                  $activities[ $i ]->is_spam           = (int) $ac->is_spam;
 701              }
 702  
 703          } elseif ( ! $only_get_count ) {
 704              // Query first for activity IDs.
 705              $activity_ids_sql = "{$select_sql} {$from_sql} {$join_sql} {$where_sql} ORDER BY {$order_by} {$sort}, a.id {$sort}";
 706  
 707              if ( ! empty( $per_page ) && ! empty( $page ) ) {
 708                  // We query for $per_page + 1 items in order to
 709                  // populate the has_more_items flag.
 710                  $activity_ids_sql .= $wpdb->prepare( " LIMIT %d, %d", absint( ( $page - 1 ) * $per_page ), $per_page + 1 );
 711              }
 712  
 713              /**
 714               * Filters the paged activities MySQL statement.
 715               *
 716               * @since 2.0.0
 717               *
 718               * @param string $activity_ids_sql MySQL statement used to query for Activity IDs.
 719               * @param array  $r                Array of arguments passed into method.
 720               */
 721              $activity_ids_sql = apply_filters( 'bp_activity_paged_activities_sql', $activity_ids_sql, $r );
 722  
 723              /*
 724               * Queries that include 'last_activity' are cached separately,
 725               * since they are generally much less long-lived.
 726               */
 727              if ( preg_match( '/a\.type NOT IN \([^\)]*\'last_activity\'[^\)]*\)/', $activity_ids_sql ) ) {
 728                  $cache_group = 'bp_activity';
 729              } else {
 730                  $cache_group = 'bp_activity_with_last_activity';
 731              }
 732  
 733              $cached = bp_core_get_incremented_cache( $activity_ids_sql, $cache_group );
 734              if ( false === $cached ) {
 735                  $activity_ids = $wpdb->get_col( $activity_ids_sql );
 736                  bp_core_set_incremented_cache( $activity_ids_sql, $cache_group, $activity_ids );
 737              } else {
 738                  $activity_ids = $cached;
 739              }
 740  
 741              $retval['has_more_items'] = ! empty( $per_page ) && count( $activity_ids ) > $per_page;
 742  
 743              // If we've fetched more than the $per_page value, we
 744              // can discard the extra now.
 745              if ( ! empty( $per_page ) && count( $activity_ids ) === $per_page + 1 ) {
 746                  array_pop( $activity_ids );
 747              }
 748  
 749              if ( 'ids' === $r['fields'] ) {
 750                  $activities = array_map( 'intval', $activity_ids );
 751              } else {
 752                  $activities = self::get_activity_data( $activity_ids );
 753              }
 754          }
 755  
 756          if ( $activities && 'ids' !== $r['fields'] ) {
 757              // Get the fullnames of users so we don't have to query in the loop.
 758              $activities = self::append_user_fullnames( $activities );
 759  
 760              // Get activity meta.
 761              $activity_ids = array();
 762              foreach ( (array) $activities as $activity ) {
 763                  $activity_ids[] = $activity->id;
 764              }
 765  
 766              if ( ! empty( $activity_ids ) && $r['update_meta_cache'] ) {
 767                  bp_activity_update_meta_cache( $activity_ids );
 768              }
 769  
 770              if ( $activities && $r['display_comments'] ) {
 771                  $activities = BP_Activity_Activity::append_comments( $activities, $r['spam'] );
 772              }
 773  
 774              // Pre-fetch data associated with activity users and other objects.
 775              BP_Activity_Activity::prefetch_object_data( $activities );
 776  
 777              // Generate action strings.
 778              $activities = BP_Activity_Activity::generate_action_strings( $activities );
 779          }
 780  
 781          $retval['activities'] = $activities;
 782  
 783          // Only query the count total if requested.
 784          if ( ! empty( $r['count_total'] ) || $only_get_count ) {
 785              /**
 786               * Filters the total activities MySQL statement.
 787               *
 788               * @since 1.5.0
 789               *
 790               * @param string $value     MySQL statement used to query for total activities.
 791               * @param string $where_sql MySQL WHERE statement portion.
 792               * @param string $sort      Sort direction for query.
 793               */
 794              $total_activities_sql = apply_filters( 'bp_activity_total_activities_sql', "SELECT count(DISTINCT a.id) FROM {$bp->activity->table_name} a {$join_sql} {$where_sql}", $where_sql, $sort );
 795  
 796              /*
 797               * Queries that include 'last_activity' are cached separately,
 798               * since they are generally much less long-lived.
 799               */
 800              if ( preg_match( '/a\.type NOT IN \([^\)]*\'last_activity\'[^\)]*\)/', $total_activities_sql ) ) {
 801                  $cache_group = 'bp_activity';
 802              } else {
 803                  $cache_group = 'bp_activity_with_last_activity';
 804              }
 805  
 806              $cached = bp_core_get_incremented_cache( $total_activities_sql, $cache_group );
 807              if ( false === $cached ) {
 808                  $total_activities = $wpdb->get_var( $total_activities_sql );
 809                  bp_core_set_incremented_cache( $total_activities_sql, $cache_group, $total_activities );
 810              } else {
 811                  $total_activities = $cached;
 812              }
 813  
 814              // If $max is set, only return up to the max results.
 815              if ( ! empty( $r['max'] ) ) {
 816                  if ( (int) $total_activities > (int) $r['max'] ) {
 817                      $total_activities = $r['max'];
 818                  }
 819              }
 820  
 821              $retval['total'] = $total_activities;
 822          }
 823  
 824          return $retval;
 825      }
 826  
 827      /**
 828       * Convert activity IDs to activity objects, as expected in template loop.
 829       *
 830       * @since 2.0.0
 831       *
 832       * @param array $activity_ids Array of activity IDs.
 833       * @return array
 834       */
 835  	protected static function get_activity_data( $activity_ids = array() ) {
 836          global $wpdb;
 837  
 838          // Bail if no activity ID's passed.
 839          if ( empty( $activity_ids ) ) {
 840              return array();
 841          }
 842  
 843          // Get BuddyPress.
 844          $bp = buddypress();
 845  
 846          $activities   = array();
 847          $uncached_ids = bp_get_non_cached_ids( $activity_ids, 'bp_activity' );
 848  
 849          // Prime caches as necessary.
 850          if ( ! empty( $uncached_ids ) ) {
 851              // Format the activity ID's for use in the query below.
 852              $uncached_ids_sql = implode( ',', wp_parse_id_list( $uncached_ids ) );
 853  
 854              // Fetch data from activity table, preserving order.
 855              $queried_adata = $wpdb->get_results( "SELECT * FROM {$bp->activity->table_name} WHERE id IN ({$uncached_ids_sql})");
 856  
 857              // Put that data into the placeholders created earlier,
 858              // and add it to the cache.
 859              foreach ( (array) $queried_adata as $adata ) {
 860                  wp_cache_set( $adata->id, $adata, 'bp_activity' );
 861              }
 862          }
 863  
 864          // Now fetch data from the cache.
 865          foreach ( $activity_ids as $activity_id ) {
 866              // Integer casting.
 867              $activity = wp_cache_get( $activity_id, 'bp_activity' );
 868              if ( ! empty( $activity ) ) {
 869                  $activity->id                = (int) $activity->id;
 870                  $activity->user_id           = (int) $activity->user_id;
 871                  $activity->item_id           = (int) $activity->item_id;
 872                  $activity->secondary_item_id = (int) $activity->secondary_item_id;
 873                  $activity->hide_sitewide     = (int) $activity->hide_sitewide;
 874                  $activity->mptt_left         = (int) $activity->mptt_left;
 875                  $activity->mptt_right        = (int) $activity->mptt_right;
 876                  $activity->is_spam           = (int) $activity->is_spam;
 877              }
 878  
 879              $activities[] = $activity;
 880          }
 881  
 882          // Then fetch user data.
 883          $user_query = new BP_User_Query( array(
 884              'user_ids'        => wp_list_pluck( $activities, 'user_id' ),
 885              'populate_extras' => false,
 886          ) );
 887  
 888          // Associated located user data with activity items.
 889          foreach ( $activities as $a_index => $a_item ) {
 890              $a_user_id = intval( $a_item->user_id );
 891              $a_user    = isset( $user_query->results[ $a_user_id ] ) ? $user_query->results[ $a_user_id ] : '';
 892  
 893              if ( !empty( $a_user ) ) {
 894                  $activities[ $a_index ]->user_email    = $a_user->user_email;
 895                  $activities[ $a_index ]->user_nicename = $a_user->user_nicename;
 896                  $activities[ $a_index ]->user_login    = $a_user->user_login;
 897                  $activities[ $a_index ]->display_name  = $a_user->display_name;
 898              }
 899          }
 900  
 901          return $activities;
 902      }
 903  
 904      /**
 905       * Append xProfile fullnames to an activity array.
 906       *
 907       * @since 2.0.0
 908       *
 909       * @param array $activities Activities array.
 910       * @return array
 911       */
 912  	protected static function append_user_fullnames( $activities ) {
 913  
 914          if ( bp_is_active( 'xprofile' ) && ! empty( $activities ) ) {
 915              $activity_user_ids = wp_list_pluck( $activities, 'user_id' );
 916  
 917              if ( ! empty( $activity_user_ids ) ) {
 918                  $fullnames = bp_core_get_user_displaynames( $activity_user_ids );
 919                  if ( ! empty( $fullnames ) ) {
 920                      foreach ( (array) $activities as $i => $activity ) {
 921                          if ( ! empty( $fullnames[ $activity->user_id ] ) ) {
 922                              $activities[ $i ]->user_fullname = $fullnames[ $activity->user_id ];
 923                          }
 924                      }
 925                  }
 926              }
 927          }
 928  
 929          return $activities;
 930      }
 931  
 932      /**
 933       * Pre-fetch data for objects associated with activity items.
 934       *
 935       * Activity items are associated with users, and often with other
 936       * BuddyPress data objects. Here, we pre-fetch data about these
 937       * associated objects, so that inline lookups - done primarily when
 938       * building action strings - do not result in excess database queries.
 939       *
 940       * The only object data required for activity component activity types
 941       * (activity_update and activity_comment) is related to users, and that
 942       * info is fetched separately in BP_Activity_Activity::get_activity_data().
 943       * So this method contains nothing but a filter that allows other
 944       * components, such as bp-friends and bp-groups, to hook in and prime
 945       * their own caches at the beginning of an activity loop.
 946       *
 947       * @since 2.0.0
 948       *
 949       * @param array $activities Array of activities.
 950       * @return array $activities Array of activities.
 951       */
 952  	protected static function prefetch_object_data( $activities ) {
 953  
 954          /**
 955           * Filters inside prefetch_object_data method to aid in pre-fetching object data associated with activity item.
 956           *
 957           * @since 2.0.0
 958           *
 959           * @param array $activities Array of activities.
 960           */
 961          return apply_filters( 'bp_activity_prefetch_object_data', $activities );
 962      }
 963  
 964      /**
 965       * Generate action strings for the activities located in BP_Activity_Activity::get().
 966       *
 967       * If no string can be dynamically generated for a given item
 968       * (typically because the activity type has not been properly
 969       * registered), the static 'action' value pulled from the database will
 970       * be left in place.
 971       *
 972       * @since 2.0.0
 973       *
 974       * @param array $activities Array of activities.
 975       * @return array
 976       */
 977  	protected static function generate_action_strings( $activities ) {
 978          foreach ( $activities as $key => $activity ) {
 979              $generated_action = bp_activity_generate_action_string( $activity );
 980              if ( false !== $generated_action ) {
 981                  $activity->action = $generated_action;
 982              }
 983  
 984              $activities[ $key ] = $activity;
 985          }
 986  
 987          return $activities;
 988      }
 989  
 990      /**
 991       * Get the SQL for the 'meta_query' param in BP_Activity_Activity::get().
 992       *
 993       * We use WP_Meta_Query to do the heavy lifting of parsing the
 994       * meta_query array and creating the necessary SQL clauses. However,
 995       * since BP_Activity_Activity::get() builds its SQL differently than
 996       * WP_Query, we have to alter the return value (stripping the leading
 997       * AND keyword from the 'where' clause).
 998       *
 999       * @since 1.8.0
1000       *
1001       * @param array $meta_query An array of meta_query filters. See the
1002       *                          documentation for WP_Meta_Query for details.
1003       * @return array $sql_array 'join' and 'where' clauses.
1004       */
1005  	public static function get_meta_query_sql( $meta_query = array() ) {
1006          global $wpdb;
1007  
1008          $sql_array = array(
1009              'join'  => '',
1010              'where' => '',
1011          );
1012  
1013          if ( ! empty( $meta_query ) ) {
1014              $activity_meta_query = new WP_Meta_Query( $meta_query );
1015  
1016              // WP_Meta_Query expects the table name at
1017              // $wpdb->activitymeta.
1018              $wpdb->activitymeta = buddypress()->activity->table_name_meta;
1019  
1020              $meta_sql = $activity_meta_query->get_sql( 'activity', 'a', 'id' );
1021  
1022              // Strip the leading AND - BP handles it in get().
1023              $sql_array['where'] = preg_replace( '/^\sAND/', '', $meta_sql['where'] );
1024              $sql_array['join']  = $meta_sql['join'];
1025          }
1026  
1027          return $sql_array;
1028      }
1029  
1030      /**
1031       * Get the SQL for the 'date_query' param in BP_Activity_Activity::get().
1032       *
1033       * We use BP_Date_Query, which extends WP_Date_Query, to do the heavy lifting
1034       * of parsing the date_query array and creating the necessary SQL clauses.
1035       *
1036       * @since 2.1.0
1037       *
1038       * @param array $date_query An array of date_query parameters. See the
1039       *                          documentation for the first parameter of WP_Date_Query.
1040       * @return string
1041       */
1042  	public static function get_date_query_sql( $date_query = array() ) {
1043          return BP_Date_Query::get_where_sql( $date_query, 'a.date_recorded' );
1044      }
1045  
1046      /**
1047       * Get the SQL for the 'scope' param in BP_Activity_Activity::get().
1048       *
1049       * A scope is a predetermined set of activity arguments.  This method is used
1050       * to grab these activity arguments and override any existing args if needed.
1051       *
1052       * Can handle multiple scopes.
1053       *
1054       * @since 2.2.0
1055       *
1056       * @param  mixed $scope  The activity scope. Accepts string or array of scopes.
1057       * @param  array $r      Current activity arguments. Same as those of BP_Activity_Activity::get(),
1058       *                       but merged with defaults.
1059       * @return false|array 'sql' WHERE SQL string and 'override' activity args.
1060       */
1061  	public static function get_scope_query_sql( $scope = false, $r = array() ) {
1062  
1063          // Define arrays for future use.
1064          $query_args = array();
1065          $override   = array();
1066          $retval     = array();
1067  
1068          // Check for array of scopes.
1069          if ( is_array( $scope ) ) {
1070              $scopes = $scope;
1071  
1072              // Explode a comma separated string of scopes.
1073          } elseif ( is_string( $scope ) ) {
1074              $scopes = explode( ',', $scope );
1075          }
1076  
1077          // Bail if no scope passed.
1078          if ( empty( $scopes ) ) {
1079              return false;
1080          }
1081  
1082          // Helper to easily grab the 'user_id'.
1083          if ( ! empty( $r['filter']['user_id'] ) ) {
1084              $r['user_id'] = $r['filter']['user_id'];
1085          }
1086  
1087          // Parse each scope; yes! we handle multiples!
1088          foreach ( $scopes as $scope ) {
1089              $scope_args = array();
1090  
1091              /**
1092               * Plugins can hook here to set their activity arguments for custom scopes.
1093               *
1094               * This is a dynamic filter based on the activity scope. eg:
1095               *   - 'bp_activity_set_groups_scope_args'
1096               *   - 'bp_activity_set_friends_scope_args'
1097               *
1098               * To see how this filter is used, plugin devs should check out:
1099               *   - bp_groups_filter_activity_scope() - used for 'groups' scope
1100               *   - bp_friends_filter_activity_scope() - used for 'friends' scope
1101               *
1102               * @since 2.2.0
1103               *
1104               * @param array {
1105               *     Activity query clauses.
1106               *     @type array {
1107               *         Activity arguments for your custom scope.
1108               *         See {@link BP_Activity_Query::_construct()} for more details.
1109               *     }
1110               *     @type array  $override Optional. Override existing activity arguments passed by $r.
1111               *     }
1112               * }
1113               * @param array $r Current activity arguments passed in BP_Activity_Activity::get().
1114               */
1115              $scope_args = apply_filters( "bp_activity_set_{$scope}_scope_args", array(), $r );
1116  
1117              if ( ! empty( $scope_args ) ) {
1118                  // Merge override properties from other scopes
1119                  // this might be a problem...
1120                  if ( ! empty( $scope_args['override'] ) ) {
1121                      $override = array_merge( $override, $scope_args['override'] );
1122                      unset( $scope_args['override'] );
1123                  }
1124  
1125                  // Save scope args.
1126                  if ( ! empty( $scope_args ) ) {
1127                      $query_args[] = $scope_args;
1128                  }
1129              }
1130          }
1131  
1132          if ( ! empty( $query_args ) ) {
1133              // Set relation to OR.
1134              $query_args['relation'] = 'OR';
1135  
1136              $query = new BP_Activity_Query( $query_args );
1137              $sql   = $query->get_sql();
1138              if ( ! empty( $sql ) ) {
1139                  $retval['sql'] = $sql;
1140              }
1141          }
1142  
1143          if ( ! empty( $override ) ) {
1144              $retval['override'] = $override;
1145          }
1146  
1147          return $retval;
1148      }
1149  
1150      /**
1151       * In BuddyPress 1.2.x, this was used to retrieve specific activity stream items (for example, on an activity's permalink page).
1152       *
1153       * As of 1.5.x, use BP_Activity_Activity::get() with an 'in' parameter instead.
1154       *
1155       * @since 1.2.0
1156       *
1157       * @deprecated 1.5
1158       * @deprecated Use BP_Activity_Activity::get() with an 'in' parameter instead.
1159       *
1160       * @param mixed    $activity_ids     Array or comma-separated string of activity IDs to retrieve.
1161       * @param int|bool $max              Maximum number of results to return. (Optional; default is no maximum).
1162       * @param int      $page             The set of results that the user is viewing. Used in pagination. (Optional; default is 1).
1163       * @param int      $per_page         Specifies how many results per page. Used in pagination. (Optional; default is 25).
1164       * @param string   $sort             MySQL column sort; ASC or DESC. (Optional; default is DESC).
1165       * @param bool     $display_comments Retrieve an activity item's associated comments or not. (Optional; default is false).
1166       * @return array
1167       */
1168  	public static function get_specific( $activity_ids, $max = false, $page = 1, $per_page = 25, $sort = 'DESC', $display_comments = false ) {
1169          _deprecated_function(
1170              __FUNCTION__,
1171              '1.5',
1172              'Use BP_Activity_Activity::get() with the "in" parameter instead.'
1173          );
1174  
1175          return BP_Activity_Activity::get( $max, $page, $per_page, $sort, false, false, $display_comments, false, false, $activity_ids );
1176      }
1177  
1178      /**
1179       * Get the first activity ID that matches a set of criteria.
1180       *
1181       * @since 1.2.0
1182       * @since 10.0.0 Parameters were made optional.
1183       *
1184       * @param array $args {
1185       *     An array of arguments. All items are optional.
1186       *     @type int    $user_id           User ID to filter by.
1187       *     @type string $component         Component to filter by.
1188       *     @type string $type              Activity type to filter by.
1189       *     @type int    $item_id           Associated item to filter by.
1190       *     @type int    $secondary_item_id Secondary associated item to filter by.
1191       *     @type string $action            Action to filter by.
1192       *     @type string $content           Content to filter by.
1193       *     @type string $date_recorded     Date to filter by.
1194       * }
1195       * @return int|false Activity ID on success, false if none is found.
1196       */
1197  	public static function get_id( $args = array() ) {
1198          global $wpdb;
1199  
1200          $function_args = func_get_args();
1201  
1202          // Backward compatibility with old method of passing arguments.
1203          if ( ! is_array( $args ) || count( $function_args ) > 1 ) {
1204              _deprecated_argument(
1205                  __METHOD__,
1206                  '10.0.0',
1207                  sprintf(
1208                      /* translators: 1: the name of the method. 2: the name of the file. */
1209                      esc_html__( 'Arguments passed to %1$s should be in an associative array. See the inline documentation at %2$s for more details.', 'buddypress' ),
1210                      __METHOD__,
1211                      __FILE__
1212                  )
1213              );
1214  
1215              $old_args_keys = array(
1216                  0 => 'user_id',
1217                  1 => 'component',
1218                  2 => 'type',
1219                  3 => 'item_id',
1220                  4 => 'secondary_item_id',
1221                  5 => 'action',
1222                  6 => 'content',
1223                  7 => 'date_recorded',
1224              );
1225  
1226              $args = bp_core_parse_args_array( $old_args_keys, $function_args );
1227          }
1228  
1229          $r = bp_parse_args(
1230              $args,
1231              array(
1232                  'user_id'           => false,
1233                  'component'         => false,
1234                  'type'              => false,
1235                  'item_id'           => false,
1236                  'secondary_item_id' => false,
1237                  'action'            => false,
1238                  'content'           => false,
1239                  'date_recorded'     => false,
1240              )
1241          );
1242  
1243          $where_args = false;
1244  
1245          if ( ! empty( $r['user_id'] ) ) {
1246              $where_args[] = $wpdb->prepare( 'user_id = %d', $r['user_id'] );
1247          }
1248  
1249          if ( ! empty( $r['component'] ) ) {
1250              $where_args[] = $wpdb->prepare( 'component = %s', $r['component'] );
1251          }
1252  
1253          if ( ! empty( $r['type'] ) ) {
1254              $where_args[] = $wpdb->prepare( 'type = %s', $r['type'] );
1255          }
1256  
1257          if ( ! empty( $r['item_id'] ) ) {
1258              $where_args[] = $wpdb->prepare( 'item_id = %d', $r['item_id'] );
1259          }
1260  
1261          if ( ! empty( $r['secondary_item_id'] ) ) {
1262              $where_args[] = $wpdb->prepare( 'secondary_item_id = %d', $r['secondary_item_id'] );
1263          }
1264  
1265          if ( ! empty( $r['action'] ) ) {
1266              $where_args[] = $wpdb->prepare( 'action = %s', $r['action'] );
1267          }
1268  
1269          if ( ! empty( $r['content'] ) ) {
1270              $where_args[] = $wpdb->prepare( 'content = %s', $r['content'] );
1271          }
1272  
1273          if ( ! empty( $r['date_recorded'] ) ) {
1274              $where_args[] = $wpdb->prepare( 'date_recorded = %s', $r['date_recorded'] );
1275          }
1276  
1277          if ( ! empty( $where_args ) ) {
1278              $bp        = buddypress();
1279              $where_sql = 'WHERE ' . join( ' AND ', $where_args );
1280              $result    = $wpdb->get_var( "SELECT id FROM {$bp->activity->table_name} {$where_sql}" );
1281  
1282              return is_numeric( $result ) ? (int) $result : false;
1283          }
1284  
1285          return false;
1286      }
1287  
1288      /**
1289       * Delete activity items from the database.
1290       *
1291       * To delete a specific activity item, pass an 'id' parameter.
1292       * Otherwise use the filters.
1293       *
1294       * @since 1.2.0
1295       *
1296       * @param array $args {
1297       *     @int    $id                Optional. The ID of a specific item to delete.
1298       *     @string $action            Optional. The action to filter by.
1299       *     @string $content           Optional. The content to filter by.
1300       *     @string $component         Optional. The component name to filter by.
1301       *     @string $type              Optional. The activity type to filter by.
1302       *     @string $primary_link      Optional. The primary URL to filter by.
1303       *     @int    $user_id           Optional. The user ID to filter by.
1304       *     @int    $item_id           Optional. The associated item ID to filter by.
1305       *     @int    $secondary_item_id Optional. The secondary associated item ID to filter by.
1306       *     @string $date_recorded     Optional. The date to filter by.
1307       *     @int    $hide_sitewide     Optional. Default: false.
1308       * }
1309       * @return array|bool An array of deleted activity IDs on success, false on failure.
1310       */
1311  	public static function delete( $args = array() ) {
1312          global $wpdb;
1313  
1314          $bp = buddypress();
1315          $r  = bp_parse_args(
1316              $args,
1317              array(
1318                  'id'                => false,
1319                  'action'            => false,
1320                  'content'           => false,
1321                  'component'         => false,
1322                  'type'              => false,
1323                  'primary_link'      => false,
1324                  'user_id'           => false,
1325                  'item_id'           => false,
1326                  'secondary_item_id' => false,
1327                  'date_recorded'     => false,
1328                  'hide_sitewide'     => false,
1329              )
1330          );
1331  
1332          // Setup empty array from where query arguments.
1333          $where_args = array();
1334  
1335          // ID.
1336          if ( ! empty( $r['id'] ) ) {
1337              $where_args[] = $wpdb->prepare( "id = %d", $r['id'] );
1338          }
1339  
1340          // User ID.
1341          if ( ! empty( $r['user_id'] ) ) {
1342              $where_args[] = $wpdb->prepare( "user_id = %d", $r['user_id'] );
1343          }
1344  
1345          // Action.
1346          if ( ! empty( $r['action'] ) ) {
1347              $where_args[] = $wpdb->prepare( "action = %s", $r['action'] );
1348          }
1349  
1350          // Content.
1351          if ( ! empty( $r['content'] ) ) {
1352              $where_args[] = $wpdb->prepare( "content = %s", $r['content'] );
1353          }
1354  
1355          // Component.
1356          if ( ! empty( $r['component'] ) ) {
1357              $where_args[] = $wpdb->prepare( "component = %s", $r['component'] );
1358          }
1359  
1360          // Type.
1361          if ( ! empty( $r['type'] ) ) {
1362              $where_args[] = $wpdb->prepare( "type = %s", $r['type'] );
1363          }
1364  
1365          // Primary Link.
1366          if ( ! empty( $r['primary_link'] ) ) {
1367              $where_args[] = $wpdb->prepare( "primary_link = %s", $r['primary_link'] );
1368          }
1369  
1370          // Item ID.
1371          if ( ! empty( $r['item_id'] ) ) {
1372              $where_args[] = $wpdb->prepare( "item_id = %d", $r['item_id'] );
1373          }
1374  
1375          // Secondary item ID.
1376          if ( ! empty( $r['secondary_item_id'] ) ) {
1377              $where_args[] = $wpdb->prepare( "secondary_item_id = %d", $r['secondary_item_id'] );
1378          }
1379  
1380          // Date Recorded.
1381          if ( ! empty( $r['date_recorded'] ) ) {
1382              $where_args[] = $wpdb->prepare( "date_recorded = %s", $r['date_recorded'] );
1383          }
1384  
1385          // Hidden sitewide.
1386          if ( ! empty( $r['hide_sitewide'] ) ) {
1387              $where_args[] = $wpdb->prepare( "hide_sitewide = %d", $r['hide_sitewide'] );
1388          }
1389  
1390          // Bail if no where arguments.
1391          if ( empty( $where_args ) ) {
1392              return false;
1393          }
1394  
1395          // Join the where arguments for querying.
1396          $where_sql = 'WHERE ' . join( ' AND ', $where_args );
1397  
1398          // Fetch all activities being deleted so we can perform more actions.
1399          $activities = $wpdb->get_results( "SELECT * FROM {$bp->activity->table_name} {$where_sql}" );
1400  
1401          /**
1402           * Action to allow intercepting activity items to be deleted.
1403           *
1404           * @since 2.3.0
1405           *
1406           * @param array $activities Array of activities.
1407           * @param array $r          Array of parsed arguments.
1408           */
1409          do_action_ref_array( 'bp_activity_before_delete', array( $activities, $r ) );
1410  
1411          // Attempt to delete activities from the database.
1412          $deleted = $wpdb->query( "DELETE FROM {$bp->activity->table_name} {$where_sql}" );
1413  
1414          // Bail if nothing was deleted.
1415          if ( empty( $deleted ) ) {
1416              return false;
1417          }
1418  
1419          /**
1420           * Action to allow intercepting activity items just deleted.
1421           *
1422           * @since 2.3.0
1423           *
1424           * @param array $activities Array of activities.
1425           * @param array $r          Array of parsed arguments.
1426           */
1427          do_action_ref_array( 'bp_activity_after_delete', array( $activities, $r ) );
1428  
1429          // Pluck the activity IDs out of the $activities array.
1430          $activity_ids = wp_parse_id_list( wp_list_pluck( $activities, 'id' ) );
1431  
1432          // Handle accompanying activity comments and meta deletion.
1433          if ( ! empty( $activity_ids ) ) {
1434  
1435              // Delete all activity meta entries for activity items.
1436              BP_Activity_Activity::delete_activity_meta_entries( $activity_ids );
1437  
1438              // Setup empty array for comments.
1439              $comment_ids = array();
1440  
1441              // Loop through activity ids and attempt to delete comments.
1442              foreach ( $activity_ids as $activity_id ) {
1443  
1444                  // Attempt to delete comments.
1445                  $comments = BP_Activity_Activity::delete( array(
1446                      'type'    => 'activity_comment',
1447                      'item_id' => $activity_id
1448                  ) );
1449  
1450                  // Merge IDs together.
1451                  if ( ! empty( $comments ) ) {
1452                      $comment_ids = array_merge( $comment_ids, $comments );
1453                  }
1454              }
1455  
1456              // Merge activity IDs with any deleted comment IDs.
1457              if ( ! empty( $comment_ids ) ) {
1458                  $activity_ids = array_unique( array_merge( $activity_ids, $comment_ids ) );
1459              }
1460          }
1461  
1462          return $activity_ids;
1463      }
1464  
1465      /**
1466       * Delete the comments associated with a set of activity items.
1467       *
1468       * This method is no longer used by BuddyPress, and it is recommended not to
1469       * use it going forward, and use BP_Activity_Activity::delete() instead.
1470       *
1471       * @since 1.2.0
1472       *
1473       * @deprecated 2.3.0
1474       *
1475       * @param array $activity_ids Activity IDs whose comments should be deleted.
1476       * @param bool  $delete_meta  Should we delete the activity meta items for these comments.
1477       * @return bool True on success.
1478       */
1479  	public static function delete_activity_item_comments( $activity_ids = array(), $delete_meta = true ) {
1480          global $wpdb;
1481  
1482          $bp = buddypress();
1483  
1484          $delete_meta  = (bool) $delete_meta;
1485          $activity_ids = implode( ',', wp_parse_id_list( $activity_ids ) );
1486  
1487          if ( $delete_meta ) {
1488              // Fetch the activity comment IDs for our deleted activity items.
1489              $activity_comment_ids = $wpdb->get_col( "SELECT id FROM {$bp->activity->table_name} WHERE type = 'activity_comment' AND item_id IN ({$activity_ids})" );
1490  
1491              if ( ! empty( $activity_comment_ids ) ) {
1492                  self::delete_activity_meta_entries( $activity_comment_ids );
1493              }
1494          }
1495  
1496          return $wpdb->query( "DELETE FROM {$bp->activity->table_name} WHERE type = 'activity_comment' AND item_id IN ({$activity_ids})" );
1497      }
1498  
1499      /**
1500       * Delete the meta entries associated with a set of activity items.
1501       *
1502       * @since 1.2.0
1503       *
1504       * @param array $activity_ids Activity IDs whose meta should be deleted.
1505       * @return bool True on success.
1506       */
1507  	public static function delete_activity_meta_entries( $activity_ids = array() ) {
1508          $activity_ids = wp_parse_id_list( $activity_ids );
1509  
1510          foreach ( $activity_ids as $activity_id ) {
1511              bp_activity_delete_meta( $activity_id );
1512          }
1513  
1514          return true;
1515      }
1516  
1517      /**
1518       * Append activity comments to their associated activity items.
1519       *
1520       * @since 1.2.0
1521       *
1522       * @global wpdb $wpdb WordPress database object.
1523       *
1524       * @param array  $activities Activities to fetch comments for.
1525       * @param string $spam       Optional. 'ham_only' (default), 'spam_only' or 'all'.
1526       * @return array The updated activities with nested comments.
1527       */
1528  	public static function append_comments( $activities, $spam = 'ham_only' ) {
1529          $activity_comments = array();
1530  
1531          // Now fetch the activity comments and parse them into the correct position in the activities array.
1532          foreach ( (array) $activities as $activity ) {
1533              $top_level_parent_id = 'activity_comment' == $activity->type ? $activity->item_id : 0;
1534              $activity_comments[$activity->id] = BP_Activity_Activity::get_activity_comments( $activity->id, $activity->mptt_left, $activity->mptt_right, $spam, $top_level_parent_id );
1535          }
1536  
1537          // Merge the comments with the activity items.
1538          foreach ( (array) $activities as $key => $activity ) {
1539              if ( isset( $activity_comments[$activity->id] ) ) {
1540                  $activities[$key]->children = $activity_comments[$activity->id];
1541              }
1542          }
1543  
1544          return $activities;
1545      }
1546  
1547      /**
1548       * Get activity comments that are associated with a specific activity ID.
1549       *
1550       * @since 1.2.0
1551       *
1552       * @global wpdb $wpdb WordPress database object.
1553       *
1554       * @param int    $activity_id         Activity ID to fetch comments for.
1555       * @param int    $left                Left-most node boundary.
1556       * @param int    $right               Right-most node boundary.
1557       * @param string $spam                Optional. 'ham_only' (default), 'spam_only' or 'all'.
1558       * @param int    $top_level_parent_id Optional. The id of the root-level parent activity item.
1559       * @return array The updated activities with nested comments.
1560       */
1561  	public static function get_activity_comments( $activity_id, $left, $right, $spam = 'ham_only', $top_level_parent_id = 0 ) {
1562          global $wpdb;
1563  
1564          $function_args = func_get_args();
1565  
1566          if ( empty( $top_level_parent_id ) ) {
1567              $top_level_parent_id = $activity_id;
1568          }
1569  
1570          $comments = wp_cache_get( $activity_id, 'bp_activity_comments' );
1571  
1572          // We store the string 'none' to cache the fact that the
1573          // activity item has no comments.
1574          if ( 'none' === $comments ) {
1575              $comments = false;
1576  
1577              // A true cache miss.
1578          } elseif ( empty( $comments ) ) {
1579  
1580              $bp = buddypress();
1581  
1582              // Select the user's fullname with the query.
1583              if ( bp_is_active( 'xprofile' ) ) {
1584                  $fullname_select = ", pd.value as user_fullname";
1585                  $fullname_from = ", {$bp->profile->table_name_data} pd ";
1586                  $fullname_where = "AND pd.user_id = a.user_id AND pd.field_id = 1";
1587  
1588                  // Prevent debug errors.
1589              } else {
1590                  $fullname_select = $fullname_from = $fullname_where = '';
1591              }
1592  
1593              // Don't retrieve activity comments marked as spam.
1594              if ( 'ham_only' == $spam ) {
1595                  $spam_sql = 'AND a.is_spam = 0';
1596              } elseif ( 'spam_only' == $spam ) {
1597                  $spam_sql = 'AND a.is_spam = 1';
1598              } else {
1599                  $spam_sql = '';
1600              }
1601  
1602              // Legacy query - not recommended.
1603  
1604              /**
1605               * Filters if BuddyPress should use the legacy activity query.
1606               *
1607               * @since 2.0.0
1608               *
1609               * @param bool                 $value     Whether or not to use the legacy query.
1610               * @param BP_Activity_Activity $value     Magic method referring to currently called method.
1611               * @param array                $func_args Array of the method's argument list.
1612               */
1613              if ( apply_filters( 'bp_use_legacy_activity_query', false, __METHOD__, $function_args ) ) {
1614  
1615                  /**
1616                   * Filters the MySQL prepared statement for the legacy activity query.
1617                   *
1618                   * @since 1.5.0
1619                   *
1620                   * @param string $value       Prepared statement for the activity query.
1621                   * @param int    $activity_id Activity ID to fetch comments for.
1622                   * @param int    $left        Left-most node boundary.
1623                   * @param int    $right       Right-most node boundary.
1624                   * @param string $spam_sql    SQL Statement portion to differentiate between ham or spam.
1625                   */
1626                  $sql = apply_filters( 'bp_activity_comments_user_join_filter', $wpdb->prepare( "SELECT a.*, u.user_email, u.user_nicename, u.user_login, u.display_name{$fullname_select} FROM {$bp->activity->table_name} a, {$wpdb->users} u{$fullname_from} WHERE u.ID = a.user_id {$fullname_where} AND a.type = 'activity_comment' {$spam_sql} AND a.item_id = %d AND a.mptt_left > %d AND a.mptt_left < %d ORDER BY a.date_recorded ASC", $top_level_parent_id, $left, $right ), $activity_id, $left, $right, $spam_sql );
1627  
1628                  $descendants = $wpdb->get_results( $sql );
1629  
1630                  // We use the mptt BETWEEN clause to limit returned
1631                  // descendants to the correct part of the tree.
1632              } else {
1633                  $sql = $wpdb->prepare( "SELECT id FROM {$bp->activity->table_name} a WHERE a.type = 'activity_comment' {$spam_sql} AND a.item_id = %d and a.mptt_left > %d AND a.mptt_left < %d ORDER BY a.date_recorded ASC", $top_level_parent_id, $left, $right );
1634  
1635                  $descendant_ids = $wpdb->get_col( $sql );
1636                  $descendants    = self::get_activity_data( $descendant_ids );
1637                  $descendants    = self::append_user_fullnames( $descendants );
1638                  $descendants    = self::generate_action_strings( $descendants );
1639              }
1640  
1641              $ref = array();
1642  
1643              // Loop descendants and build an assoc array.
1644              foreach ( (array) $descendants as $d ) {
1645                  $d->children = array();
1646  
1647                  // If we have a reference on the parent.
1648                  if ( isset( $ref[ $d->secondary_item_id ] ) ) {
1649                      $ref[ $d->secondary_item_id ]->children[ $d->id ] = $d;
1650                      $ref[ $d->id ] =& $ref[ $d->secondary_item_id ]->children[ $d->id ];
1651  
1652                      // If we don't have a reference on the parent, put in the root level.
1653                  } else {
1654                      $comments[ $d->id ] = $d;
1655                      $ref[ $d->id ] =& $comments[ $d->id ];
1656                  }
1657              }
1658  
1659              // Calculate depth for each item.
1660              foreach ( $ref as &$r ) {
1661                  $depth = 1;
1662                  $parent_id = $r->secondary_item_id;
1663  
1664                  while ( $parent_id !== $r->item_id ) {
1665                      $depth++;
1666  
1667                      // When display_comments=stream, the parent comment may not be part of the
1668                      // returned results, so we manually fetch it.
1669                      if ( empty( $ref[ $parent_id ] ) ) {
1670                          $direct_parent = new BP_Activity_Activity( $parent_id );
1671                          if ( isset( $direct_parent->secondary_item_id ) ) {
1672                              // If the direct parent is not an activity update, that means we've reached
1673                              // the parent activity item (eg. new_blog_post).
1674                              if ( 'activity_update' !== $direct_parent->type ) {
1675                                  $parent_id = $r->item_id;
1676  
1677                              } else {
1678                                  $parent_id = $direct_parent->secondary_item_id;
1679                              }
1680  
1681                          } else {
1682                              // Something went wrong.  Short-circuit the depth calculation.
1683                              $parent_id = $r->item_id;
1684                          }
1685                      } else {
1686                          $parent_id = $ref[ $parent_id ]->secondary_item_id;
1687                      }
1688                  }
1689                  $r->depth = $depth;
1690              }
1691  
1692              // If we cache a value of false, it'll count as a cache
1693              // miss the next time the activity comments are fetched.
1694              // Storing the string 'none' is a hack workaround to
1695              // avoid unnecessary queries.
1696              if ( false === $comments ) {
1697                  $cache_value = 'none';
1698              } else {
1699                  $cache_value = $comments;
1700              }
1701  
1702              wp_cache_set( $activity_id, $cache_value, 'bp_activity_comments' );
1703          }
1704  
1705          return $comments;
1706      }
1707  
1708      /**
1709       * Rebuild nested comment tree under an activity or activity comment.
1710       *
1711       * @since 1.2.0
1712       *
1713       * @global wpdb $wpdb WordPress database object.
1714       *
1715       * @param int $parent_id ID of an activity or activity comment.
1716       * @param int $left      Node boundary start for activity or activity comment.
1717       * @return int Right Node boundary of activity or activity comment.
1718       */
1719  	public static function rebuild_activity_comment_tree( $parent_id, $left = 1 ) {
1720          global $wpdb;
1721  
1722          $bp = buddypress();
1723  
1724          // The right value of this node is the left value + 1.
1725          $right = intval( $left + 1 );
1726  
1727          // Get all descendants of this node.
1728          $comments    = BP_Activity_Activity::get_child_comments( $parent_id );
1729          $descendants = wp_list_pluck( $comments, 'id' );
1730  
1731          // Loop the descendants and recalculate the left and right values.
1732          foreach ( (array) $descendants as $descendant_id ) {
1733              $right = BP_Activity_Activity::rebuild_activity_comment_tree( $descendant_id, $right );
1734          }
1735  
1736          // We've got the left value, and now that we've processed the children
1737          // of this node we also know the right value.
1738          if ( 1 === $left ) {
1739              $wpdb->query( $wpdb->prepare( "UPDATE {$bp->activity->table_name} SET mptt_left = %d, mptt_right = %d WHERE id = %d", $left, $right, $parent_id ) );
1740          } else {
1741              $wpdb->query( $wpdb->prepare( "UPDATE {$bp->activity->table_name} SET mptt_left = %d, mptt_right = %d WHERE type = 'activity_comment' AND id = %d", $left, $right, $parent_id ) );
1742          }
1743  
1744          // Return the right value of this node + 1.
1745          return intval( $right + 1 );
1746      }
1747  
1748      /**
1749       * Get child comments of an activity or activity comment.
1750       *
1751       * @since 1.2.0
1752       *
1753       * @global wpdb $wpdb WordPress database object.
1754       *
1755       * @param int $parent_id ID of an activity or activity comment.
1756       * @return object Numerically indexed array of child comments.
1757       */
1758  	public static function get_child_comments( $parent_id ) {
1759          global $wpdb;
1760  
1761          $bp = buddypress();
1762  
1763          return $wpdb->get_results( $wpdb->prepare( "SELECT id FROM {$bp->activity->table_name} WHERE type = 'activity_comment' AND secondary_item_id = %d", $parent_id ) );
1764      }
1765  
1766      /**
1767       * Get a list of components that have recorded activity associated with them.
1768       *
1769       * @since 1.2.0
1770       *
1771       * @param bool $skip_last_activity If true, components will not be
1772       *                                 included if the only activity type associated with them is
1773       *                                 'last_activity'. (Since 2.0.0, 'last_activity' is stored in
1774       *                                 the activity table, but these items are not full-fledged
1775       *                                 activity items.) Default: true.
1776       * @return array List of component names.
1777       */
1778  	public static function get_recorded_components( $skip_last_activity = true ) {
1779          global $wpdb;
1780  
1781          $bp = buddypress();
1782  
1783          if ( true === $skip_last_activity ) {
1784              $components = $wpdb->get_col( "SELECT DISTINCT component FROM {$bp->activity->table_name} WHERE action != '' AND action != 'last_activity' ORDER BY component ASC" );
1785          } else {
1786              $components = $wpdb->get_col( "SELECT DISTINCT component FROM {$bp->activity->table_name} ORDER BY component ASC" );
1787          }
1788  
1789          return $components;
1790      }
1791  
1792      /**
1793       * Get sitewide activity items for use in an RSS feed.
1794       *
1795       * @since 1.0.0
1796       *
1797       * @param int $limit Optional. Number of items to fetch. Default: 35.
1798       * @return array $activity_feed List of activity items, with RSS data added.
1799       */
1800  	public static function get_sitewide_items_for_feed( $limit = 35 ) {
1801          $activities    = bp_activity_get_sitewide( array( 'max' => $limit ) );
1802          $activity_feed = array();
1803  
1804          for ( $i = 0, $count = count( $activities ); $i < $count; ++$i ) {
1805              $title                            = explode( '<span', $activities[$i]['content'] );
1806              $activity_feed[$i]['title']       = trim( strip_tags( $title[0] ) );
1807              $activity_feed[$i]['link']        = $activities[$i]['primary_link'];
1808              $activity_feed[$i]['description'] = @sprintf( $activities[$i]['content'], '' );
1809              $activity_feed[$i]['pubdate']     = $activities[$i]['date_recorded'];
1810          }
1811  
1812          return $activity_feed;
1813      }
1814  
1815      /**
1816       * Create SQL IN clause for filter queries.
1817       *
1818       * @since 1.5.0
1819       *
1820       * @see BP_Activity_Activity::get_filter_sql()
1821       *
1822       * @param string     $field The database field.
1823       * @param array|bool $items The values for the IN clause, or false when none are found.
1824       * @return string|false
1825       */
1826  	public static function get_in_operator_sql( $field, $items ) {
1827          global $wpdb;
1828  
1829          // Split items at the comma.
1830          if ( ! is_array( $items ) ) {
1831              $items = explode( ',', $items );
1832          }
1833  
1834          // Array of prepared integers or quoted strings.
1835          $items_prepared = array();
1836  
1837          // Clean up and format each item.
1838          foreach ( $items as $item ) {
1839              // Clean up the string.
1840              $item = trim( $item );
1841              // Pass everything through prepare for security and to safely quote strings.
1842              $items_prepared[] = ( is_numeric( $item ) ) ? $wpdb->prepare( '%d', $item ) : $wpdb->prepare( '%s', $item );
1843          }
1844  
1845          // Build IN operator sql syntax.
1846          if ( count( $items_prepared ) )
1847              return sprintf( '%s IN ( %s )', trim( $field ), implode( ',', $items_prepared ) );
1848          else
1849              return false;
1850      }
1851  
1852      /**
1853       * Create filter SQL clauses.
1854       *
1855       * @since 1.5.0
1856       *
1857       * @param array $filter_array {
1858       *     Fields and values to filter by.
1859       *
1860       *     @type array|string|int $user_id      User ID(s).
1861       *     @type array|string     $object       Corresponds to the 'component'
1862       *                                          column in the database.
1863       *     @type array|string     $action       Corresponds to the 'type' column
1864       *                                          in the database.
1865       *     @type array|string|int $primary_id   Corresponds to the 'item_id'
1866       *                                          column in the database.
1867       *     @type array|string|int $secondary_id Corresponds to the
1868       *                                          'secondary_item_id' column in the database.
1869       *     @type int              $offset       Return only those items with an ID greater
1870       *                                          than the offset value.
1871       *     @type string           $since        Return only those items that have a
1872       *                                          date_recorded value greater than a
1873       *                                          given MySQL-formatted date.
1874       * }
1875       * @return string The filter clause, for use in a SQL query.
1876       */
1877  	public static function get_filter_sql( $filter_array ) {
1878  
1879          $filter_sql = array();
1880  
1881          if ( !empty( $filter_array['user_id'] ) ) {
1882              $user_sql = BP_Activity_Activity::get_in_operator_sql( 'a.user_id', $filter_array['user_id'] );
1883              if ( !empty( $user_sql ) )
1884                  $filter_sql[] = $user_sql;
1885          }
1886  
1887          if ( !empty( $filter_array['object'] ) ) {
1888              $object_sql = BP_Activity_Activity::get_in_operator_sql( 'a.component', $filter_array['object'] );
1889              if ( !empty( $object_sql ) )
1890                  $filter_sql[] = $object_sql;
1891          }
1892  
1893          if ( !empty( $filter_array['action'] ) ) {
1894              $action_sql = BP_Activity_Activity::get_in_operator_sql( 'a.type', $filter_array['action'] );
1895              if ( ! empty( $action_sql ) )
1896                  $filter_sql[] = $action_sql;
1897          }
1898  
1899          if ( !empty( $filter_array['primary_id'] ) ) {
1900              $pid_sql = BP_Activity_Activity::get_in_operator_sql( 'a.item_id', $filter_array['primary_id'] );
1901              if ( !empty( $pid_sql ) )
1902                  $filter_sql[] = $pid_sql;
1903          }
1904  
1905          if ( !empty( $filter_array['secondary_id'] ) ) {
1906              $sid_sql = BP_Activity_Activity::get_in_operator_sql( 'a.secondary_item_id', $filter_array['secondary_id'] );
1907              if ( !empty( $sid_sql ) )
1908                  $filter_sql[] = $sid_sql;
1909          }
1910  
1911          if ( ! empty( $filter_array['offset'] ) ) {
1912              $sid_sql = absint( $filter_array['offset'] );
1913              $filter_sql[] = "a.id >= {$sid_sql}";
1914          }
1915  
1916          if ( ! empty( $filter_array['since'] ) ) {
1917              // Validate that this is a proper Y-m-d H:i:s date.
1918              // Trick: parse to UNIX date then translate back.
1919              $translated_date = date( 'Y-m-d H:i:s', strtotime( $filter_array['since'] ) );
1920              if ( $translated_date === $filter_array['since'] ) {
1921                  $filter_sql[] = "a.date_recorded > '{$translated_date}'";
1922              }
1923          }
1924  
1925          if ( empty( $filter_sql ) )
1926              return false;
1927  
1928          return join( ' AND ', $filter_sql );
1929      }
1930  
1931      /**
1932       * Get the date/time of last recorded activity.
1933       *
1934       * @since 1.2.0
1935       *
1936       * @return string ISO timestamp.
1937       */
1938  	public static function get_last_updated() {
1939          global $wpdb;
1940  
1941          $bp = buddypress();
1942  
1943          return $wpdb->get_var( "SELECT date_recorded FROM {$bp->activity->table_name} ORDER BY date_recorded DESC LIMIT 1" );
1944      }
1945  
1946      /**
1947       * Get favorite count for a given user.
1948       *
1949       * @since 1.2.0
1950       *
1951       * @param int $user_id The ID of the user whose favorites you're counting.
1952       * @return int $value A count of the user's favorites.
1953       */
1954  	public static function total_favorite_count( $user_id ) {
1955  
1956          // Get activities from user meta.
1957          $favorite_activity_entries = bp_get_user_meta( $user_id, 'bp_favorite_activities', true );
1958          if ( ! empty( $favorite_activity_entries ) ) {
1959              return count( $favorite_activity_entries );
1960          }
1961  
1962          // No favorites.
1963          return 0;
1964      }
1965  
1966      /**
1967       * Check whether an activity item exists with a given string content.
1968       *
1969       * @since 1.1.0
1970       *
1971       * @param string $content The content to filter by.
1972       * @return int|false The ID of the first matching item if found, otherwise false.
1973       */
1974  	public static function check_exists_by_content( $content ) {
1975          global $wpdb;
1976  
1977          $bp = buddypress();
1978  
1979          $result = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->activity->table_name} WHERE content = %s", $content ) );
1980  
1981          return is_numeric( $result ) ? (int) $result : false;
1982      }
1983  
1984      /**
1985       * Hide all activity for a given user.
1986       *
1987       * @since 1.2.0
1988       *
1989       * @param int $user_id The ID of the user whose activity you want to mark hidden.
1990       * @return mixed
1991       */
1992  	public static function hide_all_for_user( $user_id ) {
1993          global $wpdb;
1994  
1995          $bp = buddypress();
1996  
1997          return $wpdb->get_var( $wpdb->prepare( "UPDATE {$bp->activity->table_name} SET hide_sitewide = 1 WHERE user_id = %d", $user_id ) );
1998      }
1999  }


Generated: Tue Mar 19 01:01:09 2024 Cross-referenced by PHPXref 0.7.1