Polylang - Version 2.5

Version Description

(2018-12-06) =

  • Add compatibility with WP 5.0
  • Fix custom flags when the WP content folder is not in the WP install folder
  • Fix PHP notice if a language has no flag
Download this release

Release Info

Developer Chouby
Plugin Icon 128x128 Polylang
Version 2.5
Comparing to
See all releases

Code changes from version 2.4.1 to 2.5

admin/admin-base.php CHANGED
@@ -118,7 +118,6 @@ class PLL_Admin_Base extends PLL_Base {
118
  // 3 => 1 if loaded in footer
119
  // FIXME: check if I can load more scripts in footer
120
  $scripts = array(
121
- 'classic-editor' => array( array( 'post', 'media', 'async-upload' ), array( 'jquery', 'wp-ajax-response', 'post', 'jquery-ui-autocomplete' ), 0, 1 ),
122
  'post' => array( array( 'edit' ), array( 'jquery', 'wp-ajax-response' ), 0, 1 ),
123
  'media' => array( array( 'upload' ), array( 'jquery' ), 0, 1 ),
124
  'term' => array( array( 'edit-tags', 'term' ), array( 'jquery', 'wp-ajax-response', 'jquery-ui-autocomplete' ), 0, 1 ),
@@ -126,6 +125,15 @@ class PLL_Admin_Base extends PLL_Base {
126
  'widgets' => array( array( 'widgets' ), array( 'jquery' ), 0, 0 ),
127
  );
128
 
 
 
 
 
 
 
 
 
 
129
  foreach ( $scripts as $script => $v ) {
130
  if ( in_array( $screen->base, $v[0] ) && ( $v[2] || $this->model->get_languages_list() ) ) {
131
  wp_enqueue_script( 'pll_' . $script, plugins_url( '/js/' . $script . $suffix . '.js', POLYLANG_FILE ), $v[1], POLYLANG_VERSION, $v[3] );
118
  // 3 => 1 if loaded in footer
119
  // FIXME: check if I can load more scripts in footer
120
  $scripts = array(
 
121
  'post' => array( array( 'edit' ), array( 'jquery', 'wp-ajax-response' ), 0, 1 ),
122
  'media' => array( array( 'upload' ), array( 'jquery' ), 0, 1 ),
123
  'term' => array( array( 'edit-tags', 'term' ), array( 'jquery', 'wp-ajax-response', 'jquery-ui-autocomplete' ), 0, 1 ),
125
  'widgets' => array( array( 'widgets' ), array( 'jquery' ), 0, 0 ),
126
  );
127
 
128
+ if ( ! empty( $screen->post_type ) && $this->model->is_translated_post_type( $screen->post_type ) ) {
129
+ $scripts['classic-editor'] = array( array( 'post', 'media', 'async-upload' ), array( 'jquery', 'wp-ajax-response', 'post', 'jquery-ui-autocomplete' ), 0, 1 );
130
+
131
+ // Block editor in WP 5.0+
132
+ if ( method_exists( $screen, 'is_block_editor' ) && $screen->is_block_editor() ) {
133
+ $scripts['block-editor'] = array( array( 'post' ), array( 'wp-api-fetch' ), 0, 1 );
134
+ }
135
+ }
136
+
137
  foreach ( $scripts as $script => $v ) {
138
  if ( in_array( $screen->base, $v[0] ) && ( $v[2] || $this->model->get_languages_list() ) ) {
139
  wp_enqueue_script( 'pll_' . $script, plugins_url( '/js/' . $script . $suffix . '.js', POLYLANG_FILE ), $v[1], POLYLANG_VERSION, $v[3] );
admin/admin-block-editor.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Manages filters and actions related to the block editor
5
+ *
6
+ * @since 2.5
7
+ */
8
+ class PLL_Admin_Block_Editor {
9
+
10
+ /**
11
+ * Constructor: setups filters and actions
12
+ *
13
+ * @since 2.5
14
+ *
15
+ * @param object $polylang
16
+ */
17
+ public function __construct( &$polylang ) {
18
+ add_filter( 'block_editor_preload_paths', array( $this, 'preload_paths' ), 10, 2 );
19
+ }
20
+
21
+ /**
22
+ * Filter the preload REST requests by the current language of the post
23
+ * Necessary otherwise subsequent REST requests all filtered by the language
24
+ * would not hit the preloaded requests
25
+ *
26
+ * @since 2.5
27
+ *
28
+ * @param array $preload_paths Array of paths to preload.
29
+ * @param object $post The post resource data.
30
+ * @return array
31
+ */
32
+ public function preload_paths( $preload_paths, $post ) {
33
+ $lang = pll_get_post_language( $post->ID );
34
+
35
+ foreach ( $preload_paths as $k => $path ) {
36
+ if ( is_string( $path ) && '/' !== $path ) {
37
+ $preload_paths[ $k ] = $path . "&lang={$lang}";
38
+ }
39
+ }
40
+
41
+ return $preload_paths;
42
+ }
43
+ }
admin/admin-classic-editor.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  /**
4
- * Manages filters and actions related to the editor
5
  *
6
  * @since 2.4
7
  */
1
  <?php
2
 
3
  /**
4
+ * Manages filters and actions related to the classic editor
5
  *
6
  * @since 2.4
7
  */
admin/admin-filters-media.php CHANGED
@@ -7,6 +7,8 @@
7
  * @since 1.2
8
  */
9
  class PLL_Admin_Filters_Media extends PLL_Admin_Filters_Post_Base {
 
 
10
  /**
11
  * Constructor: setups filters and actions
12
  *
@@ -17,6 +19,8 @@ class PLL_Admin_Filters_Media extends PLL_Admin_Filters_Post_Base {
17
  public function __construct( &$polylang ) {
18
  parent::__construct( $polylang );
19
 
 
 
20
  // Adds the language field and translations tables in the 'Edit Media' panel
21
  add_filter( 'attachment_fields_to_edit', array( $this, 'attachment_fields_to_edit' ), 10, 2 );
22
 
@@ -64,60 +68,6 @@ class PLL_Admin_Filters_Media extends PLL_Admin_Filters_Post_Base {
64
  return $fields;
65
  }
66
 
67
- /**
68
- * Creates a media translation
69
- *
70
- * @since 1.8
71
- *
72
- * @param int $post_id
73
- * @param string|object $lang
74
- * @return int id of the translated media
75
- */
76
- public function create_media_translation( $post_id, $lang ) {
77
- $post = get_post( $post_id );
78
-
79
- if ( empty( $post ) ) {
80
- return $post;
81
- }
82
-
83
- $lang = $this->model->get_language( $lang ); // Make sure we get a valid language slug
84
-
85
- // Create a new attachment ( translate attachment parent if exists )
86
- $post->ID = null; // Will force the creation
87
- $post->post_parent = ( $post->post_parent && $tr_parent = $this->model->post->get_translation( $post->post_parent, $lang->slug ) ) ? $tr_parent : 0;
88
- $post->tax_input = array( 'language' => array( $lang->slug ) ); // Assigns the language
89
- $tr_id = wp_insert_attachment( $post );
90
-
91
- // Copy metadata, attached file and alternative text
92
- foreach ( array( '_wp_attachment_metadata', '_wp_attached_file', '_wp_attachment_image_alt' ) as $key ) {
93
- if ( $meta = get_post_meta( $post_id, $key, true ) ) {
94
- add_post_meta( $tr_id, $key, $meta );
95
- }
96
- }
97
-
98
- $this->model->post->set_language( $tr_id, $lang );
99
-
100
- $translations = $this->model->post->get_translations( $post_id );
101
- if ( ! $translations && $src_lang = $this->model->post->get_language( $post_id ) ) {
102
- $translations[ $src_lang->slug ] = $post_id;
103
- }
104
-
105
- $translations[ $lang->slug ] = $tr_id;
106
- $this->model->post->save_translations( $tr_id, $translations );
107
-
108
- /**
109
- * Fires after a media translation is created
110
- *
111
- * @since 1.6.4
112
- *
113
- * @param int $post_id post id of the source media
114
- * @param int $tr_id post id of the new media translation
115
- * @param string $slug language code of the new translation
116
- */
117
- do_action( 'pll_translate_media', $post_id, $tr_id, $lang->slug );
118
- return $tr_id;
119
- }
120
-
121
  /**
122
  * Creates a media translation
123
  *
@@ -136,7 +86,7 @@ class PLL_Admin_Filters_Media extends PLL_Admin_Filters_Post_Base {
136
  exit;
137
  }
138
 
139
- $tr_id = $this->create_media_translation( $post_id, $_GET['new_lang'] );
140
  wp_safe_redirect( admin_url( sprintf( 'post.php?post=%d&action=edit', $tr_id ) ) ); // WP 3.5+
141
  exit;
142
  }
7
  * @since 1.2
8
  */
9
  class PLL_Admin_Filters_Media extends PLL_Admin_Filters_Post_Base {
10
+ public $posts;
11
+
12
  /**
13
  * Constructor: setups filters and actions
14
  *
19
  public function __construct( &$polylang ) {
20
  parent::__construct( $polylang );
21
 
22
+ $this->posts = &$polylang->posts;
23
+
24
  // Adds the language field and translations tables in the 'Edit Media' panel
25
  add_filter( 'attachment_fields_to_edit', array( $this, 'attachment_fields_to_edit' ), 10, 2 );
26
 
68
  return $fields;
69
  }
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  /**
72
  * Creates a media translation
73
  *
86
  exit;
87
  }
88
 
89
+ $tr_id = $this->posts->create_media_translation( $post_id, $_GET['new_lang'] );
90
  wp_safe_redirect( admin_url( sprintf( 'post.php?post=%d&action=edit', $tr_id ) ) ); // WP 3.5+
91
  exit;
92
  }
admin/admin-static-pages.php CHANGED
@@ -18,6 +18,7 @@ class PLL_Admin_Static_Pages extends PLL_Static_Pages {
18
  parent::__construct( $polylang );
19
 
20
  // Removes the editor and the template select dropdown for pages for posts
 
21
  add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 10, 2 );
22
 
23
  // Add post state for translations of the front page and posts page
@@ -34,6 +35,27 @@ class PLL_Admin_Static_Pages extends PLL_Static_Pages {
34
  add_filter( 'pre_update_option_show_on_front', array( $this, 'update_show_on_front' ), 10, 2 );
35
  }
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  /**
38
  * Removes the editor for translations of the pages for posts
39
  * Removes the page template select dropdown in page attributes metabox too
18
  parent::__construct( $polylang );
19
 
20
  // Removes the editor and the template select dropdown for pages for posts
21
+ add_filter( 'use_block_editor_for_post', array( $this, 'use_block_editor_for_post' ), 10, 2 ); // Since WP 5.0
22
  add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 10, 2 );
23
 
24
  // Add post state for translations of the front page and posts page
35
  add_filter( 'pre_update_option_show_on_front', array( $this, 'update_show_on_front' ), 10, 2 );
36
  }
37
 
38
+ /**
39
+ * Don't use the block editor for the translations of the pages for posts
40
+ *
41
+ * @since 2.5
42
+ *
43
+ * @param bool $use_block_editor Whether the post can be edited or not.
44
+ * @param WP_Post $post The post being checked.
45
+ * @return bool
46
+ */
47
+ public function use_block_editor_for_post( $use_block_editor, $post ) {
48
+ if ( 'page' === $post->post_type ) {
49
+ add_filter( 'option_page_for_posts', array( $this, 'translate_page_for_posts' ) );
50
+
51
+ if ( ( get_option( 'page_for_posts' ) == $post->ID ) && empty( $post->post_content ) ) {
52
+ return false;
53
+ }
54
+ }
55
+
56
+ return $use_block_editor;
57
+ }
58
+
59
  /**
60
  * Removes the editor for translations of the pages for posts
61
  * Removes the page template select dropdown in page attributes metabox too
admin/admin.php CHANGED
@@ -22,6 +22,7 @@
22
  * filters_term => reference to PLL_Admin_filters_Term object
23
  * nav_menu => reference to PLL_Admin_Nav_Menu object
24
  * sync => reference to PLL_Admin_Sync object
 
25
  * classic_editor => reference to PLL_Admin_Classic_Editor object
26
  * filters_media => optional, reference to PLL_Admin_Filters_Media object
27
  *
@@ -96,7 +97,7 @@ class PLL_Admin extends PLL_Admin_Base {
96
  */
97
  public function add_filters() {
98
  // All these are separated just for convenience and maintainability
99
- $classes = array( 'Filters', 'Filters_Columns', 'Filters_Post', 'Filters_Term', 'Nav_Menu', 'Sync', 'Classic_Editor' );
100
 
101
  // Don't load media filters if option is disabled or if user has no right
102
  if ( $this->options['media_support'] && ( $obj = get_post_type_object( 'attachment' ) ) && ( current_user_can( $obj->cap->edit_posts ) || current_user_can( $obj->cap->create_posts ) ) ) {
22
  * filters_term => reference to PLL_Admin_filters_Term object
23
  * nav_menu => reference to PLL_Admin_Nav_Menu object
24
  * sync => reference to PLL_Admin_Sync object
25
+ * block_editor => reference to PLL_Admin_Block_Editor object
26
  * classic_editor => reference to PLL_Admin_Classic_Editor object
27
  * filters_media => optional, reference to PLL_Admin_Filters_Media object
28
  *
97
  */
98
  public function add_filters() {
99
  // All these are separated just for convenience and maintainability
100
+ $classes = array( 'Filters', 'Filters_Columns', 'Filters_Post', 'Filters_Term', 'Nav_Menu', 'Sync', 'Classic_Editor', 'Block_Editor' );
101
 
102
  // Don't load media filters if option is disabled or if user has no right
103
  if ( $this->options['media_support'] && ( $obj = get_post_type_object( 'attachment' ) ) && ( current_user_can( $obj->cap->edit_posts ) || current_user_can( $obj->cap->create_posts ) ) ) {
frontend/frontend-filters-links.php CHANGED
@@ -345,7 +345,7 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
345
 
346
  elseif ( is_category() || is_tag() || is_tax() ) {
347
  $obj = $wp_query->get_queried_object();
348
- if ( $this->model->is_translated_taxonomy( $obj->taxonomy ) ) {
349
  $language = $this->model->term->get_language( (int) $obj->term_id );
350
  }
351
  }
345
 
346
  elseif ( is_category() || is_tag() || is_tax() ) {
347
  $obj = $wp_query->get_queried_object();
348
+ if ( ! empty( $obj ) && $this->model->is_translated_taxonomy( $obj->taxonomy ) ) {
349
  $language = $this->model->term->get_language( (int) $obj->term_id );
350
  }
351
  }
include/crud-posts.php CHANGED
@@ -231,4 +231,60 @@ class PLL_CRUD_Posts {
231
 
232
  return $file;
233
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  }
231
 
232
  return $file;
233
  }
234
+
235
+ /**
236
+ * Creates a media translation
237
+ *
238
+ * @since 1.8
239
+ *
240
+ * @param int $post_id
241
+ * @param string|object $lang
242
+ * @return int id of the translated media
243
+ */
244
+ public function create_media_translation( $post_id, $lang ) {
245
+ $post = get_post( $post_id );
246
+
247
+ if ( empty( $post ) ) {
248
+ return $post;
249
+ }
250
+
251
+ $lang = $this->model->get_language( $lang ); // Make sure we get a valid language slug
252
+
253
+ // Create a new attachment ( translate attachment parent if exists )
254
+ add_filter( 'pll_enable_duplicate_media', '__return_false', 99 ); // Avoid a conflict with automatic duplicate at upload
255
+ $post->ID = null; // Will force the creation
256
+ $post->post_parent = ( $post->post_parent && $tr_parent = $this->model->post->get_translation( $post->post_parent, $lang->slug ) ) ? $tr_parent : 0;
257
+ $post->tax_input = array( 'language' => array( $lang->slug ) ); // Assigns the language
258
+ $tr_id = wp_insert_attachment( $post );
259
+ remove_filter( 'pll_enable_duplicate_media', '__return_false', 99 ); // Restore automatic duplicate at upload
260
+
261
+ // Copy metadata, attached file and alternative text
262
+ foreach ( array( '_wp_attachment_metadata', '_wp_attached_file', '_wp_attachment_image_alt' ) as $key ) {
263
+ if ( $meta = get_post_meta( $post_id, $key, true ) ) {
264
+ add_post_meta( $tr_id, $key, $meta );
265
+ }
266
+ }
267
+
268
+ $this->model->post->set_language( $tr_id, $lang );
269
+
270
+ $translations = $this->model->post->get_translations( $post_id );
271
+ if ( ! $translations && $src_lang = $this->model->post->get_language( $post_id ) ) {
272
+ $translations[ $src_lang->slug ] = $post_id;
273
+ }
274
+
275
+ $translations[ $lang->slug ] = $tr_id;
276
+ $this->model->post->save_translations( $tr_id, $translations );
277
+
278
+ /**
279
+ * Fires after a media translation is created
280
+ *
281
+ * @since 1.6.4
282
+ *
283
+ * @param int $post_id post id of the source media
284
+ * @param int $tr_id post id of the new media translation
285
+ * @param string $slug language code of the new translation
286
+ */
287
+ do_action( 'pll_translate_media', $post_id, $tr_id, $lang->slug );
288
+ return $tr_id;
289
+ }
290
  }
include/language.php CHANGED
@@ -118,7 +118,7 @@ class PLL_Language {
118
 
119
  if ( empty( $flags['flag']['src'] ) ) {
120
  // If using predefined flags and base64 encoded flags are preferred
121
- if ( $flags['flag']['url'] === $_url && ( ! defined( 'PLL_ENCODED_FLAGS' ) || PLL_ENCODED_FLAGS ) ) {
122
  $flags['flag']['src'] = 'data:image/png;base64,' . base64_encode( file_get_contents( POLYLANG_DIR . $file ) );
123
  } else {
124
  $flags['flag']['src'] = esc_url( set_url_scheme( $flags['flag']['url'], 'relative' ) );
@@ -136,7 +136,7 @@ class PLL_Language {
136
 
137
  foreach ( $directories as $dir ) {
138
  if ( file_exists( $file = "{$dir}/{$this->locale}.png" ) || file_exists( $file = "{$dir}/{$this->locale}.jpg" ) || file_exists( $file = "{$dir}/{$this->locale}.svg" ) ) {
139
- $flags['custom_flag']['url'] = site_url( '/' . str_replace( ABSPATH, '', $file ) );
140
  break;
141
  }
142
  }
118
 
119
  if ( empty( $flags['flag']['src'] ) ) {
120
  // If using predefined flags and base64 encoded flags are preferred
121
+ if ( isset( $_url ) && $flags['flag']['url'] === $_url && ( ! defined( 'PLL_ENCODED_FLAGS' ) || PLL_ENCODED_FLAGS ) ) {
122
  $flags['flag']['src'] = 'data:image/png;base64,' . base64_encode( file_get_contents( POLYLANG_DIR . $file ) );
123
  } else {
124
  $flags['flag']['src'] = esc_url( set_url_scheme( $flags['flag']['url'], 'relative' ) );
136
 
137
  foreach ( $directories as $dir ) {
138
  if ( file_exists( $file = "{$dir}/{$this->locale}.png" ) || file_exists( $file = "{$dir}/{$this->locale}.jpg" ) || file_exists( $file = "{$dir}/{$this->locale}.svg" ) ) {
139
+ $flags['custom_flag']['url'] = content_url( '/' . str_replace( WP_CONTENT_DIR, '', $file ) );
140
  break;
141
  }
142
  }
include/model.php CHANGED
@@ -218,7 +218,7 @@ class PLL_Model {
218
  */
219
  public function get_translated_post_types( $filter = true ) {
220
  if ( false === $post_types = $this->cache->get( 'post_types' ) ) {
221
- $post_types = array( 'post' => 'post', 'page' => 'page' );
222
 
223
  if ( ! empty( $this->options['media_support'] ) ) {
224
  $post_types['attachment'] = 'attachment';
218
  */
219
  public function get_translated_post_types( $filter = true ) {
220
  if ( false === $post_types = $this->cache->get( 'post_types' ) ) {
221
+ $post_types = array( 'post' => 'post', 'page' => 'page', 'wp_block' => 'wp_block' );
222
 
223
  if ( ! empty( $this->options['media_support'] ) ) {
224
  $post_types['attachment'] = 'attachment';
js/block-editor.js ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Filter REST API requests to add the language in the request
3
+ *
4
+ * @since 2.5
5
+ */
6
+ wp.apiFetch.use( function( options, next ) {
7
+ // If options.url is defined, this is not a REST request but a direct call to post.php for legacy metaboxes.
8
+ if ( 'undefined' === typeof options.url ) {
9
+ if ( 'undefined' === typeof options.data ) {
10
+ // GET
11
+ options.path += ( ( options.path.indexOf ( '?' ) >= 0 ) ? '&lang=' : '?lang=' ) + getCurrentLanguage();
12
+ } else {
13
+ // PUT, POST
14
+ options.data.lang = getCurrentLanguage();
15
+ }
16
+ }
17
+ return next( options );
18
+ } );
19
+
20
+ /**
21
+ * Get the language from the HTML form
22
+ *
23
+ * @since 2.5
24
+ *
25
+ * @return {Element.value}
26
+ */
27
+ function getCurrentLanguage() {
28
+ return document.querySelector( '[name=post_lang_choice]' ).value;
29
+ }
30
+
31
+ /**
32
+ * save post after lang choice is done and redirect to the same page for refreshing all the data
33
+ *
34
+ * @since 2.5
35
+ */
36
+ jQuery( document ).ready(function( $ ) {
37
+ // savePost after changing the post's language and reload page for refreshing post translated data
38
+ $( '.post_lang_choice' ).change(function() {
39
+ const select = wp.data.select;
40
+ const dispatch = wp.data.dispatch;
41
+ const subscribe = wp.data.subscribe;
42
+
43
+ let unsubscribe = null;
44
+
45
+ // Listen if the savePost is done
46
+ const savePostIsDone = new Promise( function( resolve, reject ) {
47
+ unsubscribe = subscribe( function() {
48
+ const isSavePostSucceeded = select('core/editor').didPostSaveRequestSucceed();
49
+ const isSavePostFailed = select('core/editor').didPostSaveRequestFail();
50
+ if ( isSavePostSucceeded || isSavePostFailed ) {
51
+ if ( isSavePostFailed ) {
52
+ reject();
53
+ } else {
54
+ resolve();
55
+ }
56
+ }
57
+ } );
58
+ });
59
+
60
+ // Specific case for empty posts
61
+ if ( location.pathname.match( /post-new.php/gi ) ) {
62
+ const title = select('core/editor').getEditedPostAttribute('title');
63
+ const content = select('core/editor').getEditedPostAttribute('content');
64
+ const excerpt = select('core/editor').getEditedPostAttribute('excerpt');
65
+ if ( '' === title && '' === content && '' === excerpt ) {
66
+ // Change the new_lang parameter with the new language value for reloading the page
67
+ if ( -1 != location.search.indexOf( 'new_lang' ) ) {
68
+ window.location.search = window.location.search.replace( /(?:new_lang=[^&]*)(&)?(.*)/, 'new_lang=' + this.value + '$1$2' );;
69
+ } else {
70
+ window.location.search = window.location.search + ( ( -1 != window.location.search.indexOf( '?' ) ) ? '&' : '?' ) + 'new_lang=' + this.value;
71
+ }
72
+ }
73
+ }
74
+
75
+ // For empty posts savePost does nothing
76
+ dispatch( 'core/editor' ).savePost();
77
+
78
+ savePostIsDone
79
+ .then( function() {
80
+ // If the post is well saved, we can reload the page
81
+ unsubscribe();
82
+ window.location.reload();
83
+ } )
84
+ .catch( function() {
85
+ unsubscribe();
86
+ } );
87
+ } );
88
+ } );
js/block-editor.min.js ADDED
@@ -0,0 +1 @@
 
1
+ wp.apiFetch.use(function(options,next){if('undefined'===typeof options.url){if('undefined'===typeof options.data){options.path+=((options.path.indexOf('?')>=0)?'&lang=':'?lang=')+getCurrentLanguage()}else{options.data.lang=getCurrentLanguage()}}return next(options)});function getCurrentLanguage(){return document.querySelector('[name=post_lang_choice]').value}jQuery(document).ready(function($){$('.post_lang_choice').change(function(){const select=wp.data.select;const dispatch=wp.data.dispatch;const subscribe=wp.data.subscribe;let unsubscribe=null;const savePostIsDone=new Promise(function(resolve,reject){unsubscribe=subscribe(function(){const isSavePostSucceeded=select('core/editor').didPostSaveRequestSucceed();const isSavePostFailed=select('core/editor').didPostSaveRequestFail();if(isSavePostSucceeded||isSavePostFailed){if(isSavePostFailed){reject()}else{resolve()}}})});if(location.pathname.match(/post-new.php/gi)){const title=select('core/editor').getEditedPostAttribute('title');const content=select('core/editor').getEditedPostAttribute('content');const excerpt=select('core/editor').getEditedPostAttribute('excerpt');if(''===title&&''===content&&''===excerpt){if(-1!=location.search.indexOf('new_lang')){window.location.search=window.location.search.replace(/(?:new_lang=[^&]*)(&)?(.*)/,'new_lang='+this.value+'$1$2');}else{window.location.search=window.location.search+((-1!=window.location.search.indexOf('?'))?'&':'?')+'new_lang='+this.value}}}dispatch('core/editor').savePost();savePostIsDone.then(function(){unsubscribe();window.location.reload()}).catch(function(){unsubscribe()})})});
modules/sync/admin-sync.php CHANGED
@@ -18,7 +18,9 @@ class PLL_Admin_Sync extends PLL_Sync {
18
  parent::__construct( $polylang );
19
 
20
  add_filter( 'wp_insert_post_parent', array( $this, 'wp_insert_post_parent' ), 10, 3 );
21
- add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 5, 2 ); // Before Types which populates custom fields in same hook with priority 10
 
 
22
  }
23
 
24
  /**
@@ -37,38 +39,55 @@ class PLL_Admin_Sync extends PLL_Sync {
37
  }
38
 
39
  /**
40
- * Copy post metas, menu order, comment and ping status when using "Add new" ( translation )
41
- * formerly used dbx_post_advanced deprecated in WP 3.7
42
  *
43
- * @since 1.2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  *
45
- * @param string $post_type unused
46
- * @param object $post current post object
47
  */
48
- public function add_meta_boxes( $post_type, $post ) {
49
- if ( 'post-new.php' == $GLOBALS['pagenow'] && isset( $_GET['from_post'], $_GET['new_lang'] ) && $this->model->is_translated_post_type( $post->post_type ) ) {
 
 
 
50
  // Capability check already done in post-new.php
51
  $from_post_id = (int) $_GET['from_post'];
52
- $from_post = get_post( $from_post_id );
53
- $lang = $this->model->get_language( $_GET['new_lang'] );
54
 
55
- if ( ! $from_post || ! $lang ) {
56
  return;
57
  }
58
 
 
 
59
  $this->taxonomies->copy( $from_post_id, $post->ID, $lang->slug );
60
  $this->post_metas->copy( $from_post_id, $post->ID, $lang->slug );
61
 
62
- foreach ( array( 'menu_order', 'comment_status', 'ping_status' ) as $property ) {
63
- $post->$property = $from_post->$property;
64
- }
65
-
66
- // Copy the date only if the synchronization is activated
67
- if ( in_array( 'post_date', $this->options['sync'] ) ) {
68
- $post->post_date = $from_post->post_date;
69
- $post->post_date_gmt = $from_post->post_date_gmt;
70
- }
71
-
72
  if ( is_sticky( $from_post_id ) ) {
73
  stick_post( $post->ID );
74
  }
@@ -76,7 +95,7 @@ class PLL_Admin_Sync extends PLL_Sync {
76
  }
77
 
78
  /**
79
- * Get post fields to synchornize
80
  *
81
  * @since 2.4
82
  *
@@ -89,7 +108,7 @@ class PLL_Admin_Sync extends PLL_Sync {
89
  $postarr = parent::get_fields_to_sync( $post );
90
 
91
  // For new drafts, save the date now otherwise it is overriden by WP. Thanks to JoryHogeveen. See #32.
92
- if ( in_array( 'post_date', $this->options['sync'] ) && 'post-new.php' === $GLOBALS['pagenow'] && isset( $_GET['from_post'], $_GET['new_lang'] ) ) {
93
  unset( $postarr['post_date'] );
94
  unset( $postarr['post_date_gmt'] );
95
 
18
  parent::__construct( $polylang );
19
 
20
  add_filter( 'wp_insert_post_parent', array( $this, 'wp_insert_post_parent' ), 10, 3 );
21
+ add_filter( 'wp_insert_post_data', array( $this, 'wp_insert_post_data' ) );
22
+ add_action( 'rest_api_init', array( $this, 'new_post_translation' ) ); // Block editor
23
+ add_action( 'add_meta_boxes', array( $this, 'new_post_translation' ), 5 ); // Classic editor, before Types which populates custom fields in same hook with priority 10
24
  }
25
 
26
  /**
39
  }
40
 
41
  /**
42
+ * Copy menu order, comment, ping status and optionally the date when creating a new tanslation
 
43
  *
44
+ * @since 2.5
45
+ *
46
+ * @param array $data An array of slashed post data.
47
+ * @return array
48
+ */
49
+ public function wp_insert_post_data( $data ) {
50
+ if ( isset( $GLOBALS['pagenow'], $_GET['from_post'], $_GET['new_lang'] ) && 'post-new.php' === $GLOBALS['pagenow'] && $this->model->is_translated_post_type( $data['post_type'] ) ) {
51
+ $from_post_id = (int) $_GET['from_post'];
52
+ $from_post = get_post( $from_post_id );
53
+
54
+ foreach ( array( 'menu_order', 'comment_status', 'ping_status' ) as $property ) {
55
+ $data[ $property ] = $from_post->$property;
56
+ }
57
+
58
+ // Copy the date only if the synchronization is activated
59
+ if ( in_array( 'post_date', PLL()->options['sync'] ) ) {
60
+ $data['post_date'] = $from_post->post_date;
61
+ $data['post_date_gmt'] = $from_post->post_date_gmt;
62
+ }
63
+ }
64
+
65
+ return $data;
66
+ }
67
+
68
+ /**
69
+ * Copy post metas, and taxonomies when using "Add new" ( translation )
70
  *
71
+ * @since 2.5
 
72
  */
73
+ public function new_post_translation() {
74
+ global $post;
75
+ static $done = array();
76
+
77
+ if ( isset( $GLOBALS['pagenow'], $_GET['from_post'], $_GET['new_lang'] ) && 'post-new.php' === $GLOBALS['pagenow'] && $this->model->is_translated_post_type( $post->post_type ) ) {
78
  // Capability check already done in post-new.php
79
  $from_post_id = (int) $_GET['from_post'];
80
+ $lang = $this->model->get_language( $_GET['new_lang'] );
 
81
 
82
+ if ( ! $from_post_id || ! $lang || ! empty( $done[ $from_post_id ] ) ) {
83
  return;
84
  }
85
 
86
+ $done[ $from_post_id ] = true; // Avoid a second duplication in the block editor. Using an array only to allow multiple phpunit tests.
87
+
88
  $this->taxonomies->copy( $from_post_id, $post->ID, $lang->slug );
89
  $this->post_metas->copy( $from_post_id, $post->ID, $lang->slug );
90
 
 
 
 
 
 
 
 
 
 
 
91
  if ( is_sticky( $from_post_id ) ) {
92
  stick_post( $post->ID );
93
  }
95
  }
96
 
97
  /**
98
+ * Get post fields to synchronize
99
  *
100
  * @since 2.4
101
  *
108
  $postarr = parent::get_fields_to_sync( $post );
109
 
110
  // For new drafts, save the date now otherwise it is overriden by WP. Thanks to JoryHogeveen. See #32.
111
+ if ( in_array( 'post_date', $this->options['sync'] ) && isset( $GLOBALS['pagenow'], $_GET['from_post'], $_GET['new_lang'] ) && 'post-new.php' === $GLOBALS['pagenow'] ) {
112
  unset( $postarr['post_date'] );
113
  unset( $postarr['post_date_gmt'] );
114
 
modules/wpml/wpml-api.php CHANGED
@@ -20,23 +20,31 @@ class PLL_WPML_API {
20
 
21
  add_filter( 'wpml_active_languages', array( $this, 'wpml_active_languages' ), 10, 2 );
22
  add_filter( 'wpml_display_language_names', array( $this, 'wpml_display_language_names' ), 10, 5 );
23
- // wpml_translated_language_name => not applicable
24
  add_filter( 'wpml_current_language', 'pll_current_language', 10, 0 );
25
  add_filter( 'wpml_default_language', 'pll_default_language', 10, 0 );
26
- // wpml_add_language_selector => not implemented
27
- // wpml_footer_language_selector => not applicable
28
  add_action( 'wpml_add_language_form_field', array( $this, 'wpml_add_language_form_field' ) );
29
  add_filter( 'wpml_language_is_active', array( $this, 'wpml_language_is_active' ), 10, 2 );
30
  add_filter( 'wpml_is_rtl', array( $this, 'wpml_is_rtl' ) );
31
- // wpml_language_form_input_field => See wpml_add_language_form_field
32
- // wpml_language_has_switched => not implemented
 
 
 
 
 
 
 
 
33
 
34
  // Retrieving Language Information for Content
35
 
36
  add_filter( 'wpml_post_language_details', 'wpml_get_language_information', 10, 2 );
37
- // wpml_switch_language => not implemented
38
  add_filter( 'wpml_element_language_code', array( $this, 'wpml_element_language_code' ), 10, 3 );
39
- // wpml_element_language_details => not applicable
40
 
41
  // Retrieving Localized Content
42
 
@@ -44,47 +52,59 @@ class PLL_WPML_API {
44
  add_filter( 'wpml_element_link', 'icl_link_to_element', 10, 7 );
45
  add_filter( 'wpml_object_id', 'icl_object_id', 10, 4 );
46
  add_filter( 'wpml_translate_single_string', array( $this, 'wpml_translate_single_string' ), 10, 4 );
47
- // wpml_translate_string => not applicable
48
- // wpml_unfiltered_admin_string => not implemented
49
  add_filter( 'wpml_permalink', array( $this, 'wpml_permalink' ), 10, 2 );
50
- // wpml_elements_without_translations => not implemented
51
  add_filter( 'wpml_get_translated_slug', array( $this, 'wpml_get_translated_slug' ), 10, 3 );
52
 
53
  // Finding the Translation State of Content
54
 
55
- // wpml_element_translation_type
56
  add_filter( 'wpml_element_has_translations', array( $this, 'wpml_element_has_translations' ), 10, 3 );
57
- // wpml_master_post_from_duplicate => not applicable
58
- // wpml_post_duplicates => not applicable
59
 
60
  // Inserting Content
61
 
62
- // wpml_admin_make_post_duplicates => not applicable
63
- // wpml_make_post_duplicates => not applicable
64
  add_action( 'wpml_register_single_string', 'icl_register_string', 10, 3 );
65
- // wpml_register_string => not applicable
66
- // wpml_register_string_packages => not applicable
67
- // wpml_delete_package_action => not applicable
68
- // wpml_show_package_language_ui => not applicable
69
- // wpml_set_element_language_details => not implemented
 
70
 
71
  // Miscellaneous
72
 
73
- // wpml_element_type => not applicable
74
- // wpml_setting => not applicable
75
- // wpml_sub_setting => not applicable
76
- // wpml_editor_cf_to_display => not applicable
77
- // wpml_tm_save_translation_cf => not implemented
78
- // wpml_tm_xliff_export_translated_cf => not applicable
79
- // wpml_tm_xliff_export_original_cf => not applicable
80
- // wpml_duplicate_generic_string => not applicable
81
- // wpml_translatable_user_meta_fields => not implemented
82
- // wpml_cross_domain_language_data => not applicable
83
- // wpml_get_cross_domain_language_data => not applicable
84
- // wpml_loaded => not applicable
85
- // wpml_st_loaded => not applicable
86
- // wpml_tm_loaded => not applicable
87
- // wpml_hide_management_column (3.4.1) => not applicable
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
 
90
  /**
20
 
21
  add_filter( 'wpml_active_languages', array( $this, 'wpml_active_languages' ), 10, 2 );
22
  add_filter( 'wpml_display_language_names', array( $this, 'wpml_display_language_names' ), 10, 5 );
23
+ // wpml_translated_language_name => not applicable
24
  add_filter( 'wpml_current_language', 'pll_current_language', 10, 0 );
25
  add_filter( 'wpml_default_language', 'pll_default_language', 10, 0 );
26
+ // wpml_add_language_selector => not implemented
27
+ // wpml_footer_language_selector => not applicable
28
  add_action( 'wpml_add_language_form_field', array( $this, 'wpml_add_language_form_field' ) );
29
  add_filter( 'wpml_language_is_active', array( $this, 'wpml_language_is_active' ), 10, 2 );
30
  add_filter( 'wpml_is_rtl', array( $this, 'wpml_is_rtl' ) );
31
+ // wpml_language_form_input_field => See wpml_add_language_form_field
32
+ // wpml_language_has_switched => not implemented
33
+ // wpml_element_trid => not implemented
34
+ // wpml_get_element_translations => not implemented
35
+ // wpml_language_switcher => not implemented
36
+ // wpml_browser_redirect_language_params => not implemented
37
+ // wpml_enqueue_browser_redirect_language => not applicable
38
+ // wpml_enqueued_browser_redirect_language => not applicable
39
+ // wpml_encode_string => not applicable
40
+ // wpml_decode_string => not applicable
41
 
42
  // Retrieving Language Information for Content
43
 
44
  add_filter( 'wpml_post_language_details', 'wpml_get_language_information', 10, 2 );
45
+ // wpml_switch_language => not implemented
46
  add_filter( 'wpml_element_language_code', array( $this, 'wpml_element_language_code' ), 10, 3 );
47
+ // wpml_element_language_details => not applicable
48
 
49
  // Retrieving Localized Content
50
 
52
  add_filter( 'wpml_element_link', 'icl_link_to_element', 10, 7 );
53
  add_filter( 'wpml_object_id', 'icl_object_id', 10, 4 );
54
  add_filter( 'wpml_translate_single_string', array( $this, 'wpml_translate_single_string' ), 10, 4 );
55
+ // wpml_translate_string => not applicable
56
+ // wpml_unfiltered_admin_string => not implemented
57
  add_filter( 'wpml_permalink', array( $this, 'wpml_permalink' ), 10, 2 );
58
+ // wpml_elements_without_translations => not implemented
59
  add_filter( 'wpml_get_translated_slug', array( $this, 'wpml_get_translated_slug' ), 10, 3 );
60
 
61
  // Finding the Translation State of Content
62
 
63
+ // wpml_element_translation_type => not implemented
64
  add_filter( 'wpml_element_has_translations', array( $this, 'wpml_element_has_translations' ), 10, 3 );
65
+ // wpml_master_post_from_duplicate => not applicable
66
+ // wpml_post_duplicates => not applicable
67
 
68
  // Inserting Content
69
 
70
+ // wpml_admin_make_post_duplicates => not applicable
71
+ // wpml_make_post_duplicates => not applicable
72
  add_action( 'wpml_register_single_string', 'icl_register_string', 10, 3 );
73
+ // wpml_register_string => not applicable
74
+ // wpml_register_string_packages => not applicable
75
+ // wpml_delete_package_action => not applicable
76
+ // wpml_show_package_language_ui => not applicable
77
+ // wpml_set_element_language_details => not implemented
78
+ // wpml_multilingual_options => not applicable
79
 
80
  // Miscellaneous
81
 
82
+ // wpml_element_type => not applicable
83
+ // wpml_setting => not applicable
84
+ // wpml_sub_setting => not applicable
85
+ // wpml_editor_cf_to_display => not applicable
86
+ // wpml_tm_save_translation_cf => not implemented
87
+ // wpml_tm_xliff_export_translated_cf => not applicable
88
+ // wpml_tm_xliff_export_original_cf => not applicable
89
+ // wpml_duplicate_generic_string => not applicable
90
+ // wpml_translatable_user_meta_fields => not implemented
91
+ // wpml_cross_domain_language_data => not applicable
92
+ // wpml_get_cross_domain_language_data => not applicable
93
+ // wpml_loaded => not applicable
94
+ // wpml_st_loaded => not applicable
95
+ // wpml_tm_loaded => not applicable
96
+ // wpml_hide_management_column (3.4.1) => not applicable
97
+ // wpml_ls_directories_to_scan => not applicable
98
+ // wpml_ls_model_css_classes => not applicable
99
+ // wpml_ls_model_language_css_classes => not applicable
100
+ // wpml_tf_feedback_open_link => not applicable
101
+ // wpml_sync_custom_field => not implemented
102
+ // wpml_sync_all_custom_fields => not implemented
103
+ // wpml_is_redirected => not implemented
104
+
105
+ // Updating Content
106
+
107
+ // wpml_set_translation_mode_for_post_type => not implemented
108
  }
109
 
110
  /**
polylang.php CHANGED
@@ -3,7 +3,7 @@
3
  /**
4
  Plugin Name: Polylang
5
  Plugin URI: https://polylang.pro
6
- Version: 2.4.1
7
  Author: Frédéric Demarle
8
  Author uri: https://polylang.pro
9
  Description: Adds multilingual capability to WordPress
@@ -53,7 +53,7 @@ if ( defined( 'POLYLANG_BASENAME' ) ) {
53
  }
54
  } else {
55
  // Go on loading the plugin
56
- define( 'POLYLANG_VERSION', '2.4.1' );
57
  define( 'PLL_MIN_WP_VERSION', '4.7' );
58
 
59
  define( 'POLYLANG_FILE', __FILE__ ); // this file
3
  /**
4
  Plugin Name: Polylang
5
  Plugin URI: https://polylang.pro
6
+ Version: 2.5
7
  Author: Frédéric Demarle
8
  Author uri: https://polylang.pro
9
  Description: Adds multilingual capability to WordPress
53
  }
54
  } else {
55
  // Go on loading the plugin
56
+ define( 'POLYLANG_VERSION', '2.5' );
57
  define( 'PLL_MIN_WP_VERSION', '4.7' );
58
 
59
  define( 'POLYLANG_FILE', __FILE__ ); // this file
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: Chouby
3
  Donate link: https://polylang.pro
4
  Tags: multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
  Requires at least: 4.7
6
- Tested up to: 4.9
7
- Stable tag: 2.4.1
8
  License: GPLv2 or later
9
 
10
  Making WordPress multilingual
@@ -76,6 +76,12 @@ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-
76
 
77
  == Changelog ==
78
 
 
 
 
 
 
 
79
  = 2.4.1 (2018-11-27) =
80
 
81
  * Pro: Add compatibility with REST API changes made in WP 5.0
3
  Donate link: https://polylang.pro
4
  Tags: multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
  Requires at least: 4.7
6
+ Tested up to: 5.0
7
+ Stable tag: 2.5
8
  License: GPLv2 or later
9
 
10
  Making WordPress multilingual
76
 
77
  == Changelog ==
78
 
79
+ = 2.5 (2018-12-06) =
80
+
81
+ * Add compatibility with WP 5.0
82
+ * Fix custom flags when the WP content folder is not in the WP install folder
83
+ * Fix PHP notice if a language has no flag
84
+
85
  = 2.4.1 (2018-11-27) =
86
 
87
  * Pro: Add compatibility with REST API changes made in WP 5.0