Ticket #8448: 8448.2.diff
File 8448.2.diff, 58.0 KB (added by , 4 years ago) |
---|
-
src/bp-core/admin/bp-core-admin-schema.php
diff --git src/bp-core/admin/bp-core-admin-schema.php src/bp-core/admin/bp-core-admin-schema.php index d7cab5bbe..ac49a63ae 100644
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_hash 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 src/bp-core/bp-core-cache.php src/bp-core/bp-core-cache.php index 12c674241..35d381a50 100644
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 src/bp-core/bp-core-functions.php src/bp-core/bp-core-functions.php index 592f5b155..88e4b651b 100644
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 src/bp-core/bp-core-update.php src/bp-core/bp-core-update.php index a7791c445..fe6a26d9e 100644
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 src/bp-core/classes/class-bp-optout.php src/bp-core/classes/class-bp-optout.php new file mode 100644 index 000000000..6d7725c34
- + 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 hashed 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_hash' => $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_hash; 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 hashed 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 // We must hash the email address at insert. 216 $data['email_address_hash'] = wp_hash( $data['email_address_hash'] ); 217 return $wpdb->insert( BP_Optout::get_table_name(), $data, $data_format ); 218 } 219 220 /** 221 * Update opt-outs. 222 * 223 * @since 8.0.0 224 * 225 * @see wpdb::update() for further description of paramater formats. 226 * 227 * @param array $data Array of optout data to update, passed to 228 * {@link wpdb::update()}. Accepts any property of a 229 * BP_optout object. 230 * @param array $where The WHERE params as passed to wpdb::update(). 231 * Typically consists of array( 'ID' => $id ) to specify the ID 232 * of the item being updated. See {@link wpdb::update()}. 233 * @param array $data_format See {@link wpdb::insert()}. 234 * @param array $where_format See {@link wpdb::insert()}. 235 * @return int|false The number of rows updated, or false on error. 236 */ 237 protected static function _update( $data = array(), $where = array(), $data_format = array(), $where_format = array() ) { 238 global $wpdb; 239 240 // Ensure that a passed email address is hashed. 241 if ( ! empty( $data['email_address_hash'] ) && is_email( $data['email_address_hash'] ) ) { 242 $data['email_address_hash'] = wp_hash( $data['email_address_hash'] ); 243 } 244 245 return $wpdb->update( BP_Optout::get_table_name(), $data, $where, $data_format, $where_format ); 246 } 247 248 /** 249 * Delete opt-outs. 250 * 251 * @since 8.0.0 252 * 253 * @see wpdb::update() for further description of paramater formats. 254 * 255 * @param array $where Array of WHERE clauses to filter by, passed to 256 * {@link wpdb::delete()}. Accepts any property of a 257 * BP_optout object. 258 * @param array $where_format See {@link wpdb::insert()}. 259 * @return int|false The number of rows updated, or false on error. 260 */ 261 protected static function _delete( $where = array(), $where_format = array() ) { 262 global $wpdb; 263 return $wpdb->delete( BP_Optout::get_table_name(), $where, $where_format ); 264 } 265 266 /** 267 * Assemble the WHERE clause of a get() SQL statement. 268 * 269 * Used by BP_optout::get() to create its WHERE 270 * clause. 271 * 272 * @since 8.0.0 273 * 274 * @param array $args See {@link BP_optout::get()} for more details. 275 * @return string WHERE clause. 276 */ 277 protected static function get_where_sql( $args = array() ) { 278 global $wpdb; 279 280 $where_conditions = array(); 281 $where = ''; 282 283 // id. 284 if ( false !== $args['id'] ) { 285 $id_in = implode( ',', wp_parse_id_list( $args['id'] ) ); 286 $where_conditions['id'] = "id IN ({$id_in})"; 287 } 288 289 // email_address. 290 if ( ! empty( $args['email_address'] ) ) { 291 if ( ! is_array( $args['email_address'] ) ) { 292 $emails = explode( ',', $args['email_address'] ); 293 } else { 294 $emails = $args['email_address']; 295 } 296 297 $email_clean = array(); 298 foreach ( $emails as $email ) { 299 $email_hash = wp_hash( $email ); 300 $email_clean[] = $wpdb->prepare( '%s', $email_hash ); 301 } 302 303 $email_in = implode( ',', $email_clean ); 304 $where_conditions['email_address'] = "email_address_hash IN ({$email_in})"; 305 } 306 307 // user_id. 308 if ( ! empty( $args['user_id'] ) ) { 309 $user_id_in = implode( ',', wp_parse_id_list( $args['user_id'] ) ); 310 $where_conditions['user_id'] = "user_id IN ({$user_id_in})"; 311 } 312 313 // email_type. 314 if ( ! empty( $args['email_type'] ) ) { 315 if ( ! is_array( $args['email_type'] ) ) { 316 $email_types = explode( ',', $args['email_type'] ); 317 } else { 318 $email_types = $args['email_type']; 319 } 320 321 $et_clean = array(); 322 foreach ( $email_types as $et ) { 323 $et_clean[] = $wpdb->prepare( '%s', sanitize_key( $et ) ); 324 } 325 326 $et_in = implode( ',', $et_clean ); 327 $where_conditions['email_type'] = "email_type IN ({$et_in})"; 328 } 329 330 // search_terms. 331 if ( ! empty( $args['search_terms'] ) ) { 332 // Matching email_address is an exact match because of the hashing. 333 $search_terms_like = wp_hash( $args['search_terms'] ); 334 $where_conditions['search_terms'] = $wpdb->prepare( '( email_address_hash LIKE %s )', $search_terms_like ); 335 } 336 337 // Custom WHERE. 338 if ( ! empty( $where_conditions ) ) { 339 $where = 'WHERE ' . implode( ' AND ', $where_conditions ); 340 } 341 342 return $where; 343 } 344 345 /** 346 * Assemble the ORDER BY clause of a get() SQL statement. 347 * 348 * Used by BP_Optout::get() to create its ORDER BY 349 * clause. 350 * 351 * @since 8.0.0 352 * 353 * @param array $args See {@link BP_optout::get()} for more details. 354 * @return string ORDER BY clause. 355 */ 356 protected static function get_order_by_sql( $args = array() ) { 357 358 $conditions = array(); 359 $retval = ''; 360 361 // Order by. 362 if ( ! empty( $args['order_by'] ) ) { 363 $order_by_clean = array(); 364 $columns = array( 'id', 'email_address_hash', 'user_id', 'email_type', 'date_modified' ); 365 foreach ( (array) $args['order_by'] as $key => $value ) { 366 if ( in_array( $value, $columns, true ) ) { 367 $order_by_clean[] = $value; 368 } 369 } 370 if ( ! empty( $order_by_clean ) ) { 371 $order_by = implode( ', ', $order_by_clean ); 372 $conditions['order_by'] = "{$order_by}"; 373 } 374 } 375 376 // Sort order direction. 377 if ( ! empty( $args['sort_order'] ) ) { 378 $sort_order = bp_esc_sql_order( $args['sort_order'] ); 379 $conditions['sort_order'] = "{$sort_order}"; 380 } 381 382 // Custom ORDER BY. 383 if ( ! empty( $conditions['order_by'] ) ) { 384 $retval = 'ORDER BY ' . implode( ' ', $conditions ); 385 } 386 387 return $retval; 388 } 389 390 /** 391 * Assemble the LIMIT clause of a get() SQL statement. 392 * 393 * Used by BP_Optout::get() to create its LIMIT clause. 394 * 395 * @since 8.0.0 396 * 397 * @param array $args See {@link BP_optout::get()} for more details. 398 * @return string LIMIT clause. 399 */ 400 protected static function get_paged_sql( $args = array() ) { 401 global $wpdb; 402 403 // Setup local variable. 404 $retval = ''; 405 406 // Custom LIMIT. 407 if ( ! empty( $args['page'] ) && ! empty( $args['per_page'] ) ) { 408 $page = absint( $args['page'] ); 409 $per_page = absint( $args['per_page'] ); 410 $offset = $per_page * ( $page - 1 ); 411 $retval = $wpdb->prepare( "LIMIT %d, %d", $offset, $per_page ); 412 } 413 414 return $retval; 415 } 416 417 /** 418 * Assemble query clauses, based on arguments, to pass to $wpdb methods. 419 * 420 * The insert(), update(), and delete() methods of {@link wpdb} expect 421 * arguments of the following forms: 422 * 423 * - associative arrays whose key/value pairs are column => value, to 424 * be used in WHERE, SET, or VALUES clauses 425 * - arrays of "formats", which tell $wpdb->prepare() which type of 426 * value to expect when sanitizing (eg, array( '%s', '%d' )) 427 * 428 * This utility method can be used to assemble both kinds of params, 429 * out of a single set of associative array arguments, such as: 430 * 431 * $args = array( 432 * 'user_id' => 4, 433 * 'email_type' => 'type_string', 434 * ); 435 * 436 * This will be converted to: 437 * 438 * array( 439 * 'data' => array( 440 * 'user_id' => 4, 441 * 'email_type' => 'type_string', 442 * ), 443 * 'format' => array( 444 * '%d', 445 * '%s', 446 * ), 447 * ) 448 * 449 * which can easily be passed as arguments to the $wpdb methods. 450 * 451 * @since 8.0.0 452 * 453 * @param array $args Associative array of filter arguments. 454 * See {@BP_optout::get()} for a breakdown. 455 * @return array Associative array of 'data' and 'format' args. 456 */ 457 protected static function get_query_clauses( $args = array() ) { 458 $where_clauses = array( 459 'data' => array(), 460 'format' => array(), 461 ); 462 463 // id. 464 if ( ! empty( $args['id'] ) ) { 465 $where_clauses['data']['id'] = absint( $args['id'] ); 466 $where_clauses['format'][] = '%d'; 467 } 468 469 // email_address. 470 if ( ! empty( $args['email_address'] ) ) { 471 $where_clauses['data']['email_address_hash'] = $args['email_address']; 472 $where_clauses['format'][] = '%s'; 473 } 474 475 // user_id. 476 if ( ! empty( $args['user_id'] ) ) { 477 $where_clauses['data']['user_id'] = absint( $args['user_id'] ); 478 $where_clauses['format'][] = '%d'; 479 } 480 481 // email_type. 482 if ( ! empty( $args['email_type'] ) ) { 483 $where_clauses['data']['email_type'] = $args['email_type']; 484 $where_clauses['format'][] = '%s'; 485 } 486 487 return $where_clauses; 488 } 489 490 /** Public Static Methods *********************************************/ 491 492 /** 493 * Get opt-outs, based on provided filter parameters. 494 * 495 * @since 8.0.0 496 * 497 * @param array $args { 498 * Associative array of arguments. All arguments but $page and 499 * $per_page can be treated as filter values for get_where_sql() 500 * and get_query_clauses(). All items are optional. 501 * @type int|array $id ID of opt-out. 502 * Can be an array of IDs. 503 * @type string|array $email_address Email address of users who have opted out 504 * being queried. Can be an array of addresses. 505 * @type int|array $user_id ID of user whose communication prompted the 506 * opt-out. Can be an array of IDs. 507 * @type string|array $email_type Name of the emil type to filter by. 508 * Can be an array of email types. 509 * @type string $search_terms Term to match against email_address field. 510 * @type string $order_by Database column to order by. 511 * @type string $sort_order Either 'ASC' or 'DESC'. 512 * @type int $page Number of the current page of results. 513 * Default: false (no pagination, 514 * all items). 515 * @type int $per_page Number of items to show per page. 516 * Default: false (no pagination, 517 * all items). 518 * @type string $fields Which fields to return. Specify 'email_addresses' to 519 * fetch a list of opt-out email_addresses. 520 * Specify 'user_ids' to 521 * fetch a list of opt-out user_ids. 522 * Specify 'ids' to fetch a list of opt-out IDs. 523 * Default: 'all' (return BP_Optout objects). 524 * } 525 * 526 * @return array BP_Optout objects | IDs of found opt-outs | Email addresses of matches. 527 */ 528 public static function get( $args = array() ) { 529 global $wpdb; 530 $optouts_table_name = BP_Optout::get_table_name(); 531 532 // Parse the arguments. 533 $r = bp_parse_args( $args, array( 534 'id' => false, 535 'email_address' => false, 536 'user_id' => false, 537 'email_type' => false, 538 'search_terms' => '', 539 'order_by' => false, 540 'sort_order' => false, 541 'page' => false, 542 'per_page' => false, 543 'fields' => 'all', 544 ), 'bp_optout_get' ); 545 546 $sql = array( 547 'select' => "SELECT", 548 'fields' => '', 549 'from' => "FROM {$optouts_table_name} o", 550 'where' => '', 551 'orderby' => '', 552 'pagination' => '', 553 ); 554 555 if ( 'user_ids' === $r['fields'] ) { 556 $sql['fields'] = "DISTINCT o.user_id"; 557 } else if ( 'email_addresses' === $r['fields'] ) { 558 $sql['fields'] = "DISTINCT o.email_address_hash"; 559 } else { 560 $sql['fields'] = 'DISTINCT o.id'; 561 } 562 563 // WHERE. 564 $sql['where'] = self::get_where_sql( array( 565 'id' => $r['id'], 566 'email_address' => $r['email_address'], 567 'user_id' => $r['user_id'], 568 'email_type' => $r['email_type'], 569 'search_terms' => $r['search_terms'], 570 ) ); 571 572 // ORDER BY. 573 $sql['orderby'] = self::get_order_by_sql( array( 574 'order_by' => $r['order_by'], 575 'sort_order' => $r['sort_order'] 576 ) ); 577 578 // LIMIT %d, %d. 579 $sql['pagination'] = self::get_paged_sql( array( 580 'page' => $r['page'], 581 'per_page' => $r['per_page'], 582 ) ); 583 584 $paged_optouts_sql = "{$sql['select']} {$sql['fields']} {$sql['from']} {$sql['where']} {$sql['orderby']} {$sql['pagination']}"; 585 586 /** 587 * Filters the pagination SQL statement. 588 * 589 * @since 8.0.0 590 * 591 * @param string $value Concatenated SQL statement. 592 * @param array $sql Array of SQL parts before concatenation. 593 * @param array $r Array of parsed arguments for the get method. 594 */ 595 $paged_optouts_sql = apply_filters( 'bp_optouts_get_paged_optouts_sql', $paged_optouts_sql, $sql, $r ); 596 597 $cached = bp_core_get_incremented_cache( $paged_optouts_sql, 'bp_optouts' ); 598 if ( false === $cached ) { 599 $paged_optout_ids = $wpdb->get_col( $paged_optouts_sql ); 600 bp_core_set_incremented_cache( $paged_optouts_sql, 'bp_optouts', $paged_optout_ids ); 601 } else { 602 $paged_optout_ids = $cached; 603 } 604 605 // Special return format cases. 606 if ( in_array( $r['fields'], array( 'ids', 'user_ids' ), true ) ) { 607 // We only want the field that was found. 608 return array_map( 'intval', $paged_optout_ids ); 609 } else if ( 'email_addresses' === $r['fields'] ) { 610 return $paged_optout_ids; 611 } 612 613 $uncached_ids = bp_get_non_cached_ids( $paged_optout_ids, 'bp_optouts' ); 614 if ( $uncached_ids ) { 615 $ids_sql = implode( ',', array_map( 'intval', $uncached_ids ) ); 616 $data_objects = $wpdb->get_results( "SELECT o.* FROM {$optouts_table_name} o WHERE o.id IN ({$ids_sql})" ); 617 foreach ( $data_objects as $data_object ) { 618 wp_cache_set( $data_object->id, $data_object, 'bp_optouts' ); 619 } 620 } 621 622 $paged_optouts = array(); 623 foreach ( $paged_optout_ids as $paged_optout_id ) { 624 $paged_optouts[] = new BP_optout( $paged_optout_id ); 625 } 626 627 return $paged_optouts; 628 } 629 630 /** 631 * Get a count of total optouts matching a set of arguments. 632 * 633 * @since 8.0.0 634 * 635 * @see BP_optout::get() for a description of 636 * arguments. 637 * 638 * @param array $args See {@link BP_optout::get()}. 639 * @return int Count of located items. 640 */ 641 public static function get_total_count( $args ) { 642 global $wpdb; 643 $optouts_table_name = BP_Optout::get_table_name(); 644 645 // Parse the arguments. 646 $r = bp_parse_args( $args, array( 647 'id' => false, 648 'email_address' => false, 649 'user_id' => false, 650 'email_type' => false, 651 'search_terms' => '', 652 'order_by' => false, 653 'sort_order' => false, 654 'page' => false, 655 'per_page' => false, 656 'fields' => 'all', 657 ), 'bp_optout_get_total_count' ); 658 659 // Build the query 660 $select_sql = "SELECT COUNT(*)"; 661 $from_sql = "FROM {$optouts_table_name}"; 662 $where_sql = self::get_where_sql( $r ); 663 $sql = "{$select_sql} {$from_sql} {$where_sql}"; 664 665 // Return the queried results 666 return $wpdb->get_var( $sql ); 667 } 668 669 /** 670 * Update optouts. 671 * 672 * @since 8.0.0 673 * 674 * @see BP_optout::get() for a description of 675 * accepted update/where arguments. 676 * 677 * @param array $update_args Associative array of fields to update, 678 * and the values to update them to. Of the format 679 * array( 'user_id' => 4, 'email_address' => 'bar@foo.com', ). 680 * @param array $where_args Associative array of columns/values, to 681 * determine which rows should be updated. Of the format 682 * array( 'user_id' => 7, 'email_address' => 'bar@foo.com', ). 683 * @return int|bool Number of rows updated on success, false on failure. 684 */ 685 public static function update( $update_args = array(), $where_args = array() ) { 686 $update = self::get_query_clauses( $update_args ); 687 $where = self::get_query_clauses( $where_args ); 688 689 /** 690 * Fires before an opt-out is updated. 691 * 692 * @since 8.0.0 693 * 694 * @param array $where_args Associative array of columns/values describing 695 * optouts about to be deleted. 696 * @param array $update_args Array of new values. 697 */ 698 do_action( 'bp_optout_before_update', $where_args, $update_args ); 699 700 $retval = self::_update( $update['data'], $where['data'], $update['format'], $where['format'] ); 701 702 // Clear matching items from the cache. 703 $cache_args = $where_args; 704 $cache_args['fields'] = 'ids'; 705 $maybe_cached_ids = self::get( $cache_args ); 706 foreach ( $maybe_cached_ids as $invite_id ) { 707 wp_cache_delete( $invite_id, 'bp_optouts' ); 708 } 709 710 /** 711 * Fires after an opt-out is updated. 712 * 713 * @since 8.0.0 714 * 715 * @param array $where_args Associative array of columns/values describing 716 * optouts about to be deleted. 717 * @param array $update_args Array of new values. 718 */ 719 do_action( 'bp_optout_after_update', $where_args, $update_args ); 720 721 return $retval; 722 } 723 724 /** 725 * Delete opt-outs. 726 * 727 * @since 8.0.0 728 * 729 * @see BP_optout::get() for a description of 730 * accepted where arguments. 731 * 732 * @param array $args Associative array of columns/values, to determine 733 * which rows should be deleted. Of the format 734 * array( 'user_id' => 7, 'email_address' => 'bar@foo.com', ). 735 * @return int|bool Number of rows deleted on success, false on failure. 736 */ 737 public static function delete( $args = array() ) { 738 $where = self::get_query_clauses( $args ); 739 740 /** 741 * Fires before an optout is deleted. 742 * 743 * @since 8.0.0 744 * 745 * @param array $args Characteristics of the optouts to be deleted. 746 */ 747 do_action( 'bp_optout_before_delete', $args ); 748 749 // Clear matching items from the cache. 750 $cache_args = $args; 751 $cache_args['fields'] = 'ids'; 752 $maybe_cached_ids = self::get( $cache_args ); 753 foreach ( $maybe_cached_ids as $invite_id ) { 754 wp_cache_delete( $invite_id, 'bp_optouts' ); 755 } 756 757 $retval = self::_delete( $where['data'], $where['format'] ); 758 759 /** 760 * Fires after an optout is deleted. 761 * 762 * @since 8.0.0 763 * 764 * @param array $args Characteristics of the optouts just deleted. 765 */ 766 do_action( 'bp_optout_after_delete', $args ); 767 768 return $retval; 769 } 770 771 /** Convenience methods ***********************************************/ 772 773 /** 774 * Check whether an invitation exists matching the passed arguments. 775 * 776 * @since 5.0.0 777 * 778 * @see BP_Optout::get() for a description of accepted parameters. 779 * 780 * @return int|bool ID of first found invitation or false if none found. 781 */ 782 public function optout_exists( $args = array() ) { 783 $exists = false; 784 785 $args['fields'] = 'ids'; 786 $optouts = BP_Optout::get( $args ); 787 if ( $optouts ) { 788 $exists = current( $optouts ); 789 } 790 return $exists; 791 } 792 793 /** 794 * Delete a single optout by ID. 795 * 796 * @since 8.0.0 797 * 798 * @see BP_optout::delete() for explanation of 799 * return value. 800 * 801 * @param int $id ID of the optout item to be deleted. 802 * @return bool True on success, false on failure. 803 */ 804 public static function delete_by_id( $id ) { 805 return self::delete( array( 806 'id' => $id, 807 ) ); 808 } 809 810 } -
src/bp-members/classes/class-bp-members-admin.php
diff --git src/bp-members/classes/class-bp-members-admin.php src/bp-members/classes/class-bp-members-admin.php index c999cb923..5e90b094c 100644
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 src/bp-members/classes/class-bp-members-component.php src/bp-members/classes/class-bp-members-component.php index 77777d645..92ee286a1 100644
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 src/bp-members/classes/class-bp-members-optouts-list-table.php src/bp-members/classes/class-bp-members-optouts-list-table.php new file mode 100644 index 000000000..d50e40cc6
- + 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 Hash', '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_hash', 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 src/class-buddypress.php src/class-buddypress.php index 3dd1d483c..a571fe3b3 100644
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 tests/phpunit/testcases/core/optouts.php tests/phpunit/testcases/core/optouts.php new file mode 100644 index 000000000..7c0d1ecc5
- + 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' => 'one@wpfrost.org', 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 }