PluginProbe
Polylang / 2.0
Polylang v2.0
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.0, at install/upgrade.php

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