wp/wp-includes/class-wp-admin-bar.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
    21 	 * @param string $name
    21 	 * @param string $name
    22 	 * @return string|array|void
    22 	 * @return string|array|void
    23 	 */
    23 	 */
    24 	public function __get( $name ) {
    24 	public function __get( $name ) {
    25 		switch ( $name ) {
    25 		switch ( $name ) {
    26 			case 'proto' :
    26 			case 'proto':
    27 				return is_ssl() ? 'https://' : 'http://';
    27 				return is_ssl() ? 'https://' : 'http://';
    28 
    28 
    29 			case 'menu' :
    29 			case 'menu':
    30 				_deprecated_argument( 'WP_Admin_Bar', '3.3.0', 'Modify admin bar nodes with WP_Admin_Bar::get_node(), WP_Admin_Bar::add_node(), and WP_Admin_Bar::remove_node(), not the <code>menu</code> property.' );
    30 				_deprecated_argument( 'WP_Admin_Bar', '3.3.0', 'Modify admin bar nodes with WP_Admin_Bar::get_node(), WP_Admin_Bar::add_node(), and WP_Admin_Bar::remove_node(), not the <code>menu</code> property.' );
    31 				return array(); // Sorry, folks.
    31 				return array(); // Sorry, folks.
    32 		}
    32 		}
    33 	}
    33 	}
    34 
    34 
    39 
    39 
    40 		if ( is_user_logged_in() ) {
    40 		if ( is_user_logged_in() ) {
    41 			/* Populate settings we need for the menu based on the current user. */
    41 			/* Populate settings we need for the menu based on the current user. */
    42 			$this->user->blogs = get_blogs_of_user( get_current_user_id() );
    42 			$this->user->blogs = get_blogs_of_user( get_current_user_id() );
    43 			if ( is_multisite() ) {
    43 			if ( is_multisite() ) {
    44 				$this->user->active_blog = get_active_blog_for_user( get_current_user_id() );
    44 				$this->user->active_blog    = get_active_blog_for_user( get_current_user_id() );
    45 				$this->user->domain = empty( $this->user->active_blog ) ? user_admin_url() : trailingslashit( get_home_url( $this->user->active_blog->blog_id ) );
    45 				$this->user->domain         = empty( $this->user->active_blog ) ? user_admin_url() : trailingslashit( get_home_url( $this->user->active_blog->blog_id ) );
    46 				$this->user->account_domain = $this->user->domain;
    46 				$this->user->account_domain = $this->user->domain;
    47 			} else {
    47 			} else {
    48 				$this->user->active_blog = $this->user->blogs[get_current_blog_id()];
    48 				$this->user->active_blog    = $this->user->blogs[ get_current_blog_id() ];
    49 				$this->user->domain = trailingslashit( home_url() );
    49 				$this->user->domain         = trailingslashit( home_url() );
    50 				$this->user->account_domain = $this->user->domain;
    50 				$this->user->account_domain = $this->user->domain;
    51 			}
    51 			}
    52 		}
    52 		}
    53 
    53 
    54 		add_action( 'wp_head', 'wp_admin_bar_header' );
    54 		add_action( 'wp_head', 'wp_admin_bar_header' );
    58 		if ( current_theme_supports( 'admin-bar' ) ) {
    58 		if ( current_theme_supports( 'admin-bar' ) ) {
    59 			/**
    59 			/**
    60 			 * To remove the default padding styles from WordPress for the Toolbar, use the following code:
    60 			 * To remove the default padding styles from WordPress for the Toolbar, use the following code:
    61 			 * add_theme_support( 'admin-bar', array( 'callback' => '__return_false' ) );
    61 			 * add_theme_support( 'admin-bar', array( 'callback' => '__return_false' ) );
    62 			 */
    62 			 */
    63 			$admin_bar_args = get_theme_support( 'admin-bar' );
    63 			$admin_bar_args  = get_theme_support( 'admin-bar' );
    64 			$header_callback = $admin_bar_args[0]['callback'];
    64 			$header_callback = $admin_bar_args[0]['callback'];
    65 		}
    65 		}
    66 
    66 
    67 		if ( empty($header_callback) )
    67 		if ( empty( $header_callback ) ) {
    68 			$header_callback = '_admin_bar_bump_cb';
    68 			$header_callback = '_admin_bar_bump_cb';
    69 
    69 		}
    70 		add_action('wp_head', $header_callback);
    70 
       
    71 		add_action( 'wp_head', $header_callback );
    71 
    72 
    72 		wp_enqueue_script( 'admin-bar' );
    73 		wp_enqueue_script( 'admin-bar' );
    73 		wp_enqueue_style( 'admin-bar' );
    74 		wp_enqueue_style( 'admin-bar' );
    74 
    75 
    75 		/**
    76 		/**
   112 	 *                          'onclick', 'target', 'title', 'tabindex'. Default empty.
   113 	 *                          'onclick', 'target', 'title', 'tabindex'. Default empty.
   113 	 * }
   114 	 * }
   114 	 */
   115 	 */
   115 	public function add_node( $args ) {
   116 	public function add_node( $args ) {
   116 		// Shim for old method signature: add_node( $parent_id, $menu_obj, $args )
   117 		// Shim for old method signature: add_node( $parent_id, $menu_obj, $args )
   117 		if ( func_num_args() >= 3 && is_string( func_get_arg(0) ) )
   118 		if ( func_num_args() >= 3 && is_string( func_get_arg( 0 ) ) ) {
   118 			$args = array_merge( array( 'parent' => func_get_arg(0) ), func_get_arg(2) );
   119 			$args = array_merge( array( 'parent' => func_get_arg( 0 ) ), func_get_arg( 2 ) );
   119 
   120 		}
   120 		if ( is_object( $args ) )
   121 
       
   122 		if ( is_object( $args ) ) {
   121 			$args = get_object_vars( $args );
   123 			$args = get_object_vars( $args );
       
   124 		}
   122 
   125 
   123 		// Ensure we have a valid title.
   126 		// Ensure we have a valid title.
   124 		if ( empty( $args['id'] ) ) {
   127 		if ( empty( $args['id'] ) ) {
   125 			if ( empty( $args['title'] ) )
   128 			if ( empty( $args['title'] ) ) {
   126 				return;
   129 				return;
       
   130 			}
   127 
   131 
   128 			_doing_it_wrong( __METHOD__, __( 'The menu ID should not be empty.' ), '3.3.0' );
   132 			_doing_it_wrong( __METHOD__, __( 'The menu ID should not be empty.' ), '3.3.0' );
   129 			// Deprecated: Generate an ID from the title.
   133 			// Deprecated: Generate an ID from the title.
   130 			$args['id'] = esc_attr( sanitize_title( trim( $args['title'] ) ) );
   134 			$args['id'] = esc_attr( sanitize_title( trim( $args['title'] ) ) );
   131 		}
   135 		}
   138 			'group'  => false,
   142 			'group'  => false,
   139 			'meta'   => array(),
   143 			'meta'   => array(),
   140 		);
   144 		);
   141 
   145 
   142 		// If the node already exists, keep any data that isn't provided.
   146 		// If the node already exists, keep any data that isn't provided.
   143 		if ( $maybe_defaults = $this->get_node( $args['id'] ) )
   147 		if ( $maybe_defaults = $this->get_node( $args['id'] ) ) {
   144 			$defaults = get_object_vars( $maybe_defaults );
   148 			$defaults = get_object_vars( $maybe_defaults );
       
   149 		}
   145 
   150 
   146 		// Do the same for 'meta' items.
   151 		// Do the same for 'meta' items.
   147 		if ( ! empty( $defaults['meta'] ) && ! empty( $args['meta'] ) )
   152 		if ( ! empty( $defaults['meta'] ) && ! empty( $args['meta'] ) ) {
   148 			$args['meta'] = wp_parse_args( $args['meta'], $defaults['meta'] );
   153 			$args['meta'] = wp_parse_args( $args['meta'], $defaults['meta'] );
       
   154 		}
   149 
   155 
   150 		$args = wp_parse_args( $args, $defaults );
   156 		$args = wp_parse_args( $args, $defaults );
   151 
   157 
   152 		$back_compat_parents = array(
   158 		$back_compat_parents = array(
   153 			'my-account-with-avatar' => array( 'my-account', '3.3' ),
   159 			'my-account-with-avatar' => array( 'my-account', '3.3' ),
   154 			'my-blogs'               => array( 'my-sites',   '3.3' ),
   160 			'my-blogs'               => array( 'my-sites', '3.3' ),
   155 		);
   161 		);
   156 
   162 
   157 		if ( isset( $back_compat_parents[ $args['parent'] ] ) ) {
   163 		if ( isset( $back_compat_parents[ $args['parent'] ] ) ) {
   158 			list( $new_parent, $version ) = $back_compat_parents[ $args['parent'] ];
   164 			list( $new_parent, $version ) = $back_compat_parents[ $args['parent'] ];
   159 			_deprecated_argument( __METHOD__, $version, sprintf( 'Use <code>%s</code> as the parent for the <code>%s</code> admin bar node instead of <code>%s</code>.', $new_parent, $args['id'], $args['parent'] ) );
   165 			_deprecated_argument( __METHOD__, $version, sprintf( 'Use <code>%s</code> as the parent for the <code>%s</code> admin bar node instead of <code>%s</code>.', $new_parent, $args['id'], $args['parent'] ) );
   175 	 *
   181 	 *
   176 	 * @param string $id
   182 	 * @param string $id
   177 	 * @return object Node.
   183 	 * @return object Node.
   178 	 */
   184 	 */
   179 	final public function get_node( $id ) {
   185 	final public function get_node( $id ) {
   180 		if ( $node = $this->_get_node( $id ) )
   186 		if ( $node = $this->_get_node( $id ) ) {
   181 			return clone $node;
   187 			return clone $node;
       
   188 		}
   182 	}
   189 	}
   183 
   190 
   184 	/**
   191 	/**
   185 	 * @param string $id
   192 	 * @param string $id
   186 	 * @return object|void
   193 	 * @return object|void
   187 	 */
   194 	 */
   188 	final protected function _get_node( $id ) {
   195 	final protected function _get_node( $id ) {
   189 		if ( $this->bound )
   196 		if ( $this->bound ) {
   190 			return;
   197 			return;
   191 
   198 		}
   192 		if ( empty( $id ) )
   199 
       
   200 		if ( empty( $id ) ) {
   193 			$id = 'root';
   201 			$id = 'root';
   194 
   202 		}
   195 		if ( isset( $this->nodes[ $id ] ) )
   203 
       
   204 		if ( isset( $this->nodes[ $id ] ) ) {
   196 			return $this->nodes[ $id ];
   205 			return $this->nodes[ $id ];
       
   206 		}
   197 	}
   207 	}
   198 
   208 
   199 	/**
   209 	/**
   200 	 * @return array|void
   210 	 * @return array|void
   201 	 */
   211 	 */
   202 	final public function get_nodes() {
   212 	final public function get_nodes() {
   203 		if ( ! $nodes = $this->_get_nodes() )
   213 		if ( ! $nodes = $this->_get_nodes() ) {
   204 			return;
   214 			return;
       
   215 		}
   205 
   216 
   206 		foreach ( $nodes as &$node ) {
   217 		foreach ( $nodes as &$node ) {
   207 			$node = clone $node;
   218 			$node = clone $node;
   208 		}
   219 		}
   209 		return $nodes;
   220 		return $nodes;
   211 
   222 
   212 	/**
   223 	/**
   213 	 * @return array|void
   224 	 * @return array|void
   214 	 */
   225 	 */
   215 	final protected function _get_nodes() {
   226 	final protected function _get_nodes() {
   216 		if ( $this->bound )
   227 		if ( $this->bound ) {
   217 			return;
   228 			return;
       
   229 		}
   218 
   230 
   219 		return $this->nodes;
   231 		return $this->nodes;
   220 	}
   232 	}
   221 
   233 
   222 	/**
   234 	/**
   257 
   269 
   258 	/**
   270 	/**
   259 	 */
   271 	 */
   260 	public function render() {
   272 	public function render() {
   261 		$root = $this->_bind();
   273 		$root = $this->_bind();
   262 		if ( $root )
   274 		if ( $root ) {
   263 			$this->_render( $root );
   275 			$this->_render( $root );
       
   276 		}
   264 	}
   277 	}
   265 
   278 
   266 	/**
   279 	/**
   267 	 * @return object|void
   280 	 * @return object|void
   268 	 */
   281 	 */
   269 	final protected function _bind() {
   282 	final protected function _bind() {
   270 		if ( $this->bound )
   283 		if ( $this->bound ) {
   271 			return;
   284 			return;
       
   285 		}
   272 
   286 
   273 		// Add the root node.
   287 		// Add the root node.
   274 		// Clear it first, just in case. Don't mess with The Root.
   288 		// Clear it first, just in case. Don't mess with The Root.
   275 		$this->remove_node( 'root' );
   289 		$this->remove_node( 'root' );
   276 		$this->add_node( array(
   290 		$this->add_node(
   277 			'id'    => 'root',
   291 			array(
   278 			'group' => false,
   292 				'id'    => 'root',
   279 		) );
   293 				'group' => false,
       
   294 			)
       
   295 		);
   280 
   296 
   281 		// Normalize nodes: define internal 'children' and 'type' properties.
   297 		// Normalize nodes: define internal 'children' and 'type' properties.
   282 		foreach ( $this->_get_nodes() as $node ) {
   298 		foreach ( $this->_get_nodes() as $node ) {
   283 			$node->children = array();
   299 			$node->children = array();
   284 			$node->type = ( $node->group ) ? 'group' : 'item';
   300 			$node->type     = ( $node->group ) ? 'group' : 'item';
   285 			unset( $node->group );
   301 			unset( $node->group );
   286 
   302 
   287 			// The Root wants your orphans. No lonely items allowed.
   303 			// The Root wants your orphans. No lonely items allowed.
   288 			if ( ! $node->parent )
   304 			if ( ! $node->parent ) {
   289 				$node->parent = 'root';
   305 				$node->parent = 'root';
       
   306 			}
   290 		}
   307 		}
   291 
   308 
   292 		foreach ( $this->_get_nodes() as $node ) {
   309 		foreach ( $this->_get_nodes() as $node ) {
   293 			if ( 'root' == $node->id )
   310 			if ( 'root' == $node->id ) {
   294 				continue;
   311 				continue;
       
   312 			}
   295 
   313 
   296 			// Fetch the parent node. If it isn't registered, ignore the node.
   314 			// Fetch the parent node. If it isn't registered, ignore the node.
   297 			if ( ! $parent = $this->_get_node( $node->parent ) ) {
   315 			if ( ! $parent = $this->_get_node( $node->parent ) ) {
   298 				continue;
   316 				continue;
   299 			}
   317 			}
   300 
   318 
   301 			// Generate the group class (we distinguish between top level and other level groups).
   319 			// Generate the group class (we distinguish between top level and other level groups).
   302 			$group_class = ( $node->parent == 'root' ) ? 'ab-top-menu' : 'ab-submenu';
   320 			$group_class = ( $node->parent == 'root' ) ? 'ab-top-menu' : 'ab-submenu';
   303 
   321 
   304 			if ( $node->type == 'group' ) {
   322 			if ( $node->type == 'group' ) {
   305 				if ( empty( $node->meta['class'] ) )
   323 				if ( empty( $node->meta['class'] ) ) {
   306 					$node->meta['class'] = $group_class;
   324 					$node->meta['class'] = $group_class;
   307 				else
   325 				} else {
   308 					$node->meta['class'] .= ' ' . $group_class;
   326 					$node->meta['class'] .= ' ' . $group_class;
       
   327 				}
   309 			}
   328 			}
   310 
   329 
   311 			// Items in items aren't allowed. Wrap nested items in 'default' groups.
   330 			// Items in items aren't allowed. Wrap nested items in 'default' groups.
   312 			if ( $parent->type == 'item' && $node->type == 'item' ) {
   331 			if ( $parent->type == 'item' && $node->type == 'item' ) {
   313 				$default_id = $parent->id . '-default';
   332 				$default_id = $parent->id . '-default';
   316 				// The default group is added here to allow groups that are
   335 				// The default group is added here to allow groups that are
   317 				// added before standard menu items to render first.
   336 				// added before standard menu items to render first.
   318 				if ( ! $default ) {
   337 				if ( ! $default ) {
   319 					// Use _set_node because add_node can be overloaded.
   338 					// Use _set_node because add_node can be overloaded.
   320 					// Make sure to specify default settings for all properties.
   339 					// Make sure to specify default settings for all properties.
   321 					$this->_set_node( array(
   340 					$this->_set_node(
   322 						'id'        => $default_id,
   341 						array(
   323 						'parent'    => $parent->id,
   342 							'id'       => $default_id,
   324 						'type'      => 'group',
   343 							'parent'   => $parent->id,
   325 						'children'  => array(),
   344 							'type'     => 'group',
   326 						'meta'      => array(
   345 							'children' => array(),
   327 							'class'     => $group_class,
   346 							'meta'     => array(
   328 						),
   347 								'class' => $group_class,
   329 						'title'     => false,
   348 							),
   330 						'href'      => false,
   349 							'title'    => false,
   331 					) );
   350 							'href'     => false,
   332 					$default = $this->_get_node( $default_id );
   351 						)
       
   352 					);
       
   353 					$default            = $this->_get_node( $default_id );
   333 					$parent->children[] = $default;
   354 					$parent->children[] = $default;
   334 				}
   355 				}
   335 				$parent = $default;
   356 				$parent = $default;
   336 
   357 
   337 			// Groups in groups aren't allowed. Add a special 'container' node.
   358 				// Groups in groups aren't allowed. Add a special 'container' node.
   338 			// The container will invisibly wrap both groups.
   359 				// The container will invisibly wrap both groups.
   339 			} elseif ( $parent->type == 'group' && $node->type == 'group' ) {
   360 			} elseif ( $parent->type == 'group' && $node->type == 'group' ) {
   340 				$container_id = $parent->id . '-container';
   361 				$container_id = $parent->id . '-container';
   341 				$container    = $this->_get_node( $container_id );
   362 				$container    = $this->_get_node( $container_id );
   342 
   363 
   343 				// We need to create a container for this group, life is sad.
   364 				// We need to create a container for this group, life is sad.
   344 				if ( ! $container ) {
   365 				if ( ! $container ) {
   345 					// Use _set_node because add_node can be overloaded.
   366 					// Use _set_node because add_node can be overloaded.
   346 					// Make sure to specify default settings for all properties.
   367 					// Make sure to specify default settings for all properties.
   347 					$this->_set_node( array(
   368 					$this->_set_node(
   348 						'id'       => $container_id,
   369 						array(
   349 						'type'     => 'container',
   370 							'id'       => $container_id,
   350 						'children' => array( $parent ),
   371 							'type'     => 'container',
   351 						'parent'   => false,
   372 							'children' => array( $parent ),
   352 						'title'    => false,
   373 							'parent'   => false,
   353 						'href'     => false,
   374 							'title'    => false,
   354 						'meta'     => array(),
   375 							'href'     => false,
   355 					) );
   376 							'meta'     => array(),
       
   377 						)
       
   378 					);
   356 
   379 
   357 					$container = $this->_get_node( $container_id );
   380 					$container = $this->_get_node( $container_id );
   358 
   381 
   359 					// Link the container node if a grandparent node exists.
   382 					// Link the container node if a grandparent node exists.
   360 					$grandparent = $this->_get_node( $parent->parent );
   383 					$grandparent = $this->_get_node( $parent->parent );
   361 
   384 
   362 					if ( $grandparent ) {
   385 					if ( $grandparent ) {
   363 						$container->parent = $grandparent->id;
   386 						$container->parent = $grandparent->id;
   364 
   387 
   365 						$index = array_search( $parent, $grandparent->children, true );
   388 						$index = array_search( $parent, $grandparent->children, true );
   366 						if ( $index === false )
   389 						if ( $index === false ) {
   367 							$grandparent->children[] = $container;
   390 							$grandparent->children[] = $container;
   368 						else
   391 						} else {
   369 							array_splice( $grandparent->children, $index, 1, array( $container ) );
   392 							array_splice( $grandparent->children, $index, 1, array( $container ) );
       
   393 						}
   370 					}
   394 					}
   371 
   395 
   372 					$parent->parent = $container->id;
   396 					$parent->parent = $container->id;
   373 				}
   397 				}
   374 
   398 
   380 
   404 
   381 			// Add the node to the tree.
   405 			// Add the node to the tree.
   382 			$parent->children[] = $node;
   406 			$parent->children[] = $node;
   383 		}
   407 		}
   384 
   408 
   385 		$root = $this->_get_node( 'root' );
   409 		$root        = $this->_get_node( 'root' );
   386 		$this->bound = true;
   410 		$this->bound = true;
   387 		return $root;
   411 		return $root;
   388 	}
   412 	}
   389 
   413 
   390 	/**
   414 	/**
   391 	 *
       
   392 	 * @global bool $is_IE
   415 	 * @global bool $is_IE
   393 	 * @param object $root
   416 	 * @param object $root
   394 	 */
   417 	 */
   395 	final protected function _render( $root ) {
   418 	final protected function _render( $root ) {
   396 		global $is_IE;
   419 		global $is_IE;
   397 
   420 
   398 		// Add browser classes.
   421 		// Add browser classes.
   399 		// We have to do this here since admin bar shows on the front end.
   422 		// We have to do this here since admin bar shows on the front end.
   400 		$class = 'nojq nojs';
   423 		$class = 'nojq nojs';
   401 		if ( $is_IE ) {
   424 		if ( $is_IE ) {
   402 			if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 7' ) )
   425 			if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 7' ) ) {
   403 				$class .= ' ie7';
   426 				$class .= ' ie7';
   404 			elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 8' ) )
   427 			} elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 8' ) ) {
   405 				$class .= ' ie8';
   428 				$class .= ' ie8';
   406 			elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 9' ) )
   429 			} elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 9' ) ) {
   407 				$class .= ' ie9';
   430 				$class .= ' ie9';
       
   431 			}
   408 		} elseif ( wp_is_mobile() ) {
   432 		} elseif ( wp_is_mobile() ) {
   409 			$class .= ' mobile';
   433 			$class .= ' mobile';
   410 		}
   434 		}
   411 
   435 
   412 		?>
   436 		?>
   413 		<div id="wpadminbar" class="<?php echo $class; ?>">
   437 		<div id="wpadminbar" class="<?php echo $class; ?>">
   414 			<?php if ( ! is_admin() ) { ?>
   438 			<?php if ( ! is_admin() ) { ?>
   415 				<a class="screen-reader-shortcut" href="#wp-toolbar" tabindex="1"><?php _e( 'Skip to toolbar' ); ?></a>
   439 				<a class="screen-reader-shortcut" href="#wp-toolbar" tabindex="1"><?php _e( 'Skip to toolbar' ); ?></a>
   416 			<?php } ?>
   440 			<?php } ?>
   417 			<div class="quicklinks" id="wp-toolbar" role="navigation" aria-label="<?php esc_attr_e( 'Toolbar' ); ?>" tabindex="0">
   441 			<div class="quicklinks" id="wp-toolbar" role="navigation" aria-label="<?php esc_attr_e( 'Toolbar' ); ?>">
   418 				<?php foreach ( $root->children as $group ) {
   442 				<?php
       
   443 				foreach ( $root->children as $group ) {
   419 					$this->_render_group( $group );
   444 					$this->_render_group( $group );
   420 				} ?>
   445 				}
       
   446 				?>
   421 			</div>
   447 			</div>
   422 			<?php if ( is_user_logged_in() ) : ?>
   448 			<?php if ( is_user_logged_in() ) : ?>
   423 			<a class="screen-reader-shortcut" href="<?php echo esc_url( wp_logout_url() ); ?>"><?php _e('Log Out'); ?></a>
   449 			<a class="screen-reader-shortcut" href="<?php echo esc_url( wp_logout_url() ); ?>"><?php _e( 'Log Out' ); ?></a>
   424 			<?php endif; ?>
   450 			<?php endif; ?>
   425 		</div>
   451 		</div>
   426 
   452 
   427 		<?php
   453 		<?php
   428 	}
   454 	}
   429 
   455 
   430 	/**
   456 	/**
   431 	 * @param object $node
   457 	 * @param object $node
   432 	 */
   458 	 */
   433 	final protected function _render_container( $node ) {
   459 	final protected function _render_container( $node ) {
   434 		if ( $node->type != 'container' || empty( $node->children ) )
   460 		if ( $node->type != 'container' || empty( $node->children ) ) {
   435 			return;
   461 			return;
   436 
   462 		}
   437 		?><div id="<?php echo esc_attr( 'wp-admin-bar-' . $node->id ); ?>" class="ab-group-container"><?php
   463 
   438 			foreach ( $node->children as $group ) {
   464 		echo '<div id="' . esc_attr( 'wp-admin-bar-' . $node->id ) . '" class="ab-group-container">';
   439 				$this->_render_group( $group );
   465 		foreach ( $node->children as $group ) {
   440 			}
   466 			$this->_render_group( $group );
   441 		?></div><?php
   467 		}
       
   468 		echo '</div>';
   442 	}
   469 	}
   443 
   470 
   444 	/**
   471 	/**
   445 	 * @param object $node
   472 	 * @param object $node
   446 	 */
   473 	 */
   447 	final protected function _render_group( $node ) {
   474 	final protected function _render_group( $node ) {
   448 		if ( $node->type == 'container' ) {
   475 		if ( $node->type == 'container' ) {
   449 			$this->_render_container( $node );
   476 			$this->_render_container( $node );
   450 			return;
   477 			return;
   451 		}
   478 		}
   452 		if ( $node->type != 'group' || empty( $node->children ) )
   479 		if ( $node->type != 'group' || empty( $node->children ) ) {
   453 			return;
   480 			return;
   454 
   481 		}
   455 		if ( ! empty( $node->meta['class'] ) )
   482 
       
   483 		if ( ! empty( $node->meta['class'] ) ) {
   456 			$class = ' class="' . esc_attr( trim( $node->meta['class'] ) ) . '"';
   484 			$class = ' class="' . esc_attr( trim( $node->meta['class'] ) ) . '"';
   457 		else
   485 		} else {
   458 			$class = '';
   486 			$class = '';
   459 
   487 		}
   460 		?><ul id="<?php echo esc_attr( 'wp-admin-bar-' . $node->id ); ?>"<?php echo $class; ?>><?php
   488 
   461 			foreach ( $node->children as $item ) {
   489 		echo "<ul id='" . esc_attr( 'wp-admin-bar-' . $node->id ) . "'$class>";
   462 				$this->_render_item( $item );
   490 		foreach ( $node->children as $item ) {
   463 			}
   491 			$this->_render_item( $item );
   464 		?></ul><?php
   492 		}
       
   493 		echo '</ul>';
   465 	}
   494 	}
   466 
   495 
   467 	/**
   496 	/**
   468 	 * @param object $node
   497 	 * @param object $node
   469 	 */
   498 	 */
   470 	final protected function _render_item( $node ) {
   499 	final protected function _render_item( $node ) {
   471 		if ( $node->type != 'item' )
   500 		if ( $node->type != 'item' ) {
   472 			return;
   501 			return;
   473 
   502 		}
   474 		$is_parent = ! empty( $node->children );
   503 
   475 		$has_link  = ! empty( $node->href );
   504 		$is_parent             = ! empty( $node->children );
       
   505 		$has_link              = ! empty( $node->href );
       
   506 		$is_root_top_item      = 'root-default' === $node->parent;
       
   507 		$is_top_secondary_item = 'top-secondary' === $node->parent;
   476 
   508 
   477 		// Allow only numeric values, then casted to integers, and allow a tabindex value of `0` for a11y.
   509 		// Allow only numeric values, then casted to integers, and allow a tabindex value of `0` for a11y.
   478 		$tabindex = ( isset( $node->meta['tabindex'] ) && is_numeric( $node->meta['tabindex'] ) ) ? (int) $node->meta['tabindex'] : '';
   510 		$tabindex        = ( isset( $node->meta['tabindex'] ) && is_numeric( $node->meta['tabindex'] ) ) ? (int) $node->meta['tabindex'] : '';
   479 		$aria_attributes = ( '' !== $tabindex ) ? ' tabindex="' . $tabindex . '"' : '';
   511 		$aria_attributes = ( '' !== $tabindex ) ? ' tabindex="' . $tabindex . '"' : '';
   480 
   512 
   481 		$menuclass = '';
   513 		$menuclass = '';
       
   514 		$arrow     = '';
   482 
   515 
   483 		if ( $is_parent ) {
   516 		if ( $is_parent ) {
   484 			$menuclass = 'menupop ';
   517 			$menuclass        = 'menupop ';
   485 			$aria_attributes .= ' aria-haspopup="true"';
   518 			$aria_attributes .= ' aria-haspopup="true"';
   486 		}
   519 		}
   487 
   520 
   488 		if ( ! empty( $node->meta['class'] ) )
   521 		if ( ! empty( $node->meta['class'] ) ) {
   489 			$menuclass .= $node->meta['class'];
   522 			$menuclass .= $node->meta['class'];
   490 
   523 		}
   491 		if ( $menuclass )
   524 
       
   525 		// Print the arrow icon for the menu children with children.
       
   526 		if ( ! $is_root_top_item && ! $is_top_secondary_item && $is_parent ) {
       
   527 			$arrow = '<span class="wp-admin-bar-arrow" aria-hidden="true"></span>';
       
   528 		}
       
   529 
       
   530 		if ( $menuclass ) {
   492 			$menuclass = ' class="' . esc_attr( trim( $menuclass ) ) . '"';
   531 			$menuclass = ' class="' . esc_attr( trim( $menuclass ) ) . '"';
   493 
   532 		}
   494 		?>
   533 
   495 
   534 		echo "<li id='" . esc_attr( 'wp-admin-bar-' . $node->id ) . "'$menuclass>";
   496 		<li id="<?php echo esc_attr( 'wp-admin-bar-' . $node->id ); ?>"<?php echo $menuclass; ?>><?php
   535 
   497 			if ( $has_link ):
   536 		if ( $has_link ) {
   498 				?><a class="ab-item"<?php echo $aria_attributes; ?> href="<?php echo esc_url( $node->href ) ?>"<?php
   537 			$attributes = array( 'onclick', 'target', 'title', 'rel', 'lang', 'dir' );
   499 					if ( ! empty( $node->meta['onclick'] ) ) :
   538 			echo "<a class='ab-item'$aria_attributes href='" . esc_url( $node->href ) . "'";
   500 						?> onclick="<?php echo esc_js( $node->meta['onclick'] ); ?>"<?php
   539 			if ( ! empty( $node->meta['onclick'] ) ) {
   501 					endif;
   540 				echo ' onclick="' . esc_js( $node->meta['onclick'] ) . '"';
   502 				if ( ! empty( $node->meta['target'] ) ) :
   541 			}
   503 					?> target="<?php echo esc_attr( $node->meta['target'] ); ?>"<?php
   542 		} else {
   504 				endif;
   543 			$attributes = array( 'onclick', 'target', 'title', 'rel', 'lang', 'dir' );
   505 				if ( ! empty( $node->meta['title'] ) ) :
   544 			echo '<div class="ab-item ab-empty-item"' . $aria_attributes;
   506 					?> title="<?php echo esc_attr( $node->meta['title'] ); ?>"<?php
   545 		}
   507 				endif;
   546 
   508 				if ( ! empty( $node->meta['rel'] ) ) :
   547 		foreach ( $attributes as $attribute ) {
   509 					?> rel="<?php echo esc_attr( $node->meta['rel'] ); ?>"<?php
   548 			if ( ! empty( $node->meta[ $attribute ] ) ) {
   510 				endif;
   549 				echo " $attribute='" . esc_attr( $node->meta[ $attribute ] ) . "'";
   511 				if ( ! empty( $node->meta['lang'] ) ) :
   550 			}
   512 					?> lang="<?php echo esc_attr( $node->meta['lang'] ); ?>"<?php
   551 		}
   513 				endif;
   552 
   514 				if ( ! empty( $node->meta['dir'] ) ) :
   553 		echo ">{$arrow}{$node->title}";
   515 					?> dir="<?php echo esc_attr( $node->meta['dir'] ); ?>"<?php
   554 
   516 				endif;
   555 		if ( $has_link ) {
   517 				?>><?php
   556 			echo '</a>';
   518 			else:
   557 		} else {
   519 				?><div class="ab-item ab-empty-item"<?php echo $aria_attributes;
   558 			echo '</div>';
   520 				if ( ! empty( $node->meta['title'] ) ) :
   559 		}
   521 					?> title="<?php echo esc_attr( $node->meta['title'] ); ?>"<?php
   560 
   522 				endif;
   561 		if ( $is_parent ) {
   523 				if ( ! empty( $node->meta['lang'] ) ) :
   562 			echo '<div class="ab-sub-wrapper">';
   524 					?> lang="<?php echo esc_attr( $node->meta['lang'] ); ?>"<?php
   563 			foreach ( $node->children as $group ) {
   525 				endif;
   564 				$this->_render_group( $group );
   526 				if ( ! empty( $node->meta['dir'] ) ) :
   565 			}
   527 					?> dir="<?php echo esc_attr( $node->meta['dir'] ); ?>"<?php
   566 			echo '</div>';
   528 				endif;
   567 		}
   529 				?>><?php
   568 
   530 			endif;
   569 		if ( ! empty( $node->meta['html'] ) ) {
   531 
   570 			echo $node->meta['html'];
   532 			echo $node->title;
   571 		}
   533 
   572 
   534 			if ( $has_link ) :
   573 		echo '</li>';
   535 				?></a><?php
       
   536 			else:
       
   537 				?></div><?php
       
   538 			endif;
       
   539 
       
   540 			if ( $is_parent ) :
       
   541 				?><div class="ab-sub-wrapper"><?php
       
   542 					foreach ( $node->children as $group ) {
       
   543 						$this->_render_group( $group );
       
   544 					}
       
   545 				?></div><?php
       
   546 			endif;
       
   547 
       
   548 			if ( ! empty( $node->meta['html'] ) )
       
   549 				echo $node->meta['html'];
       
   550 
       
   551 			?>
       
   552 		</li><?php
       
   553 	}
   574 	}
   554 
   575 
   555 	/**
   576 	/**
   556 	 * Renders toolbar items recursively.
   577 	 * Renders toolbar items recursively.
   557 	 *
   578 	 *
   573 	public function add_menus() {
   594 	public function add_menus() {
   574 		// User related, aligned right.
   595 		// User related, aligned right.
   575 		add_action( 'admin_bar_menu', 'wp_admin_bar_my_account_menu', 0 );
   596 		add_action( 'admin_bar_menu', 'wp_admin_bar_my_account_menu', 0 );
   576 		add_action( 'admin_bar_menu', 'wp_admin_bar_search_menu', 4 );
   597 		add_action( 'admin_bar_menu', 'wp_admin_bar_search_menu', 4 );
   577 		add_action( 'admin_bar_menu', 'wp_admin_bar_my_account_item', 7 );
   598 		add_action( 'admin_bar_menu', 'wp_admin_bar_my_account_item', 7 );
       
   599 		add_action( 'admin_bar_menu', 'wp_admin_bar_recovery_mode_menu', 8 );
   578 
   600 
   579 		// Site related.
   601 		// Site related.
   580 		add_action( 'admin_bar_menu', 'wp_admin_bar_sidebar_toggle', 0 );
   602 		add_action( 'admin_bar_menu', 'wp_admin_bar_sidebar_toggle', 0 );
   581 		add_action( 'admin_bar_menu', 'wp_admin_bar_wp_menu', 10 );
   603 		add_action( 'admin_bar_menu', 'wp_admin_bar_wp_menu', 10 );
   582 		add_action( 'admin_bar_menu', 'wp_admin_bar_my_sites_menu', 20 );
   604 		add_action( 'admin_bar_menu', 'wp_admin_bar_my_sites_menu', 20 );