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