Skip to:
Content

BuddyPress.org

Ticket #4955: 4955.02.patch

File 4955.02.patch, 55.5 KB (added by johnjamesjacoby, 13 years ago)

Peer review

  • bp-groups/bp-groups-classes.php

     
    13671367 * API for creating group extensions without having to hardcode the content into
    13681368 * the theme.
    13691369 *
    1370  * This class must be extended for each group extension and the following methods overridden:
     1370 * To implement, extend this class. In your constructor, pass an optional array
     1371 * of arguments to parent::init() to configure your widget. The config array
     1372 * supports the following values:
     1373 *   - 'slug' A unique identifier for your extension. This value will be used
     1374 *     to build URLs, so make it URL-safe
     1375 *   - 'name' A translatable name for your extension. This value is used to
     1376       populate the navigation tab, as well as the default titles for admin/
     1377       edit/create tabs.
     1378 *   - 'visibility' Set to 'public' (default) for your extension (the main tab
     1379 *     as well as the widget) to be available to anyone who can access the
     1380 *     group, 'private' otherwise.
     1381 *   - 'nav_item_position' An integer explaining where the nav item should
     1382 *     appear in the tab list
     1383 *   - 'enable_nav_item' Set to true for your extension's main tab to be
     1384 *     available to anyone who can access the group.
     1385 *   - 'nav_item_name' The translatable text you want to appear in the nav tab.
     1386 *     Defaults to the value of 'name'.
     1387 *   - 'display_hook' The WordPress action that the widget_display() method is
     1388 *     hooked to
     1389 *   - 'template_file' The template file that will be used to load the content
     1390 *     of your main extension tab. Defaults to 'groups/single/plugins.php'.
     1391 *   - 'screens' A multi-dimensional array, described below
    13711392 *
    1372  * BP_Group_Extension::widget_display(), BP_Group_Extension::display(),
    1373  * BP_Group_Extension::edit_screen_save(), BP_Group_Extension::edit_screen(),
    1374  * BP_Group_Extension::create_screen_save(), BP_Group_Extension::create_screen()
     1393 * BP_Group_Extension uses the concept of "settings screens". There are three
     1394 * contexts for settings screens:
     1395 *   - 'create', which inserts a new step into the group creation process
     1396 *   - 'edit', which adds a tab for your extension into the Admin section of
     1397 *     a group
     1398 *   - 'admin', which adds a metabox to the Groups administration panel in the
     1399 *     WordPress Dashboard
     1400 * Each of these settings screens is populated by a pair of methods: one that
     1401 * creates the markup for the screen, and one that processes form data
     1402 * submitted from the screen. If your plugin needs screens in all three
     1403 * contexts, and if the markup and form processing logic will be the same in
     1404 * each case, you can define two methods to handle all of the screens:
     1405 *   function settings_screen() {}
     1406 *   function settings_screen_save() {}
     1407 * If one or more of your settings screen needs separate logic, you may define
     1408 * context-specific methods, for example:
     1409 *   function edit_screen() {}
     1410 *   function edit_screen_save() {}
     1411 * BP_Group_Extension will use the more specific methods if they are available.
    13751412 *
     1413 * You can further customize the settings screens (tab names, etc) by passing
     1414 * an optional 'screens' parameter to the init array. The format is as follows:
     1415 *   'screens' => array(
     1416 *       'create' => array(
     1417 *           'slug' => 'foo',
     1418 *           'name' => 'Foo',
     1419 *           'position' => 55,
     1420 *           'screen_callback' => 'my_create_screen_callback',
     1421 *           'screen_save_callback' => 'my_create_screen_save_callback',
     1422 *       ),
     1423 *       'edit' => array( // ...
     1424 *   ),
     1425 * Only provide those arguments that you actually want to change from the
     1426 * default configuration. BP_Group_Extension will do the rest.
     1427 *
     1428 * Note that the 'edit' screen accepts an additional parameter: 'submit_text',
     1429 * which defines the text of the Submit button automatically added to the Edit
     1430 * screen of the extension (defaults to 'Save Changes'). Also, the 'admin'
     1431 * screen accepts two additional parameters: 'metabox_priority' and
     1432 * 'metabox_context'. See the docs for add_meta_box() for more details on these
     1433 * arguments.
     1434 *
     1435 * Prior to BuddyPress 1.7, group extension configurations were set slightly
     1436 * differently. The legacy method is still supported, though deprecated.
     1437 *
    13761438 * @package BuddyPress
    13771439 * @subpackage Groups
    13781440 * @since BuddyPress (1.1)
    13791441 */
    13801442class BP_Group_Extension {
    1381         var $name = false;
    1382         var $slug = false;
    13831443
    1384         // The name/slug of the Group Admin tab for this extension
    1385         var $admin_name = '';
    1386         var $admin_slug = '';
     1444        /** Public ****************************************************************/
    13871445
    1388         // The name/slug of the Group Creation tab for this extension
    1389         var $create_name = '';
    1390         var $create_slug = '';
     1446        /**
     1447         * @var array Information about this extension's screens
     1448         * @since BuddyPress (1.8)
     1449         */
     1450        public $screens = array();
    13911451
    1392         // Will this extension be visible to non-members of a group? Options: public/private
    1393         var $visibility = 'public';
     1452        /**
     1453         * @var string The name of the extending class
     1454         * @since BuddyPress (1.8)
     1455         */
     1456        public $class_name = '';
    13941457
    1395         var $create_step_position = 81;
    1396         var $nav_item_position = 81;
     1458        /**
     1459         * @var object A ReflectionClass object of the current extension
     1460         * @since BuddyPress (1.8)
     1461         */
     1462        public $class_reflection = null;
    13971463
    13981464        /**
    1399          * @var string Context for the optional admin metabox
    1400          * @see https://codex.wordpress.org/Function_Reference/add_meta_box for
    1401          *      possible values
    1402          * @since BuddyPress (1.7)
     1465         * @var array Parsed configuration paramaters for the extension
     1466         * @since BuddyPress (1.8)
    14031467         */
    1404         var $admin_metabox_context = 'normal';
     1468        public $params = array();
    14051469
    14061470        /**
    1407          * @var string Priority for the optional admin menabox
    1408          * @see https://codex.wordpress.org/Function_Reference/add_meta_box for
    1409          *      possible values
    1410          * @since BuddyPress (1.7)
     1471         * @var int The id of the current group
     1472         * @since BuddyPress (1.8)
    14111473         */
    1412         var $admin_metabox_priority = 'core';
     1474        public $group_id = 0;
    14131475
    1414         var $enable_create_step = true;
    1415         var $enable_nav_item = true;
    1416         var $enable_edit_item = true;
    1417         var $enable_admin_item = true;
     1476        /**
     1477         * @var string The slug of the current extension
     1478         */
     1479        public $slug = '';
    14181480
    1419         var $nav_item_name = false;
     1481        /**
     1482         * @var string The translatable name of the current extension
     1483         */
     1484        public $name = '';
    14201485
    1421         var $display_hook = 'groups_custom_group_boxes';
    1422         var $template_file = 'groups/single/plugins';
     1486        /**
     1487         * @var string Whether the extension tab is visible. 'public'
     1488         *   or 'private'
     1489         */
     1490        public $visibility = 'public';
    14231491
    1424         // Methods you should override
     1492        /**
     1493         * @var int The numeric position of the main nav item
     1494         */
     1495        public $nav_item_position = 81;
    14251496
    1426         function display() {}
     1497        /**
     1498         * @var bool Whether to show the nav item
     1499         */
     1500        public $enable_nav_item = false;
    14271501
    1428         function widget_display() {}
     1502        /**
     1503         * @var string The text of the nav item. Defaults to self::name
     1504         */
     1505        public $nav_item_name = '';
    14291506
    1430         function edit_screen( $group_id = null ) {}
     1507        /**
     1508         * @var string The WP action that self::widget_display() is attached to.
     1509         *   Defaults to 'groups_custom_group_boxes'
     1510         */
     1511        public $display_hook = 'groups_custom_group_boxes';
    14311512
    1432         function edit_screen_save( $group_id = null ) {}
     1513        /**
     1514         * @var string The template file used to load the plugin content.
     1515         *   Defaults to 'groups/single/plugins'
     1516         */
     1517        public $template_file = 'groups/single/plugins';
    14331518
    1434         function create_screen( $group_id = null ) {}
     1519        /** Protected *************************************************************/
    14351520
    1436         function create_screen_save( $group_id = null ) {}
     1521        /**
     1522         * @var bool Has the extension been initialized?
     1523         * @since BuddyPress (1.8)
     1524         */
     1525        protected $initialized = false;
    14371526
    1438         // Private Methods
     1527        /**
     1528         * @var array Extension properties as set by legacy extensions
     1529         * @since BuddyPress (1.8)
     1530         */
     1531        protected $legacy_properties = array();
    14391532
    1440         function _register() {
    1441                 global $bp;
     1533        /**
     1534         * @var array Extension properties as set by legacy extensions, but
     1535         *   converted to match the new format for params
     1536         * @since BuddyPress (1.8)
     1537         */
     1538        protected $legacy_properties_converted = array();
    14421539
    1443                 // If admin/create names and slugs are not provided, they fall back on the main
    1444                 // name and slug for the extension
    1445                 if ( ! $this->admin_name ) {
    1446                         $this->admin_name = $this->name;
     1540        /**
     1541         * @var array Miscellaneous data as set by the __set() magic method
     1542         * @since BuddyPress (1.8)
     1543         */
     1544        protected $data = array();
     1545
     1546        /** Screen Overrides ******************************************************/
     1547
     1548        /**
     1549         * Screen override methods are how your extension will display content
     1550         * and handle form submits. Your extension should only override those
     1551         * methods that it needs for its purposes.
     1552         */
     1553
     1554        // The content of the group tab
     1555        public function display() {}
     1556
     1557        // Content displayed in a widget sidebar, if applicable
     1558        public function widget_display() {}
     1559
     1560        // *_screen() displays the settings form for the given context
     1561        // *_screen_save() processes data submitted via the settings form
     1562        // The settings_* methods are generic fallbacks, which can optionally
     1563        // be overridden by the more specific edit_*, create_*, and admin_*
     1564        // versions.
     1565        public function settings_screen( $group_id = null ) {}
     1566        public function settings_screen_save( $group_id = null ) {}
     1567        public function edit_screen( $group_id = null ) {}
     1568        public function edit_screen_save( $group_id = null ) {}
     1569        public function create_screen( $group_id = null ) {}
     1570        public function create_screen_save( $group_id = null ) {}
     1571        public function admin_screen( $group_id = null ) {}
     1572        public function admin_screen_save( $group_id = null ) {}
     1573
     1574        /** Setup *************************************************************/
     1575
     1576        /**
     1577         * Initialize the extension, using your config settings
     1578         *
     1579         * Your plugin should call this method at the very end of its
     1580         * constructor, like so:
     1581         *
     1582         *   public function __construct() {
     1583         *       $args = array(
     1584         *           'slug' => 'my-group-extension',
     1585         *           'name' => 'My Group Extension',
     1586         *           // ...
     1587         *       );
     1588         *
     1589         *       parent::init( $args );
     1590         *   }
     1591         *
     1592         * @since BuddyPress (1.8)
     1593         * @param array $args See inline definition below for arguments
     1594         */
     1595        public function init( $args = array() ) {
     1596
     1597                // Before this init() method was introduced, plugins were
     1598                // encouraged to set their config directly. For backward
     1599                // compatibility with these plugins, we detect whether this is
     1600                // one of those legacy plugins, and parse any legacy arguments
     1601                // with those passed to init()
     1602                $this->parse_legacy_properties();
     1603                $args = $this->parse_args_r( $args, $this->legacy_properties_converted );
     1604
     1605                // Parse with defaults
     1606                $this->params = $this->parse_args_r( $args, array(
     1607                        'slug'              => $this->slug,
     1608                        'name'              => $this->name,
     1609                        'visibility'        => $this->visibility,
     1610                        'nav_item_position' => $this->nav_item_position,
     1611                        'enable_nav_item'   => $this->enable_nav_item,
     1612                        'nav_item_name'     => $this->nav_item_name,
     1613                        'display_hook'      => $this->display_hook,
     1614                        'template_file'     => $this->template_file,
     1615                        'screens'           => $this->get_default_screens(),
     1616                ) );
     1617
     1618                $this->initialized = true;
     1619        }
     1620
     1621        /**
     1622         * The main setup routine for the extension
     1623         *
     1624         * This method contains the primary logic for setting up an extension's
     1625         * configuration, setting up backward compatibility for legacy plugins,
     1626         * and hooking the extension's screen functions into WP and BP.
     1627         *
     1628         * Marked 'public' because it must be accessible to add_action().
     1629         * However, you should never need to invoke this method yourself - it
     1630         * is called automatically at the right point in the load order by
     1631         * bp_register_group_extension().
     1632         *
     1633         * @since BuddyPress (1.1)
     1634         */
     1635        public function _register() {
     1636
     1637                // Detect and parse properties set by legacy extensions
     1638                $this->parse_legacy_properties();
     1639
     1640                // Initialize, if necessary. This should only happen for
     1641                // legacy extensions that don't call parent::init() themselves
     1642                if ( true !== $this->initialized ) {
     1643                        $this->init();
    14471644                }
    14481645
    1449                 if ( ! $this->admin_slug ) {
    1450                         $this->admin_slug = $this->slug;
     1646                // Set some config values, based on the parsed params
     1647                $this->group_id          = $this->get_group_id();
     1648                $this->slug              = $this->params['slug'];
     1649                $this->name              = $this->params['name'];
     1650                $this->visibility        = $this->params['visibility'];
     1651                $this->nav_item_position = $this->params['nav_item_position'];
     1652                $this->nav_item_name     = $this->params['nav_item_name'];
     1653                $this->display_hook      = $this->params['display_hook'];
     1654                $this->template_file     = $this->params['template_file'];
     1655
     1656                // Configure 'screens': create, admin, and edit contexts
     1657                $this->setup_screens();
     1658
     1659                // Mirror configuration data so it's accessible to plugins
     1660                // that look for it in its old locations
     1661                $this->setup_legacy_properties();
     1662
     1663                // Hook the extension into BuddyPress
     1664                $this->setup_display_hooks();
     1665                $this->setup_create_hooks();
     1666                $this->setup_edit_hooks();
     1667                $this->setup_admin_hooks();
     1668        }
     1669
     1670        /**
     1671         * Set up some basic info about the Extension
     1672         *
     1673         * Here we collect the name of the extending class, as well as a
     1674         * ReflectionClass that is used in get_screen_callback() to determine
     1675         * whether your extension overrides certain callback methods.
     1676         *
     1677         * @since BuddyPress (1.8)
     1678         */
     1679        protected function setup_class_info() {
     1680                if ( is_null( $this->class_name ) ) {
     1681                        $this->class_name = get_class( $this );
    14511682                }
    14521683
    1453                 if ( ! $this->create_name ) {
    1454                         $this->create_name = $this->name;
     1684                if ( is_null( $this->class_reflection ) ) {
     1685                        $this->class_reflection = new ReflectionClass( $this->class_name );
    14551686                }
     1687        }
    14561688
    1457                 if ( ! $this->create_slug ) {
    1458                         $this->create_slug = $this->slug;
     1689        /**
     1690         * Get the current group id
     1691         *
     1692         * Check for:
     1693         *   - current group
     1694         *   - new group
     1695         *   - group admin
     1696         *
     1697         * @since BuddyPress (1.8)
     1698         */
     1699        public static function get_group_id() {
     1700
     1701                // Usually this will work
     1702                $group_id = bp_get_current_group_id();
     1703
     1704                // On the admin, get the group id out of the $_GET params
     1705                if ( empty( $group_id ) && is_admin() && ( isset( $_GET['page'] ) && ( 'bp-groups' === $_GET['page'] ) ) && ! empty( $_GET['gid'] ) ) {
     1706                        $group_id = (int) $_GET['gid'];
    14591707                }
    14601708
    1461                 if ( ! empty( $this->enable_create_step ) ) {
    1462                         // Insert the group creation step for the new group extension
    1463                         $bp->groups->group_creation_steps[ $this->create_slug ] = array(
    1464                                 'name'     => $this->create_name,
    1465                                 'slug'     => $this->create_slug,
    1466                                 'position' => $this->create_step_position,
    1467                         );
     1709                // This fallback will only be hit when the create step is very early
     1710                if ( empty( $group_id ) && bp_get_new_group_id() ) {
     1711                        $group_id = bp_get_new_group_id();
     1712                }
    14681713
    1469                         // The maybe_ methods check to see whether the create_*
    1470                         // callbacks should be invoked (ie, are we on the
    1471                         // correct group creation step). Hooked in separate
    1472                         // methods because current creation step info not yet
    1473                         // available at this point
    1474                         add_action( 'groups_custom_create_steps', array( $this, 'maybe_create_screen' ) );
    1475                         add_action( 'groups_create_group_step_save_' . $this->create_slug, array( $this, 'maybe_create_screen_save' ) );
     1714                // On some setups, the group id has to be fetched out of the $_POST
     1715                // array
     1716                // @todo Figure out why this is happening during group creation
     1717                if ( empty( $group_id ) && isset( $_POST['group_id'] ) ) {
     1718                        $group_id = (int) $_POST['group_id'];
    14761719                }
    14771720
    1478                 // When we are viewing a single group, add the group extension nav item
    1479                 if ( bp_is_group() ) {
    1480                         if ( $this->visibility == 'public' || ( $this->visibility != 'public' && $bp->groups->current_group->user_has_access ) ) {
    1481                                 if ( $this->enable_nav_item ) {
    1482                                         bp_core_new_subnav_item( array(
    1483                                                 'name' => !$this->nav_item_name ? $this->name : $this->nav_item_name,
    1484                                                 'slug' => $this->slug,
    1485                                                 'parent_slug' => $bp->groups->current_group->slug,
    1486                                                 'parent_url' => bp_get_group_permalink( $bp->groups->current_group ),
    1487                                                 'position' => $this->nav_item_position,
    1488                                                 'item_css_id' => 'nav-' . $this->slug,
    1489                                                 'screen_function' => array( &$this, '_display_hook' ),
    1490                                                 'user_has_access' => $this->enable_nav_item
    1491                                         ) );
     1721                return $group_id;
     1722        }
    14921723
    1493                                         // When we are viewing the extension display page, set the title and options title
    1494                                         if ( bp_is_current_action( $this->slug ) ) {
    1495                                                 add_action( 'bp_template_content_header', create_function( '', 'echo "' . esc_attr( $this->name ) . '";' ) );
    1496                                                 add_action( 'bp_template_title', create_function( '', 'echo "' . esc_attr( $this->name ) . '";' ) );
    1497                                         }
    1498                                 }
     1724        /**
     1725         * Gather configuration data about your screens
     1726         *
     1727         * @since BuddyPress (1.8)
     1728         */
     1729        protected function get_default_screens() {
     1730                $this->setup_class_info();
    14991731
    1500                                 // Hook the group home widget
    1501                                 if ( ! bp_current_action() && bp_is_current_action( 'home' ) ) {
    1502                                         add_action( $this->display_hook, array( &$this, 'widget_display' ) );
    1503                                 }
    1504                         }
    1505                 }
     1732                $screens = array(
     1733                        'create' => array(),
     1734                        'edit'   => array(),
     1735                        'admin'  => array(
     1736                                'metabox_context'  => 'normal',
     1737                                'metabox_priority' => 'core',
     1738                        ),
     1739                );
    15061740
    1507                 // Construct the admin edit tab for the new group extension
    1508                 if ( ! empty( $this->enable_edit_item ) && bp_is_item_admin() ) {
    1509                         add_action( 'groups_admin_tabs', create_function( '$current, $group_slug',
    1510                                 '$selected = "";
    1511                                 if ( "' . esc_attr( $this->admin_slug ) . '" == $current )
    1512                                         $selected = " class=\"current\"";
    1513                                 echo "<li{$selected}><a href=\"' . trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/{$group_slug}/admin/' . esc_attr( $this->admin_slug ) ) . '\">' . esc_attr( $this->admin_name ) . '</a></li>";'
    1514                         ), 10, 2 );
     1741                foreach ( $screens as $context => &$screen ) {
     1742                        $screen['enabled']     = true;
     1743                        $screen['name']        = '';
     1744                        $screen['slug']        = '';
     1745                        $screen['position']    = 81;
     1746                        $screen['submit_text'] = __( 'Save Changes', 'buddypress' );
    15151747
    1516                         // Catch the edit screen and forward it to the plugin template
    1517                         if ( bp_is_groups_component() && bp_is_current_action( 'admin' ) && bp_is_action_variable( $this->admin_slug, 0 ) ) {
    1518                                 $this->edit_screen_save( bp_get_current_group_id() );
     1748                        $screen['screen_callback']      = $this->get_screen_callback( $context, 'screen'      );
     1749                        $screen['screen_save_callback'] = $this->get_screen_callback( $context, 'screen_save' );
     1750                }
    15191751
    1520                                 add_action( 'groups_custom_edit_steps', array( &$this, 'call_edit_screen' ) );
     1752                return $screens;
     1753        }
    15211754
    1522                                 if ( '' != bp_locate_template( array( 'groups/single/home.php' ), false ) ) {
    1523                                         bp_core_load_template( apply_filters( 'groups_template_group_home', 'groups/single/home' ) );
    1524                                 } else {
    1525                                         add_action( 'bp_template_content_header', create_function( '', 'echo "<ul class=\"content-header-nav\">"; bp_group_admin_tabs(); echo "</ul>";' ) );
    1526                                         add_action( 'bp_template_content', array( &$this, 'call_edit_screen' ) );
    1527                                         bp_core_load_template( apply_filters( 'bp_core_template_plugin', '/groups/single/plugins' ) );
    1528                                 }
     1755        /**
     1756         * Set up screens array based on params
     1757         *
     1758         * @since BuddyPress (1.8)
     1759         */
     1760        protected function setup_screens() {
     1761                foreach ( (array) $this->params['screens'] as $context => $screen ) {
     1762                        if ( empty( $screen['slug'] ) ) {
     1763                                $screen['slug'] = $this->slug;
    15291764                        }
     1765
     1766                        if ( empty( $screen['name'] ) ) {
     1767                                $screen['name'] = $this->name;
     1768                        }
     1769
     1770                        $this->screens[ $context ] = $screen;
    15301771                }
     1772        }
    15311773
    1532                 // Construct the admin metabox
    1533                 // Plugin authors: Note that $this->enable_admin_item must be
    1534                 // set to true, and self::admin_screen() must be defined
    1535                 if ( ! empty( $this->enable_admin_item ) && is_admin() && method_exists( get_class( $this ), 'admin_screen' ) ) {
    1536                         // Hook the admin screen markup function to the content hook
    1537                         add_action( 'bp_groups_admin_meta_box_content_' . $this->slug, array( $this, 'admin_screen' ) );
     1774        /** Display ***************************************************************/
    15381775
    1539                         // Initialize the metabox
    1540                         add_action( 'bp_groups_admin_meta_boxes', array( $this, '_meta_box_display_callback' ) );
     1776        /**
     1777         * Hook this extension's group tab into BuddyPress, if necessary
     1778         *
     1779         * @since BuddyPress (1.8)
     1780         */
     1781        protected function setup_display_hooks() {
    15411782
    1542                         // Catch the metabox save
    1543                         if ( method_exists( get_class( $this ), 'admin_screen_save' ) ) {
    1544                                 add_action( 'bp_group_admin_edit_after', array( $this, 'admin_screen_save' ), 10 );
     1783                // Bail if not a group
     1784                if ( ! bp_is_group() ) {
     1785                        return;
     1786                }
     1787
     1788                // Bail if the current user doesn't have access
     1789                if ( ( 'public' !== $this->visibility ) && ! buddypress()->groups->current_group->user_has_access ) {
     1790                        return;
     1791                }
     1792
     1793                if ( true === $this->enable_nav_item ) {
     1794                        bp_core_new_subnav_item( array(
     1795                                'name'            => ! $this->nav_item_name ? $this->name : $this->nav_item_name,
     1796                                'slug'            => $this->slug,
     1797                                'parent_slug'     => bp_get_current_group_slug(),
     1798                                'parent_url'      => bp_get_group_permalink( groups_get_current_group() ),
     1799                                'position'        => $this->nav_item_position,
     1800                                'item_css_id'     => 'nav-' . $this->slug,
     1801                                'screen_function' => array( &$this, '_display_hook' ),
     1802                                'user_has_access' => $this->enable_nav_item
     1803                        ) );
     1804
     1805                        // When we are viewing the extension display page, set the title and options title
     1806                        if ( bp_is_current_action( $this->slug ) ) {
     1807                                add_action( 'bp_template_content_header', create_function( '', 'echo "' . esc_attr( $this->name ) . '";' ) );
     1808                                add_action( 'bp_template_title',          create_function( '', 'echo "' . esc_attr( $this->name ) . '";' ) );
    15451809                        }
    15461810                }
     1811
     1812                // Hook the group home widget
     1813                if ( ! bp_current_action() && bp_is_current_action( 'home' ) ) {
     1814                        add_action( $this->display_hook, array( &$this, 'widget_display' ) );
     1815                }
    15471816        }
    15481817
    1549         function _display_hook() {
     1818        /**
     1819         * Hooks the main display method, and loads the template file
     1820         */
     1821        public function _display_hook() {
    15501822                add_action( 'bp_template_content', array( &$this, 'display' ) );
    15511823                bp_core_load_template( apply_filters( 'bp_core_template_plugin', $this->template_file ) );
    15521824        }
    15531825
     1826        /** Create ****************************************************************/
     1827
    15541828        /**
     1829         * Hook this extension's Create step into BuddyPress, if necessary
     1830         *
     1831         * @since BuddyPress (1.8)
     1832         */
     1833        protected function setup_create_hooks() {
     1834                if ( ! $this->is_screen_enabled( 'create' ) ) {
     1835                        return;
     1836                }
     1837
     1838                $screen = $this->screens['create'];
     1839
     1840                // Insert the group creation step for the new group extension
     1841                buddypress()->groups->group_creation_steps[ $screen['slug'] ] = array(
     1842                        'name'     => $screen['name'],
     1843                        'slug'     => $screen['slug'],
     1844                        'position' => $screen['position'],
     1845                );
     1846
     1847                // The maybe_ methods check to see whether the create_*
     1848                // callbacks should be invoked (ie, are we on the
     1849                // correct group creation step). Hooked in separate
     1850                // methods because current creation step info not yet
     1851                // available at this point
     1852                add_action( 'groups_custom_create_steps', array( $this, 'maybe_create_screen' ) );
     1853                add_action( 'groups_create_group_step_save_' . $screen['slug'], array( $this, 'maybe_create_screen_save' ) );
     1854        }
     1855
     1856        /**
     1857         * Call the create_screen() method, if we're on the right page
     1858         *
     1859         * @since 1.8
     1860         */
     1861        public function maybe_create_screen() {
     1862                if ( ! bp_is_group_creation_step( $this->screens['create']['slug'] ) ) {
     1863                        return;
     1864                }
     1865
     1866                call_user_func( $this->screens['create']['screen_callback'], $this->group_id );
     1867                $this->nonce_field( 'create' );
     1868
     1869                // The create screen requires an additional nonce field
     1870                // due to a quirk in the way the templates are built
     1871                wp_nonce_field( 'groups_create_save_' . bp_get_groups_current_create_step() );
     1872        }
     1873
     1874        /**
     1875         * Call the create_screen_save() method, if we're on the right page
     1876         *
     1877         * @since 1.8
     1878         */
     1879        public function maybe_create_screen_save() {
     1880                if ( ! bp_is_group_creation_step( $this->screens['create']['slug'] ) ) {
     1881                        return;
     1882                }
     1883
     1884                $this->check_nonce( 'create' );
     1885                call_user_func( $this->screens['create']['screen_save_callback'], $this->group_id );
     1886        }
     1887
     1888        /** Edit ******************************************************************/
     1889
     1890        /**
     1891         * Hook this extension's Edit panel into BuddyPress, if necessary
     1892         *
     1893         * @since BuddyPress (1.8)
     1894         */
     1895        protected function setup_edit_hooks() {
     1896
     1897                // Bail if not an edit screen
     1898                if ( ! $this->is_screen_enabled( 'edit' ) || ! bp_is_item_admin() ) {
     1899                        return;
     1900                }
     1901
     1902                $screen = $this->screens['edit'];
     1903
     1904                // Add the tab
     1905                // @todo BP should be using bp_core_new_subnav_item()
     1906                add_action( 'groups_admin_tabs', create_function( '$current, $group_slug',
     1907                        '$selected = "";
     1908                        if ( "' . esc_attr( $screen['slug'] ) . '" == $current )
     1909                                $selected = " class=\"current\"";
     1910                        echo "<li{$selected}><a href=\"' . trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/{$group_slug}/admin/' . esc_attr( $screen['slug'] ) ) . '\">' . esc_attr( $screen['name'] ) . '</a></li>";'
     1911                ), 10, 2 );
     1912
     1913                // Catch the edit screen and forward it to the plugin template
     1914                if ( bp_is_groups_component() && bp_is_current_action( 'admin' ) && bp_is_action_variable( $screen['slug'], 0 ) ) {
     1915                        $this->call_edit_screen_save( $this->group_id );
     1916
     1917                        add_action( 'groups_custom_edit_steps', array( &$this, 'call_edit_screen' ) );
     1918
     1919                        if ( '' !== bp_locate_template( array( 'groups/single/home.php' ), false ) ) {
     1920                                bp_core_load_template( apply_filters( 'groups_template_group_home', 'groups/single/home' ) );
     1921                        } else {
     1922                                add_action( 'bp_template_content_header', create_function( '', 'echo "<ul class=\"content-header-nav\">"; bp_group_admin_tabs(); echo "</ul>";' ) );
     1923                                add_action( 'bp_template_content', array( &$this, 'call_edit_screen' ) );
     1924                                bp_core_load_template( apply_filters( 'bp_core_template_plugin', '/groups/single/plugins' ) );
     1925                        }
     1926                }
     1927        }
     1928
     1929        /**
     1930         * Call the edit_screen() method
     1931         *
     1932         * Previous versions of BP_Group_Extension required plugins to provide
     1933         * their own Submit button and nonce fields when building markup. In
     1934         * BP 1.8, this requirement was lifted - BP_Group_Extension now handles
     1935         * all required submit buttons and nonces.
     1936         *
     1937         * We put the edit screen markup into an output buffer before echoing.
     1938         * This is so that we can check for the presence of a hardcoded submit
     1939         * button, as would be present in legacy plugins; if one is found, we
     1940         * do not auto-add our own button.
     1941         *
     1942         * @since BuddyPress (1.8)
     1943         */
     1944        public function call_edit_screen() {
     1945                ob_start();
     1946                call_user_func( $this->screens['edit']['screen_callback'], $this->group_id );
     1947                $screen = ob_get_contents();
     1948                ob_end_clean();
     1949
     1950                echo $this->maybe_add_submit_button( $screen );
     1951
     1952                $this->nonce_field( 'edit' );
     1953        }
     1954
     1955        /**
     1956         * Check the nonce, and call the edit_screen_save() method
     1957         *
     1958         * @since BuddyPress (1.8)
     1959         */
     1960        public function call_edit_screen_save() {
     1961                if ( empty( $_POST ) ) {
     1962                        return;
     1963                }
     1964
     1965                $this->check_nonce( 'edit' );
     1966                call_user_func( $this->screens['edit']['screen_save_callback'], $this->group_id );
     1967        }
     1968
     1969        /**
     1970         * Add a submit button to the edit form, if it needs one
     1971         *
     1972         * There's an inconsistency in the way that the group Edit and Create
     1973         * screens are rendered: the Create screen has a submit button built
     1974         * in, but the Edit screen does not. This function allows plugin
     1975         * authors to write markup that does not contain the submit button for
     1976         * use on both the Create and Edit screens - BP will provide the button
     1977         * if one is not found.
     1978         *
     1979         * @since BuddyPress (1.8)
     1980         * @param string $screen The screen markup, captured in the output buffer
     1981         * @param string $screen The same markup, with a submit button added
     1982         */
     1983        protected function maybe_add_submit_button( $screen = '' ) {
     1984                if ( $this->has_submit_button( $screen ) ) {
     1985                        return;
     1986                }
     1987
     1988                return $screen . sprintf(
     1989                        '<div id="%s"><input type="submit" name="save" value="%s" id="%s"></div>',
     1990                        'bp-group-edit-' . $this->slug . '-submit-wrapper',
     1991                        $this->screens['edit']['submit_text'],
     1992                        'bp-group-edit-' . $this->slug . '-submit'
     1993                );
     1994        }
     1995
     1996        /**
     1997         * Does the given markup have a submit button?
     1998         *
     1999         * @since BuddyPress (1.8)
     2000         * @param $screen The markup to check
     2001         * @return bool
     2002         */
     2003        public static function has_submit_button( $screen = '' ) {
     2004                $pattern = "/<input[^>]+type=[\'\"]submit[\'\"]/";
     2005                preg_match( $pattern, $screen, $matches );
     2006                return ! empty( $matches[0] );
     2007        }
     2008
     2009        /** Admin *****************************************************************/
     2010
     2011        /**
     2012         * Hook this extension's Admin metabox into BuddyPress, if necessary
     2013         *
     2014         * @since BuddyPress (1.8)
     2015         */
     2016        protected function setup_admin_hooks() {
     2017                if ( ! $this->is_screen_enabled( 'admin' ) || ! is_admin() ) {
     2018                        return;
     2019                }
     2020
     2021                // Hook the admin screen markup function to the content hook
     2022                add_action( 'bp_groups_admin_meta_box_content_' . $this->slug, array( $this, 'admin_screen' ) );
     2023
     2024                // Initialize the metabox
     2025                add_action( 'bp_groups_admin_meta_boxes', array( $this, '_meta_box_display_callback' ) );
     2026
     2027                // Catch the metabox save
     2028                add_action( 'bp_group_admin_edit_after', array( $this, 'call_admin_screen_save' ), 10 );
     2029        }
     2030
     2031        /**
     2032         * Call the admin_screen() method, and add a nonce field
     2033         *
     2034         * @since BuddyPress (1.8)
     2035         */
     2036        public function call_admin_screen() {
     2037                call_user_func( $this->screens['admin']['screen_callback'], $this->group_id );
     2038                $this->nonce_field( 'admin' );
     2039        }
     2040
     2041        /**
     2042         * Check the nonce, and call the admin_screen_save() method
     2043         *
     2044         * @since BuddyPress (1.8)
     2045         */
     2046        public function call_admin_screen_save() {
     2047                $this->check_nonce( 'admin' );
     2048                call_user_func( $this->screens['admin']['screen_save_callback'], $this->group_id );
     2049        }
     2050
     2051        /**
    15552052         * Create the Dashboard meta box for this extension
    15562053         *
    15572054         * @since BuddyPress (1.7)
    15582055         */
    1559         function _meta_box_display_callback() {
     2056        public function _meta_box_display_callback() {
    15602057                $group_id = isset( $_GET['gid'] ) ? (int) $_GET['gid'] : 0;
     2058                $screen   = $this->screens['admin'];
    15612059
    15622060                add_meta_box(
    1563                         $this->slug,
    1564                         $this->name,
     2061                        $screen['slug'],
     2062                        $screen['name'],
    15652063                        create_function( '', 'do_action( "bp_groups_admin_meta_box_content_' . $this->slug . '", ' . $group_id . ' );' ),
    15662064                        get_current_screen()->id,
    1567                         $this->admin_metabox_context,
    1568                         $this->admin_metabox_priority
     2065                        $screen['metabox_context'],
     2066                        $screen['metabox_priority']
    15692067                );
    15702068        }
    15712069
     2070
     2071        /** Utilities *************************************************************/
     2072
    15722073        /**
    1573          * Call the edit_screen() method
     2074         * Generate the nonce fields for a settings form
    15742075         *
    1575          * Broken into a standalone method so we can pass the current group id
    1576          * to edit_screen()
     2076         * The nonce field name (the second param passed to wp_nonce_field)
     2077         * contains this extension's slug and is thus unique to this extension.
     2078         * This is necessary because in some cases (namely, the Dashboard),
     2079         * more than one extension may generate nonces on the same page, and we
     2080         * must avoid name clashes.
    15772081         *
    1578          * @since 1.8
     2082         * @since BuddyPress (1.8)
     2083         * @uses wp_nonce_field()
     2084         * @param string $context 'create', 'edit', 'admin'
    15792085         */
    1580         public function call_edit_screen() {
    1581                 $this->edit_screen( bp_get_current_group_id() );
     2086        public function nonce_field( $context = '' ) {
     2087                wp_nonce_field( 'bp_group_extension_' . $this->slug . '_' . $context, '_bp_group_' . $context . '_nonce_' . $this->slug );
    15822088        }
    15832089
    15842090        /**
    1585          * Call the create_screen() method, if we're on the right page
     2091         * Check the nonce on a submitted settings form
    15862092         *
    1587          * @since 1.8
     2093         * @since BuddyPress (1.8)
     2094         * @uses check_admin_referer()
     2095         * @param string $context 'create', 'edit', 'admin'
    15882096         */
    1589         public function maybe_create_screen() {
    1590                 if ( bp_is_group_creation_step( $this->slug ) ) {
    1591                         $this->create_screen( bp_get_new_group_id() );
     2097        public function check_nonce( $context = '' ) {
     2098                check_admin_referer( 'bp_group_extension_' . $this->slug . '_' . $context, '_bp_group_' . $context . '_nonce_' . $this->slug );
     2099        }
     2100
     2101        /**
     2102         * Is the specified screen enabled?
     2103         *
     2104         * To be enabled, a screen must both have the 'enabled' key set to true
     2105         * (legacy: $this->enable_create_step, etc), and its screen_callback
     2106         * must also exist and be callable.
     2107         *
     2108         * @since BuddyPress (1.8)
     2109         * @param string $context 'create', 'edit', 'admin'
     2110         * @return bool
     2111         */
     2112        public function is_screen_enabled( $context = '' ) {
     2113                $enabled = false;
     2114
     2115                if ( isset( $this->screens[ $context ] ) ) {
     2116                        $enabled = $this->screens[ $context ]['enabled'] && is_callable( $this->screens[ $context ]['screen_callback'] );
    15922117                }
     2118
     2119                return (bool) $enabled;
    15932120        }
    15942121
    15952122        /**
    1596          * Call the create_screen_save() method, if we're on the right page
     2123         * Get the appropriate screen callback for the specified context/type
    15972124         *
    1598          * @since 1.8
     2125         * BP Group Extensions have three special "screen contexts": create,
     2126         * admin, and edit. Each of these contexts has a corresponding
     2127         * _screen() and _screen_save() method, which allow group extension
     2128         * plugins to define different markup and logic for each context.
     2129         *
     2130         * BP also supports fallback settings_screen() and
     2131         * settings_screen_save() methods, which can be used to define markup
     2132         * and logic that is shared between context. For each context, you may
     2133         * either provide context-specific methods, or you can let BP fall back
     2134         * on the shared settings_* callbacks.
     2135         *
     2136         * For example, consider a BP_Group_Extension implementation that looks
     2137         * like this:
     2138         *
     2139         *   // ...
     2140         *   function create_screen( $group_id ) { ... }
     2141         *   function create_screen_save( $group_id ) { ... }
     2142         *   function settings_screen( $group_id ) { ... }
     2143         *   function settings_screen_save( $group_id ) { ... }
     2144         *   // ...
     2145         *
     2146         * BP_Group_Extension will use your create_* methods for the Create
     2147         * steps, and will use your generic settings_* methods for the Edit
     2148         * and Admin contexts. This schema allows plugin authors maximum
     2149         * flexibility without having to repeat themselves.
     2150         *
     2151         * The get_screen_callback() method uses a ReflectionClass object to
     2152         * determine whether your extension has provided a given callback.
     2153         *
     2154         * @since BuddyPress (1.8)
     2155         * @param string $context 'create', 'edit', 'admin'
     2156         * @param string $type 'screen', 'screen_save'
     2157         * @return mixed A callable function handle
    15992158         */
    1600         public function maybe_create_screen_save() {
    1601                 if ( bp_is_group_creation_step( $this->slug ) ) {
    1602                         $this->create_screen_save( bp_get_new_group_id() );
     2159        public function get_screen_callback( $context = '', $type = 'screen' ) {
     2160                $callback = '';
     2161
     2162                // Try the context-specific callback first
     2163                $method  = $context . '_' . $type;
     2164                $rmethod = $this->class_reflection->getMethod( $method );
     2165                if ( isset( $rmethod->class ) && $this->class_name === $rmethod->class ) {
     2166                        $callback = array( $this->class_name, $method );
    16032167                }
     2168
     2169                if ( empty( $callback ) ) {
     2170                        $fallback_method  = 'settings_' . $type;
     2171                        $rfallback_method = $this->class_reflection->getMethod( $fallback_method );
     2172                        if ( isset( $rfallback_method->class ) && $this->class_name === $rfallback_method->class ) {
     2173                                $callback = array( $this->class_name, $fallback_method );
     2174                        }
     2175                }
     2176
     2177                return $callback;
    16042178        }
    16052179
     2180        /**
     2181         * Recursive argument parsing
     2182         *
     2183         * This acts like a multi-dimensional version of wp_parse_args() (minus
     2184         * the querystring parsing - you must pass arrays).
     2185         *
     2186         * Values from $a override those from $b; keys in $b that don't exist
     2187         * in $a are passed through.
     2188         *
     2189         * This is different from array_merge_recursive(), both because of the
     2190         * order of preference ($a overrides $b) and because of the fact that
     2191         * array_merge_recursive() combines arrays deep in the tree, rather
     2192         * than overwriting the b array with the a array.
     2193         *
     2194         * The implementation of this function is specific to the needs of
     2195         * BP_Group_Extension, where we know that arrays will always be
     2196         * associative, and that an argument under a given key in one array
     2197         * will be matched by a value of identical depth in the other one. The
     2198         * function is NOT designed for general use, and will probably result
     2199         * in unexpected results when used with data in the wild. See, eg,
     2200         * http://core.trac.wordpress.org/ticket/19888
     2201         *
     2202         * @since BuddyPress (1.8)
     2203         * @arg array $a
     2204         * @arg array $b
     2205         * @return array
     2206         */
     2207        public static function parse_args_r( &$a, $b ) {
     2208                $a = (array) $a;
     2209                $b = (array) $b;
     2210                $r = $b;
     2211
     2212                foreach ( $a as $k => &$v ) {
     2213                        if ( is_array( $v ) && isset( $r[ $k ] ) ) {
     2214                                $r[ $k ] = self::parse_args_r( $v, $r[ $k ] );
     2215                        } else {
     2216                                $r[ $k ] = $v;
     2217                        }
     2218                }
     2219
     2220                return $r;
     2221        }
     2222
     2223        /** Legacy Support ********************************************************/
     2224
     2225        /**
     2226         * In BuddyPress 1.8, the recommended technique for configuring
     2227         * extensions changed from directly setting various object properties
     2228         * in the class constructor, to passing a configuration array to
     2229         * parent::init(). The following methods ensure that extensions created
     2230         * in the old way continue to work, by converting legacy configuration
     2231         * data to the new format.
     2232         */
     2233
     2234        /**
     2235         * Provide access to otherwise unavailable object properties
     2236         *
     2237         * This magic method is here for backward compatibility with plugins
     2238         * that refer to config properties that have moved to a different
     2239         * location (such as enable_create_step, which is now at
     2240         * $this->screens['create']['enabled']
     2241         *
     2242         * The legacy_properties array is set up in
     2243         * self::setup_legacy_properties().
     2244         *
     2245         * @since BuddyPress (1.8)
     2246         * @param string $key
     2247         * @return mixed
     2248         */
     2249        public function __get( $key ) {
     2250                if ( isset( $this->legacy_properties[ $key ] ) ) {
     2251                        return $this->legacy_properties[ $key ];
     2252                } elseif ( isset( $this->data[ $key ] ) ) {
     2253                        return $this->data[ $key ];
     2254                } else {
     2255                        return null;
     2256                }
     2257        }
     2258
     2259        /**
     2260         * Allow plugins to set otherwise unavailable object properties
     2261         *
     2262         * This magic method is here for backward compatibility with plugins
     2263         * that may attempt to modify the group extension by manually assigning
     2264         * a value to an object property that no longer exists, such as
     2265         * $this->enable_create_step.
     2266         *
     2267         * @since BuddyPress (1.8)
     2268         * @param string $key
     2269         * @param mixed $value
     2270         */
     2271        public function __set( $key, $value ) {
     2272
     2273                if ( empty( $this->initialized ) ) {
     2274                        $this->data[ $key ] = $value;
     2275                }
     2276
     2277                switch ( $key ) {
     2278                        case 'enable_create_step' :
     2279                                $this->screens['create']['enabled'] = $value;
     2280                                break;
     2281
     2282                        case 'enable_edit_item' :
     2283                                $this->screens['edit']['enabled'] = $value;
     2284                                break;
     2285
     2286                        case 'enable_admin_item' :
     2287                                $this->screens['admin']['enabled'] = $value;
     2288                                break;
     2289
     2290                        case 'create_step_position' :
     2291                                $this->screens['create']['position'] = $value;
     2292                                break;
     2293
     2294                        // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin'
     2295                        case 'admin_name' :
     2296                                $this->screens['edit']['name'] = $value;
     2297                                break;
     2298
     2299                        case 'admin_slug' :
     2300                                $this->screens['edit']['slug'] = $value;
     2301                                break;
     2302
     2303                        case 'create_name' :
     2304                                $this->screens['create']['name'] = $value;
     2305                                break;
     2306
     2307                        case 'create_slug' :
     2308                                $this->screens['create']['slug'] = $value;
     2309                                break;
     2310
     2311                        case 'admin_metabox_context' :
     2312                                $this->screens['admin']['metabox_context'] = $value;
     2313                                break;
     2314
     2315                        case 'admin_metabox_priority' :
     2316                                $this->screens['admin']['metabox_priority'] = $value;
     2317                                break;
     2318
     2319                        default :
     2320                                $this->data[ $key ] = $value;
     2321                                break;
     2322                }
     2323        }
     2324
     2325        /**
     2326         * Returns a list of legacy properties
     2327         *
     2328         * The legacy implementation of BP_Group_Extension used all of these
     2329         * object properties for configuration. Some have been moved.
     2330         *
     2331         * @since BuddyPress (1.8)
     2332         * @return array
     2333         */
     2334        protected function get_legacy_property_list() {
     2335                return array(
     2336                        'name',
     2337                        'slug',
     2338                        'admin_name',
     2339                        'admin_slug',
     2340                        'create_name',
     2341                        'create_slug',
     2342                        'visibility',
     2343                        'create_step_position',
     2344                        'nav_item_position',
     2345                        'admin_metabox_context',
     2346                        'admin_metabox_priority',
     2347                        'enable_create_step',
     2348                        'enable_nav_item',
     2349                        'enable_edit_item',
     2350                        'enable_admin_item',
     2351                        'nav_item_name',
     2352                        'display_hook',
     2353                        'template_file',
     2354                );
     2355        }
     2356
     2357        /**
     2358         * Parse legacy properties
     2359         *
     2360         * The old standard for BP_Group_Extension was for plugins to register
     2361         * their settings as properties in their constructor. The new method is
     2362         * to pass a config array to the init() method. In order to support
     2363         * legacy plugins, we slurp up legacy properties, and later on we'll
     2364         * parse them into the new init() array.
     2365         *
     2366         * @since BuddyPress (1.8)
     2367         */
     2368        protected function parse_legacy_properties() {
     2369
     2370                // Only run this one time
     2371                if ( ! empty( $this->legacy_properties_converted ) ) {
     2372                        return;
     2373                }
     2374
     2375                $properties = $this->get_legacy_property_list();
     2376
     2377                // By-reference variable for convenience
     2378                $lpc =& $this->legacy_properties_converted;
     2379
     2380                foreach ( $properties as $property ) {
     2381
     2382                        // No legacy config exists for this key
     2383                        if ( ! isset( $this->{$property} ) ) {
     2384                                continue;
     2385                        }
     2386
     2387                        // Grab the value and record it as appropriate
     2388                        $value = $this->{$property};
     2389
     2390                        switch ( $property ) {
     2391                                case 'enable_create_step' :
     2392                                        $lpc['screens']['create']['enabled'] = (bool) $value;
     2393                                        break;
     2394
     2395                                case 'enable_edit_item' :
     2396                                        $lpc['screens']['edit']['enabled'] = (bool) $value;
     2397                                        break;
     2398
     2399                                case 'enable_admin_item' :
     2400                                        $lpc['screens']['admin']['enabled'] = (bool) $value;
     2401                                        break;
     2402
     2403                                case 'create_step_position' :
     2404                                        $lpc['screens']['create']['position'] = $value;
     2405                                        break;
     2406
     2407                                // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin'
     2408                                case 'admin_name' :
     2409                                        $lpc['screens']['edit']['name'] = $value;
     2410                                        break;
     2411
     2412                                case 'admin_slug' :
     2413                                        $lpc['screens']['edit']['slug'] = $value;
     2414                                        break;
     2415
     2416                                case 'create_name' :
     2417                                        $lpc['screens']['create']['name'] = $value;
     2418                                        break;
     2419
     2420                                case 'create_slug' :
     2421                                        $lpc['screens']['create']['slug'] = $value;
     2422                                        break;
     2423
     2424                                case 'admin_metabox_context' :
     2425                                        $lpc['screens']['admin']['metabox_context'] = $value;
     2426                                        break;
     2427
     2428                                case 'admin_metabox_priority' :
     2429                                        $lpc['screens']['admin']['metabox_priority'] = $value;
     2430                                        break;
     2431
     2432                                default :
     2433                                        $lpc[ $property ] = $value;
     2434                                        break;
     2435                        }
     2436                }
     2437        }
     2438
     2439        /**
     2440         * Set up legacy properties
     2441         *
     2442         * This method is responsible for ensuring that all legacy config
     2443         * properties are stored in an array $this->legacy_properties, so that
     2444         * they remain available to plugins that reference the variables at
     2445         * their old locations.
     2446         *
     2447         * @see self::__get()
     2448         *
     2449         * @since BuddyPress (1.8)
     2450         */
     2451        protected function setup_legacy_properties() {
     2452
     2453                // Only run this one time
     2454                if ( ! empty( $this->legacy_properties ) ) {
     2455                        return;
     2456                }
     2457
     2458                $properties = $this->get_legacy_property_list();
     2459                $params     = $this->params;
     2460                $lp         =& $this->legacy_properties;
     2461
     2462                foreach ( $properties as $property ) {
     2463                        switch ( $property ) {
     2464                                case 'enable_create_step' :
     2465                                        $lp['enable_create_step'] = $params['screens']['create']['enabled'];
     2466                                        break;
     2467
     2468                                case 'enable_edit_item' :
     2469                                        $lp['enable_edit_item'] = $params['screens']['edit']['enabled'];
     2470                                        break;
     2471
     2472                                case 'enable_admin_item' :
     2473                                        $lp['enable_admin_item'] = $params['screens']['admin']['enabled'];
     2474                                        break;
     2475
     2476                                case 'create_step_position' :
     2477                                        $lp['create_step_position'] = $params['screens']['create']['position'];
     2478                                        break;
     2479
     2480                                // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin'
     2481                                case 'admin_name' :
     2482                                        $lp['admin_name'] = $params['screens']['edit']['name'];
     2483                                        break;
     2484
     2485                                case 'admin_slug' :
     2486                                        $lp['admin_slug'] = $params['screens']['edit']['slug'];
     2487                                        break;
     2488
     2489                                case 'create_name' :
     2490                                        $lp['create_name'] = $params['screens']['create']['name'];
     2491                                        break;
     2492
     2493                                case 'create_slug' :
     2494                                        $lp['create_slug'] = $params['screens']['create']['slug'];
     2495                                        break;
     2496
     2497                                case 'admin_metabox_context' :
     2498                                        $lp['admin_metabox_context'] = $params['screens']['admin']['metabox_context'];
     2499                                        break;
     2500
     2501                                case 'admin_metabox_priority' :
     2502                                        $lp['admin_metabox_priority'] = $params['screens']['admin']['metabox_priority'];
     2503                                        break;
     2504
     2505                                default :
     2506                                        // All other items get moved over
     2507                                        $lp[ $property ] = $params[ $property ];
     2508
     2509                                        // Also reapply to the object, for backpat
     2510                                        $this->{$property} = $params[ $property ];
     2511
     2512                                        break;
     2513                        }
     2514                }
     2515        }
    16062516}
    16072517
    1608 function bp_register_group_extension( $group_extension_class ) {
     2518function bp_register_group_extension( $group_extension_class = '' ) {
    16092519
    1610         if ( !class_exists( $group_extension_class ) )
     2520        if ( ! class_exists( $group_extension_class ) ) {
    16112521                return false;
     2522        }
    16122523
    16132524        // Register the group extension on the bp_init action so we have access
    16142525        // to all plugins.
  • tests/assets/group-extensions.php

     
     1<?php
     2
     3/**
     4 * The following implementations of BP_Group_Extension act as dummy plugins
     5 * for our unit tests
     6 */
     7
     8class BPTest_Group_Extension_Parse_Legacy_Properties extends BP_Group_Extension {
     9        function __construct() {
     10                $class_name = get_class( $this );
     11                $this->name = $class_name;
     12                $this->slug = sanitize_title( $class_name );
     13                $this->admin_name = $this->name . ' Edit';
     14                $this->admin_slug = $this->slug . '-edit';
     15                $this->create_name = $this->name . ' Create';
     16                $this->create_slug = $this->slug . '-create';
     17                $this->visibility = 'private';
     18                $this->create_step_position = 58;
     19                $this->nav_item_position = 63;
     20                $this->admin_metabox_context = 'high';
     21                $this->admin_metabox_priority = 'side';
     22                $this->enable_create_step = false;
     23                $this->enable_nav_item = true;
     24                $this->enable_edit_item = false;
     25                $this->enable_admin_item = true;
     26                $this->nav_item_name = $this->name . ' Nav';
     27                $this->display_hook = 'foo_hook';
     28                $this->template_file = 'foo_template';
     29        }
     30
     31        /**
     32         * Provides access to protected method unneeded in BP
     33         */
     34        function _parse_legacy_properties() {
     35                return $this->parse_legacy_properties();
     36        }
     37
     38        /**
     39         * Provides access to protected property unneeded in BP
     40         */
     41        function _get_legacy_properties_converted() {
     42                return $this->legacy_properties_converted;
     43        }
     44}
     45
     46class BPTest_Group_Extension_Setup_Screens_Use_Global_Fallbacks extends BP_Group_Extension {
     47        function __construct() {
     48                $class_name = get_class( $this );
     49                $this->slug = sanitize_title( $class_name );
     50                $this->name = $class_name;
     51        }
     52
     53        /**
     54         * Provides access to protected method unneeded in BP
     55         */
     56        function _get_default_screens() {
     57                return $this->get_default_screens();
     58        }
     59
     60        /**
     61         * Provides access to protected method unneeded in BP
     62         */
     63        function _setup_class_info() {
     64                return $this->setup_class_info();
     65        }
     66
     67}
     68
     69class BPTest_Group_Extension_Setup_Screens_Define_Edit_Screens_Locally extends BP_Group_Extension {
     70        function __construct() {
     71                $class_name = get_class( $this );
     72                $this->slug = sanitize_title( $class_name );
     73                $this->name = $class_name;
     74        }
     75
     76        function edit_screen() {}
     77        function edit_screen_save() {}
     78
     79        /**
     80         * Provides access to protected method unneeded in BP
     81         */
     82        function _get_default_screens() {
     83                return $this->get_default_screens();
     84        }
     85
     86        /**
     87         * Provides access to protected method unneeded in BP
     88         */
     89        function _setup_class_info() {
     90                return $this->setup_class_info();
     91        }
     92
     93}
     94
     95class BPTest_Group_Extension_Access_Root_Property extends BP_Group_Extension {
     96        function __construct() {
     97                $class_name = get_class( $this );
     98
     99                $args = array(
     100                        'slug' => sanitize_title( $class_name ),
     101                        'name' => $class_name,
     102                        'nav_item_position' => 39,
     103                );
     104
     105                parent::init( $args );
     106        }
     107}
     108
     109class BPTest_Group_Extension_Access_Init_Property_Using_Legacy_Location extends BP_Group_Extension {
     110        function __construct() {
     111                $class_name = get_class( $this );
     112
     113                $args = array(
     114                        'slug' => sanitize_title( $class_name ),
     115                        'name' => $class_name,
     116                        'screens' => array(
     117                                'create' => array(
     118                                        'position' => 18,
     119                                ),
     120                        ),
     121                );
     122
     123                parent::init( $args );
     124        }
     125}
     126
     127class BPTest_Group_Extension_Get_Screen_Callback_Fallbacks extends BP_Group_Extension {
     128        function __construct() {
     129                $class_name = get_class( $this );
     130
     131                $args = array(
     132                        'slug' => sanitize_title( $class_name ),
     133                        'name' => $class_name,
     134                );
     135
     136                parent::init( $args );
     137        }
     138
     139        function settings_screen() {}
     140        function settings_screen_save() {}
     141
     142        function edit_screen() {}
     143        function edit_screen_save() {}
     144}
  • tests/bootstrap.php

     
    22
    33define( 'BP_PLUGIN_DIR', dirname( dirname( __FILE__ ) ) . '/' );
    44
     5if ( ! defined( 'BP_TESTS_DIR' ) ) {
     6        define( 'BP_TESTS_DIR', dirname( __FILE__ ) . '/' );
     7}
     8
    59require_once getenv( 'WP_TESTS_DIR' ) . '/includes/functions.php';
    610
    711function _install_and_load_buddypress() {
    8         require dirname( __FILE__ ) . '/includes/loader.php';
     12        require BP_TESTS_DIR . '/includes/loader.php';
    913}
    1014tests_add_filter( 'muplugins_loaded', '_install_and_load_buddypress' );
    1115
    1216require getenv( 'WP_TESTS_DIR' ) . '/includes/bootstrap.php';
    1317
    1418// Load the BP-specific testing tools
    15 require dirname( __FILE__ ) . '/includes/testcase.php';
     19require BP_TESTS_DIR . '/includes/testcase.php';
  • tests/testcases/groups/class-bp-group-extension.php

     
     1<?php
     2
     3include_once BP_TESTS_DIR . '/assets/group-extensions.php';
     4
     5/**
     6 * @group groups
     7 */
     8class BP_Tests_Group_Extension_TestCases extends BP_UnitTestCase {
     9        public function setUp() {
     10                parent::setUp();
     11        }
     12
     13        public function tearDown() {
     14                parent::tearDown();
     15        }
     16
     17        public function test_parse_legacy_properties() {
     18                $class_name = 'BPTest_Group_Extension_Parse_Legacy_Properties';
     19                $class_slug = sanitize_title( $class_name );
     20                $e = new $class_name();
     21                $e->_register();
     22
     23                // Test most items separately so we can ignore irrelevant props
     24                $l = $e->_get_legacy_properties_converted();
     25                $this->assertEquals( $l['name'], $class_name );
     26                $this->assertEquals( $l['slug'], $class_slug );
     27                $this->assertEquals( $l['visibility'], 'private' );
     28                $this->assertEquals( $l['nav_item_position'], 63 );
     29                $this->assertEquals( $l['enable_nav_item'], true );
     30                $this->assertEquals( $l['nav_item_name'], $class_name . ' Nav' );
     31                $this->assertEquals( $l['display_hook'], 'foo_hook' );
     32                $this->assertEquals( $l['template_file'], 'foo_template' );
     33
     34                // Build the screens array manually
     35                $expected = array(
     36                        'create' => array(
     37                                'name' => $class_name . ' Create',
     38                                'slug' => $class_slug . '-create',
     39                                'position' => 58,
     40                                'enabled' => false,
     41                        ),
     42                        'edit' => array(
     43                                'name' => $class_name . ' Edit',
     44                                'slug' => $class_slug . '-edit',
     45                                'enabled' => false,
     46                        ),
     47                        'admin' => array(
     48                                'enabled' => true,
     49                                'metabox_context' => 'high',
     50                                'metabox_priority' => 'side',
     51                        ),
     52                );
     53
     54                $this->assertEquals( $expected, $l['screens'] );
     55        }
     56
     57        public function test_setup_screens_use_global_fallbacks() {
     58                $class_name = 'BPTest_Group_Extension_Setup_Screens_Use_Global_Fallbacks';
     59                $e = new $class_name();
     60                $e->_setup_class_info();
     61                $screens = $e->_get_default_screens();
     62
     63                $fallback = array(
     64                        'screen_callback' => array( $class_name, 'settings_screen' ),
     65                        'screen_save_callback' => array( $class_name, 'settings_screen_save' ),
     66                );
     67                $fallbacks = array(
     68                        'create' => $fallback,
     69                        'edit' => $fallback,
     70                        'admin' => $fallback,
     71                );
     72
     73                // strip everything from the screens array but what we we're
     74                // testing
     75                foreach ( $screens as &$screen ) {
     76                        foreach ( $screen as $k => $v ) {
     77                                if ( ! in_array( $k, array( 'screen_callback', 'screen_save_callback' ) ) ) {
     78                                        unset( $screen[ $k ] );
     79                                }
     80                        }
     81                }
     82
     83                $this->assertEquals( $screens, $fallbacks );
     84        }
     85
     86        public function test_setup_screens_define_edit_screens_locally() {
     87                $class_name = 'BPTest_Group_Extension_Setup_Screens_Define_Edit_Screens_Locally';
     88                $e = new $class_name();
     89                $e->_setup_class_info();
     90                $screens = $e->_get_default_screens();
     91
     92                $fallback = array(
     93                        'screen_callback' => array( $class_name, 'settings_screen' ),
     94                        'screen_save_callback' => array( $class_name, 'settings_screen_save' ),
     95                );
     96                $expected = array(
     97                        'create' => $fallback,
     98                        'edit' => array(
     99                                'screen_callback' => array( $class_name, 'edit_screen' ),
     100                                'screen_save_callback' => array( $class_name, 'edit_screen_save' ),
     101                        ),
     102                        'admin' => $fallback,
     103                );
     104
     105                // strip everything from the screens array but what we we're
     106                // testing
     107                foreach ( $screens as &$screen ) {
     108                        foreach ( $screen as $k => $v ) {
     109                                if ( ! in_array( $k, array( 'screen_callback', 'screen_save_callback' ) ) ) {
     110                                        unset( $screen[ $k ] );
     111                                }
     112                        }
     113                }
     114
     115                $this->assertEquals( $screens, $expected );
     116        }
     117
     118        public function test_parse_args_r() {
     119                $a = array(
     120                        'veggies' => 'yes',
     121                        'ice_cream' => 'dope',
     122                        'fruit' => array(
     123                                'apple' => 'gross',
     124                                'berries' => array(
     125                                        'blueberries' => array(
     126                                                'in_season' => 'never',
     127                                                'oh' => 'boy',
     128                                        ),
     129                                        'cherries' => 'sometimes',
     130                                ),
     131                        ),
     132                );
     133
     134                $b = array(
     135                        'veggies' => 'no',
     136                        'cheese' => array(
     137                                'cheddar' => 'good',
     138                        ),
     139                        'fruit' => array(
     140                                'apple' => 'yum',
     141                                'berries' => array(
     142                                        'strawberries' => 'awesome',
     143                                        'blueberries' => array(
     144                                                'in_season' => 'yes',
     145                                                'out_of_season' => 'no',
     146                                        ),
     147                                ),
     148                        ),
     149                );
     150
     151                $expected = array(
     152                        'veggies' => 'yes',
     153                        'ice_cream' => 'dope',
     154                        'cheese' => array(
     155                                'cheddar' => 'good',
     156                        ),
     157                        'fruit' => array(
     158                                'apple' => 'gross',
     159                                'berries' => array(
     160                                        'strawberries' => 'awesome',
     161                                        'blueberries' => array(
     162                                                'in_season' => 'never',
     163                                                'out_of_season' => 'no',
     164                                                'oh' => 'boy',
     165                                        ),
     166                                        'cherries' => 'sometimes',
     167                                ),
     168                        ),
     169                );
     170
     171                $this->assertEquals( $expected, BP_Group_Extension::parse_args_r( $a, $b ) );
     172        }
     173
     174        /**
     175         * Config that gets intentionally stored as a direct property of object
     176         */
     177        public function test_access_root_property() {
     178                $class_name = 'BPTest_Group_Extension_Access_Root_Property';
     179                $e = new $class_name();
     180                $e->_register();
     181
     182                $this->assertEquals( 39, $e->nav_item_position );
     183        }
     184
     185        /**
     186         * Config that gets registered using init(), but is then accessed via
     187         * the legacy location
     188         */
     189        public function test_access_init_property_using_legacy_location() {
     190                $class_name = 'BPTest_Group_Extension_Access_Init_Property_Using_Legacy_Location';
     191                $e = new $class_name();
     192                $e->_register();
     193
     194                $this->assertEquals( 18, $e->create_step_position );
     195        }
     196
     197        /**
     198         * Provides settings_screen* and edit_screen*
     199         */
     200        public function test_get_screen_callback_fallbacks() {
     201                $class_name = 'BPTest_Group_Extension_Get_Screen_Callback_Fallbacks';
     202                $e = new $class_name();
     203                $e->_register();
     204
     205                $this->assertEquals( array( $class_name, 'settings_screen' ), $e->screens['create']['screen_callback'] );
     206                $this->assertEquals( array( $class_name, 'settings_screen_save' ), $e->screens['create']['screen_save_callback'] );
     207                $this->assertEquals( array( $class_name, 'settings_screen' ), $e->screens['admin']['screen_callback'] );
     208                $this->assertEquals( array( $class_name, 'settings_screen_save' ), $e->screens['admin']['screen_save_callback'] );
     209                $this->assertEquals( array( $class_name, 'edit_screen' ), $e->screens['edit']['screen_callback'] );
     210                $this->assertEquals( array( $class_name, 'edit_screen_save' ), $e->screens['edit']['screen_save_callback'] );
     211        }
     212
     213        public function test_has_submit_button() {
     214                $a = '<p>Foo bar</p><input type="text" name="awesome" /><input name="save" type="submit" id="saverrrr" />sweet';
     215                $this->assertTrue( BP_Group_Extension::has_submit_button( $a ) );
     216
     217                $b = '<p>Foo bar</p><input type="text" name="awesome" />sweet';
     218                $this->assertFalse( BP_Group_Extension::has_submit_button( $b ) );
     219
     220                // switch the quotation marks
     221                $c = "<p>Foo bar</p><input type='text' name='awesome' /><input name='save' type='submit' id='saverrrr' />sweet";
     222                $this->assertTrue( BP_Group_Extension::has_submit_button( $c ) );
     223        }
     224}