[ Index ] |
PHP Cross Reference of BuddyPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Groups Template tags 4 * 5 * @since 3.0.0 6 * @version 10.0.0 7 */ 8 9 // Exit if accessed directly. 10 defined( 'ABSPATH' ) || exit; 11 12 /** 13 * Template tag to wrap all Legacy actions that was used 14 * before the groups directory content 15 * 16 * @since 3.0.0 17 */ 18 function bp_nouveau_before_groups_directory_content() { 19 /** 20 * Fires at the begining of the templates BP injected content. 21 * 22 * @since 2.3.0 23 */ 24 do_action( 'bp_before_directory_groups_page' ); 25 26 /** 27 * Fires before the display of the groups. 28 * 29 * @since 1.1.0 30 */ 31 do_action( 'bp_before_directory_groups' ); 32 33 /** 34 * Fires before the display of the groups content. 35 * 36 * @since 1.1.0 37 */ 38 do_action( 'bp_before_directory_groups_content' ); 39 } 40 41 /** 42 * Template tag to wrap all Legacy actions that was used 43 * after the groups directory content 44 * 45 * @since 3.0.0 46 */ 47 function bp_nouveau_after_groups_directory_content() { 48 /** 49 * Fires and displays the group content. 50 * 51 * @since 1.1.0 52 */ 53 do_action( 'bp_directory_groups_content' ); 54 55 /** 56 * Fires after the display of the groups content. 57 * 58 * @since 1.1.0 59 */ 60 do_action( 'bp_after_directory_groups_content' ); 61 62 /** 63 * Fires after the display of the groups. 64 * 65 * @since 1.1.0 66 */ 67 do_action( 'bp_after_directory_groups' ); 68 } 69 70 /** 71 * Fire specific hooks into the groups create template. 72 * 73 * @since 3.0.0 74 * 75 * @param string $when Optional. Either 'before' or 'after'. 76 * @param string $suffix Optional. Use it to add terms at the end of the hook name. 77 */ 78 function bp_nouveau_groups_create_hook( $when = '', $suffix = '' ) { 79 $hook = array( 'bp' ); 80 81 if ( $when ) { 82 $hook[] = $when; 83 } 84 85 // It's a create group hook 86 $hook[] = 'create_group'; 87 88 if ( $suffix ) { 89 $hook[] = $suffix; 90 } 91 92 bp_nouveau_hook( $hook ); 93 } 94 95 /** 96 * Fire specific hooks into the single groups templates. 97 * 98 * @since 3.0.0 99 * 100 * @param string $when Optional. Either 'before' or 'after'. 101 * @param string $suffix Optional. Use it to add terms at the end of the hook name. 102 */ 103 function bp_nouveau_group_hook( $when = '', $suffix = '' ) { 104 $hook = array( 'bp' ); 105 106 if ( $when ) { 107 $hook[] = $when; 108 } 109 110 // It's a group hook 111 $hook[] = 'group'; 112 113 if ( $suffix ) { 114 $hook[] = $suffix; 115 } 116 117 bp_nouveau_hook( $hook ); 118 } 119 120 /** 121 * Fire an isolated hook inside the groups loop 122 * 123 * @since 3.0.0 124 */ 125 function bp_nouveau_groups_loop_item() { 126 /** 127 * Fires inside the listing of an individual group listing item. 128 * 129 * @since 1.1.0 130 */ 131 do_action( 'bp_directory_groups_item' ); 132 } 133 134 /** 135 * Display the current group activity post form if needed 136 * 137 * @since 3.0.0 138 */ 139 function bp_nouveau_groups_activity_post_form() { 140 /** 141 * Fires before the display of the group activity post form. 142 * 143 * @since 1.2.0 144 */ 145 do_action( 'bp_before_group_activity_post_form' ); 146 147 if ( is_user_logged_in() && bp_group_is_member() ) { 148 bp_get_template_part( 'activity/post-form' ); 149 } 150 151 /** 152 * Fires after the display of the group activity post form. 153 * 154 * @since 1.2.0 155 */ 156 do_action( 'bp_after_group_activity_post_form' ); 157 } 158 159 /** 160 * Prints the JS Templates to invite new members to join the Group. 161 * 162 * @since 10.0.0 163 */ 164 function bp_nouveau_group_print_invites_templates() { 165 bp_get_template_part( 'common/js-templates/invites/index' ); 166 } 167 168 /** 169 * Prints the HTML placeholders to invite new members to join the Group. 170 * 171 * @since 10.0.0 172 */ 173 function bp_nouveau_group_print_invites_placeholders() { 174 if ( bp_is_group_create() ) : ?> 175 176 <h3 class="bp-screen-title creation-step-name"> 177 <?php esc_html_e( 'Invite Members', 'buddypress' ); ?> 178 </h3> 179 180 <?php else : ?> 181 182 <h2 class="bp-screen-title"> 183 <?php esc_html_e( 'Invite Members', 'buddypress' ); ?> 184 </h2> 185 186 <?php endif; ?> 187 188 <div id="group-invites-container"> 189 <nav class="<?php bp_nouveau_single_item_subnav_classes(); ?>" id="subnav" role="navigation" aria-label="<?php esc_attr_e( 'Group invitations menu', 'buddypress' ); ?>"></nav> 190 <div class="group-invites-column"> 191 <div class="subnav-filters group-subnav-filters bp-invites-filters"></div> 192 <div class="bp-invites-feedback"></div> 193 <div class="members bp-invites-content"></div> 194 </div> 195 </div> 196 <?php 197 } 198 199 /** 200 * Load the Group Invites UI. 201 * 202 * @since 3.0.0 203 * 204 * @return string HTML Output. 205 */ 206 function bp_nouveau_group_invites_interface() { 207 /** 208 * Fires before the send invites content. 209 * 210 * @since 1.1.0 211 */ 212 do_action( 'bp_before_group_send_invites_content' ); 213 214 /** 215 * Get the templates to manage Group Members using the BP REST API. 216 * 217 * @since 10.0.0 Hook to the `wp_footer` action to print the JS templates. 218 */ 219 add_action( 'wp_footer', 'bp_nouveau_group_print_invites_templates' ); 220 bp_nouveau_group_print_invites_placeholders(); 221 222 /** 223 * Private hook to preserve backward compatibility with plugins needing the above placeholders to be located 224 * into: `bp-templates/bp-nouveau/buddypress/common/js-templates/invites/index.php`. 225 * 226 * @since 10.0.0 227 */ 228 do_action( '_bp_nouveau_group_print_invites_placeholders' ); 229 230 /** 231 * Fires after the send invites content. 232 * 233 * @since 1.2.0 234 */ 235 do_action( 'bp_after_group_send_invites_content' ); 236 } 237 238 /** 239 * Gets the displayed user group invites preferences 240 * 241 * @since 3.0.0 242 * @since 4.4.0 243 * 244 * @param int $user_id The user ID to check group invites preference for. 245 * @return int Returns 1 if user chose to restrict to friends, 0 otherwise. 246 */ 247 function bp_nouveau_groups_get_group_invites_setting( $user_id = 0 ) { 248 if ( ! $user_id ) { 249 $user_id = bp_displayed_user_id(); 250 } 251 252 return (int) bp_get_user_meta( $user_id, '_bp_nouveau_restrict_invites_to_friends' ); 253 } 254 255 /** 256 * Outputs the group creation numbered steps navbar 257 * 258 * @since 3.0.0 259 * 260 * @todo This output isn't localised correctly. 261 */ 262 function bp_nouveau_group_creation_tabs() { 263 $bp = buddypress(); 264 265 if ( ! is_array( $bp->groups->group_creation_steps ) ) { 266 return; 267 } 268 269 if ( ! bp_get_groups_current_create_step() ) { 270 $keys = array_keys( $bp->groups->group_creation_steps ); 271 $bp->groups->current_create_step = array_shift( $keys ); 272 } 273 274 $counter = 1; 275 276 foreach ( (array) $bp->groups->group_creation_steps as $slug => $step ) { 277 $is_enabled = bp_are_previous_group_creation_steps_complete( $slug ); ?> 278 279 <li<?php if ( bp_get_groups_current_create_step() === $slug ) : ?> class="current"<?php endif; ?>> 280 <?php if ( $is_enabled ) : ?> 281 <a href="<?php echo esc_url( bp_groups_directory_permalink() . 'create/step/' . $slug . '/' ); ?>"> 282 <?php echo (int) $counter; ?> <?php echo esc_html( $step['name'] ); ?> 283 </a> 284 <?php else : ?> 285 <?php echo (int) $counter; ?>. <?php echo esc_html( $step['name'] ); ?> 286 <?php endif ?> 287 </li> 288 <?php 289 $counter++; 290 } 291 292 unset( $is_enabled ); 293 294 /** 295 * Fires at the end of the creation of the group tabs. 296 * 297 * @since 1.0.0 298 */ 299 do_action( 'groups_creation_tabs' ); 300 } 301 302 /** 303 * Load the requested Create Screen for the new group. 304 * 305 * @since 3.0.0 306 */ 307 function bp_nouveau_group_creation_screen() { 308 return bp_nouveau_group_manage_screen(); 309 } 310 311 /** 312 * Load the requested Manage Screen for the current group. 313 * 314 * @since 3.0.0 315 */ 316 317 function bp_nouveau_group_manage_screen() { 318 $action = bp_action_variable( 0 ); 319 $is_group_create = bp_is_group_create(); 320 $output = ''; 321 322 if ( $is_group_create ) { 323 $action = bp_action_variable( 1 ); 324 } 325 326 $screen_id = sanitize_file_name( $action ); 327 if ( ! bp_is_group_admin_screen( $screen_id ) && ! bp_is_group_creation_step( $screen_id ) ) { 328 return; 329 } 330 331 if ( ! $is_group_create ) { 332 /** 333 * Fires inside the group admin form and before the content. 334 * 335 * @since 1.1.0 336 */ 337 do_action( 'bp_before_group_admin_content' ); 338 339 $core_screen = bp_nouveau_group_get_core_manage_screens( $screen_id ); 340 341 // It's a group step, get the creation screens. 342 } else { 343 $core_screen = bp_nouveau_group_get_core_create_screens( $screen_id ); 344 } 345 346 if ( ! $core_screen ) { 347 if ( ! $is_group_create ) { 348 /** 349 * Fires inside the group admin template. 350 * 351 * Allows plugins to add custom group edit screens. 352 * 353 * @since 1.1.0 354 */ 355 do_action( 'groups_custom_edit_steps' ); 356 357 // Else use the group create hook 358 } else { 359 /** 360 * Fires inside the group admin template. 361 * 362 * Allows plugins to add custom group creation steps. 363 * 364 * @since 1.1.0 365 */ 366 do_action( 'groups_custom_create_steps' ); 367 } 368 369 // Else we load the core screen. 370 } else { 371 if ( ! empty( $core_screen['hook'] ) ) { 372 /** 373 * Fires before the display of group delete admin. 374 * 375 * @since 1.1.0 For most hooks. 376 * @since 2.4.0 For the cover image hook. 377 */ 378 do_action( 'bp_before_' . $core_screen['hook'] ); 379 } 380 381 $template = 'groups/single/admin/' . $screen_id; 382 383 if ( ! empty( $core_screen['template'] ) ) { 384 $template = $core_screen['template']; 385 } 386 387 bp_get_template_part( $template ); 388 389 if ( ! empty( $core_screen['hook'] ) ) { 390 391 // Group's "Manage > Details" page. 392 if ( 'group_details_admin' === $core_screen['hook'] ) { 393 /** 394 * Fires after the group description admin details. 395 * 396 * @since 1.0.0 397 */ 398 do_action( 'groups_custom_group_fields_editable' ); 399 } 400 401 /** 402 * Fires before the display of group delete admin. 403 * 404 * @since 1.1.0 For most hooks. 405 * @since 2.4.0 For the cover image hook. 406 */ 407 do_action( 'bp_after_' . $core_screen['hook'] ); 408 } 409 410 if ( ! empty( $core_screen['nonce'] ) ) { 411 if ( ! $is_group_create ) { 412 $output = sprintf( '<p><input type="submit" value="%s" id="save" name="save" /></p>', esc_attr__( 'Save Changes', 'buddypress' ) ); 413 414 // Specific case for the delete group screen 415 if ( 'delete-group' === $screen_id ) { 416 $output = sprintf( 417 '<div class="submit"> 418 <input type="submit" disabled="disabled" value="%s" id="delete-group-button" name="delete-group-button" /> 419 </div>', 420 esc_attr__( 'Delete Group', 'buddypress' ) 421 ); 422 } 423 } 424 } 425 } 426 427 if ( $is_group_create ) { 428 /** 429 * Fires before the display of the group creation step buttons. 430 * 431 * @since 1.1.0 432 */ 433 do_action( 'bp_before_group_creation_step_buttons' ); 434 435 if ( 'crop-image' !== bp_get_avatar_admin_step() ) { 436 $creation_step_buttons = ''; 437 438 if ( ! bp_is_first_group_creation_step() ) { 439 $creation_step_buttons .= sprintf( 440 '<input type="button" value="%1$s" id="group-creation-previous" name="previous" onclick="%2$s" />', 441 esc_attr__( 'Back to Previous Step', 'buddypress' ), 442 "location.href='" . esc_js( esc_url_raw( bp_get_group_creation_previous_link() ) ) . "'" 443 ); 444 } 445 446 if ( ! bp_is_last_group_creation_step() && ! bp_is_first_group_creation_step() ) { 447 $creation_step_buttons .= sprintf( 448 '<input type="submit" value="%s" id="group-creation-next" name="save" />', 449 esc_attr__( 'Next Step', 'buddypress' ) 450 ); 451 } 452 453 if ( bp_is_first_group_creation_step() ) { 454 $creation_step_buttons .= sprintf( 455 '<input type="submit" value="%s" id="group-creation-create" name="save" />', 456 esc_attr__( 'Create Group and Continue', 'buddypress' ) 457 ); 458 } 459 460 if ( bp_is_last_group_creation_step() ) { 461 $creation_step_buttons .= sprintf( 462 '<input type="submit" value="%s" id="group-creation-finish" name="save" />', 463 esc_attr__( 'Finish', 'buddypress' ) 464 ); 465 } 466 467 // Set the output for the buttons 468 $output = sprintf( '<div class="submit" id="previous-next">%s</div>', $creation_step_buttons ); 469 } 470 471 /** 472 * Fires after the display of the group creation step buttons. 473 * 474 * @since 1.1.0 475 */ 476 do_action( 'bp_after_group_creation_step_buttons' ); 477 } 478 479 /** 480 * Avoid nested forms with the Backbone views for the group invites step. 481 */ 482 if ( 'group-invites' === bp_get_groups_current_create_step() ) { 483 printf( 484 '<form action="%s" method="post" enctype="multipart/form-data">', 485 bp_get_group_creation_form_action() 486 ); 487 } 488 489 if ( ! empty( $core_screen['nonce'] ) ) { 490 wp_nonce_field( $core_screen['nonce'] ); 491 } 492 493 printf( 494 '<input type="hidden" name="group-id" id="group-id" value="%s" />', 495 $is_group_create ? esc_attr( bp_get_new_group_id() ) : esc_attr( bp_get_group_id() ) 496 ); 497 498 // The submit actions 499 echo $output; 500 501 if ( ! $is_group_create ) { 502 /** 503 * Fires inside the group admin form and after the content. 504 * 505 * @since 1.1.0 506 */ 507 do_action( 'bp_after_group_admin_content' ); 508 509 } else { 510 /** 511 * Fires and displays the groups directory content. 512 * 513 * @since 1.1.0 514 */ 515 do_action( 'bp_directory_groups_content' ); 516 } 517 518 /** 519 * Avoid nested forms with the Backbone views for the group invites step. 520 */ 521 if ( 'group-invites' === bp_get_groups_current_create_step() ) { 522 echo '</form>'; 523 } 524 } 525 526 /** 527 * Output the action buttons for the displayed group 528 * 529 * @since 3.0.0 530 * 531 * @param array $args Optional. See bp_nouveau_wrapper() for the description of parameters. 532 */ 533 function bp_nouveau_group_header_buttons( $args = array() ) { 534 $bp_nouveau = bp_nouveau(); 535 536 $output = join( ' ', bp_nouveau_get_groups_buttons( $args ) ); 537 538 // On the group's header we need to reset the group button's global. 539 if ( ! empty( $bp_nouveau->groups->group_buttons ) ) { 540 unset( $bp_nouveau->groups->group_buttons ); 541 } 542 543 ob_start(); 544 /** 545 * Fires in the group header actions section. 546 * 547 * @since 1.2.6 548 */ 549 do_action( 'bp_group_header_actions' ); 550 $output .= ob_get_clean(); 551 552 if ( ! $output ) { 553 return; 554 } 555 556 if ( ! $args ) { 557 $args = array( 'classes' => array( 'item-buttons' ) ); 558 } 559 560 bp_nouveau_wrapper( array_merge( $args, array( 'output' => $output ) ) ); 561 } 562 563 /** 564 * Output the action buttons inside the groups loop. 565 * 566 * @since 3.0.0 567 * 568 * @param array $args Optional. See bp_nouveau_wrapper() for the description of parameters. 569 */ 570 function bp_nouveau_groups_loop_buttons( $args = array() ) { 571 if ( empty( $GLOBALS['groups_template'] ) ) { 572 return; 573 } 574 575 $args['type'] = 'loop'; 576 577 $output = join( ' ', bp_nouveau_get_groups_buttons( $args ) ); 578 579 ob_start(); 580 /** 581 * Fires inside the action section of an individual group listing item. 582 * 583 * @since 1.1.0 584 */ 585 do_action( 'bp_directory_groups_actions' ); 586 $output .= ob_get_clean(); 587 588 if ( ! $output ) { 589 return; 590 } 591 592 bp_nouveau_wrapper( array_merge( $args, array( 'output' => $output ) ) ); 593 } 594 595 /** 596 * Output the action buttons inside the invites loop of the displayed user. 597 * 598 * @since 3.0.0 599 * 600 * @param array $args Optional. See bp_nouveau_wrapper() for the description of parameters. 601 */ 602 function bp_nouveau_groups_invite_buttons( $args = array() ) { 603 if ( empty( $GLOBALS['groups_template'] ) ) { 604 return; 605 } 606 607 $args['type'] = 'invite'; 608 609 $output = join( ' ', bp_nouveau_get_groups_buttons( $args ) ); 610 611 ob_start(); 612 /** 613 * Fires inside the member group item action markup. 614 * 615 * @since 1.1.0 616 */ 617 do_action( 'bp_group_invites_item_action' ); 618 $output .= ob_get_clean(); 619 620 if ( ! $output ) { 621 return; 622 } 623 624 bp_nouveau_wrapper( array_merge( $args, array( 'output' => $output ) ) ); 625 } 626 627 /** 628 * Output the action buttons inside the requests loop of the group's manage screen. 629 * 630 * @since 3.0.0 631 * 632 * @param array $args Optional. See bp_nouveau_wrapper() for the description of parameters. 633 */ 634 function bp_nouveau_groups_request_buttons( $args = array() ) { 635 if ( empty( $GLOBALS['requests_template'] ) ) { 636 return; 637 } 638 639 $args['type'] = 'request'; 640 641 $output = join( ' ', bp_nouveau_get_groups_buttons( $args ) ); 642 643 ob_start(); 644 /** 645 * Fires inside the list of membership request actions. 646 * 647 * @since 1.1.0 648 */ 649 do_action( 'bp_group_membership_requests_admin_item_action' ); 650 $output .= ob_get_clean(); 651 652 if ( ! $output ) { 653 return; 654 } 655 656 bp_nouveau_wrapper( array_merge( $args, array( 'output' => $output ) ) ); 657 } 658 659 /** 660 * Output the action buttons inside the manage members loop of the group's manage screen. 661 * 662 * @since 3.0.0 663 * 664 * @param array $args Optional. See bp_nouveau_wrapper() for the description of parameters. 665 */ 666 function bp_nouveau_groups_manage_members_buttons( $args = array() ) { 667 if ( empty( $GLOBALS['members_template'] ) ) { 668 return; 669 } 670 671 $args['type'] = 'manage_members'; 672 673 $output = join( ' ', bp_nouveau_get_groups_buttons( $args ) ); 674 675 ob_start(); 676 /** 677 * Fires inside the display of a member admin item in group management area. 678 * 679 * @since 1.1.0 680 */ 681 do_action( 'bp_group_manage_members_admin_item' ); 682 $output .= ob_get_clean(); 683 684 if ( ! $output ) { 685 return; 686 } 687 688 if ( ! $args ) { 689 $args = array( 690 'wrapper' => 'span', 691 'classes' => array( 'small' ), 692 ); 693 } 694 695 bp_nouveau_wrapper( array_merge( $args, array( 'output' => $output ) ) ); 696 } 697 698 /** 699 * Get the action buttons for the current group in the loop, 700 * or the current displayed group. 701 * 702 * @since 3.0.0 703 * 704 * @param array $args Optional. See bp_nouveau_wrapper() for the description of parameters. 705 */ 706 function bp_nouveau_get_groups_buttons( $args = array() ) { 707 $type = ( ! empty( $args['type'] ) ) ? $args['type'] : 'group'; 708 709 // @todo Not really sure why BP Legacy needed to do this... 710 if ( 'group' === $type && is_admin() && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) { 711 return; 712 } 713 714 $buttons = array(); 715 716 if ( ( 'loop' === $type || 'invite' === $type ) && isset( $GLOBALS['groups_template']->group ) ) { 717 $group = $GLOBALS['groups_template']->group; 718 } else { 719 $group = groups_get_current_group(); 720 } 721 722 if ( empty( $group->id ) ) { 723 return $buttons; 724 } 725 726 /* 727 * If the 'container' is set to 'ul' set $parent_element to li, 728 * otherwise simply pass any value found in $args or set var false. 729 */ 730 if ( ! empty( $args['container'] ) && 'ul' === $args['container'] ) { 731 $parent_element = 'li'; 732 } elseif ( ! empty( $args['parent_element'] ) ) { 733 $parent_element = $args['parent_element']; 734 } else { 735 $parent_element = false; 736 } 737 738 /* 739 * If we have a arg value for $button_element passed through 740 * use it to default all the $buttons['button_element'] values 741 * otherwise default to 'a' (anchor) o override & hardcode the 742 * 'element' string on $buttons array. 743 * 744 * Icons sets a class for icon display if not using the button element 745 */ 746 $icons = ''; 747 if ( ! empty( $args['button_element'] ) ) { 748 $button_element = $args['button_element'] ; 749 } else { 750 $button_element = 'a'; 751 $icons = ' icons'; 752 } 753 754 // If we pass through parent classes add them to $button array 755 $parent_class = ''; 756 if ( ! empty( $args['parent_attr']['class'] ) ) { 757 $parent_class = $args['parent_attr']['class']; 758 } 759 760 // Invite buttons on member's invites screen 761 if ( 'invite' === $type ) { 762 // Don't show button if not logged in or previously banned 763 if ( ! is_user_logged_in() || bp_group_is_user_banned( $group ) || empty( $group->status ) ) { 764 return $buttons; 765 } 766 767 // Setup Accept button attributes 768 $buttons['accept_invite'] = array( 769 'id' => 'accept_invite', 770 'position' => 5, 771 'component' => 'groups', 772 'must_be_logged_in' => true, 773 'parent_element' => $parent_element, 774 'link_text' => esc_html__( 'Accept', 'buddypress' ), 775 'button_element' => $button_element, 776 'parent_attr' => array( 777 'id' => '', 778 'class' => $parent_class . ' ' . 'accept', 779 ), 780 'button_attr' => array( 781 'id' => '', 782 'class' => 'button accept group-button accept-invite', 783 'rel' => '', 784 ), 785 ); 786 787 // If button element set add nonce link to data-attr attr 788 if ( 'button' === $button_element ) { 789 $buttons['accept_invite']['button_attr']['data-bp-nonce'] = esc_url( bp_get_group_accept_invite_link() ); 790 } else { 791 $buttons['accept_invite']['button_attr']['href'] = esc_url( bp_get_group_accept_invite_link() ); 792 } 793 794 // Setup Reject button attributes 795 $buttons['reject_invite'] = array( 796 'id' => 'reject_invite', 797 'position' => 15, 798 'component' => 'groups', 799 'must_be_logged_in' => true, 800 'parent_element' => $parent_element, 801 'link_text' => __( 'Reject', 'buddypress' ), 802 'parent_attr' => array( 803 'id' => '', 804 'class' => $parent_class . ' ' . 'reject', 805 ), 806 'button_element' => $button_element, 807 'button_attr' => array( 808 'id' => '', 809 'class' => 'button reject group-button reject-invite', 810 'rel' => '', 811 ), 812 ); 813 814 // If button element set add nonce link to formaction attr 815 if ( 'button' === $button_element ) { 816 $buttons['reject_invite']['button_attr']['data-bp-nonce'] = esc_url( bp_get_group_reject_invite_link() ); 817 } else { 818 $buttons['reject_invite']['button_attr']['href'] = esc_url( bp_get_group_reject_invite_link() ); 819 } 820 821 // Request button for the group's manage screen 822 } elseif ( 'request' === $type ) { 823 // Setup Accept button attributes 824 $buttons['group_membership_accept'] = array( 825 'id' => 'group_membership_accept', 826 'position' => 5, 827 'component' => 'groups', 828 'must_be_logged_in' => true, 829 'parent_element' => $parent_element, 830 'link_text' => esc_html__( 'Accept', 'buddypress' ), 831 'button_element' => $button_element, 832 'parent_attr' => array( 833 'id' => '', 834 'class' => $parent_class, 835 ), 836 'button_attr' => array( 837 'id' => '', 838 'class' => 'button accept', 839 'rel' => '', 840 ), 841 ); 842 843 // If button element set add nonce link to data-attr attr 844 if ( 'button' === $button_element ) { 845 $buttons['group_membership_accept']['button_attr']['data-bp-nonce'] = esc_url( bp_get_group_request_accept_link() ); 846 } else { 847 $buttons['group_membership_accept']['button_attr']['href'] = esc_url( bp_get_group_request_accept_link() ); 848 } 849 850 $buttons['group_membership_reject'] = array( 851 'id' => 'group_membership_reject', 852 'position' => 15, 853 'component' => 'groups', 854 'must_be_logged_in' => true, 855 'parent_element' => $parent_element, 856 'button_element' => $button_element, 857 'link_text' => __( 'Reject', 'buddypress' ), 858 'parent_attr' => array( 859 'id' => '', 860 'class' => $parent_class, 861 ), 862 'button_attr' => array( 863 'id' => '', 864 'class' => 'button reject', 865 'rel' => '', 866 ), 867 ); 868 869 // If button element set add nonce link to data-attr attr 870 if ( 'button' === $button_element ) { 871 $buttons['group_membership_reject']['button_attr']['data-bp-nonce'] = esc_url( bp_get_group_request_reject_link() ); 872 } else { 873 $buttons['group_membership_reject']['button_attr']['href'] = esc_url( bp_get_group_request_reject_link() ); 874 } 875 876 /* 877 * Manage group members for the group's manage screen. 878 * The 'button_attr' keys 'href' & 'formaction' are set at the end of this array block 879 */ 880 } elseif ( 'manage_members' === $type && isset( $GLOBALS['members_template']->member->user_id ) ) { 881 $user_id = $GLOBALS['members_template']->member->user_id; 882 883 $buttons = array( 884 'unban_member' => array( 885 'id' => 'unban_member', 886 'position' => 5, 887 'component' => 'groups', 888 'must_be_logged_in' => true, 889 'parent_element' => $parent_element, 890 'button_element' => $button_element, 891 'link_text' => __( 'Remove Ban', 'buddypress' ), 892 'parent_attr' => array( 893 'id' => '', 894 'class' => $parent_class, 895 ), 896 'button_attr' => array( 897 'id' => '', 898 'class' => 'button confirm member-unban', 899 'rel' => '', 900 'title' => '', 901 ), 902 ), 903 'ban_member' => array( 904 'id' => 'ban_member', 905 'position' => 15, 906 'component' => 'groups', 907 'must_be_logged_in' => true, 908 'parent_element' => $parent_element, 909 'button_element' => $button_element, 910 'link_text' => __( 'Kick & Ban', 'buddypress' ), 911 'parent_attr' => array( 912 'id' => '', 913 'class' => $parent_class, 914 ), 915 'button_attr' => array( 916 'id' => '', 917 'class' => 'button confirm member-ban', 918 'rel' => '', 919 'title' => '', 920 ), 921 ), 922 'promote_mod' => array( 923 'id' => 'promote_mod', 924 'position' => 25, 925 'component' => 'groups', 926 'must_be_logged_in' => true, 927 'parent_element' => $parent_element, 928 'parent_attr' => array( 929 'id' => '', 930 'class' => $parent_class, 931 ), 932 'button_element' => $button_element, 933 'button_attr' => array( 934 'id' => '', 935 'class' => 'button confirm member-promote-to-mod', 936 'rel' => '', 937 'title' => '', 938 ), 939 'link_text' => __( 'Promote to Mod', 'buddypress' ), 940 ), 941 'promote_admin' => array( 942 'id' => 'promote_admin', 943 'position' => 35, 944 'component' => 'groups', 945 'must_be_logged_in' => true, 946 'parent_element' => $parent_element, 947 'button_element' => $button_element, 948 'link_text' => __( 'Promote to Admin', 'buddypress' ), 949 'parent_attr' => array( 950 'id' => '', 951 'class' => $parent_class, 952 ), 953 'button_attr' => array( 954 'href' => esc_url( bp_get_group_member_promote_admin_link() ), 955 'id' => '', 956 'class' => 'button confirm member-promote-to-admin', 957 'rel' => '', 958 'title' => '', 959 ), 960 ), 961 'remove_member' => array( 962 'id' => 'remove_member', 963 'position' => 45, 964 'component' => 'groups', 965 'must_be_logged_in' => true, 966 'parent_element' => $parent_element, 967 'button_element' => $button_element, 968 'link_text' => __( 'Remove from group', 'buddypress' ), 969 'parent_attr' => array( 970 'id' => '', 971 'class' => $parent_class, 972 ), 973 'button_attr' => array( 974 'id' => '', 975 'class' => 'button confirm', 976 'rel' => '', 977 'title' => '', 978 ), 979 ), 980 ); 981 982 // If 'button' element is set add the nonce link to data-attr attr, else add it to the href. 983 if ( 'button' === $button_element ) { 984 $buttons['unban_member']['button_attr']['data-bp-nonce'] = bp_get_group_member_unban_link( $user_id ); 985 $buttons['ban_member']['button_attr']['data-bp-nonce'] = bp_get_group_member_ban_link( $user_id ); 986 $buttons['promote_mod']['button_attr']['data-bp-nonce'] = bp_get_group_member_promote_mod_link(); 987 $buttons['promote_admin']['button_attr']['data-bp-nonce'] = bp_get_group_member_promote_admin_link(); 988 $buttons['remove_member']['button_attr']['data-bp-nonce'] = bp_get_group_member_remove_link( $user_id ); 989 } else { 990 $buttons['unban_member']['button_attr']['href'] = bp_get_group_member_unban_link( $user_id ); 991 $buttons['ban_member']['button_attr']['href'] = bp_get_group_member_ban_link( $user_id ); 992 $buttons['promote_mod']['button_attr']['href'] = bp_get_group_member_promote_mod_link(); 993 $buttons['promote_admin']['button_attr']['href'] = bp_get_group_member_promote_admin_link(); 994 $buttons['remove_member']['button_attr']['href'] = bp_get_group_member_remove_link( $user_id ); 995 } 996 997 // Membership button on groups loop or single group's header 998 } else { 999 /* 1000 * This filter workaround is waiting for a core adaptation 1001 * so that we can directly get the groups button arguments 1002 * instead of the button. 1003 * 1004 * See https://buddypress.trac.wordpress.org/ticket/7126 1005 */ 1006 add_filter( 'bp_get_group_join_button', 'bp_nouveau_groups_catch_button_args', 100, 1 ); 1007 1008 bp_get_group_join_button( $group ); 1009 1010 remove_filter( 'bp_get_group_join_button', 'bp_nouveau_groups_catch_button_args', 100, 1 ); 1011 1012 if ( isset( bp_nouveau()->groups->button_args ) && bp_nouveau()->groups->button_args ) { 1013 $button_args = bp_nouveau()->groups->button_args; 1014 1015 // If we pass through parent classes merge those into the existing ones 1016 if ( $parent_class ) { 1017 $parent_class .= ' ' . $button_args['wrapper_class']; 1018 } 1019 1020 // The join or leave group header button should default to 'button' 1021 // Reverse the earler button var to set default as 'button' not 'a' 1022 if ( empty( $args['button_element'] ) ) { 1023 $button_element = 'button'; 1024 } 1025 1026 $buttons['group_membership'] = array( 1027 'id' => 'group_membership', 1028 'position' => 5, 1029 'component' => $button_args['component'], 1030 'must_be_logged_in' => $button_args['must_be_logged_in'], 1031 'block_self' => $button_args['block_self'], 1032 'parent_element' => $parent_element, 1033 'button_element' => $button_element, 1034 'link_text' => $button_args['link_text'], 1035 'parent_attr' => array( 1036 'id' => $button_args['wrapper_id'], 1037 'class' => $parent_class, 1038 ), 1039 'button_attr' => array( 1040 'id' => ! empty( $button_args['link_id'] ) ? $button_args['link_id'] : '', 1041 'class' => $button_args['link_class'] . ' button', 1042 'rel' => ! empty( $button_args['link_rel'] ) ? $button_args['link_rel'] : '', 1043 'title' => '', 1044 ), 1045 ); 1046 1047 // If button element set add nonce 'href' link to data-attr attr. 1048 if ( 'button' === $button_element ) { 1049 $buttons['group_membership']['button_attr']['data-bp-nonce'] = $button_args['link_href']; 1050 } else { 1051 // Else this is an anchor so use an 'href' attr. 1052 $buttons['group_membership']['button_attr']['href'] = $button_args['link_href']; 1053 } 1054 1055 unset( bp_nouveau()->groups->button_args ); 1056 } 1057 } 1058 1059 /** 1060 * Filter to add your buttons, use the position argument to choose where to insert it. 1061 * 1062 * @since 3.0.0 1063 * @since 9.0.0 Adds the `$args` parameter to the filter. 1064 * 1065 * @param array $buttons The list of buttons. 1066 * @param int $group The current group object. 1067 * @param string $type Whether we're displaying a groups loop or a groups single item. 1068 * @param array $args Button arguments. 1069 */ 1070 $buttons_group = apply_filters( 'bp_nouveau_get_groups_buttons', $buttons, $group, $type, $args ); 1071 1072 if ( ! $buttons_group ) { 1073 return $buttons; 1074 } 1075 1076 // It's the first entry of the loop, so build the Group and sort it 1077 if ( ! isset( bp_nouveau()->groups->group_buttons ) || ! is_a( bp_nouveau()->groups->group_buttons, 'BP_Buttons_Group' ) ) { 1078 $sort = true; 1079 bp_nouveau()->groups->group_buttons = new BP_Buttons_Group( $buttons_group ); 1080 1081 // It's not the first entry, the order is set, we simply need to update the Buttons Group 1082 } else { 1083 $sort = false; 1084 bp_nouveau()->groups->group_buttons->update( $buttons_group ); 1085 } 1086 1087 $return = bp_nouveau()->groups->group_buttons->get( $sort ); 1088 1089 if ( ! $return ) { 1090 return array(); 1091 } 1092 1093 // Remove buttons according to the user's membership type. 1094 if ( 'manage_members' === $type && isset( $GLOBALS['members_template'] ) ) { 1095 if ( bp_get_group_member_is_banned() ) { 1096 unset( $return['ban_member'], $return['promote_mod'], $return['promote_admin'] ); 1097 } else { 1098 unset( $return['unban_member'] ); 1099 } 1100 } 1101 1102 /** 1103 * Leave a chance to adjust the $return 1104 * 1105 * @since 3.0.0 1106 * 1107 * @param array $return The list of buttons. 1108 * @param int $group The current group object. 1109 * @parem string $type Whether we're displaying a groups loop or a groups single item. 1110 */ 1111 do_action_ref_array( 'bp_nouveau_return_groups_buttons', array( &$return, $group, $type ) ); 1112 1113 return $return; 1114 } 1115 1116 /** 1117 * Does the group has metas or a specific meta value. 1118 * 1119 * @since 3.0.0 1120 * @since 3.2.0 Adds the $meta_key argument. 1121 * 1122 * @param string $meta_key The key of the meta to check the value for. 1123 * @return bool True if the group has meta. False otherwise. 1124 */ 1125 function bp_nouveau_group_has_meta( $meta_key = '' ) { 1126 if ( ! $meta_key ) { 1127 $meta_keys = array( 'status', 'count' ); 1128 } else { 1129 $meta_keys = array( $meta_key ); 1130 } 1131 1132 $group_meta = bp_nouveau_get_group_meta( $meta_keys ); 1133 $group_meta = array_filter( $group_meta ); 1134 1135 return ! empty( $group_meta ); 1136 } 1137 1138 /** 1139 * Does the group have extra meta? 1140 * 1141 * @since 3.0.0 1142 * 1143 * @return bool True if the group has meta. False otherwise. 1144 */ 1145 function bp_nouveau_group_has_meta_extra() { 1146 return false !== bp_nouveau_get_hooked_group_meta(); 1147 } 1148 1149 /** 1150 * Display the group meta. 1151 * 1152 * @since 3.0.0 1153 * @deprecated 7.0.0 Use bp_nouveau_the_group_meta() 1154 * @see bp_nouveau_the_group_meta() 1155 * 1156 * @return string HTML Output. 1157 */ 1158 function bp_nouveau_group_meta() { 1159 _deprecated_function( __FUNCTION__, '7.0.0', 'bp_nouveau_the_group_meta()' ); 1160 $group_meta = new BP_Nouveau_Group_Meta(); 1161 1162 if ( ! bp_is_group() ) { 1163 echo $group_meta->meta; 1164 } else { 1165 return $group_meta; 1166 } 1167 } 1168 1169 /** 1170 * Outputs or returns the group meta(s). 1171 * 1172 * @since 7.0.0 1173 * 1174 * @param array $args { 1175 * Optional. An array of arguments. 1176 * 1177 * @type array $keys The list of template meta keys. 1178 * @type string $delimeter The delimeter to use in case there is more than 1179 * one key to output. 1180 * @type boolean $echo True to output the template meta value. False otherwise. 1181 * } 1182 * @return string HTML Output. 1183 */ 1184 function bp_nouveau_the_group_meta( $args = array() ) { 1185 $r = bp_parse_args( 1186 $args, 1187 array( 1188 'keys' => array(), 1189 'delimeter' => '/', 1190 'echo' => true, 1191 ), 1192 'nouveau_the_group_meta' 1193 ); 1194 1195 $group_meta = (array) bp_nouveau_get_group_meta( $r['keys'] ); 1196 1197 if ( ! $group_meta ) { 1198 return; 1199 } 1200 1201 $meta = ''; 1202 if ( 1 < count( $group_meta ) ) { 1203 $group_meta = array_filter( $group_meta ); 1204 $meta = join( ' ' . $r['delimeter'] . ' ', array_map( 'esc_html', $group_meta ) ); 1205 } else { 1206 $meta = reset( $group_meta ); 1207 } 1208 1209 if ( ! $r['echo'] ) { 1210 return $meta; 1211 } 1212 1213 echo $meta; 1214 } 1215 1216 /** 1217 * Get the group template meta. 1218 * 1219 * @since 3.0.0 1220 * @since 7.0.0 Adds the `$keys` parameter. 1221 * 1222 * @param array $keys One or more template meta keys to populate with their values. 1223 * Possible keys are `status`, `count`, `group_type_list`, `description`, `extra`. 1224 * @return array The corresponding group template meta values. 1225 */ 1226 function bp_nouveau_get_group_meta( $keys = array() ) { 1227 $keys = (array) $keys; 1228 $group = false; 1229 $group_meta = array(); 1230 $is_group = bp_is_group(); 1231 1232 if ( isset( $GLOBALS['groups_template']->group ) ) { 1233 $group = $GLOBALS['groups_template']->group; 1234 } else { 1235 $group = groups_get_current_group(); 1236 } 1237 1238 if ( ! $group ) { 1239 return ''; 1240 } 1241 1242 if ( ! $keys && ! $is_group ) { 1243 $keys = array( 'status', 'count' ); 1244 } 1245 1246 foreach ( $keys as $key ) { 1247 switch ( $key ) { 1248 case 'status' : 1249 $group_meta['status'] = bp_get_group_type( $group ); 1250 break; 1251 1252 case 'count' : 1253 $group_meta['count'] = bp_get_group_member_count( $group ); 1254 break; 1255 1256 case 'group_type_list' : 1257 $group_meta['group_type_list'] = bp_get_group_type_list( $group->id ); 1258 break; 1259 1260 case 'description' : 1261 $group_meta['description'] = bp_get_group_description( $group ); 1262 break; 1263 1264 case 'extra' : 1265 $group_meta['extra'] = ''; 1266 1267 if ( $is_group ) { 1268 $group_meta['extra'] = bp_nouveau_get_hooked_group_meta(); 1269 } 1270 break; 1271 } 1272 } 1273 1274 /** 1275 * Filter to add/remove Group template meta. 1276 * 1277 * @since 3.0.0 1278 * 1279 * @param array $group_meta The list of meta to output. 1280 * @param object $group The current Group of the loop object. 1281 * @param bool $is_group True if a single group is displayed. False otherwise. 1282 */ 1283 return apply_filters( 'bp_nouveau_get_group_meta', $group_meta, $group, $is_group ); 1284 } 1285 1286 /** 1287 * Load the appropriate content for the single group pages 1288 * 1289 * @since 3.0.0 1290 */ 1291 function bp_nouveau_group_template_part() { 1292 /** 1293 * Fires before the display of the group home body. 1294 * 1295 * @since 1.2.0 1296 */ 1297 do_action( 'bp_before_group_body' ); 1298 1299 $bp_is_group_home = bp_is_group_home(); 1300 1301 if ( $bp_is_group_home && ! bp_current_user_can( 'groups_access_group' ) ) { 1302 /** 1303 * Fires before the display of the group status message. 1304 * 1305 * @since 1.1.0 1306 */ 1307 do_action( 'bp_before_group_status_message' ); 1308 ?> 1309 1310 <div id="message" class="info"> 1311 <p><?php bp_group_status_message(); ?></p> 1312 </div> 1313 1314 <?php 1315 1316 /** 1317 * Fires after the display of the group status message. 1318 * 1319 * @since 1.1.0 1320 */ 1321 do_action( 'bp_after_group_status_message' ); 1322 1323 // We have a front template, Use BuddyPress function to load it. 1324 } elseif ( $bp_is_group_home && false !== bp_groups_get_front_template() ) { 1325 bp_groups_front_template_part(); 1326 1327 // Otherwise use BP_Nouveau template hierarchy 1328 } else { 1329 $template = 'plugins'; 1330 1331 // the home page 1332 if ( $bp_is_group_home ) { 1333 if ( bp_is_active( 'activity' ) ) { 1334 $template = 'activity'; 1335 } else { 1336 $template = 'members'; 1337 } 1338 1339 // Not the home page 1340 } elseif ( bp_is_group_admin_page() ) { 1341 $template = 'admin'; 1342 } elseif ( bp_is_group_activity() ) { 1343 $template = 'activity'; 1344 } elseif ( bp_is_group_members() ) { 1345 $template = 'members'; 1346 } elseif ( bp_is_group_invites() ) { 1347 $template = 'send-invites'; 1348 } elseif ( bp_is_group_membership_request() ) { 1349 $template = 'request-membership'; 1350 } 1351 1352 bp_nouveau_group_get_template_part( $template ); 1353 } 1354 1355 /** 1356 * Fires after the display of the group home body. 1357 * 1358 * @since 1.2.0 1359 */ 1360 do_action( 'bp_after_group_body' ); 1361 } 1362 1363 /** 1364 * Use the appropriate Group header and enjoy a template hierarchy 1365 * 1366 * @since 3.0.0 1367 */ 1368 function bp_nouveau_group_header_template_part() { 1369 $template = 'group-header'; 1370 1371 if ( bp_group_use_cover_image_header() ) { 1372 $template = 'cover-image-header'; 1373 } 1374 1375 /** 1376 * Fires before the display of a group's header. 1377 * 1378 * @since 1.2.0 1379 */ 1380 do_action( 'bp_before_group_header' ); 1381 1382 // Get the template part for the header 1383 bp_nouveau_group_get_template_part( $template ); 1384 1385 /** 1386 * Fires after the display of a group's header. 1387 * 1388 * @since 1.2.0 1389 */ 1390 do_action( 'bp_after_group_header' ); 1391 1392 bp_nouveau_template_notices(); 1393 } 1394 1395 /** 1396 * Get a link to set the Group's default front page and directly 1397 * reach the Customizer section where it's possible to do it. 1398 * 1399 * @since 3.0.0 1400 * 1401 * @return string HTML Output 1402 */ 1403 function bp_nouveau_groups_get_customizer_option_link() { 1404 return bp_nouveau_get_customizer_link( 1405 array( 1406 'object' => 'group', 1407 'autofocus' => 'bp_nouveau_group_front_page', 1408 'text' => __( 'Groups default front page', 'buddypress' ), 1409 ) 1410 ); 1411 } 1412 1413 /** 1414 * Get a link to set the Group's front page widgets and directly 1415 * reach the Customizer section where it's possible to do it. 1416 * 1417 * @since 3.0.0 1418 * 1419 * @return string HTML Output 1420 */ 1421 function bp_nouveau_groups_get_customizer_widgets_link() { 1422 return bp_nouveau_get_customizer_link( 1423 array( 1424 'object' => 'group', 1425 'autofocus' => 'sidebar-widgets-sidebar-buddypress-groups', 1426 'text' => __( '(BuddyPress) Widgets', 'buddypress' ), 1427 ) 1428 ); 1429 } 1430 1431 /** 1432 * Output the group description excerpt 1433 * 1434 * @since 3.0.0 1435 * 1436 * @param object $group Optional. The group being referenced. 1437 * Defaults to the group currently being iterated on in the groups loop. 1438 * @param int $length Optional. Length of returned string, including ellipsis. Default: 100. 1439 * 1440 * @return string Excerpt. 1441 */ 1442 function bp_nouveau_group_description_excerpt( $group = null, $length = null ) { 1443 echo bp_nouveau_get_group_description_excerpt( $group, $length ); 1444 } 1445 1446 /** 1447 * Filters the excerpt of a group description. 1448 * 1449 * Checks if the group loop is set as a 'Grid' layout and returns a reduced excerpt. 1450 * 1451 * @since 3.0.0 1452 * 1453 * @param object $group Optional. The group being referenced. Defaults to the group currently being 1454 * iterated on in the groups loop. 1455 * @param int $length Optional. Length of returned string, including ellipsis. Default: 100. 1456 * 1457 * @return string Excerpt. 1458 */ 1459 function bp_nouveau_get_group_description_excerpt( $group = null, $length = null ) { 1460 global $groups_template; 1461 1462 if ( ! $group ) { 1463 $group =& $groups_template->group; 1464 } 1465 1466 /** 1467 * If this is a grid layout but no length is passed in set a shorter 1468 * default value otherwise use the passed in value. 1469 * If not a grid then the BP core default is used or passed in value. 1470 */ 1471 if ( bp_nouveau_loop_is_grid() && 'groups' === bp_current_component() ) { 1472 if ( ! $length ) { 1473 $length = 100; 1474 } else { 1475 $length = $length; 1476 } 1477 } 1478 1479 /** 1480 * Filters the excerpt of a group description. 1481 * 1482 * @since 3.0.0 1483 * 1484 * @param string $value Excerpt of a group description. 1485 * @param object $group Object for group whose description is made into an excerpt. 1486 */ 1487 return apply_filters( 'bp_nouveau_get_group_description_excerpt', bp_create_excerpt( $group->description, $length ), $group ); 1488 } 1489 1490 /** 1491 * Output "checked" attribute to determine if the group type should be checked. 1492 * 1493 * @since 3.2.0 1494 * 1495 * @param object $type Group type object. See bp_groups_get_group_type_object(). 1496 */ 1497 function bp_nouveau_group_type_checked( $type = null ) { 1498 if ( ! is_object( $type ) ) { 1499 return; 1500 } 1501 1502 // Group creation screen requires a different check. 1503 if ( bp_is_group_create() ) { 1504 checked( true, ! empty( $type->create_screen_checked ) ); 1505 } elseif ( bp_is_group() ) { 1506 checked( bp_groups_has_group_type( bp_get_current_group_id(), $type->name ) ); 1507 } 1508 } 1509 1510 /** 1511 * Adds the "Notify group members of these changes" checkbox to the Manage > Details panel. 1512 * 1513 * See #7837 for background on why this technique is required. 1514 * 1515 * @since 4.0.0 1516 */ 1517 function bp_nouveau_add_notify_group_members_checkbox() { 1518 printf( '<p class="bp-controls-wrap"> 1519 <label for="group-notify-members" class="bp-label-text"> 1520 <input type="checkbox" name="group-notify-members" id="group-notify-members" value="1" /> %s 1521 </label> 1522 </p>', esc_html__( 'Notify group members of these changes via email', 'buddypress' ) ); 1523 } 1524 add_action( 'groups_custom_group_fields_editable', 'bp_nouveau_add_notify_group_members_checkbox', 20 );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sat Feb 8 01:01:00 2025 | Cross-referenced by PHPXref 0.7.1 |