[ Index ] |
PHP Cross Reference of BuddyPress |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Nov 21 01:00:57 2024 | Cross-referenced by PHPXref 0.7.1 |