PLL_Frontend_Nav_Menu

Manages custom menus translations as well as the language switcher menu item on frontend.

Defined (1)

The class is defined in the following location(s).

/frontend/frontend-nav-menu.php  
  1. class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu { 
  2. public $curlang; 
  3.  
  4. /** 
  5. * Constructor 
  6. * @since 1.2 
  7. */ 
  8. public function __construct( &$polylang ) { 
  9. parent::__construct( $polylang ); 
  10.  
  11. $this->curlang = &$polylang->curlang; 
  12.  
  13. // Split the language switcher menu item in several language menu items 
  14. add_filter( 'wp_get_nav_menu_items', array( $this, 'wp_get_nav_menu_items' ), 20 ); // after the customizer menus 
  15. add_filter( 'wp_nav_menu_objects', array( $this, 'wp_nav_menu_objects' ) ); 
  16. add_filter( 'nav_menu_link_attributes', array( $this, 'nav_menu_link_attributes' ), 10, 3 ); 
  17.  
  18. // Filters menus by language 
  19. add_filter( 'theme_mod_nav_menu_locations', array( $this, 'nav_menu_locations' ), 20 ); 
  20. add_filter( 'wp_nav_menu_args', array( $this, 'wp_nav_menu_args' ) ); 
  21.  
  22. // The customizer 
  23. if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) { 
  24. add_filter( 'wp_nav_menu_args', array( $this, 'filter_args_before_customizer' ) ); 
  25. add_filter( 'wp_nav_menu_args', array( $this, 'filter_args_after_customizer' ), 2000 ); 
  26.  
  27. /** 
  28. * Sort menu items by menu order 
  29. * @since 1.7.9 
  30. * @param object $a The first object to compare 
  31. * @param object $b The second object to compare 
  32. * @return int -1 or 1 if $a is considered to be respectively less than or greater than $b. 
  33. */ 
  34. protected function usort_menu_items( $a, $b ) { 
  35. return ( $a->menu_order < $b->menu_order ) ? -1 : 1; 
  36.  
  37. /** 
  38. * Splits the one item of backend in several items on frontend 
  39. * take care to menu_order as it is used later in wp_nav_menu 
  40. * @since 1.1.1 
  41. * @param array $items menu items 
  42. * @return array modified items 
  43. */ 
  44. public function wp_get_nav_menu_items( $items ) { 
  45. if ( doing_action( 'customize_register' ) ) { // needed since WP 4.3, doing_action available since WP 3.9 
  46. return $items; 
  47.  
  48. // The customizer menus does not sort the items and we need them to be sorted before splitting the language switcher 
  49. usort( $items, array( $this, 'usort_menu_items' ) ); 
  50.  
  51. $new_items = array(); 
  52. $offset = 0; 
  53.  
  54. foreach ( $items as $key => $item ) { 
  55. if ( $options = get_post_meta( $item->ID, '_pll_menu_item', true ) ) { 
  56. $i = 0; 
  57.  
  58. $switcher = new PLL_Switcher; 
  59. $args = array_merge( array( 'raw' => 1 ), $options ); 
  60. $the_languages = $switcher->the_languages( PLL()->links, $args ); 
  61.  
  62. // parent item for dropdown 
  63. if ( ! empty( $options['dropdown'] ) ) { 
  64. $item->title = $options['show_flags'] && $options['show_names'] ? $this->curlang->flag . ' ' . esc_html( $this->curlang->name ) : ( $options['show_flags'] ? $this->curlang->flag : esc_html( $this->curlang->name ) ); 
  65. $item->url = ''; 
  66. $item->classes = array( 'pll-parent-menu-item' ); 
  67. $new_items[] = $item; 
  68. $offset++; 
  69.  
  70. foreach ( $the_languages as $lang ) { 
  71. $lang_item = clone $item; 
  72. $lang_item->ID = $lang_item->ID . '-' . $lang['slug']; // A unique ID 
  73. $lang_item->title = $options['show_flags'] && $options['show_names'] ? $lang['flag'] . '<span style="margin-left:0.3em;">' . esc_html( $lang['name'] ) . '</span>' : ( $options['show_flags'] ? $lang['flag'] : esc_html( $lang['name'] ) ); 
  74. $lang_item->url = $lang['url']; 
  75. $lang_item->lang = $lang['locale']; // Save this for use in nav_menu_link_attributes 
  76. $lang_item->classes = $lang['classes']; 
  77. $lang_item->menu_order += $offset + $i++; 
  78. if ( ! empty( $options['dropdown'] ) ) { 
  79. $lang_item->menu_item_parent = $item->db_id; 
  80. $lang_item->db_id = 0; // to avoid recursion 
  81. $new_items[] = $lang_item; 
  82. $offset += $i - 1; 
  83. } else { 
  84. $item->menu_order += $offset; 
  85. $new_items[] = $item; 
  86. return $new_items; 
  87.  
  88. /** 
  89. * Returns the ancestors of a menu item 
  90. * @since 1.1.1 
  91. * @param object $item 
  92. * @return array ancestors ids 
  93. */ 
  94. public function get_ancestors( $item ) { 
  95. $ids = array(); 
  96. $_anc_id = (int) $item->db_id; 
  97. while ( ( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) && ! in_array( $_anc_id, $ids ) ) { 
  98. $ids[] = $_anc_id; 
  99. return $ids; 
  100.  
  101. /** 
  102. * Removes current-menu and current-menu-ancestor classes to lang switcher when not on the home page 
  103. * @since 1.1.1 
  104. * @param array $items 
  105. * @return array modified menu items 
  106. */ 
  107. public function wp_nav_menu_objects( $items ) { 
  108. $r_ids = $k_ids = array(); 
  109.  
  110. foreach ( $items as $item ) { 
  111. if ( ! empty( $item->classes ) && is_array( $item->classes ) ) { 
  112. if ( in_array( 'current-lang', $item->classes ) ) { 
  113. $item->classes = array_diff( $item->classes, array( 'current-menu-item' ) ); 
  114. $r_ids = array_merge( $r_ids, $this->get_ancestors( $item ) ); // Remove the classes for these ancestors 
  115. } elseif ( in_array( 'current-menu-item', $item->classes ) ) { 
  116. $k_ids = array_merge( $k_ids, $this->get_ancestors( $item ) ); // Keep the classes for these ancestors 
  117.  
  118. $r_ids = array_diff( $r_ids, $k_ids ); 
  119.  
  120. foreach ( $items as $item ) { 
  121. if ( ! empty( $item->db_id ) && in_array( $item->db_id, $r_ids ) ) { 
  122. $item->classes = array_diff( $item->classes, array( 'current-menu-ancestor', 'current-menu-parent', 'current_page_parent', 'current_page_ancestor' ) ); 
  123.  
  124. return $items; 
  125.  
  126. /** 
  127. * Adds hreflang attribute for the language switcher menu items 
  128. * available since WP3.6 
  129. * @since 1.1 
  130. * @param array $atts 
  131. * @return array modified $atts 
  132. */ 
  133. public function nav_menu_link_attributes( $atts, $item, $args ) { 
  134. if ( isset( $item->lang ) ) { 
  135. $atts['lang'] = $atts['hreflang'] = esc_attr( $item->lang ); 
  136. return $atts; 
  137.  
  138. /** 
  139. * Fills the theme nav menus locations with the right menu in the right language 
  140. * Needs to wait for the language to be defined 
  141. * @since 1.2 
  142. * @param array|bool list of nav menus locations, false if menu locations have not been filled yet 
  143. * @return array|bool modified list of nav menus locations 
  144. */ 
  145. public function nav_menu_locations( $menus ) { 
  146. if ( is_array( $menus ) && ! empty( $this->curlang ) ) { 
  147. // First get multilingual menu locations from DB 
  148. $theme = get_option( 'stylesheet' ); 
  149.  
  150. foreach ( $menus as $loc => $menu ) { 
  151. $menus[ $loc ] = empty( $this->options['nav_menus'][ $theme ][ $loc ][ $this->curlang->slug ] ) ? 0 : $this->options['nav_menus'][ $theme ][ $loc ][ $this->curlang->slug ]; 
  152.  
  153. // Support for theme customizer 
  154. // Let's look for multilingual menu locations directly in $_POST as there are not in customizer object 
  155. if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) { 
  156. $customized = json_decode( wp_unslash( $_POST['customized'] ) ); 
  157.  
  158. if ( is_object( $customized ) ) { 
  159. foreach ( $customized as $key => $c ) { 
  160. if ( false !== strpos( $key, 'nav_menu_locations[' ) ) { 
  161. $loc = substr( trim( $key, ']' ), 19 ); 
  162. $infos = $this->explode_location( $loc ); 
  163. if ( $infos['lang'] == $this->curlang->slug ) { 
  164. $menus[ $infos['location'] ] = $c; 
  165. } elseif ( $this->curlang->slug == $this->options['default_lang'] ) { 
  166. $menus[ $loc ] = $c; 
  167. return $menus; 
  168.  
  169. /** 
  170. * Attempt to translate the nav menu when it is hardcoded or when no location is defined in wp_nav_menu 
  171. * @since 1.7.10 
  172. * @param array $args 
  173. * @return array modified $args 
  174. */ 
  175. public function wp_nav_menu_args( $args ) { 
  176. $theme = get_option( 'stylesheet' ); 
  177.  
  178. if ( empty( $this->curlang ) || empty( $this->options['nav_menus'][ $theme ] ) ) { 
  179. return $args; 
  180.  
  181. // Get the nav menu based on the requested menu 
  182. $menu = wp_get_nav_menu_object( $args['menu'] ); 
  183.  
  184. // Attempt to find a translation of this menu 
  185. // This obviously does not work if the nav menu has no associated theme location 
  186. if ( $menu ) { 
  187. foreach ( $this->options['nav_menus'][ $theme ] as $menus ) { 
  188. if ( in_array( $menu->term_id, $menus ) && ! empty( $menus[ $this->curlang->slug ] ) ) { 
  189. $args['menu'] = $menus[ $this->curlang->slug ]; 
  190. return $args; 
  191.  
  192. // Get the first menu that has items and and is in the current language if we still can't find a menu 
  193. if ( ! $menu && ! $args['theme_location'] ) { 
  194. $menus = wp_get_nav_menus(); 
  195. foreach ( $menus as $menu_maybe ) { 
  196. if ( $menu_items = wp_get_nav_menu_items( $menu_maybe->term_id, array( 'update_post_term_cache' => false ) ) ) { 
  197. foreach ( $this->options['nav_menus'][ $theme ] as $menus ) { 
  198. if ( in_array( $menu_maybe->term_id, $menus ) && ! empty( $menus[ $this->curlang->slug ] ) ) { 
  199. $args['menu'] = $menus[ $this->curlang->slug ]; 
  200. return $args; 
  201.  
  202. return $args; 
  203.  
  204. /** 
  205. * Filters the nav menu location before the customizer so that it matches the temporary location in the customizer 
  206. * @since 1.8 
  207. * @param array $args wp_nav_menu $args 
  208. * @return array modified $args 
  209. */ 
  210. public function filter_args_before_customizer( $args ) { 
  211. if ( ! empty( $this->curlang ) ) { 
  212. $args['theme_location'] = $this->combine_location( $args['theme_location'], $this->curlang ); 
  213. return $args; 
  214.  
  215. /** 
  216. * Filters the nav menu location after the customizer to get back the true nav menu location for the theme 
  217. * @since 1.8 
  218. * @param array $args wp_nav_menu $args 
  219. * @return array modified $args 
  220. */ 
  221. public function filter_args_after_customizer( $args ) { 
  222. $infos = $this->explode_location( $args['theme_location'] ); 
  223. $args['theme_location'] = $infos['location']; 
  224. return $args;