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


Generated: Thu Nov 21 01:00:57 2024 Cross-referenced by PHPXref 0.7.1