WordPress.org

Make WordPress Core

Ticket #19416: 19416.diff

File 19416.diff, 10.0 KB (added by koopersmith, 6 years ago)
  • wp-includes/class-wp-admin-bar.php

     
    11<?php
    22class WP_Admin_Bar {
    33        private $nodes = array();
     4        private $bound = false;
    45        public $user;
    56
    67        public function __get( $name ) {
     
    1819        public function initialize() {
    1920                $this->user = new stdClass;
    2021
    21                 $this->add_node( array(
    22                         'id'       => 'root',
    23                         'group'    => false,
    24                 ) );
    25 
    2622                if ( is_user_logged_in() ) {
    2723                        /* Populate settings we need for the menu based on the current user. */
    2824                        $this->user->blogs = get_blogs_of_user( get_current_user_id() );
     
    104100                );
    105101
    106102                // If the node already exists, keep any data that isn't provided.
    107                 if ( $this->get_node( $args['id'] ) )
    108                         $defaults = get_object_vars( $this->get_node( $args['id'] ) );
     103                if ( $maybe_defaults = $this->get_node( $args['id'] ) )
     104                        $defaults = get_object_vars( $maybe_defaults );
    109105
    110106                // Do the same for 'meta' items.
    111107                if ( ! empty( $defaults['meta'] ) && empty( $args['meta'] ) )
     
    126122         * @return object Node.
    127123         */
    128124        final public function get_node( $id ) {
     125                if ( $node = $this->_get_node( $id ) )
     126                        return clone $node;
     127        }
     128
     129        final protected function _get_node( $id ) {
     130                if ( $this->bound )
     131                        return;
     132
     133                if ( empty( $id ) )
     134                        $id = 'root';
     135
    129136                if ( isset( $this->nodes[ $id ] ) )
    130137                        return $this->nodes[ $id ];
    131138        }
    132139
     140        final public function get_nodes() {
     141           if ( ! $nodes = $this->_get_nodes() )
     142              return;
     143
     144           foreach ( $nodes as &$node ) {
     145               $node = clone $node;
     146           }
     147           return $nodes;
     148        }
     149
    133150        final protected function _get_nodes() {
     151                if ( $this->bound )
     152                        return;
     153
    134154                return $this->nodes;
    135155        }
    136156
     
    164184        }
    165185
    166186        public function render() {
    167                 $this->_bind();
    168                 $this->_render();
     187                $root = $this->_bind();
     188                $this->_render( $root );
    169189        }
    170190
    171191        final protected function _bind() {
     192                if ( $this->bound )
     193                        return;
     194
     195                // Add the root node.
     196                // Clear it first, just in case. Don't mess with The Root.
     197                $this->remove_node( 'root' );
     198                $this->add_node( array(
     199                        'id'    => 'root',
     200                        'group' => false,
     201                ) );
     202
     203                // Normalize nodes: define internal 'children' and 'type' properties.
    172204                foreach ( $this->_get_nodes() as $node ) {
     205                        $node->children = array();
     206                        $node->type = ( $node->group ) ? 'group' : 'item';
     207                        unset( $node->group );
     208
     209                        // The Root wants your orphans. No lonely items allowed.
     210                        if ( ! $node->parent )
     211                                $node->parent = 'root';
     212                }
     213
     214                foreach ( $this->_get_nodes() as $node ) {
    173215                        if ( 'root' == $node->id )
    174216                                continue;
    175217
    176                         // Handle root menu items
    177                         if ( empty( $node->parent ) ) {
    178                                 $parent = $this->get_node( 'root' );
    179                         } elseif ( ! $parent = $this->get_node( $node->parent ) ) {
    180                                 // If the parent node isn't registered, ignore the node.
     218                        // Fetch the parent node. If it isn't registered, ignore the node.
     219                        if ( ! $parent = $this->_get_node( $node->parent ) ) {
    181220                                continue;
    182221                        }
    183222
    184                         // Ensure that our tree is of the form "item -> group -> item -> group -> ..."
    185                         if ( ! $parent->group && ! $node->group ) { // Both are items.
     223                        // Generate the group class (we distinguish between top level and other level groups).
     224                        $group_class = ( $node->parent == 'root' ) ? 'ab-top-menu' : 'ab-submenu';
     225
     226                        if ( $node->type == 'group' ) {
     227                                if ( empty( $node->meta['class'] ) )
     228                                        $node->meta['class'] = '';
     229                                $node->meta['class'] .= ' ' . $group_class;
     230                        }
     231
     232                        // Items in items aren't allowed. Wrap nested items in 'default' groups.
     233                        if ( $parent->type == 'item' && $node->type == 'item' ) {
     234                                $default_id = $parent->id . '-default';
     235                                $default    = $this->_get_node( $default_id );
     236
    186237                                // The default group is added here to allow groups that are
    187238                                // added before standard menu items to render first.
    188                                 if ( ! isset( $parent->children['default'] ) ) {
    189                                         $parent->children['default'] = (object) array(
    190                                                 'id'       => "{$parent->id}-default",
    191                                                 'parent'   => $parent->id,
    192                                                 'group'    => true,
    193                                                 'children' => array(),
    194                                         );
     239                                if ( ! $default ) {
     240                                        // Use _set_node because add_node can be overloaded.
     241                                        // Make sure to specify default settings for all properties.
     242                                        $this->_set_node( array(
     243                                                'id'        => $default_id,
     244                                                'parent'    => $parent->id,
     245                                                'type'      => 'group',
     246                                                'children'  => array(),
     247                                                'meta'      => array(
     248                                                        'class'     => $group_class,
     249                                                ),
     250                                                'title'     => false,
     251                                                'href'      => false,
     252                                        ) );
     253                                        $default = $this->_get_node( $default_id );
     254                                        $parent->children[] = $default;
    195255                                }
    196                                 $parent = $parent->children['default'];
     256                                $parent = $default;
     257
     258                        // Groups in groups aren't allowed. Add a special 'container' node.
     259                        // The container will invisibly wrap both groups.
     260                        } elseif ( $parent->type == 'group' && $node->type == 'group' ) {
     261                                $container_id = $parent->id . '-container';
     262                                $container    = $this->_get_node( $container_id );
     263
     264                                // We need to create a container for this group, life is sad.
     265                                if ( ! $container ) {
     266                                        // Use _set_node because add_node can be overloaded.
     267                                        // Make sure to specify default settings for all properties.
     268                                        $this->_set_node( array(
     269                                                'id'       => $container_id,
     270                                                'type'     => 'container',
     271                                                'children' => array( $parent ),
     272                                                'parent'   => false,
     273                                                'title'    => false,
     274                                                'href'     => false,
     275                                                'meta'     => array(),
     276                                        ) );
     277
     278                                        $container = $this->_get_node( $container_id );
     279
     280                                        // Link the container node if a grandparent node exists.
     281                                        $grandparent = $this->_get_node( $parent->parent );
     282
     283                                        if ( $grandparent ) {
     284                                                $container->parent = $grandparent->id;
     285
     286                                                $index = array_search( $parent, $grandparent->children, true );
     287                                                if ( $index === false )
     288                                                        $grandparent->children[] = $container;
     289                                                else
     290                                                        array_splice( $grandparent->children, $index, 1, array( $container ) );
     291                                        }
     292
     293                                        $parent->parent = $container->id;
     294                                }
     295
     296                                $parent = $container;
    197297                        }
    198298
    199299                        // Update the parent ID (it might have changed).
    200300                        $node->parent = $parent->id;
    201301
    202                         if ( ! isset( $parent->children ) )
    203                                 $parent->children = array();
    204 
    205302                        // Add the node to the tree.
    206303                        $parent->children[] = $node;
    207304                }
     305
     306                $root = $this->_get_node( 'root' );
     307                $this->bound = true;
     308                return $root;
    208309        }
    209310
    210         final protected function _render() {
     311        final protected function _render( $root ) {
    211312                global $is_IE, $is_iphone;
    212313
    213314                // Add browser classes.
     
    227328                ?>
    228329                <div id="wpadminbar" class="<?php echo $class; ?>" role="navigation">
    229330                        <div class="quicklinks" role="menubar">
    230                                 <?php foreach ( $this->get_node( 'root' )->children as $group ) {
    231                                         $this->_render_group( $group, 'ab-top-menu' );
     331                                <?php foreach ( $root->children as $group ) {
     332                                        $this->_render_group( $group );
    232333                                } ?>
    233334                        </div>
    234335                </div>
     
    236337                <?php
    237338        }
    238339
    239         final protected function _render_group( $node, $class = '' ) {
    240                 if ( ! $node->group )
     340        final protected function _render_container( $node ) {
     341                if ( $node->type != 'container' || empty( $node->children ) )
    241342                        return;
    242343
    243                 // Check for groups within groups.
    244                 $groups = array();
    245                 foreach ( $node->children as $child ) {
    246                         if ( $child->group ) {
    247                                 $groups[] = $child;
    248                         } else {
    249                                 if ( ! isset( $default ) ) {
    250                                         // Create a default proxy item to be used in the case of nested groups.
    251                                         $default  = (object) wp_parse_args( array( 'children' => array() ), (array) $node );
    252                                         $groups[] = $default;
    253                                 }
    254                                 $default->children[] = $child;
     344                ?><div id="<?php echo esc_attr( 'wp-admin-bar-' . $node->id ); ?>" class="ab-group-container" role="menu"><?php
     345                        foreach ( $node->children as $group ) {
     346                                $this->_render_group( $group );
    255347                        }
    256                 }
     348                ?></div><?php
     349        }
    257350
    258                 $is_single_group = count( $groups ) === 1;
     351        final protected function _render_group( $node ) {
     352                if ( $node->type == 'container' )
     353                        return $this->_render_container( $node );
    259354
    260                 // If we don't have any subgroups, render the group.
    261                 if ( $is_single_group && ! empty( $node->children ) ) :
     355                if ( $node->type != 'group' || empty( $node->children ) )
     356                        return;
    262357
    263                         if ( ! empty( $node->meta['class'] ) )
    264                                 $class .= ' ' . $node->meta['class'];
     358                $class = empty( $node->meta['class'] ) ? '' : $node->meta['class'];
    265359
    266                         ?><ul id="<?php echo esc_attr( "wp-admin-bar-{$node->id}" ); ?>" class="<?php echo esc_attr( $class ); ?>" role="menu"><?php
    267                                 foreach ( $node->children as $item ) {
    268                                         $this->_render_item( $item );
    269                                 }
    270                         ?></ul><?php
    271 
    272                 // Wrap the subgroups in a div and render each individual subgroup.
    273                 elseif ( ! $is_single_group ) :
    274                         ?><div id="<?php echo esc_attr( "wp-admin-bar-{$node->id}-container" ); ?>" class="ab-group-container" role="menu"><?php
    275                                 foreach ( $groups as $group ) {
    276                                         $this->_render_group( $group, $class );
    277                                 }
    278                         ?></div><?php
    279                 endif;
     360                ?><ul id="<?php echo esc_attr( 'wp-admin-bar-' . $node->id ); ?>" class="<?php echo esc_attr( $class ); ?>" role="menu"><?php
     361                        foreach ( $node->children as $item ) {
     362                                $this->_render_item( $item );
     363                        }
     364                ?></ul><?php
    280365        }
    281366
    282367        final protected function _render_item( $node ) {
    283                 if ( $node->group )
     368                if ( $node->type != 'item' )
    284369                        return;
    285370
    286371                $is_parent = ! empty( $node->children );
     
    301386
    302387                ?>
    303388
    304                 <li id="<?php echo esc_attr( "wp-admin-bar-{$node->id}" ); ?>" class="<?php echo esc_attr( $menuclass ); ?>"><?php
     389                <li id="<?php echo esc_attr( 'wp-admin-bar-' . $node->id ); ?>" class="<?php echo esc_attr( $menuclass ); ?>"><?php
    305390                        if ( $has_link ):
    306391                                ?><a class="ab-item" <?php echo $aria_attributes; ?> href="<?php echo esc_url( $node->href ) ?>"<?php
    307392                                        if ( ! empty( $node->meta['onclick'] ) ) :
     
    333418                        if ( $is_parent ) :
    334419                                ?><div class="ab-sub-wrapper"><?php
    335420                                        foreach ( $node->children as $group ) {
    336                                                 $this->_render_group( $group, 'ab-submenu' );
     421                                                $this->_render_group( $group );
    337422                                        }
    338423                                ?></div><?php
    339424                        endif;