Ticket #4955: 4955.01.patch
| File 4955.01.patch, 54.8 KB (added by , 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 { 1367 1367 * API for creating group extensions without having to hardcode the content into 1368 1368 * the theme. 1369 1369 * 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 1371 1392 * 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. 1375 1437 * 1376 1438 * @package BuddyPress 1377 1439 * @subpackage Groups 1378 1440 * @since BuddyPress (1.1) 1379 1441 */ 1380 1442 class BP_Group_Extension { 1381 var $name = false;1382 var $slug = false;1383 1443 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; 1387 1461 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(); 1391 1467 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; 1394 1473 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; 1397 1478 1398 1479 /** 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 1403 1481 */ 1404 var $ admin_metabox_context = 'normal';1482 var $name; 1405 1483 1406 1484 /** 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' 1411 1487 */ 1412 var $ admin_metabox_priority = 'core';1488 var $visibility; 1413 1489 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; 1418 1494 1419 var $nav_item_name = false; 1495 /** 1496 * @var bool Whether to show the nav item 1497 */ 1498 var $enable_nav_item; 1420 1499 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; 1423 1516 1424 // Methods you should override 1517 /** 1518 * @var bool Has the extension been initialized? 1519 * @since BuddyPress (1.8) 1520 */ 1521 protected $initialized = false; 1425 1522 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(); 1427 1528 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(); 1429 1535 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(); 1431 1541 1432 function edit_screen_save( $group_id = null ) {}1542 /** Screen Overrides **************************************************/ 1433 1543 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 */ 1435 1549 1436 function create_screen_save( $group_id = null ) {} 1550 // The content of the group tab 1551 public function display() {} 1437 1552 1438 // Private Methods 1553 // Content displayed in a widget sidebar, if applicable 1554 public function widget_display() {} 1439 1555 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 ) {} 1442 1569 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(); 1447 1639 } 1448 1640 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 ); 1451 1677 } 1452 1678 1453 if ( ! $this->create_name) {1454 $this->c reate_name = $this->name;1679 if ( is_null( $this->class_reflection ) ) { 1680 $this->class_reflection = new ReflectionClass( $this->class_name ); 1455 1681 } 1682 } 1456 1683 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']; 1459 1701 } 1460 1702 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 } 1468 1708 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 } 1499 1715 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; 1504 1764 } 1765 1766 $this->screens[ $context ] = $screen; 1505 1767 } 1768 } 1506 1769 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 *************************************************************/ 1515 1771 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 } 1519 1804 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 } 1521 1810 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' ) ); 1529 1912 } 1530 1913 } 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 *************************************************************/ 1531 1995 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() ) { 1536 2003 // 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' ) ); 1538 2005 1539 2006 // Initialize the metabox 1540 2007 add_action( 'bp_groups_admin_meta_boxes', array( $this, '_meta_box_display_callback' ) ); 1541 2008 1542 2009 // 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 ); 1546 2011 } 1547 2012 } 1548 2013 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 ); 1552 2032 } 1553 2033 1554 2034 /** … … class BP_Group_Extension { 1556 2036 * 1557 2037 * @since BuddyPress (1.7) 1558 2038 */ 1559 function _meta_box_display_callback() {2039 public function _meta_box_display_callback() { 1560 2040 $group_id = isset( $_GET['gid'] ) ? (int) $_GET['gid'] : 0; 1561 2041 2042 $screen = $this->screens['admin']; 2043 1562 2044 add_meta_box( 1563 $ this->slug,1564 $ this->name,2045 $screen['slug'], 2046 $screen['name'], 1565 2047 create_function( '', 'do_action( "bp_groups_admin_meta_box_content_' . $this->slug . '", ' . $group_id . ' );' ), 1566 2048 get_current_screen()->id, 1567 $ this->admin_metabox_context,1568 $ this->admin_metabox_priority2049 $screen['metabox_context'], 2050 $screen['metabox_priority'] 1569 2051 ); 1570 2052 } 1571 2053 2054 2055 /** Utilities *********************************************************/ 2056 1572 2057 /** 1573 * Call the edit_screen() method2058 * Generate the nonce fields for a settings form 1574 2059 * 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. 1577 2065 * 1578 * @since 1.8 2066 * @since BuddyPress (1.8) 2067 * @uses wp_nonce_field() 2068 * @param string $context 'create', 'edit', 'admin' 1579 2069 */ 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 ); 1582 2072 } 1583 2073 1584 2074 /** 1585 * C all the create_screen() method, if we're on the right page2075 * Check the nonce on a submitted settings form 1586 2076 * 1587 * @since 1.8 2077 * @since BuddyPress (1.8) 2078 * @uses check_admin_referer() 2079 * @param string $context 'create', 'edit', 'admin' 1588 2080 */ 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'] ); 1592 2101 } 2102 2103 return $enabled; 1593 2104 } 1594 2105 1595 2106 /** 1596 * Call the create_screen_save() method, if we're on the right page2107 * Get the appropriate screen callback for the specified context/type 1597 2108 * 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 1599 2142 */ 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; 1603 2306 } 1604 2307 } 1605 2308 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 } 1606 2495 } 1607 2496 1608 2497 function 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 8 class 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 46 class 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 69 class 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 95 class 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 109 class 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 127 class 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
2 2 3 3 define( 'BP_PLUGIN_DIR', dirname( dirname( __FILE__ ) ) . '/' ); 4 4 5 if ( ! defined( 'BP_TESTS_DIR' ) ) { 6 define( 'BP_TESTS_DIR', dirname( __FILE__ ) . '/' ); 7 } 8 5 9 require_once getenv( 'WP_TESTS_DIR' ) . '/includes/functions.php'; 6 10 7 11 function _install_and_load_buddypress() { 8 require dirname( __FILE__ ). '/includes/loader.php';12 require BP_TESTS_DIR . '/includes/loader.php'; 9 13 } 10 14 tests_add_filter( 'muplugins_loaded', '_install_and_load_buddypress' ); 11 15 12 16 require getenv( 'WP_TESTS_DIR' ) . '/includes/bootstrap.php'; 13 17 14 18 // Load the BP-specific testing tools 15 require dirname( __FILE__ ). '/includes/testcase.php';19 require 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 3 include_once BP_TESTS_DIR . '/assets/group-extensions.php'; 4 5 /** 6 * @group groups 7 */ 8 class 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 }