[ Index ] |
PHP Cross Reference of BuddyPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * BuddyPress Groups Classes. 4 * 5 * @package BuddyPress 6 * @subpackage GroupsClasses 7 * @since 1.1.0 8 */ 9 10 // Exit if accessed directly. 11 defined( 'ABSPATH' ) || exit; 12 13 /** 14 * API for creating group extensions without having to hardcode the content into 15 * the theme. 16 * 17 * To implement, extend this class. In your constructor, pass an optional array 18 * of arguments to parent::init() to configure your widget. The config array 19 * supports the following values: 20 * - 'slug' A unique identifier for your extension. This value will be used 21 * to build URLs, so make it URL-safe. 22 * - 'name' A translatable name for your extension. This value is used to 23 * populate the navigation tab, as well as the default titles for admin/ 24 * edit/create tabs. 25 * - 'visibility' Set to 'public' (default) for your extension (the main tab 26 * as well as the widget) to be available to anyone who can access the 27 * group, 'private' otherwise. 28 * - 'nav_item_position' An integer explaining where the nav item should 29 * appear in the tab list. 30 * - 'enable_nav_item' Set to true for your extension's main tab to be 31 * available to anyone who can access the group. 32 * - 'nav_item_name' The translatable text you want to appear in the nav tab. 33 * Defaults to the value of 'name'. 34 * - 'display_hook' The WordPress action that the widget_display() method is 35 * hooked to. 36 * - 'template_file' The template file that will be used to load the content 37 * of your main extension tab. Defaults to 'groups/single/plugins.php'. 38 * - 'screens' A multi-dimensional array, described below. 39 * - 'access' Which users can visit the plugin's tab. 40 * - 'show_tab' Which users can see the plugin's navigation tab. 41 * 42 * BP_Group_Extension uses the concept of "settings screens". There are three 43 * contexts for settings screens: 44 * - 'create', which inserts a new step into the group creation process 45 * - 'edit', which adds a tab for your extension into the Admin section of 46 * a group 47 * - 'admin', which adds a metabox to the Groups administration panel in the 48 * WordPress Dashboard 49 * Each of these settings screens is populated by a pair of methods: one that 50 * creates the markup for the screen, and one that processes form data 51 * submitted from the screen. If your plugin needs screens in all three 52 * contexts, and if the markup and form processing logic will be the same in 53 * each case, you can define two methods to handle all of the screens: 54 * function settings_screen() {} 55 * function settings_screen_save() {} 56 * If one or more of your settings screen needs separate logic, you may define 57 * context-specific methods, for example: 58 * function edit_screen() {} 59 * function edit_screen_save() {} 60 * BP_Group_Extension will use the more specific methods if they are available. 61 * 62 * You can further customize the settings screens (tab names, etc) by passing 63 * an optional 'screens' parameter to the init array. The format is as follows: 64 * 'screens' => array( 65 * 'create' => array( 66 * 'slug' => 'foo', 67 * 'name' => 'Foo', 68 * 'position' => 55, 69 * 'screen_callback' => 'my_create_screen_callback', 70 * 'screen_save_callback' => 'my_create_screen_save_callback', 71 * ), 72 * 'edit' => array( // ... 73 * ), 74 * Only provide those arguments that you actually want to change from the 75 * default configuration. BP_Group_Extension will do the rest. 76 * 77 * Note that the 'edit' screen accepts an additional parameter: 'submit_text', 78 * which defines the text of the Submit button automatically added to the Edit 79 * screen of the extension (defaults to 'Save Changes'). Also, the 'admin' 80 * screen accepts two additional parameters: 'metabox_priority' and 81 * 'metabox_context'. See the docs for add_meta_box() for more details on these 82 * arguments. 83 * 84 * Prior to BuddyPress 1.7, group extension configurations were set slightly 85 * differently. The legacy method is still supported, though deprecated. 86 * 87 * @since 1.1.0 88 */ 89 class BP_Group_Extension { 90 91 /** Public ************************************************************/ 92 93 /** 94 * Information about this extension's screens. 95 * 96 * @since 1.8.0 97 * @var array 98 */ 99 public $screens = array(); 100 101 /** 102 * The name of the extending class. 103 * 104 * @since 1.8.0 105 * @var string 106 */ 107 public $class_name = ''; 108 109 /** 110 * A ReflectionClass object of the current extension. 111 * 112 * @since 1.8.0 113 * @var ReflectionClass 114 */ 115 public $class_reflection = null; 116 117 /** 118 * Parsed configuration parameters for the extension. 119 * 120 * @since 1.8.0 121 * @var array 122 */ 123 public $params = array(); 124 125 /** 126 * Raw config params, as passed by the extending class. 127 * 128 * @since 2.1.0 129 * @var array 130 */ 131 public $params_raw = array(); 132 133 /** 134 * The ID of the current group. 135 * 136 * @since 1.8.0 137 * @var int 138 */ 139 public $group_id = 0; 140 141 /** 142 * The slug of the current extension. 143 * 144 * @since 1.1.0 145 * @var string 146 */ 147 public $slug = ''; 148 149 /** 150 * The translatable name of the current extension. 151 * 152 * @since 1.1.0 153 * @var string 154 */ 155 public $name = ''; 156 157 /** 158 * The visibility of the extension tab. 'public' or 'private'. 159 * 160 * @since 1.1.0 161 * @var string 162 */ 163 public $visibility = 'public'; 164 165 /** 166 * The numeric position of the main nav item. 167 * 168 * @since 1.1.0 169 * @var int 170 */ 171 public $nav_item_position = 81; 172 173 /** 174 * Whether to show the nav item. 175 * 176 * @since 1.1.0 177 * @var bool 178 */ 179 public $enable_nav_item = true; 180 181 /** 182 * Whether the current user should see the navigation item. 183 * 184 * @since 2.1.0 185 * @var bool 186 */ 187 public $user_can_see_nav_item; 188 189 /** 190 * Whether the current user can visit the tab. 191 * 192 * @since 2.1.0 193 * @var bool 194 */ 195 public $user_can_visit; 196 197 /** 198 * The text of the nav item. Defaults to self::name. 199 * 200 * @since 1.1.0 201 * @var string 202 */ 203 public $nav_item_name = ''; 204 205 /** 206 * The WP action that self::widget_display() is attached to. 207 * 208 * Default: 'groups_custom_group_boxes'. 209 * 210 * @since 1.1.0 211 * @var string 212 */ 213 public $display_hook = 'groups_custom_group_boxes'; 214 215 /** 216 * The template file used to load the plugin content. 217 * 218 * Default: 'groups/single/plugins'. 219 * 220 * @since 1.1.0 221 * @var string 222 */ 223 public $template_file = 'groups/single/plugins'; 224 225 /** Protected *********************************************************/ 226 227 /** 228 * Has the extension been initialized? 229 * 230 * @since 1.8.0 231 * @var bool 232 */ 233 protected $initialized = false; 234 235 /** 236 * Extension properties as set by legacy extensions. 237 * 238 * @since 1.8.0 239 * @var array 240 */ 241 protected $legacy_properties = array(); 242 243 /** 244 * Converted legacy parameters. 245 * 246 * These are the extension properties as set by legacy extensions, but 247 * then converted to match the new format for params. 248 * 249 * @since 1.8.0 250 * @var array 251 */ 252 protected $legacy_properties_converted = array(); 253 254 /** 255 * Redirect location as defined by post-edit save callback. 256 * 257 * @since 2.1.0 258 * @var string 259 */ 260 protected $post_save_redirect; 261 262 /** 263 * Miscellaneous data as set by the __set() magic method. 264 * 265 * @since 1.8.0 266 * @var array 267 */ 268 protected $data = array(); 269 270 /** Screen Overrides **************************************************/ 271 272 /* 273 * Screen override methods are how your extension will display content 274 * and handle form submits. Your extension should only override those 275 * methods that it needs for its purposes. 276 */ 277 278 /** 279 * The content of the group tab. 280 * 281 * @since 1.1.0 282 * 283 * @param int|null $group_id ID of the group to display. 284 */ 285 public function display( $group_id = null ) {} 286 287 /** 288 * Content displayed in a widget sidebar, if applicable. 289 * 290 * @since 1.1.0 291 */ 292 public function widget_display() {} 293 294 /* 295 * *_screen() displays the settings form for the given context 296 * *_screen_save() processes data submitted via the settings form 297 * The settings_* methods are generic fallbacks, which can optionally 298 * be overridden by the more specific edit_*, create_*, and admin_* 299 * versions. 300 */ 301 public function settings_screen( $group_id = null ) {} 302 public function settings_screen_save( $group_id = null ) {} 303 public function edit_screen( $group_id = null ) {} 304 public function edit_screen_save( $group_id = null ) {} 305 public function create_screen( $group_id = null ) {} 306 public function create_screen_save( $group_id = null ) {} 307 public function admin_screen( $group_id = null ) {} 308 public function admin_screen_save( $group_id = null ) {} 309 310 /** Setup *************************************************************/ 311 312 /** 313 * Initialize the extension, using your config settings. 314 * 315 * Your plugin should call this method at the very end of its 316 * constructor, like so: 317 * 318 * public function __construct() { 319 * $args = array( 320 * 'slug' => 'my-group-extension', 321 * 'name' => 'My Group Extension', 322 * // ... 323 * ); 324 * 325 * parent::init( $args ); 326 * } 327 * 328 * @since 1.8.0 329 * @since 2.1.0 Added 'access' and 'show_tab' arguments to `$args`. 330 * 331 * @param array $args { 332 * Array of initialization arguments. 333 * @type string $slug Unique, URL-safe identifier for your extension. 334 * @type string $name Translatable name for your extension. Used to populate 335 * navigation items. 336 * @type string $visibility Optional. Set to 'public' for your extension (the main tab as well 337 * as the widget) to be available to anyone who can access the group; 338 * set to 'private' otherwise. Default: 'public'. 339 * @type int $nav_item_position Optional. Location of the nav item in the tab list. 340 * Default: 81. 341 * @type bool $enable_nav_item Optional. Whether the extension's tab should be accessible to 342 * anyone who can view the group. Default: true. 343 * @type string $nav_item_name Optional. The translatable text you want to appear in the nav tab. 344 * Default: the value of `$name`. 345 * @type string $display_hook Optional. The WordPress action that the widget_display() method is 346 * hooked to. Default: 'groups_custom_group_boxes'. 347 * @type string $template_file Optional. Theme-relative path to the template file BP should use 348 * to load the content of your main extension tab. 349 * Default: 'groups/single/plugins.php'. 350 * @type array $screens A multi-dimensional array of configuration information for the 351 * extension screens. See docblock of {@link BP_Group_Extension} 352 * for more details. 353 * @type string|array $access Which users can visit the plugin's tab. Possible values: 'anyone', 354 * 'loggedin', 'member', 'mod', 'admin' or 'noone'. ('member', 'mod', 355 * 'admin' refer to user's role in group.) Note that 'mod' targets 356 * only group moderators. If you want to allow access to group moderators 357 * and admins, specify `array( 'mod', 'admin' )`. Defaults to 'anyone' 358 * for public groups and 'member' for private groups. 359 * @type string|array $show_tab Which users can see the plugin's navigation tab. Possible values: 360 * 'anyone', 'loggedin', 'member', 'mod', 'admin' or 'noone'. 361 * ('member', 'mod', 'admin' refer to user's role in group.) Note 362 * that 'mod' targets only group moderators. If you want to show the 363 * tab to group moderators and admins, specify 364 * `array( 'mod', 'admin' )`. Defaults to 'anyone' for public groups 365 * and 'member' for private groups. 366 * } 367 */ 368 public function init( $args = array() ) { 369 // Store the raw arguments. 370 $this->params_raw = $args; 371 372 // Before this init() method was introduced, plugins were 373 // encouraged to set their config directly. For backward 374 // compatibility with these plugins, we detect whether this is 375 // one of those legacy plugins, and parse any legacy arguments 376 // with those passed to init(). 377 $this->parse_legacy_properties(); 378 $args = $this->parse_args_r( $args, $this->legacy_properties_converted ); 379 380 // Parse with defaults. 381 $this->params = $this->parse_args_r( $args, array( 382 'slug' => $this->slug, 383 'name' => $this->name, 384 'visibility' => $this->visibility, 385 'nav_item_position' => $this->nav_item_position, 386 'enable_nav_item' => (bool) $this->enable_nav_item, 387 'nav_item_name' => $this->nav_item_name, 388 'display_hook' => $this->display_hook, 389 'template_file' => $this->template_file, 390 'screens' => $this->get_default_screens(), 391 'access' => null, 392 'show_tab' => null, 393 ) ); 394 395 $this->initialized = true; 396 } 397 398 /** 399 * The main setup routine for the extension. 400 * 401 * This method contains the primary logic for setting up an extension's 402 * configuration, setting up backward compatibility for legacy plugins, 403 * and hooking the extension's screen functions into WP and BP. 404 * 405 * Marked 'public' because it must be accessible to add_action(). 406 * However, you should never need to invoke this method yourself - it 407 * is called automatically at the right point in the load order by 408 * bp_register_group_extension(). 409 * 410 * @since 1.1.0 411 */ 412 public function _register() { 413 414 // Detect and parse properties set by legacy extensions. 415 $this->parse_legacy_properties(); 416 417 // Initialize, if necessary. This should only happen for 418 // legacy extensions that don't call parent::init() themselves. 419 if ( true !== $this->initialized ) { 420 $this->init(); 421 } 422 423 // Set some config values, based on the parsed params. 424 $this->group_id = $this->get_group_id(); 425 $this->slug = $this->params['slug']; 426 $this->name = $this->params['name']; 427 $this->visibility = $this->params['visibility']; 428 $this->nav_item_position = $this->params['nav_item_position']; 429 $this->nav_item_name = $this->params['nav_item_name']; 430 $this->display_hook = $this->params['display_hook']; 431 $this->template_file = $this->params['template_file']; 432 433 // Configure 'screens': create, admin, and edit contexts. 434 $this->setup_screens(); 435 436 // Configure access-related settings. 437 $this->setup_access_settings(); 438 439 // Mirror configuration data so it's accessible to plugins 440 // that look for it in its old locations. 441 $this->setup_legacy_properties(); 442 443 // Hook the extension into BuddyPress. 444 $this->setup_display_hooks(); 445 $this->setup_create_hooks(); 446 $this->setup_edit_hooks(); 447 $this->setup_admin_hooks(); 448 } 449 450 /** 451 * Set up some basic info about the Extension. 452 * 453 * Here we collect the name of the extending class, as well as a 454 * ReflectionClass that is used in get_screen_callback() to determine 455 * whether your extension overrides certain callback methods. 456 * 457 * @since 1.8.0 458 */ 459 protected function setup_class_info() { 460 if ( empty( $this->class_name ) ) { 461 $this->class_name = get_class( $this ); 462 } 463 464 if ( is_null( $this->class_reflection ) ) { 465 $this->class_reflection = new ReflectionClass( $this->class_name ); 466 } 467 } 468 469 /** 470 * Get the current group ID. 471 * 472 * Check for: 473 * - current group 474 * - new group 475 * - group admin 476 * 477 * @since 1.8.0 478 * 479 * @return int 480 */ 481 public static function get_group_id() { 482 483 // Usually this will work. 484 $group_id = bp_get_current_group_id(); 485 486 // On the admin, get the group id out of the $_GET params. 487 if ( empty( $group_id ) && is_admin() && ( isset( $_GET['page'] ) && ( 'bp-groups' === $_GET['page'] ) ) && ! empty( $_GET['gid'] ) ) { 488 $group_id = (int) $_GET['gid']; 489 } 490 491 // This fallback will only be hit when the create step is very 492 // early. 493 if ( empty( $group_id ) && bp_get_new_group_id() ) { 494 $group_id = bp_get_new_group_id(); 495 } 496 497 // On some setups, the group id has to be fetched out of the 498 // $_POST array 499 // @todo Figure out why this is happening during group creation. 500 if ( empty( $group_id ) && isset( $_POST['group_id'] ) ) { 501 $group_id = (int) $_POST['group_id']; 502 } 503 504 return $group_id; 505 } 506 507 /** 508 * Gather configuration data about your screens. 509 * 510 * @since 1.8.0 511 * 512 * @return array 513 */ 514 protected function get_default_screens() { 515 $this->setup_class_info(); 516 517 $screens = array( 518 'create' => array( 519 'position' => 81, 520 ), 521 'edit' => array( 522 'submit_text' => __( 'Save Changes', 'buddypress' ), 523 ), 524 'admin' => array( 525 'metabox_context' => 'normal', 526 'metabox_priority' => 'core', 527 ), 528 ); 529 530 foreach ( $screens as $context => &$screen ) { 531 $screen['enabled'] = true; 532 $screen['name'] = $this->name; 533 $screen['slug'] = $this->slug; 534 535 $screen['screen_callback'] = $this->get_screen_callback( $context, 'screen' ); 536 $screen['screen_save_callback'] = $this->get_screen_callback( $context, 'screen_save' ); 537 } 538 539 return $screens; 540 } 541 542 /** 543 * Set up screens array based on params. 544 * 545 * @since 1.8.0 546 */ 547 protected function setup_screens() { 548 foreach ( (array) $this->params['screens'] as $context => $screen ) { 549 if ( empty( $screen['slug'] ) ) { 550 $screen['slug'] = $this->slug; 551 } 552 553 if ( empty( $screen['name'] ) ) { 554 $screen['name'] = $this->name; 555 } 556 557 $this->screens[ $context ] = $screen; 558 } 559 } 560 561 /** 562 * Set up access-related settings for this extension. 563 * 564 * @since 2.1.0 565 */ 566 protected function setup_access_settings() { 567 568 // Bail if no group ID is available. 569 if ( empty( $this->group_id ) ) { 570 return; 571 } 572 573 // Backward compatibility. 574 if ( isset( $this->params['enable_nav_item'] ) ) { 575 $this->enable_nav_item = (bool) $this->params['enable_nav_item']; 576 } 577 578 // Tab Access. 579 $this->user_can_visit = false; 580 581 // Backward compatibility for components that do not provide 582 // explicit 'access' parameter. 583 if ( empty( $this->params['access'] ) ) { 584 if ( false === $this->params['enable_nav_item'] ) { 585 $this->params['access'] = 'noone'; 586 } else { 587 $group = groups_get_group( $this->group_id ); 588 589 if ( ! empty( $group->status ) && 'public' === $group->status ) { 590 // Tabs in public groups are accessible to anyone by default. 591 $this->params['access'] = 'anyone'; 592 } else { 593 // All other groups have members-only as the default. 594 $this->params['access'] = 'member'; 595 } 596 } 597 } 598 599 // Parse multiple access conditions into an array. 600 $access_conditions = $this->params['access']; 601 if ( ! is_array( $access_conditions ) ) { 602 $access_conditions = explode( ',', $access_conditions ); 603 } 604 605 // If the current user meets at least one condition, the 606 // get access. 607 foreach ( $access_conditions as $access_condition ) { 608 if ( $this->user_meets_access_condition( $access_condition ) ) { 609 $this->user_can_visit = true; 610 break; 611 } 612 } 613 614 // Tab Visibility. 615 $this->user_can_see_nav_item = false; 616 617 // Backward compatibility for components that do not provide 618 // explicit 'show_tab' parameter. 619 if ( empty( $this->params['show_tab'] ) ) { 620 if ( false === $this->params['enable_nav_item'] ) { 621 // The enable_nav_item index is only false if it's been 622 // defined explicitly as such in the 623 // constructor. So we always trust this value. 624 $this->params['show_tab'] = 'noone'; 625 626 } elseif ( isset( $this->params_raw['enable_nav_item'] ) || isset( $this->params_raw['visibility'] ) ) { 627 // If enable_nav_item or visibility is passed, 628 // we assume this is a legacy extension. 629 // Legacy behavior is that enable_nav_item=true + 630 // visibility=private implies members-only. 631 if ( 'public' !== $this->visibility ) { 632 $this->params['show_tab'] = 'member'; 633 } else { 634 $this->params['show_tab'] = 'anyone'; 635 } 636 637 } else { 638 // No show_tab or enable_nav_item value is 639 // available, so match the value of 'access'. 640 $this->params['show_tab'] = $this->params['access']; 641 } 642 } 643 644 // Parse multiple access conditions into an array. 645 $access_conditions = $this->params['show_tab']; 646 if ( ! is_array( $access_conditions ) ) { 647 $access_conditions = explode( ',', $access_conditions ); 648 } 649 650 // If the current user meets at least one condition, the 651 // get access. 652 foreach ( $access_conditions as $access_condition ) { 653 if ( $this->user_meets_access_condition( $access_condition ) ) { 654 $this->user_can_see_nav_item = true; 655 break; 656 } 657 } 658 } 659 660 /** 661 * Check whether the current user meets an access condition. 662 * 663 * @since 2.1.0 664 * 665 * @param string $access_condition 'anyone', 'loggedin', 'member', 666 * 'mod', 'admin' or 'noone'. 667 * @return bool 668 */ 669 protected function user_meets_access_condition( $access_condition ) { 670 671 switch ( $access_condition ) { 672 case 'admin' : 673 $meets_condition = groups_is_user_admin( bp_loggedin_user_id(), $this->group_id ); 674 break; 675 676 case 'mod' : 677 $meets_condition = groups_is_user_mod( bp_loggedin_user_id(), $this->group_id ); 678 break; 679 680 case 'member' : 681 $meets_condition = groups_is_user_member( bp_loggedin_user_id(), $this->group_id ); 682 break; 683 684 case 'loggedin' : 685 $meets_condition = is_user_logged_in(); 686 break; 687 688 case 'noone' : 689 $meets_condition = false; 690 break; 691 692 case 'anyone' : 693 default : 694 $meets_condition = true; 695 break; 696 } 697 698 return $meets_condition; 699 } 700 701 /** Display ***********************************************************/ 702 703 /** 704 * Hook this extension's group tab into BuddyPress, if necessary. 705 * 706 * @since 1.8.0 707 */ 708 protected function setup_display_hooks() { 709 710 // Bail if not a group. 711 if ( ! bp_is_group() ) { 712 return; 713 } 714 715 // Backward compatibility only. 716 if ( ( 'public' !== $this->visibility ) && ! buddypress()->groups->current_group->user_has_access ) { 717 return; 718 } 719 720 // If the user can see the nav item, we create it. 721 $user_can_see_nav_item = $this->user_can_see_nav_item(); 722 723 if ( $user_can_see_nav_item ) { 724 $group_permalink = bp_get_group_permalink( groups_get_current_group() ); 725 726 bp_core_create_subnav_link( array( 727 'name' => ! $this->nav_item_name ? $this->name : $this->nav_item_name, 728 'slug' => $this->slug, 729 'parent_slug' => bp_get_current_group_slug(), 730 'parent_url' => $group_permalink, 731 'position' => $this->nav_item_position, 732 'item_css_id' => 'nav-' . $this->slug, 733 'screen_function' => array( &$this, '_display_hook' ), 734 'user_has_access' => $user_can_see_nav_item, 735 'no_access_url' => $group_permalink, 736 ), 'groups' ); 737 } 738 739 // If the user can visit the screen, we register it. 740 $user_can_visit = $this->user_can_visit(); 741 742 if ( $user_can_visit ) { 743 $group_permalink = bp_get_group_permalink( groups_get_current_group() ); 744 745 bp_core_register_subnav_screen_function( array( 746 'slug' => $this->slug, 747 'parent_slug' => bp_get_current_group_slug(), 748 'screen_function' => array( &$this, '_display_hook' ), 749 'user_has_access' => $user_can_visit, 750 'no_access_url' => $group_permalink, 751 ), 'groups' ); 752 753 // When we are viewing the extension display page, set the title and options title. 754 if ( bp_is_current_action( $this->slug ) ) { 755 add_filter( 'bp_group_user_has_access', array( $this, 'group_access_protection' ), 10, 2 ); 756 757 $extension_name = $this->name; 758 add_action( 'bp_template_content_header', function() use ( $extension_name ) { 759 echo esc_attr( $extension_name ); 760 } ); 761 add_action( 'bp_template_title', function() use ( $extension_name ) { 762 echo esc_attr( $extension_name ); 763 } ); 764 } 765 } 766 767 // Hook the group home widget. 768 if ( bp_is_group_home() ) { 769 add_action( $this->display_hook, array( &$this, 'widget_display' ) ); 770 } 771 } 772 773 /** 774 * Hook the main display method, and loads the template file. 775 * 776 * @since 1.1.0 777 */ 778 public function _display_hook() { 779 add_action( 'bp_template_content', array( &$this, 'call_display' ) ); 780 781 /** 782 * Filters the template to load for the main display method. 783 * 784 * @since 1.0.0 785 * 786 * @param string $template_file Path to the template to load. 787 */ 788 bp_core_load_template( apply_filters( 'bp_core_template_plugin', $this->template_file ) ); 789 } 790 791 /** 792 * Call the display() method. 793 * 794 * We use this wrapper so that we can pass the group_id to the 795 * display() callback. 796 * 797 * @since 2.1.1 798 */ 799 public function call_display() { 800 $this->display( $this->group_id ); 801 } 802 803 /** 804 * Determine whether the current user should see this nav tab. 805 * 806 * Note that this controls only the display of the navigation item. 807 * Access to the tab is controlled by the user_can_visit() check. 808 * 809 * @since 2.1.0 810 * 811 * @param bool $user_can_see_nav_item Whether or not the user can see the nav item. 812 * @return bool 813 */ 814 public function user_can_see_nav_item( $user_can_see_nav_item = false ) { 815 816 // Always allow moderators to see nav items, even if explicitly 'noone' 817 if ( ( 'noone' !== $this->params['show_tab'] ) && bp_current_user_can( 'bp_moderate' ) ) { 818 return true; 819 } 820 821 return $this->user_can_see_nav_item; 822 } 823 824 /** 825 * Determine whether the current user has access to visit this tab. 826 * 827 * Note that this controls the ability of a user to access a tab. 828 * Display of the navigation item is controlled by user_can_see_nav_item(). 829 * 830 * @since 2.1.0 831 * 832 * @param bool $user_can_visit Whether or not the user can visit the tab. 833 * @return bool 834 */ 835 public function user_can_visit( $user_can_visit = false ) { 836 837 // Always allow moderators to visit a tab, even if explicitly 'noone' 838 if ( ( 'noone' !== $this->params['access'] ) && bp_current_user_can( 'bp_moderate' ) ) { 839 return true; 840 } 841 842 return $this->user_can_visit; 843 } 844 845 /** 846 * Filter the access check in bp_groups_group_access_protection() for this extension. 847 * 848 * Note that $no_access_args is passed by reference, as there are some 849 * circumstances where the bp_core_no_access() arguments need to be 850 * modified before the redirect takes place. 851 * 852 * @since 2.1.0 853 * 854 * @param bool $user_can_visit Whether or not the user can visit the tab. 855 * @param array $no_access_args Array of args to help determine access. 856 * @return bool 857 */ 858 public function group_access_protection( $user_can_visit, &$no_access_args ) { 859 $user_can_visit = $this->user_can_visit(); 860 861 if ( ! $user_can_visit && is_user_logged_in() ) { 862 $current_group = groups_get_group( $this->group_id ); 863 864 $no_access_args['message'] = __( 'You do not have access to this content.', 'buddypress' ); 865 $no_access_args['root'] = bp_get_group_permalink( $current_group ) . 'home/'; 866 $no_access_args['redirect'] = false; 867 } 868 869 return $user_can_visit; 870 } 871 872 873 /** Create ************************************************************/ 874 875 /** 876 * Hook this extension's Create step into BuddyPress, if necessary. 877 * 878 * @since 1.8.0 879 */ 880 protected function setup_create_hooks() { 881 if ( ! $this->is_screen_enabled( 'create' ) ) { 882 return; 883 } 884 885 $screen = $this->screens['create']; 886 887 // Insert the group creation step for the new group extension. 888 buddypress()->groups->group_creation_steps[ $screen['slug'] ] = array( 889 'name' => $screen['name'], 890 'slug' => $screen['slug'], 891 'position' => $screen['position'], 892 ); 893 894 // The maybe_ methods check to see whether the create_* 895 // callbacks should be invoked (ie, are we on the 896 // correct group creation step). Hooked in separate 897 // methods because current creation step info not yet 898 // available at this point. 899 add_action( 'groups_custom_create_steps', array( $this, 'maybe_create_screen' ) ); 900 add_action( 'groups_create_group_step_save_' . $screen['slug'], array( $this, 'maybe_create_screen_save' ) ); 901 } 902 903 /** 904 * Call the create_screen() method, if we're on the right page. 905 * 906 * @since 1.8.0 907 */ 908 public function maybe_create_screen() { 909 if ( ! bp_is_group_creation_step( $this->screens['create']['slug'] ) ) { 910 return; 911 } 912 913 call_user_func( $this->screens['create']['screen_callback'], $this->group_id ); 914 $this->nonce_field( 'create' ); 915 916 // The create screen requires an additional nonce field 917 // due to a quirk in the way the templates are built. 918 wp_nonce_field( 'groups_create_save_' . bp_get_groups_current_create_step(), '_wpnonce', false ); 919 } 920 921 /** 922 * Call the create_screen_save() method, if we're on the right page. 923 * 924 * @since 1.8.0 925 */ 926 public function maybe_create_screen_save() { 927 if ( ! bp_is_group_creation_step( $this->screens['create']['slug'] ) ) { 928 return; 929 } 930 931 $this->check_nonce( 'create' ); 932 call_user_func( $this->screens['create']['screen_save_callback'], $this->group_id ); 933 } 934 935 /** Edit **************************************************************/ 936 937 /** 938 * Hook this extension's Edit panel into BuddyPress, if necessary. 939 * 940 * @since 1.8.0 941 */ 942 protected function setup_edit_hooks() { 943 // Bail if not in a group. 944 if ( ! bp_is_group() ) { 945 return; 946 } 947 948 // Bail if not an edit screen. 949 if ( ! $this->is_screen_enabled( 'edit' ) || ! bp_is_item_admin() ) { 950 return; 951 } 952 953 $screen = $this->screens['edit']; 954 955 $position = isset( $screen['position'] ) ? (int) $screen['position'] : 10; 956 $position += 40; 957 958 $current_group = groups_get_current_group(); 959 $admin_link = trailingslashit( bp_get_group_permalink( $current_group ) . 'admin' ); 960 961 $subnav_args = array( 962 'name' => $screen['name'], 963 'slug' => $screen['slug'], 964 'parent_slug' => $current_group->slug . '_manage', 965 'parent_url' => $admin_link, 966 'user_has_access' => bp_is_item_admin(), 967 'position' => $position, 968 'screen_function' => 'groups_screen_group_admin', 969 ); 970 971 // Should we add a menu to the Group's WP Admin Bar. 972 if ( ! empty( $screen['show_in_admin_bar'] ) ) { 973 $subnav_args['show_in_admin_bar'] = true; 974 } 975 976 // Add the tab to the manage navigation. 977 bp_core_new_subnav_item( $subnav_args, 'groups' ); 978 979 // Catch the edit screen and forward it to the plugin template. 980 if ( bp_is_groups_component() && bp_is_current_action( 'admin' ) && bp_is_action_variable( $screen['slug'], 0 ) ) { 981 $this->call_edit_screen_save( $this->group_id ); 982 983 add_action( 'groups_custom_edit_steps', array( &$this, 'call_edit_screen' ) ); 984 985 // Determine the proper template and save for later 986 // loading. 987 if ( '' !== bp_locate_template( array( 'groups/single/home.php' ), false ) ) { 988 $this->edit_screen_template = '/groups/single/home'; 989 } else { 990 add_action( 'bp_template_content_header', function() { 991 echo '<ul class="content-header-nav">'; 992 bp_group_admin_tabs(); 993 echo '</ul>'; 994 } ); 995 add_action( 'bp_template_content', array( &$this, 'call_edit_screen' ) ); 996 $this->edit_screen_template = '/groups/single/plugins'; 997 } 998 999 // We load the template at bp_screens, to give all 1000 // extensions a chance to load. 1001 add_action( 'bp_screens', array( $this, 'call_edit_screen_template_loader' ) ); 1002 } 1003 } 1004 1005 /** 1006 * Call the edit_screen() method. 1007 * 1008 * Previous versions of BP_Group_Extension required plugins to provide 1009 * their own Submit button and nonce fields when building markup. In 1010 * BP 1.8, this requirement was lifted - BP_Group_Extension now handles 1011 * all required submit buttons and nonces. 1012 * 1013 * We put the edit screen markup into an output buffer before echoing. 1014 * This is so that we can check for the presence of a hardcoded submit 1015 * button, as would be present in legacy plugins; if one is found, we 1016 * do not auto-add our own button. 1017 * 1018 * @since 1.8.0 1019 */ 1020 public function call_edit_screen() { 1021 ob_start(); 1022 call_user_func( $this->screens['edit']['screen_callback'], $this->group_id ); 1023 $screen = ob_get_contents(); 1024 ob_end_clean(); 1025 1026 echo $this->maybe_add_submit_button( $screen ); 1027 1028 $this->nonce_field( 'edit' ); 1029 } 1030 1031 /** 1032 * Check the nonce, and call the edit_screen_save() method. 1033 * 1034 * @since 1.8.0 1035 */ 1036 public function call_edit_screen_save() { 1037 if ( empty( $_POST ) ) { 1038 return; 1039 } 1040 1041 // When DOING_AJAX, the POST global will be populated, but we 1042 // should assume it's a save. 1043 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { 1044 return; 1045 } 1046 1047 $this->check_nonce( 'edit' ); 1048 1049 // Detect whether the screen_save_callback is performing a 1050 // redirect, so that we don't do one of our own. 1051 add_filter( 'wp_redirect', array( $this, 'detect_post_save_redirect' ) ); 1052 1053 // Call the extension's save routine. 1054 call_user_func( $this->screens['edit']['screen_save_callback'], $this->group_id ); 1055 1056 // Clean up detection filters. 1057 remove_filter( 'wp_redirect', array( $this, 'detect_post_save_redirect' ) ); 1058 1059 // Perform a redirect only if one has not already taken place. 1060 if ( empty( $this->post_save_redirect ) ) { 1061 1062 /** 1063 * Filters the URL to redirect to after group edit screen save. 1064 * 1065 * Only runs if a redirect has not already occurred. 1066 * 1067 * @since 2.1.0 1068 * 1069 * @param string $value URL to redirect to. 1070 */ 1071 $redirect_to = apply_filters( 'bp_group_extension_edit_screen_save_redirect', bp_get_requested_url( ) ); 1072 1073 bp_core_redirect( $redirect_to ); 1074 die(); 1075 } 1076 } 1077 1078 /** 1079 * Load the template that houses the Edit screen. 1080 * 1081 * Separated out into a callback so that it can run after all other 1082 * Group Extensions have had a chance to register their navigation, to 1083 * avoid missing tabs. 1084 * 1085 * Hooked to 'bp_screens'. 1086 * 1087 * @since 1.8.0 1088 * 1089 * @see BP_Group_Extension::setup_edit_hooks() 1090 */ 1091 public function call_edit_screen_template_loader() { 1092 bp_core_load_template( $this->edit_screen_template ); 1093 } 1094 1095 /** 1096 * Add a submit button to the edit form, if it needs one. 1097 * 1098 * There's an inconsistency in the way that the group Edit and Create 1099 * screens are rendered: the Create screen has a submit button built 1100 * in, but the Edit screen does not. This function allows plugin 1101 * authors to write markup that does not contain the submit button for 1102 * use on both the Create and Edit screens - BP will provide the button 1103 * if one is not found. 1104 * 1105 * @since 1.8.0 1106 * 1107 * @param string $screen The screen markup, captured in the output 1108 * buffer. 1109 * @return string $screen The same markup, with a submit button added. 1110 */ 1111 protected function maybe_add_submit_button( $screen = '' ) { 1112 if ( $this->has_submit_button( $screen ) ) { 1113 return $screen; 1114 } 1115 1116 return $screen . sprintf( 1117 '<div id="%s"><input type="submit" name="save" value="%s" id="%s"></div>', 1118 'bp-group-edit-' . $this->slug . '-submit-wrapper', 1119 $this->screens['edit']['submit_text'], 1120 'bp-group-edit-' . $this->slug . '-submit' 1121 ); 1122 } 1123 1124 /** 1125 * Does the given markup have a submit button? 1126 * 1127 * @since 1.8.0 1128 * 1129 * @param string $screen The markup to check. 1130 * @return bool True if a Submit button is found, otherwise false. 1131 */ 1132 public static function has_submit_button( $screen = '' ) { 1133 $pattern = "/<input[^>]+type=[\'\"]submit[\'\"]/"; 1134 preg_match( $pattern, $screen, $matches ); 1135 return ! empty( $matches[0] ); 1136 } 1137 1138 /** 1139 * Detect redirects hardcoded into edit_screen_save() callbacks. 1140 * 1141 * @since 2.1.0 1142 * 1143 * @param string $redirect Redirect string. 1144 * @return string 1145 */ 1146 public function detect_post_save_redirect( $redirect = '' ) { 1147 if ( ! empty( $redirect ) ) { 1148 $this->post_save_redirect = $redirect; 1149 } 1150 1151 return $redirect; 1152 } 1153 1154 /** Admin *************************************************************/ 1155 1156 /** 1157 * Hook this extension's Admin metabox into BuddyPress, if necessary. 1158 * 1159 * @since 1.8.0 1160 */ 1161 protected function setup_admin_hooks() { 1162 if ( ! $this->is_screen_enabled( 'admin' ) || ! is_admin() ) { 1163 return; 1164 } 1165 1166 // Hook the admin screen markup function to the content hook. 1167 add_action( 'bp_groups_admin_meta_box_content_' . $this->slug, array( $this, 'call_admin_screen' ) ); 1168 1169 // Initialize the metabox. 1170 add_action( 'bp_groups_admin_meta_boxes', array( $this, '_meta_box_display_callback' ) ); 1171 1172 // Catch the metabox save. 1173 add_action( 'bp_group_admin_edit_after', array( $this, 'call_admin_screen_save' ), 10 ); 1174 } 1175 1176 /** 1177 * Call the admin_screen() method, and add a nonce field. 1178 * 1179 * @since 1.8.0 1180 */ 1181 public function call_admin_screen() { 1182 call_user_func( $this->screens['admin']['screen_callback'], $this->group_id ); 1183 $this->nonce_field( 'admin' ); 1184 } 1185 1186 /** 1187 * Check the nonce, and call the admin_screen_save() method. 1188 * 1189 * @since 1.8.0 1190 */ 1191 public function call_admin_screen_save() { 1192 $this->check_nonce( 'admin' ); 1193 call_user_func( $this->screens['admin']['screen_save_callback'], $this->group_id ); 1194 } 1195 1196 /** 1197 * Create the Dashboard meta box for this extension. 1198 * 1199 * @since 1.7.0 1200 */ 1201 public function _meta_box_display_callback() { 1202 $group_id = isset( $_GET['gid'] ) ? (int) $_GET['gid'] : 0; 1203 $screen = $this->screens['admin']; 1204 1205 $extension_slug = $this->slug; 1206 $callback = function() use ( $extension_slug, $group_id ) { 1207 do_action( 'bp_groups_admin_meta_box_content_' . $extension_slug, $group_id ); 1208 }; 1209 1210 add_meta_box( 1211 $screen['slug'], 1212 $screen['name'], 1213 $callback, 1214 get_current_screen()->id, 1215 $screen['metabox_context'], 1216 $screen['metabox_priority'] 1217 ); 1218 } 1219 1220 1221 /** Utilities *********************************************************/ 1222 1223 /** 1224 * Generate the nonce fields for a settings form. 1225 * 1226 * The nonce field name (the second param passed to wp_nonce_field) 1227 * contains this extension's slug and is thus unique to this extension. 1228 * This is necessary because in some cases (namely, the Dashboard), 1229 * more than one extension may generate nonces on the same page, and we 1230 * must avoid name clashes. 1231 * 1232 * @since 1.8.0 1233 * 1234 * @param string $context Screen context. 'create', 'edit', or 'admin'. 1235 */ 1236 public function nonce_field( $context = '' ) { 1237 wp_nonce_field( 'bp_group_extension_' . $this->slug . '_' . $context, '_bp_group_' . $context . '_nonce_' . $this->slug ); 1238 } 1239 1240 /** 1241 * Check the nonce on a submitted settings form. 1242 * 1243 * @since 1.8.0 1244 * 1245 * @param string $context Screen context. 'create', 'edit', or 'admin'. 1246 */ 1247 public function check_nonce( $context = '' ) { 1248 check_admin_referer( 'bp_group_extension_' . $this->slug . '_' . $context, '_bp_group_' . $context . '_nonce_' . $this->slug ); 1249 } 1250 1251 /** 1252 * Is the specified screen enabled? 1253 * 1254 * To be enabled, a screen must both have the 'enabled' key set to true 1255 * (legacy: $this->enable_create_step, etc), and its screen_callback 1256 * must also exist and be callable. 1257 * 1258 * @since 1.8.0 1259 * 1260 * @param string $context Screen context. 'create', 'edit', or 'admin'. 1261 * @return bool True if the screen is enabled, otherwise false. 1262 */ 1263 public function is_screen_enabled( $context = '' ) { 1264 $enabled = false; 1265 1266 if ( isset( $this->screens[ $context ] ) ) { 1267 $enabled = $this->screens[ $context ]['enabled'] && is_callable( $this->screens[ $context ]['screen_callback'] ); 1268 } 1269 1270 return (bool) $enabled; 1271 } 1272 1273 /** 1274 * Get the appropriate screen callback for the specified context/type. 1275 * 1276 * BP Group Extensions have three special "screen contexts": create, 1277 * admin, and edit. Each of these contexts has a corresponding 1278 * _screen() and _screen_save() method, which allow group extension 1279 * plugins to define different markup and logic for each context. 1280 * 1281 * BP also supports fallback settings_screen() and 1282 * settings_screen_save() methods, which can be used to define markup 1283 * and logic that is shared between context. For each context, you may 1284 * either provide context-specific methods, or you can let BP fall back 1285 * on the shared settings_* callbacks. 1286 * 1287 * For example, consider a BP_Group_Extension implementation that looks 1288 * like this: 1289 * 1290 * // ... 1291 * function create_screen( $group_id ) { ... } 1292 * function create_screen_save( $group_id ) { ... } 1293 * function settings_screen( $group_id ) { ... } 1294 * function settings_screen_save( $group_id ) { ... } 1295 * // ... 1296 * 1297 * BP_Group_Extension will use your create_* methods for the Create 1298 * steps, and will use your generic settings_* methods for the Edit 1299 * and Admin contexts. This schema allows plugin authors maximum 1300 * flexibility without having to repeat themselves. 1301 * 1302 * The get_screen_callback() method uses a ReflectionClass object to 1303 * determine whether your extension has provided a given callback. 1304 * 1305 * @since 1.8.0 1306 * 1307 * @param string $context Screen context. 'create', 'edit', or 'admin'. 1308 * @param string $type Screen type. 'screen' or 'screen_save'. Default: 1309 * 'screen'. 1310 * @return callable A callable function handle. 1311 */ 1312 public function get_screen_callback( $context = '', $type = 'screen' ) { 1313 $callback = ''; 1314 1315 // Try the context-specific callback first. 1316 $method = $context . '_' . $type; 1317 $rmethod = $this->class_reflection->getMethod( $method ); 1318 if ( isset( $rmethod->class ) && $this->class_name === $rmethod->class ) { 1319 $callback = array( $this, $method ); 1320 } 1321 1322 if ( empty( $callback ) ) { 1323 $fallback_method = 'settings_' . $type; 1324 $rfallback_method = $this->class_reflection->getMethod( $fallback_method ); 1325 if ( isset( $rfallback_method->class ) && $this->class_name === $rfallback_method->class ) { 1326 $callback = array( $this, $fallback_method ); 1327 } 1328 } 1329 1330 return $callback; 1331 } 1332 1333 /** 1334 * Recursive argument parsing. 1335 * 1336 * This acts like a multi-dimensional version of wp_parse_args() (minus 1337 * the querystring parsing - you must pass arrays). 1338 * 1339 * Values from $a override those from $b; keys in $b that don't exist 1340 * in $a are passed through. 1341 * 1342 * This is different from array_merge_recursive(), both because of the 1343 * order of preference ($a overrides $b) and because of the fact that 1344 * array_merge_recursive() combines arrays deep in the tree, rather 1345 * than overwriting the b array with the a array. 1346 * 1347 * The implementation of this function is specific to the needs of 1348 * BP_Group_Extension, where we know that arrays will always be 1349 * associative, and that an argument under a given key in one array 1350 * will be matched by a value of identical depth in the other one. The 1351 * function is NOT designed for general use, and will probably result 1352 * in unexpected results when used with data in the wild. See, eg, 1353 * https://core.trac.wordpress.org/ticket/19888 1354 * 1355 * @since 1.8.0 1356 * 1357 * @param array $a First set of arguments. 1358 * @param array $b Second set of arguments. 1359 * @return array Parsed arguments. 1360 */ 1361 public static function parse_args_r( &$a, $b ) { 1362 $a = (array) $a; 1363 $b = (array) $b; 1364 $r = $b; 1365 1366 foreach ( $a as $k => &$v ) { 1367 if ( is_array( $v ) && isset( $r[ $k ] ) ) { 1368 $r[ $k ] = self::parse_args_r( $v, $r[ $k ] ); 1369 } else { 1370 $r[ $k ] = $v; 1371 } 1372 } 1373 1374 return $r; 1375 } 1376 1377 /** Legacy Support ********************************************************/ 1378 1379 /* 1380 * In BuddyPress 1.8, the recommended technique for configuring 1381 * extensions changed from directly setting various object properties 1382 * in the class constructor, to passing a configuration array to 1383 * parent::init(). The following methods ensure that extensions created 1384 * in the old way continue to work, by converting legacy configuration 1385 * data to the new format. 1386 */ 1387 1388 /** 1389 * Provide access to otherwise unavailable object properties. 1390 * 1391 * This magic method is here for backward compatibility with plugins 1392 * that refer to config properties that have moved to a different 1393 * location (such as enable_create_step, which is now at 1394 * $this->screens['create']['enabled'] 1395 * 1396 * The legacy_properties array is set up in 1397 * self::setup_legacy_properties(). 1398 * 1399 * @since 1.8.0 1400 * 1401 * @param string $key Property name. 1402 * @return mixed The value if found, otherwise null. 1403 */ 1404 public function __get( $key ) { 1405 if ( isset( $this->legacy_properties[ $key ] ) ) { 1406 return $this->legacy_properties[ $key ]; 1407 } elseif ( isset( $this->data[ $key ] ) ) { 1408 return $this->data[ $key ]; 1409 } else { 1410 return null; 1411 } 1412 } 1413 1414 /** 1415 * Provide a fallback for isset( $this->foo ) when foo is unavailable. 1416 * 1417 * This magic method is here for backward compatibility with plugins 1418 * that have set their class config options directly in the class 1419 * constructor. The parse_legacy_properties() method of the current 1420 * class needs to check whether any legacy keys have been put into the 1421 * $this->data array. 1422 * 1423 * @since 1.8.0 1424 * 1425 * @param string $key Property name. 1426 * @return bool True if the value is set, otherwise false. 1427 */ 1428 public function __isset( $key ) { 1429 if ( isset( $this->legacy_properties[ $key ] ) ) { 1430 return true; 1431 } elseif ( isset( $this->data[ $key ] ) ) { 1432 return true; 1433 } else { 1434 return false; 1435 } 1436 } 1437 1438 /** 1439 * Allow plugins to set otherwise unavailable object properties. 1440 * 1441 * This magic method is here for backward compatibility with plugins 1442 * that may attempt to modify the group extension by manually assigning 1443 * a value to an object property that no longer exists, such as 1444 * $this->enable_create_step. 1445 * 1446 * @since 1.8.0 1447 * 1448 * @param string $key Property name. 1449 * @param mixed $value Property value. 1450 */ 1451 public function __set( $key, $value ) { 1452 1453 if ( empty( $this->initialized ) ) { 1454 $this->data[ $key ] = $value; 1455 } 1456 1457 switch ( $key ) { 1458 case 'enable_create_step' : 1459 $this->screens['create']['enabled'] = $value; 1460 break; 1461 1462 case 'enable_edit_item' : 1463 $this->screens['edit']['enabled'] = $value; 1464 break; 1465 1466 case 'enable_admin_item' : 1467 $this->screens['admin']['enabled'] = $value; 1468 break; 1469 1470 case 'create_step_position' : 1471 $this->screens['create']['position'] = $value; 1472 break; 1473 1474 // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin'. 1475 case 'admin_name' : 1476 $this->screens['edit']['name'] = $value; 1477 break; 1478 1479 case 'admin_slug' : 1480 $this->screens['edit']['slug'] = $value; 1481 break; 1482 1483 case 'create_name' : 1484 $this->screens['create']['name'] = $value; 1485 break; 1486 1487 case 'create_slug' : 1488 $this->screens['create']['slug'] = $value; 1489 break; 1490 1491 case 'admin_metabox_context' : 1492 $this->screens['admin']['metabox_context'] = $value; 1493 break; 1494 1495 case 'admin_metabox_priority' : 1496 $this->screens['admin']['metabox_priority'] = $value; 1497 break; 1498 1499 default : 1500 $this->data[ $key ] = $value; 1501 break; 1502 } 1503 } 1504 1505 /** 1506 * Return a list of legacy properties. 1507 * 1508 * The legacy implementation of BP_Group_Extension used all of these 1509 * object properties for configuration. Some have been moved. 1510 * 1511 * @since 1.8.0 1512 * 1513 * @return array List of legacy property keys. 1514 */ 1515 protected function get_legacy_property_list() { 1516 return array( 1517 'name', 1518 'slug', 1519 'admin_name', 1520 'admin_slug', 1521 'create_name', 1522 'create_slug', 1523 'visibility', 1524 'create_step_position', 1525 'nav_item_position', 1526 'admin_metabox_context', 1527 'admin_metabox_priority', 1528 'enable_create_step', 1529 'enable_nav_item', 1530 'enable_edit_item', 1531 'enable_admin_item', 1532 'nav_item_name', 1533 'display_hook', 1534 'template_file', 1535 ); 1536 } 1537 1538 /** 1539 * Parse legacy properties. 1540 * 1541 * The old standard for BP_Group_Extension was for plugins to register 1542 * their settings as properties in their constructor. The new method is 1543 * to pass a config array to the init() method. In order to support 1544 * legacy plugins, we slurp up legacy properties, and later on we'll 1545 * parse them into the new init() array. 1546 * 1547 * @since 1.8.0 1548 */ 1549 protected function parse_legacy_properties() { 1550 1551 // Only run this one time. 1552 if ( ! empty( $this->legacy_properties_converted ) ) { 1553 return; 1554 } 1555 1556 $properties = $this->get_legacy_property_list(); 1557 1558 // By-reference variable for convenience. 1559 $lpc =& $this->legacy_properties_converted; 1560 1561 foreach ( $properties as $property ) { 1562 1563 // No legacy config exists for this key. 1564 if ( ! isset( $this->{$property} ) ) { 1565 continue; 1566 } 1567 1568 // Grab the value and record it as appropriate. 1569 $value = $this->{$property}; 1570 1571 switch ( $property ) { 1572 case 'enable_create_step' : 1573 $lpc['screens']['create']['enabled'] = (bool) $value; 1574 break; 1575 1576 case 'enable_edit_item' : 1577 $lpc['screens']['edit']['enabled'] = (bool) $value; 1578 break; 1579 1580 case 'enable_admin_item' : 1581 $lpc['screens']['admin']['enabled'] = (bool) $value; 1582 break; 1583 1584 case 'create_step_position' : 1585 $lpc['screens']['create']['position'] = $value; 1586 break; 1587 1588 // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin'. 1589 case 'admin_name' : 1590 $lpc['screens']['edit']['name'] = $value; 1591 break; 1592 1593 case 'admin_slug' : 1594 $lpc['screens']['edit']['slug'] = $value; 1595 break; 1596 1597 case 'create_name' : 1598 $lpc['screens']['create']['name'] = $value; 1599 break; 1600 1601 case 'create_slug' : 1602 $lpc['screens']['create']['slug'] = $value; 1603 break; 1604 1605 case 'admin_metabox_context' : 1606 $lpc['screens']['admin']['metabox_context'] = $value; 1607 break; 1608 1609 case 'admin_metabox_priority' : 1610 $lpc['screens']['admin']['metabox_priority'] = $value; 1611 break; 1612 1613 default : 1614 $lpc[ $property ] = $value; 1615 break; 1616 } 1617 } 1618 } 1619 1620 /** 1621 * Set up legacy properties. 1622 * 1623 * This method is responsible for ensuring that all legacy config 1624 * properties are stored in an array $this->legacy_properties, so that 1625 * they remain available to plugins that reference the variables at 1626 * their old locations. 1627 * 1628 * @since 1.8.0 1629 * 1630 * @see BP_Group_Extension::__get() 1631 */ 1632 protected function setup_legacy_properties() { 1633 1634 // Only run this one time. 1635 if ( ! empty( $this->legacy_properties ) ) { 1636 return; 1637 } 1638 1639 $properties = $this->get_legacy_property_list(); 1640 $params = $this->params; 1641 $lp =& $this->legacy_properties; 1642 1643 foreach ( $properties as $property ) { 1644 switch ( $property ) { 1645 case 'enable_create_step' : 1646 $lp['enable_create_step'] = $params['screens']['create']['enabled']; 1647 break; 1648 1649 case 'enable_edit_item' : 1650 $lp['enable_edit_item'] = $params['screens']['edit']['enabled']; 1651 break; 1652 1653 case 'enable_admin_item' : 1654 $lp['enable_admin_item'] = $params['screens']['admin']['enabled']; 1655 break; 1656 1657 case 'create_step_position' : 1658 $lp['create_step_position'] = $params['screens']['create']['position']; 1659 break; 1660 1661 // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin'. 1662 case 'admin_name' : 1663 $lp['admin_name'] = $params['screens']['edit']['name']; 1664 break; 1665 1666 case 'admin_slug' : 1667 $lp['admin_slug'] = $params['screens']['edit']['slug']; 1668 break; 1669 1670 case 'create_name' : 1671 $lp['create_name'] = $params['screens']['create']['name']; 1672 break; 1673 1674 case 'create_slug' : 1675 $lp['create_slug'] = $params['screens']['create']['slug']; 1676 break; 1677 1678 case 'admin_metabox_context' : 1679 $lp['admin_metabox_context'] = $params['screens']['admin']['metabox_context']; 1680 break; 1681 1682 case 'admin_metabox_priority' : 1683 $lp['admin_metabox_priority'] = $params['screens']['admin']['metabox_priority']; 1684 break; 1685 1686 default : 1687 // All other items get moved over. 1688 $lp[ $property ] = $params[ $property ]; 1689 1690 // Also reapply to the object, for backpat. 1691 $this->{$property} = $params[ $property ]; 1692 1693 break; 1694 } 1695 } 1696 } 1697 } 1698 1699 /** 1700 * Register a new Group Extension. 1701 * 1702 * @since 1.1.0 1703 * 1704 * @param string $group_extension_class Name of the Extension class. 1705 * @return false|null Returns false on failure, otherwise null. 1706 */ 1707 function bp_register_group_extension( $group_extension_class = '' ) { 1708 1709 if ( ! class_exists( $group_extension_class ) ) { 1710 return false; 1711 } 1712 1713 // Register the group extension on the bp_init action so we have access 1714 // to all plugins. 1715 add_action( 'bp_init', function() use ( $group_extension_class ) { 1716 $extension = new $group_extension_class; 1717 add_action( 'bp_actions', array( &$extension, '_register' ), 8 ); 1718 add_action( 'admin_init', array( &$extension, '_register' ) ); 1719 }, 11 ); 1720 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Feb 28 01:01:34 2021 | Cross-referenced by PHPXref 0.7.1 |