[ Index ] |
PHP Cross Reference of BuddyPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * BuddyPress Common Functions. 4 * 5 * @package BuddyPress 6 * @subpackage Functions 7 * @since 1.5.0 8 */ 9 10 // Exit if accessed directly. 11 defined( 'ABSPATH' ) || exit; 12 13 /** Versions ******************************************************************/ 14 15 /** 16 * Output the BuddyPress version. 17 * 18 * @since 1.6.0 19 */ 20 function bp_version() { 21 echo bp_get_version(); 22 } 23 /** 24 * Return the BuddyPress version. 25 * 26 * @since 1.6.0 27 * 28 * @return string The BuddyPress version. 29 */ 30 function bp_get_version() { 31 return buddypress()->version; 32 } 33 34 /** 35 * Output the BuddyPress database version. 36 * 37 * @since 1.6.0 38 */ 39 function bp_db_version() { 40 echo bp_get_db_version(); 41 } 42 /** 43 * Return the BuddyPress database version. 44 * 45 * @since 1.6.0 46 * 47 * @return string The BuddyPress database version. 48 */ 49 function bp_get_db_version() { 50 return buddypress()->db_version; 51 } 52 53 /** 54 * Output the BuddyPress database version. 55 * 56 * @since 1.6.0 57 */ 58 function bp_db_version_raw() { 59 echo bp_get_db_version_raw(); 60 } 61 /** 62 * Return the BuddyPress database version. 63 * 64 * @since 1.6.0 65 * 66 * @return string The BuddyPress version direct from the database. 67 */ 68 function bp_get_db_version_raw() { 69 $bp = buddypress(); 70 return !empty( $bp->db_version_raw ) ? $bp->db_version_raw : 0; 71 } 72 73 /** 74 * Check whether the current version of WP exceeds a given version. 75 * 76 * @since 7.0.0 77 * 78 * @param string $version WP version, in "PHP-standardized" format. 79 * @param string $compare Optional. Comparison operator. Default '>='. 80 * @return bool 81 */ 82 function bp_is_running_wp( $version, $compare = '>=' ) { 83 return version_compare( $GLOBALS['wp_version'], $version, $compare ); 84 } 85 86 /** Functions *****************************************************************/ 87 88 /** 89 * Get the $wpdb base prefix, run through the 'bp_core_get_table_prefix' filter. 90 * 91 * The filter is intended primarily for use in multinetwork installations. 92 * 93 * @since 1.2.6 94 * 95 * @global wpdb $wpdb WordPress database object. 96 * 97 * @return string Filtered database prefix. 98 */ 99 function bp_core_get_table_prefix() { 100 global $wpdb; 101 102 /** 103 * Filters the $wpdb base prefix. 104 * 105 * Intended primarily for use in multinetwork installations. 106 * 107 * @since 1.2.6 108 * 109 * @param string $base_prefix Base prefix to use. 110 */ 111 return apply_filters( 'bp_core_get_table_prefix', $wpdb->base_prefix ); 112 } 113 114 /** 115 * Sort an array of objects or arrays by a specific key/property. 116 * 117 * The main purpose for this function is so that you can avoid having to create 118 * your own awkward callback function for usort(). 119 * 120 * @since 2.2.0 121 * @since 2.7.0 Added $preserve_keys parameter. 122 * 123 * @param array $items The items to be sorted. Its constituent items 124 * can be either associative arrays or objects. 125 * @param string|int $key The array index or property name to sort by. 126 * @param string $type Sort type. 'alpha' for alphabetical, 'num' 127 * for numeric. Default: 'alpha'. 128 * @param bool $preserve_keys Whether to keep the keys or not. 129 * 130 * @return array $items The sorted array. 131 */ 132 function bp_sort_by_key( $items, $key, $type = 'alpha', $preserve_keys = false ) { 133 $callback = function( $a, $b ) use ( $key, $type ) { 134 $values = array( 0 => false, 1 => false ); 135 foreach ( func_get_args() as $indexi => $index ) { 136 if ( isset( $index->{$key} ) ) { 137 $values[ $indexi ] = $index->{$key}; 138 } elseif ( isset( $index[ $key ] ) ) { 139 $values[ $indexi ] = $index[ $key ]; 140 } 141 } 142 143 if ( isset( $values[0], $values[1] ) ) { 144 if ( 'num' === $type ) { 145 $cmp = $values[0] - $values[1]; 146 } else { 147 $cmp = strcmp( $values[0], $values[1] ); 148 } 149 150 if ( 0 > $cmp ) { 151 $retval = -1; 152 } elseif ( 0 < $cmp ) { 153 $retval = 1; 154 } else { 155 $retval = 0; 156 } 157 return $retval; 158 } else { 159 return 0; 160 } 161 }; 162 163 if ( true === $preserve_keys ) { 164 uasort( $items, $callback ); 165 } else { 166 usort( $items, $callback ); 167 } 168 169 return $items; 170 } 171 172 /** 173 * Sort an array of objects or arrays by alphabetically sorting by a specific key/property. 174 * 175 * For instance, if you have an array of WordPress post objects, you can sort 176 * them by post_name as follows: 177 * $sorted_posts = bp_alpha_sort_by_key( $posts, 'post_name' ); 178 * 179 * @since 1.9.0 180 * 181 * @param array $items The items to be sorted. Its constituent items can be either associative arrays or objects. 182 * @param string|int $key The array index or property name to sort by. 183 * @return array $items The sorted array. 184 */ 185 function bp_alpha_sort_by_key( $items, $key ) { 186 return bp_sort_by_key( $items, $key, 'alpha' ); 187 } 188 189 /** 190 * Format numbers the BuddyPress way. 191 * 192 * @since 1.2.0 193 * 194 * @param int $number The number to be formatted. 195 * @param bool $decimals Whether to use decimals. See {@link number_format_i18n()}. 196 * @return string The formatted number. 197 */ 198 function bp_core_number_format( $number = 0, $decimals = false ) { 199 200 // Force number to 0 if needed. 201 if ( ! is_numeric( $number ) ) { 202 $number = 0; 203 } 204 205 /** 206 * Filters the BuddyPress formatted number. 207 * 208 * @since 1.2.4 209 * 210 * @param string $value BuddyPress formatted value. 211 * @param int $number The number to be formatted. 212 * @param bool $decimals Whether or not to use decimals. 213 */ 214 return apply_filters( 'bp_core_number_format', number_format_i18n( $number, $decimals ), $number, $decimals ); 215 } 216 217 /** 218 * A utility for parsing individual function arguments into an array. 219 * 220 * The purpose of this function is to help with backward compatibility in cases where 221 * 222 * function foo( $bar = 1, $baz = false, $barry = array(), $blip = false ) { // ... 223 * 224 * is deprecated in favor of 225 * 226 * function foo( $args = array() ) { 227 * $defaults = array( 228 * 'bar' => 1, 229 * 'arg2' => false, 230 * 'arg3' => array(), 231 * 'arg4' => false, 232 * ); 233 * $r = bp_parse_args( $args, $defaults ); // ... 234 * 235 * The first argument, $old_args_keys, is an array that matches the parameter positions (keys) to 236 * the new $args keys (values): 237 * 238 * $old_args_keys = array( 239 * 0 => 'bar', // because $bar was the 0th parameter for foo() 240 * 1 => 'baz', // because $baz was the 1st parameter for foo() 241 * 2 => 'barry', // etc 242 * 3 => 'blip' 243 * ); 244 * 245 * For the second argument, $func_args, you should just pass the value of func_get_args(). 246 * 247 * @since 1.6.0 248 * 249 * @param array $old_args_keys Old argument indexes, keyed to their positions. 250 * @param array $func_args The parameters passed to the originating function. 251 * @return array $new_args The parsed arguments. 252 */ 253 function bp_core_parse_args_array( $old_args_keys, $func_args ) { 254 $new_args = array(); 255 256 foreach ( $old_args_keys as $arg_num => $arg_key ) { 257 if ( isset( $func_args[ $arg_num ] ) ) { 258 $new_args[ $arg_key ] = $func_args[ $arg_num ]; 259 } 260 } 261 262 return $new_args; 263 } 264 265 /** 266 * Merge user defined arguments into defaults array. 267 * 268 * This function is used throughout BuddyPress to allow for either a string or 269 * array to be merged into another array. It is identical to wp_parse_args() 270 * except it allows for arguments to be passively or aggressively filtered using 271 * the optional $filter_key parameter. If no $filter_key is passed, no filters 272 * are applied. 273 * 274 * @since 2.0.0 275 * 276 * @param string|array $args Value to merge with $defaults. 277 * @param array $defaults Array that serves as the defaults. 278 * @param string $filter_key String to key the filters from. 279 * @return array Merged user defined values with defaults. 280 */ 281 function bp_parse_args( $args, $defaults = array(), $filter_key = '' ) { 282 283 // Setup a temporary array from $args. 284 if ( is_object( $args ) ) { 285 $r = get_object_vars( $args ); 286 } elseif ( is_array( $args ) ) { 287 $r =& $args; 288 } else { 289 wp_parse_str( $args, $r ); 290 } 291 292 // Passively filter the args before the parse. 293 if ( ! empty( $filter_key ) ) { 294 295 /** 296 * Filters the arguments key before parsing if filter key provided. 297 * 298 * This is a dynamic filter dependent on the specified key. 299 * 300 * @since 2.0.0 301 * 302 * @param array $r Array of arguments to use. 303 */ 304 $r = apply_filters( 'bp_before_' . $filter_key . '_parse_args', $r ); 305 } 306 307 // Parse. 308 if ( is_array( $defaults ) && ! empty( $defaults ) ) { 309 $r = array_merge( $defaults, $r ); 310 } 311 312 // Aggressively filter the args after the parse. 313 if ( ! empty( $filter_key ) ) { 314 315 /** 316 * Filters the arguments key after parsing if filter key provided. 317 * 318 * This is a dynamic filter dependent on the specified key. 319 * 320 * @since 2.0.0 321 * 322 * @param array $r Array of parsed arguments. 323 */ 324 $r = apply_filters( 'bp_after_' . $filter_key . '_parse_args', $r ); 325 } 326 327 // Return the parsed results. 328 return $r; 329 } 330 331 /** 332 * Sanitizes a pagination argument based on both the request override and the 333 * original value submitted via a query argument, likely to a template class 334 * responsible for limiting the result set of a template loop. 335 * 336 * @since 2.2.0 337 * 338 * @param string $page_arg The $_REQUEST argument to look for. 339 * @param int $page The original page value to fall back to. 340 * @return int A sanitized integer value, good for pagination. 341 */ 342 function bp_sanitize_pagination_arg( $page_arg = '', $page = 1 ) { 343 344 // Check if request overrides exist. 345 if ( isset( $_REQUEST[ $page_arg ] ) ) { 346 347 // Get the absolute integer value of the override. 348 $int = absint( $_REQUEST[ $page_arg ] ); 349 350 // If override is 0, do not use it. This prevents unlimited result sets. 351 // @see https://buddypress.trac.wordpress.org/ticket/5796. 352 if ( $int ) { 353 $page = $int; 354 } 355 } 356 357 return intval( $page ); 358 } 359 360 /** 361 * Sanitize an 'order' parameter for use in building SQL queries. 362 * 363 * Strings like 'DESC', 'desc', ' desc' will be interpreted into 'DESC'. 364 * Everything else becomes 'ASC'. 365 * 366 * @since 1.8.0 367 * 368 * @param string $order The 'order' string, as passed to the SQL constructor. 369 * @return string The sanitized value 'DESC' or 'ASC'. 370 */ 371 function bp_esc_sql_order( $order = '' ) { 372 $order = strtoupper( trim( $order ) ); 373 return 'DESC' === $order ? 'DESC' : 'ASC'; 374 } 375 376 /** 377 * Escape special characters in a SQL LIKE clause. 378 * 379 * In WordPress 4.0, like_escape() was deprecated, due to incorrect 380 * documentation and improper sanitization leading to a history of misuse. To 381 * maintain compatibility with versions of WP before 4.0, we duplicate the 382 * logic of the replacement, wpdb::esc_like(). 383 * 384 * @since 2.1.0 385 * 386 * @global wpdb $wpdb WordPress database object. 387 * @see wpdb::esc_like() for more details on proper use. 388 * 389 * @param string $text The raw text to be escaped. 390 * @return string Text in the form of a LIKE phrase. Not SQL safe. Run through 391 * wpdb::prepare() before use. 392 */ 393 function bp_esc_like( $text ) { 394 global $wpdb; 395 396 if ( method_exists( $wpdb, 'esc_like' ) ) { 397 return $wpdb->esc_like( $text ); 398 } 399 400 return addcslashes( $text, '_%\\' ); 401 } 402 403 /** 404 * Are we running username compatibility mode? 405 * 406 * @since 1.5.0 407 * 408 * @todo Move to members component? 409 * 410 * @return bool False when compatibility mode is disabled, true when enabled. 411 * Default: false. 412 */ 413 function bp_is_username_compatibility_mode() { 414 415 /** 416 * Filters whether or not to use username compatibility mode. 417 * 418 * @since 1.5.0 419 * 420 * @param bool $value Whether or not username compatibility mode should be used. 421 */ 422 return apply_filters( 'bp_is_username_compatibility_mode', defined( 'BP_ENABLE_USERNAME_COMPATIBILITY_MODE' ) && BP_ENABLE_USERNAME_COMPATIBILITY_MODE ); 423 } 424 425 /** 426 * Should we use the WP Toolbar? 427 * 428 * The WP Toolbar, introduced in WP 3.1, is fully supported in BuddyPress as 429 * of BP 1.5. For BP 1.6, the WP Toolbar is the default. 430 * 431 * @since 1.5.0 432 * 433 * @return bool Default: true. False when WP Toolbar support is disabled. 434 */ 435 function bp_use_wp_admin_bar() { 436 437 // Default to true. 438 $use_admin_bar = true; 439 440 // Has the WP Toolbar constant been explicitly opted into? 441 if ( defined( 'BP_USE_WP_ADMIN_BAR' ) ) { 442 $use_admin_bar = (bool) BP_USE_WP_ADMIN_BAR; 443 } 444 445 /** 446 * Filters whether or not to use the admin bar. 447 * 448 * @since 1.5.0 449 * 450 * @param bool $use_admin_bar Whether or not to use the admin bar. 451 */ 452 return (bool) apply_filters( 'bp_use_wp_admin_bar', $use_admin_bar ); 453 } 454 455 456 /** 457 * Return the parent forum ID for the Legacy Forums abstraction layer. 458 * 459 * @since 1.5.0 460 * @since 3.0.0 Supported for compatibility with bbPress 2. 461 * 462 * @return int Forum ID. 463 */ 464 function bp_forums_parent_forum_id() { 465 466 /** 467 * Filters the parent forum ID for the bbPress abstraction layer. 468 * 469 * @since 1.5.0 470 * 471 * @param int BP_FORUMS_PARENT_FORUM_ID The Parent forum ID constant. 472 */ 473 return apply_filters( 'bp_forums_parent_forum_id', BP_FORUMS_PARENT_FORUM_ID ); 474 } 475 476 /** Directory *****************************************************************/ 477 478 /** 479 * Returns an array of core component IDs. 480 * 481 * @since 2.1.0 482 * 483 * @return array 484 */ 485 function bp_core_get_packaged_component_ids() { 486 $components = array( 487 'activity', 488 'members', 489 'groups', 490 'blogs', 491 'xprofile', 492 'friends', 493 'messages', 494 'settings', 495 'notifications', 496 ); 497 498 return $components; 499 } 500 501 /** 502 * Fetch a list of BP directory pages from the appropriate meta table. 503 * 504 * @since 1.5.0 505 * @since 10.0.0 Eventually switch the current site to BP root's one on multisite configs. 506 * 507 * @param string $status 'active' to return only pages associated with active components, 'all' to return all saved 508 * pages. When running save routines, use 'all' to avoid removing data related to inactive 509 * components. Default: 'active'. 510 * @return array|string An array of page IDs, keyed by component names, or an 511 * empty string if the list is not found. 512 */ 513 function bp_core_get_directory_page_ids( $status = 'active' ) { 514 $page_ids = bp_get_option( 'bp-pages', array() ); 515 $switched = false; 516 517 /* 518 * Make sure to switch the current site to BP root's one, if needed. 519 * 520 * @see https://buddypress.trac.wordpress.org/ticket/8592 521 */ 522 if ( is_multisite() ) { 523 $bp_site_id = bp_get_root_blog_id(); 524 525 if ( $bp_site_id !== get_current_blog_id() ) { 526 switch_to_blog( $bp_site_id ); 527 $switched = true; 528 } 529 } 530 531 // Loop through pages. 532 foreach ( $page_ids as $component_name => $page_id ) { 533 534 // Ensure that empty indexes are unset. Should only matter in edge cases. 535 if ( empty( $component_name ) || empty( $page_id ) ) { 536 unset( $page_ids[ $component_name ] ); 537 } 538 539 // Trashed pages should never appear in results. 540 if ( 'trash' == get_post_status( $page_id ) ) { 541 unset( $page_ids[ $component_name ] ); 542 } 543 544 // 'register' and 'activate' do not have components, but are allowed as special cases. 545 if ( in_array( $component_name, array( 'register', 'activate' ), true ) ) { 546 continue; 547 } 548 549 // Remove inactive component pages. 550 if ( ( 'active' === $status ) && ! bp_is_active( $component_name ) ) { 551 unset( $page_ids[ $component_name ] ); 552 } 553 } 554 555 if ( true === $switched ) { 556 restore_current_blog(); 557 } 558 559 /** 560 * Filters the list of BP directory pages from the appropriate meta table. 561 * 562 * @since 1.5.0 563 * @since 2.9.0 Add $status parameter 564 * 565 * @param array $page_ids Array of directory pages. 566 * @param string $status Page status to limit results to 567 */ 568 return (array) apply_filters( 'bp_core_get_directory_page_ids', $page_ids, $status ); 569 } 570 571 /** 572 * Get the page ID corresponding to a component directory. 573 * 574 * @since 2.6.0 575 * 576 * @param string|null $component The slug representing the component. Defaults to the current component. 577 * @return int|false The ID of the directory page associated with the component. False if none is found. 578 */ 579 function bp_core_get_directory_page_id( $component = null ) { 580 if ( ! $component ) { 581 $component = bp_current_component(); 582 } 583 584 $bp_pages = bp_core_get_directory_page_ids( 'all' ); 585 586 $page_id = false; 587 if ( $component && isset( $bp_pages[ $component ] ) ) { 588 $page_id = (int) $bp_pages[ $component ]; 589 } 590 591 return $page_id; 592 } 593 594 /** 595 * Store the list of BP directory pages in the appropriate meta table. 596 * 597 * The bp-pages data is stored in site_options (falls back to options on non-MS), 598 * in an array keyed by blog_id. This allows you to change your 599 * bp_get_root_blog_id() and go through the setup process again. 600 * 601 * @since 1.5.0 602 * 603 * @param array $blog_page_ids The IDs of the WP pages corresponding to BP 604 * component directories. 605 */ 606 function bp_core_update_directory_page_ids( $blog_page_ids ) { 607 bp_update_option( 'bp-pages', $blog_page_ids ); 608 } 609 610 /** 611 * Get names and slugs for BuddyPress component directory pages. 612 * 613 * @since 1.5.0 614 * 615 * @return object Page names, IDs, and slugs. 616 */ 617 function bp_core_get_directory_pages() { 618 global $wpdb; 619 620 // Look in cache first. 621 $pages = wp_cache_get( 'directory_pages', 'bp_pages' ); 622 623 if ( false === $pages ) { 624 625 // Set pages as standard class. 626 $pages = new stdClass; 627 628 // Get pages and IDs. 629 $page_ids = bp_core_get_directory_page_ids(); 630 if ( !empty( $page_ids ) ) { 631 632 // Always get page data from the root blog, except on multiblog mode, when it comes 633 // from the current blog. 634 $posts_table_name = bp_is_multiblog_mode() ? $wpdb->posts : $wpdb->get_blog_prefix( bp_get_root_blog_id() ) . 'posts'; 635 $page_ids_sql = implode( ',', wp_parse_id_list( $page_ids ) ); 636 $page_names = $wpdb->get_results( "SELECT ID, post_name, post_parent, post_title FROM {$posts_table_name} WHERE ID IN ({$page_ids_sql}) AND post_status = 'publish' " ); 637 638 foreach ( (array) $page_ids as $component_id => $page_id ) { 639 foreach ( (array) $page_names as $page_name ) { 640 if ( $page_name->ID == $page_id ) { 641 if ( !isset( $pages->{$component_id} ) || !is_object( $pages->{$component_id} ) ) { 642 $pages->{$component_id} = new stdClass; 643 } 644 645 $pages->{$component_id}->name = $page_name->post_name; 646 $pages->{$component_id}->id = $page_name->ID; 647 $pages->{$component_id}->title = $page_name->post_title; 648 $slug[] = $page_name->post_name; 649 650 // Get the slug. 651 while ( $page_name->post_parent != 0 ) { 652 $parent = $wpdb->get_results( $wpdb->prepare( "SELECT post_name, post_parent FROM {$posts_table_name} WHERE ID = %d", $page_name->post_parent ) ); 653 $slug[] = $parent[0]->post_name; 654 $page_name->post_parent = $parent[0]->post_parent; 655 } 656 657 $pages->{$component_id}->slug = implode( '/', array_reverse( (array) $slug ) ); 658 } 659 660 unset( $slug ); 661 } 662 } 663 } 664 665 wp_cache_set( 'directory_pages', $pages, 'bp_pages' ); 666 } 667 668 /** 669 * Filters the names and slugs for BuddyPress component directory pages. 670 * 671 * @since 1.5.0 672 * 673 * @param object $pages Object holding page names and slugs. 674 */ 675 return apply_filters( 'bp_core_get_directory_pages', $pages ); 676 } 677 678 /** 679 * Creates necessary directory pages. 680 * 681 * Directory pages are those WordPress pages used by BP components to display 682 * content (eg, the 'groups' page created by BP). 683 * 684 * @since 1.7.0 685 * 686 * @param array $components Components to create pages for. 687 * @param string $existing 'delete' if you want to delete existing page mappings 688 * and replace with new ones. Otherwise existing page mappings 689 * are kept, and the gaps filled in with new pages. Default: 'keep'. 690 */ 691 function bp_core_add_page_mappings( $components, $existing = 'keep' ) { 692 693 // If no value is passed, there's nothing to do. 694 if ( empty( $components ) ) { 695 return; 696 } 697 698 // Make sure that the pages are created on the root blog no matter which 699 // dashboard the setup is being run on. 700 if ( ! bp_is_root_blog() ) { 701 switch_to_blog( bp_get_root_blog_id() ); 702 } 703 704 $pages = bp_core_get_directory_page_ids( 'all' ); 705 706 // Delete any existing pages. 707 if ( 'delete' === $existing ) { 708 foreach ( $pages as $page_id ) { 709 wp_delete_post( $page_id, true ); 710 } 711 712 $pages = array(); 713 } 714 715 $page_titles = bp_core_get_directory_page_default_titles(); 716 717 $pages_to_create = array(); 718 foreach ( array_keys( $components ) as $component_name ) { 719 if ( ! isset( $pages[ $component_name ] ) && isset( $page_titles[ $component_name ] ) ) { 720 $pages_to_create[ $component_name ] = $page_titles[ $component_name ]; 721 } 722 } 723 724 // Register and Activate are not components, but need pages when 725 // registration is enabled. 726 if ( bp_allow_access_to_registration_pages() ) { 727 foreach ( array( 'register', 'activate' ) as $slug ) { 728 if ( ! isset( $pages[ $slug ] ) ) { 729 $pages_to_create[ $slug ] = $page_titles[ $slug ]; 730 } 731 } 732 } 733 734 // No need for a Sites directory unless we're on multisite. 735 if ( ! is_multisite() && isset( $pages_to_create['blogs'] ) ) { 736 unset( $pages_to_create['blogs'] ); 737 } 738 739 // Members must always have a page, no matter what. 740 if ( ! isset( $pages['members'] ) && ! isset( $pages_to_create['members'] ) ) { 741 $pages_to_create['members'] = $page_titles['members']; 742 } 743 744 // Create the pages. 745 foreach ( $pages_to_create as $component_name => $page_name ) { 746 $exists = get_page_by_path( $component_name ); 747 748 // If page already exists, use it. 749 if ( ! empty( $exists ) ) { 750 $pages[ $component_name ] = $exists->ID; 751 } else { 752 $pages[ $component_name ] = wp_insert_post( array( 753 'comment_status' => 'closed', 754 'ping_status' => 'closed', 755 'post_status' => 'publish', 756 'post_title' => $page_name, 757 'post_type' => 'page', 758 ) ); 759 } 760 } 761 762 // Save the page mapping. 763 bp_update_option( 'bp-pages', $pages ); 764 765 // If we had to switch_to_blog, go back to the original site. 766 if ( ! bp_is_root_blog() ) { 767 restore_current_blog(); 768 } 769 } 770 771 /** 772 * Get the default page titles for BP directory pages. 773 * 774 * @since 2.7.0 775 * 776 * @return array 777 */ 778 function bp_core_get_directory_page_default_titles() { 779 $page_default_titles = array( 780 'activity' => _x( 'Activity', 'Page title for the Activity directory.', 'buddypress' ), 781 'groups' => _x( 'Groups', 'Page title for the Groups directory.', 'buddypress' ), 782 'blogs' => _x( 'Sites', 'Page title for the Sites directory.', 'buddypress' ), 783 'members' => _x( 'Members', 'Page title for the Members directory.', 'buddypress' ), 784 'activate' => _x( 'Activate', 'Page title for the user activation screen.', 'buddypress' ), 785 'register' => _x( 'Register', 'Page title for the user registration screen.', 'buddypress' ), 786 ); 787 788 /** 789 * Filters the default page titles array 790 * 791 * @since 2.7.0 792 * 793 * @param array $page_default_titles the array of default WP (post_title) titles. 794 */ 795 return apply_filters( 'bp_core_get_directory_page_default_titles', $page_default_titles ); 796 } 797 798 /** 799 * Remove the entry from bp_pages when the corresponding WP page is deleted. 800 * 801 * Bails early on multisite installations when not viewing the root site. 802 * 803 * @link https://buddypress.trac.wordpress.org/ticket/6226 804 * 805 * @since 2.2.0 806 * 807 * @param int $post_id Post ID. 808 */ 809 function bp_core_on_directory_page_delete( $post_id ) { 810 811 // Stop if we are not on the main BP root blog. 812 if ( ! bp_is_root_blog() ) { 813 return; 814 } 815 816 $page_ids = bp_core_get_directory_page_ids( 'all' ); 817 $component_name = array_search( $post_id, $page_ids ); 818 819 if ( ! empty( $component_name ) ) { 820 unset( $page_ids[ $component_name ] ); 821 } 822 823 bp_core_update_directory_page_ids( $page_ids ); 824 } 825 add_action( 'delete_post', 'bp_core_on_directory_page_delete' ); 826 827 /** 828 * Create a default component slug from a WP page root_slug. 829 * 830 * Since 1.5, BP components get their root_slug (the slug used immediately 831 * following the root domain) from the slug of a corresponding WP page. 832 * 833 * E.g. if your BP installation at example.com has its members page at 834 * example.com/community/people, $bp->members->root_slug will be 835 * 'community/people'. 836 * 837 * By default, this function creates a shorter version of the root_slug for 838 * use elsewhere in the URL, by returning the content after the final '/' 839 * in the root_slug ('people' in the example above). 840 * 841 * Filter on 'bp_core_component_slug_from_root_slug' to override this method 842 * in general, or define a specific component slug constant (e.g. 843 * BP_MEMBERS_SLUG) to override specific component slugs. 844 * 845 * @since 1.5.0 846 * 847 * @param string $root_slug The root slug, which comes from $bp->pages->[component]->slug. 848 * @return string The short slug for use in the middle of URLs. 849 */ 850 function bp_core_component_slug_from_root_slug( $root_slug ) { 851 $slug_chunks = explode( '/', $root_slug ); 852 $slug = array_pop( $slug_chunks ); 853 854 /** 855 * Filters the default component slug from a WP page root_slug. 856 * 857 * @since 1.5.0 858 * 859 * @param string $slug Short slug for use in the middle of URLs. 860 * @param string $root_slug The root slug which comes from $bp->pages-[component]->slug. 861 */ 862 return apply_filters( 'bp_core_component_slug_from_root_slug', $slug, $root_slug ); 863 } 864 865 /** 866 * Add support for a top-level ("root") component. 867 * 868 * This function originally (pre-1.5) let plugins add support for pages in the 869 * root of the install. These root level pages are now handled by actual 870 * WordPress pages and this function is now a convenience for compatibility 871 * with the new method. 872 * 873 * @since 1.0.0 874 * 875 * @param string $slug The slug of the component being added to the root list. 876 */ 877 function bp_core_add_root_component( $slug ) { 878 $bp = buddypress(); 879 880 if ( empty( $bp->pages ) ) { 881 $bp->pages = bp_core_get_directory_pages(); 882 } 883 884 $match = false; 885 886 // Check if the slug is registered in the $bp->pages global. 887 foreach ( (array) $bp->pages as $key => $page ) { 888 if ( $key == $slug || $page->slug == $slug ) { 889 $match = true; 890 } 891 } 892 893 // Maybe create the add_root array. 894 if ( empty( $bp->add_root ) ) { 895 $bp->add_root = array(); 896 } 897 898 // If there was no match, add a page for this root component. 899 if ( empty( $match ) ) { 900 $add_root_items = $bp->add_root; 901 $add_root_items[] = $slug; 902 $bp->add_root = $add_root_items; 903 } 904 905 // Make sure that this component is registered as requiring a top-level directory. 906 if ( isset( $bp->{$slug} ) ) { 907 $bp->loaded_components[$bp->{$slug}->slug] = $bp->{$slug}->id; 908 $bp->{$slug}->has_directory = true; 909 } 910 } 911 912 /** 913 * Create WordPress pages to be used as BP component directories. 914 * 915 * @since 1.5.0 916 */ 917 function bp_core_create_root_component_page() { 918 919 // Get BuddyPress. 920 $bp = buddypress(); 921 922 $new_page_ids = array(); 923 924 foreach ( (array) $bp->add_root as $slug ) { 925 $new_page_ids[ $slug ] = wp_insert_post( array( 926 'comment_status' => 'closed', 927 'ping_status' => 'closed', 928 'post_title' => ucwords( $slug ), 929 'post_status' => 'publish', 930 'post_type' => 'page' 931 ) ); 932 } 933 934 $page_ids = array_merge( $new_page_ids, bp_core_get_directory_page_ids( 'all' ) ); 935 bp_core_update_directory_page_ids( $page_ids ); 936 } 937 938 /** 939 * Get the 'search' query argument for a given component. 940 * 941 * @since 2.4.0 942 * @since 2.7.0 The `$component` parameter was made optional, with the current component 943 * as the fallback value. 944 * 945 * @param string|null $component Optional. Component name. Defaults to current component. 946 * @return string|bool Query argument on success. False on failure. 947 */ 948 function bp_core_get_component_search_query_arg( $component = null ) { 949 if ( ! $component ) { 950 $component = bp_current_component(); 951 } 952 953 $query_arg = false; 954 if ( isset( buddypress()->{$component}->search_query_arg ) ) { 955 $query_arg = sanitize_title( buddypress()->{$component}->search_query_arg ); 956 } 957 958 /** 959 * Filters the query arg for a component search string. 960 * 961 * @since 2.4.0 962 * 963 * @param string $query_arg Query argument. 964 * @param string $component Component name. 965 */ 966 return apply_filters( 'bp_core_get_component_search_query_arg', $query_arg, $component ); 967 } 968 969 /** 970 * Get a list of all active component objects. 971 * 972 * @since 8.0.0 973 * 974 * @param array $args { 975 * Optional. An array of key => value arguments to match against the component objects. 976 * Default empty array. 977 * 978 * @type string $name Translatable name for the component. 979 * @type string $id Unique ID for the component. 980 * @type string $slug Unique slug for the component, for use in query strings and URLs. 981 * @type bool $has_directory True if the component has a top-level directory. False otherwise. 982 * @type string $root_slug Slug used by the component's directory page. 983 * } 984 * @param string $output Optional. The type of output to return. Accepts 'ids' 985 * or 'objects'. Default 'ids'. 986 * @param string $operator Optional. The logical operation to perform. 'or' means only one 987 * element from the array needs to match; 'and' means all elements 988 * must match. Accepts 'or' or 'and'. Default 'and'. 989 * @return array A list of component ids or objects. 990 */ 991 function bp_core_get_active_components( $args = array(), $output = 'ids', $operator = 'and' ) { 992 $bp = buddypress(); 993 994 $active_components = array_keys( $bp->active_components ); 995 996 $xprofile_id = array_search( 'xprofile', $active_components, true ); 997 if ( false !== $xprofile_id ) { 998 $active_components[ $xprofile_id ] = 'profile'; 999 } 1000 1001 $components = array(); 1002 foreach ( $active_components as $id ) { 1003 if ( isset( $bp->{$id} ) && $bp->{$id} instanceof BP_Component ) { 1004 $components[ $id ] = $bp->{$id}; 1005 } 1006 } 1007 1008 $components = wp_filter_object_list( $components, $args, $operator ); 1009 1010 if ( 'ids' === $output ) { 1011 $components = wp_list_pluck( $components, 'id' ); 1012 } 1013 1014 return $components; 1015 } 1016 1017 /** 1018 * Determine whether BuddyPress should register the bp-themes directory. 1019 * 1020 * @since 1.9.0 1021 * 1022 * @return bool True if bp-themes should be registered, false otherwise. 1023 */ 1024 function bp_do_register_theme_directory() { 1025 // If bp-default exists in another theme directory, bail. 1026 // This ensures that the version of bp-default in the regular themes 1027 // directory will always take precedence, as part of a migration away 1028 // from the version packaged with BuddyPress. 1029 foreach ( array_values( (array) $GLOBALS['wp_theme_directories'] ) as $directory ) { 1030 if ( is_dir( $directory . '/bp-default' ) ) { 1031 return false; 1032 } 1033 } 1034 1035 // If the current theme is bp-default (or a bp-default child), BP 1036 // should register its directory. 1037 $register = 'bp-default' === get_stylesheet() || 'bp-default' === get_template(); 1038 1039 // Legacy sites continue to have the theme registered. 1040 if ( empty( $register ) && ( 1 == get_site_option( '_bp_retain_bp_default' ) ) ) { 1041 $register = true; 1042 } 1043 1044 /** 1045 * Filters whether BuddyPress should register the bp-themes directory. 1046 * 1047 * @since 1.9.0 1048 * 1049 * @param bool $register If bp-themes should be registered. 1050 */ 1051 return apply_filters( 'bp_do_register_theme_directory', $register ); 1052 } 1053 1054 /** URI ***********************************************************************/ 1055 1056 /** 1057 * Return the domain for the root blog. 1058 * 1059 * Eg: http://example.com OR https://example.com 1060 * 1061 * @since 1.0.0 1062 * 1063 * @return string The domain URL for the blog. 1064 */ 1065 function bp_core_get_root_domain() { 1066 1067 $domain = get_home_url( bp_get_root_blog_id() ); 1068 1069 /** 1070 * Filters the domain for the root blog. 1071 * 1072 * @since 1.0.1 1073 * 1074 * @param string $domain The domain URL for the blog. 1075 */ 1076 return apply_filters( 'bp_core_get_root_domain', $domain ); 1077 } 1078 1079 /** 1080 * Perform a status-safe wp_redirect() that is compatible with BP's URI parser. 1081 * 1082 * @since 1.0.0 1083 * 1084 * @param string $location The redirect URL. 1085 * @param int $status Optional. The numeric code to give in the redirect 1086 * headers. Default: 302. 1087 */ 1088 function bp_core_redirect( $location = '', $status = 302 ) { 1089 1090 // On some setups, passing the value of wp_get_referer() may result in an 1091 // empty value for $location, which results in an error. Ensure that we 1092 // have a valid URL. 1093 if ( empty( $location ) ) { 1094 $location = bp_get_root_domain(); 1095 } 1096 1097 // Make sure we don't call status_header() in bp_core_do_catch_uri() as this 1098 // conflicts with wp_redirect() and wp_safe_redirect(). 1099 buddypress()->no_status_set = true; 1100 1101 wp_safe_redirect( $location, $status ); 1102 1103 // If PHPUnit is running, do not kill execution. 1104 if ( ! defined( 'BP_TESTS_DIR' ) ) { 1105 die; 1106 } 1107 } 1108 1109 /** 1110 * Return the URL path of the referring page. 1111 * 1112 * This is a wrapper for `wp_get_referer()` that sanitizes the referer URL to 1113 * a webroot-relative path. For example, 'http://example.com/foo/' will be 1114 * reduced to '/foo/'. 1115 * 1116 * @since 2.3.0 1117 * 1118 * @return bool|string Returns false on error, a URL path on success. 1119 */ 1120 function bp_get_referer_path() { 1121 $referer = wp_get_referer(); 1122 1123 if ( false === $referer ) { 1124 return false; 1125 } 1126 1127 // Turn into an absolute path. 1128 $referer = preg_replace( '|https?\://[^/]+/|', '/', $referer ); 1129 1130 return $referer; 1131 } 1132 1133 /** 1134 * Get the path of the current site. 1135 * 1136 * @since 1.0.0 1137 * 1138 * @global object $current_site 1139 * 1140 * @return string URL to the current site. 1141 */ 1142 function bp_core_get_site_path() { 1143 global $current_site; 1144 1145 if ( is_multisite() ) { 1146 $site_path = $current_site->path; 1147 } else { 1148 $site_path = (array) explode( '/', home_url() ); 1149 1150 if ( count( $site_path ) < 2 ) { 1151 $site_path = '/'; 1152 } else { 1153 // Unset the first three segments (http(s)://example.com part). 1154 unset( $site_path[0] ); 1155 unset( $site_path[1] ); 1156 unset( $site_path[2] ); 1157 1158 if ( !count( $site_path ) ) { 1159 $site_path = '/'; 1160 } else { 1161 $site_path = '/' . implode( '/', $site_path ) . '/'; 1162 } 1163 } 1164 } 1165 1166 /** 1167 * Filters the path of the current site. 1168 * 1169 * @since 1.2.0 1170 * 1171 * @param string $site_path URL to the current site. 1172 */ 1173 return apply_filters( 'bp_core_get_site_path', $site_path ); 1174 } 1175 1176 /** Time **********************************************************************/ 1177 1178 /** 1179 * Get the current GMT time to save into the DB. 1180 * 1181 * @since 1.2.6 1182 * 1183 * @param bool $gmt True to use GMT (rather than local) time. Default: true. 1184 * @param string $type See the 'type' parameter in {@link current_time()}. 1185 * Default: 'mysql'. 1186 * @return string Current time in 'Y-m-d h:i:s' format. 1187 */ 1188 function bp_core_current_time( $gmt = true, $type = 'mysql' ) { 1189 1190 /** 1191 * Filters the current GMT time to save into the DB. 1192 * 1193 * @since 1.2.6 1194 * 1195 * @param string $value Current GMT time. 1196 */ 1197 return apply_filters( 'bp_core_current_time', current_time( $type, $gmt ) ); 1198 } 1199 1200 /** 1201 * Calculate the human time difference between two dates. 1202 * 1203 * Based on function created by Dunstan Orchard - http://1976design.com 1204 * 1205 * @since 8.0.0 1206 * 1207 * @param array $args { 1208 * An array of arguments. All arguments are technically optional. 1209 * 1210 * @type int|string $older_date An integer Unix timestamp or a date string of the format 'Y-m-d h:i:s'. 1211 * @type int|string $newer_date An integer Unix timestamp or a date string of the format 'Y-m-d h:i:s'. 1212 * @type int $time_chunks The number of time chunks to get (1 or 2). 1213 * } 1214 * @return null|array|false Null if there's no time diff. An array containing 1 or 2 chunks 1215 * of human time. False if travelling into the future. 1216 */ 1217 function bp_core_time_diff( $args = array() ) { 1218 $retval = null; 1219 $r = bp_parse_args( 1220 $args, 1221 array( 1222 'older_date' => 0, 1223 'newer_date' => bp_core_current_time( true, 'timestamp' ), 1224 'time_chunks' => 2, 1225 ) 1226 ); 1227 1228 // Array of time period chunks. 1229 $chunks = array( 1230 YEAR_IN_SECONDS, 1231 30 * DAY_IN_SECONDS, 1232 WEEK_IN_SECONDS, 1233 DAY_IN_SECONDS, 1234 HOUR_IN_SECONDS, 1235 MINUTE_IN_SECONDS, 1236 1 1237 ); 1238 1239 foreach ( array( 'older_date', 'newer_date' ) as $date ) { 1240 if ( ! $r[ $date ] ) { 1241 continue; 1242 } 1243 1244 if ( ! is_numeric( $r[ $date ] ) ) { 1245 $time_chunks = explode( ':', str_replace( ' ', ':', $r[ $date ] ) ); 1246 $date_chunks = explode( '-', str_replace( ' ', '-', $r[ $date ] ) ); 1247 $r[ $date ] = gmmktime( 1248 (int) $time_chunks[1], 1249 (int) $time_chunks[2], 1250 (int) $time_chunks[3], 1251 (int) $date_chunks[1], 1252 (int) $date_chunks[2], 1253 (int) $date_chunks[0] 1254 ); 1255 } 1256 } 1257 1258 // Difference in seconds. 1259 $diff = $r['newer_date'] - $r['older_date']; 1260 1261 /** 1262 * We only want to return one or two chunks of time here, eg: 1263 * - `array( 'x years', 'xx months' )`, 1264 * - `array( 'x days', 'xx hours' )`. 1265 * So there's only two bits of calculation below. 1266 */ 1267 if ( 0 <= $diff && (int) $r['time_chunks'] ) { 1268 // Step one: the first chunk. 1269 for ( $i = 0, $j = count( $chunks ); $i < $j; ++$i ) { 1270 $seconds = $chunks[$i]; 1271 1272 // Finding the biggest chunk (if the chunk fits, break). 1273 $count = floor( $diff / $seconds ); 1274 if ( 0 != $count ) { 1275 break; 1276 } 1277 } 1278 1279 // Add the first chunk of time diff. 1280 if ( isset( $chunks[ $i ] ) ) { 1281 $retval = array(); 1282 1283 switch ( $seconds ) { 1284 case YEAR_IN_SECONDS : 1285 /* translators: %s: the number of years. */ 1286 $retval[] = sprintf( _n( '%s year', '%s years', $count, 'buddypress' ), $count ); 1287 break; 1288 case 30 * DAY_IN_SECONDS : 1289 /* translators: %s: the number of months. */ 1290 $retval[] = sprintf( _n( '%s month', '%s months', $count, 'buddypress' ), $count ); 1291 break; 1292 case WEEK_IN_SECONDS : 1293 /* translators: %s: the number of weeks. */ 1294 $retval[]= sprintf( _n( '%s week', '%s weeks', $count, 'buddypress' ), $count ); 1295 break; 1296 case DAY_IN_SECONDS : 1297 /* translators: %s: the number of days. */ 1298 $retval[] = sprintf( _n( '%s day', '%s days', $count, 'buddypress' ), $count ); 1299 break; 1300 case HOUR_IN_SECONDS : 1301 /* translators: %s: the number of hours. */ 1302 $retval[] = sprintf( _n( '%s hour', '%s hours', $count, 'buddypress' ), $count ); 1303 break; 1304 case MINUTE_IN_SECONDS : 1305 /* translators: %s: the number of minutes. */ 1306 $retval[] = sprintf( _n( '%s minute', '%s minutes', $count, 'buddypress' ), $count ); 1307 break; 1308 default: 1309 /* translators: %s: the number of seconds. */ 1310 $retval[] = sprintf( _n( '%s second', '%s seconds', $count, 'buddypress' ), $count ); 1311 } 1312 1313 /** 1314 * Step two: the second chunk. 1315 * 1316 * A quirk in the implementation means that this condition fails in the case of minutes and seconds. 1317 * We've left the quirk in place, since fractions of a minute are not a useful piece of information 1318 * for our purposes. 1319 */ 1320 if ( 2 === (int) $r['time_chunks'] && $i + 2 < $j ) { 1321 $seconds2 = $chunks[$i + 1]; 1322 $count2 = floor( ( $diff - ( $seconds * $count ) ) / $seconds2 ); 1323 1324 // Add the second chunk of time diff. 1325 if ( 0 !== (int) $count2 ) { 1326 1327 switch ( $seconds2 ) { 1328 case 30 * DAY_IN_SECONDS : 1329 /* translators: %s: the number of months. */ 1330 $retval[] = sprintf( _n( '%s month', '%s months', $count2, 'buddypress' ), $count2 ); 1331 break; 1332 case WEEK_IN_SECONDS : 1333 /* translators: %s: the number of weeks. */ 1334 $retval[] = sprintf( _n( '%s week', '%s weeks', $count2, 'buddypress' ), $count2 ); 1335 break; 1336 case DAY_IN_SECONDS : 1337 /* translators: %s: the number of days. */ 1338 $retval[] = sprintf( _n( '%s day', '%s days', $count2, 'buddypress' ), $count2 ); 1339 break; 1340 case HOUR_IN_SECONDS : 1341 /* translators: %s: the number of hours. */ 1342 $retval[] = sprintf( _n( '%s hour', '%s hours', $count2, 'buddypress' ), $count2 ); 1343 break; 1344 case MINUTE_IN_SECONDS : 1345 /* translators: %s: the number of minutes. */ 1346 $retval[] = sprintf( _n( '%s minute', '%s minutes', $count2, 'buddypress' ), $count2 ); 1347 break; 1348 default: 1349 /* translators: %s: the number of seconds. */ 1350 $retval[] = sprintf( _n( '%s second', '%s seconds', $count2, 'buddypress' ), $count2 ); 1351 } 1352 } 1353 } 1354 } 1355 } else { 1356 // Something went wrong with date calculation and we ended up with a negative date. 1357 $retval = false; 1358 } 1359 1360 return $retval; 1361 } 1362 1363 /** 1364 * Get an English-language representation of the time elapsed since a given date. 1365 * 1366 * This function will return an English representation of the time elapsed 1367 * since a given date. 1368 * eg: 2 hours, 50 minutes 1369 * eg: 4 days 1370 * eg: 4 weeks, 6 days 1371 * 1372 * Note that fractions of minutes are not represented in the return string. So 1373 * an interval of 3 minutes will be represented by "3 minutes ago", as will an 1374 * interval of 3 minutes 59 seconds. 1375 * 1376 * @since 1.0.0 1377 * @since 8.0.0 Move the time difference calculation into `bp_core_time_diff()`. 1378 * 1379 * @param int|string $older_date The earlier time from which you're calculating 1380 * the time elapsed. Enter either as an integer Unix timestamp, 1381 * or as a date string of the format 'Y-m-d h:i:s'. 1382 * @param int|bool $newer_date Optional. Unix timestamp of date to compare older 1383 * date to. Default: false (current time). 1384 * @return string String representing the time since the older date, eg 1385 * "2 hours, 50 minutes". 1386 */ 1387 function bp_core_time_since( $older_date, $newer_date = false ) { 1388 1389 /** 1390 * Filters whether or not to bypass BuddyPress' time_since calculations. 1391 * 1392 * @since 1.7.0 1393 * 1394 * @param bool $value Whether or not to bypass. 1395 * @param string $older_date Earlier time from which we're calculating time elapsed. 1396 * @param string $newer_date Unix timestamp of date to compare older time to. 1397 */ 1398 $pre_value = apply_filters( 'bp_core_time_since_pre', false, $older_date, $newer_date ); 1399 if ( false !== $pre_value ) { 1400 return $pre_value; 1401 } 1402 1403 $newer_date = (int) $newer_date; 1404 $args = array( 1405 'older_date' => $older_date, 1406 ); 1407 1408 if ( $newer_date) { 1409 $args['newer_date'] = $newer_date; 1410 } 1411 1412 // Calculate the time difference. 1413 $time_diff = bp_core_time_diff( $args ); 1414 1415 /** 1416 * Filters the value to use if the time since is some time ago. 1417 * 1418 * @since 1.5.0 1419 * 1420 * @param string $value String representing the time since the older date. 1421 */ 1422 $ago_text = apply_filters( 1423 'bp_core_time_since_ago_text', 1424 /* translators: %s: the human time diff. */ 1425 __( '%s ago', 'buddypress' ) 1426 ); 1427 1428 /** 1429 * Filters the value to use if the time since is right now. 1430 * 1431 * @since 1.5.0 1432 * 1433 * @param string $value String representing the time since the older date. 1434 */ 1435 $output = apply_filters( 'bp_core_time_since_right_now_text', __( 'right now', 'buddypress' ) ); 1436 1437 if ( is_array( $time_diff ) ) { 1438 $separator = _x( ',', 'Separator in time since', 'buddypress' ) . ' '; 1439 $diff_text = implode( $separator, $time_diff ); 1440 $output = sprintf( $ago_text, $diff_text ); 1441 } elseif ( false === $time_diff ) { 1442 /** 1443 * Filters the value to use if the time since is unknown. 1444 * 1445 * @since 1.5.0 1446 * 1447 * @param string $value String representing the time since the older date. 1448 */ 1449 $unknown_text = apply_filters( 'bp_core_time_since_unknown_text', __( 'sometime', 'buddypress' ) ); 1450 $output = sprintf( $ago_text, $unknown_text ); 1451 } 1452 1453 /** 1454 * Filters the English-language representation of the time elapsed since a given date. 1455 * 1456 * @since 1.7.0 1457 * 1458 * @param string $output Final 'time since' string. 1459 * @param string $older_date Earlier time from which we're calculating time elapsed. 1460 * @param string $newer_date Unix timestamp of date to compare older time to. 1461 */ 1462 return apply_filters( 'bp_core_time_since', $output, $older_date, $newer_date ); 1463 } 1464 1465 /** 1466 * Get an age to display according to the birth date. 1467 * 1468 * @since 8.0.0 1469 * 1470 * @param int|string $birth_date A timestamp or a MySQL formatted date. 1471 * @return string The age to display. 1472 */ 1473 function bp_core_time_old( $birth_date ) { 1474 $time_diff = bp_core_time_diff( array( 'older_date' => $birth_date, 'time_chunks' => 1 ) ); 1475 $retval = '—'; 1476 1477 if ( $time_diff ) { 1478 $age = reset( $time_diff ); 1479 1480 /** 1481 * Filters the value to use to display the age. 1482 * 1483 * @since 8.0.0 1484 * 1485 * @param string $value String representing the time since the older date. 1486 * @param int $age The age. 1487 */ 1488 $age_text = apply_filters( 1489 'bp_core_time_old_text', 1490 /* translators: %d: the age . */ 1491 __( '%s old', 'buddypress' ), 1492 $age 1493 ); 1494 1495 $retval = sprintf( $age_text, $age ); 1496 } 1497 1498 return $retval; 1499 } 1500 1501 /** 1502 * Output an ISO-8601 date from a date string. 1503 * 1504 * @since 2.7.0 1505 * 1506 * @param string String of date to convert. Timezone should be UTC before using this. 1507 * @return string|null 1508 */ 1509 function bp_core_iso8601_date( $timestamp = '' ) { 1510 echo bp_core_get_iso8601_date( $timestamp ); 1511 } 1512 /** 1513 * Return an ISO-8601 date from a date string. 1514 * 1515 * @since 2.7.0 1516 * 1517 * @param string String of date to convert. Timezone should be UTC before using this. 1518 * @return string 1519 */ 1520 function bp_core_get_iso8601_date( $timestamp = '' ) { 1521 if ( ! $timestamp ) { 1522 return ''; 1523 } 1524 1525 try { 1526 $date = new DateTime( $timestamp, new DateTimeZone( 'UTC' ) ); 1527 1528 // Not a valid date, so return blank string. 1529 } catch( Exception $e ) { 1530 return ''; 1531 } 1532 1533 return $date->format( DateTime::ISO8601 ); 1534 } 1535 1536 /** Messages ******************************************************************/ 1537 1538 /** 1539 * Add a feedback (error/success) message to the WP cookie so it can be displayed after the page reloads. 1540 * 1541 * @since 1.0.0 1542 * 1543 * @param string $message Feedback message to be displayed. 1544 * @param string $type Message type. 'updated', 'success', 'error', 'warning'. 1545 * Default: 'success'. 1546 */ 1547 function bp_core_add_message( $message, $type = '' ) { 1548 1549 // Success is the default. 1550 if ( empty( $type ) ) { 1551 $type = 'success'; 1552 } 1553 1554 // Send the values to the cookie for page reload display. 1555 @setcookie( 'bp-message', $message, time() + 60 * 60 * 24, COOKIEPATH, COOKIE_DOMAIN, is_ssl() ); 1556 @setcookie( 'bp-message-type', $type, time() + 60 * 60 * 24, COOKIEPATH, COOKIE_DOMAIN, is_ssl() ); 1557 1558 // Get BuddyPress. 1559 $bp = buddypress(); 1560 1561 /** 1562 * Send the values to the $bp global so we can still output messages 1563 * without a page reload 1564 */ 1565 $bp->template_message = $message; 1566 $bp->template_message_type = $type; 1567 } 1568 1569 /** 1570 * Set up the display of the 'template_notices' feedback message. 1571 * 1572 * Checks whether there is a feedback message in the WP cookie and, if so, adds 1573 * a "template_notices" action so that the message can be parsed into the 1574 * template and displayed to the user. 1575 * 1576 * After the message is displayed, it removes the message vars from the cookie 1577 * so that the message is not shown to the user multiple times. 1578 * 1579 * @since 1.1.0 1580 */ 1581 function bp_core_setup_message() { 1582 1583 // Get BuddyPress. 1584 $bp = buddypress(); 1585 1586 if ( empty( $bp->template_message ) && isset( $_COOKIE['bp-message'] ) ) { 1587 $bp->template_message = stripslashes( $_COOKIE['bp-message'] ); 1588 } 1589 1590 if ( empty( $bp->template_message_type ) && isset( $_COOKIE['bp-message-type'] ) ) { 1591 $bp->template_message_type = stripslashes( $_COOKIE['bp-message-type'] ); 1592 } 1593 1594 add_action( 'template_notices', 'bp_core_render_message' ); 1595 1596 if ( isset( $_COOKIE['bp-message'] ) ) { 1597 @setcookie( 'bp-message', false, time() - 1000, COOKIEPATH, COOKIE_DOMAIN, is_ssl() ); 1598 } 1599 1600 if ( isset( $_COOKIE['bp-message-type'] ) ) { 1601 @setcookie( 'bp-message-type', false, time() - 1000, COOKIEPATH, COOKIE_DOMAIN, is_ssl() ); 1602 } 1603 } 1604 add_action( 'bp_actions', 'bp_core_setup_message', 5 ); 1605 1606 /** 1607 * Render the 'template_notices' feedback message. 1608 * 1609 * The hook action 'template_notices' is used to call this function, it is not 1610 * called directly. 1611 * 1612 * @since 1.1.0 1613 */ 1614 function bp_core_render_message() { 1615 1616 // Get BuddyPress. 1617 $bp = buddypress(); 1618 1619 if ( !empty( $bp->template_message ) ) : 1620 $type = ( 'success' === $bp->template_message_type ) ? 'updated' : 'error'; 1621 1622 /** 1623 * Filters the 'template_notices' feedback message content. 1624 * 1625 * @since 1.5.5 1626 * 1627 * @param string $template_message Feedback message content. 1628 * @param string $type The type of message being displayed. 1629 * Either 'updated' or 'error'. 1630 */ 1631 $content = apply_filters( 'bp_core_render_message_content', $bp->template_message, $type ); ?> 1632 1633 <div id="message" class="bp-template-notice <?php echo esc_attr( $type ); ?>"> 1634 1635 <?php echo $content; ?> 1636 1637 </div> 1638 1639 <?php 1640 1641 /** 1642 * Fires after the display of any template_notices feedback messages. 1643 * 1644 * @since 1.1.0 1645 */ 1646 do_action( 'bp_core_render_message' ); 1647 1648 endif; 1649 } 1650 1651 /** Last active ***************************************************************/ 1652 1653 /** 1654 * Listener function for the logged-in user's 'last_activity' metadata. 1655 * 1656 * Many functions use a "last active" feature to show the length of time since 1657 * the user was last active. This function will update that time as a usermeta 1658 * setting for the user every 5 minutes while the user is actively browsing the 1659 * site. 1660 * 1661 * @since 1.0.0 1662 * 1663 * @return false|null Returns false if there is nothing to do. 1664 */ 1665 function bp_core_record_activity() { 1666 1667 // Bail if user is not logged in. 1668 if ( ! is_user_logged_in() ) { 1669 return false; 1670 } 1671 1672 // Get the user ID. 1673 $user_id = bp_loggedin_user_id(); 1674 1675 // Bail if user is not active. 1676 if ( bp_is_user_inactive( $user_id ) ) { 1677 return false; 1678 } 1679 1680 // Get the user's last activity. 1681 $activity = bp_get_user_last_activity( $user_id ); 1682 1683 // Make sure it's numeric. 1684 if ( ! is_numeric( $activity ) ) { 1685 $activity = strtotime( $activity ); 1686 } 1687 1688 // Get current time. 1689 $current_time = bp_core_current_time( true, 'timestamp' ); 1690 1691 // Use this action to detect the very first activity for a given member. 1692 if ( empty( $activity ) ) { 1693 1694 /** 1695 * Fires inside the recording of an activity item. 1696 * 1697 * Use this action to detect the very first activity for a given member. 1698 * 1699 * @since 1.6.0 1700 * 1701 * @param int $user_id ID of the user whose activity is recorded. 1702 */ 1703 do_action( 'bp_first_activity_for_member', $user_id ); 1704 } 1705 1706 // If it's been more than 5 minutes, record a newer last-activity time. 1707 if ( empty( $activity ) || ( $current_time >= strtotime( '+5 minutes', $activity ) ) ) { 1708 bp_update_user_last_activity( $user_id, date( 'Y-m-d H:i:s', $current_time ) ); 1709 } 1710 } 1711 add_action( 'wp_head', 'bp_core_record_activity' ); 1712 1713 /** 1714 * Format last activity string based on time since date given. 1715 * 1716 * @since 1.0.0 1717 * 1718 * @param int|string $last_activity_date The date of last activity. 1719 * @param string $string A sprintf()-able statement of the form 'Active %s'. 1720 * @return string $last_active A string of the form '3 years ago'. 1721 */ 1722 function bp_core_get_last_activity( $last_activity_date = '', $string = '' ) { 1723 1724 // Setup a default string if none was passed. 1725 $string = empty( $string ) 1726 ? '%s' // Gettext library's placeholder. 1727 : $string; 1728 1729 // Use the string if a last activity date was passed. 1730 $last_active = empty( $last_activity_date ) 1731 ? __( 'Not recently active', 'buddypress' ) 1732 : sprintf( $string, bp_core_time_since( $last_activity_date ) ); 1733 1734 /** 1735 * Filters last activity string based on time since date given. 1736 * 1737 * @since 1.2.0 1738 * 1739 * @param string $last_active Last activity string based on time since date given. 1740 * @param string $last_activity_date The date of last activity. 1741 * @param string $string A sprintf()-able statement of the form 'Active %s'. 1742 */ 1743 return apply_filters( 'bp_core_get_last_activity', $last_active, $last_activity_date, $string ); 1744 } 1745 1746 /** Meta **********************************************************************/ 1747 1748 /** 1749 * Get the meta_key for a given piece of user metadata 1750 * 1751 * BuddyPress stores a number of pieces of user data in the WordPress central 1752 * usermeta table. In order to allow plugins to enable multiple instances of 1753 * BuddyPress on a single WP installation, BP's usermeta keys are filtered 1754 * through this function, so that they can be altered on the fly. 1755 * 1756 * Plugin authors should use BP's _user_meta() functions, which bakes in 1757 * bp_get_user_meta_key(): 1758 * $friend_count = bp_get_user_meta( $user_id, 'total_friend_count', true ); 1759 * If you must use WP's _user_meta() functions directly for some reason, you 1760 * should use this function to determine the $key parameter, eg 1761 * $friend_count = get_user_meta( $user_id, bp_get_user_meta_key( 'total_friend_count' ), true ); 1762 * If using the WP functions, do not not hardcode your meta keys. 1763 * 1764 * @since 1.5.0 1765 * 1766 * @param string|bool $key The usermeta meta_key. 1767 * @return string $key The usermeta meta_key. 1768 */ 1769 function bp_get_user_meta_key( $key = false ) { 1770 1771 /** 1772 * Filters the meta_key for a given piece of user metadata. 1773 * 1774 * @since 1.5.0 1775 * 1776 * @param string $key The usermeta meta key. 1777 */ 1778 return apply_filters( 'bp_get_user_meta_key', $key ); 1779 } 1780 1781 /** 1782 * Get a piece of usermeta. 1783 * 1784 * This is a wrapper for get_user_meta() that allows for easy use of 1785 * bp_get_user_meta_key(), thereby increasing compatibility with non-standard 1786 * BP setups. 1787 * 1788 * @since 1.5.0 1789 * 1790 * @see get_user_meta() For complete details about parameters and return values. 1791 * 1792 * @param int $user_id The ID of the user whose meta you're fetching. 1793 * @param string $key The meta key to retrieve. 1794 * @param bool $single Whether to return a single value. 1795 * @return mixed Will be an array if $single is false. Will be value of meta data field if $single 1796 * is true. 1797 */ 1798 function bp_get_user_meta( $user_id, $key, $single = false ) { 1799 return get_user_meta( $user_id, bp_get_user_meta_key( $key ), $single ); 1800 } 1801 1802 /** 1803 * Update a piece of usermeta. 1804 * 1805 * This is a wrapper for update_user_meta() that allows for easy use of 1806 * bp_get_user_meta_key(), thereby increasing compatibility with non-standard 1807 * BP setups. 1808 * 1809 * @since 1.5.0 1810 * 1811 * @see update_user_meta() For complete details about parameters and return values. 1812 * 1813 * @param int $user_id The ID of the user whose meta you're setting. 1814 * @param string $key The meta key to set. 1815 * @param mixed $value Metadata value. 1816 * @param mixed $prev_value Optional. Previous value to check before removing. 1817 * @return bool False on failure, true on success. 1818 */ 1819 function bp_update_user_meta( $user_id, $key, $value, $prev_value = '' ) { 1820 return update_user_meta( $user_id, bp_get_user_meta_key( $key ), $value, $prev_value ); 1821 } 1822 1823 /** 1824 * Delete a piece of usermeta. 1825 * 1826 * This is a wrapper for delete_user_meta() that allows for easy use of 1827 * bp_get_user_meta_key(), thereby increasing compatibility with non-standard 1828 * BP setups. 1829 * 1830 * @since 1.5.0 1831 * 1832 * @see delete_user_meta() For complete details about parameters and return values. 1833 * 1834 * @param int $user_id The ID of the user whose meta you're deleting. 1835 * @param string $key The meta key to delete. 1836 * @param mixed $value Optional. Metadata value. 1837 * @return bool False for failure. True for success. 1838 */ 1839 function bp_delete_user_meta( $user_id, $key, $value = '' ) { 1840 return delete_user_meta( $user_id, bp_get_user_meta_key( $key ), $value ); 1841 } 1842 1843 /** Embeds ********************************************************************/ 1844 1845 /** 1846 * Initializes {@link BP_Embed} after everything is loaded. 1847 * 1848 * @since 1.5.0 1849 */ 1850 function bp_embed_init() { 1851 1852 // Get BuddyPress. 1853 $bp = buddypress(); 1854 1855 if ( empty( $bp->embed ) ) { 1856 $bp->embed = new BP_Embed(); 1857 } 1858 } 1859 add_action( 'bp_init', 'bp_embed_init', 9 ); 1860 1861 /** 1862 * Are oembeds allowed in activity items? 1863 * 1864 * @since 1.5.0 1865 * 1866 * @return bool False when activity embed support is disabled; true when 1867 * enabled. Default: true. 1868 */ 1869 function bp_use_embed_in_activity() { 1870 1871 /** 1872 * Filters whether or not oEmbeds are allowed in activity items. 1873 * 1874 * @since 1.5.0 1875 * 1876 * @param bool $value Whether or not oEmbeds are allowed. 1877 */ 1878 return apply_filters( 'bp_use_oembed_in_activity', !defined( 'BP_EMBED_DISABLE_ACTIVITY' ) || !BP_EMBED_DISABLE_ACTIVITY ); 1879 } 1880 1881 /** 1882 * Are oembeds allowed in activity replies? 1883 * 1884 * @since 1.5.0 1885 * 1886 * @return bool False when activity replies embed support is disabled; true 1887 * when enabled. Default: true. 1888 */ 1889 function bp_use_embed_in_activity_replies() { 1890 1891 /** 1892 * Filters whether or not oEmbeds are allowed in activity replies. 1893 * 1894 * @since 1.5.0 1895 * 1896 * @param bool $value Whether or not oEmbeds are allowed. 1897 */ 1898 return apply_filters( 'bp_use_embed_in_activity_replies', !defined( 'BP_EMBED_DISABLE_ACTIVITY_REPLIES' ) || !BP_EMBED_DISABLE_ACTIVITY_REPLIES ); 1899 } 1900 1901 /** 1902 * Are oembeds allowed in private messages? 1903 * 1904 * @since 1.5.0 1905 * 1906 * @return bool False when private message embed support is disabled; true when 1907 * enabled. Default: true. 1908 */ 1909 function bp_use_embed_in_private_messages() { 1910 1911 /** 1912 * Filters whether or not oEmbeds are allowed in private messages. 1913 * 1914 * @since 1.5.0 1915 * 1916 * @param bool $value Whether or not oEmbeds are allowed. 1917 */ 1918 return apply_filters( 'bp_use_embed_in_private_messages', !defined( 'BP_EMBED_DISABLE_PRIVATE_MESSAGES' ) || !BP_EMBED_DISABLE_PRIVATE_MESSAGES ); 1919 } 1920 1921 /** 1922 * Extracts media metadata from a given content. 1923 * 1924 * @since 2.6.0 1925 * 1926 * @param string $content The content to check. 1927 * @param string|int $type The type to check. Can also use a bitmask. See the class constants in the 1928 * BP_Media_Extractor class for more info. 1929 * @return false|array If media exists, will return array of media metadata. Else, boolean false. 1930 */ 1931 function bp_core_extract_media_from_content( $content = '', $type = 'all' ) { 1932 if ( is_string( $type ) ) { 1933 $class = new ReflectionClass( 'BP_Media_Extractor' ); 1934 $bitmask = $class->getConstant( strtoupper( $type ) ); 1935 } else { 1936 $bitmask = (int) $type; 1937 } 1938 1939 // Type isn't valid, so bail. 1940 if ( empty( $bitmask ) ) { 1941 return false; 1942 } 1943 1944 $x = new BP_Media_Extractor; 1945 $media = $x->extract( $content, $bitmask ); 1946 1947 unset( $media['has'] ); 1948 $retval = array_filter( $media ); 1949 1950 return ! empty( $retval ) ? $retval : false; 1951 } 1952 1953 /** Admin *********************************************************************/ 1954 1955 /** 1956 * Output the correct admin URL based on BuddyPress and WordPress configuration. 1957 * 1958 * @since 1.5.0 1959 * 1960 * @see bp_get_admin_url() For description of parameters. 1961 * 1962 * @param string $path See {@link bp_get_admin_url()}. 1963 * @param string $scheme See {@link bp_get_admin_url()}. 1964 */ 1965 function bp_admin_url( $path = '', $scheme = 'admin' ) { 1966 echo esc_url( bp_get_admin_url( $path, $scheme ) ); 1967 } 1968 /** 1969 * Return the correct admin URL based on BuddyPress and WordPress configuration. 1970 * 1971 * @since 1.5.0 1972 * 1973 * 1974 * @param string $path Optional. The sub-path under /wp-admin to be 1975 * appended to the admin URL. 1976 * @param string $scheme The scheme to use. Default is 'admin', which 1977 * obeys {@link force_ssl_admin()} and {@link is_ssl()}. 'http' 1978 * or 'https' can be passed to force those schemes. 1979 * @return string Admin url link with optional path appended. 1980 */ 1981 function bp_get_admin_url( $path = '', $scheme = 'admin' ) { 1982 1983 // Links belong in network admin. 1984 if ( bp_core_do_network_admin() ) { 1985 $url = network_admin_url( $path, $scheme ); 1986 1987 // Links belong in site admin. 1988 } else { 1989 $url = admin_url( $path, $scheme ); 1990 } 1991 1992 return $url; 1993 } 1994 1995 /** 1996 * Should BuddyPress appear in network admin (vs a single site Dashboard)? 1997 * 1998 * Because BuddyPress can be installed in multiple ways and with multiple 1999 * configurations, we need to check a few things to be confident about where 2000 * to hook into certain areas of WordPress's admin. 2001 * 2002 * @since 1.5.0 2003 * 2004 * @return bool True if the BP admin screen should appear in the Network Admin, 2005 * otherwise false. 2006 */ 2007 function bp_core_do_network_admin() { 2008 2009 // Default. 2010 $retval = bp_is_network_activated(); 2011 2012 if ( bp_is_multiblog_mode() ) { 2013 $retval = false; 2014 } 2015 2016 /** 2017 * Filters whether or not BuddyPress should appear in network admin. 2018 * 2019 * @since 1.5.0 2020 * 2021 * @param bool $retval Whether or not BuddyPress should be in the network admin. 2022 */ 2023 return (bool) apply_filters( 'bp_core_do_network_admin', $retval ); 2024 } 2025 2026 /** 2027 * Return the action name that BuddyPress nav setup callbacks should be hooked to. 2028 * 2029 * Functions used to set up BP Dashboard pages (wrapping such admin-panel 2030 * functions as add_submenu_page()) should use bp_core_admin_hook() for the 2031 * first parameter in add_action(). BuddyPress will then determine 2032 * automatically whether to load the panels in the Network Admin. Ie: 2033 * 2034 * add_action( bp_core_admin_hook(), 'myplugin_dashboard_panel_setup' ); 2035 * 2036 * @since 1.5.0 2037 * 2038 * @return string $hook The proper hook ('network_admin_menu' or 'admin_menu'). 2039 */ 2040 function bp_core_admin_hook() { 2041 $hook = bp_core_do_network_admin() ? 'network_admin_menu' : 'admin_menu'; 2042 2043 /** 2044 * Filters the action name that BuddyPress nav setup callbacks should be hooked to. 2045 * 2046 * @since 1.5.0 2047 * 2048 * @param string $hook Action name to be attached to. 2049 */ 2050 return apply_filters( 'bp_core_admin_hook', $hook ); 2051 } 2052 2053 /** Multisite *****************************************************************/ 2054 2055 /** 2056 * Is this the root blog? 2057 * 2058 * @since 1.5.0 2059 * 2060 * @param int $blog_id Optional. Default: the ID of the current blog. 2061 * @return bool $is_root_blog Returns true if this is bp_get_root_blog_id(). 2062 */ 2063 function bp_is_root_blog( $blog_id = 0 ) { 2064 2065 // Assume false. 2066 $is_root_blog = false; 2067 2068 // Use current blog if no ID is passed. 2069 if ( empty( $blog_id ) || ! is_int( $blog_id ) ) { 2070 $blog_id = get_current_blog_id(); 2071 } 2072 2073 // Compare to root blog ID. 2074 if ( bp_get_root_blog_id() === $blog_id ) { 2075 $is_root_blog = true; 2076 } 2077 2078 /** 2079 * Filters whether or not we're on the root blog. 2080 * 2081 * @since 1.5.0 2082 * 2083 * @param bool $is_root_blog Whether or not we're on the root blog. 2084 */ 2085 return (bool) apply_filters( 'bp_is_root_blog', (bool) $is_root_blog ); 2086 } 2087 2088 /** 2089 * Get the ID of the root blog. 2090 * 2091 * The "root blog" is the blog on a WordPress network where BuddyPress content 2092 * appears (where member profile URLs resolve, where a given theme is loaded, 2093 * etc.). 2094 * 2095 * @since 1.5.0 2096 * 2097 * @return int The root site ID. 2098 */ 2099 function bp_get_root_blog_id() { 2100 2101 /** 2102 * Filters the ID for the root blog. 2103 * 2104 * @since 1.5.0 2105 * 2106 * @param int $root_blog_id ID for the root blog. 2107 */ 2108 return (int) apply_filters( 'bp_get_root_blog_id', (int) buddypress()->root_blog_id ); 2109 } 2110 2111 /** 2112 * Are we running multiblog mode? 2113 * 2114 * Note that BP_ENABLE_MULTIBLOG is different from (but dependent on) WordPress 2115 * Multisite. "Multiblog" is BuddyPress setup that allows BuddyPress components 2116 * to be viewed on every blog on the network, each with their own settings. 2117 * 2118 * Thus, instead of having all 'boonebgorges' links go to 2119 * http://example.com/members/boonebgorges 2120 * on the root blog, each blog will have its own version of the same content, eg 2121 * http://site2.example.com/members/boonebgorges (for subdomains) 2122 * http://example.com/site2/members/boonebgorges (for subdirectories) 2123 * 2124 * Multiblog mode is disabled by default, meaning that all BuddyPress content 2125 * must be viewed on the root blog. It's also recommended not to use the 2126 * BP_ENABLE_MULTIBLOG constant beyond 1.7, as BuddyPress can now be activated 2127 * on individual sites. 2128 * 2129 * Why would you want to use this? Originally it was intended to allow 2130 * BuddyPress to live in mu-plugins and be visible on mapped domains. This is 2131 * a very small use-case with large architectural shortcomings, so do not go 2132 * down this road unless you specifically need to. 2133 * 2134 * @since 1.5.0 2135 * 2136 * @return bool False when multiblog mode is disabled; true when enabled. 2137 * Default: false. 2138 */ 2139 function bp_is_multiblog_mode() { 2140 2141 // Setup some default values. 2142 $retval = false; 2143 $is_multisite = is_multisite(); 2144 $network_active = bp_is_network_activated(); 2145 $is_multiblog = defined( 'BP_ENABLE_MULTIBLOG' ) && BP_ENABLE_MULTIBLOG; 2146 2147 // Multisite, Network Activated, and Specifically Multiblog. 2148 if ( $is_multisite && $network_active && $is_multiblog ) { 2149 $retval = true; 2150 2151 // Multisite, but not network activated. 2152 } elseif ( $is_multisite && ! $network_active ) { 2153 $retval = true; 2154 } 2155 2156 /** 2157 * Filters whether or not we're running in multiblog mode. 2158 * 2159 * @since 1.5.0 2160 * 2161 * @param bool $retval Whether or not we're running multiblog mode. 2162 */ 2163 return apply_filters( 'bp_is_multiblog_mode', $retval ); 2164 } 2165 2166 /** 2167 * Is BuddyPress active at the network level for this network? 2168 * 2169 * Used to determine admin menu placement, and where settings and options are 2170 * stored. If you're being *really* clever and manually pulling BuddyPress in 2171 * with an mu-plugin or some other method, you'll want to filter 2172 * 'bp_is_network_activated' and override the auto-determined value. 2173 * 2174 * @since 1.7.0 2175 * 2176 * @return bool True if BuddyPress is network activated. 2177 */ 2178 function bp_is_network_activated() { 2179 2180 // Default to is_multisite(). 2181 $retval = is_multisite(); 2182 2183 // Check the sitewide plugins array. 2184 $base = buddypress()->basename; 2185 $plugins = get_site_option( 'active_sitewide_plugins' ); 2186 2187 // Override is_multisite() if not network activated. 2188 if ( ! is_array( $plugins ) || ! isset( $plugins[ $base ] ) ) { 2189 $retval = false; 2190 } 2191 2192 /** 2193 * Filters whether or not we're active at the network level. 2194 * 2195 * @since 1.7.0 2196 * 2197 * @param bool $retval Whether or not we're network activated. 2198 */ 2199 return (bool) apply_filters( 'bp_is_network_activated', $retval ); 2200 } 2201 2202 /** Global Manipulators *******************************************************/ 2203 2204 /** 2205 * Set the "is_directory" global. 2206 * 2207 * @since 1.5.0 2208 * 2209 * @param bool $is_directory Optional. Default: false. 2210 * @param string $component Optional. Component name. Default: the current 2211 * component. 2212 */ 2213 function bp_update_is_directory( $is_directory = false, $component = '' ) { 2214 2215 if ( empty( $component ) ) { 2216 $component = bp_current_component(); 2217 } 2218 2219 /** 2220 * Filters the "is_directory" global value. 2221 * 2222 * @since 1.5.0 2223 * 2224 * @param bool $is_directory Whether or not we're "is_directory". 2225 * @param string $component Component name. Default: the current component. 2226 */ 2227 buddypress()->is_directory = apply_filters( 'bp_update_is_directory', $is_directory, $component ); 2228 } 2229 2230 /** 2231 * Set the "is_item_admin" global. 2232 * 2233 * @since 1.5.0 2234 * 2235 * @param bool $is_item_admin Optional. Default: false. 2236 * @param string $component Optional. Component name. Default: the current 2237 * component. 2238 */ 2239 function bp_update_is_item_admin( $is_item_admin = false, $component = '' ) { 2240 2241 if ( empty( $component ) ) { 2242 $component = bp_current_component(); 2243 } 2244 2245 /** 2246 * Filters the "is_item_admin" global value. 2247 * 2248 * @since 1.5.0 2249 * 2250 * @param bool $is_item_admin Whether or not we're "is_item_admin". 2251 * @param string $component Component name. Default: the current component. 2252 */ 2253 buddypress()->is_item_admin = apply_filters( 'bp_update_is_item_admin', $is_item_admin, $component ); 2254 } 2255 2256 /** 2257 * Set the "is_item_mod" global. 2258 * 2259 * @since 1.5.0 2260 * 2261 * @param bool $is_item_mod Optional. Default: false. 2262 * @param string $component Optional. Component name. Default: the current 2263 * component. 2264 */ 2265 function bp_update_is_item_mod( $is_item_mod = false, $component = '' ) { 2266 2267 if ( empty( $component ) ) { 2268 $component = bp_current_component(); 2269 } 2270 2271 /** 2272 * Filters the "is_item_mod" global value. 2273 * 2274 * @since 1.5.0 2275 * 2276 * @param bool $is_item_mod Whether or not we're "is_item_mod". 2277 * @param string $component Component name. Default: the current component. 2278 */ 2279 buddypress()->is_item_mod = apply_filters( 'bp_update_is_item_mod', $is_item_mod, $component ); 2280 } 2281 2282 /** 2283 * Trigger a 404. 2284 * 2285 * @since 1.5.0 2286 * 2287 * @global WP_Query $wp_query WordPress query object. 2288 * 2289 * @param string $redirect If 'remove_canonical_direct', remove WordPress' "helpful" 2290 * redirect_canonical action. Default: 'remove_canonical_redirect'. 2291 */ 2292 function bp_do_404( $redirect = 'remove_canonical_direct' ) { 2293 global $wp_query; 2294 2295 /** 2296 * Fires inside the triggering of a 404. 2297 * 2298 * @since 1.5.0 2299 * 2300 * @param string $redirect Redirect type used to determine if redirect_canonical 2301 * function should be be removed. 2302 */ 2303 do_action( 'bp_do_404', $redirect ); 2304 2305 $wp_query->set_404(); 2306 status_header( 404 ); 2307 nocache_headers(); 2308 2309 if ( 'remove_canonical_direct' === $redirect ) { 2310 remove_action( 'template_redirect', 'redirect_canonical' ); 2311 } 2312 } 2313 2314 /** Nonces ********************************************************************/ 2315 2316 /** 2317 * Makes sure the user requested an action from another page on this site. 2318 * 2319 * To avoid security exploits within the theme. 2320 * 2321 * @since 1.6.0 2322 * 2323 * @param string $action Action nonce. 2324 * @param string $query_arg Where to look for nonce in $_REQUEST. 2325 * @return bool True if the nonce is verified, otherwise false. 2326 */ 2327 function bp_verify_nonce_request( $action = '', $query_arg = '_wpnonce' ) { 2328 2329 /* Home URL **************************************************************/ 2330 2331 // Parse home_url() into pieces to remove query-strings, strange characters, 2332 // and other funny things that plugins might to do to it. 2333 $parsed_home = parse_url( home_url( '/', ( is_ssl() ? 'https' : 'http' ) ) ); 2334 2335 // Maybe include the port, if it's included in home_url(). 2336 if ( isset( $parsed_home['port'] ) ) { 2337 $parsed_host = $parsed_home['host'] . ':' . $parsed_home['port']; 2338 } else { 2339 $parsed_host = $parsed_home['host']; 2340 } 2341 2342 // Set the home URL for use in comparisons. 2343 $home_url = trim( strtolower( $parsed_home['scheme'] . '://' . $parsed_host . $parsed_home['path'] ), '/' ); 2344 2345 /* Requested URL *********************************************************/ 2346 2347 // Maybe include the port, if it's included in home_url(). 2348 if ( isset( $parsed_home['port'] ) && false === strpos( $_SERVER['HTTP_HOST'], ':' ) ) { 2349 $request_host = $_SERVER['HTTP_HOST'] . ':' . $_SERVER['SERVER_PORT']; 2350 } else { 2351 $request_host = $_SERVER['HTTP_HOST']; 2352 } 2353 2354 // Build the currently requested URL. 2355 $scheme = is_ssl() ? 'https://' : 'http://'; 2356 $requested_url = strtolower( $scheme . $request_host . $_SERVER['REQUEST_URI'] ); 2357 2358 /* Look for match ********************************************************/ 2359 2360 /** 2361 * Filters the requested URL being nonce-verified. 2362 * 2363 * Useful for configurations like reverse proxying. 2364 * 2365 * @since 1.9.0 2366 * 2367 * @param string $requested_url The requested URL. 2368 */ 2369 $matched_url = apply_filters( 'bp_verify_nonce_request_url', $requested_url ); 2370 2371 // Check the nonce. 2372 $result = isset( $_REQUEST[$query_arg] ) ? wp_verify_nonce( $_REQUEST[$query_arg], $action ) : false; 2373 2374 // Nonce check failed. 2375 if ( empty( $result ) || empty( $action ) || ( strpos( $matched_url, $home_url ) !== 0 ) ) { 2376 $result = false; 2377 } 2378 2379 /** 2380 * Fires at the end of the nonce verification check. 2381 * 2382 * @since 1.6.0 2383 * 2384 * @param string $action Action nonce. 2385 * @param bool $result Boolean result of nonce verification. 2386 */ 2387 do_action( 'bp_verify_nonce_request', $action, $result ); 2388 2389 return $result; 2390 } 2391 2392 /** Requests ******************************************************************/ 2393 2394 /** 2395 * Return true|false if this is a POST request. 2396 * 2397 * @since 1.9.0 2398 * 2399 * @return bool 2400 */ 2401 function bp_is_post_request() { 2402 return (bool) ( 'POST' === strtoupper( $_SERVER['REQUEST_METHOD'] ) ); 2403 } 2404 2405 /** 2406 * Return true|false if this is a GET request. 2407 * 2408 * @since 1.9.0 2409 * 2410 * @return bool 2411 */ 2412 function bp_is_get_request() { 2413 return (bool) ( 'GET' === strtoupper( $_SERVER['REQUEST_METHOD'] ) ); 2414 } 2415 2416 2417 /** Miscellaneous hooks *******************************************************/ 2418 2419 /** 2420 * Load the buddypress translation file for current language. 2421 * 2422 * @since 1.0.2 2423 * 2424 * @see load_textdomain() for a description of return values. 2425 * 2426 * @return bool True on success, false on failure. 2427 */ 2428 function bp_core_load_buddypress_textdomain() { 2429 $domain = 'buddypress'; 2430 2431 /** 2432 * Filters the locale to be loaded for the language files. 2433 * 2434 * @since 1.0.2 2435 * 2436 * @param string $value Current locale for the install. 2437 */ 2438 $mofile_custom = sprintf( '%s-%s.mo', $domain, apply_filters( 'buddypress_locale', get_locale() ) ); 2439 2440 /** 2441 * Filters the locations to load language files from. 2442 * 2443 * @since 2.2.0 2444 * 2445 * @param array $value Array of directories to check for language files in. 2446 */ 2447 $locations = apply_filters( 'buddypress_locale_locations', array( 2448 trailingslashit( WP_LANG_DIR . '/' . $domain ), 2449 trailingslashit( WP_LANG_DIR ), 2450 ) ); 2451 2452 // Try custom locations in WP_LANG_DIR. 2453 foreach ( $locations as $location ) { 2454 if ( load_textdomain( 'buddypress', $location . $mofile_custom ) ) { 2455 return true; 2456 } 2457 } 2458 2459 // Default to WP and glotpress. 2460 return load_plugin_textdomain( $domain ); 2461 } 2462 add_action( 'bp_core_loaded', 'bp_core_load_buddypress_textdomain' ); 2463 2464 /** 2465 * A JavaScript-free implementation of the search functions in BuddyPress. 2466 * 2467 * @since 1.0.1 2468 * @since 10.0.0 Add support for Activity search. 2469 * 2470 * @param string $slug The slug to redirect to for searching. 2471 */ 2472 function bp_core_action_search_site( $slug = '' ) { 2473 2474 if ( ! bp_is_current_component( bp_get_search_slug() ) ) { 2475 return; 2476 } 2477 2478 if ( empty( $_POST['search-terms'] ) ) { 2479 bp_core_redirect( bp_get_root_domain() ); 2480 return; 2481 } 2482 2483 $search_terms = stripslashes( $_POST['search-terms'] ); 2484 $search_which = !empty( $_POST['search-which'] ) ? $_POST['search-which'] : ''; 2485 $query_string = '/?s='; 2486 2487 if ( empty( $slug ) ) { 2488 switch ( $search_which ) { 2489 case 'posts': 2490 $slug = ''; 2491 $var = '/?s='; 2492 2493 // If posts aren't displayed on the front page, find the post page's slug. 2494 if ( 'page' == get_option( 'show_on_front' ) ) { 2495 $page = get_post( get_option( 'page_for_posts' ) ); 2496 2497 if ( !is_wp_error( $page ) && !empty( $page->post_name ) ) { 2498 $slug = $page->post_name; 2499 $var = '?s='; 2500 } 2501 } 2502 break; 2503 2504 case 'activity': 2505 $slug = bp_is_active( 'activity' ) ? bp_get_activity_root_slug() : ''; 2506 break; 2507 2508 case 'blogs': 2509 $slug = bp_is_active( 'blogs' ) ? bp_get_blogs_root_slug() : ''; 2510 break; 2511 2512 case 'groups': 2513 $slug = bp_is_active( 'groups' ) ? bp_get_groups_root_slug() : ''; 2514 break; 2515 2516 case 'members': 2517 default: 2518 $slug = bp_get_members_root_slug(); 2519 break; 2520 } 2521 2522 if ( empty( $slug ) && 'posts' != $search_which ) { 2523 bp_core_redirect( bp_get_root_domain() ); 2524 return; 2525 } 2526 } 2527 2528 /** 2529 * Filters the constructed url for use with site searching. 2530 * 2531 * @since 1.0.0 2532 * 2533 * @param string $value URL for use with site searching. 2534 * @param array $search_terms Array of search terms. 2535 */ 2536 bp_core_redirect( apply_filters( 'bp_core_search_site', home_url( $slug . $query_string . urlencode( $search_terms ) ), $search_terms ) ); 2537 } 2538 add_action( 'bp_init', 'bp_core_action_search_site', 7 ); 2539 2540 /** 2541 * Remove "prev" and "next" relational links from <head> on BuddyPress pages. 2542 * 2543 * WordPress automatically generates these relational links to the current 2544 * page. However, BuddyPress doesn't adhere to these links. In this 2545 * function, we remove these links when on a BuddyPress page. This also 2546 * prevents additional, unnecessary queries from running. 2547 * 2548 * @since 2.1.0 2549 */ 2550 function bp_remove_adjacent_posts_rel_link() { 2551 if ( ! is_buddypress() ) { 2552 return; 2553 } 2554 2555 remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10 ); 2556 } 2557 add_action( 'bp_init', 'bp_remove_adjacent_posts_rel_link' ); 2558 2559 /** 2560 * Strip the span count of a menu item or of a title part. 2561 * 2562 * @since 2.2.2 2563 * 2564 * @param string $title_part Title part to clean up. 2565 * @return string 2566 */ 2567 function _bp_strip_spans_from_title( $title_part = '' ) { 2568 $title = $title_part; 2569 $span = strpos( $title, '<span' ); 2570 if ( false !== $span ) { 2571 $title = substr( $title, 0, $span - 1 ); 2572 } 2573 return trim( $title ); 2574 } 2575 2576 /** 2577 * Get the correct filename suffix for minified assets. 2578 * 2579 * @since 2.5.0 2580 * 2581 * @return string 2582 */ 2583 function bp_core_get_minified_asset_suffix() { 2584 $ext = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min'; 2585 2586 // Ensure the assets can be located when running from /src/. 2587 if ( defined( 'BP_SOURCE_SUBDIRECTORY' ) && BP_SOURCE_SUBDIRECTORY === 'src' ) { 2588 $ext = str_replace( '.min', '', $ext ); 2589 } 2590 2591 return $ext; 2592 } 2593 2594 /** 2595 * Return a list of component information. 2596 * 2597 * @since 2.6.0 2598 * 2599 * @param string $type Optional; component type to fetch. Default value is 'all', or 'optional', 'retired', 'required'. 2600 * @return array Requested components' data. 2601 */ 2602 function bp_core_get_components( $type = 'all' ) { 2603 $required_components = array( 2604 'core' => array( 2605 'title' => __( 'BuddyPress Core', 'buddypress' ), 2606 'description' => __( 'It‘s what makes <del>time travel</del> BuddyPress possible!', 'buddypress' ) 2607 ), 2608 'members' => array( 2609 'title' => __( 'Community Members', 'buddypress' ), 2610 'description' => __( 'Everything in a BuddyPress community revolves around its members.', 'buddypress' ) 2611 ), 2612 ); 2613 2614 $retired_components = array( 2615 ); 2616 2617 $optional_components = array( 2618 'xprofile' => array( 2619 'title' => __( 'Extended Profiles', 'buddypress' ), 2620 'description' => __( 'Customize your community with fully editable profile fields that allow your users to describe themselves.', 'buddypress' ) 2621 ), 2622 'settings' => array( 2623 'title' => __( 'Account Settings', 'buddypress' ), 2624 'description' => __( 'Allow your users to modify their account and notification settings directly from within their profiles.', 'buddypress' ) 2625 ), 2626 'friends' => array( 2627 'title' => __( 'Friend Connections', 'buddypress' ), 2628 'description' => __( 'Let your users make connections so they can track the activity of others and focus on the people they care about the most.', 'buddypress' ) 2629 ), 2630 'messages' => array( 2631 'title' => __( 'Private Messaging', 'buddypress' ), 2632 'description' => __( 'Allow your users to talk to each other directly and in private. Not just limited to one-on-one discussions, messages can be sent between any number of members.', 'buddypress' ) 2633 ), 2634 'activity' => array( 2635 'title' => __( 'Activity Streams', 'buddypress' ), 2636 'description' => __( 'Global, personal, and group activity streams with threaded commenting, direct posting, favoriting, and @mentions, all with full RSS feed and email notification support.', 'buddypress' ) 2637 ), 2638 'notifications' => array( 2639 'title' => __( 'Notifications', 'buddypress' ), 2640 'description' => __( 'Notify members of relevant activity with a toolbar bubble and/or via email, and allow them to customize their notification settings.', 'buddypress' ) 2641 ), 2642 'groups' => array( 2643 'title' => __( 'User Groups', 'buddypress' ), 2644 'description' => __( 'Groups allow your users to organize themselves into specific public, private or hidden sections with separate activity streams and member listings.', 'buddypress' ) 2645 ), 2646 'blogs' => array( 2647 'title' => __( 'Site Tracking', 'buddypress' ), 2648 'description' => __( 'Record activity for new posts and comments from your site.', 'buddypress' ) 2649 ) 2650 ); 2651 2652 // Add blogs tracking if multisite. 2653 if ( is_multisite() ) { 2654 $optional_components['blogs']['description'] = __( 'Record activity for new sites, posts, and comments across your network.', 'buddypress' ); 2655 } 2656 2657 switch ( $type ) { 2658 case 'required' : 2659 $components = $required_components; 2660 break; 2661 case 'optional' : 2662 $components = $optional_components; 2663 break; 2664 case 'retired' : 2665 $components = $retired_components; 2666 break; 2667 case 'all' : 2668 default : 2669 $components = array_merge( $required_components, $optional_components, $retired_components ); 2670 break; 2671 } 2672 2673 /** 2674 * Filters the list of component information. 2675 * 2676 * @since 2.6.0 2677 * 2678 * @param array $components Array of component information. 2679 * @param string $type Type of component list requested. 2680 * Possible values are 'all', 'optional', 'retired', 'required'. 2681 */ 2682 return apply_filters( 'bp_core_get_components', $components, $type ); 2683 } 2684 2685 /** Nav Menu ******************************************************************/ 2686 2687 /** 2688 * Create fake "post" objects for BP's logged-in nav menu for use in the WordPress "Menus" settings page. 2689 * 2690 * WordPress nav menus work by representing post or tax term data as a custom 2691 * post type, which is then used to populate the checkboxes that appear on 2692 * Dashboard > Appearance > Menu as well as the menu as rendered on the front 2693 * end. Most of the items in the BuddyPress set of nav items are neither posts 2694 * nor tax terms, so we fake a post-like object so as to be compatible with the 2695 * menu. 2696 * 2697 * This technique also allows us to generate links dynamically, so that, for 2698 * example, "My Profile" will always point to the URL of the profile of the 2699 * logged-in user. 2700 * 2701 * @since 1.9.0 2702 * 2703 * @return mixed A URL or an array of dummy pages. 2704 */ 2705 function bp_nav_menu_get_loggedin_pages() { 2706 $bp = buddypress(); 2707 2708 // Try to catch the cached version first. 2709 if ( ! empty( $bp->wp_nav_menu_items->loggedin ) ) { 2710 return $bp->wp_nav_menu_items->loggedin; 2711 } 2712 2713 // Pull up a list of items registered in BP's primary nav for the member. 2714 $bp_menu_items = $bp->members->nav->get_primary(); 2715 2716 // Some BP nav menu items will not be represented in bp_nav, because 2717 // they are not real BP components. We add them manually here. 2718 $bp_menu_items[] = array( 2719 'name' => __( 'Log Out', 'buddypress' ), 2720 'slug' => 'logout', 2721 'link' => wp_logout_url(), 2722 ); 2723 2724 // If there's nothing to show, we're done. 2725 if ( count( $bp_menu_items ) < 1 ) { 2726 return false; 2727 } 2728 2729 $page_args = array(); 2730 2731 foreach ( $bp_menu_items as $bp_item ) { 2732 2733 // Remove <span>number</span>. 2734 $item_name = _bp_strip_spans_from_title( $bp_item['name'] ); 2735 2736 $page_args[ $bp_item['slug'] ] = (object) array( 2737 'ID' => -1, 2738 'post_title' => $item_name, 2739 'post_author' => 0, 2740 'post_date' => 0, 2741 'post_excerpt' => $bp_item['slug'], 2742 'post_type' => 'bp_nav_menu_item', 2743 'post_status' => 'publish', 2744 'comment_status' => 'closed', 2745 'guid' => $bp_item['link'] 2746 ); 2747 } 2748 2749 if ( empty( $bp->wp_nav_menu_items ) ) { 2750 buddypress()->wp_nav_menu_items = new stdClass; 2751 } 2752 2753 $bp->wp_nav_menu_items->loggedin = $page_args; 2754 2755 return $page_args; 2756 } 2757 2758 /** 2759 * Create fake "post" objects for BP's logged-out nav menu for use in the WordPress "Menus" settings page. 2760 * 2761 * WordPress nav menus work by representing post or tax term data as a custom 2762 * post type, which is then used to populate the checkboxes that appear on 2763 * Dashboard > Appearance > Menu as well as the menu as rendered on the front 2764 * end. Most of the items in the BuddyPress set of nav items are neither posts 2765 * nor tax terms, so we fake a post-like object so as to be compatible with the 2766 * menu. 2767 * 2768 * @since 1.9.0 2769 * 2770 * @return mixed A URL or an array of dummy pages. 2771 */ 2772 function bp_nav_menu_get_loggedout_pages() { 2773 $bp = buddypress(); 2774 2775 // Try to catch the cached version first. 2776 if ( ! empty( $bp->wp_nav_menu_items->loggedout ) ) { 2777 return $bp->wp_nav_menu_items->loggedout; 2778 } 2779 2780 $bp_menu_items = array(); 2781 2782 // Some BP nav menu items will not be represented in bp_nav, because 2783 // they are not real BP components. We add them manually here. 2784 $bp_menu_items[] = array( 2785 'name' => __( 'Log In', 'buddypress' ), 2786 'slug' => 'login', 2787 'link' => wp_login_url(), 2788 ); 2789 2790 // The Register page will not always be available (ie, when 2791 // registration is disabled). 2792 $bp_directory_page_ids = bp_core_get_directory_page_ids(); 2793 2794 if( ! empty( $bp_directory_page_ids['register'] ) ) { 2795 $register_page = get_post( $bp_directory_page_ids['register'] ); 2796 $bp_menu_items[] = array( 2797 'name' => $register_page->post_title, 2798 'slug' => 'register', 2799 'link' => get_permalink( $register_page->ID ), 2800 ); 2801 } 2802 2803 // If there's nothing to show, we're done. 2804 if ( count( $bp_menu_items ) < 1 ) { 2805 return false; 2806 } 2807 2808 $page_args = array(); 2809 2810 foreach ( $bp_menu_items as $bp_item ) { 2811 $page_args[ $bp_item['slug'] ] = (object) array( 2812 'ID' => -1, 2813 'post_title' => $bp_item['name'], 2814 'post_author' => 0, 2815 'post_date' => 0, 2816 'post_excerpt' => $bp_item['slug'], 2817 'post_type' => 'bp_nav_menu_item', 2818 'post_status' => 'publish', 2819 'comment_status' => 'closed', 2820 'guid' => $bp_item['link'] 2821 ); 2822 } 2823 2824 if ( empty( $bp->wp_nav_menu_items ) ) { 2825 $bp->wp_nav_menu_items = new stdClass; 2826 } 2827 2828 $bp->wp_nav_menu_items->loggedout = $page_args; 2829 2830 return $page_args; 2831 } 2832 2833 /** 2834 * Get the URL for a BuddyPress WP nav menu item, based on slug. 2835 * 2836 * BuddyPress-specific WP nav menu items have dynamically generated URLs, 2837 * based on the identity of the current user. This function lets you fetch the 2838 * proper URL for a given nav item slug (such as 'login' or 'messages'). 2839 * 2840 * @since 1.9.0 2841 * 2842 * @param string $slug The slug of the nav item: login, register, or one of the 2843 * slugs from the members navigation. 2844 * @return string $nav_item_url The URL generated for the current user. 2845 */ 2846 function bp_nav_menu_get_item_url( $slug ) { 2847 $nav_item_url = ''; 2848 $nav_menu_items = bp_nav_menu_get_loggedin_pages(); 2849 2850 if ( isset( $nav_menu_items[ $slug ] ) ) { 2851 $nav_item_url = $nav_menu_items[ $slug ]->guid; 2852 } 2853 2854 return $nav_item_url; 2855 } 2856 2857 /** Suggestions***************************************************************/ 2858 2859 /** 2860 * BuddyPress Suggestions API for types of at-mentions. 2861 * 2862 * This is used to power BuddyPress' at-mentions suggestions, but it is flexible enough to be used 2863 * for similar kinds of future requirements, or those implemented by third-party developers. 2864 * 2865 * @since 2.1.0 2866 * 2867 * @param array $args Array of args for the suggestions. 2868 * @return array|WP_Error Array of results. If there were any problems, returns a WP_Error object. 2869 */ 2870 function bp_core_get_suggestions( $args ) { 2871 $args = bp_parse_args( 2872 $args, 2873 array(), 2874 'get_suggestions' 2875 ); 2876 2877 if ( ! $args['type'] ) { 2878 return new WP_Error( 'missing_parameter' ); 2879 } 2880 2881 // Members @name suggestions. 2882 if ( $args['type'] === 'members' ) { 2883 $class = 'BP_Members_Suggestions'; 2884 2885 // Members @name suggestions for users in a specific Group. 2886 if ( isset( $args['group_id'] ) ) { 2887 $class = 'BP_Groups_Member_Suggestions'; 2888 } 2889 2890 } else { 2891 2892 /** 2893 * Filters the default suggestions service to use. 2894 * 2895 * Use this hook to tell BP the name of your class 2896 * if you've built a custom suggestions service. 2897 * 2898 * @since 2.1.0 2899 * 2900 * @param string $value Custom class to use. Default: none. 2901 * @param array $args Array of arguments for suggestions. 2902 */ 2903 $class = apply_filters( 'bp_suggestions_services', '', $args ); 2904 } 2905 2906 if ( ! $class || ! class_exists( $class ) ) { 2907 return new WP_Error( 'missing_parameter' ); 2908 } 2909 2910 2911 $suggestions = new $class( $args ); 2912 $validation = $suggestions->validate(); 2913 2914 if ( is_wp_error( $validation ) ) { 2915 $retval = $validation; 2916 } else { 2917 $retval = $suggestions->get_suggestions(); 2918 } 2919 2920 /** 2921 * Filters the available type of at-mentions. 2922 * 2923 * @since 2.1.0 2924 * 2925 * @param array|WP_Error $retval Array of results or WP_Error object. 2926 * @param array $args Array of arguments for suggestions. 2927 */ 2928 return apply_filters( 'bp_core_get_suggestions', $retval, $args ); 2929 } 2930 2931 /** 2932 * AJAX endpoint for Suggestions API lookups. 2933 * 2934 * @since 2.1.0 2935 * @since 4.0.0 Moved here to make sure this function is available 2936 * even if the Activity component is not active. 2937 */ 2938 function bp_ajax_get_suggestions() { 2939 if ( ! bp_is_user_active() || empty( $_GET['term'] ) || empty( $_GET['type'] ) ) { 2940 wp_send_json_error( 'missing_parameter' ); 2941 exit; 2942 } 2943 2944 $args = array( 2945 'term' => sanitize_text_field( $_GET['term'] ), 2946 'type' => sanitize_text_field( $_GET['type'] ), 2947 ); 2948 2949 // Support per-Group suggestions. 2950 if ( ! empty( $_GET['group-id'] ) ) { 2951 $args['group_id'] = absint( $_GET['group-id'] ); 2952 } 2953 2954 $results = bp_core_get_suggestions( $args ); 2955 2956 if ( is_wp_error( $results ) ) { 2957 wp_send_json_error( $results->get_error_message() ); 2958 exit; 2959 } 2960 2961 wp_send_json_success( $results ); 2962 } 2963 add_action( 'wp_ajax_bp_get_suggestions', 'bp_ajax_get_suggestions' ); 2964 2965 /** 2966 * Set data from the BP root blog's upload directory. 2967 * 2968 * Handy for multisite instances because all uploads are made on the BP root 2969 * blog and we need to query the BP root blog for the upload directory data. 2970 * 2971 * This function ensures that we only need to use {@link switch_to_blog()} 2972 * once to get what we need. 2973 * 2974 * @since 2.3.0 2975 * 2976 * @return bool|array 2977 */ 2978 function bp_upload_dir() { 2979 $bp = buddypress(); 2980 2981 if ( empty( $bp->upload_dir ) ) { 2982 $need_switch = (bool) ( is_multisite() && ! bp_is_root_blog() ); 2983 2984 // Maybe juggle to root blog. 2985 if ( true === $need_switch ) { 2986 switch_to_blog( bp_get_root_blog_id() ); 2987 } 2988 2989 // Get the upload directory (maybe for root blog). 2990 $wp_upload_dir = wp_upload_dir(); 2991 2992 // Maybe juggle back to current blog. 2993 if ( true === $need_switch ) { 2994 restore_current_blog(); 2995 } 2996 2997 // Bail if an error occurred. 2998 if ( ! empty( $wp_upload_dir['error'] ) ) { 2999 return false; 3000 } 3001 3002 $bp->upload_dir = $wp_upload_dir; 3003 } 3004 3005 return $bp->upload_dir; 3006 } 3007 3008 3009 /** Post Types *****************************************************************/ 3010 3011 /** 3012 * Output the name of the email post type. 3013 * 3014 * @since 2.5.0 3015 */ 3016 function bp_email_post_type() { 3017 echo bp_get_email_post_type(); 3018 } 3019 /** 3020 * Returns the name of the email post type. 3021 * 3022 * @since 2.5.0 3023 * 3024 * @return string The name of the email post type. 3025 */ 3026 function bp_get_email_post_type() { 3027 3028 /** 3029 * Filters the name of the email post type. 3030 * 3031 * @since 2.5.0 3032 * 3033 * @param string $value Email post type name. 3034 */ 3035 return apply_filters( 'bp_get_email_post_type', buddypress()->email_post_type ); 3036 } 3037 3038 /** 3039 * Return labels used by the email post type. 3040 * 3041 * @since 2.5.0 3042 * 3043 * @return array 3044 */ 3045 function bp_get_email_post_type_labels() { 3046 3047 /** 3048 * Filters email post type labels. 3049 * 3050 * @since 2.5.0 3051 * 3052 * @param array $value Associative array (name => label). 3053 */ 3054 return apply_filters( 'bp_get_email_post_type_labels', array( 3055 'add_new' => _x( 'Add New', 'email post type label', 'buddypress' ), 3056 'add_new_item' => _x( 'Add a New Email', 'email post type label', 'buddypress' ), 3057 'all_items' => _x( 'All Emails', 'email post type label', 'buddypress' ), 3058 'edit_item' => _x( 'Edit Email', 'email post type label', 'buddypress' ), 3059 'filter_items_list' => _x( 'Filter email list', 'email post type label', 'buddypress' ), 3060 'items_list' => _x( 'Email list', 'email post type label', 'buddypress' ), 3061 'items_list_navigation' => _x( 'Email list navigation', 'email post type label', 'buddypress' ), 3062 'menu_name' => _x( 'Emails', 'email post type name', 'buddypress' ), 3063 'name' => _x( 'BuddyPress Emails', 'email post type label', 'buddypress' ), 3064 'new_item' => _x( 'New Email', 'email post type label', 'buddypress' ), 3065 'not_found' => _x( 'No emails found', 'email post type label', 'buddypress' ), 3066 'not_found_in_trash' => _x( 'No emails found in Trash', 'email post type label', 'buddypress' ), 3067 'search_items' => _x( 'Search Emails', 'email post type label', 'buddypress' ), 3068 'singular_name' => _x( 'Email', 'email post type singular name', 'buddypress' ), 3069 'uploaded_to_this_item' => _x( 'Uploaded to this email', 'email post type label', 'buddypress' ), 3070 'view_item' => _x( 'View Email', 'email post type label', 'buddypress' ), 3071 ) ); 3072 } 3073 3074 /** 3075 * Return array of features that the email post type supports. 3076 * 3077 * @since 2.5.0 3078 * 3079 * @return array 3080 */ 3081 function bp_get_email_post_type_supports() { 3082 3083 /** 3084 * Filters the features that the email post type supports. 3085 * 3086 * @since 2.5.0 3087 * 3088 * @param array $value Supported features. 3089 */ 3090 return apply_filters( 'bp_get_email_post_type_supports', array( 3091 'custom-fields', 3092 'editor', 3093 'excerpt', 3094 'revisions', 3095 'title', 3096 ) ); 3097 } 3098 3099 3100 /** Taxonomies *****************************************************************/ 3101 3102 /** 3103 * Returns the BP Taxonomy common arguments. 3104 * 3105 * @since 7.0.0 3106 * 3107 * @return array The BP Taxonomy common arguments. 3108 */ 3109 function bp_get_taxonomy_common_args() { 3110 return array( 3111 'public' => false, 3112 'show_in_rest' => false, 3113 'query_var' => false, 3114 'rewrite' => false, 3115 'show_in_menu' => false, 3116 'show_tagcloud' => false, 3117 'show_ui' => bp_is_root_blog() && bp_current_user_can( 'bp_moderate' ), 3118 ); 3119 } 3120 3121 /** 3122 * Returns the BP Taxonomy common labels. 3123 * 3124 * @since 7.0.0 3125 * 3126 * @return array The BP Taxonomy common labels. 3127 */ 3128 function bp_get_taxonomy_common_labels() { 3129 return array( 3130 'bp_type_name' => _x( 'Plural Name', 'BP Type name label', 'buddypress' ), 3131 'bp_type_singular_name' => _x( 'Singular name', 'BP Type singular name label', 'buddypress' ), 3132 'bp_type_has_directory' => _x( 'Has Directory View', 'BP Type has directory checkbox label', 'buddypress' ), 3133 'bp_type_directory_slug' => _x( 'Custom type directory slug', 'BP Type slug label', 'buddypress' ), 3134 ); 3135 } 3136 3137 /** 3138 * Output the name of the email type taxonomy. 3139 * 3140 * @since 2.5.0 3141 */ 3142 function bp_email_tax_type() { 3143 echo bp_get_email_tax_type(); 3144 } 3145 /** 3146 * Return the name of the email type taxonomy. 3147 * 3148 * @since 2.5.0 3149 * 3150 * @return string The unique email taxonomy type ID. 3151 */ 3152 function bp_get_email_tax_type() { 3153 3154 /** 3155 * Filters the name of the email type taxonomy. 3156 * 3157 * @since 2.5.0 3158 * 3159 * @param string $value Email type taxonomy name. 3160 */ 3161 return apply_filters( 'bp_get_email_tax_type', buddypress()->email_taxonomy_type ); 3162 } 3163 3164 /** 3165 * Return labels used by the email type taxonomy. 3166 * 3167 * @since 2.5.0 3168 * 3169 * @return array 3170 */ 3171 function bp_get_email_tax_type_labels() { 3172 3173 /** 3174 * Filters email type taxonomy labels. 3175 * 3176 * @since 2.5.0 3177 * 3178 * @param array $value Associative array (name => label). 3179 */ 3180 return apply_filters( 'bp_get_email_tax_type_labels', array( 3181 'add_new_item' => _x( 'New Email Situation', 'email type taxonomy label', 'buddypress' ), 3182 'all_items' => _x( 'All Email Situations', 'email type taxonomy label', 'buddypress' ), 3183 'edit_item' => _x( 'Edit Email Situations', 'email type taxonomy label', 'buddypress' ), 3184 'items_list' => _x( 'Email list', 'email type taxonomy label', 'buddypress' ), 3185 'items_list_navigation' => _x( 'Email list navigation', 'email type taxonomy label', 'buddypress' ), 3186 'menu_name' => _x( 'Situations', 'email type taxonomy label', 'buddypress' ), 3187 'name' => _x( 'Situation', 'email type taxonomy name', 'buddypress' ), 3188 'new_item_name' => _x( 'New email situation name', 'email type taxonomy label', 'buddypress' ), 3189 'not_found' => _x( 'No email situations found.', 'email type taxonomy label', 'buddypress' ), 3190 'no_terms' => _x( 'No email situations', 'email type taxonomy label', 'buddypress' ), 3191 'popular_items' => _x( 'Popular Email Situation', 'email type taxonomy label', 'buddypress' ), 3192 'search_items' => _x( 'Search Emails', 'email type taxonomy label', 'buddypress' ), 3193 'singular_name' => _x( 'Email', 'email type taxonomy singular name', 'buddypress' ), 3194 'update_item' => _x( 'Update Email Situation', 'email type taxonomy label', 'buddypress' ), 3195 'view_item' => _x( 'View Email Situation', 'email type taxonomy label', 'buddypress' ), 3196 ) ); 3197 } 3198 3199 /** 3200 * Return arguments used by the email type taxonomy. 3201 * 3202 * @since 7.0.0 3203 * 3204 * @return array 3205 */ 3206 function bp_get_email_tax_type_args() { 3207 3208 /** 3209 * Filters emails type taxonomy args. 3210 * 3211 * @since 7.0.0 3212 * 3213 * @param array $value Associative array (key => arg). 3214 */ 3215 return apply_filters( 3216 'bp_register_email_tax_type', 3217 array_merge( 3218 array( 3219 'description' => _x( 'BuddyPress email types', 'email type taxonomy description', 'buddypress' ), 3220 'labels' => bp_get_email_tax_type_labels(), 3221 'meta_box_cb' => 'bp_email_tax_type_metabox', 3222 ), 3223 bp_get_taxonomy_common_args() 3224 ) 3225 ); 3226 } 3227 3228 /** 3229 * Returns the default BuddyPress type metadata schema. 3230 * 3231 * @since 7.0.0 3232 * 3233 * @param boolean $suppress_filters Whether to suppress filters. Default `false`. 3234 * @param string $type_taxonomy Optional. the Type's taxonomy name. 3235 * @return array The default BuddyPress type metadata schema. 3236 */ 3237 function bp_get_type_metadata_schema( $suppress_filters = false, $type_taxonomy = '' ) { 3238 $schema = array( 3239 'bp_type_singular_name' => array( 3240 'description' => __( 'The name of this type in singular form. ', 'buddypress' ), 3241 'type' => 'string', 3242 'single' => true, 3243 'sanitize_callback' => 'sanitize_text_field', 3244 ), 3245 'bp_type_name' => array( 3246 'description' => __( 'The name of this type in plural form.', 'buddypress' ), 3247 'type' => 'string', 3248 'single' => true, 3249 'sanitize_callback' => 'sanitize_text_field', 3250 ), 3251 'bp_type_has_directory' => array( 3252 'description' => __( 'Make a list matching this type available on the directory.', 'buddypress' ), 3253 'type' => 'boolean', 3254 'single' => true, 3255 'sanitize_callback' => 'absint', 3256 ), 3257 'bp_type_directory_slug' => array( 3258 'label' => __( 'Type slug', 'buddypress' ), 3259 'description' => __( 'Enter if you want the type slug to be different from its ID.', 'buddypress' ), 3260 'type' => 'string', 3261 'single' => true, 3262 'sanitize_callback' => 'sanitize_title', 3263 ), 3264 ); 3265 3266 if ( true === $suppress_filters ) { 3267 return $schema; 3268 } 3269 3270 /** 3271 * Filter here to add new meta to the BuddyPress type metadata. 3272 * 3273 * @since 7.0.0 3274 * 3275 * @param array $schema Associative array (name => arguments). 3276 * @param string $type_taxonomy The Type's taxonomy name. 3277 */ 3278 return apply_filters( 'bp_get_type_metadata_schema', $schema, $type_taxonomy ); 3279 } 3280 3281 /** 3282 * Registers a meta key for BuddyPress types. 3283 * 3284 * @since 7.0.0 3285 * 3286 * @param string $type_tax The BuddyPress type taxonomy. 3287 * @param string $meta_key The meta key to register. 3288 * @param array $args Data used to describe the meta key when registered. See 3289 * {@see register_meta()} for a list of supported arguments. 3290 * @return bool True if the meta key was successfully registered, false if not. 3291 */ 3292 function bp_register_type_meta( $type_tax, $meta_key, array $args ) { 3293 $taxonomies = wp_list_pluck( bp_get_default_taxonomies(), 'component' ); 3294 3295 if ( ! isset( $taxonomies[ $type_tax ] ) ) { 3296 return false; 3297 } 3298 3299 return register_term_meta( $type_tax, $meta_key, $args ); 3300 } 3301 3302 /** 3303 * Update a list of metadata for a given type ID and a given taxonomy. 3304 * 3305 * @since 7.0.0 3306 * 3307 * @param integer $type_id The database ID of the BP Type. 3308 * @param string $taxonomy The BP Type taxonomy. 3309 * @param array $type_metas An associative array (meta_key=>meta_value). 3310 * @return boolean False on failure. True otherwise. 3311 */ 3312 function bp_update_type_metadata( $type_id = 0, $taxonomy = '', $type_metas = array() ) { 3313 if ( ! $type_id || ! $taxonomy || ! is_array( $type_metas ) ) { 3314 return false; 3315 } 3316 3317 foreach ( $type_metas as $meta_key => $meta_value ) { 3318 if ( ! registered_meta_key_exists( 'term', $meta_key, $taxonomy ) ) { 3319 continue; 3320 } 3321 3322 update_term_meta( $type_id, $meta_key, $meta_value ); 3323 } 3324 3325 return true; 3326 } 3327 3328 /** 3329 * Get types for a given BP Taxonomy. 3330 * 3331 * @since 7.0.0 3332 * 3333 * @param string $taxonomy The taxonomy to transform terms in types for. 3334 * @param array $types Existing types to merge with the types found into the database. 3335 * For instance this function is used internally to merge Group/Member 3336 * types registered using code with the ones created by the administrator 3337 * from the Group/Member types Administration screen. If not provided, only 3338 * Types created by the administrator will be returned. 3339 * Optional. 3340 * @return array The types of the given taxonomy. 3341 */ 3342 function bp_get_taxonomy_types( $taxonomy = '', $types = array() ) { 3343 if ( ! $taxonomy ) { 3344 return $types; 3345 } 3346 3347 $db_types = wp_cache_get( $taxonomy, 'bp_object_terms' ); 3348 3349 if ( ! $db_types ) { 3350 $terms = bp_get_terms( 3351 array( 3352 'taxonomy' => $taxonomy, 3353 ) 3354 ); 3355 3356 if ( ! is_array( $terms ) || ! $terms ) { 3357 return $types; 3358 } 3359 3360 $type_metadata = array_keys( get_registered_meta_keys( 'term', $taxonomy ) ); 3361 3362 foreach ( $terms as $term ) { 3363 $type_name = $term->name; 3364 $db_types[ $type_name ] = new stdClass(); 3365 $db_types[ $type_name ]->db_id = $term->term_id; 3366 $db_types[ $type_name ]->labels = array(); 3367 $db_types[ $type_name ]->name = $type_name; 3368 3369 if ( $type_metadata ) { 3370 foreach ( $type_metadata as $meta_key ) { 3371 $type_key = str_replace( 'bp_type_', '', $meta_key ); 3372 if ( in_array( $type_key, array( 'name', 'singular_name' ), true ) ) { 3373 $db_types[ $type_name ]->labels[ $type_key ] = get_term_meta( $term->term_id, $meta_key, true ); 3374 } else { 3375 $db_types[ $type_name ]->{$type_key} = get_term_meta( $term->term_id, $meta_key, true ); 3376 } 3377 } 3378 3379 if ( ! empty( $db_types[ $type_name ]->has_directory ) && empty( $db_types[ $type_name ]->directory_slug ) ) { 3380 $db_types[ $type_name ]->directory_slug = $term->slug; 3381 } 3382 } 3383 } 3384 3385 wp_cache_set( $taxonomy, $db_types, 'bp_object_terms' ); 3386 } 3387 3388 if ( is_array( $db_types ) ) { 3389 foreach ( $db_types as $db_type_name => $db_type ) { 3390 // Override props of registered by code types if customized by the admun user. 3391 if ( isset( $types[ $db_type_name ] ) && isset( $types[ $db_type_name ]->code ) && $types[ $db_type_name ]->code ) { 3392 // Merge Labels. 3393 if ( $db_type->labels ) { 3394 foreach ( $db_type->labels as $key_label => $value_label ) { 3395 if ( '' !== $value_label ) { 3396 $types[ $db_type_name ]->labels[ $key_label ] = $value_label; 3397 } 3398 } 3399 } 3400 3401 // Merge other properties. 3402 foreach ( get_object_vars( $types[ $db_type_name ] ) as $key_prop => $value_prop ) { 3403 if ( 'labels' === $key_prop || 'name' === $key_prop ) { 3404 continue; 3405 } 3406 3407 if ( isset( $db_type->{$key_prop} ) && '' !== $db_type->{$key_prop} ) { 3408 $types[ $db_type_name ]->{$key_prop} = $db_type->{$key_prop}; 3409 } 3410 } 3411 3412 unset( $db_types[ $db_type_name ] ); 3413 } 3414 } 3415 } 3416 3417 return array_merge( $types, (array) $db_types ); 3418 } 3419 3420 /** Email *****************************************************************/ 3421 3422 /** 3423 * Get an BP_Email object for the specified email type. 3424 * 3425 * This function pre-populates the object with the subject, content, and template from the appropriate 3426 * email post type item. It does not replace placeholder tokens in the content with real values. 3427 * 3428 * @since 2.5.0 3429 * 3430 * @param string $email_type Unique identifier for a particular type of email. 3431 * @return BP_Email|WP_Error BP_Email object, or WP_Error if there was a problem. 3432 */ 3433 function bp_get_email( $email_type ) { 3434 $switched = false; 3435 3436 // Switch to the root blog, where the email posts live. 3437 if ( ! bp_is_root_blog() ) { 3438 switch_to_blog( bp_get_root_blog_id() ); 3439 $switched = true; 3440 } 3441 3442 $args = array( 3443 'no_found_rows' => true, 3444 'numberposts' => 1, 3445 'post_status' => 'publish', 3446 'post_type' => bp_get_email_post_type(), 3447 'suppress_filters' => false, 3448 3449 'tax_query' => array( 3450 array( 3451 'field' => 'slug', 3452 'taxonomy' => bp_get_email_tax_type(), 3453 'terms' => $email_type, 3454 ) 3455 ), 3456 ); 3457 3458 /** 3459 * Filters arguments used to find an email post type object. 3460 * 3461 * @since 2.5.0 3462 * 3463 * @param array $args Arguments for get_posts() used to fetch a post object. 3464 * @param string $email_type Unique identifier for a particular type of email. 3465 */ 3466 $args = apply_filters( 'bp_get_email_args', $args, $email_type ); 3467 $post = get_posts( $args ); 3468 if ( ! $post ) { 3469 if ( $switched ) { 3470 restore_current_blog(); 3471 } 3472 3473 return new WP_Error( 'missing_email', __FUNCTION__, array( $email_type, $args ) ); 3474 } 3475 3476 /** 3477 * Filters arguments used to create the BP_Email object. 3478 * 3479 * @since 2.5.0 3480 * 3481 * @param WP_Post $post Post object containing the contents of the email. 3482 * @param string $email_type Unique identifier for a particular type of email. 3483 * @param array $args Arguments used with get_posts() to fetch a post object. 3484 * @param WP_Post $post All posts retrieved by get_posts( $args ). May only contain $post. 3485 */ 3486 $post = apply_filters( 'bp_get_email_post', $post[0], $email_type, $args, $post ); 3487 $email = new BP_Email( $email_type ); 3488 3489 3490 /* 3491 * Set some email properties for convenience. 3492 */ 3493 3494 // Post object (sets subject, content, template). 3495 $email->set_post_object( $post ); 3496 3497 /** 3498 * Filters the BP_Email object returned by bp_get_email(). 3499 * 3500 * @since 2.5.0 3501 * 3502 * @param BP_Email $email An object representing a single email, ready for mailing. 3503 * @param string $email_type Unique identifier for a particular type of email. 3504 * @param array $args Arguments used with get_posts() to fetch a post object. 3505 * @param WP_Post $post All posts retrieved by get_posts( $args ). May only contain $post. 3506 */ 3507 $retval = apply_filters( 'bp_get_email', $email, $email_type, $args, $post ); 3508 3509 if ( $switched ) { 3510 restore_current_blog(); 3511 } 3512 3513 return $retval; 3514 } 3515 3516 /** 3517 * Send email, similar to WordPress' wp_mail(). 3518 * 3519 * A true return value does not automatically mean that the user received the 3520 * email successfully. It just only means that the method used was able to 3521 * process the request without any errors. 3522 * 3523 * @since 2.5.0 3524 * 3525 * @param string $email_type Type of email being sent. 3526 * @param string|array|int|WP_User $to Either a email address, user ID, WP_User object, 3527 * or an array containing the address and name. 3528 * @param array $args { 3529 * Optional. Array of extra parameters. 3530 * 3531 * @type array $tokens Optional. Associative arrays of string replacements for the email. 3532 * } 3533 * @return bool|WP_Error True if the email was sent successfully. Otherwise, a WP_Error object 3534 * describing why the email failed to send. The contents will vary based 3535 * on the email delivery class you are using. 3536 */ 3537 function bp_send_email( $email_type, $to, $args = array() ) { 3538 static $is_default_wpmail = null; 3539 static $wp_html_emails = null; 3540 3541 // Has wp_mail() been filtered to send HTML emails? 3542 if ( is_null( $wp_html_emails ) ) { 3543 /** This filter is documented in wp-includes/pluggable.php */ 3544 $wp_html_emails = apply_filters( 'wp_mail_content_type', 'text/plain' ) === 'text/html'; 3545 } 3546 3547 // Since wp_mail() is a pluggable function, has it been re-defined by another plugin? 3548 if ( is_null( $is_default_wpmail ) ) { 3549 try { 3550 $mirror = new ReflectionFunction( 'wp_mail' ); 3551 $is_default_wpmail = substr( $mirror->getFileName(), -strlen( 'pluggable.php' ) ) === 'pluggable.php'; 3552 } catch ( Exception $e ) { 3553 $is_default_wpmail = true; 3554 } 3555 } 3556 3557 $args = bp_parse_args( 3558 $args, 3559 array( 3560 'tokens' => array(), 3561 ), 3562 'send_email' 3563 ); 3564 3565 /* 3566 * Build the email. 3567 */ 3568 $email = bp_get_email( $email_type ); 3569 if ( is_wp_error( $email ) ) { 3570 return $email; 3571 } 3572 3573 // From, subject, content are set automatically. 3574 if ( 'settings-verify-email-change' === $email_type && isset( $args['tokens']['displayname'] ) ) { 3575 $email->set_to( $to, $args['tokens']['displayname'] ); 3576 // Emails sent to nonmembers will have no recipient.name populated. 3577 } else if ( 'bp-members-invitation' === $email_type ) { 3578 $email->set_to( $to, $to ); 3579 } else { 3580 $email->set_to( $to ); 3581 } 3582 3583 $email->set_tokens( $args['tokens'] ); 3584 3585 /** 3586 * Gives access to an email before it is sent. 3587 * 3588 * @since 2.8.0 3589 * 3590 * @param BP_Email $email The email (object) about to be sent. 3591 * @param string $email_type Type of email being sent. 3592 * @param string|array|int|WP_User $to Either a email address, user ID, WP_User object, 3593 * or an array containing the address and name. 3594 * @param array $args { 3595 * Optional. Array of extra parameters. 3596 * 3597 * @type array $tokens Optional. Associative arrays of string replacements for the email. 3598 * } 3599 */ 3600 do_action_ref_array( 'bp_send_email', array( &$email, $email_type, $to, $args ) ); 3601 3602 $status = $email->validate(); 3603 if ( is_wp_error( $status ) ) { 3604 return $status; 3605 } 3606 3607 /** 3608 * Filter this to skip BP's email handling and instead send everything to wp_mail(). 3609 * 3610 * This is done if wp_mail_content_type() has been configured for HTML, 3611 * or if wp_mail() has been redeclared (it's a pluggable function). 3612 * 3613 * @since 2.5.0 3614 * 3615 * @param bool $use_wp_mail Whether to fallback to the regular wp_mail() function or not. 3616 */ 3617 $must_use_wpmail = apply_filters( 'bp_email_use_wp_mail', $wp_html_emails || ! $is_default_wpmail ); 3618 3619 if ( $must_use_wpmail ) { 3620 $to = $email->get( 'to' ); 3621 3622 return wp_mail( 3623 array_shift( $to )->get_address(), 3624 $email->get( 'subject', 'replace-tokens' ), 3625 $email->get( 'content_plaintext', 'replace-tokens' ) 3626 ); 3627 } 3628 3629 3630 /* 3631 * Send the email. 3632 */ 3633 3634 /** 3635 * Filter the email delivery class. 3636 * 3637 * Defaults to BP_PHPMailer, which as you can guess, implements PHPMailer. 3638 * 3639 * @since 2.5.0 3640 * 3641 * @param string $deliver_class The email delivery class name. 3642 * @param string $email_type Type of email being sent. 3643 * @param array|string $to Array or comma-separated list of email addresses to the email to. 3644 * @param array $args { 3645 * Optional. Array of extra parameters. 3646 * 3647 * @type array $tokens Optional. Associative arrays of string replacements for the email. 3648 * } 3649 */ 3650 $delivery_class = apply_filters( 'bp_send_email_delivery_class', 'BP_PHPMailer', $email_type, $to, $args ); 3651 if ( ! class_exists( $delivery_class ) ) { 3652 return new WP_Error( 'missing_class', 'No class found by that name', $delivery_class ); 3653 } 3654 3655 $delivery = new $delivery_class(); 3656 $status = $delivery->bp_email( $email ); 3657 3658 if ( is_wp_error( $status ) ) { 3659 3660 /** 3661 * Fires after BuddyPress has tried - and failed - to send an email. 3662 * 3663 * @since 2.5.0 3664 * 3665 * @param WP_Error $status A WP_Error object describing why the email failed to send. The contents 3666 * will vary based on the email delivery class you are using. 3667 * @param BP_Email $email The email we tried to send. 3668 */ 3669 do_action( 'bp_send_email_failure', $status, $email ); 3670 3671 } else { 3672 3673 /** 3674 * Fires after BuddyPress has successfully sent an email. 3675 * 3676 * @since 2.5.0 3677 * 3678 * @param bool $status True if the email was sent successfully. 3679 * @param BP_Email $email The email sent. 3680 */ 3681 do_action( 'bp_send_email_success', $status, $email ); 3682 } 3683 3684 return $status; 3685 } 3686 3687 /** 3688 * Return email appearance settings. 3689 * 3690 * @since 2.5.0 3691 * @since 3.0.0 Added "direction" parameter for LTR/RTL email support, and 3692 * "link_text_color" to override that in the email body. 3693 * 3694 * @return array 3695 */ 3696 function bp_email_get_appearance_settings() { 3697 $footer_text = array( 3698 sprintf( 3699 /* translators: 1. Copyright year, 2. Site name */ 3700 _x( '© %1$s %2$s', 'copyright text for email footers', 'buddypress' ), 3701 date_i18n( 'Y' ), 3702 bp_get_option( 'blogname' ) 3703 ) 3704 ); 3705 3706 $privacy_policy_url = get_privacy_policy_url(); 3707 if ( $privacy_policy_url ) { 3708 $footer_text[] = sprintf( 3709 '<a href="%s">%s</a>', 3710 esc_url( $privacy_policy_url ), 3711 esc_html__( 'Privacy Policy', 'buddypress' ) 3712 ); 3713 } 3714 3715 $default_args = array( 3716 'body_bg' => '#FFFFFF', 3717 'body_text_color' => '#555555', 3718 'body_text_size' => 15, 3719 'email_bg' => '#F7F3F0', 3720 'footer_bg' => '#F7F3F0', 3721 'footer_text_color' => '#525252', 3722 'footer_text_size' => 12, 3723 'header_bg' => '#F7F3F0', 3724 'highlight_color' => '#D84800', 3725 'header_text_color' => '#000000', 3726 'header_text_size' => 30, 3727 'direction' => is_rtl() ? 'right' : 'left', 3728 3729 'footer_text' => implode( ' · ', $footer_text ), 3730 ); 3731 3732 $options = bp_parse_args( 3733 bp_get_option( 'bp_email_options', array() ), 3734 $default_args, 3735 'email_appearance_settings' 3736 ); 3737 3738 // Link text colour defaults to the highlight colour. 3739 if ( ! isset( $options['link_text_color'] ) ) { 3740 $options['link_text_color'] = $options['highlight_color']; 3741 } 3742 3743 return $options; 3744 } 3745 3746 /** 3747 * Get the paths to possible templates for the specified email object. 3748 * 3749 * @since 2.5.0 3750 * 3751 * @param WP_Post $object Post to get email template for. 3752 * @return array 3753 */ 3754 function bp_email_get_template( WP_Post $object ) { 3755 $single = "single-{$object->post_type}"; 3756 3757 /** 3758 * Filter the possible template paths for the specified email object. 3759 * 3760 * @since 2.5.0 3761 * 3762 * @param array $value Array of possible template paths. 3763 * @param WP_Post $object WP_Post object. 3764 */ 3765 return apply_filters( 'bp_email_get_template', array( 3766 "assets/emails/{$single}-{$object->post_name}.php", 3767 "{$single}-{$object->post_name}.php", 3768 "{$single}.php", 3769 "assets/emails/{$single}.php", 3770 ), $object ); 3771 } 3772 3773 /** 3774 * Replace all tokens in the input text with appropriate values. 3775 * 3776 * Intended for use with the email system introduced in BuddyPress 2.5.0. 3777 * 3778 * @since 2.5.0 3779 * 3780 * @param string $text Text to replace tokens in. 3781 * @param array $tokens Token names and replacement values for the $text. 3782 * @return string 3783 */ 3784 function bp_core_replace_tokens_in_text( $text, $tokens ) { 3785 $unescaped = array(); 3786 $escaped = array(); 3787 3788 foreach ( $tokens as $token => $value ) { 3789 if ( ! is_string( $value ) && is_callable( $value ) ) { 3790 $value = call_user_func( $value ); 3791 } 3792 3793 // Tokens could be objects or arrays. 3794 if ( ! is_scalar( $value ) ) { 3795 continue; 3796 } 3797 3798 $unescaped[ '{{{' . $token . '}}}' ] = $value; 3799 $escaped[ '{{' . $token . '}}' ] = esc_html( $value ); 3800 } 3801 3802 $text = strtr( $text, $unescaped ); // Do first. 3803 $text = strtr( $text, $escaped ); 3804 3805 /** 3806 * Filters text that has had tokens replaced. 3807 * 3808 * @since 2.5.0 3809 * 3810 * @param string $text 3811 * @param array $tokens Token names and replacement values for the $text. 3812 */ 3813 return apply_filters( 'bp_core_replace_tokens_in_text', $text, $tokens ); 3814 } 3815 3816 /** 3817 * Get a list of emails for populating the email post type. 3818 * 3819 * @since 2.5.1 3820 * @since 10.0.0 Added members-membership-request and 3821 * members-membership-request-rejected email types. 3822 * 3823 * @return array 3824 */ 3825 function bp_email_get_schema() { 3826 3827 /** 3828 * Filters the list of `bp_email_get_schema()` allowing anyone to add/remove emails. 3829 * 3830 * @since 7.0.0 3831 * 3832 * @param array $emails The array of emails schema. 3833 */ 3834 return (array) apply_filters( 'bp_email_get_schema', array( 3835 'core-user-activation' => array( 3836 /* translators: do not remove {} brackets or translate its contents. */ 3837 'post_title' => __( '[{{{site.name}}}] Welcome!', 'buddypress' ), 3838 /* translators: do not remove {} brackets or translate its contents. */ 3839 'post_content' => __( "Welcome to {{site.name}}!\n\nVisit your <a href=\"{{{profile.url}}}\">profile</a>, where you can tell us more about yourself, change your preferences, or make new connections, to get started.\n\nForgot your password? Don't worry, you can reset it with your email address from <a href=\"{{{lostpassword.url}}}\">this page</a> of our site", 'buddypress' ), 3840 /* translators: do not remove {} brackets or translate its contents. */ 3841 'post_excerpt' => __( "Welcome to {{site.name}}!\n\nVisit your profile, where you can tell us more about yourself, change your preferences, or make new connections, to get started: {{{profile.url}}}\n\nForgot your password? Don't worry, you can reset it with your email address from this page of our site: {{{lostpassword.url}}}", 'buddypress' ), 3842 ), 3843 'activity-comment' => array( 3844 /* translators: do not remove {} brackets or translate its contents. */ 3845 'post_title' => __( '[{{{site.name}}}] {{poster.name}} replied to one of your updates', 'buddypress' ), 3846 /* translators: do not remove {} brackets or translate its contents. */ 3847 'post_content' => __( "{{poster.name}} replied to one of your updates:\n\n<blockquote>"{{usermessage}}"</blockquote>\n\n<a href=\"{{{thread.url}}}\">Go to the discussion</a> to reply or catch up on the conversation.", 'buddypress' ), 3848 /* translators: do not remove {} brackets or translate its contents. */ 3849 'post_excerpt' => __( "{{poster.name}} replied to one of your updates:\n\n\"{{usermessage}}\"\n\nGo to the discussion to reply or catch up on the conversation: {{{thread.url}}}", 'buddypress' ), 3850 ), 3851 'activity-comment-author' => array( 3852 /* translators: do not remove {} brackets or translate its contents. */ 3853 'post_title' => __( '[{{{site.name}}}] {{poster.name}} replied to one of your comments', 'buddypress' ), 3854 /* translators: do not remove {} brackets or translate its contents. */ 3855 'post_content' => __( "{{poster.name}} replied to one of your comments:\n\n<blockquote>"{{usermessage}}"</blockquote>\n\n<a href=\"{{{thread.url}}}\">Go to the discussion</a> to reply or catch up on the conversation.", 'buddypress' ), 3856 /* translators: do not remove {} brackets or translate its contents. */ 3857 'post_excerpt' => __( "{{poster.name}} replied to one of your comments:\n\n\"{{usermessage}}\"\n\nGo to the discussion to reply or catch up on the conversation: {{{thread.url}}}", 'buddypress' ), 3858 ), 3859 'activity-at-message' => array( 3860 /* translators: do not remove {} brackets or translate its contents. */ 3861 'post_title' => __( '[{{{site.name}}}] {{poster.name}} mentioned you in a status update', 'buddypress' ), 3862 /* translators: do not remove {} brackets or translate its contents. */ 3863 'post_content' => __( "{{poster.name}} mentioned you in a status update:\n\n<blockquote>"{{usermessage}}"</blockquote>\n\n<a href=\"{{{mentioned.url}}}\">Go to the discussion</a> to reply or catch up on the conversation.", 'buddypress' ), 3864 /* translators: do not remove {} brackets or translate its contents. */ 3865 'post_excerpt' => __( "{{poster.name}} mentioned you in a status update:\n\n\"{{usermessage}}\"\n\nGo to the discussion to reply or catch up on the conversation: {{{mentioned.url}}}", 'buddypress' ), 3866 ), 3867 'groups-at-message' => array( 3868 /* translators: do not remove {} brackets or translate its contents. */ 3869 'post_title' => __( '[{{{site.name}}}] {{poster.name}} mentioned you in an update', 'buddypress' ), 3870 /* translators: do not remove {} brackets or translate its contents. */ 3871 'post_content' => __( "{{poster.name}} mentioned you in the group \"{{group.name}}\":\n\n<blockquote>"{{usermessage}}"</blockquote>\n\n<a href=\"{{{mentioned.url}}}\">Go to the discussion</a> to reply or catch up on the conversation.", 'buddypress' ), 3872 /* translators: do not remove {} brackets or translate its contents. */ 3873 'post_excerpt' => __( "{{poster.name}} mentioned you in the group \"{{group.name}}\":\n\n\"{{usermessage}}\"\n\nGo to the discussion to reply or catch up on the conversation: {{{mentioned.url}}}", 'buddypress' ), 3874 ), 3875 'core-user-registration' => array( 3876 /* translators: do not remove {} brackets or translate its contents. */ 3877 'post_title' => __( '[{{{site.name}}}] Activate your account', 'buddypress' ), 3878 /* translators: do not remove {} brackets or translate its contents. */ 3879 'post_content' => __( "Thanks for registering!\n\nTo complete the activation of your account, go to the following link and click on the <strong>Activate</strong> button:\n<a href=\"{{{activate.url}}}\">{{{activate.url}}}</a>\n\nIf the 'Activation Key' field is empty, copy and paste the following into the field - {{key}}", 'buddypress' ), 3880 /* translators: do not remove {} brackets or translate its contents. */ 3881 'post_excerpt' => __( "Thanks for registering!\n\nTo complete the activation of your account, go to the following link and click on the 'Activate' button: {{{activate.url}}}\n\nIf the 'Activation Key' field is empty, copy and paste the following into the field - {{key}}", 'buddypress' ) 3882 ), 3883 'core-user-registration-with-blog' => array( 3884 /* translators: do not remove {} brackets or translate its contents. */ 3885 'post_title' => __( '[{{{site.name}}}] Activate {{{user-site.url}}}', 'buddypress' ), 3886 /* translators: do not remove {} brackets or translate its contents. */ 3887 'post_content' => __( "Thanks for registering!\n\nTo complete the activation of your account and site, go to the following link: <a href=\"{{{activate-site.url}}}\">{{{activate-site.url}}}</a>.\n\nAfter you activate, you can visit your site at <a href=\"{{{user-site.url}}}\">{{{user-site.url}}}</a>.", 'buddypress' ), 3888 /* translators: do not remove {} brackets or translate its contents. */ 3889 'post_excerpt' => __( "Thanks for registering!\n\nTo complete the activation of your account and site, go to the following link: {{{activate-site.url}}}\n\nAfter you activate, you can visit your site at {{{user-site.url}}}.", 'buddypress' ), 3890 'args' => array( 3891 'multisite' => true, 3892 ), 3893 ), 3894 'friends-request' => array( 3895 /* translators: do not remove {} brackets or translate its contents. */ 3896 'post_title' => __( '[{{{site.name}}}] New friendship request from {{initiator.name}}', 'buddypress' ), 3897 /* translators: do not remove {} brackets or translate its contents. */ 3898 'post_content' => __( "<a href=\"{{{initiator.url}}}\">{{initiator.name}}</a> wants to add you as a friend.\n\nTo accept this request and manage all of your pending requests, visit: <a href=\"{{{friend-requests.url}}}\">{{{friend-requests.url}}}</a>", 'buddypress' ), 3899 /* translators: do not remove {} brackets or translate its contents. */ 3900 'post_excerpt' => __( "{{initiator.name}} wants to add you as a friend.\n\nTo accept this request and manage all of your pending requests, visit: {{{friend-requests.url}}}\n\nTo view {{initiator.name}}'s profile, visit: {{{initiator.url}}}", 'buddypress' ), 3901 ), 3902 'friends-request-accepted' => array( 3903 /* translators: do not remove {} brackets or translate its contents. */ 3904 'post_title' => __( '[{{{site.name}}}] {{friend.name}} accepted your friendship request', 'buddypress' ), 3905 /* translators: do not remove {} brackets or translate its contents. */ 3906 'post_content' => __( "<a href=\"{{{friendship.url}}}\">{{friend.name}}</a> accepted your friend request.", 'buddypress' ), 3907 /* translators: do not remove {} brackets or translate its contents. */ 3908 'post_excerpt' => __( "{{friend.name}} accepted your friend request.\n\nTo learn more about them, visit their profile: {{{friendship.url}}}", 'buddypress' ), 3909 ), 3910 'groups-details-updated' => array( 3911 /* translators: do not remove {} brackets or translate its contents. */ 3912 'post_title' => __( '[{{{site.name}}}] Group details updated', 'buddypress' ), 3913 /* translators: do not remove {} brackets or translate its contents. */ 3914 'post_content' => __( "Group details for the group "<a href=\"{{{group.url}}}\">{{group.name}}</a>" were updated:\n<blockquote>{{changed_text}}</blockquote>", 'buddypress' ), 3915 /* translators: do not remove {} brackets or translate its contents. */ 3916 'post_excerpt' => __( "Group details for the group \"{{group.name}}\" were updated:\n\n{{changed_text}}\n\nTo view the group, visit: {{{group.url}}}", 'buddypress' ), 3917 ), 3918 'groups-invitation' => array( 3919 /* translators: do not remove {} brackets or translate its contents. */ 3920 'post_title' => __( '[{{{site.name}}}] You have an invitation to the group: "{{group.name}}"', 'buddypress' ), 3921 /* translators: do not remove {} brackets or translate its contents. */ 3922 'post_content' => __( "<a href=\"{{{inviter.url}}}\">{{inviter.name}}</a> has invited you to join the group: "{{group.name}}".\n\n{{invite.message}}\n\n<a href=\"{{{invites.url}}}\">Go here to accept your invitation</a> or <a href=\"{{{group.url}}}\">visit the group</a> to learn more.", 'buddypress' ), 3923 /* translators: do not remove {} brackets or translate its contents. */ 3924 'post_excerpt' => __( "{{inviter.name}} has invited you to join the group: \"{{group.name}}\".\n\n{{invite.message}}\n\nTo accept your invitation, visit: {{{invites.url}}}\n\nTo learn more about the group, visit: {{{group.url}}}.\nTo view {{inviter.name}}'s profile, visit: {{{inviter.url}}}", 'buddypress' ), 3925 ), 3926 'groups-member-promoted' => array( 3927 /* translators: do not remove {} brackets or translate its contents. */ 3928 'post_title' => __( '[{{{site.name}}}] You have been promoted in the group: "{{group.name}}"', 'buddypress' ), 3929 /* translators: do not remove {} brackets or translate its contents. */ 3930 'post_content' => __( "You have been promoted to <b>{{promoted_to}}</b> in the group "<a href=\"{{{group.url}}}\">{{group.name}}</a>".", 'buddypress' ), 3931 /* translators: do not remove {} brackets or translate its contents. */ 3932 'post_excerpt' => __( "You have been promoted to {{promoted_to}} in the group: \"{{group.name}}\".\n\nTo visit the group, go to: {{{group.url}}}", 'buddypress' ), 3933 ), 3934 'groups-membership-request' => array( 3935 /* translators: do not remove {} brackets or translate its contents. */ 3936 'post_title' => __( '[{{{site.name}}}] Membership request for group: {{group.name}}', 'buddypress' ), 3937 /* translators: do not remove {} brackets or translate its contents. */ 3938 'post_content' => __( "<a href=\"{{{profile.url}}}\">{{requesting-user.name}}</a> wants to join the group "{{group.name}}".\n {{request.message}}\n As you are an administrator of this group, you must either accept or reject the membership request.\n\n<a href=\"{{{group-requests.url}}}\">Go here to manage this</a> and all other pending requests.", 'buddypress' ), 3939 /* translators: do not remove {} brackets or translate its contents. */ 3940 'post_excerpt' => __( "{{requesting-user.name}} wants to join the group \"{{group.name}}\". As you are the administrator of this group, you must either accept or reject the membership request.\n\nTo manage this and all other pending requests, visit: {{{group-requests.url}}}\n\nTo view {{requesting-user.name}}'s profile, visit: {{{profile.url}}}", 'buddypress' ), 3941 ), 3942 'messages-unread' => array( 3943 /* translators: do not remove {} brackets or translate its contents. */ 3944 'post_title' => __( '[{{{site.name}}}] New message from {{sender.name}}', 'buddypress' ), 3945 /* translators: do not remove {} brackets or translate its contents. */ 3946 'post_content' => __( "{{sender.name}} sent you a new message: "{{usersubject}}"\n\n<blockquote>"{{usermessage}}"</blockquote>\n\n<a href=\"{{{message.url}}}\">Go to the discussion</a> to reply or catch up on the conversation.", 'buddypress' ), 3947 /* translators: do not remove {} brackets or translate its contents. */ 3948 'post_excerpt' => __( "{{sender.name}} sent you a new message: \"{{usersubject}}\"\n\n\"{{usermessage}}\"\n\nGo to the discussion to reply or catch up on the conversation: {{{message.url}}}", 'buddypress' ), 3949 ), 3950 'settings-verify-email-change' => array( 3951 /* translators: do not remove {} brackets or translate its contents. */ 3952 'post_title' => __( '[{{{site.name}}}] Verify your new email address', 'buddypress' ), 3953 /* translators: do not remove {} brackets or translate its contents. */ 3954 'post_content' => __( "You recently changed the email address associated with your account on {{site.name}} to {{user.email}}. If this is correct, <a href=\"{{{verify.url}}}\">go here to confirm the change</a>.\n\nOtherwise, you can safely ignore and delete this email if you have changed your mind, or if you think you have received this email in error.", 'buddypress' ), 3955 /* translators: do not remove {} brackets or translate its contents. */ 3956 'post_excerpt' => __( "You recently changed the email address associated with your account on {{site.name}} to {{user.email}}. If this is correct, go to the following link to confirm the change: {{{verify.url}}}\n\nOtherwise, you can safely ignore and delete this email if you have changed your mind, or if you think you have received this email in error.", 'buddypress' ), 3957 ), 3958 'groups-membership-request-accepted' => array( 3959 /* translators: do not remove {} brackets or translate its contents. */ 3960 'post_title' => __( '[{{{site.name}}}] Membership request for group "{{group.name}}" accepted', 'buddypress' ), 3961 /* translators: do not remove {} brackets or translate its contents. */ 3962 'post_content' => __( "Your membership request for the group "<a href=\"{{{group.url}}}\">{{group.name}}</a>" has been accepted.", 'buddypress' ), 3963 /* translators: do not remove {} brackets or translate its contents. */ 3964 'post_excerpt' => __( "Your membership request for the group \"{{group.name}}\" has been accepted.\n\nTo view the group, visit: {{{group.url}}}", 'buddypress' ), 3965 ), 3966 'groups-membership-request-rejected' => array( 3967 /* translators: do not remove {} brackets or translate its contents. */ 3968 'post_title' => __( '[{{{site.name}}}] Membership request for group "{{group.name}}" rejected', 'buddypress' ), 3969 /* translators: do not remove {} brackets or translate its contents. */ 3970 'post_content' => __( "Your membership request for the group "<a href=\"{{{group.url}}}\">{{group.name}}</a>" has been rejected.", 'buddypress' ), 3971 /* translators: do not remove {} brackets or translate its contents. */ 3972 'post_excerpt' => __( "Your membership request for the group \"{{group.name}}\" has been rejected.\n\nTo request membership again, visit: {{{group.url}}}", 'buddypress' ), 3973 ), 3974 'groups-membership-request-accepted-by-admin' => array( 3975 /* translators: do not remove {} brackets or translate its contents. */ 3976 'post_title' => __( '[{{{site.name}}}] Membership request for group "{{group.name}}" accepted', 'buddypress' ), 3977 /* translators: do not remove {} brackets or translate its contents. */ 3978 'post_content' => __( "An administrator accepted an invitation to join "<a href=\"{{{group.url}}}\">{{group.name}}</a>" on your behalf.\n\nIf you disagree with this, you can leave the group at anytime visiting your <a href=\"{{{leave-group.url}}}\">groups memberships page</a>.", 'buddypress' ), 3979 /* translators: do not remove {} brackets or translate its contents. */ 3980 'post_excerpt' => __( "An administrator accepted an invitation to join \"{{group.name}}\" on your behalf.\n\nIf you disagree with this, you can leave the group at anytime visiting your groups memberships page: {{{leave-group.url}}}", 'buddypress' ), 3981 ), 3982 'groups-membership-request-rejected-by-admin' => array( 3983 /* translators: do not remove {} brackets or translate its contents. */ 3984 'post_title' => __( '[{{{site.name}}}] Membership request for group "{{group.name}}" rejected', 'buddypress' ), 3985 /* translators: do not remove {} brackets or translate its contents. */ 3986 'post_content' => __( "An administrator rejected an invitation to join "<a href=\"{{{group.url}}}\">{{group.name}}</a>" on your behalf.\n\nIf you disagree with this, please contact the site administrator.", 'buddypress' ), 3987 /* translators: do not remove {} brackets or translate its contents. */ 3988 'post_excerpt' => __( "An administrator rejected an invitation to join \"{{group.name}}\" on your behalf.\n\nIf you disagree with this, please contact the site administrator.", 'buddypress' ), 3989 ), 3990 'bp-members-invitation' => array( 3991 /* translators: do not remove {} brackets or translate its contents. */ 3992 'post_title' => __( '{{inviter.name}} has invited you to join {{site.name}}', 'buddypress' ), 3993 /* translators: do not remove {} brackets or translate its contents. */ 3994 'post_content' => __( "<a href=\"{{{inviter.url}}}\">{{inviter.name}}</a> has invited you to join the site: "{{site.name}}".\n\n{{usermessage}}\n\n<a href=\"{{{invite.accept_url}}}\">Accept your invitation</a> or <a href=\"{{{site.url}}}\">visit the site</a> to learn more.", 'buddypress' ), 3995 /* translators: do not remove {} brackets or translate its contents. */ 3996 'post_excerpt' => __( "{{inviter.name}} has invited you to join the site \"{{site.name}}\".\n\n{{usermessage}}\n\nTo accept your invitation, visit: {{{invite.accept_url}}}\n\nTo learn more about the site, visit: {{{site.url}}}.\nTo view {{inviter.name}}'s profile, visit: {{{inviter.url}}}", 'buddypress' ), 3997 ), 3998 'members-membership-request' => array( 3999 /* translators: do not remove {} brackets or translate its contents. */ 4000 'post_title' => __( '{{requesting-user.user_login}} would like to join {{site.name}}', 'buddypress' ), 4001 /* translators: do not remove {} brackets or translate its contents. */ 4002 'post_content' => __( "{{requesting-user.user_login}} would like to join the site: "{{site.name}}".\n\n<a href=\"{{{manage.url}}}\">Manage the request</a>.", 'buddypress' ), 4003 /* translators: do not remove {} brackets or translate its contents. */ 4004 'post_excerpt' => __( "{{requesting-user.user_login}} would like to join the site \"{{site.name}}\".\n\nTo manage the request, visit: {{{manage.url}}}.", 'buddypress' ), 4005 ), 4006 'members-membership-request-rejected' => array( 4007 /* translators: do not remove {} brackets or translate its contents. */ 4008 'post_title' => __( 'Your request to join {{site.name}} has been declined', 'buddypress' ), 4009 /* translators: do not remove {} brackets or translate its contents. */ 4010 'post_content' => __( "Sorry, your request to join the site "{{site.name}}" has been declined.", 'buddypress' ), 4011 /* translators: do not remove {} brackets or translate its contents. */ 4012 'post_excerpt' => __( "Sorry, your request to join the site \"{{site.name}}\" has been declined.", 'buddypress' ), 4013 ), 4014 ) ); 4015 } 4016 4017 /** 4018 * Get a list of emails for populating email type taxonomy terms. 4019 * 4020 * @since 2.5.1 4021 * @since 2.7.0 $field argument added. 4022 * 4023 * @param string $field Optional; defaults to "description" for backwards compatibility. Other values: "all". 4024 * @return array { 4025 * The array of email types and their schema. 4026 * 4027 * @type string $description The description of the action which causes this to trigger. 4028 * @type array $unsubscribe { 4029 * Replacing this with false indicates that a user cannot unsubscribe from this type. 4030 * 4031 * @type string $meta_key The meta_key used to toggle the email setting for this notification. 4032 * @type string $message The message shown when the user has successfully unsubscribed. 4033 * } 4034 */ 4035 function bp_email_get_type_schema( $field = 'description' ) { 4036 $activity_comment = array( 4037 'description' => __( 'A member has replied to an activity update that the recipient posted.', 'buddypress' ), 4038 'named_salutation' => true, 4039 'unsubscribe' => array( 4040 'meta_key' => 'notification_activity_new_reply', 4041 'message' => __( 'You will no longer receive emails when someone replies to an update or comment you posted.', 'buddypress' ), 4042 ), 4043 ); 4044 4045 $activity_comment_author = array( 4046 'description' => __( 'A member has replied to a comment on an activity update that the recipient posted.', 'buddypress' ), 4047 'named_salutation' => true, 4048 'unsubscribe' => array( 4049 'meta_key' => 'notification_activity_new_reply', 4050 'message' => __( 'You will no longer receive emails when someone replies to an update or comment you posted.', 'buddypress' ), 4051 ), 4052 ); 4053 4054 $activity_at_message = array( 4055 'description' => __( 'Recipient was mentioned in an activity update.', 'buddypress' ), 4056 'named_salutation' => true, 4057 'unsubscribe' => array( 4058 'meta_key' => 'notification_activity_new_mention', 4059 'message' => __( 'You will no longer receive emails when someone mentions you in an update.', 'buddypress' ), 4060 ), 4061 ); 4062 4063 $groups_at_message = array( 4064 'description' => __( 'Recipient was mentioned in a group activity update.', 'buddypress' ), 4065 'named_salutation' => true, 4066 'unsubscribe' => array( 4067 'meta_key' => 'notification_activity_new_mention', 4068 'message' => __( 'You will no longer receive emails when someone mentions you in an update.', 'buddypress' ), 4069 ), 4070 ); 4071 4072 $core_user_registration = array( 4073 'description' => __( 'Recipient has registered for an account.', 'buddypress' ), 4074 'named_salutation' => true, 4075 'unsubscribe' => false, 4076 ); 4077 4078 $core_user_registration_with_blog = array( 4079 'description' => __( 'Recipient has registered for an account and site.', 'buddypress' ), 4080 'named_salutation' => true, 4081 'unsubscribe' => false, 4082 ); 4083 4084 $friends_request = array( 4085 'description' => __( 'A member has sent a friend request to the recipient.', 'buddypress' ), 4086 'named_salutation' => true, 4087 'unsubscribe' => array( 4088 'meta_key' => 'notification_friends_friendship_request', 4089 'message' => __( 'You will no longer receive emails when someone sends you a friend request.', 'buddypress' ), 4090 ), 4091 ); 4092 4093 $friends_request_accepted = array( 4094 'description' => __( 'Recipient has had a friend request accepted by a member.', 'buddypress' ), 4095 'named_salutation' => true, 4096 'unsubscribe' => array( 4097 'meta_key' => 'notification_friends_friendship_accepted', 4098 'message' => __( 'You will no longer receive emails when someone accepts your friendship request.', 'buddypress' ), 4099 ), 4100 ); 4101 4102 $groups_details_updated = array( 4103 'description' => __( "A group's details were updated.", 'buddypress' ), 4104 'named_salutation' => true, 4105 'unsubscribe' => array( 4106 'meta_key' => 'notification_groups_group_updated', 4107 'message' => __( 'You will no longer receive emails when one of your groups is updated.', 'buddypress' ), 4108 ), 4109 ); 4110 4111 $groups_invitation = array( 4112 'description' => __( 'A member has sent a group invitation to the recipient.', 'buddypress' ), 4113 'named_salutation' => true, 4114 'unsubscribe' => array( 4115 'meta_key' => 'notification_groups_invite', 4116 'message' => __( 'You will no longer receive emails when you are invited to join a group.', 'buddypress' ), 4117 ), 4118 ); 4119 4120 $groups_member_promoted = array( 4121 'description' => __( "Recipient's status within a group has changed.", 'buddypress' ), 4122 'named_salutation' => true, 4123 'unsubscribe' => array( 4124 'meta_key' => 'notification_groups_admin_promotion', 4125 'message' => __( 'You will no longer receive emails when you have been promoted in a group.', 'buddypress' ), 4126 ), 4127 ); 4128 4129 $groups_membership_request = array( 4130 'description' => __( 'A member has requested permission to join a group.', 'buddypress' ), 4131 'named_salutation' => true, 4132 'unsubscribe' => array( 4133 'meta_key' => 'notification_groups_membership_request', 4134 'message' => __( 'You will no longer receive emails when someone requests to be a member of your group.', 'buddypress' ), 4135 ), 4136 ); 4137 4138 $messages_unread = array( 4139 'description' => __( 'Recipient has received a private message.', 'buddypress' ), 4140 'named_salutation' => true, 4141 'unsubscribe' => array( 4142 'meta_key' => 'notification_messages_new_message', 4143 'message' => __( 'You will no longer receive emails when someone sends you a message.', 'buddypress' ), 4144 ), 4145 ); 4146 4147 $settings_verify_email_change = array( 4148 'description' => __( 'Recipient has changed their email address.', 'buddypress' ), 4149 'named_salutation' => true, 4150 'unsubscribe' => false, 4151 ); 4152 4153 $groups_membership_request_accepted = array( 4154 'description' => __( 'Recipient had requested to join a group, which was accepted.', 'buddypress' ), 4155 'named_salutation' => true, 4156 'unsubscribe' => array( 4157 'meta_key' => 'notification_membership_request_completed', 4158 'message' => __( 'You will no longer receive emails when your request to join a group has been accepted or denied.', 'buddypress' ), 4159 ), 4160 ); 4161 4162 $groups_membership_request_rejected = array( 4163 'description' => __( 'Recipient had requested to join a group, which was rejected.', 'buddypress' ), 4164 'named_salutation' => true, 4165 'unsubscribe' => array( 4166 'meta_key' => 'notification_membership_request_completed', 4167 'message' => __( 'You will no longer receive emails when your request to join a group has been accepted or denied.', 'buddypress' ), 4168 ), 4169 ); 4170 4171 $groups_membership_request_accepted_by_admin = array( 4172 'description' => __( 'Recipient had requested to join a group, which was accepted by admin.', 'buddypress' ), 4173 'named_salutation' => true, 4174 'unsubscribe' => false, 4175 ); 4176 4177 $groups_membership_request_rejected_by_admin = array( 4178 'description' => __( 'Recipient had requested to join a group, which was rejected by admin.', 'buddypress' ), 4179 'named_salutation' => true, 4180 'unsubscribe' => false, 4181 ); 4182 4183 $core_user_activation = array( 4184 'description' => __( 'Recipient has successfully activated an account.', 'buddypress' ), 4185 'named_salutation' => true, 4186 'unsubscribe' => false, 4187 ); 4188 4189 $members_invitation = array( 4190 'description' => __( 'A site member has sent a site invitation to the recipient.', 'buddypress' ), 4191 'named_salutation' => false, 4192 'unsubscribe' => array( 4193 'meta_key' => 'notification_bp_members_invite', 4194 'message' => __( 'You will no longer receive emails when you are invited to join this site.', 'buddypress' ), 4195 ), 4196 ); 4197 4198 $members_membership_request = array( 4199 'description' => __( 'Someone has requested membership on this site.', 'buddypress' ), 4200 'named_salutation' => true, 4201 'unsubscribe' => array( 4202 'meta_key' => 'notification_members_membership_request', 4203 'message' => __( 'You will no longer receive emails when people submit requests to join this site.', 'buddypress' ), 4204 ), 4205 ); 4206 4207 $members_membership_request_rejected = array( 4208 'description' => __( 'A site membership request has been rejected.', 'buddypress' ), 4209 'named_salutation' => false, 4210 'unsubscribe' => false, 4211 ); 4212 4213 $types = array( 4214 'activity-comment' => $activity_comment, 4215 'activity-comment-author' => $activity_comment_author, 4216 'activity-at-message' => $activity_at_message, 4217 'groups-at-message' => $groups_at_message, 4218 'core-user-registration' => $core_user_registration, 4219 'core-user-registration-with-blog' => $core_user_registration_with_blog, 4220 'friends-request' => $friends_request, 4221 'friends-request-accepted' => $friends_request_accepted, 4222 'groups-details-updated' => $groups_details_updated, 4223 'groups-invitation' => $groups_invitation, 4224 'groups-member-promoted' => $groups_member_promoted, 4225 'groups-membership-request' => $groups_membership_request, 4226 'messages-unread' => $messages_unread, 4227 'settings-verify-email-change' => $settings_verify_email_change, 4228 'groups-membership-request-accepted' => $groups_membership_request_accepted, 4229 'groups-membership-request-rejected' => $groups_membership_request_rejected, 4230 'core-user-activation' => $core_user_activation, 4231 'bp-members-invitation' => $members_invitation, 4232 'members-membership-request' => $members_membership_request, 4233 'members-membership-request-rejected' => $members_membership_request_rejected, 4234 'groups-membership-request-accepted-by-admin' => $groups_membership_request_accepted_by_admin, 4235 'groups-membership-request-rejected-by-admin' => $groups_membership_request_rejected_by_admin, 4236 ); 4237 4238 if ( $field !== 'all' ) { 4239 return wp_list_pluck( $types, $field ); 4240 } else { 4241 return $types; 4242 } 4243 } 4244 4245 /** 4246 * Handles unsubscribing user from notification emails. 4247 * 4248 * @since 2.7.0 4249 */ 4250 function bp_email_unsubscribe_handler() { 4251 $emails = bp_email_get_unsubscribe_type_schema(); 4252 $raw_email_type = ! empty( $_GET['nt'] ) ? $_GET['nt'] : ''; 4253 $raw_hash = ! empty( $_GET['nh'] ) ? $_GET['nh'] : ''; 4254 $raw_user_id = ! empty( $_GET['uid'] ) ? absint( $_GET['uid'] ) : 0; 4255 $raw_user_email = ! empty( $_GET['uem'] ) ? $_GET['uem'] : ''; 4256 $raw_member_id = ! empty( $_GET['mid'] ) ? absint( $_GET['mid'] ) : 0; 4257 $redirect_to = ''; 4258 4259 $new_hash = ''; 4260 if ( ! empty( $raw_user_id ) ) { 4261 $new_hash = hash_hmac( 'sha1', "{$raw_email_type}:{$raw_user_id}", bp_email_get_salt() ); 4262 } else if ( ! empty( $raw_user_email ) ) { 4263 $new_hash = hash_hmac( 'sha1', "{$raw_email_type}:{$raw_user_email}", bp_email_get_salt() ); 4264 } 4265 4266 // Check required values. 4267 if ( ( ! $raw_user_id && ! $raw_user_email ) || ! $raw_email_type || ! $raw_hash || ! array_key_exists( $raw_email_type, $emails ) ) { 4268 $redirect_to = wp_login_url(); 4269 $result_msg = __( 'Something has gone wrong.', 'buddypress' ); 4270 $unsub_msg = __( 'Please log in and go to your settings to unsubscribe from notification emails.', 'buddypress' ); 4271 4272 // Check valid hash. 4273 } elseif ( ! hash_equals( $new_hash, $raw_hash ) ) { 4274 $redirect_to = wp_login_url(); 4275 $result_msg = __( 'Something has gone wrong.', 'buddypress' ); 4276 $unsub_msg = __( 'Please log in and go to your settings to unsubscribe from notification emails.', 'buddypress' ); 4277 4278 // Don't let authenticated users unsubscribe other users' email notifications. 4279 } elseif ( is_user_logged_in() && get_current_user_id() !== $raw_user_id ) { 4280 $result_msg = __( 'Something has gone wrong.', 'buddypress' ); 4281 $unsub_msg = __( 'Please go to your notifications settings to unsubscribe from emails.', 'buddypress' ); 4282 4283 if ( bp_is_active( 'settings' ) ) { 4284 $redirect_to = sprintf( 4285 '%s%s/notifications/', 4286 bp_core_get_user_domain( get_current_user_id() ), 4287 bp_get_settings_slug() 4288 ); 4289 } else { 4290 $redirect_to = bp_core_get_user_domain( get_current_user_id() ); 4291 } 4292 4293 // This is an unsubscribe request from a nonmember. 4294 } else if ( $raw_user_email ) { 4295 // Unsubscribe. 4296 if ( bp_user_has_opted_out() ) { 4297 $result_msg = $emails[ $raw_email_type ]['unsubscribe']['message']; 4298 $unsub_msg = __( 'You have already unsubscribed from all communication from this site.', 'buddypress' ); 4299 } else { 4300 $optout_args = array( 4301 'email_address' => $raw_user_email, 4302 'user_id' => $raw_member_id, 4303 'email_type' => $raw_email_type, 4304 'date_modified' => bp_core_current_time(), 4305 ); 4306 bp_add_optout( $optout_args ); 4307 $result_msg = $emails[ $raw_email_type ]['unsubscribe']['message']; 4308 $unsub_msg = __( 'You have been unsubscribed.', 'buddypress' ); 4309 } 4310 4311 // This is an unsubscribe request from a current member. 4312 } else { 4313 if ( bp_is_active( 'settings' ) ) { 4314 $redirect_to = sprintf( 4315 '%s%s/notifications/', 4316 bp_core_get_user_domain( $raw_user_id ), 4317 bp_get_settings_slug() 4318 ); 4319 } else { 4320 $redirect_to = bp_core_get_user_domain( $raw_user_id ); 4321 } 4322 4323 // Unsubscribe. 4324 $meta_key = $emails[ $raw_email_type ]['unsubscribe']['meta_key']; 4325 bp_update_user_meta( $raw_user_id, $meta_key, 'no' ); 4326 4327 $result_msg = $emails[ $raw_email_type ]['unsubscribe']['message']; 4328 $unsub_msg = __( 'You can change this or any other email notification preferences in your email settings.', 'buddypress' ); 4329 } 4330 4331 if ( $raw_user_id && $redirect_to ) { 4332 $message = sprintf( 4333 '%1$s <a href="%2$s">%3$s</a>', 4334 $result_msg, 4335 esc_url( $redirect_to ), 4336 esc_html( $unsub_msg ) 4337 ); 4338 4339 // Template notices are only displayed on BP pages. 4340 bp_core_add_message( $message ); 4341 bp_core_redirect( bp_core_get_user_domain( $raw_user_id ) ); 4342 4343 exit; 4344 } else { 4345 wp_die( 4346 sprintf( '%1$s %2$s', esc_html( $unsub_msg ), esc_html( $result_msg ) ), 4347 esc_html( $unsub_msg ), 4348 array( 4349 'link_url' => home_url(), 4350 'link_text' => __( 'Go to website\'s home page.', 'buddypress' ), 4351 ) 4352 ); 4353 } 4354 } 4355 4356 /** 4357 * Creates unsubscribe link for notification emails. 4358 * 4359 * @since 2.7.0 4360 * 4361 * @param string $redirect_to The URL to which the unsubscribe query string is appended. 4362 * @param array $args { 4363 * Used to build unsubscribe query string. 4364 * 4365 * @type string $notification_type Which notification type is being sent. 4366 * @type string $user_id The ID of the user to whom the notification is sent. 4367 * @type string $redirect_to Optional. The url to which the user will be redirected. Default is the activity directory. 4368 * @type string $email Optional. The email address of the user to whom the notification is sent. 4369 * } 4370 * @return string The unsubscribe link. 4371 */ 4372 function bp_email_get_unsubscribe_link( $args ) { 4373 $emails = bp_email_get_unsubscribe_type_schema(); 4374 4375 if ( empty( $args['notification_type'] ) || ! array_key_exists( $args['notification_type'], $emails ) ) { 4376 return wp_login_url(); 4377 } 4378 4379 $email_type = $args['notification_type']; 4380 $redirect_to = ! empty( $args['redirect_to'] ) ? $args['redirect_to'] : site_url(); 4381 $user_id = (int) $args['user_id']; 4382 4383 // Bail out if the activity type is not un-unsubscribable. 4384 if ( empty( $emails[ $email_type ]['unsubscribe'] ) ) { 4385 return ''; 4386 } 4387 4388 $link = ''; 4389 // Case where the recipient is a member of the site. 4390 if ( ! empty( $user_id ) ) { 4391 $link = add_query_arg( 4392 array( 4393 'action' => 'unsubscribe', 4394 'nh' => hash_hmac( 'sha1', "{$email_type}:{$user_id}", bp_email_get_salt() ), 4395 'nt' => $args['notification_type'], 4396 'uid' => $user_id, 4397 ), 4398 $redirect_to 4399 ); 4400 4401 // Case where the recipient is not a member of the site. 4402 } else if ( ! empty( $args['email_address'] ) ) { 4403 $email_address = $args['email_address']; 4404 $member_id = (int) $args['member_id']; 4405 $link = add_query_arg( 4406 array( 4407 'action' => 'unsubscribe', 4408 'nh' => hash_hmac( 'sha1', "{$email_type}:{$email_address}", bp_email_get_salt() ), 4409 'nt' => $args['notification_type'], 4410 'mid' => $member_id, 4411 'uem' => $email_address, 4412 ), 4413 $redirect_to 4414 ); 4415 } 4416 4417 /** 4418 * Filters the unsubscribe link. 4419 * 4420 * @since 2.7.0 4421 */ 4422 return apply_filters( 'bp_email_get_link', $link, $redirect_to, $args ); 4423 } 4424 4425 /** 4426 * Get a persistent salt for email unsubscribe links. 4427 * 4428 * @since 2.7.0 4429 * 4430 * @return string|null Returns null if value isn't set, otherwise string. 4431 */ 4432 function bp_email_get_salt() { 4433 return bp_get_option( 'bp-emails-unsubscribe-salt', null ); 4434 } 4435 4436 /** 4437 * Get a list of emails for use in our unsubscribe functions. 4438 * 4439 * @since 2.8.0 4440 * 4441 * @see https://buddypress.trac.wordpress.org/ticket/7431 4442 * 4443 * @return array The array of email types and their schema. 4444 */ 4445 function bp_email_get_unsubscribe_type_schema() { 4446 $emails = bp_email_get_type_schema( 'all' ); 4447 4448 /** 4449 * Filters the return of `bp_email_get_type_schema( 'all' )` for use with 4450 * our unsubscribe functionality. 4451 * 4452 * @since 2.8.0 4453 * 4454 * @param array $emails The array of email types and their schema. 4455 */ 4456 return (array) apply_filters( 'bp_email_get_unsubscribe_type_schema', $emails ); 4457 } 4458 4459 /** 4460 * Gets the BP Email type of a BP Email ID or object. 4461 * 4462 * @since 8.0.0 4463 * 4464 * @param int|WP_Post $email Optional. BP Email ID or BP Email object. Defaults to global $post. 4465 * @return string The type of the BP Email object. 4466 */ 4467 function bp_email_get_type( $email = null ) { 4468 $email = get_post( $email ); 4469 4470 if ( ! $email ) { 4471 return ''; 4472 } 4473 4474 $types = bp_get_object_terms( $email->ID, bp_get_email_tax_type(), array( 'fields' => 'slugs' ) ); 4475 $type = reset( $types ); 4476 4477 return $type; 4478 } 4479 4480 /** 4481 * Get BuddyPress content allowed tags. 4482 * 4483 * @since 3.0.0 4484 * 4485 * @global array $allowedtags KSES allowed HTML elements. 4486 * @return array BuddyPress content allowed tags. 4487 */ 4488 function bp_get_allowedtags() { 4489 global $allowedtags; 4490 4491 return array_merge_recursive( $allowedtags, array( 4492 'a' => array( 4493 'aria-label' => array(), 4494 'class' => array(), 4495 'data-bp-tooltip' => array(), 4496 'id' => array(), 4497 'rel' => array(), 4498 ), 4499 'img' => array( 4500 'src' => array(), 4501 'alt' => array(), 4502 'width' => array(), 4503 'height' => array(), 4504 'class' => array(), 4505 'id' => array(), 4506 ), 4507 'span'=> array( 4508 'class' => array(), 4509 'data-livestamp' => array(), 4510 ), 4511 'ul' => array(), 4512 'ol' => array(), 4513 'li' => array(), 4514 ) ); 4515 } 4516 4517 /** 4518 * Remove script and style tags from a string. 4519 * 4520 * @since 3.0.1 4521 * 4522 * @param string $string The string to strip tags from. 4523 * @return string The stripped tags string. 4524 */ 4525 function bp_strip_script_and_style_tags( $string ) { 4526 return preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $string ); 4527 } 4528 4529 /** 4530 * Checks whether the current installation is "large". 4531 * 4532 * By default, an installation counts as "large" if there are 10000 users or more. 4533 * Filter 'bp_is_large_install' to adjust. 4534 * 4535 * @since 4.1.0 4536 * 4537 * @return bool 4538 */ 4539 function bp_is_large_install() { 4540 // Use the Multisite function if available. 4541 if ( function_exists( 'wp_is_large_network' ) ) { 4542 $is_large = wp_is_large_network( 'users' ); 4543 } else { 4544 $is_large = bp_core_get_total_member_count() > 10000; 4545 } 4546 4547 /** 4548 * Filters whether the current installation is "large". 4549 * 4550 * @since 4.1.0 4551 * 4552 * @param bool $is_large True if the network is "large". 4553 */ 4554 return (bool) apply_filters( 'bp_is_large_install', $is_large ); 4555 } 4556 4557 /** 4558 * Returns the upper limit on the "max" item count, for widgets that support it. 4559 * 4560 * @since 5.0.0 4561 * 4562 * @param string $widget_class Optional. Class name of the calling widget. 4563 * @return int 4564 */ 4565 function bp_get_widget_max_count_limit( $widget_class = '' ) { 4566 /** 4567 * Filters the upper limit on the "max" item count, for widgets that support it. 4568 * 4569 * @since 5.0.0 4570 * 4571 * @param int $count Defaults to 50. 4572 * @param string $widget_class Class name of the calling widget. 4573 */ 4574 return apply_filters( 'bp_get_widget_max_count_limit', 50, $widget_class ); 4575 } 4576 4577 /** 4578 * Add a new BP_Optout. 4579 * 4580 * @since 8.0.0 4581 * 4582 * @param array $args { 4583 * An array of arguments describing the new opt-out. 4584 * @type string $email_address Email address of user who has opted out. 4585 * @type int $user_id Optional. ID of user whose communication 4586 * prompted the user to opt-out. 4587 * @type string $email_type Optional. Name of the email type that 4588 * prompted the user to opt-out. 4589 * @type string $date_modified Optional. Specify a time, else now will be used. 4590 * } 4591 * @return false|int False on failure, ID of new (or existing) opt-out if successful. 4592 */ 4593 function bp_add_optout( $args = array() ) { 4594 $optout = new BP_Optout(); 4595 $r = bp_parse_args( 4596 $args, array( 4597 'email_address' => '', 4598 'user_id' => 0, 4599 'email_type' => '', 4600 'date_modified' => bp_core_current_time(), 4601 ), 4602 'add_optout' 4603 ); 4604 4605 // Opt-outs must have an email address. 4606 if ( empty( $r['email_address'] ) ) { 4607 return false; 4608 } 4609 4610 // Avoid creating duplicate opt-outs. 4611 $optout_id = $optout->optout_exists( 4612 array( 4613 'email_address' => $r['email_address'], 4614 'user_id' => $r['user_id'], 4615 'email_type' => $r['email_type'], 4616 ) 4617 ); 4618 4619 if ( ! $optout_id ) { 4620 // Set up the new opt-out. 4621 $optout->email_address = $r['email_address']; 4622 $optout->user_id = $r['user_id']; 4623 $optout->email_type = $r['email_type']; 4624 $optout->date_modified = $r['date_modified']; 4625 4626 $optout_id = $optout->save(); 4627 } 4628 4629 return $optout_id; 4630 } 4631 4632 /** 4633 * Find matching BP_Optouts. 4634 * 4635 * @since 8.0.0 4636 * 4637 * @see BP_Optout::get() for a description of parameters and return values. 4638 * 4639 * @param array $args See {@link BP_Optout::get()}. 4640 * @return array See {@link BP_Optout::get()}. 4641 */ 4642 function bp_get_optouts( $args = array() ) { 4643 $optout_class = new BP_Optout(); 4644 return $optout_class::get( $args ); 4645 } 4646 4647 /** 4648 * Check an email address to see if that individual has opted out. 4649 * 4650 * @since 8.0.0 4651 * 4652 * @param string $email_address Email address to check. 4653 * @return bool True if the user has opted out, false otherwise. 4654 */ 4655 function bp_user_has_opted_out( $email_address = '' ) { 4656 $optout_class = new BP_Optout(); 4657 $optout_id = $optout_class->optout_exists( 4658 array( 4659 'email_address' => $email_address, 4660 ) 4661 ); 4662 return (bool) $optout_id; 4663 } 4664 4665 /** 4666 * Delete a BP_Optout by ID. 4667 * 4668 * @since 8.0.0 4669 * 4670 * @param int $id ID of the optout to delete. 4671 * @return bool True on success, false on failure. 4672 */ 4673 function bp_delete_optout_by_id( $id = 0 ) { 4674 $optout_class = new BP_Optout(); 4675 return $optout_class::delete_by_id( $id ); 4676 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Nov 21 01:00:57 2024 | Cross-referenced by PHPXref 0.7.1 |