wp/wp-includes/class-wp-admin-bar.php
changeset 0 d970ebf37754
child 5 5e2f62d02dcd
equal deleted inserted replaced
-1:000000000000 0:d970ebf37754
       
     1 <?php
       
     2 /**
       
     3  * The WordPress Toolbar
       
     4  *
       
     5  * @since 3.1.0
       
     6  *
       
     7  * @package WordPress
       
     8  * @subpackage Toolbar
       
     9  */
       
    10 class WP_Admin_Bar {
       
    11 	private $nodes = array();
       
    12 	private $bound = false;
       
    13 	public $user;
       
    14 
       
    15 	public function __get( $name ) {
       
    16 		switch ( $name ) {
       
    17 			case 'proto' :
       
    18 				return is_ssl() ? 'https://' : 'http://';
       
    19 				break;
       
    20 			case 'menu' :
       
    21 				_deprecated_argument( 'WP_Admin_Bar', '3.3', '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.' );
       
    22 				return array(); // Sorry, folks.
       
    23 				break;
       
    24 		}
       
    25 	}
       
    26 
       
    27 	public function initialize() {
       
    28 		$this->user = new stdClass;
       
    29 
       
    30 		if ( is_user_logged_in() ) {
       
    31 			/* Populate settings we need for the menu based on the current user. */
       
    32 			$this->user->blogs = get_blogs_of_user( get_current_user_id() );
       
    33 			if ( is_multisite() ) {
       
    34 				$this->user->active_blog = get_active_blog_for_user( get_current_user_id() );
       
    35 				$this->user->domain = empty( $this->user->active_blog ) ? user_admin_url() : trailingslashit( get_home_url( $this->user->active_blog->blog_id ) );
       
    36 				$this->user->account_domain = $this->user->domain;
       
    37 			} else {
       
    38 				$this->user->active_blog = $this->user->blogs[get_current_blog_id()];
       
    39 				$this->user->domain = trailingslashit( home_url() );
       
    40 				$this->user->account_domain = $this->user->domain;
       
    41 			}
       
    42 		}
       
    43 
       
    44 		add_action( 'wp_head', 'wp_admin_bar_header' );
       
    45 
       
    46 		add_action( 'admin_head', 'wp_admin_bar_header' );
       
    47 
       
    48 		if ( current_theme_supports( 'admin-bar' ) ) {
       
    49 			/**
       
    50 			 * To remove the default padding styles from WordPress for the Toolbar, use the following code:
       
    51 			 * add_theme_support( 'admin-bar', array( 'callback' => '__return_false' ) );
       
    52 			 */
       
    53 			$admin_bar_args = get_theme_support( 'admin-bar' );
       
    54 			$header_callback = $admin_bar_args[0]['callback'];
       
    55 		}
       
    56 
       
    57 		if ( empty($header_callback) )
       
    58 			$header_callback = '_admin_bar_bump_cb';
       
    59 
       
    60 		add_action('wp_head', $header_callback);
       
    61 
       
    62 		wp_enqueue_script( 'admin-bar' );
       
    63 		wp_enqueue_style( 'admin-bar' );
       
    64 
       
    65 		/**
       
    66 		 * Fires after WP_Admin_Bar is initialized.
       
    67 		 *
       
    68 		 * @since 3.1.0
       
    69 		 */
       
    70 		do_action( 'admin_bar_init' );
       
    71 	}
       
    72 
       
    73 	public function add_menu( $node ) {
       
    74 		$this->add_node( $node );
       
    75 	}
       
    76 
       
    77 	public function remove_menu( $id ) {
       
    78 		$this->remove_node( $id );
       
    79 	}
       
    80 
       
    81 	/**
       
    82 	 * Add a node to the menu.
       
    83 	 *
       
    84 	 * @param array $args - The arguments for each node.
       
    85 	 * - id         - string    - The ID of the item.
       
    86 	 * - title      - string    - The title of the node.
       
    87 	 * - parent     - string    - The ID of the parent node. Optional.
       
    88 	 * - href       - string    - The link for the item. Optional.
       
    89 	 * - group      - boolean   - If the node is a group. Optional. Default false.
       
    90 	 * - meta       - array     - Meta data including the following keys: html, class, onclick, target, title, tabindex.
       
    91 	 */
       
    92 	public function add_node( $args ) {
       
    93 		// Shim for old method signature: add_node( $parent_id, $menu_obj, $args )
       
    94 		if ( func_num_args() >= 3 && is_string( func_get_arg(0) ) )
       
    95 			$args = array_merge( array( 'parent' => func_get_arg(0) ), func_get_arg(2) );
       
    96 
       
    97 		if ( is_object( $args ) )
       
    98 			$args = get_object_vars( $args );
       
    99 
       
   100 		// Ensure we have a valid title.
       
   101 		if ( empty( $args['id'] ) ) {
       
   102 			if ( empty( $args['title'] ) )
       
   103 				return;
       
   104 
       
   105 			_doing_it_wrong( __METHOD__, __( 'The menu ID should not be empty.' ), '3.3' );
       
   106 			// Deprecated: Generate an ID from the title.
       
   107 			$args['id'] = esc_attr( sanitize_title( trim( $args['title'] ) ) );
       
   108 		}
       
   109 
       
   110 		$defaults = array(
       
   111 			'id'     => false,
       
   112 			'title'  => false,
       
   113 			'parent' => false,
       
   114 			'href'   => false,
       
   115 			'group'  => false,
       
   116 			'meta'   => array(),
       
   117 		);
       
   118 
       
   119 		// If the node already exists, keep any data that isn't provided.
       
   120 		if ( $maybe_defaults = $this->get_node( $args['id'] ) )
       
   121 			$defaults = get_object_vars( $maybe_defaults );
       
   122 
       
   123 		// Do the same for 'meta' items.
       
   124 		if ( ! empty( $defaults['meta'] ) && ! empty( $args['meta'] ) )
       
   125 			$args['meta'] = wp_parse_args( $args['meta'], $defaults['meta'] );
       
   126 
       
   127 		$args = wp_parse_args( $args, $defaults );
       
   128 
       
   129 		$back_compat_parents = array(
       
   130 			'my-account-with-avatar' => array( 'my-account', '3.3' ),
       
   131 			'my-blogs'               => array( 'my-sites',   '3.3' ),
       
   132 		);
       
   133 
       
   134 		if ( isset( $back_compat_parents[ $args['parent'] ] ) ) {
       
   135 			list( $new_parent, $version ) = $back_compat_parents[ $args['parent'] ];
       
   136 			_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'] ) );
       
   137 			$args['parent'] = $new_parent;
       
   138 		}
       
   139 
       
   140 		$this->_set_node( $args );
       
   141 	}
       
   142 
       
   143 	final protected function _set_node( $args ) {
       
   144 		$this->nodes[ $args['id'] ] = (object) $args;
       
   145 	}
       
   146 
       
   147 	/**
       
   148 	 * Gets a node.
       
   149 	 *
       
   150 	 * @return object Node.
       
   151 	 */
       
   152 	final public function get_node( $id ) {
       
   153 		if ( $node = $this->_get_node( $id ) )
       
   154 			return clone $node;
       
   155 	}
       
   156 
       
   157 	final protected function _get_node( $id ) {
       
   158 		if ( $this->bound )
       
   159 			return;
       
   160 
       
   161 		if ( empty( $id ) )
       
   162 			$id = 'root';
       
   163 
       
   164 		if ( isset( $this->nodes[ $id ] ) )
       
   165 			return $this->nodes[ $id ];
       
   166 	}
       
   167 
       
   168 	final public function get_nodes() {
       
   169 		if ( ! $nodes = $this->_get_nodes() )
       
   170 			return;
       
   171 
       
   172 		foreach ( $nodes as &$node ) {
       
   173 			$node = clone $node;
       
   174 		}
       
   175 		return $nodes;
       
   176 	}
       
   177 
       
   178 	final protected function _get_nodes() {
       
   179 		if ( $this->bound )
       
   180 			return;
       
   181 
       
   182 		return $this->nodes;
       
   183 	}
       
   184 
       
   185 	/**
       
   186 	 * Add a group to a menu node.
       
   187 	 *
       
   188 	 * @since 3.3.0
       
   189 	 *
       
   190 	 * @param array $args - The arguments for each node.
       
   191 	 * - id         - string    - The ID of the item.
       
   192 	 * - parent     - string    - The ID of the parent node. Optional. Default root.
       
   193 	 * - meta       - array     - Meta data including the following keys: class, onclick, target, title.
       
   194 	 */
       
   195 	final public function add_group( $args ) {
       
   196 		$args['group'] = true;
       
   197 
       
   198 		$this->add_node( $args );
       
   199 	}
       
   200 
       
   201 	/**
       
   202 	 * Remove a node.
       
   203 	 *
       
   204 	 * @param string The ID of the item.
       
   205 	 */
       
   206 	public function remove_node( $id ) {
       
   207 		$this->_unset_node( $id );
       
   208 	}
       
   209 
       
   210 	final protected function _unset_node( $id ) {
       
   211 		unset( $this->nodes[ $id ] );
       
   212 	}
       
   213 
       
   214 	public function render() {
       
   215 		$root = $this->_bind();
       
   216 		if ( $root )
       
   217 			$this->_render( $root );
       
   218 	}
       
   219 
       
   220 	final protected function _bind() {
       
   221 		if ( $this->bound )
       
   222 			return;
       
   223 
       
   224 		// Add the root node.
       
   225 		// Clear it first, just in case. Don't mess with The Root.
       
   226 		$this->remove_node( 'root' );
       
   227 		$this->add_node( array(
       
   228 			'id'    => 'root',
       
   229 			'group' => false,
       
   230 		) );
       
   231 
       
   232 		// Normalize nodes: define internal 'children' and 'type' properties.
       
   233 		foreach ( $this->_get_nodes() as $node ) {
       
   234 			$node->children = array();
       
   235 			$node->type = ( $node->group ) ? 'group' : 'item';
       
   236 			unset( $node->group );
       
   237 
       
   238 			// The Root wants your orphans. No lonely items allowed.
       
   239 			if ( ! $node->parent )
       
   240 				$node->parent = 'root';
       
   241 		}
       
   242 
       
   243 		foreach ( $this->_get_nodes() as $node ) {
       
   244 			if ( 'root' == $node->id )
       
   245 				continue;
       
   246 
       
   247 			// Fetch the parent node. If it isn't registered, ignore the node.
       
   248 			if ( ! $parent = $this->_get_node( $node->parent ) ) {
       
   249 				continue;
       
   250 			}
       
   251 
       
   252 			// Generate the group class (we distinguish between top level and other level groups).
       
   253 			$group_class = ( $node->parent == 'root' ) ? 'ab-top-menu' : 'ab-submenu';
       
   254 
       
   255 			if ( $node->type == 'group' ) {
       
   256 				if ( empty( $node->meta['class'] ) )
       
   257 					$node->meta['class'] = $group_class;
       
   258 				else
       
   259 					$node->meta['class'] .= ' ' . $group_class;
       
   260 			}
       
   261 
       
   262 			// Items in items aren't allowed. Wrap nested items in 'default' groups.
       
   263 			if ( $parent->type == 'item' && $node->type == 'item' ) {
       
   264 				$default_id = $parent->id . '-default';
       
   265 				$default    = $this->_get_node( $default_id );
       
   266 
       
   267 				// The default group is added here to allow groups that are
       
   268 				// added before standard menu items to render first.
       
   269 				if ( ! $default ) {
       
   270 					// Use _set_node because add_node can be overloaded.
       
   271 					// Make sure to specify default settings for all properties.
       
   272 					$this->_set_node( array(
       
   273 						'id'        => $default_id,
       
   274 						'parent'    => $parent->id,
       
   275 						'type'      => 'group',
       
   276 						'children'  => array(),
       
   277 						'meta'      => array(
       
   278 							'class'     => $group_class,
       
   279 						),
       
   280 						'title'     => false,
       
   281 						'href'      => false,
       
   282 					) );
       
   283 					$default = $this->_get_node( $default_id );
       
   284 					$parent->children[] = $default;
       
   285 				}
       
   286 				$parent = $default;
       
   287 
       
   288 			// Groups in groups aren't allowed. Add a special 'container' node.
       
   289 			// The container will invisibly wrap both groups.
       
   290 			} elseif ( $parent->type == 'group' && $node->type == 'group' ) {
       
   291 				$container_id = $parent->id . '-container';
       
   292 				$container    = $this->_get_node( $container_id );
       
   293 
       
   294 				// We need to create a container for this group, life is sad.
       
   295 				if ( ! $container ) {
       
   296 					// Use _set_node because add_node can be overloaded.
       
   297 					// Make sure to specify default settings for all properties.
       
   298 					$this->_set_node( array(
       
   299 						'id'       => $container_id,
       
   300 						'type'     => 'container',
       
   301 						'children' => array( $parent ),
       
   302 						'parent'   => false,
       
   303 						'title'    => false,
       
   304 						'href'     => false,
       
   305 						'meta'     => array(),
       
   306 					) );
       
   307 
       
   308 					$container = $this->_get_node( $container_id );
       
   309 
       
   310 					// Link the container node if a grandparent node exists.
       
   311 					$grandparent = $this->_get_node( $parent->parent );
       
   312 
       
   313 					if ( $grandparent ) {
       
   314 						$container->parent = $grandparent->id;
       
   315 
       
   316 						$index = array_search( $parent, $grandparent->children, true );
       
   317 						if ( $index === false )
       
   318 							$grandparent->children[] = $container;
       
   319 						else
       
   320 							array_splice( $grandparent->children, $index, 1, array( $container ) );
       
   321 					}
       
   322 
       
   323 					$parent->parent = $container->id;
       
   324 				}
       
   325 
       
   326 				$parent = $container;
       
   327 			}
       
   328 
       
   329 			// Update the parent ID (it might have changed).
       
   330 			$node->parent = $parent->id;
       
   331 
       
   332 			// Add the node to the tree.
       
   333 			$parent->children[] = $node;
       
   334 		}
       
   335 
       
   336 		$root = $this->_get_node( 'root' );
       
   337 		$this->bound = true;
       
   338 		return $root;
       
   339 	}
       
   340 
       
   341 	final protected function _render( $root ) {
       
   342 		global $is_IE;
       
   343 
       
   344 		// Add browser classes.
       
   345 		// We have to do this here since admin bar shows on the front end.
       
   346 		$class = 'nojq nojs';
       
   347 		if ( $is_IE ) {
       
   348 			if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 7' ) )
       
   349 				$class .= ' ie7';
       
   350 			elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 8' ) )
       
   351 				$class .= ' ie8';
       
   352 			elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 9' ) )
       
   353 				$class .= ' ie9';
       
   354 		} elseif ( wp_is_mobile() ) {
       
   355 			$class .= ' mobile';
       
   356 		}
       
   357 
       
   358 		?>
       
   359 		<div id="wpadminbar" class="<?php echo $class; ?>" role="navigation">
       
   360 			<a class="screen-reader-shortcut" href="#wp-toolbar" tabindex="1"><?php _e('Skip to toolbar'); ?></a>
       
   361 			<div class="quicklinks" id="wp-toolbar" role="navigation" aria-label="<?php esc_attr_e('Top navigation toolbar.'); ?>" tabindex="0">
       
   362 				<?php foreach ( $root->children as $group ) {
       
   363 					$this->_render_group( $group );
       
   364 				} ?>
       
   365 			</div>
       
   366 			<?php if ( is_user_logged_in() ) : ?>
       
   367 			<a class="screen-reader-shortcut" href="<?php echo esc_url( wp_logout_url() ); ?>"><?php _e('Log Out'); ?></a>
       
   368 			<?php endif; ?>
       
   369 		</div>
       
   370 
       
   371 		<?php
       
   372 	}
       
   373 
       
   374 	final protected function _render_container( $node ) {
       
   375 		if ( $node->type != 'container' || empty( $node->children ) )
       
   376 			return;
       
   377 
       
   378 		?><div id="<?php echo esc_attr( 'wp-admin-bar-' . $node->id ); ?>" class="ab-group-container"><?php
       
   379 			foreach ( $node->children as $group ) {
       
   380 				$this->_render_group( $group );
       
   381 			}
       
   382 		?></div><?php
       
   383 	}
       
   384 
       
   385 	final protected function _render_group( $node ) {
       
   386 		if ( $node->type == 'container' )
       
   387 			return $this->_render_container( $node );
       
   388 
       
   389 		if ( $node->type != 'group' || empty( $node->children ) )
       
   390 			return;
       
   391 
       
   392 		if ( ! empty( $node->meta['class'] ) )
       
   393 			$class = ' class="' . esc_attr( trim( $node->meta['class'] ) ) . '"';
       
   394 		else
       
   395 			$class = '';
       
   396 
       
   397 		?><ul id="<?php echo esc_attr( 'wp-admin-bar-' . $node->id ); ?>"<?php echo $class; ?>><?php
       
   398 			foreach ( $node->children as $item ) {
       
   399 				$this->_render_item( $item );
       
   400 			}
       
   401 		?></ul><?php
       
   402 	}
       
   403 
       
   404 	final protected function _render_item( $node ) {
       
   405 		if ( $node->type != 'item' )
       
   406 			return;
       
   407 
       
   408 		$is_parent = ! empty( $node->children );
       
   409 		$has_link  = ! empty( $node->href );
       
   410 
       
   411 		$tabindex = isset( $node->meta['tabindex'] ) ? (int) $node->meta['tabindex'] : '';
       
   412 		$aria_attributes = $tabindex ? 'tabindex="' . $tabindex . '"' : '';
       
   413 
       
   414 		$menuclass = '';
       
   415 
       
   416 		if ( $is_parent ) {
       
   417 			$menuclass = 'menupop ';
       
   418 			$aria_attributes .= ' aria-haspopup="true"';
       
   419 		}
       
   420 
       
   421 		if ( ! empty( $node->meta['class'] ) )
       
   422 			$menuclass .= $node->meta['class'];
       
   423 
       
   424 		if ( $menuclass )
       
   425 			$menuclass = ' class="' . esc_attr( trim( $menuclass ) ) . '"';
       
   426 
       
   427 		?>
       
   428 
       
   429 		<li id="<?php echo esc_attr( 'wp-admin-bar-' . $node->id ); ?>"<?php echo $menuclass; ?>><?php
       
   430 			if ( $has_link ):
       
   431 				?><a class="ab-item" <?php echo $aria_attributes; ?> href="<?php echo esc_url( $node->href ) ?>"<?php
       
   432 					if ( ! empty( $node->meta['onclick'] ) ) :
       
   433 						?> onclick="<?php echo esc_js( $node->meta['onclick'] ); ?>"<?php
       
   434 					endif;
       
   435 				if ( ! empty( $node->meta['target'] ) ) :
       
   436 					?> target="<?php echo esc_attr( $node->meta['target'] ); ?>"<?php
       
   437 				endif;
       
   438 				if ( ! empty( $node->meta['title'] ) ) :
       
   439 					?> title="<?php echo esc_attr( $node->meta['title'] ); ?>"<?php
       
   440 				endif;
       
   441 				?>><?php
       
   442 			else:
       
   443 				?><div class="ab-item ab-empty-item" <?php echo $aria_attributes;
       
   444 				if ( ! empty( $node->meta['title'] ) ) :
       
   445 					?> title="<?php echo esc_attr( $node->meta['title'] ); ?>"<?php
       
   446 				endif;
       
   447 				?>><?php
       
   448 			endif;
       
   449 
       
   450 			echo $node->title;
       
   451 
       
   452 			if ( $has_link ) :
       
   453 				?></a><?php
       
   454 			else:
       
   455 				?></div><?php
       
   456 			endif;
       
   457 
       
   458 			if ( $is_parent ) :
       
   459 				?><div class="ab-sub-wrapper"><?php
       
   460 					foreach ( $node->children as $group ) {
       
   461 						$this->_render_group( $group );
       
   462 					}
       
   463 				?></div><?php
       
   464 			endif;
       
   465 
       
   466 			if ( ! empty( $node->meta['html'] ) )
       
   467 				echo $node->meta['html'];
       
   468 
       
   469 			?>
       
   470 		</li><?php
       
   471 	}
       
   472 
       
   473 	public function recursive_render( $id, $node ) {
       
   474 		_deprecated_function( __METHOD__, '3.3', 'WP_Admin_bar::render(), WP_Admin_Bar::_render_item()' );
       
   475 		$this->_render_item( $node );
       
   476 	}
       
   477 
       
   478 	public function add_menus() {
       
   479 		// User related, aligned right.
       
   480 		add_action( 'admin_bar_menu', 'wp_admin_bar_my_account_menu', 0 );
       
   481 		add_action( 'admin_bar_menu', 'wp_admin_bar_search_menu', 4 );
       
   482 		add_action( 'admin_bar_menu', 'wp_admin_bar_my_account_item', 7 );
       
   483 
       
   484 		// Site related.
       
   485 		add_action( 'admin_bar_menu', 'wp_admin_bar_wp_menu', 10 );
       
   486 		add_action( 'admin_bar_menu', 'wp_admin_bar_my_sites_menu', 20 );
       
   487 		add_action( 'admin_bar_menu', 'wp_admin_bar_site_menu', 30 );
       
   488 		add_action( 'admin_bar_menu', 'wp_admin_bar_updates_menu', 40 );
       
   489 
       
   490 		// Content related.
       
   491 		if ( ! is_network_admin() && ! is_user_admin() ) {
       
   492 			add_action( 'admin_bar_menu', 'wp_admin_bar_comments_menu', 60 );
       
   493 			add_action( 'admin_bar_menu', 'wp_admin_bar_new_content_menu', 70 );
       
   494 		}
       
   495 		add_action( 'admin_bar_menu', 'wp_admin_bar_edit_menu', 80 );
       
   496 
       
   497 		add_action( 'admin_bar_menu', 'wp_admin_bar_add_secondary_groups', 200 );
       
   498 
       
   499 		/**
       
   500 		 * Fires after menus are added to the menu bar.
       
   501 		 *
       
   502 		 * @since 3.1.0
       
   503 		 */
       
   504 		do_action( 'add_admin_bar_menus' );
       
   505 	}
       
   506 }