PluginProbe
Polylang / 2.5.2
Polylang v2.5.2
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 / install / upgrade.php

upgrade.php in Polylang 2.5.2, at install/upgrade.php

612 lines 19.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Manages Polylang upgrades
5 *
6 * @since 1.2
7 */
8 class PLL_Upgrade {
9 public $options;
10
11 /**
12 * Constructor
13 *
14 * @since 1.2
15 *
16 * @param array $options Polylang options
17 */
18 public function __construct( &$options ) {
19 $this->options = &$options;
20 }
21
22 /**
23 * Check if upgrade is possible otherwise die to avoid activation
24 *
25 * @since 1.2
26 */
27 public function can_activate() {
28 if ( ! $this->can_upgrade() ) {
29 ob_start();
30 $this->admin_notices(); // FIXME the error message is displayed two times
31 die( ob_get_contents() );
32 }
33 }
34
35 /**
36 * Upgrades if possible otherwise returns false to stop Polylang loading
37 *
38 * @since 1.2
39 *
40 * @return bool true if upgrade is possible, false otherwise
41 */
42 public function upgrade() {
43 if ( ! $this->can_upgrade() ) {
44 add_action( 'all_admin_notices', array( $this, 'admin_notices' ) );
45 return false;
46 }
47
48 add_action( 'admin_init', array( $this, '_upgrade' ) );
49 return true;
50 }
51
52
53 /**
54 * Check if we the previous version is not too old
55 * Upgrades if OK
56 * /!\ never start any upgrade before admin_init as it is likely to conflict with some other plugins
57 *
58 * @since 1.2
59 *
60 * @return bool true if upgrade is possible, false otherwise
61 */
62 public function can_upgrade() {
63 // Don't manage upgrade from version < 0.8
64 return version_compare( $this->options['version'], '0.8', '>=' );
65 }
66
67 /**
68 * Displays a notice when ugrading from a too old version
69 *
70 * @since 1.0
71 */
72 public function admin_notices() {
73 load_plugin_textdomain( 'polylang', false, basename( POLYLANG_DIR ) . '/languages' );
74 printf(
75 '<div class="error"><p>%s</p><p>%s</p></div>',
76 esc_html__( 'Polylang has been deactivated because you upgraded from a too old version.', 'polylang' ),
77 sprintf(
78 /* translators: %1$s and %2$s are Polylang version numbers */
79 esc_html__( 'Please upgrade first to %1$s before ugrading to %2$s.', 'polylang' ),
80 '<strong>0.9.8</strong>',
81 POLYLANG_VERSION
82 )
83 );
84 }
85
86 /**
87 * Upgrades the plugin depending on the previous version
88 *
89 * @since 1.2
90 */
91 public function _upgrade() {
92 foreach ( array( '0.9', '1.0', '1.1', '1.2', '1.2.1', '1.2.3', '1.3', '1.4', '1.4.1', '1.4.4', '1.5', '1.6', '1.7.4', '1.8', '2.0.8', '2.1', '2.3' ) as $version ) {
93 if ( version_compare( $this->options['version'], $version, '<' ) ) {
94 call_user_func( array( $this, 'upgrade_' . str_replace( '.', '_', $version ) ) );
95 }
96 }
97
98 $delete_pre_1_2_data = get_transient( 'pll_upgrade_1_4' );
99 if ( false !== $delete_pre_1_2_data && absint( $delete_pre_1_2_data ) < time() ) {
100 $this->delete_pre_1_2_data();
101 }
102
103 $this->options['previous_version'] = $this->options['version']; // Remember the previous version of Polylang since v1.7.7
104 $this->options['version'] = POLYLANG_VERSION;
105 update_option( 'polylang', $this->options );
106 }
107
108 /**
109 * Upgrades if the previous version is < 0.9
110 *
111 * @since 1.2
112 */
113 protected function upgrade_0_9() {
114 $this->options['sync'] = defined( 'PLL_SYNC' ) && ! PLL_SYNC ? 0 : 1; // The option replaces PLL_SYNC in 0.9
115 }
116
117 /**
118 * Upgrades if the previous version is < 1.0
119 *
120 * @since 1.2
121 */
122 protected function upgrade_1_0() {
123 // The option replaces PLL_MEDIA_SUPPORT in 1.0
124 $this->options['media_support'] = defined( 'PLL_MEDIA_SUPPORT' ) && ! PLL_MEDIA_SUPPORT ? 0 : 1;
125
126 // Split the synchronization options in 1.0
127 $this->options['sync'] = empty( $this->options['sync'] ) ? array() : array_keys( PLL_Settings_Sync::list_metas_to_sync() );
128
129 // Set default values for post types and taxonomies to translate
130 $this->options['post_types'] = array_values( get_post_types( array( '_builtin' => false, 'show_ui' => true ) ) );
131 $this->options['taxonomies'] = array_values( get_taxonomies( array( '_builtin' => false, 'show_ui' => true ) ) );
132 update_option( 'polylang', $this->options );
133
134 flush_rewrite_rules(); // Rewrite rules have been modified in 1.0
135 }
136
137 /**
138 * Upgrades if the previous version is < 1.1
139 *
140 * @since 1.2
141 */
142 protected function upgrade_1_1() {
143 // Update strings register with icl_register_string
144 $strings = get_option( 'polylang_wpml_strings' );
145 if ( $strings ) {
146 foreach ( $strings as $key => $string ) {
147 $strings[ $key ]['icl'] = 1;
148 }
149 update_option( 'polylang_wpml_strings', $strings );
150 }
151
152 // Move polylang_widgets options
153 if ( $widgets = get_option( 'polylang_widgets' ) ) {
154 $this->options['widgets'] = $widgets;
155 delete_option( 'polylang_widgets' );
156 }
157 }
158
159 /**
160 * Upgrades if the previous version is < 1.2
161 *
162 * @since 1.2
163 */
164 protected function upgrade_1_2() {
165 $this->options['domains'] = array(); // Option added in 1.2
166
167 // Need to register the taxonomies
168 foreach ( array( 'language', 'term_language', 'post_translations', 'term_translations' ) as $taxonomy ) {
169 register_taxonomy( $taxonomy, null, array( 'label' => false, 'public' => false, 'query_var' => false, 'rewrite' => false ) );
170 }
171
172 // Abort if the db upgrade has already been done previously
173 if ( get_terms( 'term_language', array( 'hide_empty' => 0 ) ) ) {
174 return;
175 }
176
177 set_time_limit( 0 ); // In case we upgrade a huge site
178
179 // Upgrade old model based on metas to new model based on taxonomies
180 global $wpdb;
181 $wpdb->termmeta = $wpdb->prefix . 'termmeta'; // Registers the termmeta table in wpdb
182 $languages = get_terms( 'language', array( 'hide_empty' => 0 ) ); // Don't use get_languages_list which can't work with the old model
183
184 foreach ( $languages as $lang ) {
185 // First update language with new storage for locale and text direction
186 $text_direction = get_metadata( 'term', $lang->term_id, '_rtl', true );
187 $desc = serialize( array( 'locale' => $lang->description, 'rtl' => $text_direction ) );
188 wp_update_term( (int) $lang->term_id, 'language', array( 'description' => $desc ) );
189
190 // Add language to new 'term_language' taxonomy
191 $term_lang = wp_insert_term( $lang->name, 'term_language', array( 'slug' => 'pll_' . $lang->slug ) );
192 $lang_tt_ids[ $lang->term_id ] = $term_lang['term_taxonomy_id']; // Keep the term taxonomy id for future
193 }
194
195 // Get all terms with a language defined
196 $terms = $wpdb->get_results( "SELECT term_id, meta_value FROM {$wpdb->termmeta} WHERE meta_key = '_language'" );
197 foreach ( $terms as $key => $term ) {
198 $terms[ $key ] = $wpdb->prepare( '( %d, %d )', $term->term_id, $lang_tt_ids[ $term->meta_value ] );
199 }
200
201 $terms = array_unique( $terms );
202
203 // Assign language to each term
204 if ( ! empty( $terms ) ) {
205 // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
206 $wpdb->query( "INSERT INTO {$wpdb->term_relationships} ( object_id, term_taxonomy_id ) VALUES " . implode( ',', $terms ) );
207 }
208
209 // Translations
210 foreach ( array( 'post', 'term' ) as $type ) {
211 $table = $type . 'meta';
212 $terms = $slugs = $tts = $trs = array();
213
214 // Get all translated objects
215 // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
216 $objects = $wpdb->get_col( "SELECT DISTINCT meta_value FROM {$wpdb->$table} WHERE meta_key = '_translations'" );
217
218 if ( empty( $objects ) ) {
219 continue;
220 }
221
222 foreach ( $objects as $obj ) {
223 $term = uniqid( 'pll_' ); // The term name
224 $terms[] = $wpdb->prepare( '( %s, %s )', $term, $term );
225 $slugs[] = $wpdb->prepare( '%s', $term );
226 $translations = maybe_unserialize( maybe_unserialize( $obj ) ); // 2 unserialize due to an old storage bug
227 $description[ $term ] = serialize( $translations );
228 }
229
230 $terms = array_unique( $terms );
231
232 // Insert terms
233 if ( ! empty( $terms ) ) {
234 // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
235 $wpdb->query( "INSERT INTO {$wpdb->terms} ( slug, name ) VALUES " . implode( ',', $terms ) );
236 }
237
238 // Get all terms with their term_id
239 // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
240 $terms = $wpdb->get_results( "SELECT term_id, slug FROM $wpdb->terms WHERE slug IN ( " . implode( ',', $slugs ) . ' )' );
241
242 // Prepare terms taxonomy relationship
243 foreach ( $terms as $term ) {
244 $tts[] = $wpdb->prepare( '( %d, %s, %s )', $term->term_id, $type . '_translations', $description[ $term->slug ] );
245 }
246
247 $tts = array_unique( $tts );
248
249 // Insert term_taxonomy
250 if ( ! empty( $tts ) ) {
251 // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
252 $wpdb->query( "INSERT INTO {$wpdb->term_taxonomy} ( term_id, taxonomy, description ) VALUES " . implode( ',', $tts ) );
253 }
254
255 // Get all terms with term_taxonomy_id
256 $terms = get_terms( $type . '_translations', array( 'hide_empty' => false ) );
257
258 // Prepare objects relationships
259 foreach ( $terms as $term ) {
260 $translations = unserialize( $term->description );
261 foreach ( $translations as $object_id ) {
262 if ( ! empty( $object_id ) ) {
263 $trs[] = $wpdb->prepare( '( %d, %d )', $object_id, $term->term_taxonomy_id );
264 }
265 }
266 }
267
268 $trs = array_unique( $trs );
269
270 // Insert term_relationships
271 if ( ! empty( $trs ) ) {
272 // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
273 $wpdb->query( "INSERT INTO {$wpdb->term_relationships} ( object_id, term_taxonomy_id ) VALUES " . implode( ',', $trs ) );
274 }
275 }
276
277 // Upgrade of string translations is now in upgrade_1_2_1
278 // Upgrade of nav menus is now in upgrade_1_2_3
279 }
280
281 /**
282 * Upgrades if the previous version is < 1.2.1
283 *
284 * @since 1.2.1
285 */
286 protected function upgrade_1_2_1() {
287 // Strings translations
288 foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
289 if ( $strings = get_option( 'polylang_mo' . $lang->term_id ) ) {
290 $mo = new PLL_MO();
291 foreach ( $strings as $msg ) {
292 $mo->add_entry( $mo->make_entry( $msg[0], $msg[1] ) );
293 }
294 $mo->export_to_db( $lang );
295 }
296 }
297 }
298
299 /**
300 * Upgrades if the previous version is < 1.2.3
301 * Uprades multilingual menus depending on the old version due to multiple changes in menus management
302 *
303 * @since 1.2.3
304 */
305 public function upgrade_1_2_3() {
306 // Old version < 1.1
307 // Multilingal locations and switcher item were stored in a dedicated option
308 if ( version_compare( $this->options['version'], '1.1', '<' ) ) {
309 if ( $menu_lang = get_option( 'polylang_nav_menus' ) ) {
310 foreach ( $menu_lang as $location => $arr ) {
311 if ( ! in_array( $location, array_keys( get_registered_nav_menus() ) ) ) {
312 continue;
313 }
314
315 $switch_options = array_slice( $arr, -5, 5 );
316 $translations = array_diff_key( $arr, $switch_options );
317 $has_switcher = array_shift( $switch_options );
318
319 foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
320 // Move nav menus locations
321 if ( ! empty( $translations[ $lang->slug ] ) ) {
322 $locations[ $location ][ $lang->slug ] = $translations[ $lang->slug ];
323 }
324
325 // Create the menu items for the language switcher
326 if ( ! empty( $has_switcher ) ) {
327 $menu_item_db_id = wp_update_nav_menu_item(
328 $translations[ $lang->slug ],
329 0,
330 array(
331 'menu-item-title' => __( 'Language switcher', 'polylang' ),
332 'menu-item-url' => '#pll_switcher',
333 'menu-item-status' => 'publish',
334 )
335 );
336
337 update_post_meta( $menu_item_db_id, '_pll_menu_item', $switch_options );
338 }
339 }
340 }
341
342 if ( ! empty( $locations ) ) {
343 $this->options['nav_menus'][ get_option( 'stylesheet' ) ] = $locations;
344 }
345
346 delete_option( 'polylang_nav_menus' );
347 }
348 }
349
350 elseif ( empty( $this->options['nav_menus'] ) ) {
351 $menus = get_theme_mod( 'nav_menu_locations' );
352
353 if ( is_array( $menus ) ) {
354 // If old version < 1.2
355 // Clean the WP option as it was a bad idea to pollute it
356 if ( version_compare( $this->options['version'], '1.2', '<' ) ) {
357 foreach ( $menus as $loc => $menu ) {
358 if ( $pos = strpos( $loc, '#' ) ) {
359 unset( $menus[ $loc ] );
360 }
361 }
362
363 set_theme_mod( 'nav_menu_locations', $menus );
364 }
365
366 // Get the multilingual locations
367 foreach ( $menus as $loc => $menu ) {
368 foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
369 $arr[ $loc ][ $lang->slug ] = pll_get_term( $menu, $lang );
370 }
371 }
372
373 if ( ! empty( $arr ) ) {
374 $this->options['nav_menus'][ get_option( 'stylesheet' ) ] = $arr;
375 }
376 }
377 }
378 }
379
380 /**
381 * Upgrades if the previous version is < 1.3
382 * Moves the user biographies in default language to the 'description' user meta
383 *
384 * @since 1.3
385 */
386 protected function upgrade_1_3() {
387 $usermeta = 'description_' . $this->options['default_lang'];
388 $query = new WP_User_Query( array( 'blog_id' => $GLOBALS['blog_id'], 'meta_key' => $usermeta ) );
389
390 foreach ( $query->get_results() as $user ) {
391 $desc = get_user_meta( $user->ID, $usermeta, true );
392 if ( ! empty( $desc ) ) {
393 update_user_meta( $user->ID, 'description', $desc );
394 delete_user_meta( $user->ID, $usermeta );
395 }
396 }
397 }
398
399 /**
400 * Upgrades if the previous version is < 1.4
401 * Sets a transient to delete old model data
402 * Deletes language cache (due to bug correction in home urls in 1.3.1 and added mo_id in 1.4)
403 *
404 * @since 1.4
405 */
406 protected function upgrade_1_4() {
407 set_transient( 'pll_upgrade_1_4', time() + 60 * 24 * 60 * 60 ); // 60 days
408 delete_transient( 'pll_languages_list' );
409 }
410
411 /**
412 * Old data were not deleted in 1.2, just in case...
413 * Delete them at first upgrade at least 60 days after upgrade to 1.4
414 *
415 * @since 1.4
416 */
417 protected function delete_pre_1_2_data() {
418 // Suppress data of the old model < 1.2
419 global $wpdb;
420 $wpdb->termmeta = $wpdb->prefix . 'termmeta'; // registers the termmeta table in wpdb in case WP < 4.4
421
422 // Do nothing if the termmeta table does not exists
423 if ( count( $wpdb->get_results( "SHOW TABLES LIKE '$wpdb->termmeta'" ) ) ) {
424 $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key = '_translations'" );
425 $wpdb->query( "DELETE FROM $wpdb->termmeta WHERE meta_key = '_language'" );
426 $wpdb->query( "DELETE FROM $wpdb->termmeta WHERE meta_key = '_rtl'" );
427 $wpdb->query( "DELETE FROM $wpdb->termmeta WHERE meta_key = '_translations'" );
428 }
429
430 // Delete the strings translations
431 $languages = get_terms( 'language', array( 'hide_empty' => false ) );
432 foreach ( $languages as $lang ) {
433 delete_option( 'polylang_mo' . $lang->term_id );
434 }
435
436 delete_transient( 'pll_upgrade_1_4' );
437 }
438
439 /**
440 * Upgrades if the previous version is < 1.4.1
441 * Disables the browser detection when using multiple domains
442 *
443 * @since 1.4.1
444 */
445 protected function upgrade_1_4_1() {
446 if ( 3 == $this->options['force_lang'] ) {
447 $this->options['browser'] = $this->options['hide_default'] = 0;
448 }
449 }
450
451 /**
452 * Upgrades if the previous version is < 1.4.4
453 * Uprades widgets options for language filter
454 *
455 * @since 1.4.4
456 */
457 protected function upgrade_1_4_4() {
458 foreach ( $GLOBALS['wp_registered_widgets'] as $widget ) {
459 if ( ! empty( $this->options['widgets'][ $widget['id'] ] ) && ! empty( $widget['callback'][0] ) && ! empty( $widget['params'][0]['number'] ) ) {
460 $obj = $widget['callback'][0];
461 if ( is_object( $obj ) && method_exists( $obj, 'get_settings' ) && method_exists( $obj, 'save_settings' ) ) {
462 $settings = $obj->get_settings();
463 $settings[ $widget['params'][0]['number'] ]['pll_lang'] = $this->options['widgets'][ $widget['id'] ];
464 $obj->save_settings( $settings );
465 }
466 }
467 }
468 unset( $this->options['widgets'] );
469 }
470
471 /**
472 * Upgrades if the previous version is < 1.5
473 * Deletes language cache (due to host property added and bug on search url)
474 *
475 * @since 1.5
476 */
477 protected function upgrade_1_5() {
478 delete_transient( 'pll_languages_list' );
479 }
480
481 /**
482 * Upgrades if the previous version is < 1.6
483 * Upgrades core language files to get the .po file (only for WP 4.0+)
484 *
485 * @since 1.6
486 */
487 protected function upgrade_1_6() {
488 if ( version_compare( $GLOBALS['wp_version'], '4.0', '>=' ) ) {
489 self::download_language_packs();
490 }
491 }
492
493 /**
494 * Downloads language packs
495 * Intended to be used only one time (at upgrade to Polylang 1.6 or first upgrade of WP 4.0 or later)
496 * Adapted from wp_download_language_pack
497 * Rewritten because wp_download_language_pack checks the existence of .mo and I need to download .po
498 *
499 * @since 1.6
500 */
501 public static function download_language_packs() {
502 $languages = pll_languages_list( array( 'fields' => 'locale' ) );
503
504 // Prevents upgrade if the .po file is already here. Let WP manage the upgrades :)
505 foreach ( $languages as $key => $locale ) {
506 if ( file_exists( WP_LANG_DIR . "/$locale.po" ) ) {
507 unset( $languages[ $key ] );
508 }
509 }
510
511 if ( empty( $languages ) ) {
512 return;
513 }
514
515 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
516 $translations = wp_get_available_translations();
517 if ( ! $translations ) {
518 return;
519 }
520
521 foreach ( $translations as $translation ) {
522 if ( in_array( $translation['language'], $languages ) ) {
523 $translation['type'] = 'core';
524 $translations_to_load[] = (object) $translation;
525 }
526 }
527
528 if ( ! empty( $translations_to_load ) ) {
529 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
530 $upgrader = new Language_Pack_Upgrader( new Automatic_Upgrader_Skin() );
531 $upgrader->bulk_upgrade( $translations_to_load, array( 'clear_update_cache' => false ) );
532 }
533 }
534
535 /**
536 * Upgrades if the previous version is < 1.7.4
537 *
538 * @since 1.7.4
539 */
540 protected function upgrade_1_7_4() {
541 delete_transient( 'pll_languages_list' ); // Deletes language cache (due to flag properties added in 1.7, page on front removed in 1.7.2, home url fixes in 1.7.4)
542 flush_rewrite_rules(); // Flush rewrite rules due to custom taxonomy rewrite rule bug fix
543 }
544
545 /**
546 * Upgrades if the previous version is < 1.8
547 *
548 * @since 1.8
549 */
550 protected function upgrade_1_8() {
551 // Adds the flag code in languages stored in DB
552 include PLL_SETTINGS_INC . '/languages.php';
553
554 $terms = get_terms( 'language', array( 'hide_empty' => 0 ) );
555
556 foreach ( $terms as $lang ) {
557 $description = maybe_unserialize( $lang->description );
558 if ( isset( $languages[ $description['locale'] ] ) ) {
559 $description['flag_code'] = $languages[ $description['locale'] ]['flag'];
560 $description = serialize( $description );
561 wp_update_term( (int) $lang->term_id, 'language', array( 'description' => $description ) );
562 }
563 }
564
565 delete_transient( 'pll_languages_list' );
566 }
567
568 /**
569 * Upgrades if the previous version is < 2.0.8
570 * Changes the user meta 'user_lang' to 'locale' to match WP 4.7 choice
571 *
572 * @since 2.0.8
573 */
574 protected function upgrade_2_0_8() {
575 global $wpdb;
576 $wpdb->update( $wpdb->usermeta, array( 'meta_key' => 'locale' ), array( 'meta_key' => 'user_lang' ) );
577 }
578
579 /**
580 * Upgrades if the previous version is < 2.1
581 * Moves strings translations from polylang_mo post_content to post meta _pll_strings_translations
582 *
583 * @since 2.1
584 */
585 protected function upgrade_2_1() {
586 foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
587 $mo_id = PLL_MO::get_id( $lang );
588 $meta = get_post_meta( $mo_id, '_pll_strings_translations', true );
589
590 if ( empty( $meta ) ) {
591 $post = get_post( $mo_id, OBJECT );
592 $strings = unserialize( $post->post_content );
593 if ( is_array( $strings ) ) {
594 update_post_meta( $mo_id, '_pll_strings_translations', $strings );
595 }
596 }
597 }
598 }
599
600 /**
601 * Upgrades if the previous version is < 2.3
602 *
603 * Deletes language cache due to 'redirect_lang' option removed for subdomains and multiple domains in 2.2
604 * and W3C and Facebook locales added to PLL_Language objects in 2.3
605 *
606 * @since 2.3
607 */
608 protected function upgrade_2_3() {
609 delete_transient( 'pll_languages_list' );
610 }
611 }
612