PluginProbe
Polylang / trunk
Polylang vtrunk
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 trunk, at src/admin/admin-nav-menu.php

351 lines 11.1 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 use WP_Syntex\Polylang\Switcher\Fields\Menu as Fields;
7 use WP_Syntex\Polylang\Switcher\Settings\Menu as Settings;
8
9 /**
10 * Manages custom menus translations as well as the language switcher menu item on admin side
11 *
12 * @since 1.2
13 */
14 class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
15
16 /**
17 * Constructor: setups filters and actions
18 *
19 * @since 1.2
20 *
21 * @param PLL_Admin $polylang The Polylang object.
22 */
23 public function __construct( &$polylang ) {
24 parent::__construct( $polylang );
25
26 // Populates nav menus locations
27 // Since WP 4.4, must be done before customize_register is fired
28 add_filter( 'theme_mod_nav_menu_locations', array( $this, 'theme_mod_nav_menu_locations' ), 20 );
29
30 // Integration in the WP menu interface.
31 add_action( 'admin_init', array( $this, 'admin_init' ) ); // after Polylang upgrade.
32 add_action( 'load-nav-menus.php', array( $this, 'add_meta_box' ) );
33 }
34
35 /**
36 * Setups filters and terms and create new nav menu locations.
37 *
38 * @since 1.1
39 *
40 * @return void
41 */
42 public function admin_init() {
43 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
44 add_action( 'wp_update_nav_menu_item', array( $this, 'wp_update_nav_menu_item' ), 10, 2 );
45
46 // Translation of menus based on chosen locations.
47 add_filter( 'pre_update_option_theme_mods_' . $this->theme, array( $this, 'pre_update_option_theme_mods' ) );
48 add_action( 'delete_nav_menu', array( $this, 'delete_nav_menu' ) );
49
50 $this->create_nav_menu_locations();
51 }
52
53 /**
54 * Adds the language switcher metabox.
55 *
56 * @since 3.7.7
57 *
58 * @return void
59 */
60 public function add_meta_box() {
61 // FIXME not displayed if Polylang is activated before the first time the user goes to nav menus http://core.trac.wordpress.org/ticket/16828
62 add_meta_box( 'pll_lang_switch_box', __( 'Language switcher', 'polylang' ), array( $this, 'lang_switch' ), 'nav-menus', 'side', 'high' );
63 }
64
65 /**
66 * Language switcher metabox
67 * The checkbox and all hidden fields are important
68 * 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/
69 *
70 * @since 1.1
71 *
72 * @return void
73 */
74 public function lang_switch() {
75 global $_nav_menu_placeholder, $nav_menu_selected_id;
76 $_nav_menu_placeholder = 0 > $_nav_menu_placeholder ? $_nav_menu_placeholder - 1 : -1;
77 ?>
78 <div id="posttype-lang-switch" class="posttypediv">
79 <div id="tabs-panel-lang-switch" class="tabs-panel tabs-panel-active">
80 <ul id="lang-switch-checklist" class="categorychecklist form-no-clear">
81 <li>
82 <label class="menu-item-title">
83 <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' ); ?>
84 </label>
85 <input type="hidden" class="menu-item-type" name="menu-item[<?php echo (int) $_nav_menu_placeholder; ?>][menu-item-type]" value="custom">
86 <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' ); ?>">
87 <input type="hidden" class="menu-item-url" name="menu-item[<?php echo (int) $_nav_menu_placeholder; ?>][menu-item-url]" value="#pll_switcher">
88 </li>
89 </ul>
90 </div>
91 <p class="button-controls">
92 <span class="add-to-menu">
93 <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">
94 <span class="spinner"></span>
95 </span>
96 </p>
97 </div>
98 <?php
99 }
100
101 /**
102 * Prepares javascript to modify the language switcher menu item
103 *
104 * @since 1.1
105 *
106 * @return void
107 */
108 public function admin_enqueue_scripts() {
109 $screen = get_current_screen();
110 if ( empty( $screen ) || 'nav-menus' !== $screen->base ) {
111 return;
112 }
113
114 $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
115 wp_enqueue_script( 'pll_nav_menu', plugins_url( "/js/build/nav-menu{$suffix}.js", POLYLANG_ROOT_FILE ), array(), POLYLANG_VERSION );
116
117 $fields = Fields::get();
118 $defaults = new Settings( array() );
119
120 foreach ( $fields as $key => $field ) {
121 $fields[ $key ]['default'] = $defaults->$key;
122 }
123
124 $data = array(
125 'data' => $fields,
126 'title' => __( 'Languages', 'polylang' ),
127 'val' => array(),
128 );
129
130 // Get all language switcher menu items
131 $items = get_posts(
132 array(
133 'numberposts' => -1,
134 'nopaging' => true,
135 'post_type' => 'nav_menu_item',
136 'fields' => 'ids',
137 'meta_key' => '_pll_menu_item',
138 )
139 );
140
141 // The options values for the language switcher
142 foreach ( $items as $item ) {
143 $meta = get_post_meta( $item, '_pll_menu_item', true );
144
145 if ( ! is_array( $meta ) ) {
146 continue;
147 }
148
149 $data['val'][ $item ] = Fields::filter( new Settings( Fields::remove_legacy_settings( $meta ) ) );
150 }
151
152 // Send all these data to javascript
153 wp_localize_script( 'pll_nav_menu', 'pll_data', $data );
154 }
155
156 /**
157 * Save our menu item options.
158 *
159 * @since 1.1
160 *
161 * @param int $menu_id ID of the updated menu.
162 * @param int $menu_item_db_id ID of the updated menu item.
163 * @return void
164 */
165 public function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0 ) {
166 if ( ! current_user_can( 'edit_theme_options' ) ) {
167 return;
168 }
169
170 if ( ! isset( $_REQUEST['update-nav-menu-nonce'] ) || ! wp_verify_nonce( $_REQUEST['update-nav-menu-nonce'], 'update-nav_menu' ) ) {
171 return;
172 }
173
174 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 ] ) {
175 return;
176 }
177
178 // Our JS form has not been displayed.
179 if ( empty( $_POST['menu-item-pll-detect'][ $menu_item_db_id ] ) ) {
180 if ( ! get_post_meta( $menu_item_db_id, '_pll_menu_item', true ) ) { // Our options were never saved.
181 $this->update_menu_meta( $menu_item_db_id, array() );
182 }
183 return;
184 }
185
186 $meta = array();
187
188 foreach ( Fields::get() as $name => $field ) {
189 if ( isset( $_POST[ "menu-item-{$name}" ][ $menu_item_db_id ] ) ) {
190 $meta[ $name ] = $_POST[ "menu-item-{$name}" ][ $menu_item_db_id ]; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
191 }
192 }
193
194 $this->update_menu_meta( $menu_item_db_id, $meta );
195 }
196
197 /**
198 * Assigns menu languages and translations based on (temporary) locations.
199 *
200 * @since 1.8
201 *
202 * @param array $locations Nav menu locations.
203 * @return array
204 */
205 public function update_nav_menu_locations( $locations ) {
206 // Extract language and menu from locations.
207 $nav_menus = $this->options->get( 'nav_menus' );
208
209 foreach ( $locations as $loc => $menu ) {
210 $infos = $this->explode_location( $loc );
211 $nav_menus[ $this->theme ][ $infos['location'] ][ $infos['lang'] ] = $menu ?: 0;
212
213 if ( $this->options->get( 'default_lang' ) !== $infos['lang'] ) {
214 unset( $locations[ $loc ] ); // Remove temporary locations before database update.
215 }
216 }
217
218 $this->options->set( 'nav_menus', $nav_menus );
219
220 return $locations;
221 }
222
223 /**
224 * Assigns menu languages and translations based on (temporary) locations.
225 *
226 * @since 1.1
227 *
228 * @param mixed $mods Theme mods.
229 * @return mixed
230 */
231 public function pre_update_option_theme_mods( $mods ) {
232 global $wp_customize;
233
234 if ( ! current_user_can( 'edit_theme_options' ) || ! is_array( $mods ) || ! isset( $mods['nav_menu_locations'] ) || ! is_array( $mods['nav_menu_locations'] ) ) {
235 return $mods;
236 }
237
238 /*
239 * Manage Locations tab in Appearance -> Menus.
240 */
241 if ( isset( $_GET['action'], $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'save-menu-locations' ) && 'locations' === $_GET['action'] ) {
242 $nav_menus = $this->options->get( 'nav_menus' );
243
244 $nav_menus[ $this->theme ] = array();
245 $this->options->set( 'nav_menus', $nav_menus );
246
247 $mods['nav_menu_locations'] = $this->update_nav_menu_locations( $mods['nav_menu_locations'] );
248 return $mods;
249 }
250
251 /*
252 * Edit Menus tab in Appearance -> Menus.
253 */
254 if ( isset( $_POST['action'], $_REQUEST['update-nav-menu-nonce'] ) && wp_verify_nonce( $_REQUEST['update-nav-menu-nonce'], 'update-nav_menu' ) && 'update' === $_POST['action'] ) {
255 $nav_menus = $this->options->get( 'nav_menus' );
256
257 $nav_menus[ $this->theme ] = array();
258 $this->options->set( 'nav_menus', $nav_menus );
259
260 $mods['nav_menu_locations'] = $this->update_nav_menu_locations( $mods['nav_menu_locations'] );
261 return $mods;
262 }
263
264 if ( ! $wp_customize instanceof WP_Customize_Manager ) {
265 return $mods;
266 }
267
268 /*
269 * Customizer. Don't reset locations in this case.
270 */
271 $action = 'save-customize_' . $wp_customize->get_stylesheet();
272 if ( isset( $_POST['action'], $_REQUEST['nonce'] ) && wp_verify_nonce( $_REQUEST['nonce'], $action ) && 'customize_save' == $_POST['action'] ) {
273 $mods['nav_menu_locations'] = $this->update_nav_menu_locations( $mods['nav_menu_locations'] );
274 }
275
276 return $mods;
277 }
278
279 /**
280 * Fills temporary menu locations based on menus translations
281 *
282 * @since 1.2
283 *
284 * @param bool|array $menus Associative array of registered navigation menu IDs keyed by their location name.
285 * @return bool|array
286 */
287 public function theme_mod_nav_menu_locations( $menus ) {
288 // Prefill locations with 0 value in case a location does not exist in $menus
289 $locations = get_registered_nav_menus();
290 if ( is_array( $locations ) ) {
291 $locations = array_fill_keys( array_keys( $locations ), 0 );
292 $menus = is_array( $menus ) ? array_merge( $locations, $menus ) : $locations;
293 }
294
295 if ( is_array( $menus ) ) {
296 foreach ( array_keys( $menus ) as $loc ) {
297 foreach ( $this->model->get_languages_list() as $lang ) {
298 if ( ! empty( $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ] ) && term_exists( $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ], 'nav_menu' ) ) {
299 $menus[ $this->combine_location( $loc, $lang ) ] = $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ];
300 }
301 }
302 }
303 }
304
305 return $menus;
306 }
307
308 /**
309 * Removes the nav menu term_id from the locations stored in Polylang options when a nav menu is deleted
310 *
311 * @since 1.7.3
312 *
313 * @param int $term_id nav menu id
314 * @return void
315 */
316 public function delete_nav_menu( $term_id ) {
317 $nav_menus = $this->options->get( 'nav_menus' );
318
319 if ( empty( $nav_menus ) ) {
320 return;
321 }
322
323 foreach ( $nav_menus as $theme => $locations ) {
324 foreach ( $locations as $loc => $languages ) {
325 foreach ( $languages as $lang => $menu_id ) {
326 if ( $menu_id === $term_id ) {
327 unset( $nav_menus[ $theme ][ $loc ][ $lang ] );
328 }
329 }
330 }
331 }
332
333 $this->options->set( 'nav_menus', $nav_menus );
334 }
335
336 /**
337 * Stores the menu data into the database.
338 *
339 * @since 3.9
340 *
341 * @param int $menu_id Menu ID.
342 * @param array $data Data to store.
343 * @return void
344 */
345 private function update_menu_meta( int $menu_id, array $data ): void {
346 $validated = Fields::filter( new Settings( $data ) );
347 $validated = Fields::add_legacy_settings( $validated );
348 update_post_meta( $menu_id, '_pll_menu_item', $validated );
349 }
350 }
351