PluginProbe
Polylang / 3.8.6
Polylang v3.8.6
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 / src / admin / admin-nav-menu.php

admin-nav-menu.php in Polylang 3.8.6, at src/admin/admin-nav-menu.php

316 lines 10.5 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 PLL_Admin $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 add_action( 'load-nav-menus.php', array( $this, 'add_meta_box' ) );
30 }
31
32 /**
33 * Setups filters and terms 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 $this->create_nav_menu_locations();
48 }
49
50 /**
51 * Adds the language switcher metabox.
52 *
53 * @since 3.7.7
54 *
55 * @return void
56 */
57 public function add_meta_box() {
58 // FIXME not displayed if Polylang is activated before the first time the user goes to nav menus http://core.trac.wordpress.org/ticket/16828
59 add_meta_box( 'pll_lang_switch_box', __( 'Language switcher', 'polylang' ), array( $this, 'lang_switch' ), 'nav-menus', 'side', 'high' );
60 }
61
62 /**
63 * Language switcher metabox
64 * The checkbox and all hidden fields are important
65 * 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/
66 *
67 * @since 1.1
68 *
69 * @return void
70 */
71 public function lang_switch() {
72 global $_nav_menu_placeholder, $nav_menu_selected_id;
73 $_nav_menu_placeholder = 0 > $_nav_menu_placeholder ? $_nav_menu_placeholder - 1 : -1;
74 ?>
75 <div id="posttype-lang-switch" class="posttypediv">
76 <div id="tabs-panel-lang-switch" class="tabs-panel tabs-panel-active">
77 <ul id="lang-switch-checklist" class="categorychecklist form-no-clear">
78 <li>
79 <label class="menu-item-title">
80 <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' ); ?>
81 </label>
82 <input type="hidden" class="menu-item-type" name="menu-item[<?php echo (int) $_nav_menu_placeholder; ?>][menu-item-type]" value="custom">
83 <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' ); ?>">
84 <input type="hidden" class="menu-item-url" name="menu-item[<?php echo (int) $_nav_menu_placeholder; ?>][menu-item-url]" value="#pll_switcher">
85 </li>
86 </ul>
87 </div>
88 <p class="button-controls">
89 <span class="add-to-menu">
90 <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">
91 <span class="spinner"></span>
92 </span>
93 </p>
94 </div>
95 <?php
96 }
97
98 /**
99 * Prepares javascript to modify the language switcher menu item
100 *
101 * @since 1.1
102 *
103 * @return void
104 */
105 public function admin_enqueue_scripts() {
106 $screen = get_current_screen();
107 if ( empty( $screen ) || 'nav-menus' !== $screen->base ) {
108 return;
109 }
110
111 $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
112 wp_enqueue_script( 'pll_nav_menu', plugins_url( "/js/build/nav-menu{$suffix}.js", POLYLANG_ROOT_FILE ), array(), POLYLANG_VERSION );
113
114 $data = array(
115 'strings' => PLL_Switcher::get_switcher_options( 'menu', 'string' ), // The strings for the options
116 'title' => __( 'Languages', 'polylang' ), // The title
117 'val' => array(),
118 );
119
120 // Get all language switcher menu items
121 $items = get_posts(
122 array(
123 'numberposts' => -1,
124 'nopaging' => true,
125 'post_type' => 'nav_menu_item',
126 'fields' => 'ids',
127 'meta_key' => '_pll_menu_item',
128 )
129 );
130
131 // The options values for the language switcher
132 foreach ( $items as $item ) {
133 $data['val'][ $item ] = get_post_meta( $item, '_pll_menu_item', true );
134 }
135
136 // Send all these data to javascript
137 wp_localize_script( 'pll_nav_menu', 'pll_data', $data );
138 }
139
140 /**
141 * Save our menu item options.
142 *
143 * @since 1.1
144 *
145 * @param int $menu_id ID of the updated menu.
146 * @param int $menu_item_db_id ID of the updated menu item.
147 * @return void
148 */
149 public function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0 ) {
150 if ( ! current_user_can( 'edit_theme_options' ) ) {
151 return;
152 }
153
154 if ( ! isset( $_REQUEST['update-nav-menu-nonce'] ) || ! wp_verify_nonce( $_REQUEST['update-nav-menu-nonce'], 'update-nav_menu' ) ) {
155 return;
156 }
157
158 if ( empty( $_POST['menu-item-url'] ) || ! is_array( $_POST['menu-item-url'] ) || empty( $_POST['menu-item-url'][ $menu_item_db_id ] ) || '#pll_switcher' !== $_POST['menu-item-url'][ $menu_item_db_id ] ) {
159 return;
160 }
161
162 $options = array( 'hide_if_no_translation' => 0, 'hide_current' => 0, 'force_home' => 0, 'show_flags' => 0, 'show_names' => 1, 'dropdown' => 0 ); // Default values.
163
164 // Our jQuery form has not been displayed.
165 if ( empty( $_POST['menu-item-pll-detect'][ $menu_item_db_id ] ) ) {
166 if ( ! get_post_meta( $menu_item_db_id, '_pll_menu_item', true ) ) { // Our options were never saved.
167 update_post_meta( $menu_item_db_id, '_pll_menu_item', $options );
168 }
169 } else {
170 foreach ( array_keys( $options ) as $opt ) {
171 $options[ $opt ] = empty( $_POST[ 'menu-item-' . $opt ][ $menu_item_db_id ] ) ? 0 : 1;
172 }
173 update_post_meta( $menu_item_db_id, '_pll_menu_item', $options ); // Allow us to easily identify our nav menu item.
174 }
175 }
176
177 /**
178 * Assigns menu languages and translations based on (temporary) locations.
179 *
180 * @since 1.8
181 *
182 * @param array $locations Nav menu locations.
183 * @return array
184 */
185 public function update_nav_menu_locations( $locations ) {
186 // Extract language and menu from locations.
187 $nav_menus = $this->options->get( 'nav_menus' );
188
189 foreach ( $locations as $loc => $menu ) {
190 $infos = $this->explode_location( $loc );
191 $nav_menus[ $this->theme ][ $infos['location'] ][ $infos['lang'] ] = $menu ?: 0;
192
193 if ( $this->options->get( 'default_lang' ) !== $infos['lang'] ) {
194 unset( $locations[ $loc ] ); // Remove temporary locations before database update.
195 }
196 }
197
198 $this->options->set( 'nav_menus', $nav_menus );
199
200 return $locations;
201 }
202
203 /**
204 * Assigns menu languages and translations based on (temporary) locations.
205 *
206 * @since 1.1
207 *
208 * @param mixed $mods Theme mods.
209 * @return mixed
210 */
211 public function pre_update_option_theme_mods( $mods ) {
212 global $wp_customize;
213
214 if ( ! current_user_can( 'edit_theme_options' ) || ! is_array( $mods ) || ! isset( $mods['nav_menu_locations'] ) || ! is_array( $mods['nav_menu_locations'] ) ) {
215 return $mods;
216 }
217
218 /*
219 * Manage Locations tab in Appearance -> Menus.
220 */
221 if ( isset( $_GET['action'], $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'save-menu-locations' ) && 'locations' === $_GET['action'] ) {
222 $nav_menus = $this->options->get( 'nav_menus' );
223
224 $nav_menus[ $this->theme ] = array();
225 $this->options->set( 'nav_menus', $nav_menus );
226
227 $mods['nav_menu_locations'] = $this->update_nav_menu_locations( $mods['nav_menu_locations'] );
228 return $mods;
229 }
230
231 /*
232 * Edit Menus tab in Appearance -> Menus.
233 */
234 if ( isset( $_POST['action'], $_REQUEST['update-nav-menu-nonce'] ) && wp_verify_nonce( $_REQUEST['update-nav-menu-nonce'], 'update-nav_menu' ) && 'update' === $_POST['action'] ) {
235 $nav_menus = $this->options->get( 'nav_menus' );
236
237 $nav_menus[ $this->theme ] = array();
238 $this->options->set( 'nav_menus', $nav_menus );
239
240 $mods['nav_menu_locations'] = $this->update_nav_menu_locations( $mods['nav_menu_locations'] );
241 return $mods;
242 }
243
244 if ( ! $wp_customize instanceof WP_Customize_Manager ) {
245 return $mods;
246 }
247
248 /*
249 * Customizer. Don't reset locations in this case.
250 */
251 $action = 'save-customize_' . $wp_customize->get_stylesheet();
252 if ( isset( $_POST['action'], $_REQUEST['nonce'] ) && wp_verify_nonce( $_REQUEST['nonce'], $action ) && 'customize_save' == $_POST['action'] ) {
253 $mods['nav_menu_locations'] = $this->update_nav_menu_locations( $mods['nav_menu_locations'] );
254 }
255
256 return $mods;
257 }
258
259 /**
260 * Fills temporary menu locations based on menus translations
261 *
262 * @since 1.2
263 *
264 * @param bool|array $menus Associative array of registered navigation menu IDs keyed by their location name.
265 * @return bool|array
266 */
267 public function theme_mod_nav_menu_locations( $menus ) {
268 // Prefill locations with 0 value in case a location does not exist in $menus
269 $locations = get_registered_nav_menus();
270 if ( is_array( $locations ) ) {
271 $locations = array_fill_keys( array_keys( $locations ), 0 );
272 $menus = is_array( $menus ) ? array_merge( $locations, $menus ) : $locations;
273 }
274
275 if ( is_array( $menus ) ) {
276 foreach ( array_keys( $menus ) as $loc ) {
277 foreach ( $this->model->get_languages_list() as $lang ) {
278 if ( ! empty( $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ] ) && term_exists( $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ], 'nav_menu' ) ) {
279 $menus[ $this->combine_location( $loc, $lang ) ] = $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ];
280 }
281 }
282 }
283 }
284
285 return $menus;
286 }
287
288 /**
289 * Removes the nav menu term_id from the locations stored in Polylang options when a nav menu is deleted
290 *
291 * @since 1.7.3
292 *
293 * @param int $term_id nav menu id
294 * @return void
295 */
296 public function delete_nav_menu( $term_id ) {
297 $nav_menus = $this->options->get( 'nav_menus' );
298
299 if ( empty( $nav_menus ) ) {
300 return;
301 }
302
303 foreach ( $nav_menus as $theme => $locations ) {
304 foreach ( $locations as $loc => $languages ) {
305 foreach ( $languages as $lang => $menu_id ) {
306 if ( $menu_id === $term_id ) {
307 unset( $nav_menus[ $theme ][ $loc ][ $lang ] );
308 }
309 }
310 }
311 }
312
313 $this->options->set( 'nav_menus', $nav_menus );
314 }
315 }
316