/includes/core/classes/class-cp-cluster-toolbar.php

https://gitlab.com/clusterpress/clusterpress · PHP · 404 lines · 181 code · 62 blank · 161 comment · 33 complexity · 051ef8f7f95a8ce0092cf6a1580006d2 MD5 · raw file

  1. <?php
  2. /**
  3. * ClusterPress Toolbar Class.
  4. *
  5. * Extends the WP Admin Bar to manage any Cluster's Toolbar.
  6. *
  7. * @package ClusterPress\core\classes
  8. * @subpackage cluster-toolbar
  9. *
  10. * @since 1.0.0
  11. */
  12. /**
  13. * Make sure the WP_Admin_Bar is loaded.
  14. */
  15. if ( ! class_exists( 'WP_Admin_Bar' ) ) {
  16. require_once( ABSPATH . WPINC . '/class-wp-admin-bar.php' );
  17. }
  18. /**
  19. * The ClusterPress Toolbars class.
  20. *
  21. * @since 1.0.0
  22. */
  23. class CP_Cluster_Toolbar extends WP_Admin_Bar {
  24. /**
  25. * Only Used to avoid the WP Admin Bar to run the parent method.
  26. *
  27. * @since 1.0.0
  28. */
  29. public function initialize() {}
  30. /**
  31. * Only Used to avoid the WP Admin Bar to run the parent method.
  32. *
  33. * @since 1.0.0
  34. */
  35. public function add_menus() {}
  36. /**
  37. * Only Used to avoid the WP Admin Bar to run the parent method.
  38. *
  39. * @since 1.0.0
  40. */
  41. public function render() {}
  42. /**
  43. * Add a node to the nav items.
  44. *
  45. * @since 1.0.0
  46. *
  47. * @param array $args {
  48. * @see WP_Admin_Bar::add_node() for the argument descriptions.
  49. * }
  50. */
  51. public function add_node( $args ) {
  52. $r = wp_parse_args( $args, array(
  53. 'capability' => 'cp_read_single_user',
  54. ) );
  55. parent::add_node( $r );
  56. }
  57. /**
  58. * Edit a node of the nav.
  59. *
  60. * @since 1.0.0
  61. *
  62. * @param string $node_id The Node ID to edit (NB: ClusterPress is using the rewrite_ids).
  63. * @param array $args {
  64. * @see WP_Admin_Bar::add_node() for the argument descriptions.
  65. * }
  66. */
  67. public function edit_node( $node_id, $args = array() ) {
  68. $node = $this->get_node( $node_id );
  69. if ( ! $node ) {
  70. return false;
  71. }
  72. $r = wp_parse_args( $args, (array) $node );
  73. $this->_set_node( $r );
  74. }
  75. /**
  76. * Set the URLs of the nav items.
  77. *
  78. * @since 1.0.0
  79. *
  80. * @param array $args {
  81. * An array of arguments.
  82. * @type WP_User|WP_Site $object The Object the nav item applies to.
  83. * @type string $type The Object type (WP_User or WP_Site).
  84. * @type string $parent_group The Parent Nav. Defaults to 'single-bar'.
  85. * @type string $url_callback The callback function to build the URL.
  86. * @type string $filter The name of the filter to override the nav item's data.
  87. * @type array $capability_args An array containing the capability and additional arguments.
  88. * }
  89. * @return array The list of nav items.
  90. */
  91. public function populate_urls( $args = array() ) {
  92. $r = wp_parse_args( $args, array(
  93. 'object' => null,
  94. 'type' => '',
  95. 'parent_group' => 'single-bar',
  96. 'url_callback' => '',
  97. 'filter' => '',
  98. 'capability_args' => array(),
  99. ) );
  100. if ( empty( $r['object'] ) || empty( $r['type'] ) || empty( $r['url_callback'] ) || ! is_a( $r['object'], $r['type'] ) ) {
  101. return false;
  102. }
  103. $nodes = wp_list_filter( $this->get_nodes(), array( 'parent_group' => $r['parent_group'] ) );
  104. if ( ! is_array( $nodes ) ) {
  105. return false;
  106. }
  107. foreach ( $nodes as $key => $node ) {
  108. $args = array();
  109. if ( 'home' !== $node->id ) {
  110. $args = array(
  111. 'rewrite_id' => $node->id,
  112. 'slug' => $node->slug,
  113. );
  114. // Build the url for nav items having a parent.
  115. if ( ! empty( $node->parent ) ) {
  116. $parent = wp_list_filter( $nodes, array( 'id' => $node->parent ) );
  117. if ( $parent ) {
  118. $parent = reset( $parent );
  119. $args['slug'] = trailingslashit( $parent->slug ) . $args['slug'];
  120. $args['rewrite_id'] = $parent->id;
  121. }
  122. // Check if there is a parent to set his url to the first found child.
  123. } else {
  124. $childnodes = wp_list_filter( $nodes, array( 'parent' => $node->id ) );
  125. if ( $childnodes ) {
  126. $first_child = reset( $childnodes );
  127. $args['slug'] = trailingslashit( $args['slug'] ) . $first_child->slug;
  128. }
  129. }
  130. }
  131. // Do not generate links to items the user can't access to.
  132. if ( ! current_user_can( $nodes[ $key ]->capability, $r['capability_args'] ) ) {
  133. continue;
  134. }
  135. if ( is_callable( $r['url_callback'] ) ) {
  136. $nodes[ $key ]->href = call_user_func_array( $r['url_callback'], array( $args, $r['object'] ) );
  137. }
  138. }
  139. if ( ! empty( $r['filter'] ) ) {
  140. /**
  141. * Dynamic filter to edit the nav node once the URL is set
  142. * @see CP_User_Cluster::get_account_bar_menus() for an example.
  143. *
  144. * @since 1.0.0
  145. *
  146. * @param array $nodes A list of nav objects.
  147. * @param WP_User|WP_Site $object The object the nav applies to.
  148. */
  149. $nodes = apply_filters( $r['filter'], $nodes, $r['object'] );
  150. }
  151. return $nodes;
  152. }
  153. /**
  154. * Validate the current user's access to the nav and set some nav metas.
  155. *
  156. * @since 1.0.0
  157. *
  158. * @param array $args {
  159. * An array of arguments.
  160. * @type array $nodes The list of nav items.
  161. * @type WP_User|WP_Site $object The Object the nav item applies to.
  162. * @type WP_Query $query The WordPress query being processed.
  163. * @type string $default_cap The required capability to view the nav item's content.
  164. * @type string $filter_prefix The prefix of the filter to override the nav item's data.
  165. * }
  166. * @return $result An array containing the values of the Action and Sub Action globals.
  167. */
  168. public function validate_access( $args = array() ) {
  169. $r = wp_parse_args( $args, array(
  170. 'nodes' => array(),
  171. 'object' => null,
  172. 'query' => null,
  173. 'default_cap' => 'manage_options',
  174. 'filter_prefix' => 'cp_cluster',
  175. ) );
  176. $result = array(
  177. 'action' => '',
  178. 'subaction' => '',
  179. );
  180. if ( empty( $r['nodes'] ) || ! is_array( $r['nodes'] ) || empty( $r['query'] ) || ! is_a( $r['query'], 'WP_Query' ) ) {
  181. return $result;
  182. }
  183. foreach ( $r['nodes'] as $node ) {
  184. $args = array();
  185. $qv = $r['query']->get( $node->id );
  186. if ( $qv ) {
  187. // First look for subactions
  188. $childnodes = wp_list_filter( $r['nodes'], array( 'parent' => $node->id, 'slug' => $qv ) );
  189. if ( $childnodes ) {
  190. $childnode = reset( $childnodes );
  191. if ( isset( $childnode->meta['classes'] ) ) {
  192. $childnode->meta['classes'] = array_merge( $childnode->meta['classes'], array( 'current' ) );
  193. } else {
  194. $childnode->meta['classes'] = array( 'current' );
  195. }
  196. $this->edit_node( $childnode->id, (array) $childnode );
  197. // Set the subaction
  198. $result['subaction'] = $qv;
  199. }
  200. // Take care of meta.
  201. if ( isset( $node->meta['classes'] ) ) {
  202. $node->meta['classes'] = array_merge( $node->meta['classes'], array( 'current' ) );
  203. } else {
  204. $node->meta['classes'] = array( 'current' );
  205. }
  206. // Manage access
  207. if ( ! isset( $node->capability ) ) {
  208. $node->capability = $r['default_cap'];
  209. }
  210. // No access at all or not viewing self profile.
  211. if ( ! current_user_can( $node->capability ) ) {
  212. // Reset the result array.
  213. return array(
  214. 'action' => '',
  215. 'subaction' => '',
  216. );
  217. }
  218. // Set the current action
  219. $result['action'] = $node->slug;
  220. }
  221. /**
  222. * Dynamic filter to allow overrides just before validating the nav.
  223. *
  224. * @since 1.0.0
  225. *
  226. * @param object $node The nav item's data.
  227. * @param WP_User|WP_Site $object The object the nav applies to.
  228. */
  229. $this->edit_node( $node->id, (array) apply_filters( $r['filter_prefix'] . '_' . $node->id, $node, $r['object'] ) );
  230. }
  231. return $result;
  232. }
  233. /**
  234. * Get all nodes for a given toolbar parent group
  235. *
  236. * @since 1.0.0
  237. *
  238. * @param string $toolbar The parent group of the nav.
  239. * @return array The list of nav items.
  240. */
  241. public function get_toolbar_nodes( $toolbar = 'single-bar' ) {
  242. return wp_list_filter( $this->get_nodes(), array( 'parent_group' => $toolbar ) );
  243. }
  244. /**
  245. * Get a nav item using its slug argument.
  246. *
  247. * @since 1.0.0
  248. *
  249. * @param string $slug The slug of the nav item.
  250. * @retun object The nav item object.
  251. */
  252. public function get_node_by_slug( $slug = '' ) {
  253. $nodes = $this->get_nodes();
  254. $node = wp_list_filter( $nodes, array( 'slug' => $slug ) );
  255. if ( ! is_array( $node ) ) {
  256. return false;
  257. }
  258. return reset( $node );
  259. }
  260. /**
  261. * Sort the nav items according to their position argument.
  262. *
  263. * @since 1.0.0
  264. *
  265. * @param array $items The nav items.
  266. * @return array The sorted nav items.
  267. */
  268. public function sort_items( $items ) {
  269. $sorted = array();
  270. foreach ( $items as $item ) {
  271. // Default position
  272. $position = 99;
  273. if ( isset( $item->position ) ) {
  274. $position = (int) $item->position;
  275. }
  276. // If position is already taken, move to the first next available
  277. if ( isset( $sorted[ $position ] ) ) {
  278. $sorted_keys = array_keys( $sorted );
  279. do {
  280. $position += 1;
  281. } while ( in_array( $position, $sorted_keys ) );
  282. }
  283. $sorted[ $position ] = $item;
  284. }
  285. ksort( $sorted );
  286. return $sorted;
  287. }
  288. /**
  289. * Check if there is a nav matching the requested arguments.
  290. *
  291. * @since 1.0.0
  292. *
  293. * @param array $args {
  294. * An array of arguments.
  295. * @type string $parent The parent nav items must match.
  296. * @type string $parent_group The Parent's nav. Defaults to 'single-bar'.
  297. * }
  298. * @return bool Whether there are some nav items to dsiplay.
  299. */
  300. public function has_items( $args = array() ) {
  301. $this->current_nav = null;
  302. $r = wp_parse_args( $args, array(
  303. 'parent' => false,
  304. 'parent_group' => 'single-bar',
  305. ) );
  306. $items = $this->get_nodes();
  307. // Filter according to args
  308. $items = wp_list_filter( $items, $r );
  309. // Remove toolbar menus the user can't access to.
  310. foreach ( $items as $k => $n ) {
  311. if ( ! current_user_can( $n->capability ) ) {
  312. unset( $items[ $k ] );
  313. }
  314. }
  315. // Filter again to remove the items the user has no access to.
  316. $this->current_nav = array_values( $this->sort_items( $items ) );
  317. $this->current_nav_index = 0;
  318. return ! empty( $this->current_nav );
  319. }
  320. /**
  321. * Iterate nav items within the Nav loop.
  322. *
  323. * @since 1.0.0.
  324. */
  325. public function nav_items() {
  326. if ( isset( $this->current_nav[ $this->current_nav_index ] ) ) {
  327. return true;
  328. }
  329. $this->current_nav_index = 0;
  330. unset( $this->current_item );
  331. return false;
  332. }
  333. /**
  334. * Set the nav item being iterated on.
  335. *
  336. * @since 1.0.0
  337. */
  338. public function nav_item() {
  339. $this->current_item = $this->current_nav[ $this->current_nav_index ];
  340. $this->current_nav_index += 1;
  341. }
  342. }