Ticket #8448: 8448.1.diff
File 8448.1.diff, 57.4 KB (added by , 4 years ago) |
---|
-
src/bp-core/admin/bp-core-admin-schema.php
diff --git a/src/bp-core/admin/bp-core-admin-schema.php b/src/bp-core/admin/bp-core-admin-schema.php index d7cab5bbe..3c1fd0a90 100644
a b function bp_core_install( $active_components = false ) { 40 40 // Install the invitations table. 41 41 bp_core_install_invitations(); 42 42 43 // Install the nonmember opt-outs table. 44 bp_core_install_nonmember_opt_outs(); 45 43 46 // Notifications. 44 47 if ( !empty( $active_components['notifications'] ) ) { 45 48 bp_core_install_notifications(); … … function bp_core_install_invitations() { 589 592 */ 590 593 do_action( 'bp_core_install_invitations' ); 591 594 } 595 596 /** 597 * Install database tables to store opt-out requests from nonmembers. 598 * 599 * @since 8.0.0 600 * 601 * @uses bp_core_set_charset() 602 * @uses bp_core_get_table_prefix() 603 * @uses dbDelta() 604 */ 605 function bp_core_install_nonmember_opt_outs() { 606 $sql = array(); 607 $charset_collate = $GLOBALS['wpdb']->get_charset_collate(); 608 $bp_prefix = bp_core_get_table_prefix(); 609 $optouts_class = new BP_Optout(); 610 $table_name = $optouts_class->get_table_name(); 611 $sql = "CREATE TABLE {$table_name} ( 612 id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, 613 email_address varchar(255) NOT NULL, 614 user_id bigint(20) NOT NULL, 615 email_type varchar(255) NOT NULL, 616 date_modified datetime NOT NULL, 617 KEY user_id (user_id), 618 KEY email_type (email_type), 619 KEY date_modified (date_modified) 620 ) {$charset_collate};"; 621 dbDelta( $sql ); 622 623 /** 624 * Fires after BuddyPress adds the nonmember opt-outs table. 625 * 626 * @since 8.0.0 627 */ 628 do_action( 'bp_core_install_nonmember_opt_outs' ); 629 } -
src/bp-core/bp-core-cache.php
diff --git a/src/bp-core/bp-core-cache.php b/src/bp-core/bp-core-cache.php index 12c674241..35d381a50 100644
a b function bp_clear_object_type_terms_cache( $type_id = 0, $taxonomy = '' ) { 415 415 add_action( 'bp_type_inserted', 'bp_clear_object_type_terms_cache' ); 416 416 add_action( 'bp_type_updated', 'bp_clear_object_type_terms_cache' ); 417 417 add_action( 'bp_type_deleted', 'bp_clear_object_type_terms_cache' ); 418 419 /** 420 * Resets all incremented bp_optout caches. 421 * 422 * @since 8.0.0 423 */ 424 function bp_optouts_reset_cache_incrementor() { 425 bp_core_reset_incrementor( 'bp_optouts' ); 426 } 427 add_action( 'bp_optout_after_save', 'bp_optouts_reset_cache_incrementor' ); 428 add_action( 'bp_optout_after_delete', 'bp_optouts_reset_cache_incrementor' ); -
src/bp-core/bp-core-functions.php
diff --git a/src/bp-core/bp-core-functions.php b/src/bp-core/bp-core-functions.php index 592f5b155..88e4b651b 100644
a b function bp_get_widget_max_count_limit( $widget_class = '' ) { 4232 4232 */ 4233 4233 return apply_filters( 'bp_get_widget_max_count_limit', 50, $widget_class ); 4234 4234 } 4235 4236 /** 4237 * Add a new BP_Optout. 4238 * 4239 * @since 8.0.0 4240 * 4241 * @param array $args { 4242 * An array of arguments describing the new opt-out. 4243 * @type string $email_address Email address of user who has opted out. 4244 * @type int $user_id Optional. ID of user whose communication 4245 * prompted the user to opt-out. 4246 * @type string $email_type Optional. Name of the email type that 4247 * prompted the user to opt-out. 4248 * @type string $date_modified Optional. Specify a time, else now will be used. 4249 * } 4250 * @return false | int False on failure, ID of new (or existing) opt-out if successful. 4251 */ 4252 function bp_add_optout( $args = array() ) { 4253 $optout = new BP_Optout(); 4254 $r = bp_parse_args( $args, array( 4255 'email_address' => '', 4256 'user_id' => 0, 4257 'email_type' => '', 4258 'date_modified' => bp_core_current_time(), 4259 ), 'add_optout' ); 4260 4261 // Opt-outs must have an email address. 4262 if ( empty( $r['email_address'] ) ) { 4263 return false; 4264 } 4265 4266 // Avoid creating duplicate opt-outs. 4267 $optout_id = $optout->optout_exists( array( 4268 'email_address' => $r['email_address'], 4269 'user_id' => $r['user_id'], 4270 'email_type' => $r['email_type'], 4271 ) ); 4272 4273 if ( ! $optout_id ) { 4274 // Set up the new opt-out. 4275 $optout->email_address = $r['email_address']; 4276 $optout->user_id = $r['user_id']; 4277 $optout->email_type = $r['email_type']; 4278 $optout->date_modified = $r['date_modified']; 4279 4280 $optout_id = $optout->save(); 4281 } 4282 4283 return $optout_id; 4284 } 4285 4286 /** 4287 * Find matching BP_Optouts. 4288 * 4289 * @since 8.0.0 4290 * 4291 * @see BP_Optout::get() for a description of parameters and return values. 4292 */ 4293 function bp_get_optouts( $args = array() ) { 4294 $optout_class = new BP_Optout(); 4295 return $optout_class::get( $args ); 4296 } 4297 4298 /** 4299 * Delete a BP_Optout by ID. 4300 * 4301 * @since 8.0.0 4302 * 4303 * @param int $id ID of the optout to delete. 4304 */ 4305 function bp_delete_optout_by_id( $id = 0 ) { 4306 $optout_class = new BP_Optout(); 4307 return $optout_class::delete_by_id( $id ); 4308 } -
src/bp-core/bp-core-update.php
diff --git a/src/bp-core/bp-core-update.php b/src/bp-core/bp-core-update.php index a7791c445..fe6a26d9e 100644
a b function bp_update_to_8_0() { 621 621 '%s', 622 622 ) 623 623 ); 624 625 bp_core_install_nonmember_opt_outs(); 624 626 } 625 627 626 628 /** -
new file src/bp-core/classes/class-bp-optout.php
diff --git a/src/bp-core/classes/class-bp-optout.php b/src/bp-core/classes/class-bp-optout.php new file mode 100644 index 000000000..c2957eeea
- + 1 <?php 2 /** 3 * BuddyPress Nonmember Opt-out Class 4 * 5 * @package BuddyPress 6 * @subpackage Nonmember Opt-outs 7 * 8 * @since 8.0.0 9 */ 10 11 // Exit if accessed directly. 12 defined( 'ABSPATH' ) || exit; 13 14 /** 15 * BuddyPress opt-outs. 16 * 17 * Use this class to create, access, edit, or delete BuddyPress Nonmember Opt-outs. 18 * 19 * @since 8.0.0 20 */ 21 class BP_Optout { 22 23 /** 24 * The opt-out ID. 25 * 26 * @since 8.0.0 27 * @access public 28 * @var int 29 */ 30 public $id; 31 32 /** 33 * The email address of the user that wishes to opt out of 34 * communications from this site. 35 * 36 * @since 8.0.0 37 * @access public 38 * @var string 39 */ 40 public $email_address; 41 42 /** 43 * The ID of the user that generated the contact that resulted in the opt-out. 44 * 45 * @since 8.0.0 46 * @access public 47 * @var int 48 */ 49 public $user_id; 50 51 /** 52 * The type of email contact that resulted in the opt-out. 53 * This should be one of the known BP_Email types. 54 * 55 * @since 8.0.0 56 * @access public 57 * @var string 58 */ 59 public $email_type; 60 61 /** 62 * The date the opt-out was last modified. 63 * 64 * @since 8.0.0 65 * @access public 66 * @var string 67 */ 68 public $date_modified; 69 70 /** Public Methods ****************************************************/ 71 72 /** 73 * Constructor method. 74 * 75 * @since 8.0.0 76 * 77 * @param int $id Optional. Provide an ID to access an existing 78 * optout item. 79 */ 80 public function __construct( $id = 0 ) { 81 if ( ! empty( $id ) ) { 82 $this->id = (int) $id; 83 $this->populate(); 84 } 85 } 86 87 /** 88 * Get the opt-outs table name. 89 * 90 * @since 8.0.0 91 * @access public 92 * @return string 93 */ 94 public static function get_table_name() { 95 return buddypress()->members->table_name_optouts; 96 } 97 98 /** 99 * Update or insert opt-out details into the database. 100 * 101 * @since 8.0.0 102 * 103 * @global wpdb $wpdb WordPress database object. 104 * 105 * @return bool True on success, false on failure. 106 */ 107 public function save() { 108 109 // Return value 110 $retval = false; 111 112 // Default data and format 113 $data = array( 114 'email_address' => $this->email_address, 115 'user_id' => $this->user_id, 116 'email_type' => sanitize_key( $this->email_type ), 117 'date_modified' => $this->date_modified, 118 ); 119 $data_format = array( '%s', '%d', '%s', '%s' ); 120 121 /** 122 * Fires before an opt-out is saved. 123 * 124 * @since 8.0.0 125 * 126 * @param BP_Optout object $this Characteristics of the opt-out to be saved. 127 */ 128 do_action_ref_array( 'bp_optout_before_save', array( &$this ) ); 129 130 // Update. 131 if ( ! empty( $this->id ) ) { 132 $result = self::_update( $data, array( 'ID' => $this->id ), $data_format, array( '%d' ) ); 133 // Insert. 134 } else { 135 $result = self::_insert( $data, $data_format ); 136 } 137 138 // Set the opt-out ID if successful. 139 if ( ! empty( $result ) && ! is_wp_error( $result ) ) { 140 global $wpdb; 141 142 $this->id = $wpdb->insert_id; 143 $retval = $wpdb->insert_id; 144 } 145 146 wp_cache_delete( $this->id, 'bp_optouts' ); 147 148 /** 149 * Fires after an optout is saved. 150 * 151 * @since 8.0.0 152 * 153 * @param BP_optout object $this Characteristics of the opt-out just saved. 154 */ 155 do_action_ref_array( 'bp_optout_after_save', array( &$this ) ); 156 157 // Return the result. 158 return $retval; 159 } 160 161 /** 162 * Fetch data for an existing opt-out from the database. 163 * 164 * @since 8.0.0 165 * 166 * @global BuddyPress $bp The one true BuddyPress instance. 167 * @global wpdb $wpdb WordPress database object. 168 */ 169 public function populate() { 170 global $wpdb; 171 $optouts_table_name = $this->get_table_name(); 172 173 // Check cache for optout data. 174 $optout = wp_cache_get( $this->id, 'bp_optouts' ); 175 176 // Cache missed, so query the DB. 177 if ( false === $optout ) { 178 $optout = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$optouts_table_name} WHERE id = %d", $this->id ) ); 179 wp_cache_set( $this->id, $optout, 'bp_optouts' ); 180 } 181 182 // No optout found so set the ID and bail. 183 if ( empty( $optout ) || is_wp_error( $optout ) ) { 184 $this->id = 0; 185 return; 186 } 187 188 $this->email_address = $optout->email_address; 189 $this->user_id = (int) $optout->user_id; 190 $this->email_type = sanitize_key( $optout->email_type ); 191 $this->date_modified = $optout->date_modified; 192 193 } 194 195 /** Protected Static Methods ******************************************/ 196 197 /** 198 * Create an opt-out entry. 199 * 200 * @since 8.0.0 201 * 202 * @param array $data { 203 * Array of optout data, passed to {@link wpdb::insert()}. 204 * @type string $email_address The email address of the user that wishes to opt out of 205 * communications from this site. 206 * @type int $user_id The ID of the user that generated the contact that resulted in the opt-out. 207 * @type string $email_type The type of email contact that resulted in the opt-out. 208 * @type string $date_modified Date the opt-out was last modified. 209 * } 210 * @param array $data_format See {@link wpdb::insert()}. 211 * @return int|false The number of rows inserted, or false on error. 212 */ 213 protected static function _insert( $data = array(), $data_format = array() ) { 214 global $wpdb; 215 return $wpdb->insert( BP_Optout::get_table_name(), $data, $data_format ); 216 } 217 218 /** 219 * Update opt-outs. 220 * 221 * @since 8.0.0 222 * 223 * @see wpdb::update() for further description of paramater formats. 224 * 225 * @param array $data Array of optout data to update, passed to 226 * {@link wpdb::update()}. Accepts any property of a 227 * BP_optout object. 228 * @param array $where The WHERE params as passed to wpdb::update(). 229 * Typically consists of array( 'ID' => $id ) to specify the ID 230 * of the item being updated. See {@link wpdb::update()}. 231 * @param array $data_format See {@link wpdb::insert()}. 232 * @param array $where_format See {@link wpdb::insert()}. 233 * @return int|false The number of rows updated, or false on error. 234 */ 235 protected static function _update( $data = array(), $where = array(), $data_format = array(), $where_format = array() ) { 236 global $wpdb; 237 return $wpdb->update( BP_Optout::get_table_name(), $data, $where, $data_format, $where_format ); 238 } 239 240 /** 241 * Delete opt-outs. 242 * 243 * @since 8.0.0 244 * 245 * @see wpdb::update() for further description of paramater formats. 246 * 247 * @param array $where Array of WHERE clauses to filter by, passed to 248 * {@link wpdb::delete()}. Accepts any property of a 249 * BP_optout object. 250 * @param array $where_format See {@link wpdb::insert()}. 251 * @return int|false The number of rows updated, or false on error. 252 */ 253 protected static function _delete( $where = array(), $where_format = array() ) { 254 global $wpdb; 255 return $wpdb->delete( BP_Optout::get_table_name(), $where, $where_format ); 256 } 257 258 /** 259 * Assemble the WHERE clause of a get() SQL statement. 260 * 261 * Used by BP_optout::get() to create its WHERE 262 * clause. 263 * 264 * @since 8.0.0 265 * 266 * @param array $args See {@link BP_optout::get()} for more details. 267 * @return string WHERE clause. 268 */ 269 protected static function get_where_sql( $args = array() ) { 270 global $wpdb; 271 272 $where_conditions = array(); 273 $where = ''; 274 275 // id. 276 if ( false !== $args['id'] ) { 277 $id_in = implode( ',', wp_parse_id_list( $args['id'] ) ); 278 $where_conditions['id'] = "id IN ({$id_in})"; 279 } 280 281 // email_address. 282 if ( ! empty( $args['email_address'] ) ) { 283 if ( ! is_array( $args['email_address'] ) ) { 284 $emails = explode( ',', $args['email_address'] ); 285 } else { 286 $emails = $args['email_address']; 287 } 288 289 $email_clean = array(); 290 foreach ( $emails as $email ) { 291 $email_clean[] = $wpdb->prepare( '%s', $email ); 292 } 293 294 $email_in = implode( ',', $email_clean ); 295 $where_conditions['email_address'] = "email_address IN ({$email_in})"; 296 } 297 298 // user_id. 299 if ( ! empty( $args['user_id'] ) ) { 300 $user_id_in = implode( ',', wp_parse_id_list( $args['user_id'] ) ); 301 $where_conditions['user_id'] = "user_id IN ({$user_id_in})"; 302 } 303 304 // email_type. 305 if ( ! empty( $args['email_type'] ) ) { 306 if ( ! is_array( $args['email_type'] ) ) { 307 $email_types = explode( ',', $args['email_type'] ); 308 } else { 309 $email_types = $args['email_type']; 310 } 311 312 $et_clean = array(); 313 foreach ( $email_types as $et ) { 314 $et_clean[] = $wpdb->prepare( '%s', sanitize_key( $et ) ); 315 } 316 317 $et_in = implode( ',', $et_clean ); 318 $where_conditions['email_type'] = "email_type IN ({$et_in})"; 319 } 320 321 // search_terms. 322 if ( ! empty( $args['search_terms'] ) ) { 323 $search_terms_like = '%' . bp_esc_like( $args['search_terms'] ) . '%'; 324 $where_conditions['search_terms'] = $wpdb->prepare( '( email_address LIKE %s )', $search_terms_like ); 325 } 326 327 // Custom WHERE. 328 if ( ! empty( $where_conditions ) ) { 329 $where = 'WHERE ' . implode( ' AND ', $where_conditions ); 330 } 331 332 return $where; 333 } 334 335 /** 336 * Assemble the ORDER BY clause of a get() SQL statement. 337 * 338 * Used by BP_Optout::get() to create its ORDER BY 339 * clause. 340 * 341 * @since 8.0.0 342 * 343 * @param array $args See {@link BP_optout::get()} for more details. 344 * @return string ORDER BY clause. 345 */ 346 protected static function get_order_by_sql( $args = array() ) { 347 348 // Setup local variable. 349 $conditions = array(); 350 $retval = ''; 351 352 // Order by. 353 if ( ! empty( $args['order_by'] ) ) { 354 $order_by = implode( ', ', (array) $args['order_by'] ); 355 $conditions['order_by'] = "{$order_by}"; 356 } 357 358 // Sort order direction. 359 if ( ! empty( $args['sort_order'] ) ) { 360 $sort_order = bp_esc_sql_order( $args['sort_order'] ); 361 $conditions['sort_order'] = "{$sort_order}"; 362 } 363 364 // Custom ORDER BY. 365 if ( ! empty( $conditions ) ) { 366 $retval = 'ORDER BY ' . implode( ' ', $conditions ); 367 } 368 369 return $retval; 370 } 371 372 /** 373 * Assemble the LIMIT clause of a get() SQL statement. 374 * 375 * Used by BP_Optout::get() to create its LIMIT clause. 376 * 377 * @since 8.0.0 378 * 379 * @param array $args See {@link BP_optout::get()} for more details. 380 * @return string LIMIT clause. 381 */ 382 protected static function get_paged_sql( $args = array() ) { 383 global $wpdb; 384 385 // Setup local variable. 386 $retval = ''; 387 388 // Custom LIMIT. 389 if ( ! empty( $args['page'] ) && ! empty( $args['per_page'] ) ) { 390 $page = absint( $args['page'] ); 391 $per_page = absint( $args['per_page'] ); 392 $offset = $per_page * ( $page - 1 ); 393 $retval = $wpdb->prepare( "LIMIT %d, %d", $offset, $per_page ); 394 } 395 396 return $retval; 397 } 398 399 /** 400 * Assemble query clauses, based on arguments, to pass to $wpdb methods. 401 * 402 * The insert(), update(), and delete() methods of {@link wpdb} expect 403 * arguments of the following forms: 404 * 405 * - associative arrays whose key/value pairs are column => value, to 406 * be used in WHERE, SET, or VALUES clauses 407 * - arrays of "formats", which tell $wpdb->prepare() which type of 408 * value to expect when sanitizing (eg, array( '%s', '%d' )) 409 * 410 * This utility method can be used to assemble both kinds of params, 411 * out of a single set of associative array arguments, such as: 412 * 413 * $args = array( 414 * 'user_id' => 4, 415 * 'email_type' => 'type_string', 416 * ); 417 * 418 * This will be converted to: 419 * 420 * array( 421 * 'data' => array( 422 * 'user_id' => 4, 423 * 'email_type' => 'type_string', 424 * ), 425 * 'format' => array( 426 * '%d', 427 * '%s', 428 * ), 429 * ) 430 * 431 * which can easily be passed as arguments to the $wpdb methods. 432 * 433 * @since 8.0.0 434 * 435 * @param array $args Associative array of filter arguments. 436 * See {@BP_optout::get()} for a breakdown. 437 * @return array Associative array of 'data' and 'format' args. 438 */ 439 protected static function get_query_clauses( $args = array() ) { 440 $where_clauses = array( 441 'data' => array(), 442 'format' => array(), 443 ); 444 445 // id. 446 if ( ! empty( $args['id'] ) ) { 447 $where_clauses['data']['id'] = absint( $args['id'] ); 448 $where_clauses['format'][] = '%d'; 449 } 450 451 // email_address. 452 if ( ! empty( $args['email_address'] ) ) { 453 $where_clauses['data']['email_address'] = $args['email_address']; 454 $where_clauses['format'][] = '%s'; 455 } 456 457 // user_id. 458 if ( ! empty( $args['user_id'] ) ) { 459 $where_clauses['data']['user_id'] = absint( $args['user_id'] ); 460 $where_clauses['format'][] = '%d'; 461 } 462 463 // email_type. 464 if ( ! empty( $args['email_type'] ) ) { 465 $where_clauses['data']['email_type'] = $args['email_type']; 466 $where_clauses['format'][] = '%s'; 467 } 468 469 return $where_clauses; 470 } 471 472 /** Public Static Methods *********************************************/ 473 474 /** 475 * Get opt-outs, based on provided filter parameters. 476 * 477 * @since 8.0.0 478 * 479 * @param array $args { 480 * Associative array of arguments. All arguments but $page and 481 * $per_page can be treated as filter values for get_where_sql() 482 * and get_query_clauses(). All items are optional. 483 * @type int|array $id ID of opt-out. 484 * Can be an array of IDs. 485 * @type string|array $email_address Email address of users who have opted out 486 * being queried. Can be an array of addresses. 487 * @type int|array $user_id ID of user whose communication prompted the 488 * opt-out. Can be an array of IDs. 489 * @type string|array $email_type Name of the emil type to filter by. 490 * Can be an array of email types. 491 * @type string $search_terms Term to match against email_address field. 492 * @type string $order_by Database column to order by. 493 * @type string $sort_order Either 'ASC' or 'DESC'. 494 * @type string $order_by Field to order results by. 495 * @type string $sort_order ASC or DESC. 496 * @type int $page Number of the current page of results. 497 * Default: false (no pagination, 498 * all items). 499 * @type int $per_page Number of items to show per page. 500 * Default: false (no pagination, 501 * all items). 502 * @type string $fields Which fields to return. Specify 'email_addresses' to 503 * fetch a list of opt-out email_addresses. 504 * Specify 'user_ids' to 505 * fetch a list of opt-out user_ids. 506 * Specify 'ids' to fetch a list of opt-out IDs. 507 * Default: 'all' (return BP_Optout objects). 508 * } 509 * 510 * @return array BP_Optout objects | IDs of found opt-outs | Email addresses of matches. 511 */ 512 public static function get( $args = array() ) { 513 global $wpdb; 514 $optouts_table_name = BP_Optout::get_table_name(); 515 516 // Parse the arguments. 517 $r = bp_parse_args( $args, array( 518 'id' => false, 519 'email_address' => false, 520 'user_id' => false, 521 'email_type' => false, 522 'search_terms' => '', 523 'order_by' => false, 524 'sort_order' => false, 525 'page' => false, 526 'per_page' => false, 527 'fields' => 'all', 528 ), 'bp_optout_get' ); 529 530 $sql = array( 531 'select' => "SELECT", 532 'fields' => '', 533 'from' => "FROM {$optouts_table_name} o", 534 'where' => '', 535 'orderby' => '', 536 'pagination' => '', 537 ); 538 539 if ( 'user_ids' === $r['fields'] ) { 540 $sql['fields'] = "DISTINCT o.user_id"; 541 } else if ( 'email_addresses' === $r['fields'] ) { 542 $sql['fields'] = "DISTINCT o.email_address"; 543 } else { 544 $sql['fields'] = 'DISTINCT o.id'; 545 } 546 547 // WHERE. 548 $sql['where'] = self::get_where_sql( array( 549 'id' => $r['id'], 550 'email_address' => $r['email_address'], 551 'user_id' => $r['user_id'], 552 'email_type' => $r['email_type'], 553 'search_terms' => $r['search_terms'], 554 ) ); 555 556 // ORDER BY. 557 $sql['orderby'] = self::get_order_by_sql( array( 558 'order_by' => $r['order_by'], 559 'sort_order' => $r['sort_order'] 560 ) ); 561 562 // LIMIT %d, %d. 563 $sql['pagination'] = self::get_paged_sql( array( 564 'page' => $r['page'], 565 'per_page' => $r['per_page'], 566 ) ); 567 568 $paged_optouts_sql = "{$sql['select']} {$sql['fields']} {$sql['from']} {$sql['where']} {$sql['orderby']} {$sql['pagination']}"; 569 570 /** 571 * Filters the pagination SQL statement. 572 * 573 * @since 8.0.0 574 * 575 * @param string $value Concatenated SQL statement. 576 * @param array $sql Array of SQL parts before concatenation. 577 * @param array $r Array of parsed arguments for the get method. 578 */ 579 $paged_optouts_sql = apply_filters( 'bp_optouts_get_paged_optouts_sql', $paged_optouts_sql, $sql, $r ); 580 581 $cached = bp_core_get_incremented_cache( $paged_optouts_sql, 'bp_optouts' ); 582 if ( false === $cached ) { 583 $paged_optout_ids = $wpdb->get_col( $paged_optouts_sql ); 584 bp_core_set_incremented_cache( $paged_optouts_sql, 'bp_optouts', $paged_optout_ids ); 585 } else { 586 $paged_optout_ids = $cached; 587 } 588 589 // Special return format cases. 590 if ( in_array( $r['fields'], array( 'ids', 'user_ids' ), true ) ) { 591 // We only want the field that was found. 592 return array_map( 'intval', $paged_optout_ids ); 593 } else if ( 'email_addresses' === $r['fields'] ) { 594 return $paged_optout_ids; 595 } 596 597 $uncached_ids = bp_get_non_cached_ids( $paged_optout_ids, 'bp_optouts' ); 598 if ( $uncached_ids ) { 599 $ids_sql = implode( ',', array_map( 'intval', $uncached_ids ) ); 600 $data_objects = $wpdb->get_results( "SELECT o.* FROM {$optouts_table_name} o WHERE o.id IN ({$ids_sql})" ); 601 foreach ( $data_objects as $data_object ) { 602 wp_cache_set( $data_object->id, $data_object, 'bp_optouts' ); 603 } 604 } 605 606 $paged_optouts = array(); 607 foreach ( $paged_optout_ids as $paged_optout_id ) { 608 $paged_optouts[] = new BP_optout( $paged_optout_id ); 609 } 610 611 return $paged_optouts; 612 } 613 614 /** 615 * Get a count of total optouts matching a set of arguments. 616 * 617 * @since 8.0.0 618 * 619 * @see BP_optout::get() for a description of 620 * arguments. 621 * 622 * @param array $args See {@link BP_optout::get()}. 623 * @return int Count of located items. 624 */ 625 public static function get_total_count( $args ) { 626 global $wpdb; 627 $optouts_table_name = BP_Optout::get_table_name(); 628 629 // Parse the arguments. 630 $r = bp_parse_args( $args, array( 631 'id' => false, 632 'email_address' => false, 633 'user_id' => false, 634 'email_type' => false, 635 'search_terms' => '', 636 'order_by' => false, 637 'sort_order' => false, 638 'page' => false, 639 'per_page' => false, 640 'fields' => 'all', 641 ), 'bp_optout_get_total_count' ); 642 643 // Build the query 644 $select_sql = "SELECT COUNT(*)"; 645 $from_sql = "FROM {$optouts_table_name}"; 646 $where_sql = self::get_where_sql( $r ); 647 $sql = "{$select_sql} {$from_sql} {$where_sql}"; 648 649 // Return the queried results 650 return $wpdb->get_var( $sql ); 651 } 652 653 /** 654 * Update optouts. 655 * 656 * @since 8.0.0 657 * 658 * @see BP_optout::get() for a description of 659 * accepted update/where arguments. 660 * 661 * @param array $update_args Associative array of fields to update, 662 * and the values to update them to. Of the format 663 * array( 'user_id' => 4, 'email_address' => 'bar@foo.com', ). 664 * @param array $where_args Associative array of columns/values, to 665 * determine which rows should be updated. Of the format 666 * array( 'user_id' => 7, 'email_address' => 'bar@foo.com', ). 667 * @return int|bool Number of rows updated on success, false on failure. 668 */ 669 public static function update( $update_args = array(), $where_args = array() ) { 670 $update = self::get_query_clauses( $update_args ); 671 $where = self::get_query_clauses( $where_args ); 672 673 /** 674 * Fires before an opt-out is updated. 675 * 676 * @since 8.0.0 677 * 678 * @param array $where_args Associative array of columns/values describing 679 * optouts about to be deleted. 680 * @param array $update_args Array of new values. 681 */ 682 do_action( 'bp_optout_before_update', $where_args, $update_args ); 683 684 $retval = self::_update( $update['data'], $where['data'], $update['format'], $where['format'] ); 685 686 // Clear matching items from the cache. 687 $cache_args = $where_args; 688 $cache_args['fields'] = 'ids'; 689 $maybe_cached_ids = self::get( $cache_args ); 690 foreach ( $maybe_cached_ids as $invite_id ) { 691 wp_cache_delete( $invite_id, 'bp_optouts' ); 692 } 693 694 /** 695 * Fires after an opt-out is updated. 696 * 697 * @since 8.0.0 698 * 699 * @param array $where_args Associative array of columns/values describing 700 * optouts about to be deleted. 701 * @param array $update_args Array of new values. 702 */ 703 do_action( 'bp_optout_after_update', $where_args, $update_args ); 704 705 return $retval; 706 } 707 708 /** 709 * Delete opt-outs. 710 * 711 * @since 8.0.0 712 * 713 * @see BP_optout::get() for a description of 714 * accepted where arguments. 715 * 716 * @param array $args Associative array of columns/values, to determine 717 * which rows should be deleted. Of the format 718 * array( 'user_id' => 7, 'email_address' => 'bar@foo.com', ). 719 * @return int|bool Number of rows deleted on success, false on failure. 720 */ 721 public static function delete( $args = array() ) { 722 $where = self::get_query_clauses( $args ); 723 724 /** 725 * Fires before an optout is deleted. 726 * 727 * @since 8.0.0 728 * 729 * @param array $args Characteristics of the optouts to be deleted. 730 */ 731 do_action( 'bp_optout_before_delete', $args ); 732 733 // Clear matching items from the cache. 734 $cache_args = $args; 735 $cache_args['fields'] = 'ids'; 736 $maybe_cached_ids = self::get( $cache_args ); 737 foreach ( $maybe_cached_ids as $invite_id ) { 738 wp_cache_delete( $invite_id, 'bp_optouts' ); 739 } 740 741 $retval = self::_delete( $where['data'], $where['format'] ); 742 743 /** 744 * Fires after an optout is deleted. 745 * 746 * @since 8.0.0 747 * 748 * @param array $args Characteristics of the optouts just deleted. 749 */ 750 do_action( 'bp_optout_after_delete', $args ); 751 752 return $retval; 753 } 754 755 /** Convenience methods ***********************************************/ 756 757 /** 758 * Check whether an invitation exists matching the passed arguments. 759 * 760 * @since 5.0.0 761 * 762 * @see BP_Optout::get() for a description of accepted parameters. 763 * 764 * @return int|bool ID of first found invitation or false if none found. 765 */ 766 public function optout_exists( $args = array() ) { 767 $exists = false; 768 769 $args['fields'] = 'ids'; 770 $optouts = BP_Optout::get( $args ); 771 if ( $optouts ) { 772 $exists = current( $optouts ); 773 } 774 return $exists; 775 } 776 777 /** 778 * Delete a single optout by ID. 779 * 780 * @since 8.0.0 781 * 782 * @see BP_optout::delete() for explanation of 783 * return value. 784 * 785 * @param int $id ID of the optout item to be deleted. 786 * @return bool True on success, false on failure. 787 */ 788 public static function delete_by_id( $id ) { 789 return self::delete( array( 790 'id' => $id, 791 ) ); 792 } 793 794 } -
src/bp-members/classes/class-bp-members-admin.php
diff --git a/src/bp-members/classes/class-bp-members-admin.php b/src/bp-members/classes/class-bp-members-admin.php index c999cb923..5e90b094c 100644
a b class BP_Members_Admin { 140 140 $this->users_url = bp_get_admin_url( 'users.php' ); 141 141 $this->users_screen = bp_core_do_network_admin() ? 'users-network' : 'users'; 142 142 143 $this->members_optouts_page = ''; 144 143 145 // Specific config: BuddyPress is not network activated. 144 146 $this->subsite_activated = (bool) is_multisite() && ! bp_is_network_activated(); 145 147 … … class BP_Members_Admin { 488 490 'bp-signups', 489 491 array( $this, 'signups_admin' ) 490 492 ); 493 494 // Manage opt-outs. 495 $hooks['members_optouts'] = $this->members_optouts_page = add_users_page( 496 __( 'Manage Opt-outs', 'buddypress' ), 497 __( 'Manage Opt-outs', 'buddypress' ), 498 $this->capability, 499 'bp-members-optouts', 500 array( $this, 'optouts_admin' ) 501 ); 491 502 } 492 503 493 504 $edit_page = 'user-edit'; … … class BP_Members_Admin { 509 520 $this->user_page .= '-network'; 510 521 $this->users_page .= '-network'; 511 522 $this->signups_page .= '-network'; 523 524 $this->members_optouts_page .= '-network'; 512 525 } 513 526 514 527 // Setup the screen ID's. … … class BP_Members_Admin { 2564 2577 2565 2578 return $value; 2566 2579 } 2580 2581 /** 2582 * Set up the Opt-outs admin page. 2583 * 2584 * Loaded before the page is rendered, this function does all initial 2585 * setup, including: processing form requests, registering contextual 2586 * help, and setting up screen options. 2587 * 2588 * @since 8.0.0 2589 * 2590 * @global $bp_members_optouts_list_table 2591 */ 2592 public function members_optouts_admin_load() { 2593 global $bp_members_optouts_list_table; 2594 2595 // Build redirection URL. 2596 $redirect_to = remove_query_arg( array( 'action', 'error', 'updated', 'activated', 'notactivated', 'deleted', 'notdeleted', 'resent', 'notresent', 'do_delete', 'do_resend', 'do_activate', '_wpnonce', 'signup_ids' ), $_SERVER['REQUEST_URI'] ); 2597 $doaction = bp_admin_list_table_current_bulk_action(); 2598 2599 /** 2600 * Fires at the start of the nonmember optouts admin load. 2601 * 2602 * @since 8.0.0 2603 * 2604 * @param string $doaction Current bulk action being processed. 2605 * @param array $_REQUEST Current $_REQUEST global. 2606 */ 2607 do_action( 'bp_members_optouts_admin_load', $doaction, $_REQUEST ); 2608 2609 /** 2610 * Filters the allowed actions for use in the nonmember optouts admin page. 2611 * 2612 * @since 8.0.0 2613 * 2614 * @param array $value Array of allowed actions to use. 2615 */ 2616 $allowed_actions = apply_filters( 'bp_members_optouts_admin_allowed_actions', array( 'do_delete', 'do_resend' ) ); 2617 2618 if ( ! in_array( $doaction, $allowed_actions ) || ( -1 == $doaction ) ) { 2619 2620 $bp_members_optouts_list_table = self::get_list_table_class( 'BP_Members_Optouts_List_Table', 'users' ); 2621 2622 // The per_page screen option. 2623 add_screen_option( 'per_page', array( 'label' => _x( 'Nonmember opt-outs', 'Nonmember opt-outs per page (screen options)', 'buddypress' ) ) ); 2624 2625 get_current_screen()->add_help_tab( array( 2626 'id' => 'bp-members-optouts-overview', 2627 'title' => __( 'Overview', 'buddypress' ), 2628 'content' => 2629 '<p>' . __( 'This is the administration screen for nonmember opt-outs on your site.', 'buddypress' ) . '</p>' . 2630 '<p>' . __( 'From the screen options, you can customize the displayed columns and the pagination of this screen.', 'buddypress' ) . '</p>' . 2631 '<p>' . __( 'You can reorder the list of opt-outs by clicking on the Email Address, User Who Contacted, Email Type or Date Modified column headers.', 'buddypress' ) . '</p>' . 2632 '<p>' . __( 'Using the search form, you can find specific opt-outs more easily. The Email Address field will be included in the search.', 'buddypress' ) . '</p>' 2633 ) ); 2634 2635 get_current_screen()->add_help_tab( array( 2636 'id' => 'bp-members-optouts-actions', 2637 'title' => __( 'Actions', 'buddypress' ), 2638 'content' => 2639 '<p>' . __( 'Hovering over a row in the opt-outs list will display action links that allow you to manage the opt-out. You can perform the following actions:', 'buddypress' ) . '</p>' . 2640 '<ul><li>' . __( '"Delete" allows you to delete the record of an opt-out. You will be asked to confirm this deletion.', 'buddypress' ) . '</li></ul>' . 2641 '<p>' . __( 'Bulk actions allow you to perform these actions for the selected rows.', 'buddypress' ) . '</p>' 2642 ) ); 2643 2644 // Help panel - sidebar links. 2645 get_current_screen()->set_help_sidebar( 2646 '<p><strong>' . __( 'For more information:', 'buddypress' ) . '</strong></p>' . 2647 '<p>' . __( '<a href="https://buddypress.org/support/">Support Forums</a>', 'buddypress' ) . '</p>' 2648 ); 2649 2650 // Add accessible hidden headings and text for the Pending Users screen. 2651 get_current_screen()->set_screen_reader_content( array( 2652 /* translators: accessibility text */ 2653 'heading_views' => __( 'Filter opt-outs list', 'buddypress' ), 2654 /* translators: accessibility text */ 2655 'heading_pagination' => __( 'Opt-out list navigation', 'buddypress' ), 2656 /* translators: accessibility text */ 2657 'heading_list' => __( 'Opt-outs list', 'buddypress' ), 2658 ) ); 2659 2660 } else { 2661 if ( empty( $_REQUEST['optout_ids' ] ) ) { 2662 return; 2663 } 2664 $optout_ids = wp_parse_id_list( $_REQUEST['optout_ids' ] ); 2665 2666 // Handle optout deletion. 2667 if ( 'do_delete' == $doaction ) { 2668 2669 // Nonce check. 2670 check_admin_referer( 'optouts_delete' ); 2671 2672 $success = 0; 2673 foreach ( $optout_ids as $optout_id ) { 2674 if ( bp_delete_optout_by_id( $optout_id ) ) { 2675 $success++; 2676 } 2677 } 2678 2679 $query_arg = array( 'updated' => 'deleted' ); 2680 2681 if ( ! empty( $success ) ) { 2682 $query_arg['deleted'] = $success; 2683 } 2684 2685 $notdeleted = count( $optout_ids ) - $success; 2686 if ( $notdeleted > 0 ) { 2687 $query_arg['notdeleted'] = $notdeleted; 2688 } 2689 2690 $redirect_to = add_query_arg( $query_arg, $redirect_to ); 2691 2692 bp_core_redirect( $redirect_to ); 2693 2694 // Plugins can update other stuff from here. 2695 } else { 2696 $this->redirect = $redirect_to; 2697 2698 /** 2699 * Fires at end of member opt-outs admin load 2700 * if doaction does not match any actions. 2701 * 2702 * @since 2.0.0 2703 * 2704 * @param string $doaction Current bulk action being processed. 2705 * @param array $_REQUEST Current $_REQUEST global. 2706 * @param string $redirect Determined redirect url to send user to. 2707 */ 2708 do_action( 'bp_members_admin_update_optouts', $doaction, $_REQUEST, $this->redirect ); 2709 2710 bp_core_redirect( $this->redirect ); 2711 } 2712 } 2713 } 2714 2715 /** 2716 * Get admin notice when viewing the optouts management page. 2717 * 2718 * @since 8.0.0 2719 * 2720 * @return array 2721 */ 2722 private function get_members_optouts_notice() { 2723 2724 // Setup empty notice for return value. 2725 $notice = array(); 2726 2727 // Updates. 2728 if ( ! empty( $_REQUEST['updated'] ) && 'deleted' === $_REQUEST['updated'] ) { 2729 $notice = array( 2730 'class' => 'updated', 2731 'message' => '' 2732 ); 2733 2734 if ( ! empty( $_REQUEST['deleted'] ) ) { 2735 $notice['message'] .= sprintf( 2736 /* translators: %s: number of deleted optouts */ 2737 _nx( '%s optout successfully deleted!', '%s optouts successfully deleted!', 2738 absint( $_REQUEST['deleted'] ), 2739 'members optout deleted', 2740 'buddypress' 2741 ), 2742 number_format_i18n( absint( $_REQUEST['deleted'] ) ) 2743 ); 2744 } 2745 2746 if ( ! empty( $_REQUEST['notdeleted'] ) ) { 2747 $notice['message'] .= sprintf( 2748 /* translators: %s: number of optouts that failed to be deleted */ 2749 _nx( '%s optout was not deleted.', '%s optouts were not deleted.', 2750 absint( $_REQUEST['notdeleted'] ), 2751 'members optout notdeleted', 2752 'buddypress' 2753 ), 2754 number_format_i18n( absint( $_REQUEST['notdeleted'] ) ) 2755 ); 2756 2757 if ( empty( $_REQUEST['deleted'] ) ) { 2758 $notice['class'] = 'error'; 2759 } 2760 } 2761 } 2762 2763 // Errors. 2764 if ( ! empty( $_REQUEST['error'] ) && 'do_delete' === $_REQUEST['error'] ) { 2765 $notice = array( 2766 'class' => 'error', 2767 'message' => esc_html__( 'There was a problem deleting optouts. Please try again.', 'buddypress' ), 2768 ); 2769 } 2770 2771 return $notice; 2772 } 2773 2774 /** 2775 * Member opt-outs admin page router. 2776 * 2777 * Depending on the context, display 2778 * - the list of optouts, 2779 * - or the delete confirmation screen, 2780 * 2781 * Also prepare the admin notices. 2782 * 2783 * @since 8.0.0 2784 */ 2785 public function optouts_admin() { 2786 $doaction = bp_admin_list_table_current_bulk_action(); 2787 2788 // Prepare notices for admin. 2789 $notice = $this->get_members_optouts_notice(); 2790 2791 // Display notices. 2792 if ( ! empty( $notice ) ) : 2793 if ( 'updated' === $notice['class'] ) : ?> 2794 2795 <div id="message" class="<?php echo esc_attr( $notice['class'] ); ?> notice is-dismissible"> 2796 2797 <?php else: ?> 2798 2799 <div class="<?php echo esc_attr( $notice['class'] ); ?> notice is-dismissible"> 2800 2801 <?php endif; ?> 2802 2803 <p><?php echo $notice['message']; ?></p> 2804 </div> 2805 2806 <?php endif; 2807 2808 // Show the proper screen. 2809 switch ( $doaction ) { 2810 case 'delete' : 2811 $this->optouts_admin_manage( $doaction ); 2812 break; 2813 2814 default: 2815 $this->optouts_admin_index(); 2816 break; 2817 } 2818 } 2819 2820 /** 2821 * This is the list of optouts. 2822 * 2823 * @since 8.0.0 2824 * 2825 * @global $plugin_page 2826 * @global $bp_members_optouts_list_table 2827 */ 2828 public function optouts_admin_index() { 2829 global $plugin_page, $bp_members_optouts_list_table; 2830 2831 $usersearch = ! empty( $_REQUEST['s'] ) ? stripslashes( $_REQUEST['s'] ) : ''; 2832 2833 // Prepare the group items for display. 2834 $bp_members_optouts_list_table->prepare_items(); 2835 2836 if ( is_network_admin() ) { 2837 $form_url = network_admin_url( 'users.php' ); 2838 } else { 2839 $form_url = bp_get_admin_url( 'users.php' ); 2840 } 2841 2842 $form_url = add_query_arg( 2843 array( 2844 'page' => 'bp-members-optouts', 2845 ), 2846 $form_url 2847 ); 2848 2849 $search_form_url = remove_query_arg( 2850 array( 2851 'action', 2852 'deleted', 2853 'notdeleted', 2854 'error', 2855 'updated', 2856 'delete', 2857 'activate', 2858 'activated', 2859 'notactivated', 2860 'resend', 2861 'resent', 2862 'notresent', 2863 'do_delete', 2864 'do_activate', 2865 'do_resend', 2866 'action2', 2867 '_wpnonce', 2868 'optout_ids' 2869 ), $_SERVER['REQUEST_URI'] 2870 ); 2871 2872 ?> 2873 2874 <div class="wrap"> 2875 <h1 class="wp-heading-inline"><?php _e( 'Nonmember Opt-outs', 'buddypress' ); ?></h1> 2876 2877 <?php 2878 if ( $usersearch ) { 2879 printf( '<span class="subtitle">' . __( 'Search results for “%s”', 'buddypress' ) . '</span>', esc_html( $usersearch ) ); 2880 } 2881 ?> 2882 <p class="description"><?php _e( 'This table shows opt-out requests from people who are not members of this site, but have been contacted via communication from this site, and wish to receive no further communications.', 'buddypress' ); ?></p> 2883 2884 <hr class="wp-header-end"> 2885 2886 <?php // Display each opt-out on its own row. ?> 2887 <?php $bp_members_optouts_list_table->views(); ?> 2888 2889 <form id="bp-members-optouts-search-form" action="<?php echo esc_url( $search_form_url ) ;?>"> 2890 <input type="hidden" name="page" value="<?php echo esc_attr( $plugin_page ); ?>" /> 2891 <?php $bp_members_optouts_list_table->search_box( __( 'Search opt-outs', 'buddypress' ), 'bp-members-optouts' ); ?> 2892 </form> 2893 2894 <form id="bp-members-optouts-form" action="<?php echo esc_url( $form_url );?>" method="post"> 2895 <?php $bp_members_optouts_list_table->display(); ?> 2896 </form> 2897 </div> 2898 <?php 2899 } 2900 2901 /** 2902 * This is the confirmation screen for actions. 2903 * 2904 * @since 2.0.0 2905 * 2906 * @param string $action Delete or resend optout. 2907 * 2908 * @return null|false 2909 */ 2910 public function optouts_admin_manage( $action = '' ) { 2911 if ( ! current_user_can( $this->capability ) || empty( $action ) ) { 2912 die( '-1' ); 2913 } 2914 2915 // Get the IDs from the URL. 2916 $ids = false; 2917 if ( ! empty( $_POST['optout_ids'] ) ) { 2918 $ids = wp_parse_id_list( $_POST['optout_ids'] ); 2919 } elseif ( ! empty( $_GET['optout_id'] ) ) { 2920 $ids = absint( $_GET['optout_id'] ); 2921 } 2922 2923 if ( empty( $ids ) ) { 2924 return false; 2925 } 2926 2927 // Query for matching optouts, and filter out bad IDs. 2928 $args = array( 2929 'id' => $ids, 2930 ); 2931 $optouts = bp_get_optouts( $args ); 2932 $optout_ids = wp_list_pluck( $optouts, 'id' ); 2933 2934 // Check optout IDs and set up strings. 2935 switch ( $action ) { 2936 case 'delete' : 2937 $header_text = __( 'Delete optouts', 'buddypress' ); 2938 if ( 1 == count( $optouts ) ) { 2939 $helper_text = __( 'You are about to delete the following opt-out request:', 'buddypress' ); 2940 } else { 2941 $helper_text = __( 'You are about to delete the following opt-out requests:', 'buddypress' ); 2942 } 2943 break; 2944 } 2945 2946 // These arguments are added to all URLs. 2947 $url_args = array( 'page' => 'bp-members-optouts' ); 2948 2949 // These arguments are only added when performing an action. 2950 $action_args = array( 2951 'action' => 'do_' . $action, 2952 'optout_ids' => implode( ',', $optout_ids ) 2953 ); 2954 2955 if ( is_network_admin() ) { 2956 $base_url = network_admin_url( 'users.php' ); 2957 } else { 2958 $base_url = bp_get_admin_url( 'users.php' ); 2959 } 2960 2961 $cancel_url = add_query_arg( $url_args, $base_url ); 2962 $action_url = wp_nonce_url( 2963 add_query_arg( 2964 array_merge( $url_args, $action_args ), 2965 $base_url 2966 ), 2967 'optouts_' . $action 2968 ); 2969 2970 ?> 2971 2972 <div class="wrap"> 2973 <h1 class="wp-heading-inline"><?php echo esc_html( $header_text ); ?></h1> 2974 <hr class="wp-header-end"> 2975 2976 <p><?php echo esc_html( $helper_text ); ?></p> 2977 2978 <ol class="bp-optouts-list"> 2979 <?php foreach ( $optouts as $optout ) : ?> 2980 2981 <li> 2982 <strong><?php echo esc_html( $optout->email_address ) ?></strong> 2983 <p class="description"> 2984 <?php 2985 $last_modified = mysql2date( 'Y/m/d g:i:s a', $optout->date_modified ); 2986 /* translators: %s: modification date */ 2987 printf( esc_html__( 'Date modified: %s', 'buddypress'), $last_modified ); 2988 ?> 2989 </p> 2990 </li> 2991 2992 <?php endforeach; ?> 2993 </ol> 2994 2995 <?php if ( 'delete' === $action ) : ?> 2996 2997 <p><strong><?php esc_html_e( 'This action cannot be undone.', 'buddypress' ) ?></strong></p> 2998 2999 <?php endif ; ?> 3000 3001 <a class="button-primary" href="<?php echo esc_url( $action_url ); ?>"><?php esc_html_e( 'Confirm', 'buddypress' ); ?></a> 3002 <a class="button" href="<?php echo esc_url( $cancel_url ); ?>"><?php esc_html_e( 'Cancel', 'buddypress' ) ?></a> 3003 </div> 3004 3005 <?php 3006 } 3007 2567 3008 } 2568 3009 endif; // End class_exists check. -
src/bp-members/classes/class-bp-members-component.php
diff --git a/src/bp-members/classes/class-bp-members-component.php b/src/bp-members/classes/class-bp-members-component.php index 77777d645..92ee286a1 100644
a b class BP_Members_Component extends BP_Component { 178 178 'global_tables' => array( 179 179 'table_name_invitations' => bp_core_get_table_prefix() . 'bp_invitations', 180 180 'table_name_last_activity' => bp_core_get_table_prefix() . 'bp_activity', 181 'table_name_optouts' => bp_core_get_table_prefix() . 'bp_optouts', 181 182 'table_name_signups' => $wpdb->base_prefix . 'signups', // Signups is a global WordPress table. 182 183 ) 183 184 ); -
new file src/bp-members/classes/class-bp-members-optouts-list-table.php
diff --git a/src/bp-members/classes/class-bp-members-optouts-list-table.php b/src/bp-members/classes/class-bp-members-optouts-list-table.php new file mode 100644 index 000000000..35e55b11b
- + 1 <?php 2 /** 3 * BuddyPress Members Opt-outs List Table class. 4 * 5 * @package BuddyPress 6 * @subpackage MembersAdminClasses 7 * @since 8.0.0 8 */ 9 10 // Exit if accessed directly. 11 defined( 'ABSPATH' ) || exit; 12 13 /** 14 * List table class for nonmember opt-outs admin page. 15 * 16 * @since 8.0.0 17 */ 18 class BP_Members_Optouts_List_Table extends WP_Users_List_Table { 19 20 /** 21 * Opt-out count. 22 * 23 * @since 8.0.0 24 * 25 * @var int 26 */ 27 public $total_items = 0; 28 29 /** 30 * Constructor. 31 * 32 * @since 8.0.0 33 */ 34 public function __construct() { 35 // Define singular and plural labels, as well as whether we support AJAX. 36 parent::__construct( array( 37 'ajax' => false, 38 'plural' => 'optouts', 39 'singular' => 'optout', 40 'screen' => get_current_screen()->id, 41 ) ); 42 } 43 44 /** 45 * Set up items for display in the list table. 46 * 47 * Handles filtering of data, sorting, pagination, and any other data 48 * manipulation required prior to rendering. 49 * 50 * @since 8.0.0 51 */ 52 public function prepare_items() { 53 global $usersearch; 54 55 $search = isset( $_REQUEST['s'] ) ? $_REQUEST['s'] : ''; 56 $per_page = $this->get_items_per_page( str_replace( '-', '_', "{$this->screen->id}_per_page" ) ); 57 $paged = $this->get_pagenum(); 58 59 $args = array( 60 'search_terms' => $search, 61 'order_by' => 'date_modified', 62 'sort_order' => 'DESC', 63 'page' => $paged, 64 'per_page' => $per_page, 65 ); 66 67 if ( isset( $_REQUEST['orderby'] ) ) { 68 $args['order_by'] = $_REQUEST['orderby']; 69 } 70 71 if ( isset( $_REQUEST['order'] ) ) { 72 $args['sort_order'] = $_REQUEST['order']; 73 } 74 75 $this->items = bp_get_optouts( $args ); 76 $optouts_class = new BP_Optout(); 77 $this->total_items = $optouts_class->get_total_count( $args ); 78 79 $this->set_pagination_args( array( 80 'total_items' => $this->total_items, 81 'per_page' => $per_page, 82 ) ); 83 } 84 85 /** 86 * Get the list of views available on this table (e.g. "all", "public"). 87 * 88 * @since 8.0.0 89 */ 90 public function views() { 91 $url_base = add_query_arg( 92 array( 93 'page' => 'bp-members-optouts', 94 ), 95 bp_get_admin_url( 'users.php' ) 96 ); 97 ?> 98 99 <h2 class="screen-reader-text"><?php 100 /* translators: accessibility text */ 101 _e( 'Filter optouts list', 'buddypress' ); 102 ?></h2> 103 <ul class="subsubsub"> 104 <?php 105 /** 106 * Fires inside listing of views so plugins can add their own. 107 * 108 * @since 8.0.0 109 * 110 * @param string $url_base Current URL base for view. 111 * @param array $active_filters Current filters being requested. 112 */ 113 do_action( 'bp_members_optouts_list_table_get_views', $url_base, $this->active_filters ); ?> 114 </ul> 115 <?php 116 } 117 118 /** 119 * Get rid of the extra nav. 120 * 121 * WP_Users_List_Table will add an extra nav to change user's role. 122 * As we're dealing with opt-outs, we don't need this. 123 * 124 * @since 8.0.0 125 * 126 * @param array $which Current table nav item. 127 */ 128 public function extra_tablenav( $which ) { 129 return; 130 } 131 132 /** 133 * Specific opt-out columns. 134 * 135 * @since 8.0.0 136 * 137 * @return array 138 */ 139 public function get_columns() { 140 /** 141 * Filters the nonmember opt-outs columns. 142 * 143 * @since 8.0.0 144 * 145 * @param array $value Array of columns to display. 146 */ 147 return apply_filters( 'bp_members_optouts_list_columns', array( 148 'cb' => '<input type="checkbox" />', 149 'email_address' => __( 'Email Address', 'buddypress' ), 150 'username' => __( 'Email Sender', 'buddypress' ), 151 'user_registered' => __( 'Email Sender Registered', 'buddypress' ), 152 'email_type' => __( 'Email Type', 'buddypress' ), 153 'email_type_description' => __( 'Email Description', 'buddypress' ), 154 'optout_date_modified' => __( 'Date Modified', 'buddypress' ), 155 ) ); 156 } 157 158 /** 159 * Specific bulk actions for opt-outs. 160 * 161 * @since 8.0.0 162 */ 163 public function get_bulk_actions() { 164 if ( current_user_can( 'delete_users' ) ) { 165 $actions['delete'] = _x( 'Delete', 'Optout database record action', 'buddypress' ); 166 } 167 168 return $actions; 169 } 170 171 /** 172 * The text shown when no items are found. 173 * 174 * Nice job, clean sheet! 175 * 176 * @since 8.0.0 177 */ 178 public function no_items() { 179 esc_html_e( 'No opt-outs found.', 'buddypress' ); 180 } 181 182 /** 183 * The columns opt-outs can be reordered by. 184 * 185 * @since 8.0.0 186 */ 187 public function get_sortable_columns() { 188 return array( 189 'email_address' => 'email_address', 190 'username' => 'user_id', 191 'email_type' => 'email_type', 192 'optout_date_modified' => 'date_modified', 193 ); 194 } 195 196 /** 197 * Display opt-out rows. 198 * 199 * @since 8.0.0 200 */ 201 public function display_rows() { 202 $style = ''; 203 foreach ( $this->items as $optout ) { 204 $style = ( ' class="alternate"' == $style ) ? '' : ' class="alternate"'; 205 echo "\n\t" . $this->single_row( $optout, $style ); 206 } 207 } 208 209 /** 210 * Display an opt-out row. 211 * 212 * @since 8.0.0 213 * 214 * @see WP_List_Table::single_row() for explanation of params. 215 * 216 * @param BP_Optout $optout BP_Optout object. 217 * @param string $style Styles for the row. 218 * @param string $role Role to be assigned to user. 219 * @param int $numposts Number of posts. 220 * @return void 221 */ 222 public function single_row( $optout = null, $style = '', $role = '', $numposts = 0 ) { 223 echo '<tr' . $style . ' id="optout-' . esc_attr( $optout->id ) . '">'; 224 echo $this->single_row_columns( $optout ); 225 echo '</tr>'; 226 } 227 228 /** 229 * Markup for the checkbox used to select items for bulk actions. 230 * 231 * @since 8.0.0 232 * 233 * @param BP_Optout $optout BP_Optout object. 234 */ 235 public function column_cb( $optout = null ) { 236 ?> 237 <label class="screen-reader-text" for="optout_<?php echo intval( $optout->id ); ?>"><?php 238 /* translators: accessibility text */ 239 printf( esc_html__( 'Select opt-out request: %s', 'buddypress' ), $optout->id ); 240 ?></label> 241 <input type="checkbox" id="optout_<?php echo intval( $optout->id ) ?>" name="optout_ids[]" value="<?php echo esc_attr( $optout->id ) ?>" /> 242 <?php 243 } 244 245 /** 246 * Markup for the checkbox used to select items for bulk actions. 247 * 248 * @since 8.0.0 249 * 250 * @param BP_Optout $optout BP_Optout object. 251 */ 252 public function column_email_address( $optout = null ) { 253 printf( '<a href="mailto:%1$s">%2$s</a>', esc_attr( $optout->email_address ), esc_html( $optout->email_address ) ); 254 255 $actions = array(); 256 257 // Delete link. 258 $delete_link = add_query_arg( 259 array( 260 'page' => 'bp-members-optouts', 261 'optout_id' => $optout->id, 262 'action' => 'delete', 263 ), 264 bp_get_admin_url( 'users.php' ) 265 ); 266 $actions['delete'] = sprintf( '<a href="%1$s" class="delete">%2$s</a>', esc_url( $delete_link ), __( 'Delete', 'buddypress' ) ); 267 268 /** 269 * Filters the row actions for each opt-out in list. 270 * 271 * @since 8.0.0 272 * 273 * @param array $actions Array of actions and corresponding links. 274 * @param object $optout The BP_Optout. 275 */ 276 $actions = apply_filters( 'bp_members_optouts_management_row_actions', $actions, $optout ); 277 278 echo $this->row_actions( $actions ); 279 } 280 281 /** 282 * The inviter/site member who sent the email that prompted the opt-out. 283 * 284 * @since 8.0.0 285 * 286 * @param BP_Optout $optout BP_Optout object. 287 */ 288 public function column_username( $optout = null ) { 289 $avatar = get_avatar( $optout->user_id, 32 ); 290 $inviter = get_user_by( 'id', $optout->user_id ); 291 if ( ! $inviter ) { 292 return; 293 } 294 $user_link = bp_core_get_user_domain( $optout->user_id ); 295 echo $avatar . sprintf( '<strong><a href="%1$s" class="edit">%2$s</a></strong><br/>', esc_url( $user_link ), $inviter->user_login ); 296 } 297 298 /** 299 * Display registration date of user whose communication prompted opt-out. 300 * 301 * @since 8.0.0 302 * 303 * @param BP_Optout $optout BP_Optout object. 304 */ 305 public function column_user_registered( $optout = null ) { 306 $inviter = get_user_by( 'id', $optout->user_id ); 307 if ( ! $inviter ) { 308 return; 309 } 310 echo esc_html( mysql2date( 'Y/m/d g:i:s a', $inviter->user_registered ) ); 311 } 312 313 /** 314 * Display type of email that prompted opt-out. 315 * 316 * @since 8.0.0 317 * 318 * @param BP_Optout $optout BP_Optout object. 319 */ 320 public function column_email_type( $optout = null ) { 321 echo esc_html( $optout->email_type ); 322 } 323 324 /** 325 * Display description of bp-email-type that prompted opt-out. 326 * 327 * @since 8.0.0 328 * 329 * @param BP_Optout $optout BP_Optout object. 330 */ 331 public function column_email_type_description( $optout = null ) { 332 $type_term = get_term_by( 'slug', $optout->email_type, 'bp-email-type' ); 333 if ( $type_term ) { 334 echo esc_html( $type_term->description ); 335 } 336 337 } 338 339 /** 340 * Display opt-out date. 341 * 342 * @since 8.0.0 343 * 344 * @param BP_Optout $optout BP_Optout object. 345 */ 346 public function column_optout_date_modified( $optout = null ) { 347 echo esc_html( mysql2date( 'Y/m/d g:i:s a', $optout->date_modified ) ); 348 } 349 350 /** 351 * Allow plugins to add custom columns. 352 * 353 * @since 8.0.0 354 * 355 * @param BP_Optout $optout BP_Optout object. 356 * @param string $column_name The column name. 357 * @return string 358 */ 359 function column_default( $optout = null, $column_name = '' ) { 360 361 /** 362 * Filters the single site custom columns for plugins. 363 * 364 * @since 8.0.0 365 * 366 * @param string $column_name The column name. 367 * @param BP_Optout $optout BP_Optout object. 368 */ 369 return apply_filters( 'bp_members_optouts_management_custom_column', '', $column_name, $optout ); 370 } 371 } -
src/class-buddypress.php
diff --git a/src/class-buddypress.php b/src/class-buddypress.php index 3dd1d483c..a571fe3b3 100644
a b class BuddyPress { 597 597 'BP_REST_Components_Endpoint' => 'core', 598 598 'BP_REST_Attachments' => 'core', 599 599 'BP_Admin_Types' => 'core', 600 'BP_Optout' => 'core', 600 601 601 602 'BP_Core_Friends_Widget' => 'friends', 602 603 'BP_REST_Friends_Endpoint' => 'friends', -
new file tests/phpunit/testcases/core/optouts.php
diff --git a/tests/phpunit/testcases/core/optouts.php b/tests/phpunit/testcases/core/optouts.php new file mode 100644 index 000000000..aebca0d7c
- + 1 <?php 2 /** 3 * @group core 4 * @group optouts 5 */ 6 class BP_Tests_Optouts extends BP_UnitTestCase { 7 public function test_bp_optouts_add_optout_vanilla() { 8 $old_current_user = get_current_user_id(); 9 10 $u1 = $this->factory->user->create(); 11 $this->set_current_user( $u1 ); 12 13 // Create a couple of optouts. 14 $args = array( 15 'email_address' => 'one@wp.org', 16 'user_id' => $u1, 17 'email_type' => 'annoyance' 18 ); 19 $i1 = bp_add_optout( $args ); 20 $args['email_address'] = 'two@wp.org'; 21 $i2 = bp_add_optout( $args ); 22 23 $get_args = array( 24 'user_id' => $u1, 25 'fields' => 'ids', 26 ); 27 $optouts = bp_get_optouts( $get_args ); 28 $this->assertEqualSets( array( $i1, $i2 ), $optouts ); 29 30 $this->set_current_user( $old_current_user ); 31 } 32 33 public function test_bp_optouts_add_optout_avoid_duplicates() { 34 $old_current_user = get_current_user_id(); 35 36 $u1 = $this->factory->user->create(); 37 $this->set_current_user( $u1 ); 38 39 // Create an optouts. 40 $args = array( 41 'email_address' => 'one@wp.org', 42 'user_id' => $u1, 43 'email_type' => 'annoyance' 44 ); 45 $i1 = bp_add_optout( $args ); 46 // Attempt to create a duplicate. Should return existing optout id. 47 $i2 = bp_add_optout( $args ); 48 $this->assertEquals( $i1, $i2 ); 49 50 $this->set_current_user( $old_current_user ); 51 } 52 53 public function test_bp_optouts_delete_optout() { 54 $old_current_user = get_current_user_id(); 55 56 $u1 = $this->factory->user->create(); 57 $this->set_current_user( $u1 ); 58 59 $args = array( 60 'email_address' => 'one@wp.org', 61 'user_id' => $u1, 62 'email_type' => 'annoyance' 63 ); 64 $i1 = bp_add_optout( $args ); 65 bp_delete_optout_by_id( $i1 ); 66 67 $get_args = array( 68 'user_id' => $u1, 69 'fields' => 'ids', 70 ); 71 $optouts = bp_get_optouts( $get_args ); 72 $this->assertTrue( empty( $optouts ) ); 73 74 $this->set_current_user( $old_current_user ); 75 } 76 77 public function test_bp_optouts_get_by_search_terms() { 78 $old_current_user = get_current_user_id(); 79 80 $u1 = $this->factory->user->create(); 81 $this->set_current_user( $u1 ); 82 83 // Create a couple of optouts. 84 $args = array( 85 'email_address' => 'one@wpfrost.org', 86 'user_id' => $u1, 87 'email_type' => 'annoyance' 88 ); 89 $i1 = bp_add_optout( $args ); 90 $args['email_address'] = 'two@wp.org'; 91 $i2 = bp_add_optout( $args ); 92 93 $get_args = array( 94 'search_terms' => 'frost', 95 'fields' => 'ids', 96 ); 97 $optouts = bp_get_optouts( $get_args ); 98 $this->assertEqualSets( array( $i1 ), $optouts ); 99 100 $this->set_current_user( $old_current_user ); 101 } 102 103 }