Skip to:
Content

BuddyPress.org

Ticket #4955: 4955.01.patch

File 4955.01.patch, 54.8 KB (added by boonebgorges, 13 years ago)
  • bp-groups/bp-groups-classes.php

    diff --git bp-groups/bp-groups-classes.php bp-groups/bp-groups-classes.php
    index 64c3c3a..a542426 100644
    class BP_Groups_Member { 
    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.
     1412 *
     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.
    13751437 *
    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        /**
     1445         * @var array Information about this extension's screens
     1446         * @since BuddyPress (1.8)
     1447         */
     1448        var $screens = array();
     1449
     1450        /**
     1451         * @var string The name of the extending class
     1452         * @since BuddyPress (1.8)
     1453         */
     1454        var $class_name;
     1455
     1456        /**
     1457         * @var object A ReflectionClass object of the current extension
     1458         * @since BuddyPress (1.8)
     1459         */
     1460        var $class_reflection;
    13871461
    1388         // The name/slug of the Group Creation tab for this extension
    1389         var $create_name = '';
    1390         var $create_slug = '';
     1462        /**
     1463         * @var array Parsed configuration paramaters for the extension
     1464         * @since BuddyPress (1.8)
     1465         */
     1466        var $params = array();
    13911467
    1392         // Will this extension be visible to non-members of a group? Options: public/private
    1393         var $visibility = 'public';
     1468        /**
     1469         * @var int The id of the current group
     1470         * @since BuddyPress (1.8)
     1471         */
     1472        var $group_id;
    13941473
    1395         var $create_step_position = 81;
    1396         var $nav_item_position = 81;
     1474        /**
     1475         * @var string The slug of the current extension
     1476         */
     1477        var $slug;
    13971478
    13981479        /**
    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)
     1480         * @var string The translatable name of the current extension
    14031481         */
    1404         var $admin_metabox_context = 'normal';
     1482        var $name;
    14051483
    14061484        /**
    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)
     1485         * @var string Whether the extension tab is visible. 'public'
     1486         *   or 'private'
    14111487         */
    1412         var $admin_metabox_priority = 'core';
     1488        var $visibility;
    14131489
    1414         var $enable_create_step = true;
    1415         var $enable_nav_item = true;
    1416         var $enable_edit_item = true;
    1417         var $enable_admin_item = true;
     1490        /**
     1491         * @var int The numeric position of the main nav item
     1492         */
     1493        var $nav_item_position;
    14181494
    1419         var $nav_item_name = false;
     1495        /**
     1496         * @var bool Whether to show the nav item
     1497         */
     1498        var $enable_nav_item;
    14201499
    1421         var $display_hook = 'groups_custom_group_boxes';
    1422         var $template_file = 'groups/single/plugins';
     1500        /**
     1501         * @var string The text of the nav item. Defaults to self::name
     1502         */
     1503        var $nav_item_name;
     1504
     1505        /**
     1506         * @var string The WP action that self::widget_display() is attached
     1507         *   to. Defaults to 'groups_custom_group_boxes'
     1508         */
     1509        var $display_hook;
     1510
     1511        /**
     1512         * @var string The template file used to load the plugin content.
     1513         *   Defaults to 'groups/single/plugins'
     1514         */
     1515        var $template_file;
    14231516
    1424         // Methods you should override
     1517        /**
     1518         * @var bool Has the extension been initialized?
     1519         * @since BuddyPress (1.8)
     1520         */
     1521        protected $initialized = false;
    14251522
    1426         function display() {}
     1523        /**
     1524         * @var array Extension properties as set by legacy extensions
     1525         * @since BuddyPress (1.8)
     1526         */
     1527        protected $legacy_properties = array();
    14271528
    1428         function widget_display() {}
     1529        /**
     1530         * @var array Extension properties as set by legacy extensions, but
     1531         *   converted to match the new format for params
     1532         * @since BuddyPress (1.8)
     1533         */
     1534        protected $legacy_properties_converted = array();
    14291535
    1430         function edit_screen( $group_id = null ) {}
     1536        /**
     1537         * @var array Miscellaneous data as set by the __set() magic method
     1538         * @since BuddyPress (1.8)
     1539         */
     1540        protected $data = array();
    14311541
    1432         function edit_screen_save( $group_id = null ) {}
     1542        /** Screen Overrides **************************************************/
    14331543
    1434         function create_screen( $group_id = null ) {}
     1544        /**
     1545         * Screen override methods are how your extension will display content
     1546         * and handle form submits. Your extension should only override those
     1547         * methods that it needs for its purposes.
     1548         */
    14351549
    1436         function create_screen_save( $group_id = null ) {}
     1550        // The content of the group tab
     1551        public function display() {}
    14371552
    1438         // Private Methods
     1553        // Content displayed in a widget sidebar, if applicable
     1554        public function widget_display() {}
    14391555
    1440         function _register() {
    1441                 global $bp;
     1556        // *_screen() displays the settings form for the given context
     1557        // *_screen_save() processes data submitted via the settings form
     1558        // The settings_* methods are generic fallbacks, which can optionally
     1559        // be overridden by the more specific edit_*, create_*, and admin_*
     1560        // versions.
     1561        public function settings_screen( $group_id = null ) {}
     1562        public function settings_screen_save( $group_id = null ) {}
     1563        public function edit_screen( $group_id = null ) {}
     1564        public function edit_screen_save( $group_id = null ) {}
     1565        public function create_screen( $group_id = null ) {}
     1566        public function create_screen_save( $group_id = null ) {}
     1567        public function admin_screen( $group_id = null ) {}
     1568        public function admin_screen_save( $group_id = null ) {}
    14421569
    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;
     1570        /** Setup *************************************************************/
     1571
     1572        /**
     1573         * Initialize the extension, using your config settings
     1574         *
     1575         * Your plugin should call this method at the very end of its
     1576         * constructor, like so:
     1577         *
     1578         *   public function __construct() {
     1579         *       $args = array(
     1580         *           'slug' => 'my-group-extension',
     1581         *           'name' => 'My Group Extension',
     1582         *           // ...
     1583         *       );
     1584         *
     1585         *       parent::init( $args );
     1586         *   }
     1587         *
     1588         * @since BuddyPress (1.8)
     1589         * @param array $args See inline definition below for arguments
     1590         */
     1591        public function init( $args = array() ) {
     1592
     1593                // Before this init() method was introduced, plugins were
     1594                // encouraged to set their config directly. For backward
     1595                // compatibility with these plugins, we detect whether this is
     1596                // one of those legacy plugins, and parse any legacy arguments
     1597                // with those passed to init()
     1598                $this->parse_legacy_properties();
     1599                $args = $this->parse_args_r( $args, $this->legacy_properties_converted );
     1600
     1601                // Parse with defaults
     1602                $this->params = $this->parse_args_r( $args, array(
     1603                        'slug'                   => '',
     1604                        'name'                   => '',
     1605                        'visibility'             => 'public',
     1606                        'nav_item_position'      => 81,
     1607                        'enable_nav_item'        => true,
     1608                        'nav_item_name'          => false,
     1609                        'display_hook'           => 'groups_custom_group_boxes',
     1610                        'template_file'          => 'groups/single/plugins',
     1611                        'screens'                => $this->get_default_screens(),
     1612                ) );
     1613
     1614                $this->initialized = true;
     1615        }
     1616
     1617        /**
     1618         * The main setup routine for the extension
     1619         *
     1620         * This method contains the primary logic for setting up an extension's
     1621         * configuration, setting up backward compatibility for legacy plugins,
     1622         * and hooking the extension's screen functions into WP and BP.
     1623         *
     1624         * Marked 'public' because it must be accessible to add_action().
     1625         * However, you should never need to invoke this method yourself - it
     1626         * is called automatically at the right point in the load order by
     1627         * bp_register_group_extension().
     1628         *
     1629         * @since BuddyPress (1.1)
     1630         */
     1631        public function _register() {
     1632                // Detect and parse properties set by legacy extensions
     1633                $this->parse_legacy_properties();
     1634
     1635                // Initialize, if necessary. This should only happen for
     1636                // legacy extensions that don't call parent::init() themselves
     1637                if ( ! $this->initialized ) {
     1638                        $this->init();
    14471639                }
    14481640
    1449                 if ( ! $this->admin_slug ) {
    1450                         $this->admin_slug = $this->slug;
     1641                // Set some config values, based on the parsed params
     1642                $this->group_id          = $this->get_group_id();
     1643                $this->slug              = $this->params['slug'];
     1644                $this->name              = $this->params['name'];
     1645                $this->visibility        = $this->params['visibility'];
     1646                $this->nav_item_position = $this->params['nav_item_position'];
     1647                $this->nav_item_name     = $this->params['nav_item_name'];
     1648                $this->display_hook      = $this->params['display_hook'];
     1649                $this->template_file     = $this->params['template_file'];
     1650
     1651                // Configure 'screens': create, admin, and edit contexts
     1652                $this->setup_screens();
     1653
     1654                // Mirror configuration data so it's accessible to plugins
     1655                // that look for it in its old locations
     1656                $this->setup_legacy_properties();
     1657
     1658                // Hook the extension into BuddyPress
     1659                $this->setup_display_hooks();
     1660                $this->setup_create_hooks();
     1661                $this->setup_edit_hooks();
     1662                $this->setup_admin_hooks();
     1663        }
     1664
     1665        /**
     1666         * Set up some basic info about the Extension
     1667         *
     1668         * Here we collect the name of the extending class, as well as a
     1669         * ReflectionClass that is used in get_screen_callback() to determine
     1670         * whether your extension overrides certain callback methods.
     1671         *
     1672         * @since BuddyPress (1.8)
     1673         */
     1674        protected function setup_class_info() {
     1675                if ( is_null( $this->class_name ) ) {
     1676                        $this->class_name = get_class( $this );
    14511677                }
    14521678
    1453                 if ( ! $this->create_name ) {
    1454                         $this->create_name = $this->name;
     1679                if ( is_null( $this->class_reflection ) ) {
     1680                        $this->class_reflection = new ReflectionClass( $this->class_name );
    14551681                }
     1682        }
    14561683
    1457                 if ( ! $this->create_slug ) {
    1458                         $this->create_slug = $this->slug;
     1684        /**
     1685         * Get the current group id
     1686         *
     1687         * Check for:
     1688         *   - current group
     1689         *   - new group
     1690         *   - group admin
     1691         *
     1692         * @since BuddyPress (1.8)
     1693         */
     1694        public static function get_group_id() {
     1695                // Usually this will work
     1696                $group_id = bp_get_current_group_id();
     1697
     1698                // On the admin, get the group id out of the $_GET params
     1699                if ( ! $group_id && is_admin() && isset( $_GET['page'] ) && 'bp-groups' === $_GET['page'] && ! empty( $_GET['gid'] ) ) {
     1700                        $group_id = (int) $_GET['gid'];
    14591701                }
    14601702
    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                         );
     1703                // This fallback will only be hit when the create step is
     1704                // very early
     1705                if ( ! $group_id && bp_get_new_group_id() ) {
     1706                        $group_id = bp_get_new_group_id();
     1707                }
    14681708
    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' ) );
    1476                 }
    1477 
    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                                         ) );
    1492 
    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                                 }
     1709                // On some setups, the group id has to be fetched out of the
     1710                // $_POST array
     1711                // @todo Figure out why this is happening during group creation
     1712                if ( ! $group_id && isset( $_POST['group_id'] ) ) {
     1713                        $group_id = (int) $_POST['group_id'];
     1714                }
    14991715
    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                                 }
     1716                return $group_id;
     1717        }
     1718
     1719        /**
     1720         * Gather configuration data about your screens
     1721         *
     1722         * @since BuddyPress (1.8)
     1723         */
     1724        protected function get_default_screens() {
     1725                $this->setup_class_info();
     1726
     1727                $screens = array(
     1728                        'create' => array(),
     1729                        'edit'   => array(),
     1730                        'admin'  => array(
     1731                                'metabox_context'  => 'normal',
     1732                                'metabox_priority' => 'core',
     1733                        ),
     1734                );
     1735
     1736                foreach ( $screens as $context => &$screen ) {
     1737                        $screen['enabled'] = true;
     1738
     1739                        $screen['screen_callback']      = $this->get_screen_callback( $context, 'screen' );
     1740                        $screen['screen_save_callback'] = $this->get_screen_callback( $context, 'screen_save' );
     1741
     1742                        $screen['name']        = '';
     1743                        $screen['slug']        = '';
     1744                        $screen['position']    = 81;
     1745                        $screen['submit_text'] = __( 'Save Changes', 'buddypress' );
     1746                }
     1747
     1748                return $screens;
     1749        }
     1750
     1751        /**
     1752         * Set up screens array based on params
     1753         *
     1754         * @since BuddyPress (1.8)
     1755         */
     1756        protected function setup_screens() {
     1757                foreach ( (array) $this->params['screens'] as $context => $screen ) {
     1758                        if ( empty( $screen['slug'] ) ) {
     1759                                $screen['slug'] = $this->slug;
     1760                        }
     1761
     1762                        if ( empty( $screen['name'] ) ) {
     1763                                $screen['name'] = $this->name;
    15041764                        }
     1765
     1766                        $this->screens[ $context ] = $screen;
    15051767                }
     1768        }
    15061769
    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 );
     1770        /** Display *************************************************************/
    15151771
    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() );
     1772        /**
     1773         * Hook this extension's group tab into BuddyPress, if necessary
     1774         *
     1775         * @since BuddyPress (1.8)
     1776         */
     1777        protected function setup_display_hooks() {
     1778                if ( ! bp_is_group() ) {
     1779                        return;
     1780                }
     1781
     1782                if ( 'public' != $this->visibility && ! buddypress()->groups->current_group->user_has_access ) {
     1783                        false;
     1784                }
     1785
     1786                if ( $this->enable_nav_item ) {
     1787                        bp_core_new_subnav_item( array(
     1788                                'name'            => ! $this->nav_item_name ? $this->name : $this->nav_item_name,
     1789                                'slug'            => $this->slug,
     1790                                'parent_slug'     => bp_get_current_group_slug(),
     1791                                'parent_url'      => bp_get_group_permalink( groups_get_current_group() ),
     1792                                'position'        => $this->nav_item_position,
     1793                                'item_css_id'     => 'nav-' . $this->slug,
     1794                                'screen_function' => array( &$this, '_display_hook' ),
     1795                                'user_has_access' => $this->enable_nav_item
     1796                        ) );
     1797
     1798                        // When we are viewing the extension display page, set the title and options title
     1799                        if ( bp_is_current_action( $this->slug ) ) {
     1800                                add_action( 'bp_template_content_header', create_function( '', 'echo "' . esc_attr( $this->name ) . '";' ) );
     1801                                add_action( 'bp_template_title', create_function( '', 'echo "' . esc_attr( $this->name ) . '";' ) );
     1802                        }
     1803                }
    15191804
    1520                                 add_action( 'groups_custom_edit_steps', array( &$this, 'call_edit_screen' ) );
     1805                // Hook the group home widget
     1806                if ( ! bp_current_action() && bp_is_current_action( 'home' ) ) {
     1807                        add_action( $this->display_hook, array( &$this, 'widget_display' ) );
     1808                }
     1809        }
    15211810
    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                                 }
     1811        /**
     1812         * Hooks the main display method, and loads the template file
     1813         */
     1814        public function _display_hook() {
     1815                add_action( 'bp_template_content', array( &$this, 'display' ) );
     1816                bp_core_load_template( apply_filters( 'bp_core_template_plugin', $this->template_file ) );
     1817        }
     1818
     1819        /** Create *************************************************************/
     1820
     1821        /**
     1822         * Hook this extension's Create step into BuddyPress, if necessary
     1823         *
     1824         * @since BuddyPress (1.8)
     1825         */
     1826        protected function setup_create_hooks() {
     1827                if ( ! $this->is_screen_enabled( 'create' ) ) {
     1828                        return;
     1829                }
     1830
     1831                $screen = $this->screens['create'];
     1832
     1833                // Insert the group creation step for the new group extension
     1834                buddypress()->groups->group_creation_steps[ $screen['slug'] ] = array(
     1835                        'name'     => $screen['name'],
     1836                        'slug'     => $screen['slug'],
     1837                        'position' => $screen['position'],
     1838                );
     1839
     1840                // The maybe_ methods check to see whether the create_*
     1841                // callbacks should be invoked (ie, are we on the
     1842                // correct group creation step). Hooked in separate
     1843                // methods because current creation step info not yet
     1844                // available at this point
     1845                add_action( 'groups_custom_create_steps', array( $this, 'maybe_create_screen' ) );
     1846                add_action( 'groups_create_group_step_save_' . $screen['slug'], array( $this, 'maybe_create_screen_save' ) );
     1847        }
     1848
     1849        /**
     1850         * Call the create_screen() method, if we're on the right page
     1851         *
     1852         * @since 1.8
     1853         */
     1854        public function maybe_create_screen() {
     1855                if ( bp_is_group_creation_step( $this->screens['create']['slug'] ) ) {
     1856                        call_user_func( $this->screens['create']['screen_callback'], $this->group_id );
     1857                        $this->nonce_field( 'create' );
     1858
     1859                        // The create screen requires an additional nonce field
     1860                        // due to a quirk in the way the templates are built
     1861                        wp_nonce_field( 'groups_create_save_' . bp_get_groups_current_create_step() );
     1862                }
     1863        }
     1864
     1865        /**
     1866         * Call the create_screen_save() method, if we're on the right page
     1867         *
     1868         * @since 1.8
     1869         */
     1870        public function maybe_create_screen_save() {
     1871                if ( bp_is_group_creation_step( $this->screens['create']['slug'] ) ) {
     1872                        $this->check_nonce( 'create' );
     1873                        call_user_func( $this->screens['create']['screen_save_callback'], $this->group_id );
     1874                }
     1875        }
     1876
     1877        /** Edit *************************************************************/
     1878
     1879        /**
     1880         * Hook this extension's Edit panel into BuddyPress, if necessary
     1881         *
     1882         * @since BuddyPress (1.8)
     1883         */
     1884        protected function setup_edit_hooks() {
     1885                if ( ! $this->is_screen_enabled( 'edit' ) || ! bp_is_item_admin() ) {
     1886                        return;
     1887                }
     1888
     1889                $screen = $this->screens['edit'];
     1890
     1891                // Add the tab
     1892                // @todo BP should be using bp_core_new_subnav_item()
     1893                add_action( 'groups_admin_tabs', create_function( '$current, $group_slug',
     1894                        '$selected = "";
     1895                        if ( "' . esc_attr( $screen['slug'] ) . '" == $current )
     1896                                $selected = " class=\"current\"";
     1897                        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>";'
     1898                ), 10, 2 );
     1899
     1900                // Catch the edit screen and forward it to the plugin template
     1901                if ( bp_is_groups_component() && bp_is_current_action( 'admin' ) && bp_is_action_variable( $screen['slug'], 0 ) ) {
     1902                        $this->call_edit_screen_save( $this->group_id );
     1903
     1904                        add_action( 'groups_custom_edit_steps', array( &$this, 'call_edit_screen' ) );
     1905
     1906                        if ( '' != bp_locate_template( array( 'groups/single/home.php' ), false ) ) {
     1907                                bp_core_load_template( apply_filters( 'groups_template_group_home', 'groups/single/home' ) );
     1908                        } else {
     1909                                add_action( 'bp_template_content_header', create_function( '', 'echo "<ul class=\"content-header-nav\">"; bp_group_admin_tabs(); echo "</ul>";' ) );
     1910                                add_action( 'bp_template_content', array( &$this, 'call_edit_screen' ) );
     1911                                bp_core_load_template( apply_filters( 'bp_core_template_plugin', '/groups/single/plugins' ) );
    15291912                        }
    15301913                }
     1914        }
     1915
     1916        /**
     1917         * Call the edit_screen() method
     1918         *
     1919         * Previous versions of BP_Group_Extension required plugins to provide
     1920         * their own Submit button and nonce fields when building markup. In
     1921         * BP 1.8, this requirement was lifted - BP_Group_Extension now handles
     1922         * all required submit buttons and nonces.
     1923         *
     1924         * We put the edit screen markup into an output buffer before echoing.
     1925         * This is so that we can check for the presence of a hardcoded submit
     1926         * button, as would be present in legacy plugins; if one is found, we
     1927         * do not auto-add our own button.
     1928         *
     1929         * @since BuddyPress (1.8)
     1930         */
     1931        public function call_edit_screen() {
     1932                ob_start();
     1933                call_user_func( $this->screens['edit']['screen_callback'], $this->group_id );
     1934                $screen = ob_get_contents();
     1935                ob_end_clean();
     1936
     1937                echo $this->maybe_add_submit_button( $screen );
     1938
     1939                $this->nonce_field( 'edit' );
     1940        }
     1941
     1942        /**
     1943         * Check the nonce, and call the edit_screen_save() method
     1944         *
     1945         * @since BuddyPress (1.8)
     1946         */
     1947        public function call_edit_screen_save() {
     1948                if ( ! empty( $_POST ) ) {
     1949                        $this->check_nonce( 'edit' );
     1950                        call_user_func( $this->screens['edit']['screen_save_callback'], $this->group_id );
     1951                }
     1952        }
     1953
     1954        /**
     1955         * Add a submit button to the edit form, if it needs one
     1956         *
     1957         * There's an inconsistency in the way that the group Edit and Create
     1958         * screens are rendered: the Create screen has a submit button built
     1959         * in, but the Edit screen does not. This function allows plugin
     1960         * authors to write markup that does not contain the submit button for
     1961         * use on both the Create and Edit screens - BP will provide the button
     1962         * if one is not found.
     1963         *
     1964         * @since BuddyPress (1.8)
     1965         * @param string $screen The screen markup, captured in the output buffer
     1966         * @param string $screen The same markup, with a submit button added
     1967         */
     1968        protected function maybe_add_submit_button( $screen ) {
     1969                if ( ! $this->has_submit_button( $screen ) ) {
     1970                        $screen .= sprintf(
     1971                                '<div id="%s"><input type="submit" name="save" value="%s" id="%s"></div>',
     1972                                'bp-group-edit-' . $this->slug . '-submit-wrapper',
     1973                                $this->screens['edit']['submit_text'],
     1974                                'bp-group-edit-' . $this->slug . '-submit'
     1975                        );
     1976                }
     1977
     1978                return $screen;
     1979        }
     1980
     1981        /**
     1982         * Does the given markup have a submit button?
     1983         *
     1984         * @since BuddyPress (1.8)
     1985         * @param $screen The markup to check
     1986         * @return bool
     1987         */
     1988        public static function has_submit_button( $screen ) {
     1989                $pattern = "/<input[^>]+type=[\'\"]submit[\'\"]/";
     1990                preg_match( $pattern, $screen, $matches );
     1991                return ! empty( $matches[0] );
     1992        }
     1993
     1994        /** Admin *************************************************************/
    15311995
    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' ) ) {
     1996        /**
     1997         * Hook this extension's Admin metabox into BuddyPress, if necessary
     1998         *
     1999         * @since BuddyPress (1.8)
     2000         */
     2001        protected function setup_admin_hooks() {
     2002                if ( $this->is_screen_enabled( 'admin' ) && is_admin() ) {
    15362003                        // 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' ) );
     2004                        add_action( 'bp_groups_admin_meta_box_content_' . $this->slug, array( $this, 'call_admin_screen' ) );
    15382005
    15392006                        // Initialize the metabox
    15402007                        add_action( 'bp_groups_admin_meta_boxes', array( $this, '_meta_box_display_callback' ) );
    15412008
    15422009                        // 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 );
    1545                         }
     2010                        add_action( 'bp_group_admin_edit_after', array( $this, 'call_admin_screen_save' ), 10 );
    15462011                }
    15472012        }
    15482013
    1549         function _display_hook() {
    1550                 add_action( 'bp_template_content', array( &$this, 'display' ) );
    1551                 bp_core_load_template( apply_filters( 'bp_core_template_plugin', $this->template_file ) );
     2014        /**
     2015         * Call the admin_screen() method, and add a nonce field
     2016         *
     2017         * @since BuddyPress (1.8)
     2018         */
     2019        public function call_admin_screen( $group_id = null ) {
     2020                call_user_func( $this->screens['admin']['screen_callback'], $this->group_id );
     2021                $this->nonce_field( 'admin' );
     2022        }
     2023
     2024        /**
     2025         * Check the nonce, and call the admin_screen_save() method
     2026         *
     2027         * @since BuddyPress (1.8)
     2028         */
     2029        public function call_admin_screen_save( $group_id = null ) {
     2030                $this->check_nonce( 'admin' );
     2031                call_user_func( $this->screens['admin']['screen_save_callback'], $this->group_id );
    15522032        }
    15532033
    15542034        /**
    class BP_Group_Extension { 
    15562036         *
    15572037         * @since BuddyPress (1.7)
    15582038         */
    1559         function _meta_box_display_callback() {
     2039        public function _meta_box_display_callback() {
    15602040                $group_id = isset( $_GET['gid'] ) ? (int) $_GET['gid'] : 0;
    15612041
     2042                $screen = $this->screens['admin'];
     2043
    15622044                add_meta_box(
    1563                         $this->slug,
    1564                         $this->name,
     2045                        $screen['slug'],
     2046                        $screen['name'],
    15652047                        create_function( '', 'do_action( "bp_groups_admin_meta_box_content_' . $this->slug . '", ' . $group_id . ' );' ),
    15662048                        get_current_screen()->id,
    1567                         $this->admin_metabox_context,
    1568                         $this->admin_metabox_priority
     2049                        $screen['metabox_context'],
     2050                        $screen['metabox_priority']
    15692051                );
    15702052        }
    15712053
     2054
     2055        /** Utilities *********************************************************/
     2056
    15722057        /**
    1573          * Call the edit_screen() method
     2058         * Generate the nonce fields for a settings form
    15742059         *
    1575          * Broken into a standalone method so we can pass the current group id
    1576          * to edit_screen()
     2060         * The nonce field name (the second param passed to wp_nonce_field)
     2061         * contains this extension's slug and is thus unique to this extension.
     2062         * This is necessary because in some cases (namely, the Dashboard),
     2063         * more than one extension may generate nonces on the same page, and we
     2064         * must avoid name clashes.
    15772065         *
    1578          * @since 1.8
     2066         * @since BuddyPress (1.8)
     2067         * @uses wp_nonce_field()
     2068         * @param string $context 'create', 'edit', 'admin'
    15792069         */
    1580         public function call_edit_screen() {
    1581                 $this->edit_screen( bp_get_current_group_id() );
     2070        public function nonce_field( $context = '' ) {
     2071                wp_nonce_field( 'bp_group_extension_' . $this->slug . '_' . $context, '_bp_group_' . $context . '_nonce_' . $this->slug );
    15822072        }
    15832073
    15842074        /**
    1585          * Call the create_screen() method, if we're on the right page
     2075         * Check the nonce on a submitted settings form
    15862076         *
    1587          * @since 1.8
     2077         * @since BuddyPress (1.8)
     2078         * @uses check_admin_referer()
     2079         * @param string $context 'create', 'edit', 'admin'
    15882080         */
    1589         public function maybe_create_screen() {
    1590                 if ( bp_is_group_creation_step( $this->slug ) ) {
    1591                         $this->create_screen( bp_get_new_group_id() );
     2081        public function check_nonce( $context = '' ) {
     2082                check_admin_referer( 'bp_group_extension_' . $this->slug . '_' . $context, '_bp_group_' . $context . '_nonce_' . $this->slug );
     2083        }
     2084
     2085        /**
     2086         * Is the specified screen enabled?
     2087         *
     2088         * To be enabled, a screen must both have the 'enabled' key set to true
     2089         * (legacy: $this->enable_create_step, etc), and its screen_callback
     2090         * must also exist and be callable.
     2091         *
     2092         * @since BuddyPress (1.8)
     2093         * @param string $context 'create', 'edit', 'admin'
     2094         * @return bool
     2095         */
     2096        public function is_screen_enabled( $context = '' ) {
     2097                $enabled = false;
     2098
     2099                if ( isset( $this->screens[ $context ] ) ) {
     2100                        $enabled = $this->screens[ $context ]['enabled'] && is_callable( $this->screens[ $context ]['screen_callback'] );
    15922101                }
     2102
     2103                return $enabled;
    15932104        }
    15942105
    15952106        /**
    1596          * Call the create_screen_save() method, if we're on the right page
     2107         * Get the appropriate screen callback for the specified context/type
    15972108         *
    1598          * @since 1.8
     2109         * BP Group Extensions have three special "screen contexts": create,
     2110         * admin, and edit. Each of these contexts has a corresponding
     2111         * _screen() and _screen_save() method, which allow group extension
     2112         * plugins to define different markup and logic for each context.
     2113         *
     2114         * BP also supports fallback settings_screen() and
     2115         * settings_screen_save() methods, which can be used to define markup
     2116         * and logic that is shared between context. For each context, you may
     2117         * either provide context-specific methods, or you can let BP fall back
     2118         * on the shared settings_* callbacks.
     2119         *
     2120         * For example, consider a BP_Group_Extension implementation that looks
     2121         * like this:
     2122         *
     2123         *   // ...
     2124         *   function create_screen( $group_id ) { ... }
     2125         *   function create_screen_save( $group_id ) { ... }
     2126         *   function settings_screen( $group_id ) { ... }
     2127         *   function settings_screen_save( $group_id ) { ... }
     2128         *   // ...
     2129         *
     2130         * BP_Group_Extension will use your create_* methods for the Create
     2131         * steps, and will use your generic settings_* methods for the Edit
     2132         * and Admin contexts. This schema allows plugin authors maximum
     2133         * flexibility without having to repeat themselves.
     2134         *
     2135         * The get_screen_callback() method uses a ReflectionClass object to
     2136         * determine whether your extension has provided a given callback.
     2137         *
     2138         * @since BuddyPress (1.8)
     2139         * @param string $context 'create', 'edit', 'admin'
     2140         * @param string $type 'screen', 'screen_save'
     2141         * @return mixed A callable function handle
    15992142         */
    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() );
     2143        public function get_screen_callback( $context = '', $type = 'screen' ) {
     2144                $callback = '';
     2145
     2146                // Try the context-specific callback first
     2147                $method  = $context . '_' . $type;
     2148                $rmethod = $this->class_reflection->getMethod( $method );
     2149                if ( isset( $rmethod->class ) && $this->class_name === $rmethod->class ) {
     2150                        $callback = array( $this->class_name, $method );
     2151                }
     2152
     2153                if ( ! $callback ) {
     2154                        $fallback_method  = 'settings_' . $type;
     2155                        $rfallback_method = $this->class_reflection->getMethod( $fallback_method );
     2156                        if ( isset( $rfallback_method->class ) && $this->class_name === $rfallback_method->class ) {
     2157                                $callback = array( $this->class_name, $fallback_method );
     2158                        }
     2159                }
     2160
     2161                return $callback;
     2162        }
     2163
     2164        /**
     2165         * Recursive argument parsing
     2166         *
     2167         * This acts like a multi-dimensional version of wp_parse_args() (minus
     2168         * the querystring parsing - you must pass arrays).
     2169         *
     2170         * Values from $a override those from $b; keys in $b that don't exist
     2171         * in $a are passed through.
     2172         *
     2173         * This is different from array_merge_recursive(), both because of the
     2174         * order of preference ($a overrides $b) and because of the fact that
     2175         * array_merge_recursive() combines arrays deep in the tree, rather
     2176         * than overwriting the b array with the a array.
     2177         *
     2178         * The implementation of this function is specific to the needs of
     2179         * BP_Group_Extension, where we know that arrays will always be
     2180         * associative, and that an argument under a given key in one array
     2181         * will be matched by a value of identical depth in the other one. The
     2182         * function is NOT designed for general use, and will probably result
     2183         * in unexpected results when used with data in the wild. See, eg,
     2184         * http://core.trac.wordpress.org/ticket/19888
     2185         *
     2186         * @since BuddyPress (1.8)
     2187         * @arg array $a
     2188         * @arg array $b
     2189         * @return array
     2190         */
     2191        public static function parse_args_r( &$a, $b ) {
     2192                $a = (array) $a;
     2193                $b = (array) $b;
     2194                $r = $b;
     2195
     2196                foreach ( $a as $k => &$v ) {
     2197                        if ( is_array( $v ) && isset( $r[ $k ] ) ) {
     2198                                $r[ $k ] = self::parse_args_r( $v, $r[ $k ] );
     2199                        } else {
     2200                                $r[ $k ] = $v;
     2201                        }
     2202                }
     2203
     2204                return $r;
     2205        }
     2206
     2207        /** Legacy Support ****************************************************/
     2208
     2209        /**
     2210         * In BuddyPress 1.8, the recommended technique for configuring
     2211         * extensions changed from directly setting various object properties
     2212         * in the class constructor, to passing a configuration array to
     2213         * parent::init(). The following methods ensure that extensions created
     2214         * in the old way continue to work, by converting legacy configuration
     2215         * data to the new format.
     2216         */
     2217
     2218        /**
     2219         * Provide access to otherwise unavailable object properties
     2220         *
     2221         * This magic method is here for backward compatibility with plugins
     2222         * that refer to config properties that have moved to a different
     2223         * location (such as enable_create_step, which is now at
     2224         * $this->screens['create']['enabled']
     2225         *
     2226         * The legacy_properties array is set up in
     2227         * self::setup_legacy_properties().
     2228         *
     2229         * @since BuddyPress (1.8)
     2230         * @param string $key
     2231         * @return mixed
     2232         */
     2233        public function __get( $key ) {
     2234                if ( isset( $this->legacy_properties[ $key ] ) ) {
     2235                        return $this->legacy_properties[ $key ];
     2236                } else if ( isset( $this->data[ $key ] ) ) {
     2237                        return $this->data[ $key ];
     2238                } else {
     2239                        return null;
     2240                }
     2241        }
     2242
     2243        /**
     2244         * Allow plugins to set otherwise unavailable object properties
     2245         *
     2246         * This magic method is here for backward compatibility with plugins
     2247         * that may attempt to modify the group extension by manually assigning
     2248         * a value to an object property that no longer exists, such as
     2249         * $this->enable_create_step.
     2250         *
     2251         * @since BuddyPress (1.8)
     2252         * @param string $key
     2253         * @param mixed $value
     2254         */
     2255        public function __set( $key, $value ) {
     2256
     2257                if ( ! $this->is_initialized ) {
     2258                        $this->data[ $key ] = $value;
     2259                }
     2260
     2261                switch ( $key ) {
     2262                        case 'enable_create_step' :
     2263                                $this->screens['create']['enabled'] = $value;
     2264                                break;
     2265
     2266                        case 'enable_edit_item' :
     2267                                $this->screens['edit']['enabled'] = $value;
     2268                                break;
     2269
     2270                        case 'enable_admin_item' :
     2271                                $this->screens['admin']['enabled'] = $value;
     2272                                break;
     2273
     2274                        case 'create_step_position' :
     2275                                $this->screens['create']['position'] = $value;
     2276                                break;
     2277
     2278                        // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin'
     2279                        case 'admin_name' :
     2280                                $this->screens['edit']['name'] = $value;
     2281                                break;
     2282
     2283                        case 'admin_slug' :
     2284                                $this->screens['edit']['slug'] = $value;
     2285                                break;
     2286
     2287                        case 'create_name' :
     2288                                $this->screens['create']['name'] = $value;
     2289                                break;
     2290
     2291                        case 'create_slug' :
     2292                                $this->screens['create']['slug'] = $value;
     2293                                break;
     2294
     2295                        case 'admin_metabox_context' :
     2296                                $this->screens['admin']['metabox_context'] = $value;
     2297                                break;
     2298
     2299                        case 'admin_metabox_priority' :
     2300                                $this->screens['admin']['metabox_priority'] = $value;
     2301                                break;
     2302
     2303                        default :
     2304                                $this->data[ $key ] = $value;
     2305                                break;
    16032306                }
    16042307        }
    16052308
     2309        /**
     2310         * Returns a list of legacy properties
     2311         *
     2312         * The legacy implementation of BP_Group_Extension used all of these
     2313         * object properties for configuration. Some have been moved.
     2314         *
     2315         * @since BuddyPress (1.8)
     2316         * @return array
     2317         */
     2318        protected function get_legacy_property_list() {
     2319                return array(
     2320                        'name',
     2321                        'slug',
     2322                        'admin_name',
     2323                        'admin_slug',
     2324                        'create_name',
     2325                        'create_slug',
     2326                        'visibility',
     2327                        'create_step_position',
     2328                        'nav_item_position',
     2329                        'admin_metabox_context',
     2330                        'admin_metabox_priority',
     2331                        'enable_create_step',
     2332                        'enable_nav_item',
     2333                        'enable_edit_item',
     2334                        'enable_admin_item',
     2335                        'nav_item_name',
     2336                        'display_hook',
     2337                        'template_file',
     2338                );
     2339        }
     2340
     2341        /**
     2342         * Parse legacy properties
     2343         *
     2344         * The old standard for BP_Group_Extension was for plugins to register
     2345         * their settings as properties in their constructor. The new method is
     2346         * to pass a config array to the init() method. In order to support
     2347         * legacy plugins, we slurp up legacy properties, and later on we'll
     2348         * parse them into the new init() array.
     2349         *
     2350         * @since BuddyPress (1.8)
     2351         */
     2352        protected function parse_legacy_properties() {
     2353
     2354                // Only run this one time
     2355                if ( empty( $this->legacy_properties_converted ) ) {
     2356                        $properties = $this->get_legacy_property_list();
     2357
     2358                        // By-reference variable for convenience
     2359                        $lpc =& $this->legacy_properties_converted;
     2360
     2361                        foreach ( $properties as $property ) {
     2362                                // No legacy config exists for this key
     2363                                if ( ! isset( $this->{$property} ) ) {
     2364                                        continue;
     2365                                }
     2366
     2367                                // Grab the value and record it as appropriate
     2368                                $value = $this->{$property};
     2369
     2370                                switch ( $property ) {
     2371                                        case 'enable_create_step' :
     2372                                                $lpc['screens']['create']['enabled'] = (bool) $value;
     2373                                                break;
     2374
     2375                                        case 'enable_edit_item' :
     2376                                                $lpc['screens']['edit']['enabled'] = (bool) $value;
     2377                                                break;
     2378
     2379                                        case 'enable_admin_item' :
     2380                                                $lpc['screens']['admin']['enabled'] = (bool) $value;
     2381                                                break;
     2382
     2383                                        case 'create_step_position' :
     2384                                                $lpc['screens']['create']['position'] = $value;
     2385                                                break;
     2386
     2387                                        // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin'
     2388                                        case 'admin_name' :
     2389                                                $lpc['screens']['edit']['name'] = $value;
     2390                                                break;
     2391
     2392                                        case 'admin_slug' :
     2393                                                $lpc['screens']['edit']['slug'] = $value;
     2394                                                break;
     2395
     2396                                        case 'create_name' :
     2397                                                $lpc['screens']['create']['name'] = $value;
     2398                                                break;
     2399
     2400                                        case 'create_slug' :
     2401                                                $lpc['screens']['create']['slug'] = $value;
     2402                                                break;
     2403
     2404                                        case 'admin_metabox_context' :
     2405                                                $lpc['screens']['admin']['metabox_context'] = $value;
     2406                                                break;
     2407
     2408                                        case 'admin_metabox_priority' :
     2409                                                $lpc['screens']['admin']['metabox_priority'] = $value;
     2410                                                break;
     2411
     2412                                        default :
     2413                                                $lpc[ $property ] = $value;
     2414                                                break;
     2415                                }
     2416                        }
     2417                }
     2418        }
     2419
     2420        /**
     2421         * Set up legacy properties
     2422         *
     2423         * This method is responsible for ensuring that all legacy config
     2424         * properties are stored in an array $this->legacy_properties, so that
     2425         * they remain available to plugins that reference the variables at
     2426         * their old locations.
     2427         *
     2428         * @see self::__get()
     2429         *
     2430         * @since BuddyPress (1.8)
     2431         */
     2432        protected function setup_legacy_properties() {
     2433
     2434                // Only run this one time
     2435                if ( empty( $this->legacy_properties ) ) {
     2436                        $properties = $this->get_legacy_property_list();
     2437                        $params     = $this->params;
     2438                        $lp         =& $this->legacy_properties;
     2439
     2440                        foreach ( $properties as $property ) {
     2441                                switch ( $property ) {
     2442                                        case 'enable_create_step' :
     2443                                                $lp['enable_create_step'] = $params['screens']['create']['enabled'];
     2444                                                break;
     2445
     2446                                        case 'enable_edit_item' :
     2447                                                $lp['enable_edit_item'] = $params['screens']['edit']['enabled'];
     2448                                                break;
     2449
     2450                                        case 'enable_admin_item' :
     2451                                                $lp['enable_admin_item'] = $params['screens']['admin']['enabled'];
     2452                                                break;
     2453
     2454                                        case 'create_step_position' :
     2455                                                $lp['create_step_position'] = $params['screens']['create']['position'];
     2456                                                break;
     2457
     2458                                        // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin'
     2459                                        case 'admin_name' :
     2460                                                $lp['admin_name'] = $params['screens']['edit']['name'];
     2461                                                break;
     2462
     2463                                        case 'admin_slug' :
     2464                                                $lp['admin_slug'] = $params['screens']['edit']['slug'];
     2465                                                break;
     2466
     2467                                        case 'create_name' :
     2468                                                $lp['create_name'] = $params['screens']['create']['name'];
     2469                                                break;
     2470
     2471                                        case 'create_slug' :
     2472                                                $lp['create_slug'] = $params['screens']['create']['slug'];
     2473                                                break;
     2474
     2475                                        case 'admin_metabox_context' :
     2476                                                $lp['admin_metabox_context'] = $params['screens']['admin']['metabox_context'];
     2477                                                break;
     2478
     2479                                        case 'admin_metabox_priority' :
     2480                                                $lp['admin_metabox_priority'] = $params['screens']['admin']['metabox_priority'];
     2481                                                break;
     2482
     2483                                        default :
     2484                                                // All other items get moved over
     2485                                                $lp[ $property ] = $params[ $property ];
     2486
     2487                                                // Also reapply to the object, for backpat
     2488                                                $this->{$property} = $params[ $property ];
     2489
     2490                                                break;
     2491                                }
     2492                        }
     2493                }
     2494        }
    16062495}
    16072496
    16082497function bp_register_group_extension( $group_extension_class ) {
  • new file tests/assets/group-extensions.php

    diff --git tests/assets/group-extensions.php tests/assets/group-extensions.php
    new file mode 100644
    index 0000000..ca4a551
    - +  
     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

    diff --git tests/bootstrap.php tests/bootstrap.php
    index 5d6b734..5dc3f60 100644
     
    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';
  • new file tests/testcases/groups/class-bp-group-extension.php

    diff --git tests/testcases/groups/class-bp-group-extension.php tests/testcases/groups/class-bp-group-extension.php
    new file mode 100644
    index 0000000..4d0a01c
    - +  
     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}