PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/htdocs/wp-includes/class-wp-admin-bar.php

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