[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

/src/bp-groups/classes/ -> class-bp-group-extension.php (source)

   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  }


Generated: Mon Jul 22 01:01:43 2019 Cross-referenced by PHPXref 0.7.1