PluginProbe
Polylang / 3.7.1
Polylang v3.7.1
3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0.3 All 233 releases
polylang / admin / admin-nav-menu.php

admin-nav-menu.php in Polylang 3.7.1, at admin/admin-nav-menu.php

295 lines 10.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Polylang
4 */
5
6 /**
7 * Manages custom menus translations as well as the language switcher menu item on admin side
8 *
9 * @since 1.2
10 */
11 class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
12
13 /**
14 * Constructor: setups filters and actions
15 *
16 * @since 1.2
17 *
18 * @param object $polylang The Polylang object.
19 */
20 public function __construct( &$polylang ) {
21 parent::__construct( $polylang );
22
23 // Populates nav menus locations
24 // Since WP 4.4, must be done before customize_register is fired
25 add_filter( 'theme_mod_nav_menu_locations', array( $this, 'theme_mod_nav_menu_locations' ), 20 );
26
27 // Integration in the WP menu interface
28 add_action( 'admin_init', array( $this, 'admin_init' ) ); // after Polylang upgrade
29 }
30
31 /**
32 * Setups filters and terms
33 * adds the language switcher metabox and create new nav menu locations
34 *
35 * @since 1.1
36 *
37 * @return void
38 */
39 public function admin_init() {
40 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
41 add_action( 'wp_update_nav_menu_item', array( $this, 'wp_update_nav_menu_item' ), 10, 2 );
42
43 // Translation of menus based on chosen locations
44 add_filter( 'pre_update_option_theme_mods_' . $this->theme, array( $this, 'pre_update_option_theme_mods' ) );
45 add_action( 'delete_nav_menu', array( $this, 'delete_nav_menu' ) );
46
47 // FIXME is it possible to choose the order ( after theme locations in WP3.5 and older ) ?
48 // FIXME not displayed if Polylang is activated before the first time the user goes to nav menus http://core.trac.wordpress.org/ticket/16828
49 add_meta_box( 'pll_lang_switch_box', __( 'Language switcher', 'polylang' ), array( $this, 'lang_switch' ), 'nav-menus', 'side', 'high' );
50
51 $this->create_nav_menu_locations();
52 }
53
54 /**
55 * Language switcher metabox
56 * The checkbox and all hidden fields are important
57 * Thanks to John Morris for his very interesting post http://www.johnmorrisonline.com/how-to-add-a-fully-functional-custom-meta-box-to-wordpress-navigation-menus/
58 *
59 * @since 1.1
60 *
61 * @return void
62 */
63 public function lang_switch() {
64 global $_nav_menu_placeholder, $nav_menu_selected_id;
65 $_nav_menu_placeholder = 0 > $_nav_menu_placeholder ? $_nav_menu_placeholder - 1 : -1;
66 ?>
67 <div id="posttype-lang-switch" class="posttypediv">
68 <div id="tabs-panel-lang-switch" class="tabs-panel tabs-panel-active">
69 <ul id="lang-switch-checklist" class="categorychecklist form-no-clear">
70 <li>
71 <label class="menu-item-title">
72 <input type="checkbox" class="menu-item-checkbox" name="menu-item[<?php echo (int) $_nav_menu_placeholder; ?>][menu-item-object-id]" value="-1"> <?php esc_html_e( 'Languages', 'polylang' ); ?>
73 </label>
74 <input type="hidden" class="menu-item-type" name="menu-item[<?php echo (int) $_nav_menu_placeholder; ?>][menu-item-type]" value="custom">
75 <input type="hidden" class="menu-item-title" name="menu-item[<?php echo (int) $_nav_menu_placeholder; ?>][menu-item-title]" value="<?php esc_attr_e( 'Languages', 'polylang' ); ?>">
76 <input type="hidden" class="menu-item-url" name="menu-item[<?php echo (int) $_nav_menu_placeholder; ?>][menu-item-url]" value="#pll_switcher">
77 </li>
78 </ul>
79 </div>
80 <p class="button-controls">
81 <span class="add-to-menu">
82 <input type="submit" <?php disabled( $nav_menu_selected_id, 0 ); ?> class="button-secondary submit-add-to-menu right" value="<?php esc_attr_e( 'Add to Menu', 'polylang' ); ?>" name="add-post-type-menu-item" id="submit-posttype-lang-switch">
83 <span class="spinner"></span>
84 </span>
85 </p>
86 </div>
87 <?php
88 }
89
90 /**
91 * Prepares javascript to modify the language switcher menu item
92 *
93 * @since 1.1
94 *
95 * @return void
96 */
97 public function admin_enqueue_scripts() {
98 $screen = get_current_screen();
99 if ( empty( $screen ) || 'nav-menus' !== $screen->base ) {
100 return;
101 }
102
103 $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
104 wp_enqueue_script( 'pll_nav_menu', plugins_url( "/js/build/nav-menu{$suffix}.js", POLYLANG_ROOT_FILE ), array(), POLYLANG_VERSION );
105
106 $data = array(
107 'strings' => PLL_Switcher::get_switcher_options( 'menu', 'string' ), // The strings for the options
108 'title' => __( 'Languages', 'polylang' ), // The title
109 'val' => array(),
110 );
111
112 // Get all language switcher menu items
113 $items = get_posts(
114 array(
115 'numberposts' => -1,
116 'nopaging' => true,
117 'post_type' => 'nav_menu_item',
118 'fields' => 'ids',
119 'meta_key' => '_pll_menu_item',
120 )
121 );
122
123 // The options values for the language switcher
124 foreach ( $items as $item ) {
125 $data['val'][ $item ] = get_post_meta( $item, '_pll_menu_item', true );
126 }
127
128 // Send all these data to javascript
129 wp_localize_script( 'pll_nav_menu', 'pll_data', $data );
130 }
131
132 /**
133 * Save our menu item options.
134 *
135 * @since 1.1
136 *
137 * @param int $menu_id ID of the updated menu.
138 * @param int $menu_item_db_id ID of the updated menu item.
139 * @return void
140 */
141 public function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0 ) {
142 if ( empty( $_POST['menu-item-url'][ $menu_item_db_id ] ) || '#pll_switcher' !== $_POST['menu-item-url'][ $menu_item_db_id ] ) { // phpcs:ignore WordPress.Security.NonceVerification
143 return;
144 }
145
146 // Security check as 'wp_update_nav_menu_item' can be called from outside WP admin
147 if ( current_user_can( 'edit_theme_options' ) ) {
148 check_admin_referer( 'update-nav_menu', 'update-nav-menu-nonce' );
149
150 $options = array( 'hide_if_no_translation' => 0, 'hide_current' => 0, 'force_home' => 0, 'show_flags' => 0, 'show_names' => 1, 'dropdown' => 0 ); // Default values
151 // Our jQuery form has not been displayed
152 if ( empty( $_POST['menu-item-pll-detect'][ $menu_item_db_id ] ) ) {
153 if ( ! get_post_meta( $menu_item_db_id, '_pll_menu_item', true ) ) { // Our options were never saved
154 update_post_meta( $menu_item_db_id, '_pll_menu_item', $options );
155 }
156 }
157 else {
158 foreach ( array_keys( $options ) as $opt ) {
159 $options[ $opt ] = empty( $_POST[ 'menu-item-' . $opt ][ $menu_item_db_id ] ) ? 0 : 1;
160 }
161 update_post_meta( $menu_item_db_id, '_pll_menu_item', $options ); // Allow us to easily identify our nav menu item
162 }
163 }
164 }
165
166 /**
167 * Assigns menu languages and translations based on (temporary) locations.
168 *
169 * @since 1.8
170 *
171 * @param array $locations Nav menu locations.
172 * @return array
173 */
174 public function update_nav_menu_locations( $locations ) {
175 // Extract language and menu from locations.
176 $nav_menus = $this->options->get( 'nav_menus' );
177
178 foreach ( $locations as $loc => $menu ) {
179 $infos = $this->explode_location( $loc );
180 $nav_menus[ $this->theme ][ $infos['location'] ][ $infos['lang'] ] = $menu;
181
182 if ( $this->options->get( 'default_lang' ) !== $infos['lang'] ) {
183 unset( $locations[ $loc ] ); // Remove temporary locations before database update.
184 }
185 }
186
187 $this->options->set( 'nav_menus', $nav_menus );
188
189 return $locations;
190 }
191
192 /**
193 * Assigns menu languages and translations based on (temporary) locations.
194 *
195 * @since 1.1
196 *
197 * @param mixed $mods Theme mods.
198 * @return mixed
199 */
200 public function pre_update_option_theme_mods( $mods ) {
201 if ( current_user_can( 'edit_theme_options' ) && is_array( $mods ) && isset( $mods['nav_menu_locations'] ) ) {
202
203 // Manage Locations tab in Appearance -> Menus
204 if ( isset( $_GET['action'] ) && 'locations' === $_GET['action'] ) { // phpcs:ignore WordPress.Security.NonceVerification
205 check_admin_referer( 'save-menu-locations' );
206
207 $nav_menus = $this->options->get( 'nav_menus' );
208 $nav_menus[ $this->theme ] = array();
209 $this->options->set( 'nav_menus', $nav_menus );
210 }
211
212 // Edit Menus tab in Appearance -> Menus
213 // Add the test of $_POST['update-nav-menu-nonce'] to avoid conflict with Vantage theme
214 elseif ( isset( $_POST['action'], $_POST['update-nav-menu-nonce'] ) && 'update' === $_POST['action'] ) {
215 check_admin_referer( 'update-nav_menu', 'update-nav-menu-nonce' );
216
217 $nav_menus = $this->options->get( 'nav_menus' );
218 $nav_menus[ $this->theme ] = array();
219 $this->options->set( 'nav_menus', $nav_menus );
220 }
221
222 // Customizer
223 // Don't reset locations in this case.
224 // see http://wordpress.org/support/topic/menus-doesnt-show-and-not-saved-in-theme-settings-multilingual-site
225 elseif ( isset( $_POST['action'] ) && 'customize_save' == $_POST['action'] ) {
226 check_ajax_referer( 'save-customize_' . $GLOBALS['wp_customize']->get_stylesheet(), 'nonce' );
227 }
228
229 else {
230 return $mods; // No modification for nav menu locations
231 }
232
233 $mods['nav_menu_locations'] = $this->update_nav_menu_locations( $mods['nav_menu_locations'] );
234 }
235 return $mods;
236 }
237
238 /**
239 * Fills temporary menu locations based on menus translations
240 *
241 * @since 1.2
242 *
243 * @param bool|array $menus Associative array of registered navigation menu IDs keyed by their location name.
244 * @return bool|array
245 */
246 public function theme_mod_nav_menu_locations( $menus ) {
247 // Prefill locations with 0 value in case a location does not exist in $menus
248 $locations = get_registered_nav_menus();
249 if ( is_array( $locations ) ) {
250 $locations = array_fill_keys( array_keys( $locations ), 0 );
251 $menus = is_array( $menus ) ? array_merge( $locations, $menus ) : $locations;
252 }
253
254 if ( is_array( $menus ) ) {
255 foreach ( array_keys( $menus ) as $loc ) {
256 foreach ( $this->model->get_languages_list() as $lang ) {
257 if ( ! empty( $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ] ) && term_exists( $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ], 'nav_menu' ) ) {
258 $menus[ $this->combine_location( $loc, $lang ) ] = $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ];
259 }
260 }
261 }
262 }
263
264 return $menus;
265 }
266
267 /**
268 * Removes the nav menu term_id from the locations stored in Polylang options when a nav menu is deleted
269 *
270 * @since 1.7.3
271 *
272 * @param int $term_id nav menu id
273 * @return void
274 */
275 public function delete_nav_menu( $term_id ) {
276 $nav_menus = $this->options->get( 'nav_menus' );
277
278 if ( empty( $nav_menus ) ) {
279 return;
280 }
281
282 foreach ( $nav_menus as $theme => $locations ) {
283 foreach ( $locations as $loc => $languages ) {
284 foreach ( $languages as $lang => $menu_id ) {
285 if ( $menu_id === $term_id ) {
286 unset( $nav_menus[ $theme ][ $loc ][ $lang ] );
287 }
288 }
289 }
290 }
291
292 $this->options->set( 'nav_menus', $nav_menus );
293 }
294 }
295