PluginProbe
Polylang / 2.2.2
Polylang v2.2.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.2.2, at install/upgrade.php

599 lines 18.7 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: %s are Polylang version numbers */
79 esc_html__( 'Please upgrade first to %s before ugrading to %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.2' ) 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 $wpdb->query( "INSERT INTO $wpdb->term_relationships ( object_id, term_taxonomy_id ) VALUES " . implode( ',', $terms ) );
206 }
207
208 // Translations
209 foreach ( array( 'post', 'term' ) as $type ) {
210 $table = $type . 'meta';
211 $terms = $slugs = $tts = $trs = array();
212
213 // Get all translated objects
214 $objects = $wpdb->get_col( "SELECT DISTINCT meta_value FROM {$wpdb->$table} WHERE meta_key = '_translations'" );
215
216 if ( empty( $objects ) ) {
217 continue;
218 }
219
220 foreach ( $objects as $obj ) {
221 $term = uniqid( 'pll_' ); // The term name
222 $terms[] = $wpdb->prepare( '( "%s", "%s" )', $term, $term );
223 $slugs[] = $wpdb->prepare( '"%s"', $term );
224 $translations = maybe_unserialize( maybe_unserialize( $obj ) ); // 2 unserialize due to an old storage bug
225 $description[ $term ] = serialize( $translations );
226 }
227
228 $terms = array_unique( $terms );
229
230 // Insert terms
231 if ( ! empty( $terms ) ) {
232 $wpdb->query( "INSERT INTO $wpdb->terms ( slug, name ) VALUES " . implode( ',', $terms ) );
233 }
234
235 // Get all terms with their term_id
236 $terms = $wpdb->get_results( "SELECT term_id, slug FROM $wpdb->terms WHERE slug IN ( " . implode( ',', $slugs ) . ' )' );
237
238 // Prepare terms taxonomy relationship
239 foreach ( $terms as $term ) {
240 $tts[] = $wpdb->prepare( '( %d, "%s", "%s" )', $term->term_id, $type . '_translations', $description[ $term->slug ] );
241 }
242
243 $tts = array_unique( $tts );
244
245 // Insert term_taxonomy
246 if ( ! empty( $tts ) ) {
247 $wpdb->query( "INSERT INTO $wpdb->term_taxonomy ( term_id, taxonomy, description ) VALUES " . implode( ',', $tts ) );
248 }
249
250 // Get all terms with term_taxonomy_id
251 $terms = get_terms( $type . '_translations', array( 'hide_empty' => false ) );
252
253 // Prepare objects relationships
254 foreach ( $terms as $term ) {
255 $translations = unserialize( $term->description );
256 foreach ( $translations as $object_id ) {
257 if ( ! empty( $object_id ) ) {
258 $trs[] = $wpdb->prepare( '( %d, %d )', $object_id, $term->term_taxonomy_id );
259 }
260 }
261 }
262
263 $trs = array_unique( $trs );
264
265 // Insert term_relationships
266 if ( ! empty( $trs ) ) {
267 $wpdb->query( "INSERT INTO $wpdb->term_relationships ( object_id, term_taxonomy_id ) VALUES " . implode( ',', $trs ) );
268 }
269 }
270
271 // Upgrade of string translations is now in upgrade_1_2_1
272 // Upgrade of nav menus is now in upgrade_1_2_3
273 }
274
275 /**
276 * Upgrades if the previous version is < 1.2.1
277 *
278 * @since 1.2.1
279 */
280 protected function upgrade_1_2_1() {
281 // Strings translations
282 foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
283 if ( $strings = get_option( 'polylang_mo' . $lang->term_id ) ) {
284 $mo = new PLL_MO();
285 foreach ( $strings as $msg ) {
286 $mo->add_entry( $mo->make_entry( $msg[0], $msg[1] ) );
287 }
288 $mo->export_to_db( $lang );
289 }
290 }
291 }
292
293 /**
294 * Upgrades if the previous version is < 1.2.3
295 * Uprades multilingual menus depending on the old version due to multiple changes in menus management
296 *
297 * @since 1.2.3
298 */
299 public function upgrade_1_2_3() {
300 // Old version < 1.1
301 // Multilingal locations and switcher item were stored in a dedicated option
302 if ( version_compare( $this->options['version'], '1.1', '<' ) ) {
303 if ( $menu_lang = get_option( 'polylang_nav_menus' ) ) {
304 foreach ( $menu_lang as $location => $arr ) {
305 if ( ! in_array( $location, array_keys( get_registered_nav_menus() ) ) ) {
306 continue;
307 }
308
309 $switch_options = array_slice( $arr, -5, 5 );
310 $translations = array_diff_key( $arr, $switch_options );
311 $has_switcher = array_shift( $switch_options );
312
313 foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
314 // Move nav menus locations
315 if ( ! empty( $translations[ $lang->slug ] ) ) {
316 $locations[ $location ][ $lang->slug ] = $translations[ $lang->slug ];
317 }
318
319 // Create the menu items for the language switcher
320 if ( ! empty( $has_switcher ) ) {
321 $menu_item_db_id = wp_update_nav_menu_item( $translations[ $lang->slug ], 0, array(
322 'menu-item-title' => __( 'Language switcher', 'polylang' ),
323 'menu-item-url' => '#pll_switcher',
324 'menu-item-status' => 'publish',
325 ) );
326
327 update_post_meta( $menu_item_db_id, '_pll_menu_item', $switch_options );
328 }
329 }
330 }
331
332 if ( ! empty( $locations ) ) {
333 $this->options['nav_menus'][ get_option( 'stylesheet' ) ] = $locations;
334 }
335
336 delete_option( 'polylang_nav_menus' );
337 }
338 }
339
340 elseif ( empty( $this->options['nav_menus'] ) ) {
341 $menus = get_theme_mod( 'nav_menu_locations' );
342
343 if ( is_array( $menus ) ) {
344 // If old version < 1.2
345 // Clean the WP option as it was a bad idea to pollute it
346 if ( version_compare( $this->options['version'], '1.2', '<' ) ) {
347 foreach ( $menus as $loc => $menu ) {
348 if ( $pos = strpos( $loc, '#' ) ) {
349 unset( $menus[ $loc ] );
350 }
351 }
352
353 set_theme_mod( 'nav_menu_locations', $menus );
354 }
355
356 // Get the multilingual locations
357 foreach ( $menus as $loc => $menu ) {
358 foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
359 $arr[ $loc ][ $lang->slug ] = pll_get_term( $menu, $lang );
360 }
361 }
362
363 if ( ! empty( $arr ) ) {
364 $this->options['nav_menus'][ get_option( 'stylesheet' ) ] = $arr;
365 }
366 }
367 }
368 }
369
370 /**
371 * Upgrades if the previous version is < 1.3
372 * Moves the user biographies in default language to the 'description' user meta
373 *
374 * @since 1.3
375 */
376 protected function upgrade_1_3() {
377 $usermeta = 'description_' . $this->options['default_lang'];
378 $query = new WP_User_Query( array( 'blog_id' => $GLOBALS['blog_id'], 'meta_key' => $usermeta ) );
379
380 foreach ( $query->get_results() as $user ) {
381 $desc = get_user_meta( $user->ID, $usermeta, true );
382 if ( ! empty( $desc ) ) {
383 update_user_meta( $user->ID, 'description', $desc );
384 delete_user_meta( $user->ID, $usermeta );
385 }
386 }
387 }
388
389 /**
390 * Upgrades if the previous version is < 1.4
391 * Sets a transient to delete old model data
392 * Deletes language cache (due to bug correction in home urls in 1.3.1 and added mo_id in 1.4)
393 *
394 * @since 1.4
395 */
396 protected function upgrade_1_4() {
397 set_transient( 'pll_upgrade_1_4', time() + 60 * 24 * 60 * 60 ); // 60 days
398 delete_transient( 'pll_languages_list' );
399 }
400
401 /**
402 * Old data were not deleted in 1.2, just in case...
403 * Delete them at first upgrade at least 60 days after upgrade to 1.4
404 *
405 * @since 1.4
406 */
407 protected function delete_pre_1_2_data() {
408 // Suppress data of the old model < 1.2
409 global $wpdb;
410 $wpdb->termmeta = $wpdb->prefix . 'termmeta'; // registers the termmeta table in wpdb in case WP < 4.4
411
412 // Do nothing if the termmeta table does not exists
413 if ( count( $wpdb->get_results( "SHOW TABLES LIKE '$wpdb->termmeta'" ) ) ) {
414 $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key = '_translations'" );
415 $wpdb->query( "DELETE FROM $wpdb->termmeta WHERE meta_key = '_language'" );
416 $wpdb->query( "DELETE FROM $wpdb->termmeta WHERE meta_key = '_rtl'" );
417 $wpdb->query( "DELETE FROM $wpdb->termmeta WHERE meta_key = '_translations'" );
418 }
419
420 // Delete the strings translations
421 $languages = get_terms( 'language', array( 'hide_empty' => false ) );
422 foreach ( $languages as $lang ) {
423 delete_option( 'polylang_mo' . $lang->term_id );
424 }
425
426 delete_transient( 'pll_upgrade_1_4' );
427 }
428
429 /**
430 * Upgrades if the previous version is < 1.4.1
431 * Disables the browser detection when using multiple domains
432 *
433 * @since 1.4.1
434 */
435 protected function upgrade_1_4_1() {
436 if ( 3 == $this->options['force_lang'] ) {
437 $this->options['browser'] = $this->options['hide_default'] = 0;
438 }
439 }
440
441 /**
442 * Upgrades if the previous version is < 1.4.4
443 * Uprades widgets options for language filter
444 *
445 * @since 1.4.4
446 */
447 protected function upgrade_1_4_4() {
448 foreach ( $GLOBALS['wp_registered_widgets'] as $widget ) {
449 if ( ! empty( $this->options['widgets'][ $widget['id'] ] ) && ! empty( $widget['callback'][0] ) && ! empty( $widget['params'][0]['number'] ) ) {
450 $obj = $widget['callback'][0];
451 if ( is_object( $obj ) && method_exists( $obj, 'get_settings' ) && method_exists( $obj, 'save_settings' ) ) {
452 $settings = $obj->get_settings();
453 $settings[ $widget['params'][0]['number'] ]['pll_lang'] = $this->options['widgets'][ $widget['id'] ];
454 $obj->save_settings( $settings );
455 }
456 }
457 }
458 unset( $this->options['widgets'] );
459 }
460
461 /**
462 * Upgrades if the previous version is < 1.5
463 * Deletes language cache (due to host property added and bug on search url)
464 *
465 * @since 1.5
466 */
467 protected function upgrade_1_5() {
468 delete_transient( 'pll_languages_list' );
469 }
470
471 /**
472 * Upgrades if the previous version is < 1.6
473 * Upgrades core language files to get the .po file (only for WP 4.0+)
474 *
475 * @since 1.6
476 */
477 protected function upgrade_1_6() {
478 if ( version_compare( $GLOBALS['wp_version'], '4.0', '>=' ) ) {
479 self::download_language_packs();
480 }
481 }
482
483 /**
484 * Downloads language packs
485 * Intended to be used only one time (at upgrade to Polylang 1.6 or first upgrade of WP 4.0 or later)
486 * Adapted from wp_download_language_pack
487 * Rewritten because wp_download_language_pack checks the existence of .mo and I need to download .po
488 *
489 * @since 1.6
490 */
491 static function download_language_packs() {
492 $languages = pll_languages_list( array( 'fields' => 'locale' ) );
493
494 // Prevents upgrade if the .po file is already here. Let WP manage the upgrades :)
495 foreach ( $languages as $key => $locale ) {
496 if ( file_exists( WP_LANG_DIR . "/$locale.po" ) ) {
497 unset( $languages[ $key ] );
498 }
499 }
500
501 if ( empty( $languages ) ) {
502 return;
503 }
504
505 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
506 $translations = wp_get_available_translations();
507 if ( ! $translations ) {
508 return;
509 }
510
511 foreach ( $translations as $translation ) {
512 if ( in_array( $translation['language'], $languages ) ) {
513 $translation['type'] = 'core';
514 $translations_to_load[] = (object) $translation;
515 }
516 }
517
518 if ( ! empty( $translations_to_load ) ) {
519 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
520 $upgrader = new Language_Pack_Upgrader( new Automatic_Upgrader_Skin );
521 $upgrader->bulk_upgrade( $translations_to_load, array( 'clear_update_cache' => false ) );
522 }
523 }
524
525 /**
526 * Upgrades if the previous version is < 1.7.4
527 *
528 * @since 1.7.4
529 */
530 protected function upgrade_1_7_4() {
531 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)
532 flush_rewrite_rules(); // Flush rewrite rules due to custom taxonomy rewrite rule bug fix
533 }
534
535 /**
536 * Upgrades if the previous version is < 1.8
537 *
538 * @since 1.8
539 */
540 protected function upgrade_1_8() {
541 // Adds the flag code in languages stored in DB
542 include PLL_SETTINGS_INC . '/languages.php';
543
544 $terms = get_terms( 'language', array( 'hide_empty' => 0 ) );
545
546 foreach ( $terms as $lang ) {
547 $description = maybe_unserialize( $lang->description );
548 if ( isset( $languages[ $description['locale'] ] ) ) {
549 $description['flag_code'] = $languages[ $description['locale'] ][4];
550 $description = serialize( $description );
551 wp_update_term( (int) $lang->term_id, 'language', array( 'description' => $description ) );
552 }
553 }
554
555 delete_transient( 'pll_languages_list' );
556 }
557
558 /**
559 * Upgrades if the previous version is < 2.0.8
560 * Changes the user meta 'user_lang' to 'locale' to match WP 4.7 choice
561 *
562 * @since 2.0.8
563 */
564 protected function upgrade_2_0_8() {
565 global $wpdb;
566 $wpdb->update( $wpdb->usermeta, array( 'meta_key' => 'locale' ), array( 'meta_key' => 'user_lang' ) );
567 }
568
569 /**
570 * Upgrades if the previous version is < 2.1
571 * Moves strings translations from polylang_mo post_content to post meta _pll_strings_translations
572 *
573 * @since 2.1
574 */
575 protected function upgrade_2_1() {
576 foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
577 $mo_id = PLL_MO::get_id( $lang );
578 $meta = get_post_meta( $mo_id, '_pll_strings_translations', true );
579
580 if ( empty( $meta ) ) {
581 $post = get_post( $mo_id, OBJECT );
582 $strings = unserialize( $post->post_content );
583 if ( is_array( $strings ) ) {
584 update_post_meta( $mo_id, '_pll_strings_translations', $strings );
585 }
586 }
587 }
588 }
589
590 /**
591 * Upgrades if the previous version is < 2.2
592 *
593 * @since 2.2
594 */
595 protected function upgrade_2_2() {
596 delete_transient( 'pll_languages_list' ); // Deletes language cache (due to 'redirect_lang' option removed for subdomains and multiple domains)
597 }
598 }
599