Polylang - Version 2.9

Version Description

(2020-12-07) =

  • Add compatibility with WordPress 5.6
  • Pro: Add locale fallback used when the theme or plugins translations are not available
  • Pro: Fix SSO and browser preferred language redirect when using multiple domains
  • Pro: Fix post slugs for German and Danish in the REST API
  • Pro: Fix a fatal error in ACF integration when saving url modifications with multiple domains
  • Pro: Fix a deprecated notice fired by ACF since the version 5.9.2
  • Pro: Fix ACF relationship fields not reloaded when changing the language in the classic editor
  • Update plugin updater to version 1.8
  • Add Lower Sorbian to the list of predefined language
  • Options are now translated on backend when using the admin language filter
  • Keep previous translations when modifying an option value
  • Add navigation markup to the language switcher widget
  • Fix canonical redirect for taxonomy terms
  • Fix a fatal error when deleting a post with a translation group corrupted in the database
  • Fix a fatal error when switching to plain permalinks and using multiple domains
  • Fix a conflict with WP Sweep which could corrupt languages
  • Fix title displayed instead of meta description with Yoast SEO > 14.0
  • Fix PHP Notice: Undefined index: wp_the_query in /frontend/choose-lang-content.php on line 92
Download this release

Release Info

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

Code changes from version 2.8.4 to 2.9

Files changed (69) hide show
  1. admin/admin-base.php +36 -35
  2. admin/admin-classic-editor.php +3 -4
  3. admin/admin-filters-columns.php +3 -4
  4. admin/admin-filters-term.php +3 -3
  5. admin/admin-filters.php +4 -83
  6. admin/admin-model.php +1 -1
  7. admin/admin-strings.php +0 -16
  8. admin/admin.php +94 -20
  9. css/admin.css +42 -1
  10. css/admin.min.css +1 -1
  11. css/selectmenu.css +52 -9
  12. css/selectmenu.min.css +1 -1
  13. frontend/choose-lang-content.php +1 -1
  14. frontend/choose-lang.php +6 -22
  15. frontend/frontend-filters-links.php +87 -6
  16. frontend/frontend-filters.php +5 -14
  17. frontend/frontend-static-pages.php +2 -3
  18. include/api.php +1 -1
  19. include/base.php +6 -0
  20. include/class-polylang.php +2 -2
  21. include/cookie.php +82 -0
  22. include/filters-sanitization.php +106 -0
  23. include/functions.php +0 -45
  24. include/mo.php +11 -0
  25. include/olt-manager.php +9 -10
  26. include/switcher.php +1 -1
  27. include/translate-option.php +326 -0
  28. include/translated-object.php +22 -5
  29. include/walker-dropdown.php +2 -2
  30. include/walker-list.php +2 -2
  31. include/widget-calendar.php +1 -1
  32. include/widget-languages.php +26 -51
  33. install/install-base.php +2 -21
  34. install/plugin-updater.php +43 -56
  35. integrations/cache/cache-compat.php +6 -3
  36. integrations/integrations.php +1 -1
  37. integrations/jetpack/jetpack.php +7 -10
  38. integrations/twenty-seventeen/twenty-seven-teen.php +2 -5
  39. integrations/wp-sweep/wp-sweep.php +29 -0
  40. integrations/wpseo/wpseo.php +19 -75
  41. js/admin.js +205 -67
  42. js/admin.min.js +1 -1
  43. js/block-editor.js +9 -6
  44. js/block-editor.min.js +1 -1
  45. js/classic-editor.js +58 -50
  46. js/classic-editor.min.js +1 -1
  47. js/post.js +136 -124
  48. js/post.min.js +1 -1
  49. js/term.js +103 -95
  50. js/term.min.js +1 -1
  51. js/widgets.js +32 -0
  52. js/widgets.min.js +1 -1
  53. modules/share-slug/settings-share-slug.php +13 -11
  54. modules/site-health/admin-site-health.php +1 -1
  55. modules/sitemaps/sitemaps.php +9 -6
  56. modules/wizard/js/languages-step.js +12 -12
  57. modules/wizard/js/languages-step.min.js +1 -1
  58. modules/wpml/wpml-api.php +6 -9
  59. modules/wpml/wpml-config.php +15 -107
  60. modules/wpml/wpml-legacy-api.php +5 -9
  61. polylang.php +7 -6
  62. readme.txt +25 -4
  63. settings/languages.php +7 -0
  64. settings/settings-browser.php +13 -11
  65. settings/settings-module.php +1 -1
  66. settings/settings.php +2 -2
  67. settings/table-settings.php +1 -1
  68. vendor/composer/autoload_classmap.php +3 -0
  69. vendor/composer/autoload_static.php +3 -0
admin/admin-base.php CHANGED
@@ -22,9 +22,6 @@ class PLL_Admin_Base extends PLL_Base {
22
  public function __construct( &$links_model ) {
23
  parent::__construct( $links_model );
24
 
25
- // Plugin i18n, only needed for backend
26
- load_plugin_textdomain( 'polylang' );
27
-
28
  // Adds the link to the languages panel in the WordPress admin menu
29
  add_action( 'admin_menu', array( $this, 'add_menus' ) );
30
 
@@ -219,32 +216,34 @@ class PLL_Admin_Base extends PLL_Base {
219
  ?>
220
  <script type="text/javascript">
221
  if (typeof jQuery != 'undefined') {
222
- (function($){
223
- $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
224
- if ( -1 != options.url.indexOf( ajaxurl ) || -1 != ajaxurl.indexOf( options.url ) ) {
225
- if ( 'undefined' === typeof options.data ) {
226
- options.data = ( 'get' === options.type.toLowerCase() ) ? '<?php echo $str; // phpcs:ignore WordPress.Security.EscapeOutput ?>' : <?php echo $arr; // phpcs:ignore WordPress.Security.EscapeOutput ?>;
227
- } else {
228
- if ( 'string' === typeof options.data ) {
229
- if ( '' === options.data && 'get' === options.type.toLowerCase() ) {
230
- options.url = options.url+'&<?php echo $str; // phpcs:ignore WordPress.Security.EscapeOutput ?>';
231
- } else {
232
- try {
233
- var o = $.parseJSON(options.data);
234
- o = $.extend(o, <?php echo $arr; // phpcs:ignore WordPress.Security.EscapeOutput ?>);
235
- options.data = JSON.stringify(o);
236
- }
237
- catch(e) {
238
- options.data = '<?php echo $str; // phpcs:ignore WordPress.Security.EscapeOutput ?>&'+options.data;
 
 
239
  }
 
 
240
  }
241
- } else {
242
- options.data = $.extend(options.data, <?php echo $arr; // phpcs:ignore WordPress.Security.EscapeOutput ?>);
243
  }
244
  }
245
- }
246
- });
247
- })(jQuery)
248
  }
249
  </script>
250
  <?php
@@ -284,6 +283,15 @@ class PLL_Admin_Base extends PLL_Base {
284
  if ( wp_doing_ajax() && ! empty( $_REQUEST['lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
285
  $this->curlang = $this->model->get_language( sanitize_key( $_REQUEST['lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
286
  }
 
 
 
 
 
 
 
 
 
287
  }
288
 
289
  /**
@@ -316,22 +324,15 @@ class PLL_Admin_Base extends PLL_Base {
316
 
317
  $this->set_current_language();
318
 
319
- // Inform that the admin language has been set
320
- // Only if the admin language is one of the Polylang defined language
321
- if ( $curlang = $this->model->get_language( get_user_locale() ) ) {
322
- /** This action is documented in frontend/choose-lang.php */
323
- do_action( 'pll_language_defined', $curlang->slug, $curlang );
324
- } else {
325
- /** This action is documented in include/class-polylang.php */
326
- do_action( 'pll_no_language_defined' ); // to load overridden textdomains
327
- }
328
  }
329
 
330
  /**
331
  * Avoids parsing a tax query when all languages are requested
332
  * Fixes https://wordpress.org/support/topic/notice-undefined-offset-0-in-wp-includesqueryphp-on-line-3877 introduced in WP 4.1
333
  *
334
- * @see the suggestion of @boonebgorges, https://core.trac.wordpress.org/ticket/31246
335
  *
336
  * @since 1.6.5
337
  *
22
  public function __construct( &$links_model ) {
23
  parent::__construct( $links_model );
24
 
 
 
 
25
  // Adds the link to the languages panel in the WordPress admin menu
26
  add_action( 'admin_menu', array( $this, 'add_menus' ) );
27
 
216
  ?>
217
  <script type="text/javascript">
218
  if (typeof jQuery != 'undefined') {
219
+ jQuery(
220
+ function($){
221
+ $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
222
+ if ( -1 != options.url.indexOf( ajaxurl ) || -1 != ajaxurl.indexOf( options.url ) ) {
223
+ if ( 'undefined' === typeof options.data ) {
224
+ options.data = ( 'get' === options.type.toLowerCase() ) ? '<?php echo $str; // phpcs:ignore WordPress.Security.EscapeOutput ?>' : <?php echo $arr; // phpcs:ignore WordPress.Security.EscapeOutput ?>;
225
+ } else {
226
+ if ( 'string' === typeof options.data ) {
227
+ if ( '' === options.data && 'get' === options.type.toLowerCase() ) {
228
+ options.url = options.url+'&<?php echo $str; // phpcs:ignore WordPress.Security.EscapeOutput ?>';
229
+ } else {
230
+ try {
231
+ var o = JSON.parse(options.data);
232
+ o = $.extend(o, <?php echo $arr; // phpcs:ignore WordPress.Security.EscapeOutput ?>);
233
+ options.data = JSON.stringify(o);
234
+ }
235
+ catch(e) {
236
+ options.data = '<?php echo $str; // phpcs:ignore WordPress.Security.EscapeOutput ?>&'+options.data;
237
+ }
238
  }
239
+ } else {
240
+ options.data = $.extend(options.data, <?php echo $arr; // phpcs:ignore WordPress.Security.EscapeOutput ?>);
241
  }
 
 
242
  }
243
  }
244
+ });
245
+ }
246
+ );
247
  }
248
  </script>
249
  <?php
283
  if ( wp_doing_ajax() && ! empty( $_REQUEST['lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
284
  $this->curlang = $this->model->get_language( sanitize_key( $_REQUEST['lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
285
  }
286
+
287
+ // Inform that the admin language has been set.
288
+ if ( $this->curlang ) {
289
+ /** This action is documented in frontend/choose-lang.php */
290
+ do_action( 'pll_language_defined', $this->curlang->slug, $this->curlang );
291
+ } else {
292
+ /** This action is documented in include/class-polylang.php */
293
+ do_action( 'pll_no_language_defined' ); // To load overridden textdomains.
294
+ }
295
  }
296
 
297
  /**
324
 
325
  $this->set_current_language();
326
 
327
+ // Plugin i18n, only needed for backend.
328
+ load_plugin_textdomain( 'polylang' );
 
 
 
 
 
 
 
329
  }
330
 
331
  /**
332
  * Avoids parsing a tax query when all languages are requested
333
  * Fixes https://wordpress.org/support/topic/notice-undefined-offset-0-in-wp-includesqueryphp-on-line-3877 introduced in WP 4.1
334
  *
335
+ * @see https://core.trac.wordpress.org/ticket/31246 the suggestion of @boonebgorges.
336
  *
337
  * @since 1.6.5
338
  *
admin/admin-classic-editor.php CHANGED
@@ -25,7 +25,7 @@ class PLL_Admin_Classic_Editor {
25
  $this->pref_lang = &$polylang->pref_lang;
26
 
27
  // Adds the Languages box in the 'Edit Post' and 'Edit Page' panels
28
- add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 10, 2 );
29
 
30
  // Ajax response for changing the language in the post metabox
31
  add_action( 'wp_ajax_post_lang_choice', array( $this, 'post_lang_choice' ) );
@@ -44,9 +44,8 @@ class PLL_Admin_Classic_Editor {
44
  * @since 0.1
45
  *
46
  * @param string $post_type Current post type
47
- * @param object $post Current post
48
  */
49
- public function add_meta_boxes( $post_type, $post ) {
50
  if ( $this->model->is_translated_post_type( $post_type ) ) {
51
  add_meta_box(
52
  'ml_box',
@@ -71,7 +70,7 @@ class PLL_Admin_Classic_Editor {
71
  global $post_ID;
72
  $post_type = get_post_type( $post_ID );
73
 
74
- // phpcs:ignore WordPress.Security.NonceVerification, WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable
75
  $from_post_id = isset( $_GET['from_post'] ) ? (int) $_GET['from_post'] : 0;
76
 
77
  $lang = ( $lg = $this->model->post->get_language( $post_ID ) ) ? $lg :
25
  $this->pref_lang = &$polylang->pref_lang;
26
 
27
  // Adds the Languages box in the 'Edit Post' and 'Edit Page' panels
28
+ add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
29
 
30
  // Ajax response for changing the language in the post metabox
31
  add_action( 'wp_ajax_post_lang_choice', array( $this, 'post_lang_choice' ) );
44
  * @since 0.1
45
  *
46
  * @param string $post_type Current post type
 
47
  */
48
+ public function add_meta_boxes( $post_type ) {
49
  if ( $this->model->is_translated_post_type( $post_type ) ) {
50
  add_meta_box(
51
  'ml_box',
70
  global $post_ID;
71
  $post_type = get_post_type( $post_ID );
72
 
73
+ // phpcs:ignore WordPress.Security.NonceVerification, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
74
  $from_post_id = isset( $_GET['from_post'] ) ? (int) $_GET['from_post'] : 0;
75
 
76
  $lang = ( $lg = $this->model->post->get_language( $post_ID ) ) ? $lg :
admin/admin-filters-columns.php CHANGED
@@ -36,8 +36,8 @@ class PLL_Admin_Filters_Columns {
36
  }
37
 
38
  // Quick edit and bulk edit.
39
- add_filter( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
40
- add_filter( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
41
 
42
  // Adds the language column in the 'Categories' and 'Post Tags' tables.
43
  foreach ( $this->model->get_translated_taxonomies() as $tax ) {
@@ -185,10 +185,9 @@ class PLL_Admin_Filters_Columns {
185
  * @since 0.9
186
  *
187
  * @param string $column column name
188
- * @param string $type either 'edit-tags' for terms list table or post type for posts list table
189
  * @return string unmodified $column
190
  */
191
- public function quick_edit_custom_box( $column, $type ) {
192
  if ( $column == $this->get_first_language_column() ) {
193
 
194
  $elements = $this->model->get_languages_list();
36
  }
37
 
38
  // Quick edit and bulk edit.
39
+ add_filter( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ) );
40
+ add_filter( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ) );
41
 
42
  // Adds the language column in the 'Categories' and 'Post Tags' tables.
43
  foreach ( $this->model->get_translated_taxonomies() as $tax ) {
185
  * @since 0.9
186
  *
187
  * @param string $column column name
 
188
  * @return string unmodified $column
189
  */
190
+ public function quick_edit_custom_box( $column ) {
191
  if ( $column == $this->get_first_language_column() ) {
192
 
193
  $elements = $this->model->get_languages_list();
admin/admin-filters-term.php CHANGED
@@ -137,7 +137,7 @@ class PLL_Admin_Filters_Term {
137
  }
138
 
139
  $term_id = $tag->term_id;
140
- $taxonomy = $tag->taxonomy; // phpcs:ignore WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable
141
 
142
  $lang = $this->model->term->get_language( $term_id );
143
  $lang = empty( $lang ) ? $this->pref_lang : $lang;
@@ -443,8 +443,8 @@ class PLL_Admin_Filters_Term {
443
  }
444
 
445
  $lang = $this->model->get_language( sanitize_key( $_POST['lang'] ) );
446
- $term_id = isset( $_POST['term_id'] ) ? (int) $_POST['term_id'] : null; // phpcs:ignore WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable
447
- $taxonomy = sanitize_key( $_POST['taxonomy'] ); // phpcs:ignore WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable
448
  $post_type = sanitize_key( $_POST['post_type'] );
449
 
450
  if ( ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
137
  }
138
 
139
  $term_id = $tag->term_id;
140
+ $taxonomy = $tag->taxonomy; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
141
 
142
  $lang = $this->model->term->get_language( $term_id );
143
  $lang = empty( $lang ) ? $this->pref_lang : $lang;
443
  }
444
 
445
  $lang = $this->model->get_language( sanitize_key( $_POST['lang'] ) );
446
+ $term_id = isset( $_POST['term_id'] ) ? (int) $_POST['term_id'] : null; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
447
+ $taxonomy = sanitize_key( $_POST['taxonomy'] );
448
  $post_type = sanitize_key( $_POST['post_type'] );
449
 
450
  if ( ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
admin/admin-filters.php CHANGED
@@ -37,13 +37,6 @@ class PLL_Admin_Filters extends PLL_Filters {
37
  add_filter( 'themes_update_check_locales', array( $this, 'update_check_locales' ) );
38
  add_filter( 'plugins_update_check_locales', array( $this, 'update_check_locales' ) );
39
 
40
- // We need specific filters for German and Danish
41
- $specific_locales = array( 'da_DK', 'de_DE', 'de_DE_formal', 'de_CH', 'de_CH_informal', 'ca', 'sr_RS', 'bs_BA' );
42
- if ( array_intersect( $this->model->get_languages_list( array( 'fields' => 'locale' ) ), $specific_locales ) ) {
43
- add_filter( 'sanitize_title', array( $this, 'sanitize_title' ), 10, 3 );
44
- add_filter( 'sanitize_user', array( $this, 'sanitize_user' ), 10, 3 );
45
- }
46
-
47
  add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
48
 
49
  // Add post state for translations of the privacy policy page
@@ -174,87 +167,15 @@ class PLL_Admin_Filters extends PLL_Filters {
174
  }
175
 
176
  /**
177
- * Allows to update translations files for plugins and themes
178
  *
179
  * @since 1.6
180
  *
181
- * @param array $locales Not used
182
- * @return array list of locales to update
183
  */
184
  public function update_check_locales( $locales ) {
185
- return $this->model->get_languages_list( array( 'fields' => 'locale' ) );
186
- }
187
-
188
- /**
189
- * Filters the locale according to the current language instead of the language
190
- * of the admin interface
191
- *
192
- * @since 2.0
193
- *
194
- * @param string $locale
195
- * @return string
196
- */
197
- public function get_locale( $locale ) {
198
- if ( isset( $_POST['post_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['post_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
199
- $locale = $lang->locale;
200
- } elseif ( isset( $_POST['term_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['term_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
201
- $locale = $lang->locale;
202
- } elseif ( isset( $_POST['inline_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
203
- $locale = $lang->locale;
204
- } elseif ( ! empty( $this->curlang ) ) {
205
- $locale = $this->curlang->locale;
206
- }
207
-
208
- return $locale;
209
- }
210
-
211
- /**
212
- * Maybe fix the result of sanitize_title() in case the languages include German or Danish
213
- * Without this filter, if language of the title being sanitized is different from the language
214
- * used for the admin interface and if one this language is German or Danish, some specific
215
- * characters such as ä, ö, ü, ß are incorrectly sanitized.
216
- *
217
- * @since 2.0
218
- *
219
- * @param string $title Sanitized title.
220
- * @param string $raw_title The title prior to sanitization.
221
- * @param string $context The context for which the title is being sanitized.
222
- * @return string
223
- */
224
- public function sanitize_title( $title, $raw_title, $context ) {
225
- static $once = false;
226
-
227
- if ( ! $once && 'save' == $context && ! empty( $title ) ) {
228
- $once = true;
229
- add_filter( 'locale', array( $this, 'get_locale' ), 20 ); // After the filter for the admin interface
230
- $title = sanitize_title( $raw_title, '', $context );
231
- remove_filter( 'locale', array( $this, 'get_locale' ), 20 );
232
- $once = false;
233
- }
234
- return $title;
235
- }
236
-
237
- /**
238
- * Maybe fix the result of sanitize_user() in case the languages include German or Danish
239
- *
240
- * @since 2.0
241
- *
242
- * @param string $username Sanitized username.
243
- * @param string $raw_username The username prior to sanitization.
244
- * @param bool $strict Whether to limit the sanitization to specific characters. Default false.
245
- * @return string
246
- */
247
- public function sanitize_user( $username, $raw_username, $strict ) {
248
- static $once = false;
249
-
250
- if ( ! $once ) {
251
- $once = true;
252
- add_filter( 'locale', array( $this, 'get_locale' ), 20 ); // After the filter for the admin interface
253
- $username = sanitize_user( $raw_username, '', $strict );
254
- remove_filter( 'locale', array( $this, 'get_locale' ), 20 );
255
- $once = false;
256
- }
257
- return $username;
258
  }
259
 
260
  /**
37
  add_filter( 'themes_update_check_locales', array( $this, 'update_check_locales' ) );
38
  add_filter( 'plugins_update_check_locales', array( $this, 'update_check_locales' ) );
39
 
 
 
 
 
 
 
 
40
  add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
41
 
42
  // Add post state for translations of the privacy policy page
167
  }
168
 
169
  /**
170
+ * Allows to update translations files for plugins and themes.
171
  *
172
  * @since 1.6
173
  *
174
+ * @param array $locales List of locales to update for plugins and themes.
175
+ * @return array
176
  */
177
  public function update_check_locales( $locales ) {
178
+ return array_merge( $locales, $this->model->get_languages_list( array( 'fields' => 'locale' ) ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  }
180
 
181
  /**
admin/admin-model.php CHANGED
@@ -259,7 +259,7 @@ class PLL_Admin_Model extends PLL_Model {
259
  /**
260
  * Validates data entered when creating or updating a language
261
  *
262
- * @see PLL_Admin_Model::add_language
263
  *
264
  * @since 0.4
265
  *
259
  /**
260
  * Validates data entered when creating or updating a language
261
  *
262
+ * @see PLL_Admin_Model::add_language()
263
  *
264
  * @since 0.4
265
  *
admin/admin-strings.php CHANGED
@@ -53,21 +53,10 @@ class PLL_Admin_Strings {
53
  */
54
  public static function &get_strings() {
55
  self::$default_strings = array(
56
- 'options' => array(
57
- 'blogname' => __( 'Site Title', 'polylang' ),
58
- 'blogdescription' => __( 'Tagline', 'polylang' ),
59
- 'date_format' => __( 'Date Format', 'polylang' ),
60
- 'time_format' => __( 'Time Format', 'polylang' ),
61
- ),
62
  'widget_title' => __( 'Widget title', 'polylang' ),
63
  'widget_text' => __( 'Widget text', 'polylang' ),
64
  );
65
 
66
- // WP strings
67
- foreach ( self::$default_strings['options'] as $option => $string ) {
68
- self::register_string( $string, get_option( $option ), 'WordPress' );
69
- }
70
-
71
  // Widgets titles
72
  global $wp_registered_widgets;
73
  $sidebars = wp_get_sidebars_widgets();
@@ -121,11 +110,6 @@ class PLL_Admin_Strings {
121
  * @return string
122
  */
123
  public static function sanitize_string_translation( $translation, $name ) {
124
-
125
- if ( false !== ( $option = array_search( $name, self::$default_strings['options'], true ) ) ) {
126
- $translation = sanitize_option( $option, $translation );
127
- }
128
-
129
  if ( $name == self::$default_strings['widget_title'] ) {
130
  $translation = sanitize_text_field( $translation );
131
  }
53
  */
54
  public static function &get_strings() {
55
  self::$default_strings = array(
 
 
 
 
 
 
56
  'widget_title' => __( 'Widget title', 'polylang' ),
57
  'widget_text' => __( 'Widget text', 'polylang' ),
58
  );
59
 
 
 
 
 
 
60
  // Widgets titles
61
  global $wp_registered_widgets;
62
  $sidebars = wp_get_sidebars_widgets();
110
  * @return string
111
  */
112
  public static function sanitize_string_translation( $translation, $name ) {
 
 
 
 
 
113
  if ( $name == self::$default_strings['widget_title'] ) {
114
  $translation = sanitize_text_field( $translation );
115
  }
admin/admin.php CHANGED
@@ -8,30 +8,80 @@
8
  * accessible in $polylang global object
9
  *
10
  * Properties:
11
- * options => inherited, reference to Polylang options array
12
- * model => inherited, reference to PLL_Model object
13
- * links_model => inherited, reference to PLL_Links_Model object
14
- * links => inherited, reference to PLL_Admin_Links object
15
- * static_pages => inherited, reference to PLL_Admin_Static_Pages object
16
- * filters_links => inherited, reference to PLL_Filters_Links object
17
- * curlang => inherited, optional, current language used to filter the content (language of the post or term being edited, equal to filter_lang otherwise)
18
- * filter_lang => inherited, optional, current status of the admin languages filter (in the admin bar)
19
- * pref_lang => inherited, preferred language used as default when saving posts or terms
20
- * posts => reference to PLL_CRUD_Posts object
21
- * terms => reference to PLL_CRUD_Terms object
22
- * filters => reference to PLL_Admin_Filters object
23
- * filters_columns => reference to PLL_Admin_Filters_Columns object
24
- * filters_post => reference to PLL_Admin_Filters_Post object
25
- * filters_term => reference to PLL_Admin_filters_Term object
26
- * nav_menu => reference to PLL_Admin_Nav_Menu object
27
- * block_editor => reference to PLL_Admin_Block_Editor object
28
- * classic_editor => reference to PLL_Admin_Classic_Editor object
29
- * filters_media => optional, reference to PLL_Admin_Filters_Media object
 
30
  *
31
  * @since 1.2
32
  */
33
  class PLL_Admin extends PLL_Admin_Base {
34
- public $filters, $filters_columns, $filters_post, $filters_term, $nav_menu, $filters_media;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  /**
37
  * Loads the polylang text domain
@@ -99,6 +149,7 @@ class PLL_Admin extends PLL_Admin_Base {
99
  * @since 2.7 instantiate a PLL_Bulk_Translate instance.
100
  */
101
  public function add_filters() {
 
102
  // All these are separated just for convenience and maintainability
103
  $classes = array( 'Filters', 'Filters_Columns', 'Filters_Post', 'Filters_Term', 'Nav_Menu', 'Classic_Editor', 'Block_Editor' );
104
 
@@ -121,4 +172,27 @@ class PLL_Admin extends PLL_Admin_Base {
121
  $this->$obj = new $class( $this );
122
  }
123
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  }
8
  * accessible in $polylang global object
9
  *
10
  * Properties:
11
+ * options => inherited, reference to Polylang options array
12
+ * model => inherited, reference to PLL_Model object
13
+ * links_model => inherited, reference to PLL_Links_Model object
14
+ * links => inherited, reference to PLL_Admin_Links object
15
+ * static_pages => inherited, reference to PLL_Admin_Static_Pages object
16
+ * filters_links => inherited, reference to PLL_Filters_Links object
17
+ * curlang => inherited, optional, current language used to filter the content (language of the post or term being edited, equal to filter_lang otherwise)
18
+ * filter_lang => inherited, optional, current status of the admin languages filter (in the admin bar)
19
+ * pref_lang => inherited, preferred language used as default when saving posts or terms
20
+ * posts => reference to PLL_CRUD_Posts object
21
+ * terms => reference to PLL_CRUD_Terms object
22
+ * filters => reference to PLL_Admin_Filters object
23
+ * filters_sanitization => reference to PLL_Filters_Sanitization object
24
+ * filters_columns => reference to PLL_Admin_Filters_Columns object
25
+ * filters_post => reference to PLL_Admin_Filters_Post object
26
+ * filters_term => reference to PLL_Admin_filters_Term object
27
+ * nav_menu => reference to PLL_Admin_Nav_Menu object
28
+ * block_editor => reference to PLL_Admin_Block_Editor object
29
+ * classic_editor => reference to PLL_Admin_Classic_Editor object
30
+ * filters_media => optional, reference to PLL_Admin_Filters_Media object
31
  *
32
  * @since 1.2
33
  */
34
  class PLL_Admin extends PLL_Admin_Base {
35
+ /**
36
+ * Instance of PLL_Admin_Filters
37
+ *
38
+ * @var PLL_Admin_Filters
39
+ */
40
+ public $filters;
41
+
42
+ /**
43
+ * Instance of PLL_Admin_Filters_Columns
44
+ *
45
+ * @var PLL_Admin_Filters_Columns
46
+ */
47
+ public $filters_columns;
48
+
49
+ /**
50
+ * Instance of PLL_Admin_Filters_Post
51
+ *
52
+ * @var PLL_Admin_Filters_Post
53
+ */
54
+ public $filters_post;
55
+
56
+ /**
57
+ * Instance of PLL_Admin_filters_Term
58
+ *
59
+ * @var PLL_Admin_filters_Term
60
+ */
61
+ public $filters_term;
62
+
63
+ /**
64
+ * Instance of PLL_Admin_Nav_Menu
65
+ *
66
+ * @var PLL_Admin_Nav_Menu
67
+ */
68
+ public $nav_menu;
69
+
70
+ /**
71
+ * Instance of PLL_Admin_Filters_Media
72
+ *
73
+ * @var PLL_Admin_Filters_Media
74
+ */
75
+ public $filters_media;
76
+
77
+ /**
78
+ * Instance of PLL_Filters_Sanitization
79
+ *
80
+ * @since 2.9
81
+ *
82
+ * @var PLL_Filters_Sanitization
83
+ */
84
+ public $filters_sanitization;
85
 
86
  /**
87
  * Loads the polylang text domain
149
  * @since 2.7 instantiate a PLL_Bulk_Translate instance.
150
  */
151
  public function add_filters() {
152
+ $this->filters_sanitization = new PLL_Filters_Sanitization( $this->get_locale_for_sanitization() );
153
  // All these are separated just for convenience and maintainability
154
  $classes = array( 'Filters', 'Filters_Columns', 'Filters_Post', 'Filters_Term', 'Nav_Menu', 'Classic_Editor', 'Block_Editor' );
155
 
172
  $this->$obj = new $class( $this );
173
  }
174
  }
175
+ /**
176
+ * Retrieve the locale according to the current language instead of the language
177
+ * of the admin interface.
178
+ *
179
+ * @since 2.0
180
+ *
181
+ * @return string
182
+ */
183
+ public function get_locale_for_sanitization() {
184
+ $locale = get_locale();
185
+
186
+ if ( isset( $_POST['post_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['post_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
187
+ $locale = $lang->locale;
188
+ } elseif ( isset( $_POST['term_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['term_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
189
+ $locale = $lang->locale;
190
+ } elseif ( isset( $_POST['inline_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
191
+ $locale = $lang->locale;
192
+ } elseif ( ! empty( $this->curlang ) ) {
193
+ $locale = $this->curlang->locale;
194
+ }
195
+
196
+ return $locale;
197
+ }
198
  }
css/admin.css CHANGED
@@ -63,9 +63,22 @@
63
  vertical-align: top;
64
  }
65
 
 
 
 
 
 
 
 
 
 
 
 
66
  .translation input,
67
  .translation textarea {
68
  width: 72%;
 
 
69
  }
70
 
71
  /* settings */
@@ -314,6 +327,25 @@
314
  margin-right: 10px;
315
  }
316
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  @media screen and ( max-width: 782px ) {
318
  /* settings */
319
  #wpbody-content .pll-settings .pll-configure > td {
@@ -355,10 +387,19 @@
355
  padding-left: 0;
356
  }
357
 
358
- .translation input {
 
359
  width: 95%;
360
  }
361
 
 
 
 
 
 
 
 
 
362
  /* hide selected language flag and translations language name */
363
  #select-add-term-language .pll-select-flag,
364
  #select-edit-term-language .pll-select-flag,
63
  vertical-align: top;
64
  }
65
 
66
+ .translation {
67
+ display: flex; /* fix #691 to remove default margin bottom */
68
+ }
69
+ @media screen and (max-width: 782px) { /* reset default display property for small device */
70
+ .translation{
71
+ display: block;
72
+ }
73
+ }
74
+ .translation textarea{
75
+ display: block; /* fix #691 to remove default margin bottom */
76
+ }
77
  .translation input,
78
  .translation textarea {
79
  width: 72%;
80
+ box-sizing: border-box; /* to be sure field don't overrun outside their wrapper */
81
+ margin-bottom: 4px; /* fix #691 set the same margin bottom for both textarea and input tags */
82
  }
83
 
84
  /* settings */
327
  margin-right: 10px;
328
  }
329
 
330
+ /* Metaboxes holder in Strings translations screen */
331
+ .languages_page_mlang_strings .metabox-holder > div {
332
+ display: flex;
333
+ }
334
+ .languages_page_mlang_strings .metabox-holder > div > div {
335
+ flex-grow: 1;
336
+ }
337
+ .languages_page_mlang_strings .metabox-holder > div > div:nth-child(2n) {
338
+ margin-left: 1rem;
339
+ }
340
+ .languages_page_mlang_strings .metabox-holder > div > div.closed {
341
+ border:0;
342
+ background: none;
343
+ }
344
+ .languages_page_mlang_strings .metabox-holder > div > div.closed .postbox-header{
345
+ border: 1px solid #ccd0d4;
346
+ background: #fff;
347
+ }
348
+
349
  @media screen and ( max-width: 782px ) {
350
  /* settings */
351
  #wpbody-content .pll-settings .pll-configure > td {
387
  padding-left: 0;
388
  }
389
 
390
+ .translation input,
391
+ .translation textarea {
392
  width: 95%;
393
  }
394
 
395
+ /* Metaboxes holder in Strings translations screen */
396
+ .languages_page_mlang_strings .metabox-holder > div {
397
+ flex-direction: column;
398
+ }
399
+ .languages_page_mlang_strings .metabox-holder > div > div:nth-child(2n) {
400
+ margin-left: 0;
401
+ }
402
+
403
  /* hide selected language flag and translations language name */
404
  #select-add-term-language .pll-select-flag,
405
  #select-edit-term-language .pll-select-flag,
css/admin.min.css CHANGED
@@ -1 +1 @@
1
- #add-lang select{width:95%}.column-locale,.languages .column-slug{width:15%}.column-default_lang{width:5%}.column-count,.column-flag,.column-term_group{width:10%}.icon-default-lang:before{font-family:dashicons;content:"\f155"}.pll-icon:before{display:inline-block;text-align:left;width:15px}.pll-circle:before{content:"\25cf"}.form-field input[type=radio]{width:auto;margin-right:2px}#pll-about-box p,#pll-recommended p{text-align:justify}#pll-about-box input{margin:0;padding:0;float:right}.stringstranslations .column-context,.stringstranslations .column-name{width:10%}.stringstranslations .column-string{width:33%}.translation label{display:inline-block;width:23%;vertical-align:top}.translation input,.translation textarea{width:72%}.pll-settings{margin-top:20px}.pll-settings .plugin-title{width:25%}#wpbody-content .pll-settings .pll-configure tr{display:table-row}#wpbody-content .pll-settings .pll-configure td{display:table-cell}#wpbody-content .pll-settings .pll-configure>td{padding:20px 20px 20px 40px}.pll-configure legend{font-size:14px;font-weight:600;margin-bottom:.5em}.pll-configure td .description{margin-top:2px;margin-bottom:.5em}.pll-configure p.submit{margin-top:20px}.pll-configure .button{margin-right:20px}.pll-configure fieldset{margin-bottom:1.5em}.pll-inline-block-list{margin:0}.pll-inline-block-list li{display:inline-block;margin:0;width:250px}#pll-domains-table td{padding:2px 2px 2px 1.5em;-webkit-box-shadow:none;box-shadow:none;border:none}.pll-settings-url-col{display:inline-block;width:49%;vertical-align:top}#pll-licenses-table td{vertical-align:top}#pll-licenses-table label{font-size:1em;font-weight:600}.pll-configure .pll-deactivate-license{margin:0 0 0 20px}.wp-list-table td[class*=column-language_],.wp-list-table th[class*=column-language_]{width:1.5em;box-sizing:content-box}.pll-dir-rtl input[type=text],.pll-dir-rtl textarea{direction:rtl}.pll-dir-ltr input[type=text],.pll-dir-ltr textarea{direction:ltr}.pll-dir-ltr .tr_lang,.pll-dir-rtl .tr_lang{direction:inherit}#post-translations p{float:left}#post-translations table{table-layout:fixed;width:100%;clear:both}#post-translations a{text-decoration:none}#post-translations .pll-column-icon,#post-translations .pll-language-column{width:20px}#post-translations .tr_lang{width:100%}#post-translations td{padding:2px}#post-translations .spinner,#term-translations .spinner{float:none;margin:0;background-position:center;width:auto}.pll-column-icon{text-align:center}#select-post-language .pll-select-flag{padding:4px;margin-right:32px}#select-media-language .pll-select-flag{padding:4px;margin-right:10px}.pll-media-edit-column{float:right}.pll-translation-flag{margin-right:14px}#select-add-term-language .pll-select-flag{padding:11px;margin-right:13px}#select-edit-term-language .pll-select-flag{padding:11px;margin-right:4px}#term-translations p{font-weight:400;font-style:normal;padding:2px;color:#23282d}#add-term-translations,#edit-term-translations{width:95%}#term-translations .pll-language-column{line-height:28px;width:20%}#add-term-translations .pll-language-column,#term-translations .pll-edit-column{width:20px}#edit-term-translations .pll-language-column{padding:15px 10px;font-weight:400}.pll_icon_add:before{content:"\f132"}.pll_icon_edit:before{content:"\f464"}[class^=pll_icon_]{font:20px/1 dashicons;vertical-align:middle}#wpadminbar #wp-admin-bar-languages .ab-item img{margin:0 8px 0 2px}#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon{float:none;top:4px}#wpadminbar #wp-admin-bar-languages .ab-icon:before{content:"\f326";top:1px}.pll-notice.notice{padding-right:38px;position:relative}.pll-notice a.notice-dismiss{text-decoration:none}.pll-notice .button{margin-right:10px}@media screen and (max-width:782px){#wpbody-content .pll-settings .pll-configure>td{padding:20px}#wpbody-content .pll-settings #cb{padding:20px 9px}.pll-inline-block{width:auto}.pll-settings-url-col{display:block;width:100%}#wpbody-content .pll-settings #pll-licenses-table td{display:block}.pll-configure .pll-deactivate-license{margin:10px 0 5px}.stringstranslations .column-context,.stringstranslations .column-name{display:none}.translation label{display:block;width:95%;padding-left:0}.translation input{width:95%}#edit-term-translations .pll-language-name,#select-add-term-language .pll-select-flag,#select-edit-term-language .pll-select-flag{display:none}#edit-term-translations{width:100%}#add-term-translations .pll-language-column{line-height:38px}#edit-term-translations td{padding:8px 10px}#edit-term-translations .pll-edit-column,#edit-term-translations .pll-language-column{width:20px}.term-translations .pll-edit-column,.term-translations .pll-language-column,.term-translations .pll-translation-column{display:table-cell}.term-translations .hidden{display:none}#wpadminbar #wp-admin-bar-languages{display:block}#wpadminbar #wp-admin-bar-languages>.ab-item{width:50px;text-align:center}#wpadminbar #wp-admin-bar-languages>.ab-item .ab-icon:before{font:32px/1 dashicons;top:-1px}#wpadminbar #wp-admin-bar-languages>.ab-item img{margin:19px 0}#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon{margin-right:6px;font-size:20px!important;line-height:20px!important}}
1
+ #add-lang select{width:95%}.column-locale,.languages .column-slug{width:15%}.column-default_lang{width:5%}.column-count,.column-flag,.column-term_group{width:10%}.icon-default-lang:before{font-family:dashicons;content:"\f155"}.pll-icon:before{display:inline-block;text-align:left;width:15px}.pll-circle:before{content:"\25cf"}.form-field input[type=radio]{width:auto;margin-right:2px}#pll-about-box p,#pll-recommended p{text-align:justify}#pll-about-box input{margin:0;padding:0;float:right}.stringstranslations .column-context,.stringstranslations .column-name{width:10%}.stringstranslations .column-string{width:33%}.translation label{display:inline-block;width:23%;vertical-align:top}.translation{display:flex}@media screen and (max-width:782px){.translation{display:block}}.translation textarea{display:block}.translation input,.translation textarea{width:72%;box-sizing:border-box;margin-bottom:4px}.pll-settings{margin-top:20px}.pll-settings .plugin-title{width:25%}#wpbody-content .pll-settings .pll-configure tr{display:table-row}#wpbody-content .pll-settings .pll-configure td{display:table-cell}#wpbody-content .pll-settings .pll-configure>td{padding:20px 20px 20px 40px}.pll-configure legend{font-size:14px;font-weight:600;margin-bottom:.5em}.pll-configure td .description{margin-top:2px;margin-bottom:.5em}.pll-configure p.submit{margin-top:20px}.pll-configure .button{margin-right:20px}.pll-configure fieldset{margin-bottom:1.5em}.pll-inline-block-list{margin:0}.pll-inline-block-list li{display:inline-block;margin:0;width:250px}#pll-domains-table td{padding:2px 2px 2px 1.5em;-webkit-box-shadow:none;box-shadow:none;border:none}.pll-settings-url-col{display:inline-block;width:49%;vertical-align:top}#pll-licenses-table td{vertical-align:top}#pll-licenses-table label{font-size:1em;font-weight:600}.pll-configure .pll-deactivate-license{margin:0 0 0 20px}.wp-list-table td[class*=column-language_],.wp-list-table th[class*=column-language_]{width:1.5em;box-sizing:content-box}.pll-dir-rtl input[type=text],.pll-dir-rtl textarea{direction:rtl}.pll-dir-ltr input[type=text],.pll-dir-ltr textarea{direction:ltr}.pll-dir-ltr .tr_lang,.pll-dir-rtl .tr_lang{direction:inherit}#post-translations p{float:left}#post-translations table{table-layout:fixed;width:100%;clear:both}#post-translations a{text-decoration:none}#post-translations .pll-column-icon,#post-translations .pll-language-column{width:20px}#post-translations .tr_lang{width:100%}#post-translations td{padding:2px}#post-translations .spinner,#term-translations .spinner{float:none;margin:0;background-position:center;width:auto}.pll-column-icon{text-align:center}#select-post-language .pll-select-flag{padding:4px;margin-right:32px}#select-media-language .pll-select-flag{padding:4px;margin-right:10px}.pll-media-edit-column{float:right}.pll-translation-flag{margin-right:14px}#select-add-term-language .pll-select-flag{padding:11px;margin-right:13px}#select-edit-term-language .pll-select-flag{padding:11px;margin-right:4px}#term-translations p{font-weight:400;font-style:normal;padding:2px;color:#23282d}#add-term-translations,#edit-term-translations{width:95%}#term-translations .pll-language-column{line-height:28px;width:20%}#add-term-translations .pll-language-column,#term-translations .pll-edit-column{width:20px}#edit-term-translations .pll-language-column{padding:15px 10px;font-weight:400}.pll_icon_add:before{content:"\f132"}.pll_icon_edit:before{content:"\f464"}[class^=pll_icon_]{font:20px/1 dashicons;vertical-align:middle}#wpadminbar #wp-admin-bar-languages .ab-item img{margin:0 8px 0 2px}#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon{float:none;top:4px}#wpadminbar #wp-admin-bar-languages .ab-icon:before{content:"\f326";top:1px}.pll-notice.notice{padding-right:38px;position:relative}.pll-notice a.notice-dismiss{text-decoration:none}.pll-notice .button{margin-right:10px}.languages_page_mlang_strings .metabox-holder>div{display:flex}.languages_page_mlang_strings .metabox-holder>div>div{flex-grow:1}.languages_page_mlang_strings .metabox-holder>div>div:nth-child(2n){margin-left:1rem}.languages_page_mlang_strings .metabox-holder>div>div.closed{border:0;background:0 0}.languages_page_mlang_strings .metabox-holder>div>div.closed .postbox-header{border:1px solid #ccd0d4;background:#fff}@media screen and (max-width:782px){#wpbody-content .pll-settings .pll-configure>td{padding:20px}#wpbody-content .pll-settings #cb{padding:20px 9px}.pll-inline-block{width:auto}.pll-settings-url-col{display:block;width:100%}#wpbody-content .pll-settings #pll-licenses-table td{display:block}.pll-configure .pll-deactivate-license{margin:10px 0 5px}.stringstranslations .column-context,.stringstranslations .column-name{display:none}.translation label{display:block;width:95%;padding-left:0}.translation input,.translation textarea{width:95%}.languages_page_mlang_strings .metabox-holder>div{flex-direction:column}.languages_page_mlang_strings .metabox-holder>div>div:nth-child(2n){margin-left:0}#edit-term-translations .pll-language-name,#select-add-term-language .pll-select-flag,#select-edit-term-language .pll-select-flag{display:none}#edit-term-translations{width:100%}#add-term-translations .pll-language-column{line-height:38px}#edit-term-translations td{padding:8px 10px}#edit-term-translations .pll-edit-column,#edit-term-translations .pll-language-column{width:20px}.term-translations .pll-edit-column,.term-translations .pll-language-column,.term-translations .pll-translation-column{display:table-cell}.term-translations .hidden{display:none}#wpadminbar #wp-admin-bar-languages{display:block}#wpadminbar #wp-admin-bar-languages>.ab-item{width:50px;text-align:center}#wpadminbar #wp-admin-bar-languages>.ab-item .ab-icon:before{font:32px/1 dashicons;top:-1px}#wpadminbar #wp-admin-bar-languages>.ab-item img{margin:19px 0}#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon{margin-right:6px;font-size:20px!important;line-height:20px!important}}
css/selectmenu.css CHANGED
@@ -30,20 +30,29 @@
30
  list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
31
  }
32
 
 
 
 
 
 
 
 
 
33
  .rtl .ui-menu .ui-menu-item {
34
  text-align: right;
35
  }
36
 
37
-
38
  /* icon support */
39
  .ui-menu-icons {
40
  position: relative;
41
  }
42
 
43
- .ui-menu-icons .ui-menu-item {
44
  padding-left: 2em;
45
  }
46
- .rtl .ui-menu-icons .ui-menu-item {
 
 
47
  padding-left: 1em;
48
  padding-right: 2em;
49
  }
@@ -105,14 +114,16 @@
105
  overflow: hidden;
106
  position: relative;
107
  text-decoration: none;
 
108
  }
109
 
110
  .ui-selectmenu-button span.ui-icon {
111
  right: 0.5em;
112
  left: auto;
113
- margin-top: -10px;
114
  position: absolute;
115
- top: 50%;
 
 
116
  text-indent: 0; /* due to text-indent for jquery ui-dialog in wizard */
117
  }
118
 
@@ -139,6 +150,8 @@
139
 
140
  .ui-widget-content,
141
  .ui-state-default,
 
 
142
  .ui-widget-content .ui-state-default,
143
  .ui-widget-header .ui-state-default {
144
  background: #fff;
@@ -146,10 +159,30 @@
146
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.07) inset;
147
  color: #32373c;
148
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
  .ui-widget-content .ui-state-hover,
151
- .ui-widget-content .ui-state-focus {
152
- background: #f5f5f5;
 
153
  }
154
 
155
  .ui-selectmenu-button.ui-state-focus {
@@ -158,8 +191,18 @@
158
  }
159
 
160
  .ui-icon-triangle-1-s:before {
161
- content: "\f140";
162
- font: 20px/1 'dashicons';
 
 
 
 
 
 
 
 
 
 
163
  }
164
 
165
  .ui-widget-content {
30
  list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
31
  }
32
 
33
+ /* for jQuery UI 1.12 which introduces a wrapper */
34
+ .ui-menu .ui-menu-item:not([role]) {
35
+ padding: 0;
36
+ }
37
+
38
+ .ui-menu-item-wrapper {
39
+ padding: 3px 1em 3px 2em;
40
+ }
41
  .rtl .ui-menu .ui-menu-item {
42
  text-align: right;
43
  }
44
 
 
45
  /* icon support */
46
  .ui-menu-icons {
47
  position: relative;
48
  }
49
 
50
+ .ui-menu-icons .ui-menu-item[role] {
51
  padding-left: 2em;
52
  }
53
+
54
+ .rtl .ui-menu-item-wrapper, /* for jQuery UI 1.12 which introduces a wrapper */
55
+ .rtl .ui-menu-icons .ui-menu-item[role] {
56
  padding-left: 1em;
57
  padding-right: 2em;
58
  }
114
  overflow: hidden;
115
  position: relative;
116
  text-decoration: none;
117
+ box-sizing: border-box; /* To keep width calculation in percent since WP 5.6 */
118
  }
119
 
120
  .ui-selectmenu-button span.ui-icon {
121
  right: 0.5em;
122
  left: auto;
 
123
  position: absolute;
124
+ top: 26%;
125
+ width: 16px;
126
+ height: 16px;
127
  text-indent: 0; /* due to text-indent for jquery ui-dialog in wizard */
128
  }
129
 
150
 
151
  .ui-widget-content,
152
  .ui-state-default,
153
+ .ui-selectmenu-button-closed, /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
154
+ .ui-selectmenu-button-open, /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
155
  .ui-widget-content .ui-state-default,
156
  .ui-widget-header .ui-state-default {
157
  background: #fff;
159
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.07) inset;
160
  color: #32373c;
161
  }
162
+ /* Override to have same styles as WP form styles since WordPress 5.4 */
163
+ .toplevel_page_mlang .ui-selectmenu-button.ui-state-default,
164
+ .toplevel_page_mlang .ui-selectmenu-button.ui-selectmenu-button-closed, /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
165
+ .toplevel_page_mlang .ui-selectmenu-button.ui-selectmenu-button-open{ /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
166
+ box-shadow: 0 0 0 transparent;
167
+ border-radius: 4px;
168
+ border: 1px solid #7e8993;
169
+ }
170
+
171
+ .toplevel_page_mlang .ui-selectmenu-button:focus{
172
+ color: #016087; /* Same color as WordPress focused select HTML tag */
173
+ border-color: #007cba;
174
+ box-shadow: 0 0 0 1px #007cba;
175
+ outline: 2px solid transparent;
176
+ }
177
+
178
+ .toplevel_page_mlang .ui-menu-item {
179
+ color: #016087; /* Same color as option in a WordPress focused select HTML tag */
180
+ }
181
 
182
  .ui-widget-content .ui-state-hover,
183
+ .ui-widget-content .ui-state-focus,
184
+ .ui-widget-content .ui-state-active { /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
185
+ background: #d5d5d5;
186
  }
187
 
188
  .ui-selectmenu-button.ui-state-focus {
191
  }
192
 
193
  .ui-icon-triangle-1-s:before {
194
+ content: "";
195
+ background: #fff url(data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%206l5%205%205-5%202%201-7%207-7-7%202-1z%22%20fill%3D%22%23555%22%2F%3E%3C%2Fsvg%3E) no-repeat right 0px top 55%;
196
+ background-size: 16px 16px;
197
+ box-sizing: border-box;
198
+ position: absolute;
199
+ width: 16px;
200
+ height: 16px;
201
+ }
202
+
203
+ .pll-wizard .ui-button:hover,
204
+ .pll-wizard .ui-button:focus {
205
+ background: #fff; /* To override jQuery ui-dialog styles provided by WordPress */
206
  }
207
 
208
  .ui-widget-content {
css/selectmenu.min.css CHANGED
@@ -1 +1 @@
1
- .ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;cursor:pointer;min-height:0;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.rtl .ui-menu .ui-menu-item{text-align:right}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item{padding-left:2em}.rtl .ui-menu-icons .ui-menu-item{padding-left:1em;padding-right:2em}.ui-menu .ui-icon,.ui-selectmenu-text .ui-icon{position:absolute;top:0;bottom:0;left:.3em;margin:auto 0}.rtl .ui-menu .ui-icon,.rtl .ui-selectmenu-text .ui-icon{right:.3em;left:auto}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:700;line-height:23px;padding:2px .4em;margin:.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-button{display:inline-block;overflow:hidden;position:relative;text-decoration:none}.ui-selectmenu-button span.ui-icon{right:.5em;left:auto;margin-top:-10px;position:absolute;top:50%;text-indent:0}.rtl .ui-selectmenu-button span.ui-icon{left:.5em;right:auto}.ui-selectmenu-button span.ui-selectmenu-text{text-align:left;padding:.2em 2.1em .2em 2em;display:block;line-height:23px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rtl .ui-selectmenu-button span.ui-selectmenu-text{text-align:right;padding:.2em 2em .2em 2.1em}.ui-state-default,.ui-widget-content,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{background:#fff;border:1px solid #ddd;box-shadow:0 1px 2px rgba(0,0,0,.07) inset;color:#32373c}.ui-widget-content .ui-state-focus,.ui-widget-content .ui-state-hover{background:#f5f5f5}.ui-selectmenu-button.ui-state-focus{border:1px solid #5b9dd9;box-shadow:0 0 2px rgba(30,140,190,.8)}.ui-icon-triangle-1-s:before{content:"\f140";font:20px/1 dashicons}.ui-widget-content{max-height:231px;box-shadow:0 2px 6px rgba(100,100,100,.3)}
1
+ .ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;cursor:pointer;min-height:0;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.ui-menu .ui-menu-item:not([role]){padding:0}.ui-menu-item-wrapper{padding:3px 1em 3px 2em}.rtl .ui-menu .ui-menu-item{text-align:right}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item[role]{padding-left:2em}.rtl .ui-menu-icons .ui-menu-item[role],.rtl .ui-menu-item-wrapper{padding-left:1em;padding-right:2em}.ui-menu .ui-icon,.ui-selectmenu-text .ui-icon{position:absolute;top:0;bottom:0;left:.3em;margin:auto 0}.rtl .ui-menu .ui-icon,.rtl .ui-selectmenu-text .ui-icon{right:.3em;left:auto}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:700;line-height:23px;padding:2px .4em;margin:.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-button{display:inline-block;overflow:hidden;position:relative;text-decoration:none;box-sizing:border-box}.ui-selectmenu-button span.ui-icon{right:.5em;left:auto;position:absolute;top:26%;width:16px;height:16px;text-indent:0}.rtl .ui-selectmenu-button span.ui-icon{left:.5em;right:auto}.ui-selectmenu-button span.ui-selectmenu-text{text-align:left;padding:.2em 2.1em .2em 2em;display:block;line-height:23px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rtl .ui-selectmenu-button span.ui-selectmenu-text{text-align:right;padding:.2em 2em .2em 2.1em}.ui-selectmenu-button-closed,.ui-selectmenu-button-open,.ui-state-default,.ui-widget-content,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{background:#fff;border:1px solid #ddd;box-shadow:0 1px 2px rgba(0,0,0,.07) inset;color:#32373c}.toplevel_page_mlang .ui-selectmenu-button.ui-selectmenu-button-closed,.toplevel_page_mlang .ui-selectmenu-button.ui-selectmenu-button-open,.toplevel_page_mlang .ui-selectmenu-button.ui-state-default{box-shadow:0 0 0 transparent;border-radius:4px;border:1px solid #7e8993}.toplevel_page_mlang .ui-selectmenu-button:focus{color:#016087;border-color:#007cba;box-shadow:0 0 0 1px #007cba;outline:2px solid transparent}.toplevel_page_mlang .ui-menu-item{color:#016087}.ui-widget-content .ui-state-active,.ui-widget-content .ui-state-focus,.ui-widget-content .ui-state-hover{background:#d5d5d5}.ui-selectmenu-button.ui-state-focus{border:1px solid #5b9dd9;box-shadow:0 0 2px rgba(30,140,190,.8)}.ui-icon-triangle-1-s:before{content:"";background:#fff url(data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%206l5%205%205-5%202%201-7%207-7-7%202-1z%22%20fill%3D%22%23555%22%2F%3E%3C%2Fsvg%3E) no-repeat right 0 top 55%;background-size:16px 16px;box-sizing:border-box;position:absolute;width:16px;height:16px}.pll-wizard .ui-button:focus,.pll-wizard .ui-button:hover{background:#fff}.ui-widget-content{max-height:231px;box-shadow:0 2px 6px rgba(100,100,100,.3)}
frontend/choose-lang-content.php CHANGED
@@ -89,7 +89,7 @@ class PLL_Choose_Lang_Content extends PLL_Choose_Lang {
89
  * @param object $query instance of WP_Query
90
  */
91
  public function parse_main_query( $query ) {
92
- if ( $query !== $GLOBALS['wp_the_query'] ) {
93
  return;
94
  }
95
 
89
  * @param object $query instance of WP_Query
90
  */
91
  public function parse_main_query( $query ) {
92
+ if ( empty( $GLOBALS['wp_the_query'] ) || $query !== $GLOBALS['wp_the_query'] ) {
93
  return;
94
  }
95
 
frontend/choose-lang.php CHANGED
@@ -84,29 +84,13 @@ abstract class PLL_Choose_Lang {
84
  * @since 1.5
85
  */
86
  public function maybe_setcookie() {
87
- // Don't set cookie in javascript when a cache plugin is active
88
- // Check headers have not been sent to avoid ugly error
89
- // Cookie domain must be set to false for localhost ( default value for COOKIE_DOMAIN ) thanks to Stephen Harris.
90
- if ( ! pll_is_cache_active() && ! headers_sent() && PLL_COOKIE !== false && ! empty( $this->curlang ) && ( ! isset( $_COOKIE[ PLL_COOKIE ] ) || $_COOKIE[ PLL_COOKIE ] != $this->curlang->slug ) && ! is_404() ) {
91
-
92
- /**
93
- * Filter the Polylang cookie duration
94
- * /!\ this filter may be fired *before* the theme is loaded
95
- *
96
- * @since 1.8
97
- *
98
- * @param int $duration cookie duration in seconds
99
- */
100
- $expiration = apply_filters( 'pll_cookie_expiration', YEAR_IN_SECONDS );
101
-
102
- setcookie(
103
- PLL_COOKIE,
104
- $this->curlang->slug,
105
- time() + $expiration,
106
- COOKIEPATH,
107
- 2 == $this->options['force_lang'] ? wp_parse_url( $this->links_model->home, PHP_URL_HOST ) : COOKIE_DOMAIN,
108
- is_ssl()
109
  );
 
110
  }
111
  }
112
 
84
  * @since 1.5
85
  */
86
  public function maybe_setcookie() {
87
+ // Don't set cookie in javascript when a cache plugin is active.
88
+ if ( ! pll_is_cache_active() && ! empty( $this->curlang ) && ! is_404() ) {
89
+ $args = array(
90
+ 'domain' => 2 === $this->options['force_lang'] ? wp_parse_url( $this->links_model->home, PHP_URL_HOST ) : COOKIE_DOMAIN,
91
+ 'samesite' => 3 === $this->options['force_lang'] ? 'None' : 'Lax',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  );
93
+ PLL_Cookie::set( $this->curlang->slug, $args );
94
  }
95
  }
96
 
frontend/frontend-filters-links.php CHANGED
@@ -383,22 +383,39 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
383
  }
384
  }
385
 
 
 
 
 
 
 
 
 
 
386
  elseif ( is_category() || is_tag() || is_tax() ) {
 
387
  $obj = $wp_query->get_queried_object();
388
  if ( ! empty( $obj ) && $this->model->is_translated_taxonomy( $obj->taxonomy ) ) {
389
  $language = $this->model->term->get_language( (int) $obj->term_id );
390
  }
391
  }
392
 
393
- elseif ( $wp_query->is_posts_page ) {
394
- $obj = $wp_query->get_queried_object();
395
- $language = $this->model->post->get_language( (int) $obj->ID );
 
 
 
396
  }
397
 
398
- elseif ( is_404() && ! empty( $wp_query->query['page_id'] ) && $id = get_query_var( 'page_id' ) ) {
399
- // Special case for page shortlinks when using subdomains or multiple domains
400
- // Needed because redirect_canonical doesn't accept to change the domain name
401
  $language = $this->model->post->get_language( (int) $id );
 
 
 
 
 
 
402
  }
403
 
404
  if ( 3 === $this->options['force_lang'] ) {
@@ -445,4 +462,68 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
445
 
446
  return $redirect_url;
447
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  }
383
  }
384
  }
385
 
386
+ elseif ( $this->links_model->using_permalinks && is_category() && ! empty( $wp_query->query['cat'] ) ) {
387
+ // When we receive a plain permaling with a cat query var, we need to redirect to the pretty permalink.
388
+ if ( $this->model->is_translated_taxonomy( $this->get_queried_taxonomy( $wp_query->tax_query ) ) ) {
389
+ $term_id = $this->get_queried_term_id( $wp_query->tax_query );
390
+ $language = $this->model->term->get_language( $term_id );
391
+ $redirect_url = $this->maybe_add_page_to_redirect_url( get_term_link( $term_id ) );
392
+ }
393
+ }
394
+
395
  elseif ( is_category() || is_tag() || is_tax() ) {
396
+ // We need to switch the language when there is no language provided in a pretty permalink.
397
  $obj = $wp_query->get_queried_object();
398
  if ( ! empty( $obj ) && $this->model->is_translated_taxonomy( $obj->taxonomy ) ) {
399
  $language = $this->model->term->get_language( (int) $obj->term_id );
400
  }
401
  }
402
 
403
+ elseif ( is_404() && ! empty( $wp_query->tax_query ) ) {
404
+ // When a wrong language is passed through a pretty permalink, we just need to switch the language.
405
+ if ( $this->model->is_translated_taxonomy( $this->get_queried_taxonomy( $wp_query->tax_query ) ) ) {
406
+ $term_id = $this->get_queried_term_id( $wp_query->tax_query );
407
+ $language = $this->model->term->get_language( $term_id );
408
+ }
409
  }
410
 
411
+ elseif ( $this->links_model->using_permalinks && $wp_query->is_posts_page && ! empty( $wp_query->query['page_id'] ) && $id = get_query_var( 'page_id' ) ) {
 
 
412
  $language = $this->model->post->get_language( (int) $id );
413
+ $redirect_url = $this->maybe_add_page_to_redirect_url( get_permalink( $id ) );
414
+ }
415
+
416
+ elseif ( $wp_query->is_posts_page ) {
417
+ $obj = $wp_query->get_queried_object();
418
+ $language = $this->model->post->get_language( (int) $obj->ID );
419
  }
420
 
421
  if ( 3 === $this->options['force_lang'] ) {
462
 
463
  return $redirect_url;
464
  }
465
+
466
+ /**
467
+ * Returns the link to the paged page if requested.
468
+ *
469
+ * @since 2.9
470
+ *
471
+ * @param string $redirect_url The url to redirect to.
472
+ * @return string
473
+ */
474
+ protected function maybe_add_page_to_redirect_url( $redirect_url ) {
475
+ global $wp_query;
476
+
477
+ if ( ! empty( $wp_query->query['paged'] ) && $page = get_query_var( 'paged' ) ) {
478
+ $redirect_url = $this->links_model->add_paged_to_link( $redirect_url, $page );
479
+ }
480
+ return $redirect_url;
481
+ }
482
+
483
+ /**
484
+ * Returns the term_id of the requested term.
485
+ *
486
+ * @since 2.9
487
+ *
488
+ * @param object $tax_query An instance of WP_Tax_Query.
489
+ * @return int
490
+ */
491
+ protected function get_queried_term_id( $tax_query ) {
492
+ $queried_terms = $tax_query->queried_terms;
493
+ $taxonomy = $this->get_queried_taxonomy( $tax_query );
494
+
495
+ $field = $queried_terms[ $taxonomy ]['field'];
496
+ $term = reset( $queried_terms[ $taxonomy ]['terms'] );
497
+
498
+ // We can get a term_id when requesting a plain permalink, eg /?cat=1.
499
+ if ( 'term_id' === $field ) {
500
+ return $term;
501
+ }
502
+
503
+ // We get a slug when requesting a pretty permalink with the wrong language.
504
+ $args = array(
505
+ 'lang' => '',
506
+ 'taxonomy' => $taxonomy,
507
+ $field => $term,
508
+ 'hide_empty' => false,
509
+ 'fields' => 'ids',
510
+ );
511
+ $terms = get_terms( $args );
512
+ return reset( $terms );
513
+ }
514
+
515
+ /**
516
+ * Find the taxonomy being queried.
517
+ *
518
+ * @since 2.9
519
+ *
520
+ * @param object $tax_query An instance of WP_Tax_Query.
521
+ * @return string A taxonomy slug
522
+ */
523
+ protected function get_queried_taxonomy( $tax_query ) {
524
+ $queried_terms = $tax_query->queried_terms;
525
+ unset( $queried_terms['language'] );
526
+
527
+ return key( $queried_terms );
528
+ }
529
  }
frontend/frontend-filters.php CHANGED
@@ -34,7 +34,7 @@ class PLL_Frontend_Filters extends PLL_Filters {
34
  add_filter( 'getarchives_where', array( $this, 'getarchives_where' ), 10, 2 );
35
 
36
  // Filters the widgets according to the current language
37
- add_filter( 'widget_display_callback', array( $this, 'widget_display_callback' ), 10, 2 );
38
  add_filter( 'sidebars_widgets', array( $this, 'sidebars_widgets' ) );
39
 
40
  if ( $this->options['media_support'] ) {
@@ -42,20 +42,13 @@ class PLL_Frontend_Filters extends PLL_Filters {
42
  }
43
 
44
  // Strings translation ( must be applied before WordPress applies its default formatting filters )
45
- foreach ( array( 'widget_text', 'widget_title', 'option_blogname', 'option_blogdescription', 'option_date_format', 'option_time_format' ) as $filter ) {
46
  add_filter( $filter, 'pll__', 1 );
47
  }
48
 
49
  // Translates biography
50
  add_filter( 'get_user_metadata', array( $this, 'get_user_metadata' ), 10, 4 );
51
 
52
- // Support theme customizer
53
- // FIXME of course does not work if 'transport' is set to 'postMessage'
54
- if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
55
- add_filter( 'pre_option_blogname', 'pll__', 20 );
56
- add_filter( 'pre_option_blogdescription', 'pll__', 20 );
57
- }
58
-
59
  // FIXME test get_user_locale for backward compatibility with WP < 4.7
60
  if ( Polylang::is_ajax_on_front() && function_exists( 'get_user_locale' ) ) {
61
  add_filter( 'load_textdomain_mofile', array( $this, 'load_textdomain_mofile' ) );
@@ -67,10 +60,9 @@ class PLL_Frontend_Filters extends PLL_Filters {
67
  *
68
  * @since 0.1
69
  *
70
- * @param string $locale
71
  * @return string
72
  */
73
- public function get_locale( $locale ) {
74
  return $this->curlang->locale;
75
  }
76
 
@@ -144,11 +136,10 @@ class PLL_Frontend_Filters extends PLL_Filters {
144
  *
145
  * @since 0.3
146
  *
147
- * @param array $instance Widget settings
148
- * @param object $widget WP_Widget object
149
  * @return bool|array false if we hide the widget, unmodified $instance otherwise
150
  */
151
- public function widget_display_callback( $instance, $widget ) {
152
  // FIXME it looks like this filter is useless, now the we use the filter sidebars_widgets
153
  return ! empty( $instance['pll_lang'] ) && $instance['pll_lang'] != $this->curlang->slug ? false : $instance;
154
  }
34
  add_filter( 'getarchives_where', array( $this, 'getarchives_where' ), 10, 2 );
35
 
36
  // Filters the widgets according to the current language
37
+ add_filter( 'widget_display_callback', array( $this, 'widget_display_callback' ) );
38
  add_filter( 'sidebars_widgets', array( $this, 'sidebars_widgets' ) );
39
 
40
  if ( $this->options['media_support'] ) {
42
  }
43
 
44
  // Strings translation ( must be applied before WordPress applies its default formatting filters )
45
+ foreach ( array( 'widget_text', 'widget_title' ) as $filter ) {
46
  add_filter( $filter, 'pll__', 1 );
47
  }
48
 
49
  // Translates biography
50
  add_filter( 'get_user_metadata', array( $this, 'get_user_metadata' ), 10, 4 );
51
 
 
 
 
 
 
 
 
52
  // FIXME test get_user_locale for backward compatibility with WP < 4.7
53
  if ( Polylang::is_ajax_on_front() && function_exists( 'get_user_locale' ) ) {
54
  add_filter( 'load_textdomain_mofile', array( $this, 'load_textdomain_mofile' ) );
60
  *
61
  * @since 0.1
62
  *
 
63
  * @return string
64
  */
65
+ public function get_locale() {
66
  return $this->curlang->locale;
67
  }
68
 
136
  *
137
  * @since 0.3
138
  *
139
+ * @param array $instance Widget settings
 
140
  * @return bool|array false if we hide the widget, unmodified $instance otherwise
141
  */
142
+ public function widget_display_callback( $instance ) {
143
  // FIXME it looks like this filter is useless, now the we use the filter sidebars_widgets
144
  return ! empty( $instance['pll_lang'] ) && $instance['pll_lang'] != $this->curlang->slug ? false : $instance;
145
  }
frontend/frontend-static-pages.php CHANGED
@@ -27,7 +27,7 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
27
  add_action( 'pll_home_requested', array( $this, 'pll_home_requested' ) );
28
 
29
  // Manages the redirection of the homepage
30
- add_filter( 'redirect_canonical', array( $this, 'redirect_canonical' ), 10, 2 );
31
 
32
  add_filter( 'pll_pre_translation_url', array( $this, 'pll_pre_translation_url' ), 10, 3 );
33
  add_filter( 'pll_check_canonical_url', array( $this, 'pll_check_canonical_url' ) );
@@ -84,10 +84,9 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
84
  * @since 0.1
85
  *
86
  * @param string $redirect_url
87
- * @param string $requested_url
88
  * @return bool|string modified url, false if redirection is canceled
89
  */
90
- public function redirect_canonical( $redirect_url, $requested_url ) {
91
  global $wp_query;
92
  if ( is_page() && ! is_feed() && isset( $wp_query->queried_object ) && $wp_query->queried_object->ID == $this->curlang->page_on_front ) {
93
  $url = is_paged() ? $this->links_model->add_paged_to_link( $this->links->get_home_url(), $wp_query->query_vars['page'] ) : $this->links->get_home_url();
27
  add_action( 'pll_home_requested', array( $this, 'pll_home_requested' ) );
28
 
29
  // Manages the redirection of the homepage
30
+ add_filter( 'redirect_canonical', array( $this, 'redirect_canonical' ) );
31
 
32
  add_filter( 'pll_pre_translation_url', array( $this, 'pll_pre_translation_url' ), 10, 3 );
33
  add_filter( 'pll_check_canonical_url', array( $this, 'pll_check_canonical_url' ) );
84
  * @since 0.1
85
  *
86
  * @param string $redirect_url
 
87
  * @return bool|string modified url, false if redirection is canceled
88
  */
89
+ public function redirect_canonical( $redirect_url ) {
90
  global $wp_query;
91
  if ( is_page() && ! is_feed() && isset( $wp_query->queried_object ) && $wp_query->queried_object->ID == $this->curlang->page_on_front ) {
92
  $url = is_paged() ? $this->links_model->add_paged_to_link( $this->links->get_home_url(), $wp_query->query_vars['page'] ) : $this->links->get_home_url();
include/api.php CHANGED
@@ -130,7 +130,7 @@ function pll_home_url( $lang = '' ) {
130
  * @param string $context optional the group in which the string is registered, defaults to 'polylang'
131
  * @param bool $multiline optional whether the string table should display a multiline textarea or a single line input, defaults to single line
132
  */
133
- function pll_register_string( $name, $string, $context = 'polylang', $multiline = false ) {
134
  if ( PLL() instanceof PLL_Admin_Base ) {
135
  PLL_Admin_Strings::register_string( $name, $string, $context, $multiline );
136
  }
130
  * @param string $context optional the group in which the string is registered, defaults to 'polylang'
131
  * @param bool $multiline optional whether the string table should display a multiline textarea or a single line input, defaults to single line
132
  */
133
+ function pll_register_string( $name, $string, $context = 'Polylang', $multiline = false ) {
134
  if ( PLL() instanceof PLL_Admin_Base ) {
135
  PLL_Admin_Strings::register_string( $name, $string, $context, $multiline );
136
  }
include/base.php CHANGED
@@ -78,6 +78,12 @@ abstract class PLL_Base {
78
  if ( $this->model->get_languages_list() ) {
79
  $this->posts = new PLL_CRUD_Posts( $this );
80
  $this->terms = new PLL_CRUD_Terms( $this );
 
 
 
 
 
 
81
  }
82
  }
83
 
78
  if ( $this->model->get_languages_list() ) {
79
  $this->posts = new PLL_CRUD_Posts( $this );
80
  $this->terms = new PLL_CRUD_Terms( $this );
81
+
82
+ // WordPress options.
83
+ new PLL_Translate_Option( 'blogname', array(), array( 'context' => 'WorPress' ) );
84
+ new PLL_Translate_Option( 'blogdescription', array(), array( 'context' => 'WorPress' ) );
85
+ new PLL_Translate_Option( 'date_format', array(), array( 'context' => 'WorPress' ) );
86
+ new PLL_Translate_Option( 'time_format', array(), array( 'context' => 'WorPress' ) );
87
  }
88
  }
89
 
include/class-polylang.php CHANGED
@@ -209,7 +209,7 @@ class Polylang {
209
  $class = 'PLL_Admin';
210
  } elseif ( self::is_rest_request() ) {
211
  $class = 'PLL_REST_Request';
212
- } elseif ( $model->get_languages_list() && empty( $_GET['deactivate-polylang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
213
  $class = 'PLL_Frontend';
214
  }
215
 
@@ -237,7 +237,7 @@ class Polylang {
237
  require_once __DIR__ . '/api.php'; // Loads the API
238
 
239
  // Loads the modules.
240
- foreach ( glob( POLYLANG_DIR . '/modules/*/load.php', GLOB_NOSORT ) as $load_script ) { // phpcs:ignore WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable
241
  require_once $load_script; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
242
  }
243
 
209
  $class = 'PLL_Admin';
210
  } elseif ( self::is_rest_request() ) {
211
  $class = 'PLL_REST_Request';
212
+ } elseif ( $model->get_languages_list() ) {
213
  $class = 'PLL_Frontend';
214
  }
215
 
237
  require_once __DIR__ . '/api.php'; // Loads the API
238
 
239
  // Loads the modules.
240
+ foreach ( glob( POLYLANG_DIR . '/modules/*/load.php', GLOB_NOSORT ) as $load_script ) {
241
  require_once $load_script; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
242
  }
243
 
include/cookie.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Polylang
4
+ */
5
+
6
+ /**
7
+ * A class to manage manage the language cookie
8
+ *
9
+ * @since 2.9
10
+ */
11
+ class PLL_Cookie {
12
+ /**
13
+ * Parses the cookie parameters
14
+ *
15
+ * @since 2.9
16
+ *
17
+ * @param array $args {@see PLL_Cookie::set()}
18
+ * @return array
19
+ */
20
+ protected static function parse_args( $args ) {
21
+ /**
22
+ * Filter the Polylang cookie duration
23
+ * /!\ this filter may be fired *before* the theme is loaded
24
+ *
25
+ * @since 1.8
26
+ *
27
+ * @param int $duration cookie duration in seconds
28
+ */
29
+ $expiration = apply_filters( 'pll_cookie_expiration', YEAR_IN_SECONDS );
30
+
31
+ $defaults = array(
32
+ 'expires' => time() + $expiration,
33
+ 'path' => COOKIEPATH,
34
+ 'domain' => COOKIE_DOMAIN, // Cookie domain must be set to false for localhost ( default value for COOKIE_DOMAIN ) thanks to Stephen Harris.
35
+ 'secure' => is_ssl(),
36
+ 'httponly' => false,
37
+ 'samesite' => 'Lax',
38
+ );
39
+
40
+ return wp_parse_args( $args, $defaults );
41
+ }
42
+
43
+ /**
44
+ * Sets the cookie.
45
+ *
46
+ * @since 2.9
47
+ *
48
+ * @param string $lang Language cookie value.
49
+ * @param array $args {
50
+ * Optional. Array of arguments for setting the cookie.
51
+ *
52
+ * @type string $path Cookie path, defaults to COOKIEPATH.
53
+ * @type string $domain Cookie domain, defaults to COOKIE_DOMAIN
54
+ * @type bool $secure Should the cookie be sent only over https?
55
+ * @type bool $httponly Should the cookie accessed only over http protocol? Defaults to false.
56
+ * @type string $samesite Either 'Strict', 'Lax' or 'None', defaults to 'Lax'.
57
+ * }
58
+ */
59
+ public static function set( $lang, $args = array() ) {
60
+ $args = self::parse_args( $args );
61
+
62
+ if ( ! headers_sent() && PLL_COOKIE !== false && self::get() !== $lang ) {
63
+ if ( version_compare( PHP_VERSION, '7.3', '<' ) ) {
64
+ $args['path'] .= '; SameSite=' . $args['samesite']; // Hack to set SameSite value in PHP < 7.3. Doesn't work with newer versions.
65
+ setcookie( PLL_COOKIE, $lang, $args['expires'], $args['path'], $args['domain'], $args['secure'], $args['httponly'] );
66
+ } else {
67
+ setcookie( PLL_COOKIE, $lang, $args );
68
+ }
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Returns the language cookie value.
74
+ *
75
+ * @since 2.9
76
+ *
77
+ * @return string
78
+ */
79
+ public static function get() {
80
+ return isset( $_COOKIE[ PLL_COOKIE ] ) ? sanitize_key( $_COOKIE[ PLL_COOKIE ] ) : '';
81
+ }
82
+ }
include/filters-sanitization.php ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Polylang
4
+ */
5
+
6
+ /**
7
+ * Setup specific filters useful for sanitization.
8
+ *
9
+ * Extract from PLL_Admin_Filters to be able to use in a REST API context.
10
+ *
11
+ * @since 2.9
12
+ */
13
+ class PLL_Filters_Sanitization {
14
+ /**
15
+ * Language used for the sanitization depending on the context.
16
+ *
17
+ * @var string $locale
18
+ */
19
+ public $locale;
20
+
21
+ /**
22
+ * Constructor: setups filters and actions.
23
+ *
24
+ * @since 2.9
25
+ *
26
+ * @param string $locale Locale code of the language.
27
+ */
28
+ public function __construct( $locale ) {
29
+ $this->locale = $locale;
30
+
31
+ // We need specific filters for some languages like German and Danish
32
+ $specific_locales = array( 'da_DK', 'de_DE', 'de_DE_formal', 'de_CH', 'de_CH_informal', 'ca', 'sr_RS', 'bs_BA' );
33
+ if ( in_array( $locale, $specific_locales ) ) {
34
+ add_filter( 'sanitize_title', array( $this, 'sanitize_title' ), 10, 3 );
35
+ add_filter( 'sanitize_user', array( $this, 'sanitize_user' ), 10, 3 );
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Retrieve the locale code of the language.
41
+ *
42
+ * @since 2.0
43
+ *
44
+ * @return string
45
+ */
46
+ public function get_locale() {
47
+ return $this->locale;
48
+ }
49
+
50
+ /**
51
+ * Maybe fix the result of sanitize_title() in case the languages include German or Danish
52
+ * Without this filter, if language of the title being sanitized is different from the language
53
+ * used for the admin interface and if one this language is German or Danish, some specific
54
+ * characters such as ä, ö, ü, ß are incorrectly sanitized.
55
+ *
56
+ * All the process is done by the remove_accents() WordPress function based on the locale value
57
+ *
58
+ * @link https://github.com/WordPress/WordPress/blob/5.5.1/wp-includes/formatting.php#L1920-L1944
59
+ *
60
+ * @since 2.0
61
+ *
62
+ * @param string $title Sanitized title.
63
+ * @param string $raw_title The title prior to sanitization.
64
+ * @param string $context The context for which the title is being sanitized.
65
+ * @return string
66
+ */
67
+ public function sanitize_title( $title, $raw_title, $context ) {
68
+ static $once = false;
69
+
70
+ if ( ! $once && 'save' == $context && ! empty( $title ) ) {
71
+ $once = true;
72
+ add_filter( 'locale', array( $this, 'get_locale' ), 20 ); // After the filter for the admin interface
73
+ $title = sanitize_title( $raw_title, '', $context );
74
+ remove_filter( 'locale', array( $this, 'get_locale' ), 20 );
75
+ $once = false;
76
+ }
77
+ return $title;
78
+ }
79
+
80
+ /**
81
+ * Maybe fix the result of sanitize_user() in case the languages include German or Danish
82
+ *
83
+ * All the process is done by the remove_accents() WordPress function based on the locale value
84
+ *
85
+ * @link https://github.com/WordPress/WordPress/blob/5.5-branch/wp-includes/formatting.php#L1920-L1944
86
+ *
87
+ * @since 2.0
88
+ *
89
+ * @param string $username Sanitized username.
90
+ * @param string $raw_username The username prior to sanitization.
91
+ * @param bool $strict Whether to limit the sanitization to specific characters. Default false.
92
+ * @return string
93
+ */
94
+ public function sanitize_user( $username, $raw_username, $strict ) {
95
+ static $once = false;
96
+
97
+ if ( ! $once ) {
98
+ $once = true;
99
+ add_filter( 'locale', array( $this, 'get_locale' ), 20 ); // After the filter for the admin interface
100
+ $username = sanitize_user( $raw_username, '', $strict );
101
+ remove_filter( 'locale', array( $this, 'get_locale' ), 20 );
102
+ $once = false;
103
+ }
104
+ return $username;
105
+ }
106
+ }
include/functions.php CHANGED
@@ -42,51 +42,6 @@ if ( ! function_exists( 'wpcom_vip_get_page_by_path' ) ) {
42
  }
43
  }
44
 
45
- if ( ! function_exists( 'wp_doing_ajax' ) ) {
46
- /**
47
- * Determines whether the current request is a WordPress Ajax request.
48
- * Backward compatibility function for WP < 4.7
49
- *
50
- * @since 2.2
51
- *
52
- * @return bool True if it's a WordPress Ajax request, false otherwise.
53
- */
54
- function wp_doing_ajax() {
55
- /** This filter is documented in wp-includes/load.php */
56
- return apply_filters( 'wp_doing_ajax', defined( 'DOING_AJAX' ) && DOING_AJAX );
57
- }
58
- }
59
-
60
- if ( ! function_exists( 'wp_doing_cron' ) ) {
61
- /**
62
- * Determines whether the current request is a WordPress cron request.
63
- * Backward compatibility function for WP < 4.8
64
- *
65
- * @since 2.6
66
- *
67
- * @return bool True if it's a WordPress cron request, false otherwise.
68
- */
69
- function wp_doing_cron() {
70
- /** This filter is documented in wp-includes/load.php */
71
- return apply_filters( 'wp_doing_cron', defined( 'DOING_CRON' ) && DOING_CRON );
72
- }
73
- }
74
-
75
- if ( ! function_exists( 'wp_using_themes' ) ) {
76
- /**
77
- * Determines whether the current request should use themes.
78
- * Backward compatibility function for WP < 5.1
79
- *
80
- * @since 2.6
81
- *
82
- * @return bool True if themes should be used, false otherwise.
83
- */
84
- function wp_using_themes() {
85
- /** This filter is documented in wp-includes/load.php */
86
- return apply_filters( 'wp_using_themes', defined( 'WP_USE_THEMES' ) && WP_USE_THEMES );
87
- }
88
- }
89
-
90
  /**
91
  * Determines whether we should load the cache compatibility
92
  *
42
  }
43
  }
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  /**
46
  * Determines whether we should load the cache compatibility
47
  *
include/mo.php CHANGED
@@ -105,4 +105,15 @@ class PLL_MO extends MO {
105
  public function clean_cache() {
106
  wp_cache_delete( 'polylang_mo_ids' );
107
  }
 
 
 
 
 
 
 
 
 
 
 
108
  }
105
  public function clean_cache() {
106
  wp_cache_delete( 'polylang_mo_ids' );
107
  }
108
+
109
+ /**
110
+ * Deletes a string
111
+ *
112
+ * @since 2.9
113
+ *
114
+ * @param string $string The source string to remove from the translations.
115
+ */
116
+ public function delete_entry( $string ) {
117
+ unset( $this->entries[ $string ] );
118
+ }
119
  }
include/olt-manager.php CHANGED
@@ -77,11 +77,12 @@ class PLL_OLT_Manager {
77
  // Don't try to save time for en_US as some users have theme written in another language
78
  // Now we can load all overridden text domains with the right language
79
  if ( ! empty( $this->list_textdomains ) ) {
80
-
81
- // Since WP 4.7 we need to reset the internal cache of _get_path_to_translation when switching from any locale to en_US
82
- // See WP_Locale_Switcher::change_locale()
83
- // FIXME test _get_path_to_translation for backward compatibility with WP < 4.7
84
- if ( function_exists( '_get_path_to_translation' ) ) {
 
85
  _get_path_to_translation( null, true );
86
  }
87
 
@@ -139,16 +140,14 @@ class PLL_OLT_Manager {
139
 
140
  /**
141
  * FIXME: Backward compatibility with Polylang for WooCommerce < 0.3.4
142
- * To remove in Polylang 2.1
143
  *
144
  * @since 0.1
145
  *
146
- * @param bool $bool not used
147
- * @param string $domain text domain name
148
- * @param string $mofile translation file name
149
  * @return bool
150
  */
151
- public function mofile( $bool, $domain, $mofile ) {
152
  return $bool;
153
  }
154
 
77
  // Don't try to save time for en_US as some users have theme written in another language
78
  // Now we can load all overridden text domains with the right language
79
  if ( ! empty( $this->list_textdomains ) ) {
80
+ /*
81
+ * FIXME: Backward compatibility with WP < 5.6
82
+ * From WP 4.7 to 5.5, we need to reset the internal cache of _get_path_to_translation when switching from any locale to en_US.
83
+ * See WP_Locale_Switcher::change_locale()
84
+ */
85
+ if ( ! class_exists( 'WP_Textdomain_Registry' ) && function_exists( '_get_path_to_translation' ) ) {
86
  _get_path_to_translation( null, true );
87
  }
88
 
140
 
141
  /**
142
  * FIXME: Backward compatibility with Polylang for WooCommerce < 0.3.4
143
+ * Was formerly hooked to the filter 'override_load_textdomain'
144
  *
145
  * @since 0.1
146
  *
147
+ * @param bool $bool Whether to override the .mo file loading.
 
 
148
  * @return bool
149
  */
150
+ public function mofile( $bool ) {
151
  return $bool;
152
  }
153
 
include/switcher.php CHANGED
@@ -35,7 +35,7 @@ class PLL_Switcher {
35
  /**
36
  * Get the language elements for use in a walker
37
  *
38
- * @see list of parameters accepted in $args documented for PLL_Switcher::the_languages
39
  *
40
  * @since 1.2
41
  *
35
  /**
36
  * Get the language elements for use in a walker
37
  *
38
+ * @see PLL_Switcher::the_languages() for the list of parameters accepted in $args
39
  *
40
  * @since 1.2
41
  *
include/translate-option.php ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Polylang
4
+ */
5
+
6
+ /**
7
+ * Registers and translates strings in an option.
8
+ * When a string is updated in an original option, the translations of the old string are assigned to the new original string.
9
+ *
10
+ * @since 2.9
11
+ */
12
+ class PLL_Translate_Option {
13
+
14
+ /**
15
+ * Array of option keys to translate.
16
+ *
17
+ * @var array
18
+ */
19
+ private $keys;
20
+
21
+ /**
22
+ * Array of updated strings.
23
+ *
24
+ * @var array
25
+ */
26
+ private $updated_strings = array();
27
+
28
+ /**
29
+ * Constructor
30
+ *
31
+ * @since 2.9
32
+ *
33
+ * @param string $name Option name.
34
+ * @param object $keys Recursive array of option keys to translate in the form:
35
+ * array(
36
+ * 'option_key_to_translate_1' => 1,
37
+ * 'option_key_to_translate_2' => 1,
38
+ * 'my_group' => array(
39
+ * 'sub_key_to_translate_1' => 1,
40
+ * 'sub_key_to_translate_2' => 1,
41
+ * ),
42
+ * )
43
+ * Note: only keys are interpreted. Any scalar can be used as values.
44
+ * @param string $args {
45
+ * Optional. Array of arguments for registering the option.
46
+ *
47
+ * @type string $context The group in which the strings will be registered.
48
+ * @type string $sanitize_callback A callback function that sanitizes the option's value.
49
+ * }
50
+ */
51
+ public function __construct( $name, $keys = array(), $args = array() ) {
52
+ // Registers the strings.
53
+ $context = isset( $args['context'] ) ? $args['context'] : 'Polylang';
54
+ $this->register_string_recursive( $context, $name, get_option( $name ), $keys );
55
+
56
+ // Translates the strings.
57
+ $this->keys = $keys;
58
+ add_filter( 'option_' . $name, array( $this, 'translate' ) ); // Make sure to add this filter after options are registered.
59
+
60
+ // Filters updated values.
61
+ add_filter( 'pre_update_option_' . $name, array( $this, 'pre_update_option' ), 10, 3 );
62
+ add_action( 'update_option_' . $name, array( $this, 'update_option' ) );
63
+
64
+ // Sanitizes translated strings.
65
+ if ( empty( $args['sanitize_callback'] ) ) {
66
+ add_filter( 'pll_sanitize_string_translation', array( $this, 'sanitize_option' ), 10, 2 );
67
+ } else {
68
+ add_filter( 'pll_sanitize_string_translation', $args['sanitize_callback'], 10, 3 );
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Translates the strings registered for an option.
74
+ *
75
+ * @since 1.0
76
+ *
77
+ * @param mixed $value Either a string to translate or a list of strings to translate.
78
+ * @return mixed Translated string(s).
79
+ */
80
+ public function translate( $value ) {
81
+ return $this->translate_string_recursive( $value, $this->keys );
82
+ }
83
+
84
+ /**
85
+ * Recursively translates the strings registered for an option.
86
+ *
87
+ * @since 1.0
88
+ *
89
+ * @param array|string $values Either a string to translate or a list of strings to translate.
90
+ * @param array|bool $key Array of option keys to translate.
91
+ * @return array|string Translated string(s)
92
+ */
93
+ protected function translate_string_recursive( $values, $key ) {
94
+ $children = is_array( $key ) ? $key : array();
95
+
96
+ if ( is_array( $values ) || is_object( $values ) ) {
97
+ if ( count( $children ) ) {
98
+ foreach ( $children as $name => $child ) {
99
+ if ( is_array( $values ) && isset( $values[ $name ] ) ) {
100
+ $values[ $name ] = $this->translate_string_recursive( $values[ $name ], $child );
101
+ continue;
102
+ }
103
+
104
+ if ( is_object( $values ) && isset( $values->$name ) ) {
105
+ $values->$name = $this->translate_string_recursive( $values->$name, $child );
106
+ continue;
107
+ }
108
+
109
+ $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
110
+
111
+ foreach ( $values as $n => &$value ) {
112
+ // The first case could be handled by the next one, but we avoid calls to preg_match here.
113
+ if ( '*' === $name || ( false !== strpos( $name, '*' ) && preg_match( $pattern, $n ) ) ) {
114
+ $value = $this->translate_string_recursive( $value, $child );
115
+ }
116
+ }
117
+ }
118
+ } else {
119
+ // Parent key is a wildcard and no sub-key has been whitelisted.
120
+ foreach ( $values as &$value ) {
121
+ $value = $this->translate_string_recursive( $value, $key );
122
+ }
123
+ }
124
+ } else {
125
+ $values = pll__( $values );
126
+ }
127
+
128
+ return $values;
129
+ }
130
+
131
+ /**
132
+ * Recursively registers strings for an option.
133
+ *
134
+ * @since 1.0
135
+ * @since 2.7 Signature modified
136
+ *
137
+ * @param string $context The group in which the strings will be registered.
138
+ * @param string $option Option name.
139
+ * @param array $values Option value.
140
+ * @param array|bool $key Array of option keys to translate.
141
+ */
142
+ protected function register_string_recursive( $context, $option, $values, $key ) {
143
+ if ( is_object( $values ) ) {
144
+ $values = (array) $values;
145
+ }
146
+
147
+ if ( is_array( $values ) ) {
148
+ $children = is_array( $key ) ? $key : array();
149
+
150
+ if ( count( $children ) ) {
151
+ foreach ( $children as $name => $child ) {
152
+ if ( isset( $values[ $name ] ) ) {
153
+ $this->register_string_recursive( $context, $name, $values[ $name ], $child );
154
+ continue;
155
+ }
156
+
157
+ $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
158
+
159
+ foreach ( $values as $n => $value ) {
160
+ // The first case could be handled by the next one, but we avoid calls to preg_match here.
161
+ if ( '*' === $name || ( false !== strpos( $name, '*' ) && preg_match( $pattern, $n ) ) ) {
162
+ $this->register_string_recursive( $context, $n, $value, $child );
163
+ }
164
+ }
165
+ }
166
+ } else {
167
+ foreach ( $values as $n => $value ) {
168
+ // Parent key is a wildcard and no sub-key has been whitelisted.
169
+ $this->register_string_recursive( $context, $n, $value, $key );
170
+ }
171
+ }
172
+ } else {
173
+ PLL_Admin_Strings::register_string( $option, $values, $context, true );
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Filters an option before it is updated.
179
+ *
180
+ * This is the step 1 in the update process, in which we prevent the update of
181
+ * strings to their translations by filtering them out, and we store the updated strings
182
+ * for the next step.
183
+ *
184
+ * @since 2.9
185
+ *
186
+ * @param mixed $value The new, unserialized option value.
187
+ * @param mixed $old_value The old (filtered) option value.
188
+ * @param string $name Option name.
189
+ */
190
+ public function pre_update_option( $value, $old_value, $name ) {
191
+ // Stores the unfiltered old option value before it is updated in DB.
192
+ remove_filter( 'option_' . $name, array( $this, 'translate' ), 10, 2 );
193
+ $this->old_value = get_option( $name );
194
+ add_filter( 'option_' . $name, array( $this, 'translate' ), 20, 2 );
195
+
196
+ // Load strings translations according to the admin language filter
197
+ $locale = pll_current_language( 'locale' );
198
+ if ( empty( $locale ) ) {
199
+ $locale = pll_default_language( 'locale' );
200
+ }
201
+ PLL()->load_strings_translations( $locale );
202
+
203
+ // Filters out the strings which would be updated to their translations and stores the updated strings.
204
+ $value = $this->check_value_recursive( $this->old_value, $value, $this->keys );
205
+
206
+ return $value;
207
+ }
208
+
209
+ /**
210
+ * Updates the string translations to keep the same translated value when updating the original option.
211
+ *
212
+ * This is the step 2 in the update process. Knowing all strings that have been updated,
213
+ * we remove the old strings from the strings translations and replace them by
214
+ * the new strings with the old translations.
215
+ *
216
+ * @since 2.9
217
+ */
218
+ public function update_option() {
219
+ $curlang = pll_current_language();
220
+
221
+ if ( ! empty( $this->updated_strings ) ) {
222
+ foreach ( pll_languages_list() as $lang ) {
223
+
224
+ $language = PLL()->model->get_language( $lang );
225
+ $mo = new PLL_MO();
226
+ $mo->import_from_db( $language );
227
+
228
+ foreach ( $this->updated_strings as $old_string => $string ) {
229
+ $translation = $mo->translate( $old_string );
230
+ if ( ( empty( $curlang ) && $translation === $old_string ) || $lang === $curlang ) {
231
+ $translation = $string;
232
+ }
233
+
234
+ // Removes the old entry and replace it by the new one, with the same translation.
235
+ $mo->delete_entry( $old_string );
236
+ $mo->add_entry( $mo->make_entry( $string, $translation ) );
237
+ }
238
+
239
+ $mo->export_to_db( $language );
240
+ }
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Recursively compares the updated strings to the translation of the old string.
246
+ *
247
+ * This is the heart of the update process. If an updated string is found to be
248
+ * the same as the translation of the old string, we restore the old string to
249
+ * prevent the update in {@see PLL_Translate_Option::pre_update_option()}, otherwise
250
+ * the updated string is stored in {@see PLL_Translate_Option::updated_strings} to be able to
251
+ * later assign the translations to the new value in {@see PLL_Translate_Option::update_option()}.
252
+ *
253
+ * @since 2.9
254
+ *
255
+ * @param mixed $old_values The old option value.
256
+ * @param mixed $values The new option value..
257
+ * @param array|bool $key Array of option keys to translate.
258
+ * @return mixed
259
+ */
260
+ protected function check_value_recursive( $old_values, $values, $key ) {
261
+ $children = is_array( $key ) ? $key : array();
262
+
263
+ if ( is_array( $values ) || is_object( $values ) ) {
264
+ if ( count( $children ) ) {
265
+ foreach ( $children as $name => $child ) {
266
+ if ( is_array( $values ) && is_array( $old_values ) && isset( $old_values[ $name ], $values[ $name ] ) ) {
267
+ $values[ $name ] = $this->check_value_recursive( $old_values[ $name ], $values[ $name ], $child );
268
+ continue;
269
+ }
270
+
271
+ if ( is_object( $values ) && is_object( $old_values ) && isset( $old_values->$name, $values->$name ) ) {
272
+ $values->$name = $this->check_value_recursive( $old_values->$name, $values->$name, $child );
273
+ continue;
274
+ }
275
+
276
+ $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
277
+
278
+ foreach ( $values as $n => $value ) {
279
+ // The first case could be handled by the next one, but we avoid calls to preg_match here.
280
+ if ( '*' === $name || ( false !== strpos( $name, '*' ) && preg_match( $pattern, $n ) ) ) {
281
+ if ( is_array( $values ) && is_array( $old_values ) && isset( $old_values[ $n ] ) ) {
282
+ $values[ $n ] = $this->check_value_recursive( $old_values[ $n ], $value, $child );
283
+ }
284
+
285
+ if ( is_object( $values ) && is_object( $old_values ) && isset( $old_values->$n ) ) {
286
+ $values->$n = $this->check_value_recursive( $old_values->$n, $value, $child );
287
+ }
288
+ }
289
+ }
290
+ }
291
+ } else {
292
+ // Parent key is a wildcard and no sub-key has been whitelisted.
293
+ foreach ( $values as $n => $value ) {
294
+ if ( is_array( $values ) && is_array( $old_values ) && isset( $old_values[ $n ] ) ) {
295
+ $values[ $n ] = $this->check_value_recursive( $old_values[ $n ], $value, $key );
296
+ }
297
+
298
+ if ( is_object( $values ) && is_object( $old_values ) && isset( $old_values->$n ) ) {
299
+ $values->$n = $this->check_value_recursive( $old_values->$n, $value, $key );
300
+ }
301
+ }
302
+ }
303
+ } elseif ( $old_values !== $values ) {
304
+ if ( pll__( $old_values ) === $values ) {
305
+ $values = $old_values; // Prevents updating the value to its translation.
306
+ } else {
307
+ $this->updated_strings[ $old_values ] = $values; // Stores the updated strings.
308
+ }
309
+ }
310
+
311
+ return $values;
312
+ }
313
+
314
+ /**
315
+ * Sanitizes the option value.
316
+ *
317
+ * @since 2.9
318
+ *
319
+ * @param string $value The unsanitised value.
320
+ * @param string $name The name of the option.
321
+ * @return string Sanitized value.
322
+ */
323
+ public function sanitize_option( $value, $name ) {
324
+ return sanitize_option( $name, $value );
325
+ }
326
+ }
include/translated-object.php CHANGED
@@ -163,13 +163,14 @@ abstract class PLL_Translated_Object {
163
 
164
  if ( ! empty( $term ) ) {
165
  $d = maybe_unserialize( $term->description );
166
- $slug = array_search( $id, $this->get_translations( $id ) ); // in case some plugin stores the same value with different key
167
- unset( $d[ $slug ] );
 
 
168
 
169
  if ( empty( $d ) ) {
170
  wp_delete_term( (int) $term->term_id, $this->tax_translations );
171
- }
172
- else {
173
  wp_update_term( (int) $term->term_id, $this->tax_translations, array( 'description' => maybe_serialize( $d ) ) );
174
  }
175
  }
@@ -279,7 +280,23 @@ abstract class PLL_Translated_Object {
279
  public function get_objects_in_language( $lang ) {
280
  global $wpdb;
281
  $tt_id = $this->tax_tt;
282
- return $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $lang->$tt_id ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  }
284
 
285
  /**
163
 
164
  if ( ! empty( $term ) ) {
165
  $d = maybe_unserialize( $term->description );
166
+ if ( is_array( $d ) ) {
167
+ $slug = array_search( $id, $this->get_translations( $id ) ); // In case some plugin stores the same value with different key.
168
+ unset( $d[ $slug ] );
169
+ }
170
 
171
  if ( empty( $d ) ) {
172
  wp_delete_term( (int) $term->term_id, $this->tax_translations );
173
+ } else {
 
174
  wp_update_term( (int) $term->term_id, $this->tax_translations, array( 'description' => maybe_serialize( $d ) ) );
175
  }
176
  }
280
  public function get_objects_in_language( $lang ) {
281
  global $wpdb;
282
  $tt_id = $this->tax_tt;
283
+
284
+ $last_changed = wp_cache_get_last_changed( 'terms' );
285
+ $cache_key = "polylang:get_objects_in_language:{$lang->$tt_id}:{$last_changed}";
286
+ $cache = wp_cache_get( $cache_key, 'terms' );
287
+
288
+ if ( false === $cache ) {
289
+ $object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $lang->$tt_id ) );
290
+ wp_cache_set( $cache_key, $object_ids, 'terms' );
291
+ } else {
292
+ $object_ids = (array) $cache;
293
+ }
294
+
295
+ if ( ! $object_ids ) {
296
+ return array();
297
+ }
298
+
299
+ return $object_ids;
300
  }
301
 
302
  /**
include/walker-dropdown.php CHANGED
@@ -22,7 +22,7 @@ class PLL_Walker_Dropdown extends Walker {
22
  * @param array $args An array of additional arguments.
23
  * @param int $current_object_id ID of the current item.
24
  */
25
- public function start_el( &$output, $element, $depth = 0, $args = array(), $current_object_id = 0 ) {
26
  $value = $args['value'];
27
  $output .= sprintf(
28
  "\t" . '<option value="%1$s"%2$s%3$s>%4$s</option>' . "\n",
@@ -45,7 +45,7 @@ class PLL_Walker_Dropdown extends Walker {
45
  * @param array $args An array of arguments.
46
  * @param string $output Passed by reference. Used to append additional content.
47
  */
48
- public function display_element( $element, &$children_elements, $max_depth, $depth = 0, $args, &$output ) {
49
  $element = (object) $element; // Make sure we have an object
50
  $element->parent = $element->id = 0; // Don't care about this
51
  parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
22
  * @param array $args An array of additional arguments.
23
  * @param int $current_object_id ID of the current item.
24
  */
25
+ public function start_el( &$output, $element, $depth = 0, $args = array(), $current_object_id = 0 ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
26
  $value = $args['value'];
27
  $output .= sprintf(
28
  "\t" . '<option value="%1$s"%2$s%3$s>%4$s</option>' . "\n",
45
  * @param array $args An array of arguments.
46
  * @param string $output Passed by reference. Used to append additional content.
47
  */
48
+ public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
49
  $element = (object) $element; // Make sure we have an object
50
  $element->parent = $element->id = 0; // Don't care about this
51
  parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
include/walker-list.php CHANGED
@@ -22,7 +22,7 @@ class PLL_Walker_List extends Walker {
22
  * @param array $args An array of additional arguments.
23
  * @param int $current_object_id ID of the current item.
24
  */
25
- public function start_el( &$output, $element, $depth = 0, $args = array(), $current_object_id = 0 ) {
26
  $output .= sprintf(
27
  '%6$s<li class="%1$s"><a lang="%2$s" hreflang="%2$s" href="%3$s">%4$s%5$s</a></li>%7$s',
28
  esc_attr( implode( ' ', $element->classes ) ),
@@ -47,7 +47,7 @@ class PLL_Walker_List extends Walker {
47
  * @param array $args An array of arguments.
48
  * @param string $output Passed by reference. Used to append additional content.
49
  */
50
- public function display_element( $element, &$children_elements, $max_depth, $depth = 0, $args, &$output ) {
51
  $element = (object) $element; // Make sure we have an object
52
  $element->parent = $element->id = 0; // Don't care about this
53
  parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
22
  * @param array $args An array of additional arguments.
23
  * @param int $current_object_id ID of the current item.
24
  */
25
+ public function start_el( &$output, $element, $depth = 0, $args = array(), $current_object_id = 0 ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
26
  $output .= sprintf(
27
  '%6$s<li class="%1$s"><a lang="%2$s" hreflang="%2$s" href="%3$s">%4$s%5$s</a></li>%7$s',
28
  esc_attr( implode( ' ', $element->classes ) ),
47
  * @param array $args An array of arguments.
48
  * @param string $output Passed by reference. Used to append additional content.
49
  */
50
+ public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
51
  $element = (object) $element; // Make sure we have an object
52
  $element->parent = $element->id = 0; // Don't care about this
53
  parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
include/widget-calendar.php CHANGED
@@ -104,7 +104,7 @@ class PLL_Widget_Calendar extends WP_Widget_Calendar {
104
 
105
  // Let's figure out when we are.
106
  if ( ! empty( $monthnum ) && ! empty( $year ) ) {
107
- $thismonth = zeroise( intval( $monthnum ), 2 );
108
  $thisyear = (int) $year;
109
  } elseif ( ! empty( $w ) ) {
110
  // We need to get the month from MySQL.
104
 
105
  // Let's figure out when we are.
106
  if ( ! empty( $monthnum ) && ! empty( $year ) ) {
107
+ $thismonth = zeroise( (int) $monthnum, 2 );
108
  $thisyear = (int) $year;
109
  } elseif ( ! empty( $w ) ) {
110
  // We need to get the month from MySQL.
include/widget-languages.php CHANGED
@@ -35,24 +35,47 @@ class PLL_Widget_Languages extends WP_Widget {
35
  * @param array $instance The settings for the particular instance of the widget
36
  */
37
  public function widget( $args, $instance ) {
38
- // Sets a unique id for dropdown
39
  $instance['dropdown'] = empty( $instance['dropdown'] ) ? 0 : $args['widget_id'];
40
 
41
  if ( $list = pll_the_languages( array_merge( $instance, array( 'echo' => 0 ) ) ) ) {
42
  $title = empty( $instance['title'] ) ? '' : $instance['title'];
 
43
  /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
44
  $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
45
 
46
  echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput
 
47
  if ( $title ) {
48
  echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput
49
  }
 
 
 
 
 
 
 
50
  if ( $instance['dropdown'] ) {
51
- echo '<label class="screen-reader-text" for="' . esc_attr( 'lang_choice_' . $instance['dropdown'] ) . '">' . esc_html__( 'Choose a language', 'polylang' ) . '</label>';
52
  echo $list; // phpcs:ignore WordPress.Security.EscapeOutput
53
  } else {
 
 
 
 
 
 
 
 
 
54
  echo "<ul>\n" . $list . "</ul>\n"; // phpcs:ignore WordPress.Security.EscapeOutput
 
 
 
 
55
  }
 
56
  echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput
57
  }
58
  }
@@ -66,7 +89,7 @@ class PLL_Widget_Languages extends WP_Widget {
66
  * @param array $old_instance Old settings for this instance
67
  * @return array Settings to save or bool false to cancel saving
68
  */
69
- public function update( $new_instance, $old_instance ) {
70
  $instance = array( 'title' => sanitize_text_field( $new_instance['title'] ) );
71
  foreach ( array_keys( PLL_Switcher::get_switcher_options( 'widget' ) ) as $key ) {
72
  $instance[ $key ] = ! empty( $new_instance[ $key ] ) ? 1 : 0;
@@ -107,53 +130,5 @@ class PLL_Widget_Languages extends WP_Widget {
107
  esc_attr( 'pll-' . $key )
108
  );
109
  }
110
-
111
- // FIXME echoing script in form is not very clean
112
- // but it does not work if enqueued properly :
113
- // clicking save on a widget makes this code unreachable for the just saved widget ( ?! )
114
- $this->admin_print_script();
115
- }
116
-
117
- /**
118
- * Add javascript to control the language switcher options
119
- *
120
- * @since 1.3
121
- */
122
- public function admin_print_script() {
123
- static $done = false;
124
-
125
- if ( $done ) {
126
- return;
127
- }
128
-
129
- $done = true;
130
- ?>
131
- <script type='text/javascript'>
132
- //<![CDATA[
133
- jQuery( document ).ready( function( $ ) {
134
- function pll_toggle( a, test ) {
135
- test ? a.show() : a.hide();
136
- }
137
-
138
- // Remove all options if dropdown is checked
139
- $( '.widgets-sortables,.control-section-sidebar' ).on( 'change', '.pll-dropdown', function() {
140
- var this_id = $( this ).parent().parent().parent().children( '.widget-id' ).attr( 'value' );
141
- pll_toggle( $( '.no-dropdown-' + this_id ), true != $( this ).prop( 'checked' ) );
142
- } );
143
-
144
- // Disallow unchecking both show names and show flags
145
- var options = ['-show_flags', '-show_names'];
146
- $.each( options, function( i, v ) {
147
- $( '.widgets-sortables,.control-section-sidebar' ).on( 'change', '.pll' + v, function() {
148
- var this_id = $( this ).parent().parent().parent().children( '.widget-id' ).attr( 'value' );
149
- if ( true != $( this ).prop( 'checked' ) ) {
150
- $( '#widget-' + this_id + options[ 1-i ] ).prop( 'checked', true );
151
- }
152
- } );
153
- } );
154
- } );
155
- //]]>
156
- </script>
157
- <?php
158
  }
159
  }
35
  * @param array $instance The settings for the particular instance of the widget
36
  */
37
  public function widget( $args, $instance ) {
38
+ // Sets a unique id for dropdown.
39
  $instance['dropdown'] = empty( $instance['dropdown'] ) ? 0 : $args['widget_id'];
40
 
41
  if ( $list = pll_the_languages( array_merge( $instance, array( 'echo' => 0 ) ) ) ) {
42
  $title = empty( $instance['title'] ) ? '' : $instance['title'];
43
+
44
  /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
45
  $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
46
 
47
  echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput
48
+
49
  if ( $title ) {
50
  echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput
51
  }
52
+
53
+ // The title may be filtered: Strip out HTML and make sure the aria-label is never empty.
54
+ $aria_label = trim( wp_strip_all_tags( $title ) );
55
+ if ( ! $aria_label ) {
56
+ $aria_label = __( 'Choose a language', 'polylang' );
57
+ }
58
+
59
  if ( $instance['dropdown'] ) {
60
+ echo '<label class="screen-reader-text" for="' . esc_attr( 'lang_choice_' . $instance['dropdown'] ) . '">' . esc_html( $aria_label ) . '</label>';
61
  echo $list; // phpcs:ignore WordPress.Security.EscapeOutput
62
  } else {
63
+ $format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml';
64
+
65
+ /** This filter is documented in wp-includes/widgets/class-wp-nav-menu-widget.php */
66
+ $format = apply_filters( 'navigation_widgets_format', $format );
67
+
68
+ if ( 'html5' === $format ) {
69
+ echo '<nav role="navigation" aria-label="' . esc_attr( $aria_label ) . '">';
70
+ }
71
+
72
  echo "<ul>\n" . $list . "</ul>\n"; // phpcs:ignore WordPress.Security.EscapeOutput
73
+
74
+ if ( 'html5' === $format ) {
75
+ echo '</nav>';
76
+ }
77
  }
78
+
79
  echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput
80
  }
81
  }
89
  * @param array $old_instance Old settings for this instance
90
  * @return array Settings to save or bool false to cancel saving
91
  */
92
+ public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
93
  $instance = array( 'title' => sanitize_text_field( $new_instance['title'] ) );
94
  foreach ( array_keys( PLL_Switcher::get_switcher_options( 'widget' ) ) as $key ) {
95
  $instance[ $key ] = ! empty( $new_instance[ $key ] ) ? 1 : 0;
130
  esc_attr( 'pll-' . $key )
131
  );
132
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  }
134
  }
install/install-base.php CHANGED
@@ -25,13 +25,8 @@ class PLL_Install_Base {
25
  register_activation_hook( $plugin_basename, array( $this, 'activate' ) );
26
  register_deactivation_hook( $plugin_basename, array( $this, 'deactivate' ) );
27
 
28
- // Blog creation on multisite.
29
- if ( version_compare( $GLOBALS['wp_version'], '5.1', '<' ) ) {
30
- // FIXME: Backward compatibility with WP < 5.1.
31
- add_action( 'wpmu_new_blog', array( $this, 'wpmu_new_blog' ), 5 ); // Before WP attempts to send mails which can break on some PHP versions
32
- } else {
33
- add_action( 'wp_insert_site', array( $this, 'new_site' ) );
34
- }
35
  }
36
 
37
  /**
@@ -123,18 +118,4 @@ class PLL_Install_Base {
123
  $this->_activate();
124
  restore_current_blog();
125
  }
126
-
127
- /**
128
- * Blog creation on multisite ( to set default options )
129
- * Backward compatibility with WP < 5.1
130
- *
131
- * @since 0.9.4
132
- *
133
- * @param int $blog_id
134
- */
135
- public function wpmu_new_blog( $blog_id ) {
136
- switch_to_blog( $blog_id );
137
- $this->_activate();
138
- restore_current_blog();
139
- }
140
  }
25
  register_activation_hook( $plugin_basename, array( $this, 'activate' ) );
26
  register_deactivation_hook( $plugin_basename, array( $this, 'deactivate' ) );
27
 
28
+ // Site creation on multisite.
29
+ add_action( 'wp_insert_site', array( $this, 'new_site' ) );
 
 
 
 
 
30
  }
31
 
32
  /**
118
  $this->_activate();
119
  restore_current_blog();
120
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  }
install/plugin-updater.php CHANGED
@@ -10,10 +10,10 @@ if ( ! defined( 'ABSPATH' ) ) {
10
 
11
  /**
12
  * Allows plugins to use their own update API.
13
- * Modified version with 'polylang' text domain and comments for translators
14
  *
15
  * @author Easy Digital Downloads
16
- * @version 1.7.1
17
  */
18
  class PLL_Plugin_Updater {
19
 
@@ -112,47 +112,50 @@ class PLL_Plugin_Updater {
112
  return $_transient_data;
113
  }
114
 
115
- $version_info = $this->get_cached_version_info();
116
-
117
- if ( false === $version_info ) {
118
- $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
119
-
120
- $this->set_version_info_cache( $version_info );
121
-
 
122
  }
 
 
123
 
124
- if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
125
-
126
- $no_update = false;
127
- if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
128
-
129
- $_transient_data->response[ $this->name ] = $version_info;
130
 
131
- // Make sure the plugin property is set to the plugin's name/location. See issue 1463 on Software Licensing's GitHub repo.
132
- $_transient_data->response[ $this->name ]->plugin = $this->name;
 
 
 
 
 
 
133
 
134
- } else {
135
- $no_update = new stdClass();
136
- $no_update->id = '';
137
- $no_update->slug = $this->slug;
138
- $no_update->plugin = $this->name;
139
- $no_update->new_version = $version_info->new_version;
140
- $no_update->url = $version_info->homepage;
141
- $no_update->package = isset( $version_info->package ) ? $version_info->package : ''; #modified#
142
- $no_update->icons = $version_info->icons;
143
- $no_update->banners = $version_info->banners;
144
- $no_update->banners_rtl = array();
145
  }
146
 
147
- $_transient_data->last_checked = time();
148
- $_transient_data->checked[ $this->name ] = $this->version;
 
149
 
150
- if ( $no_update ) {
151
- $_transient_data->no_update[ $this->name ] = $no_update;
152
- }
153
  }
154
 
155
- return $_transient_data;
156
  }
157
 
158
  /**
@@ -188,7 +191,7 @@ class PLL_Plugin_Updater {
188
 
189
  if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
190
 
191
- $version_info = $this->get_cached_version_info();
192
 
193
  if ( false === $version_info ) {
194
  $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
@@ -217,29 +220,14 @@ class PLL_Plugin_Updater {
217
  return;
218
  }
219
 
220
- $no_update = false;
221
  if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
222
-
223
  $update_cache->response[ $this->name ] = $version_info;
224
-
225
  } else {
226
- $no_update = new stdClass();
227
- $no_update->id = '';
228
- $no_update->slug = $this->slug;
229
- $no_update->plugin = $this->name;
230
- $no_update->new_version = $version_info->new_version;
231
- $no_update->url = $version_info->homepage;
232
- $no_update->package = isset( $version_info->package ) ? $version_info->package : ''; #modified#
233
- $no_update->icons = $version_info->icons;
234
- $no_update->banners = $version_info->banners;
235
- $no_update->banners_rtl = array();
236
  }
237
 
238
  $update_cache->last_checked = time();
239
  $update_cache->checked[ $this->name ] = $this->version;
240
- if ( $no_update ) {
241
- $update_cache->no_update[ $this->name ] = $no_update;
242
- }
243
 
244
  set_site_transient( 'update_plugins', $update_cache );
245
 
@@ -445,16 +433,16 @@ class PLL_Plugin_Updater {
445
  }
446
 
447
  if ( false === $edd_plugin_url_available[ $store_hash ] ) {
448
- return;
449
  }
450
 
451
  $data = array_merge( $this->api_data, $_data );
452
 
453
  if ( $data['slug'] != $this->slug ) {
454
- return;
455
  }
456
 
457
- if( $this->api_url == trailingslashit ( home_url() ) ) {
458
  return false; // Don't allow a plugin to ping itself
459
  }
460
 
@@ -490,7 +478,7 @@ class PLL_Plugin_Updater {
490
  $request->icons = maybe_unserialize( $request->icons );
491
  }
492
 
493
- if( ! empty( $request->sections ) ) {
494
  foreach( $request->sections as $key => $section ) {
495
  $request->$key = (array) $section;
496
  }
@@ -634,4 +622,3 @@ class PLL_Plugin_Updater {
634
  }
635
 
636
  }
637
-
10
 
11
  /**
12
  * Allows plugins to use their own update API.
13
+ * Modified version with 'polylang' text domain and comments for translators.
14
  *
15
  * @author Easy Digital Downloads
16
+ * @version 1.8.0
17
  */
18
  class PLL_Plugin_Updater {
19
 
112
  return $_transient_data;
113
  }
114
 
115
+ $current = $this->get_repo_api_data();
116
+ if ( false !== $current && is_object( $current ) && isset( $current->new_version ) ) {
117
+ if ( version_compare( $this->version, $current->new_version, '<' ) ) {
118
+ $_transient_data->response[ $this->name ] = $current;
119
+ } else {
120
+ // Populating the no_update information is required to support auto-updates in WordPress 5.5.
121
+ $_transient_data->no_update[ $this->name ] = $current;
122
+ }
123
  }
124
+ $_transient_data->last_checked = time();
125
+ $_transient_data->checked[ $this->name ] = $this->version;
126
 
127
+ return $_transient_data;
128
+ }
 
 
 
 
129
 
130
+ /**
131
+ * Get repo API data from store.
132
+ * Save to cache.
133
+ *
134
+ * @return \stdClass
135
+ */
136
+ public function get_repo_api_data() {
137
+ $version_info = $this->get_cached_version_info();
138
 
139
+ if ( false === $version_info ) {
140
+ $version_info = $this->api_request(
141
+ 'plugin_latest_version',
142
+ array(
143
+ 'slug' => $this->slug,
144
+ 'beta' => $this->beta,
145
+ )
146
+ );
147
+ if ( ! $version_info ) {
148
+ return false;
 
149
  }
150
 
151
+ // This is required for your plugin to support auto-updates in WordPress 5.5.
152
+ $version_info->plugin = $this->name;
153
+ $version_info->id = $this->name;
154
 
155
+ $this->set_version_info_cache( $version_info );
 
 
156
  }
157
 
158
+ return $version_info;
159
  }
160
 
161
  /**
191
 
192
  if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
193
 
194
+ $version_info = $this->get_repo_api_data();
195
 
196
  if ( false === $version_info ) {
197
  $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
220
  return;
221
  }
222
 
 
223
  if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
 
224
  $update_cache->response[ $this->name ] = $version_info;
 
225
  } else {
226
+ $update_cache->no_update[ $this->name ] = $version_info;
 
 
 
 
 
 
 
 
 
227
  }
228
 
229
  $update_cache->last_checked = time();
230
  $update_cache->checked[ $this->name ] = $this->version;
 
 
 
231
 
232
  set_site_transient( 'update_plugins', $update_cache );
233
 
433
  }
434
 
435
  if ( false === $edd_plugin_url_available[ $store_hash ] ) {
436
+ return false;
437
  }
438
 
439
  $data = array_merge( $this->api_data, $_data );
440
 
441
  if ( $data['slug'] != $this->slug ) {
442
+ return false;
443
  }
444
 
445
+ if ( $this->api_url == trailingslashit ( home_url() ) ) {
446
  return false; // Don't allow a plugin to ping itself
447
  }
448
 
478
  $request->icons = maybe_unserialize( $request->icons );
479
  }
480
 
481
+ if ( ! empty( $request->sections ) ) {
482
  foreach( $request->sections as $key => $section ) {
483
  $request->$key = (array) $section;
484
  }
622
  }
623
 
624
  }
 
integrations/cache/cache-compat.php CHANGED
@@ -34,19 +34,22 @@ class PLL_Cache_Compat {
34
  * @since 2.3
35
  */
36
  public function add_cookie_script() {
37
- $domain = ( 2 == PLL()->options['force_lang'] ) ? wp_parse_url( PLL()->links_model->home, PHP_URL_HOST ) : COOKIE_DOMAIN;
 
 
38
  $js = sprintf(
39
  '(function() {
40
  var expirationDate = new Date();
41
  expirationDate.setTime( expirationDate.getTime() + %d * 1000 );
42
- document.cookie = "%s=%s; expires=" + expirationDate.toUTCString() + "; path=%s%s%s";
43
  }());',
44
  esc_js( apply_filters( 'pll_cookie_expiration', YEAR_IN_SECONDS ) ),
45
  esc_js( PLL_COOKIE ),
46
  esc_js( pll_current_language() ),
47
  esc_js( COOKIEPATH ),
48
  $domain ? '; domain=' . esc_js( $domain ) : '',
49
- is_ssl() ? '; secure' : ''
 
50
  );
51
  echo '<script type="text/javascript">' . $js . '</script>'; // phpcs:ignore WordPress.Security.EscapeOutput
52
  }
34
  * @since 2.3
35
  */
36
  public function add_cookie_script() {
37
+ $domain = ( 2 === PLL()->options['force_lang'] ) ? wp_parse_url( PLL()->links_model->home, PHP_URL_HOST ) : COOKIE_DOMAIN;
38
+ $samesite = ( 3 === $this->options['force_lang'] ) ? 'None' : 'Lax';
39
+
40
  $js = sprintf(
41
  '(function() {
42
  var expirationDate = new Date();
43
  expirationDate.setTime( expirationDate.getTime() + %d * 1000 );
44
+ document.cookie = "%s=%s; expires=" + expirationDate.toUTCString() + "; path=%s%s%s%s";
45
  }());',
46
  esc_js( apply_filters( 'pll_cookie_expiration', YEAR_IN_SECONDS ) ),
47
  esc_js( PLL_COOKIE ),
48
  esc_js( pll_current_language() ),
49
  esc_js( COOKIEPATH ),
50
  $domain ? '; domain=' . esc_js( $domain ) : '',
51
+ is_ssl() ? '; secure' : '',
52
+ '; SameSite=' . $samesite
53
  );
54
  echo '<script type="text/javascript">' . $js . '</script>'; // phpcs:ignore WordPress.Security.EscapeOutput
55
  }
integrations/integrations.php CHANGED
@@ -25,7 +25,7 @@ class PLL_Integrations {
25
  */
26
  protected function __construct() {
27
  // Loads external integrations.
28
- foreach ( glob( __DIR__ . '/*/load.php', GLOB_NOSORT ) as $load_script ) { // phpcs:ignore WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable
29
  require_once $load_script; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
30
  }
31
  }
25
  */
26
  protected function __construct() {
27
  // Loads external integrations.
28
+ foreach ( glob( __DIR__ . '/*/load.php', GLOB_NOSORT ) as $load_script ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable
29
  require_once $load_script; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
30
  }
31
  }
integrations/jetpack/jetpack.php CHANGED
@@ -16,8 +16,8 @@ class PLL_Jetpack {
16
  */
17
  public function __construct() {
18
  add_action( 'init', array( $this, 'jetpack_init' ) );
19
- add_action( 'jetpack_widget_get_top_posts', array( $this, 'jetpack_widget_get_top_posts' ), 10, 3 );
20
- add_filter( 'grunion_contact_form_field_html', array( $this, 'grunion_contact_form_field_html_filter' ), 10, 3 );
21
  add_filter( 'jetpack_open_graph_tags', array( $this, 'jetpack_ogp' ) );
22
  add_filter( 'jetpack_relatedposts_filter_filters', array( $this, 'jetpack_relatedposts_filter_filters' ), 10, 2 );
23
 
@@ -50,12 +50,10 @@ class PLL_Jetpack {
50
  *
51
  * @since 1.5.4
52
  *
53
- * @param array $posts Array of the most popular posts.
54
- * @param array $post_ids Array of Post IDs.
55
- * @param string $count Number of Top Posts we want to display.
56
  * @return array
57
  */
58
- public function jetpack_widget_get_top_posts( $posts, $post_ids, $count ) {
59
  foreach ( $posts as $k => $post ) {
60
  if ( pll_current_language() !== pll_get_post_language( $post['post_id'] ) ) {
61
  unset( $posts[ $k ] );
@@ -72,12 +70,11 @@ class PLL_Jetpack {
72
  *
73
  * @since 1.5.4
74
  *
75
- * @param string $r Contact Form HTML output.
76
- * @param string $field_label Field label.
77
- * @param int|null $id Post ID.
78
  * @return string
79
  */
80
- public function grunion_contact_form_field_html_filter( $r, $field_label, $id ) {
81
  if ( function_exists( 'icl_translate' ) ) {
82
  if ( pll_current_language() !== pll_default_language() ) {
83
  $label_translation = icl_translate( 'jetpack ', $field_label . '_label', $field_label );
16
  */
17
  public function __construct() {
18
  add_action( 'init', array( $this, 'jetpack_init' ) );
19
+ add_action( 'jetpack_widget_get_top_posts', array( $this, 'jetpack_widget_get_top_posts' ) );
20
+ add_filter( 'grunion_contact_form_field_html', array( $this, 'grunion_contact_form_field_html_filter' ), 10, 2 );
21
  add_filter( 'jetpack_open_graph_tags', array( $this, 'jetpack_ogp' ) );
22
  add_filter( 'jetpack_relatedposts_filter_filters', array( $this, 'jetpack_relatedposts_filter_filters' ), 10, 2 );
23
 
50
  *
51
  * @since 1.5.4
52
  *
53
+ * @param array $posts Array of the most popular posts.
 
 
54
  * @return array
55
  */
56
+ public function jetpack_widget_get_top_posts( $posts ) {
57
  foreach ( $posts as $k => $post ) {
58
  if ( pll_current_language() !== pll_get_post_language( $post['post_id'] ) ) {
59
  unset( $posts[ $k ] );
70
  *
71
  * @since 1.5.4
72
  *
73
+ * @param string $r Contact Form HTML output.
74
+ * @param string $field_label Field label.
 
75
  * @return string
76
  */
77
+ public function grunion_contact_form_field_html_filter( $r, $field_label ) {
78
  if ( function_exists( 'icl_translate' ) ) {
79
  if ( pll_current_language() !== pll_default_language() ) {
80
  $label_translation = icl_translate( 'jetpack ', $field_label . '_label', $field_label );
integrations/twenty-seventeen/twenty-seven-teen.php CHANGED
@@ -23,11 +23,8 @@ class PLL_Twenty_Seventeen {
23
  }
24
  }
25
 
26
- if ( PLL() instanceof PLL_Frontend ) {
27
- add_filter( 'theme_mod_external_header_video', 'pll__' );
28
- } else {
29
- pll_register_string( __( 'Header video', 'polylang' ), get_theme_mod( 'external_header_video' ), 'Twenty Seventeen', false );
30
- }
31
  }
32
  }
33
  }
23
  }
24
  }
25
 
26
+ $theme_slug = get_option( 'stylesheet' ); // In case we are using a child theme.
27
+ new PLL_Translate_Option( "theme_mods_$theme_slug", array( 'external_header_video' => 1 ), array( 'context' => 'Twenty Seventeen' ) );
 
 
 
28
  }
29
  }
30
  }
integrations/wp-sweep/wp-sweep.php CHANGED
@@ -16,6 +16,7 @@ class PLL_WP_Sweep {
16
  */
17
  public function init() {
18
  add_filter( 'wp_sweep_excluded_taxonomies', array( $this, 'wp_sweep_excluded_taxonomies' ) );
 
19
  }
20
 
21
  /**
@@ -29,4 +30,32 @@ class PLL_WP_Sweep {
29
  public function wp_sweep_excluded_taxonomies( $excluded_taxonomies ) {
30
  return array_merge( $excluded_taxonomies, array( 'term_language', 'term_translations' ) );
31
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  }
16
  */
17
  public function init() {
18
  add_filter( 'wp_sweep_excluded_taxonomies', array( $this, 'wp_sweep_excluded_taxonomies' ) );
19
+ add_filter( 'wp_sweep_excluded_termids', array( $this, 'wp_sweep_excluded_termids' ), 0 );
20
  }
21
 
22
  /**
30
  public function wp_sweep_excluded_taxonomies( $excluded_taxonomies ) {
31
  return array_merge( $excluded_taxonomies, array( 'term_language', 'term_translations' ) );
32
  }
33
+
34
+ /**
35
+ * Add the translation of the default taxonomy terms and our language terms to the excluded terms.
36
+ *
37
+ * @since 2.9
38
+ *
39
+ * @param array $excluded_term_ids List of term ids excluded from sweeping.
40
+ * @return array
41
+ */
42
+ public function wp_sweep_excluded_termids( $excluded_term_ids ) {
43
+ // We got a list of excluded terms (defaults and parents). Let exclude their translations too.
44
+ $_term_ids = array();
45
+
46
+ foreach ( $excluded_term_ids as $excluded_term_id ) {
47
+ $_term_ids = array_merge( $_term_ids, array_values( pll_get_term_translations( $excluded_term_id ) ) );
48
+ }
49
+
50
+ $excluded_term_ids = array_merge( $excluded_term_ids, $_term_ids );
51
+
52
+ // Add the terms of our languages.
53
+ $excluded_term_ids = array_merge(
54
+ $excluded_term_ids,
55
+ pll_languages_list( array( 'fields' => 'term_id' ) ),
56
+ pll_languages_list( array( 'fields' => 'tl_term_id' ) )
57
+ );
58
+
59
+ return array_unique( $excluded_term_ids );
60
+ }
61
  }
integrations/wpseo/wpseo.php CHANGED
@@ -16,9 +16,9 @@ class PLL_WPSEO {
16
  * @since 1.6.4
17
  */
18
  public function init() {
19
- if ( PLL() instanceof PLL_Frontend ) {
20
- add_filter( 'option_wpseo_titles', array( $this, 'wpseo_translate_titles' ) );
21
 
 
22
  // Filters sitemap queries to remove inactive language or to get
23
  // one sitemap per language when using multiple domains or subdomains
24
  // because WPSEO does not accept several domains or subdomains in one sitemap
@@ -47,8 +47,6 @@ class PLL_WPSEO {
47
  add_filter( 'wpseo_frontend_presentation', array( $this, 'frontend_presentation' ) );
48
  add_filter( 'wpseo_breadcrumb_indexables', array( $this, 'breadcrumb_indexables' ) );
49
  } else {
50
- add_action( 'admin_init', array( $this, 'wpseo_register_strings' ) );
51
-
52
  // Primary category
53
  add_filter( 'pll_copy_post_metas', array( $this, 'copy_post_metas' ) );
54
  add_filter( 'pll_translate_post_meta', array( $this, 'translate_post_meta' ), 10, 3 );
@@ -56,74 +54,38 @@ class PLL_WPSEO {
56
  }
57
 
58
  /**
59
- * Registers strings for custom post types and custom taxonomies titles and meta descriptions
60
  *
61
- * @since 2.0
62
  */
63
- public function wpseo_register_strings() {
64
- $options = get_option( 'wpseo_titles' );
 
65
  foreach ( get_post_types( array( 'public' => true, '_builtin' => false ) ) as $t ) {
66
  if ( pll_is_translated_post_type( $t ) ) {
67
- $this->_wpseo_register_strings( $options, array( 'title-' . $t, 'metadesc-' . $t ) );
 
68
  }
69
  }
 
70
  foreach ( get_post_types( array( 'has_archive' => true, '_builtin' => false ) ) as $t ) {
71
  if ( pll_is_translated_post_type( $t ) ) {
72
- $this->_wpseo_register_strings( $options, array( 'title-ptarchive-' . $t, 'metadesc-ptarchive-' . $t, 'bctitle-ptarchive-' . $t ) );
 
 
73
  }
74
  }
 
75
  foreach ( get_taxonomies( array( 'public' => true, '_builtin' => false ) ) as $t ) {
76
  if ( pll_is_translated_taxonomy( $t ) ) {
77
- $this->_wpseo_register_strings( $options, array( 'title-tax-' . $t, 'metadesc-tax-' . $t ) );
 
78
  }
79
  }
80
- }
81
 
82
- /**
83
- * Helper function to translate custom post types and custom taxonomies titles and meta descriptions
84
- *
85
- * @since 2.1.6
86
- *
87
- * @param array $options
88
- * @param array $titles
89
- * @return array
90
- */
91
- protected function _wpseo_translate_titles( $options, $titles ) {
92
- foreach ( $titles as $title ) {
93
- if ( ! empty( $options[ $title ] ) ) {
94
- $options[ $title ] = pll__( $options[ $title ] );
95
- }
96
  }
97
- return $options;
98
- }
99
-
100
- /**
101
- * Translates strings for custom post types and custom taxonomies titles and meta descriptions
102
- *
103
- * @since 2.0
104
- *
105
- * @param array $options
106
- * @return array
107
- */
108
- public function wpseo_translate_titles( $options ) {
109
- if ( PLL() instanceof PLL_Frontend ) {
110
- foreach ( get_post_types( array( 'public' => true, '_builtin' => false ) ) as $t ) {
111
- if ( pll_is_translated_post_type( $t ) ) {
112
- $options = $this->_wpseo_translate_titles( $options, array( 'title-' . $t, 'metadesc-' . $t ) );
113
- }
114
- }
115
- foreach ( get_post_types( array( 'has_archive' => true, '_builtin' => false ) ) as $t ) {
116
- if ( pll_is_translated_post_type( $t ) ) {
117
- $options = $this->_wpseo_translate_titles( $options, array( 'title-ptarchive-' . $t, 'metadesc-ptarchive-' . $t, 'bctitle-ptarchive-' . $t ) );
118
- }
119
- }
120
- foreach ( get_taxonomies( array( 'public' => true, '_builtin' => false ) ) as $t ) {
121
- if ( pll_is_translated_taxonomy( $t ) ) {
122
- $options = $this->_wpseo_translate_titles( $options, array( 'title-tax-' . $t, 'metadesc-tax-' . $t ) );
123
- }
124
- }
125
- }
126
- return $options;
127
  }
128
 
129
  /**
@@ -407,7 +369,7 @@ class PLL_WPSEO {
407
  case 'home-page':
408
  $presentation->model->permalink = pll_home_url();
409
  $presentation->model->title = WPSEO_Options::get( 'title-home-wpseo' );
410
- $presentation->model->description = WPSEO_Options::get( 'title-home-wpseo' );
411
  break;
412
 
413
  case 'post-type-archive':
@@ -460,24 +422,6 @@ class PLL_WPSEO {
460
  return $indexables;
461
  }
462
 
463
- /**
464
- * Helper function to register strings for custom post types and custom taxonomies titles and meta descriptions
465
- *
466
- * @since 2.1.6
467
- *
468
- * @param array $options
469
- * @param array $titles
470
- * @return array
471
- */
472
- protected function _wpseo_register_strings( $options, $titles ) {
473
- foreach ( $titles as $title ) {
474
- if ( ! empty( $options[ $title ] ) ) {
475
- pll_register_string( $title, $options[ $title ], 'wordpress-seo' );
476
- }
477
- }
478
- return $options;
479
- }
480
-
481
  /**
482
  * Synchronize the primary term
483
  *
16
  * @since 1.6.4
17
  */
18
  public function init() {
19
+ add_action( 'wp_loaded', array( $this, 'wpseo_translate_options' ) );
 
20
 
21
+ if ( PLL() instanceof PLL_Frontend ) {
22
  // Filters sitemap queries to remove inactive language or to get
23
  // one sitemap per language when using multiple domains or subdomains
24
  // because WPSEO does not accept several domains or subdomains in one sitemap
47
  add_filter( 'wpseo_frontend_presentation', array( $this, 'frontend_presentation' ) );
48
  add_filter( 'wpseo_breadcrumb_indexables', array( $this, 'breadcrumb_indexables' ) );
49
  } else {
 
 
50
  // Primary category
51
  add_filter( 'pll_copy_post_metas', array( $this, 'copy_post_metas' ) );
52
  add_filter( 'pll_translate_post_meta', array( $this, 'translate_post_meta' ), 10, 3 );
54
  }
55
 
56
  /**
57
+ * Registers custom post types and taxonomy titles for translation.
58
  *
59
+ * @since 2.9
60
  */
61
+ public function wpseo_translate_options() {
62
+ $keys = array();
63
+
64
  foreach ( get_post_types( array( 'public' => true, '_builtin' => false ) ) as $t ) {
65
  if ( pll_is_translated_post_type( $t ) ) {
66
+ $keys[] = 'title-' . $t;
67
+ $keys[] = 'metadesc-' . $t;
68
  }
69
  }
70
+
71
  foreach ( get_post_types( array( 'has_archive' => true, '_builtin' => false ) ) as $t ) {
72
  if ( pll_is_translated_post_type( $t ) ) {
73
+ $keys[] = 'title-ptarchive-' . $t;
74
+ $keys[] = 'metadesc-ptarchive-' . $t;
75
+ $keys[] = 'bctitle-ptarchive-' . $t;
76
  }
77
  }
78
+
79
  foreach ( get_taxonomies( array( 'public' => true, '_builtin' => false ) ) as $t ) {
80
  if ( pll_is_translated_taxonomy( $t ) ) {
81
+ $keys[] = 'title-tax-' . $t;
82
+ $keys[] = 'metadesc-tax-' . $t;
83
  }
84
  }
 
85
 
86
+ if ( ! empty( $keys ) ) {
87
+ new PLL_Translate_Option( 'wpseo_titles', array_fill_keys( $keys, 1 ), array( 'context' => 'wordpress-seo' ) );
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
 
91
  /**
369
  case 'home-page':
370
  $presentation->model->permalink = pll_home_url();
371
  $presentation->model->title = WPSEO_Options::get( 'title-home-wpseo' );
372
+ $presentation->model->description = WPSEO_Options::get( 'metadesc-home-wpseo' );
373
  break;
374
 
375
  case 'post-type-archive':
422
  return $indexables;
423
  }
424
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  /**
426
  * Synchronize the primary term
427
  *
js/admin.js CHANGED
@@ -2,18 +2,18 @@
2
  * @package Polylang
3
  */
4
 
5
- jQuery( document ).ready(
6
  function( $ ) {
7
- var transitionTimeout;
8
 
9
  // languages list table
10
  // accessibility to row actions on focus
11
  // mainly copy paste of WP code from common.js
 
12
  $( 'table.languages' ).on(
13
  { // restricted to languages list table
14
  focusin: function() {
15
  clearTimeout( transitionTimeout );
16
- focusedRowActions = $( this ).find( '.row-actions' );
17
  // transitionTimeout is necessary for Firefox, but Chrome won't remove the CSS class without a little help.
18
  $( '.row-actions' ).not( this ).removeClass( 'visible' );
19
  focusedRowActions.addClass( 'visible' );
@@ -32,79 +32,214 @@ jQuery( document ).ready(
32
  'tr'
33
  ); // acts on the whole tr instead of single td as we have actions links in several columns
34
 
35
- // Common functions for overriding language and flag dropdown list.
36
- var selectmenuRenderItem = function( wrapper, item ) {
37
- var li = $( '<li>' ).text( item.label ).prepend( $( item.element ).data( 'flag-html' ) );
38
- li.children( 'img' ).addClass( 'ui-icon' );
39
- return li.appendTo( wrapper );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  };
 
41
  var selectmenuRefreshButtonText = function( selectElement ) {
42
  var buttonText = $( selectElement ).selectmenu( 'instance' ).buttonText;
43
  buttonText.prepend( $( selectElement ).children( ':selected' ).data( 'flag-html' ) );
44
  buttonText.children( 'img' ).addClass( 'ui-icon' );
45
  };
46
- // Overrides the flag dropdown list with our customized jquery ui selectmenu.
 
 
 
 
 
47
 
48
- // Inject flag image when jQuery UI selectmenu is created or an item is selected.
49
- $( '#flag_list' ).on(
50
- 'selectmenucreate selectmenuselect',
51
- function(){
52
- selectmenuRefreshButtonText( this );
53
- }
54
- );
55
- // Refresh jQuery UI selectmenu when the value is changed programmatically: select another language or edit an existing language.
56
- // For putting the focus in the list on the selected item and injecting the right flag on the selected item.
57
- $( '#flag_list' ).on(
58
- 'selectmenuopen',
59
- function(){
60
- $( this ).selectmenu( 'refresh' ).trigger( 'selectmenuselect' );
61
- }
62
- );
63
- // Create the jQuery UI selectmenu widget
64
- $( '#flag_list' ).selectmenu( { width: '100%' } );
65
- // Overrides each item in the jQuery UI selectmenu list by injecting flag image.
66
- $( '#flag_list' ).selectmenu( 'instance' )._renderItem = selectmenuRenderItem;
67
 
68
- // Language choice in predefined languages in Polylang Languages settings page and wizard.
69
- // Overrides the predefined language dropdown list with our customized jquery ui selectmenu.
70
 
71
- // Inject flag image when jQuery UI selectmenu is created or an item is selected.
72
- $( '#lang_list' ).on(
73
- 'selectmenucreate selectmenuselect',
74
- function() {
75
- selectmenuRefreshButtonText( this );
 
 
 
 
 
 
 
 
 
 
 
 
76
  }
77
- );
78
- // Create the jQuery UI selectmenu widget
79
- $( '#lang_list' ).selectmenu( { width: '100%' } );
80
- // Overrides each element in the jQuery UI selectmenu list by injecting flag image.
81
- $( '#lang_list' ).selectmenu( 'instance' )._renderItem = selectmenuRenderItem;
82
-
83
- // Languages form
84
- // Fills the fields based on the language dropdown list choice
85
- $( '#add-lang #lang_list' ).on(
86
- 'selectmenuchange',
87
- function() {
88
- var value = $( this ).val().split( ':' );
89
- var selected = $( "option:selected", this ).text().split( ' - ' );
90
- $( '#lang_slug' ).val( value[0] );
91
- $( '#lang_locale' ).val( value[1] );
92
- $( 'input[name="rtl"]' ).val( [value[2]] );
93
- $( '#lang_name' ).val( selected[0] );
94
- $( '#flag_list').val( value[3] );
95
-
96
- // Refresh the jQuery UI selectmenu flags list.
97
- $( '#flag_list' ).selectmenu( 'refresh' ).trigger( 'selectmenuselect' );
98
- }
99
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
  // strings translations
102
  // save translations when pressing enter
103
- $( '.translation input' ).keypress(
 
104
  function( event ){
105
- if ( 13 === event.keyCode ) {
106
  event.preventDefault();
107
- $( '#submit' ).click();
108
  }
109
  }
110
  );
@@ -209,23 +344,25 @@ jQuery( document ).ready(
209
  );
210
 
211
  // act when pressing enter or esc in configurations
212
- $( '.pll-configure' ).keypress(
 
213
  function( event ){
214
- if ( 13 === event.keyCode ) {
215
  event.preventDefault();
216
- $( this ).find( '.save' ).click();
217
  }
218
 
219
- if ( 27 === event.keyCode ) {
220
  event.preventDefault();
221
- $( this ).find( '.cancel' ).click();
222
  }
223
  }
224
  );
225
 
226
  // settings URL modifications
227
  // manages visibility of fields
228
- $( "input[name='force_lang']" ).change(
 
229
  function() {
230
  function pll_toggle( a, test ) {
231
  test ? a.show() : a.hide();
@@ -269,3 +406,4 @@ jQuery( document ).ready(
269
  }
270
  }
271
  );
 
2
  * @package Polylang
3
  */
4
 
5
+ jQuery(
6
  function( $ ) {
 
7
 
8
  // languages list table
9
  // accessibility to row actions on focus
10
  // mainly copy paste of WP code from common.js
11
+ var transitionTimeout;
12
  $( 'table.languages' ).on(
13
  { // restricted to languages list table
14
  focusin: function() {
15
  clearTimeout( transitionTimeout );
16
+ var focusedRowActions = $( this ).find( '.row-actions' );
17
  // transitionTimeout is necessary for Firefox, but Chrome won't remove the CSS class without a little help.
18
  $( '.row-actions' ).not( this ).removeClass( 'visible' );
19
  focusedRowActions.addClass( 'visible' );
32
  'tr'
33
  ); // acts on the whole tr instead of single td as we have actions links in several columns
34
 
35
+ /**
36
+ * Common functions and variables for overriding languages and flags dropdown list by a jQuery UI selectmenu widget.
37
+ */
38
+
39
+ // Add a boolean variable to be able to check jQuery UI >= 1.12 which is introduced in WP 5.6.
40
+ // Backward compatibility WP < 5.6
41
+ var isJqueryUImin112 = $.ui.version >= '1.12.0';
42
+ // Allow to check if a flag list dropdown is present. Not present in the Wizard steps or other settings page.
43
+ var flagListExist = $( "#flag_list" ).length;
44
+ // Allow to check if a language list dropdown is present. Not present in other settings page.
45
+ var langListExist = $( "#lang_list" ).length;
46
+ // jQuery UI selectmenu widget width option
47
+ var defaultSelectmenuWidth = '95%';
48
+ var wizardSelectmenuWidth = '100%';
49
+
50
+ // Inject flag image when jQuery UI selectmenu is created or an item is selected.
51
+ // jQuery UI 1.12 introduce a wrapper inside de li tag which is necessary to selectmenu widget to work correctly.
52
+ // Mainly copy from the orginal jQuery UI 1.12 selectmenu widget _renderItem method.
53
+ // Note this code works fine with jQuery UI 1.11.4 too.
54
+ var selectmenuRenderItem = function( ul, item ) {
55
+ var li = $( '<li>' );
56
+ var wrapper = $( '<div>');
57
+
58
+ if ( item.disabled ) {
59
+ this._addClass( li, null, "ui-state-disabled" );
60
+ }
61
+ this._setText( wrapper, item.label );
62
+
63
+ // Add the flag from the data attribute in the selected element.
64
+ wrapper.prepend( $( item.element ).data( 'flag-html' ) );
65
+ wrapper.children( 'img' ).addClass( 'ui-icon' );
66
+
67
+ return li.append( wrapper ).appendTo( ul );
68
  };
69
+ // Override selected item to inject flag for jQuery UI less than 1.12.
70
  var selectmenuRefreshButtonText = function( selectElement ) {
71
  var buttonText = $( selectElement ).selectmenu( 'instance' ).buttonText;
72
  buttonText.prepend( $( selectElement ).children( ':selected' ).data( 'flag-html' ) );
73
  buttonText.children( 'img' ).addClass( 'ui-icon' );
74
  };
75
+ // Override selected item since jQuery UI 1.12 which introduces extension point method _renderButtonItem.
76
+ // @see https://api.jqueryui.com/1.12/selectmenu/#method-_renderButtonItem _renderButtonItem documentation.
77
+ var selectmenuRenderButtonItem = function ( selectElement ) {
78
+ var buttonItem = $( '<span>' );
79
+ this._setText( buttonItem, selectElement.label );
80
+ this._addClass( buttonItem, "ui-selectmenu-text" );
81
 
82
+ // Add the flag from the data attribute in the selected element.
83
+ buttonItem.prepend( $( selectElement.element ).data( 'flag-html' ) );
84
+ buttonItem.children( 'img' ).addClass( 'ui-icon' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
+ return buttonItem;
87
+ }
88
 
89
+ /**
90
+ * Initialize a jQuery UI selectmenu widget on a DOM element
91
+ *
92
+ * @param {*} element - The jQuery object representing the DOM element to attach the widget with.
93
+ * @param {*} config - All the parameters - options and callbacks - necessary to configure the jQuery UI selectmenu widget.
94
+ * @return {Object} - The jQuery UI selectmenu widget object instance.
95
+ */
96
+ function initializeSelectmenuWidget( element, config ) {
97
+ // Create the jQuery UI selectmenu widget for flags list dropdown and return its instance.
98
+ var selectmenuWidgetInstance = element.selectmenu( config ).selectmenu( 'instance' );
99
+ // Overrides each item in the jQuery UI selectmenu list by injecting flag image.
100
+ selectmenuWidgetInstance._renderItem = selectmenuRenderItem;
101
+ // Override the selected item rendering for jQuery UI 1.12
102
+ if ( isJqueryUImin112 ) {
103
+ selectmenuWidgetInstance._renderButtonItem = selectmenuRenderButtonItem;
104
+ // Need to refresh to take in account the new button item rendering method after the selectmenu widget instanciaion.
105
+ selectmenuWidgetInstance.refresh();
106
  }
107
+ return selectmenuWidgetInstance
108
+ }
109
+ /**
110
+ * Selectmenu widget common parameters for its configuration: options and callbacks.
111
+ */
112
+
113
+ // Selectmenu widget options
114
+ var selectmenuOptions = { width: defaultSelectmenuWidth };
115
+
116
+ // Selectmenu widget callbacks
117
+ var selectmenuFlagListCallbacks = {};
118
+ // Callbacks when Selectmenu widget create or select event is triggered.
119
+ var createSelectCallback = function( event, ui ) {
120
+ selectmenuRefreshButtonText( event.target );
121
+ }
122
+
123
+ /**
124
+ * Overrides the flag dropdown list with our customized jquery ui selectmenu.
125
+ */
126
+
127
+ // Callbacks when Selectmenu widget change or open event is triggered.
128
+ // Needed to correctly refresh the selected element in the list when editing an existing language or when the value change is triggered by the language choice.
129
+ // jQuery UI 1.11 callback version.
130
+ var changeOpenCallback = function( event, ui ){
131
+ selectmenuRefreshButtonText( $( event.target ).selectmenu( 'refresh' ) );
132
+ }
133
+ // jQueryUI 1.12 callback version.
134
+ var changeOpenCallbackjQueryUI112 = function( event, ui ){
135
+ // Just a refresh of the menu is needed with jQuery UI 1.12 because _renderButtonItem is triggered and then inject correctly the flag.
136
+ $( event.target ).selectmenu( 'refresh' );
137
+ }
138
+ // There is no need of create and select callbacks with jQuery UI 1.12 because overriding _renderButtonItem method do the job.
139
+ if ( isJqueryUImin112 ) {
140
+ selectmenuFlagListCallbacks =
141
+ {
142
+ change: changeOpenCallbackjQueryUI112,
143
+ open: changeOpenCallbackjQueryUI112,
144
+ };
145
+ } else {
146
+ selectmenuFlagListCallbacks = {
147
+ create: createSelectCallback,
148
+ select: createSelectCallback,
149
+ change: changeOpenCallback,
150
+ open: changeOpenCallback,
151
+ };
152
+ }
153
+
154
+ // Create the selectmenu widget only if the field is present.
155
+ if ( flagListExist ) {
156
+ // Create the jQuery UI selectmenu widget for flags list dropdown and return its instance.
157
+ var selectmenuFlagList = initializeSelectmenuWidget( $( '#flag_list' ), Object.assign( {}, selectmenuOptions, selectmenuFlagListCallbacks ) );
158
+ $( '#lang_list' ).on(
159
+ 'languageChanged',
160
+ function( event, flag ) {
161
+ // Refresh the flag field
162
+ selectmenuFlagList.element.val( flag );
163
+ selectmenuFlagList._trigger( 'change' );
164
+ }
165
+ );
166
+ }
167
+
168
+ /**
169
+ * Language choice in predefined languages in Polylang Languages settings page and wizard.
170
+ * Overrides the predefined language dropdown list with our customized jQuery ui selectmenu widget.
171
+ */
172
+
173
+ /**
174
+ * Fill the other language form fields from the language element selected in the language list dropdown.
175
+ *
176
+ * @param {Object} language - language object of the selected element in the language list dropdown.
177
+ */
178
+ function fillLanguageFields( language ) {
179
+ $( '#lang_slug' ).val( language.slug );
180
+ $( '#lang_locale' ).val( language.locale );
181
+ $( 'input[name="rtl"]' ).val( language.rtl );
182
+ $( '#lang_name' ).val( language.name );
183
+ }
184
+
185
+ /**
186
+ * Parse selected language element in the language list dropdown.
187
+ *
188
+ * @param {object} event - jQuery triggered event.
189
+ * @return {object} The language object with its named properties.
190
+ */
191
+ function parseSelectedLanguage( event ) {
192
+ var selectedElement = $('option:selected', event.target);
193
+ var values = selectedElement.val().split(':')
194
+ return {
195
+ slug: values[0],
196
+ locale: values[1],
197
+ rtl: [values[2]],
198
+ flag: values[3],
199
+ name: selectedElement.text().split(' - ')[0] // At the moment there is no need of the 2nd part because it corresponds on the locale which is already known by splitting the selected element value
200
+ };
201
+ }
202
+
203
+ // Callback when selectmenu widget change event is triggered.
204
+ var changeCallback = function( event, ui ) {
205
+ var language = parseSelectedLanguage( event );
206
+
207
+ fillLanguageFields( language );
208
+
209
+ $( event.target ).trigger( 'languageChanged', language.flag );
210
+ };
211
+
212
+ // Create the jQuery UI selectmenu widget languages list dropdown and return its instance.
213
+ var selectmenuLangListCallbacks = {};
214
+ // For the wizard we need a 100% width. So we override the previous defined value of selectmenuOptions.
215
+ if( $( '#lang_list' ).closest( '.pll-wizard-content' ).length > 0 ) {
216
+ selectmenuOptions = Object.assign( selectmenuOptions, { width: wizardSelectmenuWidth } );
217
+ }
218
+
219
+ // There is no need of create and select callbacks with jQuery UI 1.12 because overrinding _renderButtonItem method do the job.
220
+ if ( isJqueryUImin112 ) {
221
+ selectmenuLangListCallbacks = {
222
+ change: changeCallback,
223
+ };
224
+ } else {
225
+ selectmenuLangListCallbacks = {
226
+ create: createSelectCallback,
227
+ select: createSelectCallback,
228
+ change: changeCallback,
229
+ };
230
+ }
231
+ if ( langListExist ) {
232
+ initializeSelectmenuWidget( $( '#lang_list' ), Object.assign( {}, selectmenuOptions, selectmenuLangListCallbacks ) );
233
+ }
234
 
235
  // strings translations
236
  // save translations when pressing enter
237
+ $( '.translation input' ).on(
238
+ 'keydown',
239
  function( event ){
240
+ if ( 'Enter' === event.key ) {
241
  event.preventDefault();
242
+ $( '#submit' ).trigger( 'click' );
243
  }
244
  }
245
  );
344
  );
345
 
346
  // act when pressing enter or esc in configurations
347
+ $( '.pll-configure' ).on(
348
+ 'keydown',
349
  function( event ){
350
+ if ( 'Enter' === event.key ) {
351
  event.preventDefault();
352
+ $( this ).find( '.save' ).trigger( 'click' );
353
  }
354
 
355
+ if ( 'Escape' === event.key ) {
356
  event.preventDefault();
357
+ $( this ).find( '.cancel' ).trigger( 'click' );
358
  }
359
  }
360
  );
361
 
362
  // settings URL modifications
363
  // manages visibility of fields
364
+ $( "input[name='force_lang']" ).on(
365
+ 'change',
366
  function() {
367
  function pll_toggle( a, test ) {
368
  test ? a.show() : a.hide();
406
  }
407
  }
408
  );
409
+
js/admin.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(document).ready(function(e){var t;e("table.languages").on({focusin:function(){clearTimeout(t),focusedRowActions=e(this).find(".row-actions"),e(".row-actions").not(this).removeClass("visible"),focusedRowActions.addClass("visible")},focusout:function(){t=setTimeout(function(){focusedRowActions.removeClass("visible")},30)}},"tr");var n=function(t,n){var s=e("<li>").text(n.label).prepend(e(n.element).data("flag-html"));return s.children("img").addClass("ui-icon"),s.appendTo(t)},s=function(t){var n=e(t).selectmenu("instance").buttonText;n.prepend(e(t).children(":selected").data("flag-html")),n.children("img").addClass("ui-icon")};e("#flag_list").on("selectmenucreate selectmenuselect",function(){s(this)}),e("#flag_list").on("selectmenuopen",function(){e(this).selectmenu("refresh").trigger("selectmenuselect")}),e("#flag_list").selectmenu({width:"100%"}),e("#flag_list").selectmenu("instance")._renderItem=n,e("#lang_list").on("selectmenucreate selectmenuselect",function(){s(this)}),e("#lang_list").selectmenu({width:"100%"}),e("#lang_list").selectmenu("instance")._renderItem=n,e("#add-lang #lang_list").on("selectmenuchange",function(){var t=e(this).val().split(":"),n=e("option:selected",this).text().split(" - ");e("#lang_slug").val(t[0]),e("#lang_locale").val(t[1]),e('input[name="rtl"]').val([t[2]]),e("#lang_name").val(n[0]),e("#flag_list").val(t[3]),e("#flag_list").selectmenu("refresh").trigger("selectmenuselect")}),e(".translation input").keypress(function(t){13===t.keyCode&&(t.preventDefault(),e("#submit").click())}),e("#the-list").on("click",".configure>a",function(){return e(".pll-configure").hide().prev().show(),e(this).closest("tr").hide().next().show(),!1}),e("#the-list").on("click",".cancel",function(){e(this).closest("tr").hide().prev().show()}),e("#the-list").on("click",".save",function(){var t=e(this).closest("tr"),n=t.attr("id").split("-"),s={action:"pll_save_options",pll_ajax_settings:!0,module:n[n.length-1],_pll_nonce:e("#_pll_nonce").val()};s=t.find(":input").serialize()+"&"+e.param(s),e.post(ajaxurl,s,function(n){var s=wpAjax.parseAjaxResponse(n,"ajax-response");e.each(s.responses,function(){switch(this.what){case"license-update":e("#pll-license-"+this.data).replaceWith(this.supplemental.html);break;case"success":t.hide().prev().show();case"error":e(".settings-error").remove(),e("h1").after(this.data),e(".notice.is-dismissible").each(function(){var t=e(this),n=e('<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>'),s=pll_dismiss_notice||"";n.find(".screen-reader-text").text(s),t.append(n),n.on("click.wp-dismiss-notice",function(n){n.preventDefault(),t.fadeTo(100,0,function(){e(this).slideUp(100,function(){e(this).remove()})})})})}})})}),e(".pll-configure").keypress(function(t){13===t.keyCode&&(t.preventDefault(),e(this).find(".save").click()),27===t.keyCode&&(t.preventDefault(),e(this).find(".cancel").click())}),e("input[name='force_lang']").change(function(){function t(e,t){t?e.show():e.hide()}var n=e(this).val();t(e("#pll-domains-table"),3==n),t(e("#pll-hide-default"),3>n),t(e("#pll-rewrite"),2>n),t(e("#pll-redirect-lang"),2>n)}),e(".pll-deactivate-license").on("click",function(){var t={action:"pll_deactivate_license",pll_ajax_settings:!0,id:e(this).attr("id"),_pll_nonce:e("#_pll_nonce").val()};e.post(ajaxurl,t,function(t){e("#pll-license-"+t.id).replaceWith(t.html)})}),e(".if-js-closed").removeClass("if-js-closed").addClass("closed"),"undefined"!=typeof postboxes&&postboxes.add_postbox_toggles(pagenow)});
1
+ jQuery((function($){var transitionTimeout;$("table.languages").on({focusin:function(){clearTimeout(transitionTimeout);var focusedRowActions=$(this).find(".row-actions");$(".row-actions").not(this).removeClass("visible"),focusedRowActions.addClass("visible")},focusout:function(){transitionTimeout=setTimeout((function(){focusedRowActions.removeClass("visible")}),30)}},"tr");var isJqueryUImin112=$.ui.version>="1.12.0",flagListExist=$("#flag_list").length,langListExist=$("#lang_list").length,defaultSelectmenuWidth="95%",wizardSelectmenuWidth="100%",selectmenuRenderItem=function(ul,item){var li=$("<li>"),wrapper=$("<div>");return item.disabled&&this._addClass(li,null,"ui-state-disabled"),this._setText(wrapper,item.label),wrapper.prepend($(item.element).data("flag-html")),wrapper.children("img").addClass("ui-icon"),li.append(wrapper).appendTo(ul)},selectmenuRefreshButtonText=function(selectElement){var buttonText=$(selectElement).selectmenu("instance").buttonText;buttonText.prepend($(selectElement).children(":selected").data("flag-html")),buttonText.children("img").addClass("ui-icon")},selectmenuRenderButtonItem=function(selectElement){var buttonItem=$("<span>");return this._setText(buttonItem,selectElement.label),this._addClass(buttonItem,"ui-selectmenu-text"),buttonItem.prepend($(selectElement.element).data("flag-html")),buttonItem.children("img").addClass("ui-icon"),buttonItem};function initializeSelectmenuWidget(element,config){var selectmenuWidgetInstance=element.selectmenu(config).selectmenu("instance");return selectmenuWidgetInstance._renderItem=selectmenuRenderItem,isJqueryUImin112&&(selectmenuWidgetInstance._renderButtonItem=selectmenuRenderButtonItem,selectmenuWidgetInstance.refresh()),selectmenuWidgetInstance}var selectmenuOptions={width:"95%"},selectmenuFlagListCallbacks={},createSelectCallback=function(event,ui){selectmenuRefreshButtonText(event.target)},changeOpenCallback=function(event,ui){selectmenuRefreshButtonText($(event.target).selectmenu("refresh"))},changeOpenCallbackjQueryUI112=function(event,ui){$(event.target).selectmenu("refresh")};if(selectmenuFlagListCallbacks=isJqueryUImin112?{change:changeOpenCallbackjQueryUI112,open:changeOpenCallbackjQueryUI112}:{create:createSelectCallback,select:createSelectCallback,change:changeOpenCallback,open:changeOpenCallback},flagListExist){var selectmenuFlagList=initializeSelectmenuWidget($("#flag_list"),Object.assign({},selectmenuOptions,selectmenuFlagListCallbacks));$("#lang_list").on("languageChanged",(function(event,flag){selectmenuFlagList.element.val(flag),selectmenuFlagList._trigger("change")}))}function fillLanguageFields(language){$("#lang_slug").val(language.slug),$("#lang_locale").val(language.locale),$('input[name="rtl"]').val(language.rtl),$("#lang_name").val(language.name)}function parseSelectedLanguage(event){var selectedElement=$("option:selected",event.target),values=selectedElement.val().split(":");return{slug:values[0],locale:values[1],rtl:[values[2]],flag:values[3],name:selectedElement.text().split(" - ")[0]}}var changeCallback=function(event,ui){var language=parseSelectedLanguage(event);fillLanguageFields(language),$(event.target).trigger("languageChanged",language.flag)},selectmenuLangListCallbacks={};$("#lang_list").closest(".pll-wizard-content").length>0&&(selectmenuOptions=Object.assign(selectmenuOptions,{width:"100%"})),selectmenuLangListCallbacks=isJqueryUImin112?{change:changeCallback}:{create:createSelectCallback,select:createSelectCallback,change:changeCallback},langListExist&&initializeSelectmenuWidget($("#lang_list"),Object.assign({},selectmenuOptions,selectmenuLangListCallbacks)),$(".translation input").on("keydown",(function(event){"Enter"===event.key&&(event.preventDefault(),$("#submit").trigger("click"))})),$("#the-list").on("click",".configure>a",(function(){return $(".pll-configure").hide().prev().show(),$(this).closest("tr").hide().next().show(),!1})),$("#the-list").on("click",".cancel",(function(){$(this).closest("tr").hide().prev().show()})),$("#the-list").on("click",".save",(function(){var tr=$(this).closest("tr"),parts=tr.attr("id").split("-"),data={action:"pll_save_options",pll_ajax_settings:!0,module:parts[parts.length-1],_pll_nonce:$("#_pll_nonce").val()};data=tr.find(":input").serialize()+"&"+$.param(data),$.post(ajaxurl,data,(function(response){var res=wpAjax.parseAjaxResponse(response,"ajax-response");$.each(res.responses,(function(){switch(this.what){case"license-update":$("#pll-license-"+this.data).replaceWith(this.supplemental.html);break;case"success":tr.hide().prev().show();case"error":$(".settings-error").remove(),$("h1").after(this.data),$(".notice.is-dismissible").each((function(){var $this=$(this),$button=$('<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>'),btnText=pll_dismiss_notice||"";$button.find(".screen-reader-text").text(btnText),$this.append($button),$button.on("click.wp-dismiss-notice",(function(event){event.preventDefault(),$this.fadeTo(100,0,(function(){$(this).slideUp(100,(function(){$(this).remove()}))}))}))}))}}))}))})),$(".pll-configure").on("keydown",(function(event){"Enter"===event.key&&(event.preventDefault(),$(this).find(".save").trigger("click")),"Escape"===event.key&&(event.preventDefault(),$(this).find(".cancel").trigger("click"))})),$("input[name='force_lang']").on("change",(function(){function pll_toggle(a,test){test?a.show():a.hide()}var value=$(this).val();pll_toggle($("#pll-domains-table"),3==value),pll_toggle($("#pll-hide-default"),3>value),pll_toggle($("#pll-rewrite"),2>value),pll_toggle($("#pll-redirect-lang"),2>value)})),$(".pll-deactivate-license").on("click",(function(){var data={action:"pll_deactivate_license",pll_ajax_settings:!0,id:$(this).attr("id"),_pll_nonce:$("#_pll_nonce").val()};$.post(ajaxurl,data,(function(response){$("#pll-license-"+response.id).replaceWith(response.html)}))})),$(".if-js-closed").removeClass("if-js-closed").addClass("closed"),"undefined"!=typeof postboxes&&postboxes.add_postbox_toggles(pagenow)}));
js/block-editor.js CHANGED
@@ -11,7 +11,7 @@ wp.apiFetch.use(
11
  function( options, next ) {
12
  // If options.url is defined, this is not a REST request but a direct call to post.php for legacy metaboxes.
13
  if ( 'undefined' === typeof options.url ) {
14
- if ( 'undefined' === typeof options.data ) {
15
  // GET
16
  options.path += ( ( options.path.indexOf( '?' ) >= 0 ) ? '&lang=' : '?lang=' ) + getCurrentLanguage();
17
  } else {
@@ -39,10 +39,11 @@ function getCurrentLanguage() {
39
  *
40
  * @since 2.5
41
  */
42
- jQuery( document ).ready(
43
  function( $ ) {
44
  // savePost after changing the post's language and reload page for refreshing post translated data
45
- $( '.post_lang_choice' ).change(
 
46
  function() {
47
  const select = wp.data.select;
48
  const dispatch = wp.data.dispatch;
@@ -117,10 +118,11 @@ jQuery( document ).ready(
117
  *
118
  * @since 1.5
119
  */
120
- jQuery( document ).ready(
121
  function( $ ) {
122
  // Ajax for changing the post's language in the languages metabox
123
- $( '.post_lang_choice' ).change(
 
124
  function() {
125
  var data = {
126
  action: 'post_lang_choice',
@@ -182,7 +184,8 @@ jQuery( document ).ready(
182
  );
183
 
184
  // When the input box is emptied
185
- $( this ).blur(
 
186
  function() {
187
  if ( ! $( this ).val() ) {
188
  $( '#htr_lang_' + tr_lang ).val( 0 );
11
  function( options, next ) {
12
  // If options.url is defined, this is not a REST request but a direct call to post.php for legacy metaboxes.
13
  if ( 'undefined' === typeof options.url ) {
14
+ if ( 'undefined' === typeof options.data || null === options.data ) {
15
  // GET
16
  options.path += ( ( options.path.indexOf( '?' ) >= 0 ) ? '&lang=' : '?lang=' ) + getCurrentLanguage();
17
  } else {
39
  *
40
  * @since 2.5
41
  */
42
+ jQuery(
43
  function( $ ) {
44
  // savePost after changing the post's language and reload page for refreshing post translated data
45
+ $( '.post_lang_choice' ).on(
46
+ 'change',
47
  function() {
48
  const select = wp.data.select;
49
  const dispatch = wp.data.dispatch;
118
  *
119
  * @since 1.5
120
  */
121
+ jQuery(
122
  function( $ ) {
123
  // Ajax for changing the post's language in the languages metabox
124
+ $( '.post_lang_choice' ).on(
125
+ 'change',
126
  function() {
127
  var data = {
128
  action: 'post_lang_choice',
184
  );
185
 
186
  // When the input box is emptied
187
+ $( this ).on(
188
+ 'blur',
189
  function() {
190
  if ( ! $( this ).val() ) {
191
  $( '#htr_lang_' + tr_lang ).val( 0 );
js/block-editor.min.js CHANGED
@@ -1 +1 @@
1
- function getCurrentLanguage(){return document.querySelector("[name=post_lang_choice]").value}wp.apiFetch.use(function(t,e){return void 0===t.url&&(void 0===t.data?t.path+=(t.path.indexOf("?")>=0?"&lang=":"?lang=")+getCurrentLanguage():t.data.lang=getCurrentLanguage()),e(t)}),jQuery(document).ready(function(t){t(".post_lang_choice").change(function(){const t=wp.data.select,e=wp.data.dispatch,n=wp.data.subscribe;let a=null;const o=new Promise(function(e,o){a=n(function(){const n=t("core/editor").didPostSaveRequestSucceed(),a=t("core/editor").didPostSaveRequestFail();(n||a)&&(a?o():e())})});if(location.pathname.match(/post-new.php/gi)){const e=t("core/editor").getEditedPostAttribute("title"),n=t("core/editor").getEditedPostAttribute("content"),a=t("core/editor").getEditedPostAttribute("excerpt");""===e&&""===n&&""===a&&(-1!=location.search.indexOf("new_lang")?window.location.search=window.location.search.replace(/(?:new_lang=[^&]*)(&)?(.*)/,"new_lang="+this.value+"$1$2"):window.location.search=window.location.search+(-1!=window.location.search.indexOf("?")?"&":"?")+"new_lang="+this.value)}e("core/editor").savePost(),o.then(function(){a(),window.location.reload()},function(){a()}).catch(function(){a()})})}),jQuery(document).ready(function(t){function e(){t(".tr_lang").each(function(){var e=t(this).attr("id").substring(8),n=t(this).parent().parent().siblings(".pll-edit-column");t(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_posts_not_translated&post_language="+t(".post_lang_choice").val()+"&translation_language="+e+"&post_type="+t("#post_type").val()+"&_pll_nonce="+t("#_pll_nonce").val(),select:function(a,o){t("#htr_lang_"+e).val(o.item.id),n.html(o.item.link)}}),t(this).blur(function(){t(this).val()||(t("#htr_lang_"+e).val(0),n.html(n.siblings(".hidden").children().clone()))})})}t(".post_lang_choice").change(function(){var n={action:"post_lang_choice",lang:t(this).val(),post_type:t("#post_type").val(),post_id:t("#post_ID").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,n,function(n){var a=wpAjax.parseAjaxResponse(n,"ajax-response");t.each(a.responses,function(){switch(this.what){case"translations":t(".translations").html(this.data),e();break;case"flag":t(".pll-select-flag").html(this.data)}})})}),e()});
1
+ function getCurrentLanguage(){return document.querySelector("[name=post_lang_choice]").value}wp.apiFetch.use((function(options,next){return void 0===options.url&&(void 0===options.data||null===options.data?options.path+=(options.path.indexOf("?")>=0?"&lang=":"?lang=")+getCurrentLanguage():options.data.lang=getCurrentLanguage()),next(options)})),jQuery((function($){$(".post_lang_choice").on("change",(function(){const select=wp.data.select,dispatch=wp.data.dispatch,subscribe=wp.data.subscribe;let unsubscribe=null;const savePostIsDone=new Promise((function(resolve,reject){unsubscribe=subscribe((function(){const isSavePostSucceeded=select("core/editor").didPostSaveRequestSucceed(),isSavePostFailed=select("core/editor").didPostSaveRequestFail();(isSavePostSucceeded||isSavePostFailed)&&(isSavePostFailed?reject():resolve())}))}));if(location.pathname.match(/post-new.php/gi)){const title=select("core/editor").getEditedPostAttribute("title"),content=select("core/editor").getEditedPostAttribute("content"),excerpt=select("core/editor").getEditedPostAttribute("excerpt");""===title&&""===content&&""===excerpt&&(-1!=location.search.indexOf("new_lang")?window.location.search=window.location.search.replace(/(?:new_lang=[^&]*)(&)?(.*)/,"new_lang="+this.value+"$1$2"):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()}),(function(){unsubscribe()})).catch((function(){unsubscribe()}))}))})),jQuery((function($){function init_translations(){$(".tr_lang").each((function(){var tr_lang=$(this).attr("id").substring(8),td=$(this).parent().parent().siblings(".pll-edit-column");$(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_posts_not_translated&post_language="+$(".post_lang_choice").val()+"&translation_language="+tr_lang+"&post_type="+$("#post_type").val()+"&_pll_nonce="+$("#_pll_nonce").val(),select:function(event,ui){$("#htr_lang_"+tr_lang).val(ui.item.id),td.html(ui.item.link)}}),$(this).on("blur",(function(){$(this).val()||($("#htr_lang_"+tr_lang).val(0),td.html(td.siblings(".hidden").children().clone()))}))}))}$(".post_lang_choice").on("change",(function(){var data={action:"post_lang_choice",lang:$(this).val(),post_type:$("#post_type").val(),post_id:$("#post_ID").val(),_pll_nonce:$("#_pll_nonce").val()};$.post(ajaxurl,data,(function(response){var res=wpAjax.parseAjaxResponse(response,"ajax-response");$.each(res.responses,(function(){switch(this.what){case"translations":$(".translations").html(this.data),init_translations();break;case"flag":$(".pll-select-flag").html(this.data)}}))}))})),init_translations()}));
js/classic-editor.js CHANGED
@@ -3,64 +3,71 @@
3
  */
4
 
5
  // tag suggest in metabox
6
- (function( $ ){
7
- $.ajaxPrefilter(
8
- function( options, originalOptions, jqXHR ) {
9
- if ( 'string' === typeof options.data && -1 !== options.url.indexOf( 'action=ajax-tag-search' ) && ( lang = $( '.post_lang_choice' ).val() ) ) {
10
- options.data = 'lang=' + lang + '&' + options.data;
 
 
 
11
  }
12
- }
13
- );
14
- })( jQuery );
15
 
16
  // overrides tagBox.get
17
- (function( $ ){
18
- // overrides function to add the language
19
- tagBox.get = function( id ) {
20
- var tax = id.substr( id.indexOf( '-' ) + 1 );
21
-
22
- // add the language in the $_POST variable
23
- var data = {
24
- action: 'get-tagcloud',
25
- lang: $( '.post_lang_choice' ).val(),
26
- tax: tax
27
- }
28
-
29
- $.post(
30
- ajaxurl,
31
- data,
32
- function( r, stat ) {
33
- if ( 0 == r || 'success' != stat ) {
34
- r = wpAjax.broken;
35
- }
36
 
37
- // @see code from WordPress core https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/js/tags-box.js#L291
38
- // @see wp_generate_tag_cloud function which generate the escaped HTML https://github.com/WordPress/WordPress/blob/a02b5cc2a8eecb8e076fbb7cf4de7bd2ec8a8eb1/wp-includes/category-template.php#L966-L975
39
- r = $( '<div />' ).addClass( 'the-tagcloud' ).attr( 'id', 'tagcloud-' + tax ).html( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
40
- $( 'a', r ).click(
41
- function(){
42
- tagBox.flushTags( $( this ).closest( '.inside' ).children( '.tagsdiv' ), this );
43
- return false;
44
  }
45
- );
46
 
47
- // add an if else condition to allow modifying the tags outputed when switching the language
48
- if ( v = $( '#tagcloud-' + tax ).css( 'display' ) ) {
49
- // See the comment above when r variable is created.
50
- $( '#tagcloud-' + tax ).replaceWith( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
51
- $( '#tagcloud-' + tax ).css( 'display', v );
52
- }
53
- else {
54
- // See the comment above when r variable is created.
55
- $( '#' + id ).after( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  }
57
- }
58
- );
59
  }
60
- })( jQuery );
61
 
62
- jQuery( document ).ready(
63
- function( $ ) {
64
  // collect taxonomies - code partly copied from WordPress
65
  var taxonomies = new Array();
66
  $( '.categorydiv' ).each(
@@ -187,7 +194,8 @@ jQuery( document ).ready(
187
  );
188
 
189
  // when the input box is emptied
190
- $( this ).blur(
 
191
  function() {
192
  if ( ! $( this ).val() ) {
193
  $( '#htr_lang_' + tr_lang ).val( 0 );
3
  */
4
 
5
  // tag suggest in metabox
6
+ jQuery(
7
+ function( $ ) {
8
+ $.ajaxPrefilter(
9
+ function( options, originalOptions, jqXHR ) {
10
+ var lang = $( '.post_lang_choice' ).val();
11
+ if ( 'string' === typeof options.data && -1 !== options.url.indexOf( 'action=ajax-tag-search' ) && lang ) {
12
+ options.data = 'lang=' + lang + '&' + options.data;
13
+ }
14
  }
15
+ );
16
+ }
17
+ );
18
 
19
  // overrides tagBox.get
20
+ jQuery(
21
+ function( $ ) {
22
+ // overrides function to add the language
23
+ tagBox.get = function( id ) {
24
+ var tax = id.substr( id.indexOf( '-' ) + 1 );
25
+
26
+ // add the language in the $_POST variable
27
+ var data = {
28
+ action: 'get-tagcloud',
29
+ lang: $( '.post_lang_choice' ).val(),
30
+ tax: tax
31
+ }
 
 
 
 
 
 
 
32
 
33
+ $.post(
34
+ ajaxurl,
35
+ data,
36
+ function( r, stat ) {
37
+ if ( 0 == r || 'success' != stat ) {
38
+ r = wpAjax.broken;
 
39
  }
 
40
 
41
+ // @see code from WordPress core https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/js/tags-box.js#L291
42
+ // @see wp_generate_tag_cloud function which generate the escaped HTML https://github.com/WordPress/WordPress/blob/a02b5cc2a8eecb8e076fbb7cf4de7bd2ec8a8eb1/wp-includes/category-template.php#L966-L975
43
+ r = $( '<div />' ).addClass( 'the-tagcloud' ).attr( 'id', 'tagcloud-' + tax ).html( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
44
+ $( 'a', r ).click(
45
+ function(){
46
+ tagBox.flushTags( $( this ).closest( '.inside' ).children( '.tagsdiv' ), this );
47
+ return false;
48
+ }
49
+ );
50
+
51
+ var tagCloud = $( '#tagcloud-' + tax );
52
+ // add an if else condition to allow modifying the tags outputed when switching the language
53
+ var v = tagCloud.css( 'display' );
54
+ if ( v ) {
55
+ // See the comment above when r variable is created.
56
+ $( '#tagcloud-' + tax ).replaceWith( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
57
+ $( '#tagcloud-' + tax ).css( 'display', v );
58
+ }
59
+ else {
60
+ // See the comment above when r variable is created.
61
+ $( '#' + id ).after( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
62
+ }
63
  }
64
+ );
65
+ }
66
  }
67
+ );
68
 
69
+ jQuery(
70
+ function ( $ ) {
71
  // collect taxonomies - code partly copied from WordPress
72
  var taxonomies = new Array();
73
  $( '.categorydiv' ).each(
194
  );
195
 
196
  // when the input box is emptied
197
+ $( this ).on(
198
+ 'blur',
199
  function() {
200
  if ( ! $( this ).val() ) {
201
  $( '#htr_lang_' + tr_lang ).val( 0 );
js/classic-editor.min.js CHANGED
@@ -1 +1 @@
1
- !function(t){t.ajaxPrefilter(function(a,l,n){"string"==typeof a.data&&-1!==a.url.indexOf("action=ajax-tag-search")&&(lang=t(".post_lang_choice").val())&&(a.data="lang="+lang+"&"+a.data)})}(jQuery),function(t){tagBox.get=function(a){var l=a.substr(a.indexOf("-")+1),n={action:"get-tagcloud",lang:t(".post_lang_choice").val(),tax:l};t.post(ajaxurl,n,function(n,e){0!=n&&"success"==e||(n=wpAjax.broken),n=t("<div />").addClass("the-tagcloud").attr("id","tagcloud-"+l).html(n),t("a",n).click(function(){return tagBox.flushTags(t(this).closest(".inside").children(".tagsdiv"),this),!1}),(v=t("#tagcloud-"+l).css("display"))?(t("#tagcloud-"+l).replaceWith(n),t("#tagcloud-"+l).css("display",v)):t("#"+a).after(n)})}}(jQuery),jQuery(document).ready(function(t){var a=new Array;function l(){t(".tr_lang").each(function(){var a=t(this).attr("id").substring(8),l=t(this).parent().parent().siblings(".pll-edit-column");t(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_posts_not_translated&post_language="+t(".post_lang_choice").val()+"&translation_language="+a+"&post_type="+t("#post_type").val()+"&_pll_nonce="+t("#_pll_nonce").val(),select:function(n,e){t("#htr_lang_"+a).val(e.item.id),l.html(e.item.link)}}),t(this).blur(function(){t(this).val()||(t("#htr_lang_"+a).val(0),l.html(l.siblings(".hidden").children().clone()))})})}t(".categorydiv").each(function(){var l,n;(l=t(this).attr("id").split("-")).shift(),n=l.join("-"),a.push(n),t("#"+n+"-add-submit").before(t("<input />").attr("type","hidden").attr("id",n+"-lang").attr("name","term_lang_choice").attr("value",t(".post_lang_choice").val()))}),t(".post_lang_choice").change(function(){var n=t(this).val(),e=t(this).children('option[value="'+n+'"]').attr("lang"),i=t('.pll-translation-column > span[lang="'+e+'"]').attr("dir"),s={action:"post_lang_choice",lang:n,post_type:t("#post_type").val(),taxonomies:a,post_id:t("#post_ID").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,s,function(a){var n=wpAjax.parseAjaxResponse(a,"ajax-response");t.each(n.responses,function(){switch(this.what){case"translations":t(".translations").html(this.data),l();break;case"taxonomy":var a=this.data;t("#"+a+"checklist").html(this.supplemental.all),t("#"+a+"checklist-pop").html(this.supplemental.populars),t("#new"+a+"_parent").replaceWith(this.supplemental.dropdown),t("#"+a+"-lang").val(t(".post_lang_choice").val());break;case"pages":t("#parent_id").html(this.data);break;case"flag":t(".pll-select-flag").html(this.data);break;case"permalink":var n=t("#edit-slug-box");"-1"!=this.data&&n.children().length&&n.html(this.data)}}),t(".tagcloud-link").each(function(){var a=t(this).attr("id");tagBox.get(a)}),t("body").removeClass("pll-dir-rtl").removeClass("pll-dir-ltr").addClass("pll-dir-"+i),t("#content_ifr").contents().find("html").attr("lang",e).attr("dir",i),t("#content_ifr").contents().find("body").attr("dir",i)})}),l()});
1
+ jQuery(function(t){t.ajaxPrefilter(function(a,l,n){var e=t(".post_lang_choice").val();"string"==typeof a.data&&-1!==a.url.indexOf("action=ajax-tag-search")&&e&&(a.data="lang="+e+"&"+a.data)})}),jQuery(function(t){tagBox.get=function(a){var l=a.substr(a.indexOf("-")+1),n={action:"get-tagcloud",lang:t(".post_lang_choice").val(),tax:l};t.post(ajaxurl,n,function(n,e){0!=n&&"success"==e||(n=wpAjax.broken),n=t("<div />").addClass("the-tagcloud").attr("id","tagcloud-"+l).html(n),t("a",n).click(function(){return tagBox.flushTags(t(this).closest(".inside").children(".tagsdiv"),this),!1});var i=t("#tagcloud-"+l).css("display");i?(t("#tagcloud-"+l).replaceWith(n),t("#tagcloud-"+l).css("display",i)):t("#"+a).after(n)})}}),jQuery(function(t){var a=new Array;function l(){t(".tr_lang").each(function(){var a=t(this).attr("id").substring(8),l=t(this).parent().parent().siblings(".pll-edit-column");t(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_posts_not_translated&post_language="+t(".post_lang_choice").val()+"&translation_language="+a+"&post_type="+t("#post_type").val()+"&_pll_nonce="+t("#_pll_nonce").val(),select:function(n,e){t("#htr_lang_"+a).val(e.item.id),l.html(e.item.link)}}),t(this).on("blur",function(){t(this).val()||(t("#htr_lang_"+a).val(0),l.html(l.siblings(".hidden").children().clone()))})})}t(".categorydiv").each(function(){var l,n;(l=t(this).attr("id").split("-")).shift(),n=l.join("-"),a.push(n),t("#"+n+"-add-submit").before(t("<input />").attr("type","hidden").attr("id",n+"-lang").attr("name","term_lang_choice").attr("value",t(".post_lang_choice").val()))}),t(".post_lang_choice").change(function(){var n=t(this).val(),e=t(this).children('option[value="'+n+'"]').attr("lang"),i=t('.pll-translation-column > span[lang="'+e+'"]').attr("dir"),s={action:"post_lang_choice",lang:n,post_type:t("#post_type").val(),taxonomies:a,post_id:t("#post_ID").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,s,function(a){var n=wpAjax.parseAjaxResponse(a,"ajax-response");t.each(n.responses,function(){switch(this.what){case"translations":t(".translations").html(this.data),l();break;case"taxonomy":var a=this.data;t("#"+a+"checklist").html(this.supplemental.all),t("#"+a+"checklist-pop").html(this.supplemental.populars),t("#new"+a+"_parent").replaceWith(this.supplemental.dropdown),t("#"+a+"-lang").val(t(".post_lang_choice").val());break;case"pages":t("#parent_id").html(this.data);break;case"flag":t(".pll-select-flag").html(this.data);break;case"permalink":var n=t("#edit-slug-box");"-1"!=this.data&&n.children().length&&n.html(this.data)}}),t(".tagcloud-link").each(function(){var a=t(this).attr("id");tagBox.get(a)}),t("body").removeClass("pll-dir-rtl").removeClass("pll-dir-ltr").addClass("pll-dir-"+i),t("#content_ifr").contents().find("html").attr("lang",e).attr("dir",i),t("#content_ifr").contents().find("body").attr("dir",i)})}),l()});
js/post.js CHANGED
@@ -5,160 +5,172 @@
5
  /**
6
  * Tag suggest in quick edit
7
  */
8
- (function( $ ){
9
- $.ajaxPrefilter(
10
- function( options, originalOptions, jqXHR ) {
11
- if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=ajax-tag-search' ) && ( lang = $( ':input[name="inline_lang_choice"]' ).val() ) ) {
12
- options.data = 'lang=' + lang + '&' + options.data;
 
 
13
  }
14
- }
15
- );
16
- })( jQuery );
17
 
18
  /**
19
  * Quick edit
20
  */
21
- (function( $ ) {
22
- $( document ).bind(
23
- 'DOMNodeInserted',
24
- function( e ) {
25
- var t = $( e.target );
 
26
 
27
- // WP inserts the quick edit from
28
- if ( 'inline-edit' == t.attr( 'id' ) ) {
29
- var post_id = t.prev().attr( 'id' ).replace( "post-", "" );
30
 
31
- if ( post_id > 0 ) {
32
- // language dropdown
33
- var select = t.find( ':input[name="inline_lang_choice"]' );
34
- var lang = $( '#lang_' + post_id ).html();
35
- select.val( lang ); // populates the dropdown
36
 
37
- filter_terms( lang ); // initial filter for category checklist
38
- filter_pages( lang ); // initial filter for parent dropdown
39
 
40
- // modify category checklist an parent dropdown on language change
41
- select.change(
42
- function() {
43
- filter_terms( $( this ).val() );
44
- filter_pages( $( this ).val() );
45
- }
46
- );
 
47
  }
48
- }
49
 
50
- // filter category checklist
51
- function filter_terms( lang ) {
52
- if ( "undefined" != typeof( pll_term_languages ) ) {
53
- $.each(
54
- pll_term_languages,
55
- function( lg, term_tax ) {
56
- $.each(
57
- term_tax,
58
- function( tax, terms ) {
59
- $.each(
60
- terms,
61
- function( i ) {
62
- id = '#' + tax + '-' + pll_term_languages[ lg ][ tax ][ i ];
63
- lang == lg ? $( id ).show() : $( id ).hide();
64
- }
65
- );
66
- }
67
- );
68
- }
69
- );
 
 
 
70
  }
71
- }
72
 
73
- // filter parent page dropdown list
74
- function filter_pages( lang ) {
75
- if ( "undefined" != typeof( pll_page_languages ) ) {
76
- $.each(
77
- pll_page_languages,
78
- function( lg, pages ) {
79
- $.each(
80
- pages,
81
- function( i ) {
82
- v = $( '#post_parent option[value="' + pll_page_languages[ lg ][ i ] + '"]' );
83
- lang == lg ? v.show() : v.hide();
84
- }
85
- );
86
- }
87
- );
 
 
 
88
  }
89
  }
90
- }
91
- );
92
- })( jQuery );
93
 
94
  /**
95
  * Update rows of translated posts when the language is modified in quick edit
96
  * Acts on ajaxSuccess event
97
  */
98
- (function( $ ) {
99
- $( document ).ajaxSuccess(
100
- function( event, xhr, settings ) {
101
- function update_rows( post_id ) {
102
- // collect old translations
103
- var translations = new Array();
104
- $( '.translation_' + post_id ).each(
105
- function() {
106
- translations.push( $( this ).parent().parent().attr( 'id' ).substring( 5 ) );
107
- }
108
- );
 
109
 
110
- var data = {
111
- action: 'pll_update_post_rows',
112
- post_id: post_id,
113
- translations: translations.join( ',' ),
114
- post_type: $( "input[name='post_type']" ).val(),
115
- screen: $( "input[name='screen']" ).val(),
116
- _pll_nonce: $( "input[name='_inline_edit']" ).val() // reuse quick edit nonce
117
- };
118
 
119
- // get the modified rows in ajax and update them
120
- $.post(
121
- ajaxurl,
122
- data,
123
- function( response ) {
124
- if ( response ) {
125
- var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
126
- $.each(
127
- res.responses,
128
- function() {
129
- if ( 'row' == this.what ) {
130
- // data is built with a call to WP_Posts_List_Table::single_row method
131
- // which uses internally other WordPress methods which escape correctly values.
132
- // For Polylang language columns the HTML code is correctly escaped in PLL_Admin_Filters_Columns::post_column method.
133
- $( "#post-" + this.supplemental.post_id ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
 
134
  }
135
- }
136
- );
137
  }
138
- }
139
- );
140
- }
141
 
142
- if ( 'string' == typeof( settings.data ) ) { // Need to check the type due to block editor sometime sending FormData objects
143
- var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
144
- if ( 'undefined' != typeof( data['action'] ) && 'inline-save' == data['action'] ) {
145
- update_rows( data['post_ID'] );
 
146
  }
147
  }
148
- }
149
- );
150
- })( jQuery );
151
 
152
  /**
153
  * Media list table
154
  * When clicking on attach link, filters find post list per media language
155
  */
156
- (function( $ ){
157
- $.ajaxPrefilter(
158
- function ( options, originalOptions, jqXHR ) {
159
- if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=find_posts' ) ) {
160
- options.data = 'pll_post_id=' + $( '#affected' ).val() + '&' + options.data;
 
 
161
  }
162
- }
163
- );
164
- })( jQuery );
5
  /**
6
  * Tag suggest in quick edit
7
  */
8
+ jQuery(
9
+ function( $ ) {
10
+ $.ajaxPrefilter(
11
+ function( options, originalOptions, jqXHR ) {
12
+ if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=ajax-tag-search' ) && ( lang = $( ':input[name="inline_lang_choice"]' ).val() ) ) {
13
+ options.data = 'lang=' + lang + '&' + options.data;
14
+ }
15
  }
16
+ );
17
+ }
18
+ );
19
 
20
  /**
21
  * Quick edit
22
  */
23
+ jQuery(
24
+ function( $ ) {
25
+ $( document ).on(
26
+ 'DOMNodeInserted',
27
+ function( e ) {
28
+ var t = $( e.target );
29
 
30
+ // WP inserts the quick edit from
31
+ if ( 'inline-edit' == t.attr( 'id' ) ) {
32
+ var post_id = t.prev().attr( 'id' ).replace( "post-", "" );
33
 
34
+ if ( post_id > 0 ) {
35
+ // language dropdown
36
+ var select = t.find( ':input[name="inline_lang_choice"]' );
37
+ var lang = $( '#lang_' + post_id ).html();
38
+ select.val( lang ); // populates the dropdown
39
 
40
+ filter_terms( lang ); // initial filter for category checklist
41
+ filter_pages( lang ); // initial filter for parent dropdown
42
 
43
+ // modify category checklist an parent dropdown on language change
44
+ select.change(
45
+ function() {
46
+ filter_terms( $( this ).val() );
47
+ filter_pages( $( this ).val() );
48
+ }
49
+ );
50
+ }
51
  }
 
52
 
53
+ /**
54
+ * Filters the category checklist.
55
+ */
56
+ function filter_terms( lang ) {
57
+ if ( "undefined" != typeof( pll_term_languages ) ) {
58
+ $.each(
59
+ pll_term_languages,
60
+ function( lg, term_tax ) {
61
+ $.each(
62
+ term_tax,
63
+ function( tax, terms ) {
64
+ $.each(
65
+ terms,
66
+ function( i ) {
67
+ id = '#' + tax + '-' + pll_term_languages[ lg ][ tax ][ i ];
68
+ lang == lg ? $( id ).show() : $( id ).hide();
69
+ }
70
+ );
71
+ }
72
+ );
73
+ }
74
+ );
75
+ }
76
  }
 
77
 
78
+ /**
79
+ * Filters the parent page dropdown list.
80
+ */
81
+ function filter_pages( lang ) {
82
+ if ( "undefined" != typeof( pll_page_languages ) ) {
83
+ $.each(
84
+ pll_page_languages,
85
+ function( lg, pages ) {
86
+ $.each(
87
+ pages,
88
+ function( i ) {
89
+ v = $( '#post_parent option[value="' + pll_page_languages[ lg ][ i ] + '"]' );
90
+ lang == lg ? v.show() : v.hide();
91
+ }
92
+ );
93
+ }
94
+ );
95
+ }
96
  }
97
  }
98
+ );
99
+ }
100
+ );
101
 
102
  /**
103
  * Update rows of translated posts when the language is modified in quick edit
104
  * Acts on ajaxSuccess event
105
  */
106
+ jQuery(
107
+ function( $ ) {
108
+ $( document ).ajaxSuccess(
109
+ function( event, xhr, settings ) {
110
+ function update_rows( post_id ) {
111
+ // collect old translations
112
+ var translations = new Array();
113
+ $( '.translation_' + post_id ).each(
114
+ function() {
115
+ translations.push( $( this ).parent().parent().attr( 'id' ).substring( 5 ) );
116
+ }
117
+ );
118
 
119
+ var data = {
120
+ action: 'pll_update_post_rows',
121
+ post_id: post_id,
122
+ translations: translations.join( ',' ),
123
+ post_type: $( "input[name='post_type']" ).val(),
124
+ screen: $( "input[name='screen']" ).val(),
125
+ _pll_nonce: $( "input[name='_inline_edit']" ).val() // reuse quick edit nonce
126
+ };
127
 
128
+ // get the modified rows in ajax and update them
129
+ $.post(
130
+ ajaxurl,
131
+ data,
132
+ function( response ) {
133
+ if ( response ) {
134
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
135
+ $.each(
136
+ res.responses,
137
+ function() {
138
+ if ( 'row' == this.what ) {
139
+ // data is built with a call to WP_Posts_List_Table::single_row method
140
+ // which uses internally other WordPress methods which escape correctly values.
141
+ // For Polylang language columns the HTML code is correctly escaped in PLL_Admin_Filters_Columns::post_column method.
142
+ $( "#post-" + this.supplemental.post_id ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
143
+ }
144
  }
145
+ );
146
+ }
147
  }
148
+ );
149
+ }
 
150
 
151
+ if ( 'string' == typeof( settings.data ) ) { // Need to check the type due to block editor sometime sending FormData objects
152
+ var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
153
+ if ( 'undefined' != typeof( data['action'] ) && 'inline-save' == data['action'] ) {
154
+ update_rows( data['post_ID'] );
155
+ }
156
  }
157
  }
158
+ );
159
+ }
160
+ );
161
 
162
  /**
163
  * Media list table
164
  * When clicking on attach link, filters find post list per media language
165
  */
166
+ jQuery(
167
+ function( $ ) {
168
+ $.ajaxPrefilter(
169
+ function ( options, originalOptions, jqXHR ) {
170
+ if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=find_posts' ) ) {
171
+ options.data = 'pll_post_id=' + $( '#affected' ).val() + '&' + options.data;
172
+ }
173
  }
174
+ );
175
+ }
176
+ );
js/post.min.js CHANGED
@@ -1 +1 @@
1
- !function(a){a.ajaxPrefilter(function(n,t,e){"string"==typeof n.data&&-1!==n.data.indexOf("action=ajax-tag-search")&&(lang=a(':input[name="inline_lang_choice"]').val())&&(n.data="lang="+lang+"&"+n.data)})}(jQuery),function(a){a(document).bind("DOMNodeInserted",function(n){var t=a(n.target);if("inline-edit"==t.attr("id")){var e=t.prev().attr("id").replace("post-","");if(e>0){var i=t.find(':input[name="inline_lang_choice"]'),o=a("#lang_"+e).html();i.val(o),l(o),s(o),i.change(function(){l(a(this).val()),s(a(this).val())})}}function l(n){"undefined"!=typeof pll_term_languages&&a.each(pll_term_languages,function(t,e){a.each(e,function(e,i){a.each(i,function(i){id="#"+e+"-"+pll_term_languages[t][e][i],n==t?a(id).show():a(id).hide()})})})}function s(n){"undefined"!=typeof pll_page_languages&&a.each(pll_page_languages,function(t,e){a.each(e,function(e){v=a('#post_parent option[value="'+pll_page_languages[t][e]+'"]'),n==t?v.show():v.hide()})})}})}(jQuery),function(a){a(document).ajaxSuccess(function(n,t,e){if("string"==typeof e.data){var i=wpAjax.unserialize(e.data);void 0!==i.action&&"inline-save"==i.action&&function(n){var t=new Array;a(".translation_"+n).each(function(){t.push(a(this).parent().parent().attr("id").substring(5))});var e={action:"pll_update_post_rows",post_id:n,translations:t.join(","),post_type:a("input[name='post_type']").val(),screen:a("input[name='screen']").val(),_pll_nonce:a("input[name='_inline_edit']").val()};a.post(ajaxurl,e,function(n){if(n){var t=wpAjax.parseAjaxResponse(n,"ajax-response");a.each(t.responses,function(){"row"==this.what&&a("#post-"+this.supplemental.post_id).replaceWith(this.data)})}})}(i.post_ID)}})}(jQuery),function(a){a.ajaxPrefilter(function(n,t,e){"string"==typeof n.data&&-1!==n.data.indexOf("action=find_posts")&&(n.data="pll_post_id="+a("#affected").val()+"&"+n.data)})}(jQuery);
1
+ jQuery(function(a){a.ajaxPrefilter(function(n,t,e){"string"==typeof n.data&&-1!==n.data.indexOf("action=ajax-tag-search")&&(lang=a(':input[name="inline_lang_choice"]').val())&&(n.data="lang="+lang+"&"+n.data)})}),jQuery(function(a){a(document).on("DOMNodeInserted",function(n){var t=a(n.target);if("inline-edit"==t.attr("id")){var e=t.prev().attr("id").replace("post-","");if(e>0){var i=t.find(':input[name="inline_lang_choice"]'),o=a("#lang_"+e).html();i.val(o),l(o),s(o),i.change(function(){l(a(this).val()),s(a(this).val())})}}function l(n){"undefined"!=typeof pll_term_languages&&a.each(pll_term_languages,function(t,e){a.each(e,function(e,i){a.each(i,function(i){id="#"+e+"-"+pll_term_languages[t][e][i],n==t?a(id).show():a(id).hide()})})})}function s(n){"undefined"!=typeof pll_page_languages&&a.each(pll_page_languages,function(t,e){a.each(e,function(e){v=a('#post_parent option[value="'+pll_page_languages[t][e]+'"]'),n==t?v.show():v.hide()})})}})}),jQuery(function(a){a(document).ajaxSuccess(function(n,t,e){if("string"==typeof e.data){var i=wpAjax.unserialize(e.data);void 0!==i.action&&"inline-save"==i.action&&function(n){var t=new Array;a(".translation_"+n).each(function(){t.push(a(this).parent().parent().attr("id").substring(5))});var e={action:"pll_update_post_rows",post_id:n,translations:t.join(","),post_type:a("input[name='post_type']").val(),screen:a("input[name='screen']").val(),_pll_nonce:a("input[name='_inline_edit']").val()};a.post(ajaxurl,e,function(n){if(n){var t=wpAjax.parseAjaxResponse(n,"ajax-response");a.each(t.responses,function(){"row"==this.what&&a("#post-"+this.supplemental.post_id).replaceWith(this.data)})}})}(i.post_ID)}})}),jQuery(function(a){a.ajaxPrefilter(function(n,t,e){"string"==typeof n.data&&-1!==n.data.indexOf("action=find_posts")&&(n.data="pll_post_id="+a("#affected").val()+"&"+n.data)})});
js/term.js CHANGED
@@ -2,117 +2,124 @@
2
  * @package Polylang
3
  */
4
 
5
- // quick edit
6
- (function( $ ) {
7
- $( document ).bind(
8
- 'DOMNodeInserted',
9
- function( e ) {
10
- var t = $( e.target );
11
-
12
- // WP inserts the quick edit from
13
- if ( 'inline-edit' == t.attr( 'id' ) ) {
14
- var term_id = t.prev().attr( 'id' ).replace( "tag-", "" );
15
-
16
- if ( term_id > 0 ) {
17
- // language dropdown
18
- var select = t.find( ':input[name="inline_lang_choice"]' );
19
- var lang = $( '#lang_' + term_id ).html();
20
- select.val( lang ); // populates the dropdown
21
-
22
- // disable the language dropdown for default categories
23
- var default_cat = $( '#default_cat_' + term_id ).html();
24
- if ( term_id == default_cat ) {
25
- select.prop( 'disabled', true );
 
 
 
 
26
  }
27
  }
28
  }
29
- }
30
- );
31
- })( jQuery );
32
-
33
-
34
- // update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit
35
- // acts on ajaxSuccess event
36
- (function( $ ) {
37
- $( document ).ajaxSuccess(
38
- function( event, xhr, settings ) {
39
- function update_rows( term_id ) {
40
- // collect old translations
41
- var translations = new Array();
42
- $( '.translation_' + term_id ).each(
43
- function() {
44
- translations.push( $( this ).parent().parent().attr( 'id' ).substring( 4 ) );
45
- }
46
- );
47
 
48
- var data = {
49
- action: 'pll_update_term_rows',
50
- term_id: term_id,
51
- translations: translations.join( ',' ),
52
- taxonomy: $( "input[name='taxonomy']" ).val(),
53
- post_type: $( "input[name='post_type']" ).val(),
54
- screen: $( "input[name='screen']" ).val(),
55
- _pll_nonce: $( '#_pll_nonce' ).val()
56
- };
 
 
 
 
 
 
 
57
 
58
- // get the modified rows in ajax and update them
59
- $.post(
60
- ajaxurl,
61
- data,
62
- function( response ) {
63
- if ( response ) {
64
- var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  $.each(
66
  res.responses,
67
  function() {
68
- if ( 'row' == this.what ) {
69
- // data is built with a call to WP_Terms_List_Table::single_row method
70
- // which uses internally other WordPress methods which escape correctly values.
71
- // For Polylang language columns the HTML code is correctly escaped in PLL_Admin_Filters_Columns::term_column method.
72
- $( "#tag-" + this.supplemental.term_id ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
73
  }
74
  }
75
  );
76
- }
77
- }
78
- );
79
- }
80
-
81
- var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
82
- if ( 'undefined' != typeof( data['action'] ) ) {
83
- switch ( data['action'] ) {
84
- // when adding a term, the new term_id is in the ajax response
85
- case 'add-tag':
86
- res = wpAjax.parseAjaxResponse( xhr.responseXML, 'ajax-response' );
87
- $.each(
88
- res.responses,
89
- function() {
90
- if ( 'term' == this.what ) {
91
- update_rows( this.supplemental.term_id );
92
- }
93
- }
94
- );
95
 
96
- // and also reset translations hidden input fields
97
- $( '.htr_lang' ).val( 0 );
98
- break;
99
 
100
- // when deleting a term
101
- case 'delete-tag':
102
- update_rows( data['tag_ID'] );
103
- break;
104
 
105
- // in case the language is modified in quick edit and breaks translations
106
- case 'inline-save-tax':
107
- update_rows( data['tax_ID'] );
108
- break;
 
109
  }
110
  }
111
- }
112
- );
113
- })( jQuery );
114
 
115
- jQuery( document ).ready(
116
  function( $ ) {
117
  // translations autocomplete input box
118
  function init_translations() {
@@ -140,7 +147,8 @@ jQuery( document ).ready(
140
  );
141
 
142
  // when the input box is emptied
143
- $( this ).blur(
 
144
  function() {
145
  if ( ! $( this ).val() ) {
146
  $( '#htr_lang_' + tr_lang ).val( 0 );
2
  * @package Polylang
3
  */
4
 
5
+ /**
6
+ * Quick edit
7
+ */
8
+ jQuery(
9
+ function( $ ) {
10
+ $( document ).on(
11
+ 'DOMNodeInserted',
12
+ function( e ) {
13
+ var t = $( e.target );
14
+
15
+ // WP inserts the quick edit from
16
+ if ( 'inline-edit' == t.attr( 'id' ) ) {
17
+ var term_id = t.prev().attr( 'id' ).replace( "tag-", "" );
18
+
19
+ if ( term_id > 0 ) {
20
+ // language dropdown
21
+ var select = t.find( ':input[name="inline_lang_choice"]' );
22
+ var lang = $( '#lang_' + term_id ).html();
23
+ select.val( lang ); // populates the dropdown
24
+
25
+ // disable the language dropdown for default categories
26
+ var default_cat = $( '#default_cat_' + term_id ).html();
27
+ if ( term_id == default_cat ) {
28
+ select.prop( 'disabled', true );
29
+ }
30
  }
31
  }
32
  }
33
+ );
34
+ }
35
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
+ /**
38
+ * Update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit.
39
+ * Acts on ajaxSuccess event.
40
+ */
41
+ jQuery(
42
+ function( $ ) {
43
+ $( document ).ajaxSuccess(
44
+ function( event, xhr, settings ) {
45
+ function update_rows( term_id ) {
46
+ // collect old translations
47
+ var translations = new Array();
48
+ $( '.translation_' + term_id ).each(
49
+ function() {
50
+ translations.push( $( this ).parent().parent().attr( 'id' ).substring( 4 ) );
51
+ }
52
+ );
53
 
54
+ var data = {
55
+ action: 'pll_update_term_rows',
56
+ term_id: term_id,
57
+ translations: translations.join( ',' ),
58
+ taxonomy: $( "input[name='taxonomy']" ).val(),
59
+ post_type: $( "input[name='post_type']" ).val(),
60
+ screen: $( "input[name='screen']" ).val(),
61
+ _pll_nonce: $( '#_pll_nonce' ).val()
62
+ };
63
+
64
+ // get the modified rows in ajax and update them
65
+ $.post(
66
+ ajaxurl,
67
+ data,
68
+ function( response ) {
69
+ if ( response ) {
70
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
71
+ $.each(
72
+ res.responses,
73
+ function() {
74
+ if ( 'row' == this.what ) {
75
+ // data is built with a call to WP_Terms_List_Table::single_row method
76
+ // which uses internally other WordPress methods which escape correctly values.
77
+ // For Polylang language columns the HTML code is correctly escaped in PLL_Admin_Filters_Columns::term_column method.
78
+ $( "#tag-" + this.supplemental.term_id ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
79
+ }
80
+ }
81
+ );
82
+ }
83
+ }
84
+ );
85
+ }
86
+
87
+ var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
88
+ if ( 'undefined' != typeof( data['action'] ) ) {
89
+ switch ( data['action'] ) {
90
+ // when adding a term, the new term_id is in the ajax response
91
+ case 'add-tag':
92
+ res = wpAjax.parseAjaxResponse( xhr.responseXML, 'ajax-response' );
93
  $.each(
94
  res.responses,
95
  function() {
96
+ if ( 'term' == this.what ) {
97
+ update_rows( this.supplemental.term_id );
 
 
 
98
  }
99
  }
100
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
+ // and also reset translations hidden input fields
103
+ $( '.htr_lang' ).val( 0 );
104
+ break;
105
 
106
+ // when deleting a term
107
+ case 'delete-tag':
108
+ update_rows( data['tag_ID'] );
109
+ break;
110
 
111
+ // in case the language is modified in quick edit and breaks translations
112
+ case 'inline-save-tax':
113
+ update_rows( data['tax_ID'] );
114
+ break;
115
+ }
116
  }
117
  }
118
+ );
119
+ }
120
+ );
121
 
122
+ jQuery(
123
  function( $ ) {
124
  // translations autocomplete input box
125
  function init_translations() {
147
  );
148
 
149
  // when the input box is emptied
150
+ $( this ).on(
151
+ 'blur',
152
  function() {
153
  if ( ! $( this ).val() ) {
154
  $( '#htr_lang_' + tr_lang ).val( 0 );
js/term.min.js CHANGED
@@ -1 +1 @@
1
- !function(a){a(document).bind("DOMNodeInserted",function(t){var n=a(t.target);if("inline-edit"==n.attr("id")){var e=n.prev().attr("id").replace("tag-","");if(e>0){var l=n.find(':input[name="inline_lang_choice"]'),i=a("#lang_"+e).html();l.val(i),e==a("#default_cat_"+e).html()&&l.prop("disabled",!0)}}})}(jQuery),function(a){a(document).ajaxSuccess(function(t,n,e){function l(t){var n=new Array;a(".translation_"+t).each(function(){n.push(a(this).parent().parent().attr("id").substring(4))});var e={action:"pll_update_term_rows",term_id:t,translations:n.join(","),taxonomy:a("input[name='taxonomy']").val(),post_type:a("input[name='post_type']").val(),screen:a("input[name='screen']").val(),_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,e,function(t){if(t){var n=wpAjax.parseAjaxResponse(t,"ajax-response");a.each(n.responses,function(){"row"==this.what&&a("#tag-"+this.supplemental.term_id).replaceWith(this.data)})}})}var i=wpAjax.unserialize(e.data);if(void 0!==i.action)switch(i.action){case"add-tag":res=wpAjax.parseAjaxResponse(n.responseXML,"ajax-response"),a.each(res.responses,function(){"term"==this.what&&l(this.supplemental.term_id)}),a(".htr_lang").val(0);break;case"delete-tag":l(i.tag_ID);break;case"inline-save-tax":l(i.tax_ID)}})}(jQuery),jQuery(document).ready(function(a){function t(){a(".tr_lang").each(function(){var t=a(this).attr("id").substring(8),n=a(this).parent().parent().siblings(".pll-edit-column");a(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_terms_not_translated&term_language="+a("#term_lang_choice").val()+"&term_id="+a("input[name='tag_ID']").val()+"&taxonomy="+a("input[name='taxonomy']").val()+"&translation_language="+t+"&post_type="+typenow+"&_pll_nonce="+a("#_pll_nonce").val(),select:function(e,l){a("#htr_lang_"+t).val(l.item.id),n.html(l.item.link)}}),a(this).blur(function(){a(this).val()||(a("#htr_lang_"+t).val(0),n.html(n.siblings(".hidden").children().clone()))})})}t(),a("#term_lang_choice").change(function(){var n=a(this).val(),e=a(this).children('option[value="'+n+'"]').attr("lang"),l=a('.pll-translation-column > span[lang="'+e+'"]').attr("dir"),i={action:"term_lang_choice",lang:n,from_tag:a("input[name='from_tag']").val(),term_id:a("input[name='tag_ID']").val(),taxonomy:a("input[name='taxonomy']").val(),post_type:typenow,_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,i,function(n){var e=wpAjax.parseAjaxResponse(n,"ajax-response");a.each(e.responses,function(){switch(this.what){case"translations":a("#term-translations").html(this.data),t();break;case"parent":a("#parent").replaceWith(this.data);break;case"tag_cloud":a(".tagcloud").replaceWith(this.data);break;case"flag":a(".pll-select-flag").html(this.data)}}),a("body").removeClass("pll-dir-rtl").removeClass("pll-dir-ltr").addClass("pll-dir-"+l)})})});
1
+ jQuery(function(a){a(document).on("DOMNodeInserted",function(t){var n=a(t.target);if("inline-edit"==n.attr("id")){var e=n.prev().attr("id").replace("tag-","");if(e>0){var l=n.find(':input[name="inline_lang_choice"]'),i=a("#lang_"+e).html();l.val(i),e==a("#default_cat_"+e).html()&&l.prop("disabled",!0)}}})}),jQuery(function(a){a(document).ajaxSuccess(function(t,n,e){function l(t){var n=new Array;a(".translation_"+t).each(function(){n.push(a(this).parent().parent().attr("id").substring(4))});var e={action:"pll_update_term_rows",term_id:t,translations:n.join(","),taxonomy:a("input[name='taxonomy']").val(),post_type:a("input[name='post_type']").val(),screen:a("input[name='screen']").val(),_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,e,function(t){if(t){var n=wpAjax.parseAjaxResponse(t,"ajax-response");a.each(n.responses,function(){"row"==this.what&&a("#tag-"+this.supplemental.term_id).replaceWith(this.data)})}})}var i=wpAjax.unserialize(e.data);if(void 0!==i.action)switch(i.action){case"add-tag":res=wpAjax.parseAjaxResponse(n.responseXML,"ajax-response"),a.each(res.responses,function(){"term"==this.what&&l(this.supplemental.term_id)}),a(".htr_lang").val(0);break;case"delete-tag":l(i.tag_ID);break;case"inline-save-tax":l(i.tax_ID)}})}),jQuery(function(a){function t(){a(".tr_lang").each(function(){var t=a(this).attr("id").substring(8),n=a(this).parent().parent().siblings(".pll-edit-column");a(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_terms_not_translated&term_language="+a("#term_lang_choice").val()+"&term_id="+a("input[name='tag_ID']").val()+"&taxonomy="+a("input[name='taxonomy']").val()+"&translation_language="+t+"&post_type="+typenow+"&_pll_nonce="+a("#_pll_nonce").val(),select:function(e,l){a("#htr_lang_"+t).val(l.item.id),n.html(l.item.link)}}),a(this).on("blur",function(){a(this).val()||(a("#htr_lang_"+t).val(0),n.html(n.siblings(".hidden").children().clone()))})})}t(),a("#term_lang_choice").change(function(){var n=a(this).val(),e=a(this).children('option[value="'+n+'"]').attr("lang"),l=a('.pll-translation-column > span[lang="'+e+'"]').attr("dir"),i={action:"term_lang_choice",lang:n,from_tag:a("input[name='from_tag']").val(),term_id:a("input[name='tag_ID']").val(),taxonomy:a("input[name='taxonomy']").val(),post_type:typenow,_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,i,function(n){var e=wpAjax.parseAjaxResponse(n,"ajax-response");a.each(e.responses,function(){switch(this.what){case"translations":a("#term-translations").html(this.data),t();break;case"parent":a("#parent").replaceWith(this.data);break;case"tag_cloud":a(".tagcloud").replaceWith(this.data);break;case"flag":a(".pll-select-flag").html(this.data)}}),a("body").removeClass("pll-dir-rtl").removeClass("pll-dir-ltr").addClass("pll-dir-"+l)})})});
js/widgets.js CHANGED
@@ -96,5 +96,37 @@ jQuery(
96
  }
97
  );
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  }
100
  );
96
  }
97
  );
98
 
99
+ function pll_toggle( a, test ) {
100
+ test ? a.show() : a.hide();
101
+ }
102
+
103
+ // Remove all options if dropdown is checked.
104
+ $( '.widgets-sortables,.control-section-sidebar' ).on(
105
+ 'change',
106
+ '.pll-dropdown',
107
+ function() {
108
+ var this_id = $( this ).parent().parent().parent().children( '.widget-id' ).attr( 'value' );
109
+ pll_toggle( $( '.no-dropdown-' + this_id ), true != $( this ).prop( 'checked' ) );
110
+ }
111
+ );
112
+
113
+ // Disallow unchecking both show names and show flags.
114
+ var options = ['-show_flags', '-show_names'];
115
+ $.each(
116
+ options,
117
+ function( i, v ) {
118
+ $( '.widgets-sortables,.control-section-sidebar' ).on(
119
+ 'change',
120
+ '.pll' + v,
121
+ function() {
122
+ var this_id = $( this ).parent().parent().parent().children( '.widget-id' ).attr( 'value' );
123
+ if ( true != $( this ).prop( 'checked' ) ) {
124
+ $( '#widget-' + this_id + options[ 1 - i ] ).prop( 'checked', true );
125
+ }
126
+ }
127
+ );
128
+ }
129
+ );
130
+
131
  }
132
  );
js/widgets.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(function(e){var t,l,n;function i(t){if(n){t=e(t);var l=e(".widget-top .widget-title h3",t),i=e(".pll-lang-choice option:selected",t).val(),o=i&&n.hasOwnProperty(i)?n[i]:null;if(o){o+=" &nbsp; ";var d=e(".pll-lang",l);d.length?d.html(o):(flag=e("<span />").addClass("pll-lang").html(o),l.prepend(flag))}else e(".pll-lang",l).remove()}}if("undefined"!=typeof pll_widgets&&pll_widgets.hasOwnProperty("flags")&&(n=pll_widgets.flags),void 0!==wp.customize){function o(e){e.extended(wp.customize.Widgets.WidgetControl)&&(e.embedWidgetContent(),i(e.container.find(".widget")))}t=e("#customize-controls"),l=".customize-control .widget",wp.customize.control.each(o),wp.customize.control.bind("add",o)}else t=e("#widgets-right"),l=".widget";e(l,t).each(function(){i(this)}),t.on("change",".pll-lang-choice",function(){i(e(this).parents(".widget"))})});
1
+ jQuery(function(e){var t,n,i;function o(t){if(i){t=e(t);var n=e(".widget-top .widget-title h3",t),o=e(".pll-lang-choice option:selected",t).val(),l=o&&i.hasOwnProperty(o)?i[o]:null;if(l){l+=" &nbsp; ";var a=e(".pll-lang",n);a.length?a.html(l):(flag=e("<span />").addClass("pll-lang").html(l),n.prepend(flag))}else e(".pll-lang",n).remove()}}if("undefined"!=typeof pll_widgets&&pll_widgets.hasOwnProperty("flags")&&(i=pll_widgets.flags),void 0!==wp.customize){function l(e){e.extended(wp.customize.Widgets.WidgetControl)&&(e.embedWidgetContent(),o(e.container.find(".widget")))}t=e("#customize-controls"),n=".customize-control .widget",wp.customize.control.each(l),wp.customize.control.bind("add",l)}else t=e("#widgets-right"),n=".widget";e(n,t).each(function(){o(this)}),t.on("change",".pll-lang-choice",function(){o(e(this).parents(".widget"))}),e(".widgets-sortables,.control-section-sidebar").on("change",".pll-dropdown",function(){var t,n=e(this).parent().parent().parent().children(".widget-id").attr("value");t=e(".no-dropdown-"+n),1!=e(this).prop("checked")?t.show():t.hide()});var a=["-show_flags","-show_names"];e.each(a,function(t,n){e(".widgets-sortables,.control-section-sidebar").on("change",".pll"+n,function(){var n=e(this).parent().parent().parent().children(".widget-id").attr("value");1!=e(this).prop("checked")&&e("#widget-"+n+a[1-t]).prop("checked",!0)})})});
modules/share-slug/settings-share-slug.php CHANGED
@@ -75,17 +75,19 @@ class PLL_Settings_Share_Slug extends PLL_Settings_Module {
75
  ?>
76
  <script type='text/javascript'>
77
  //<![CDATA[
78
- ( function( $ ){
79
- $( "input[name='force_lang']" ).change( function() {
80
- var value = $( this ).val();
81
- if ( value > 0 ) {
82
- $( "#pll-module-share-slugs" ).removeClass( "inactive" ).addClass( "active" ).children( "td" ).children( ".row-actions" ).html( '<?php echo $activated; // phpcs:ignore WordPress.Security.EscapeOutput ?>' );
83
- }
84
- else {
85
- $( "#pll-module-share-slugs" ).removeClass( "active" ).addClass( "inactive" ).children( "td" ).children( ".row-actions" ).html( '<?php echo $deactivated; // phpcs:ignore WordPress.Security.EscapeOutput ?>' );
86
- }
87
- } );
88
- } )( jQuery );
 
 
89
  // ]]>
90
  </script>
91
  <?php
75
  ?>
76
  <script type='text/javascript'>
77
  //<![CDATA[
78
+ jQuery(
79
+ function( $ ){
80
+ $( "input[name='force_lang']" ).on( 'change', function() {
81
+ var value = $( this ).val();
82
+ if ( value > 0 ) {
83
+ $( "#pll-module-share-slugs" ).removeClass( "inactive" ).addClass( "active" ).children( "td" ).children( ".row-actions" ).html( '<?php echo $activated; // phpcs:ignore WordPress.Security.EscapeOutput ?>' );
84
+ }
85
+ else {
86
+ $( "#pll-module-share-slugs" ).removeClass( "active" ).addClass( "inactive" ).children( "td" ).children( ".row-actions" ).html( '<?php echo $deactivated; // phpcs:ignore WordPress.Security.EscapeOutput ?>' );
87
+ }
88
+ } );
89
+ }
90
+ );
91
  // ]]>
92
  </script>
93
  <?php
modules/site-health/admin-site-health.php CHANGED
@@ -6,7 +6,7 @@
6
  /**
7
  * Class PLL_Admin_Site_Health to add debug info in WP Site Health.
8
  *
9
- * @link https://make.wordpress.org/core/2019/04/25/site-health-check-in-5-2/
10
  *
11
  * @since 2.8
12
  */
6
  /**
7
  * Class PLL_Admin_Site_Health to add debug info in WP Site Health.
8
  *
9
+ * @see https://make.wordpress.org/core/2019/04/25/site-health-check-in-5-2/ since WordPress 5.2
10
  *
11
  * @since 2.8
12
  */
modules/sitemaps/sitemaps.php CHANGED
@@ -69,7 +69,7 @@ class PLL_Sitemaps {
69
  add_filter( 'pll_set_language_from_query', array( $this, 'set_language_from_query' ), 10, 2 );
70
  add_filter( 'rewrite_rules_array', array( $this, 'rewrite_rules' ) );
71
  add_filter( 'wp_sitemaps_add_provider', array( $this, 'replace_provider' ) );
72
- } else {
73
  add_filter( 'wp_sitemaps_index_entry', array( $this, 'index_entry' ) );
74
  add_filter( 'wp_sitemaps_stylesheet_url', array( $this->links_model, 'site_url' ) );
75
  add_filter( 'wp_sitemaps_stylesheet_index_url', array( $this->links_model, 'site_url' ) );
@@ -118,16 +118,19 @@ class PLL_Sitemaps {
118
  public function rewrite_rules( $rules ) {
119
  global $wp_rewrite;
120
 
121
- $newrules = array();
122
-
123
  $languages = $this->model->get_languages_list( array( 'fields' => 'slug' ) );
 
 
 
 
 
124
  if ( $this->options['hide_default'] ) {
125
  $languages = array_diff( $languages, array( $this->options['default_lang'] ) );
126
  }
127
 
128
- if ( ! empty( $languages ) ) {
129
- $slug = $wp_rewrite->root . ( $this->options['rewrite'] ? '^' : '^language/' ) . '(' . implode( '|', $languages ) . ')/';
130
- }
131
 
132
  foreach ( $rules as $key => $rule ) {
133
  if ( isset( $slug ) && false !== strpos( $rule, 'sitemap=$matches[1]' ) ) {
69
  add_filter( 'pll_set_language_from_query', array( $this, 'set_language_from_query' ), 10, 2 );
70
  add_filter( 'rewrite_rules_array', array( $this, 'rewrite_rules' ) );
71
  add_filter( 'wp_sitemaps_add_provider', array( $this, 'replace_provider' ) );
72
+ } elseif ( method_exists( $this->links_model, 'site_url' ) ) {
73
  add_filter( 'wp_sitemaps_index_entry', array( $this, 'index_entry' ) );
74
  add_filter( 'wp_sitemaps_stylesheet_url', array( $this->links_model, 'site_url' ) );
75
  add_filter( 'wp_sitemaps_stylesheet_index_url', array( $this->links_model, 'site_url' ) );
118
  public function rewrite_rules( $rules ) {
119
  global $wp_rewrite;
120
 
 
 
121
  $languages = $this->model->get_languages_list( array( 'fields' => 'slug' ) );
122
+
123
+ if ( empty( $languages ) ) {
124
+ return $rules;
125
+ }
126
+
127
  if ( $this->options['hide_default'] ) {
128
  $languages = array_diff( $languages, array( $this->options['default_lang'] ) );
129
  }
130
 
131
+ $slug = $wp_rewrite->root . ( $this->options['rewrite'] ? '^' : '^language/' ) . '(' . implode( '|', $languages ) . ')/';
132
+
133
+ $newrules = array();
134
 
135
  foreach ( $rules as $key => $rule ) {
136
  if ( isset( $slug ) && false !== strpos( $rule, 'sitemap=$matches[1]' ) ) {
modules/wizard/js/languages-step.js CHANGED
@@ -2,7 +2,7 @@
2
  * @package Polylang
3
  */
4
 
5
- jQuery( document ).ready(
6
  function( $ ) {
7
  var addLanguageForm = $( '.languages-step' ); // Form element.
8
  var languageFields = $( '#language-fields' ); // Element where to append hidden fields for creating language.
@@ -25,16 +25,16 @@ jQuery( document ).ready(
25
  // see template view-wizard-step-languages.php.
26
  var languageValueHtml = $( '<td />' ).text( language.text ).prepend( language.flagUrl ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
27
  var languageTrashIconHtml = $( '<td />' )
 
 
 
 
28
  .append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
29
  $( '<span />' )
30
- .addClass( 'dashicons dashicons-trash' )
31
- .attr( 'data-language', language.locale )
32
- .append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
33
- $( '<span />' )
34
- .addClass( 'screen-reader-text' )
35
- .text( pll_wizard_params.i18n_remove_language_icon )
36
- )
37
- );
38
  // see the comment and the harcoded code above. languageTrashIconHtml and languageValueHtml are safe.
39
  var languageLineHtml = $( '<tr />' ).prepend( languageTrashIconHtml ).prepend( languageValueHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
40
  var languageFieldHtml = $( '<input />' ).attr(
@@ -60,10 +60,10 @@ jQuery( document ).ready(
60
  // Remove line in languages table.
61
  $( this ).parents( 'tr' ).remove();
62
  // Remove input field.
63
- languageField = languageFields.children( 'input[value=' + $( this ).data( 'language' ) + ']' ).remove();
64
  // If there is no more languages hide languages table.
65
  if ( languagesListTable.children().length <= 0 ) {
66
- languagesTable.hide();
67
  }
68
  // Remove language from the Map.
69
  languagesMap.delete( $( this ).data( 'language' ) );
@@ -112,7 +112,7 @@ jQuery( document ).ready(
112
  * @param {object} field The jQuery element which will be focused
113
  */
114
  function focusOnField( field ) {
115
- field.focus();
116
  }
117
 
118
  /**
2
  * @package Polylang
3
  */
4
 
5
+ jQuery(
6
  function( $ ) {
7
  var addLanguageForm = $( '.languages-step' ); // Form element.
8
  var languageFields = $( '#language-fields' ); // Element where to append hidden fields for creating language.
25
  // see template view-wizard-step-languages.php.
26
  var languageValueHtml = $( '<td />' ).text( language.text ).prepend( language.flagUrl ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
27
  var languageTrashIconHtml = $( '<td />' )
28
+ .append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
29
+ $( '<span />' )
30
+ .addClass( 'dashicons dashicons-trash' )
31
+ .attr( 'data-language', language.locale )
32
  .append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
33
  $( '<span />' )
34
+ .addClass( 'screen-reader-text' )
35
+ .text( pll_wizard_params.i18n_remove_language_icon )
36
+ )
37
+ );
 
 
 
 
38
  // see the comment and the harcoded code above. languageTrashIconHtml and languageValueHtml are safe.
39
  var languageLineHtml = $( '<tr />' ).prepend( languageTrashIconHtml ).prepend( languageValueHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
40
  var languageFieldHtml = $( '<input />' ).attr(
60
  // Remove line in languages table.
61
  $( this ).parents( 'tr' ).remove();
62
  // Remove input field.
63
+ var languageField = languageFields.children( 'input[value=' + $( this ).data( 'language' ) + ']' ).remove();
64
  // If there is no more languages hide languages table.
65
  if ( languagesListTable.children().length <= 0 ) {
66
+ languagesTable.hide();
67
  }
68
  // Remove language from the Map.
69
  languagesMap.delete( $( this ).data( 'language' ) );
112
  * @param {object} field The jQuery element which will be focused
113
  */
114
  function focusOnField( field ) {
115
+ field.trigger( 'focus' );
116
  }
117
 
118
  /**
modules/wizard/js/languages-step.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(document).ready(function(a){var e=a(".languages-step"),n=a("#language-fields"),t=a("#languages"),l=a("#languages tbody"),i=a("#defined-languages tbody"),r=a("#lang_list"),d=a('[name="save_step"]'),s=a("#messages"),o=new Map,g=a("#dialog");function u(e){var i=a("<td />").text(e.text).prepend(e.flagUrl),d=a("<td />").append(a("<span />").addClass("dashicons dashicons-trash").attr("data-language",e.locale).append(a("<span />").addClass("screen-reader-text").text(pll_wizard_params.i18n_remove_language_icon))),s=a("<tr />").prepend(d).prepend(i),g=a("<input />").attr({type:"hidden",name:"languages[]"}).val(e.locale);r.val(""),r.selectmenu("refresh"),o.set(e.locale,e),l.append(s),l.on("click","span[data-language="+e.locale+"]",function(e){e.preventDefault(),a(this).parents("tr").remove(),languageField=n.children("input[value="+a(this).data("language")+"]").remove(),l.children().length<=0&&t.hide(),o.delete(a(this).data("language")),c()}),n.append(g)}function p(e){s.empty(),s.prepend(a("<p/>").addClass("error").text(e))}function c(){s.empty(),e.find(".error").removeClass("error field-in-error")}function _(a){a.addClass("error field-in-error")}function m(a){a.focus()}r.on("selectmenuchange",function(){c()}),a("#add-language").on("click",function(e){c();var n=e.currentTarget.form.lang_list.options[e.currentTarget.form.lang_list.selectedIndex];if(""===n.value||o.has(n.value)){var l=pll_wizard_params.i18n_no_language_selected;o.has(n.value)&&(l=pll_wizard_params.i18n_language_already_added),p(l),_(r.next("span.ui-selectmenu-button")),m(a("#lang_list-button"))}else u({locale:n.value,text:n.innerText,name:a(n).data("language-name"),flagUrl:a(n).data("flag-html")}),t.show(),m(a("#lang_list-button"))}),e.on("submit",function(n){var t,l=i.children().length>0,s=a("#lang_list").val();return o.size<=0&&!l?(""===s?(p(pll_wizard_params.i18n_no_language_added),_(r.next("span.ui-selectmenu-button")),m(a("#lang_list-button"))):(p(pll_wizard_params.i18n_add_language_needed),_(r.next("span.ui-selectmenu-button")),m(a("#add-language"))),!1):""!==s?(o.has(s)?(p(pll_wizard_params.i18n_language_already_added),_(r.next("span.ui-selectmenu-button")),m(a("#lang_list-button"))):g.dialog("open"),!1):((t=d).prop("disabled",!0),void e.append(a("<input />").prop({type:"hidden",name:t.prop("name"),value:t.prop("value")})))});var h=new URLSearchParams(document.location.search);function f(n){switch(n){case"yes":var t=a("#lang_list").children(":selected");u({locale:t[0].value,text:t[0].innerText,name:a(t).data("language-name"),flagUrl:a(t).data("flag-html")});break;case"no":r.val("")}g.dialog("close"),"ignore"===n?m(a("#lang_list-button")):e.submit()}h.has("activate_error")&&void 0!==pll_wizard_params[h.get("activate_error")]&&p(pll_wizard_params[h.get("activate_error")]),g.dialog({autoOpen:!1,modal:!0,draggable:!1,resizable:!1,title:pll_wizard_params.i18n_dialog_title,minWidth:600,maxWidth:"100%",open:function(e,n){a("body").hasClass("rtl")&&a(this).parent().css({right:a(this).parent().css("left"),left:"auto"}),a(this).find("#dialog-language").text(a("#lang_list").children(":selected")[0].innerText),a(this).find("#dialog-language-flag").empty().prepend(a("#lang_list").children(":selected").data("flag-html"))},buttons:[{text:pll_wizard_params.i18n_dialog_yes_button,click:function(a){f("yes")}},{text:pll_wizard_params.i18n_dialog_no_button,click:function(a){f("no")}},{text:pll_wizard_params.i18n_dialog_ignore_button,click:function(a){f("ignore")}}]})});
1
+ jQuery(function(a){var e=a(".languages-step"),n=a("#language-fields"),t=a("#languages"),l=a("#languages tbody"),i=a("#defined-languages tbody"),r=a("#lang_list"),d=a('[name="save_step"]'),s=a("#messages"),o=new Map,g=a("#dialog");function u(e){var i=a("<td />").text(e.text).prepend(e.flagUrl),d=a("<td />").append(a("<span />").addClass("dashicons dashicons-trash").attr("data-language",e.locale).append(a("<span />").addClass("screen-reader-text").text(pll_wizard_params.i18n_remove_language_icon))),s=a("<tr />").prepend(d).prepend(i),g=a("<input />").attr({type:"hidden",name:"languages[]"}).val(e.locale);r.val(""),r.selectmenu("refresh"),o.set(e.locale,e),l.append(s),l.on("click","span[data-language="+e.locale+"]",function(e){e.preventDefault(),a(this).parents("tr").remove();n.children("input[value="+a(this).data("language")+"]").remove();l.children().length<=0&&t.hide(),o.delete(a(this).data("language")),c()}),n.append(g)}function p(e){s.empty(),s.prepend(a("<p/>").addClass("error").text(e))}function c(){s.empty(),e.find(".error").removeClass("error field-in-error")}function _(a){a.addClass("error field-in-error")}function m(a){a.trigger("focus")}r.on("selectmenuchange",function(){c()}),a("#add-language").on("click",function(e){c();var n=e.currentTarget.form.lang_list.options[e.currentTarget.form.lang_list.selectedIndex];if(""===n.value||o.has(n.value)){var l=pll_wizard_params.i18n_no_language_selected;o.has(n.value)&&(l=pll_wizard_params.i18n_language_already_added),p(l),_(r.next("span.ui-selectmenu-button")),m(a("#lang_list-button"))}else u({locale:n.value,text:n.innerText,name:a(n).data("language-name"),flagUrl:a(n).data("flag-html")}),t.show(),m(a("#lang_list-button"))}),e.on("submit",function(n){var t,l=i.children().length>0,s=a("#lang_list").val();return o.size<=0&&!l?(""===s?(p(pll_wizard_params.i18n_no_language_added),_(r.next("span.ui-selectmenu-button")),m(a("#lang_list-button"))):(p(pll_wizard_params.i18n_add_language_needed),_(r.next("span.ui-selectmenu-button")),m(a("#add-language"))),!1):""!==s?(o.has(s)?(p(pll_wizard_params.i18n_language_already_added),_(r.next("span.ui-selectmenu-button")),m(a("#lang_list-button"))):g.dialog("open"),!1):((t=d).prop("disabled",!0),void e.append(a("<input />").prop({type:"hidden",name:t.prop("name"),value:t.prop("value")})))});var h=new URLSearchParams(document.location.search);function f(n){switch(n){case"yes":var t=a("#lang_list").children(":selected");u({locale:t[0].value,text:t[0].innerText,name:a(t).data("language-name"),flagUrl:a(t).data("flag-html")});break;case"no":r.val("")}g.dialog("close"),"ignore"===n?m(a("#lang_list-button")):e.submit()}h.has("activate_error")&&void 0!==pll_wizard_params[h.get("activate_error")]&&p(pll_wizard_params[h.get("activate_error")]),g.dialog({autoOpen:!1,modal:!0,draggable:!1,resizable:!1,title:pll_wizard_params.i18n_dialog_title,minWidth:600,maxWidth:"100%",open:function(e,n){a("body").hasClass("rtl")&&a(this).parent().css({right:a(this).parent().css("left"),left:"auto"}),a(this).find("#dialog-language").text(a("#lang_list").children(":selected")[0].innerText),a(this).find("#dialog-language-flag").empty().prepend(a("#lang_list").children(":selected").data("flag-html"))},buttons:[{text:pll_wizard_params.i18n_dialog_yes_button,click:function(a){f("yes")}},{text:pll_wizard_params.i18n_dialog_no_button,click:function(a){f("no")}},{text:pll_wizard_params.i18n_dialog_ignore_button,click:function(a){f("ignore")}}]})});
modules/wpml/wpml-api.php CHANGED
@@ -126,18 +126,16 @@ class PLL_WPML_API {
126
 
127
  /**
128
  * In WPML, get a language's native and translated name for display in a custom language switcher
129
- * Since Polylang does not implement the translated name, always returns only the native name
 
130
  *
131
  * @since 2.2
132
  *
133
- * @param mixed $null Not used.
134
- * @param string $native_name The language native name.
135
- * @param string|bool $translated_name The language translated name. Not used.
136
- * @param bool $native_hidden Whether to hide the language native name or not. Not used.
137
- * @param bool $translated_hidden Whether to hide the language translated name or not. Not used.
138
  * @return string
139
  */
140
- public function wpml_display_language_names( $null, $native_name, $translated_name = false, $native_hidden = false, $translated_hidden = false ) {
141
  return $native_name;
142
  }
143
 
@@ -172,10 +170,9 @@ class PLL_WPML_API {
172
  *
173
  * @since 2.0
174
  *
175
- * @param mixed $null Not used
176
  * @return bool
177
  */
178
- public function wpml_is_rtl( $null ) {
179
  return pll_current_language( 'is_rtl' );
180
  }
181
 
126
 
127
  /**
128
  * In WPML, get a language's native and translated name for display in a custom language switcher
129
+ * Since Polylang does not implement the translated name, always returns only the native name,
130
+ * so the 3rd, 4th and 5th parameters are not used.
131
  *
132
  * @since 2.2
133
  *
134
+ * @param mixed $null Not used.
135
+ * @param string $native_name The language native name.
 
 
 
136
  * @return string
137
  */
138
+ public function wpml_display_language_names( $null, $native_name ) {
139
  return $native_name;
140
  }
141
 
170
  *
171
  * @since 2.0
172
  *
 
173
  * @return bool
174
  */
175
+ public function wpml_is_rtl() {
176
  return pll_current_language( 'is_rtl' );
177
  }
178
 
modules/wpml/wpml-config.php CHANGED
@@ -201,123 +201,31 @@ class PLL_WPML_Config {
201
  * @param object $key XML node.
202
  */
203
  protected function register_or_translate_option( $context, $name, $key ) {
204
- if ( PLL() instanceof PLL_Frontend ) {
205
- $this->options[ $name ] = $key;
206
- add_filter( 'option_' . $name, array( $this, 'translate_strings' ) );
207
- } else {
208
- $this->register_string_recursive( $context, $name, get_option( $name ), $key );
209
- }
210
- }
211
-
212
- /**
213
- * Translates the strings for an option
214
- *
215
- * @since 1.0
216
- *
217
- * @param array|string $value Either a string to translate or a list of strings to translate
218
- * @return array|string translated string(s)
219
- */
220
- public function translate_strings( $value ) {
221
- $option = substr( current_filter(), 7 );
222
- return $this->translate_strings_recursive( $value, $this->options[ $option ] );
223
  }
224
 
225
  /**
226
- * Recursively registers strings for a serialized option
227
  *
228
- * @since 1.0
229
- * @since 2.7 Signature modified
230
  *
231
- * @param string $context The group in which the strings will be registered.
232
- * @param string $option Option name.
233
- * @param array $values Option value.
234
- * @param object $key XML node.
235
  */
236
- protected function register_string_recursive( $context, $option, $values, $key ) {
237
- if ( is_object( $values ) ) {
238
- $values = (array) $values;
239
- }
240
-
241
  $children = $key->children();
242
 
243
- if ( is_array( $values ) ) {
244
- if ( count( $children ) ) {
245
- foreach ( $children as $child ) {
246
- $attributes = $child->attributes();
247
- $name = (string) $attributes['name'];
248
-
249
- if ( isset( $values[ $name ] ) ) {
250
- $this->register_string_recursive( $context, $name, $values[ $name ], $child );
251
- continue;
252
- }
253
-
254
- $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
255
-
256
- foreach ( $values as $n => $value ) {
257
- // The first case could be handled by the next one, but we avoid calls to preg_match here.
258
- if ( '*' === $name || ( false !== strpos( $name, '*' ) && preg_match( $pattern, $n ) ) ) {
259
- $this->register_string_recursive( $context, $n, $value, $child );
260
- }
261
- }
262
- }
263
- } else {
264
- foreach ( $values as $n => $value ) {
265
- // Parent key is a wildcard and no sub-key has been whitelisted.
266
- $this->register_string_recursive( $context, $n, $value, $key );
267
- }
268
  }
269
  } else {
270
- pll_register_string( $option, $values, $context, true ); // Multiline as in WPML.
271
  }
272
- }
273
-
274
- /**
275
- * Recursively translates strings for a serialized option
276
- *
277
- * @since 1.0
278
- *
279
- * @param array|string $values Either a string to translate or a list of strings to translate.
280
- * @param object $key XML node.
281
- * @return array|string Translated string(s)
282
- */
283
- protected function translate_strings_recursive( $values, $key ) {
284
- $children = $key->children();
285
-
286
- if ( is_array( $values ) || is_object( $values ) ) {
287
- if ( count( $children ) ) {
288
- foreach ( $children as $child ) {
289
- $attributes = $child->attributes();
290
- $name = (string) $attributes['name'];
291
-
292
- if ( is_array( $values ) && isset( $values[ $name ] ) ) {
293
- $values[ $name ] = $this->translate_strings_recursive( $values[ $name ], $child );
294
- continue;
295
- }
296
-
297
- if ( is_object( $values ) && isset( $values->$name ) ) {
298
- $values->$name = $this->translate_strings_recursive( $values->$name, $child );
299
- continue;
300
- }
301
-
302
- $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
303
-
304
- foreach ( $values as $n => &$value ) {
305
- // The first case could be handled by the next one, but we avoid calls to preg_match here.
306
- if ( '*' === $name || ( false !== strpos( $name, '*' ) && preg_match( $pattern, $n ) ) ) {
307
- $value = $this->translate_strings_recursive( $value, $child );
308
- }
309
- }
310
- }
311
- } else {
312
- // Parent key is a wildcard and no sub-key has been whitelisted.
313
- foreach ( $values as &$value ) {
314
- $value = $this->translate_strings_recursive( $value, $key );
315
- }
316
- }
317
- } else {
318
- $values = pll__( $values );
319
- }
320
-
321
- return $values;
322
  }
323
  }
201
  * @param object $key XML node.
202
  */
203
  protected function register_or_translate_option( $context, $name, $key ) {
204
+ $option_keys = $this->xml_to_array( $key );
205
+ new PLL_Translate_Option( $name, reset( $option_keys ), array( 'context' => $context ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  }
207
 
208
  /**
209
+ * Recursively transforms xml nodes to an array, ready for PLL_Translate_Option.
210
  *
211
+ * @since 2.9
 
212
  *
213
+ * @param object $key XML node.
214
+ * @param array $arr Array of option keys to translate.
215
+ * @return array
 
216
  */
217
+ protected function xml_to_array( $key, &$arr = array() ) {
218
+ $attributes = $key->attributes();
219
+ $name = (string) $attributes['name'];
 
 
220
  $children = $key->children();
221
 
222
+ if ( count( $children ) ) {
223
+ foreach ( $children as $child ) {
224
+ $arr[ $name ] = $this->xml_to_array( $child, $arr[ $name ] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  }
226
  } else {
227
+ $arr[ $name ] = true; // Multiline as in WPML.
228
  }
229
+ return $arr;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  }
231
  }
modules/wpml/wpml-legacy-api.php CHANGED
@@ -59,11 +59,7 @@ if ( ! function_exists( 'icl_get_languages' ) ) {
59
 
60
  // NB: When 'skip_missing' is false, WPML returns all languages even if there is no content
61
  $languages = PLL()->model->get_languages_list( array( 'hide_empty' => $args['skip_missing'] ) );
62
-
63
- // FIXME: Backward compatibility with WP < 4.7
64
- if ( function_exists( 'wp_list_sort' ) ) {
65
- $languages = wp_list_sort( $languages, $orderby, $order ); // Since WP 4.7
66
- }
67
 
68
  foreach ( $languages as $lang ) {
69
  // We can find a translation only on frontend once the global $wp_query object has been instantiated
@@ -214,7 +210,7 @@ if ( ! function_exists( 'wpml_get_language_information' ) ) {
214
  * Undocumented function used by the theme Maya
215
  * returns the post language
216
  *
217
- * @see original WPML code at https://wpml.org/forums/topic/canonical-urls-for-wpml-duplicated-posts/#post-52198
218
  *
219
  * @since 1.8
220
  *
@@ -243,15 +239,15 @@ if ( ! function_exists( 'icl_register_string' ) ) {
243
  /**
244
  * Registers a string for translation in the "strings translation" panel
245
  *
 
 
246
  * @since 0.9.3
247
  *
248
  * @param string $context the group in which the string is registered, defaults to 'polylang'
249
  * @param string $name a unique name for the string
250
  * @param string $string the string to register
251
- * @param bool $allow_empty_value not used
252
- * @param string $source_lang not used by Polylang
253
  */
254
- function icl_register_string( $context, $name, $string, $allow_empty_value = false, $source_lang = '' ) {
255
  PLL_WPML_Compat::instance()->register_string( $context, $name, $string );
256
  }
257
  }
59
 
60
  // NB: When 'skip_missing' is false, WPML returns all languages even if there is no content
61
  $languages = PLL()->model->get_languages_list( array( 'hide_empty' => $args['skip_missing'] ) );
62
+ $languages = wp_list_sort( $languages, $orderby, $order ); // Since WP 4.7
 
 
 
 
63
 
64
  foreach ( $languages as $lang ) {
65
  // We can find a translation only on frontend once the global $wp_query object has been instantiated
210
  * Undocumented function used by the theme Maya
211
  * returns the post language
212
  *
213
+ * @see https://wpml.org/forums/topic/canonical-urls-for-wpml-duplicated-posts/#post-52198 for the original WPML code
214
  *
215
  * @since 1.8
216
  *
239
  /**
240
  * Registers a string for translation in the "strings translation" panel
241
  *
242
+ * The 4th and 5th parameters $allow_empty_value and $source_lang are not used by Polylang.
243
+ *
244
  * @since 0.9.3
245
  *
246
  * @param string $context the group in which the string is registered, defaults to 'polylang'
247
  * @param string $name a unique name for the string
248
  * @param string $string the string to register
 
 
249
  */
250
+ function icl_register_string( $context, $name, $string ) {
251
  PLL_WPML_Compat::instance()->register_string( $context, $name, $string );
252
  }
253
  }
polylang.php CHANGED
@@ -10,8 +10,8 @@
10
  * Plugin Name: Polylang
11
  * Plugin URI: https://polylang.pro
12
  * Description: Adds multilingual capability to WordPress
13
- * Version: 2.8.4
14
- * Requires at least: 4.9
15
  * Requires PHP: 5.6
16
  * Author: WP SYNTEX
17
  * Author URI: https://polylang.pro
@@ -53,8 +53,8 @@ if ( defined( 'POLYLANG_VERSION' ) ) {
53
  }
54
  } else {
55
  // Go on loading the plugin
56
- define( 'POLYLANG_VERSION', '2.8.4' );
57
- define( 'PLL_MIN_WP_VERSION', '4.9' );
58
  define( 'PLL_MIN_PHP_VERSION', '5.6' );
59
 
60
  define( 'POLYLANG_FILE', __FILE__ );
@@ -67,6 +67,7 @@ if ( defined( 'POLYLANG_VERSION' ) ) {
67
 
68
  define( 'POLYLANG', ucwords( str_replace( '-', ' ', dirname( POLYLANG_BASENAME ) ) ) );
69
 
70
- require __DIR__ . '/include/class-polylang.php';
71
- new Polylang();
 
72
  }
10
  * Plugin Name: Polylang
11
  * Plugin URI: https://polylang.pro
12
  * Description: Adds multilingual capability to WordPress
13
+ * Version: 2.9
14
+ * Requires at least: 5.1
15
  * Requires PHP: 5.6
16
  * Author: WP SYNTEX
17
  * Author URI: https://polylang.pro
53
  }
54
  } else {
55
  // Go on loading the plugin
56
+ define( 'POLYLANG_VERSION', '2.9' );
57
+ define( 'PLL_MIN_WP_VERSION', '5.1' );
58
  define( 'PLL_MIN_PHP_VERSION', '5.6' );
59
 
60
  define( 'POLYLANG_FILE', __FILE__ );
67
 
68
  define( 'POLYLANG', ucwords( str_replace( '-', ' ', dirname( POLYLANG_BASENAME ) ) ) );
69
 
70
+ if ( empty( $_GET['deactivate-polylang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
71
+ new Polylang();
72
+ }
73
  }
readme.txt CHANGED
@@ -2,10 +2,10 @@
2
  Contributors: Chouby, manooweb, raaaahman, marianne38, sebastienserre
3
  Donate link: https://polylang.pro
4
  Tags: multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
- Requires at least: 4.9
6
- Tested up to: 5.5
7
  Requires PHP: 5.6
8
- Stable tag: 2.8.4
9
  License: GPLv3 or later
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
11
 
@@ -43,7 +43,7 @@ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-
43
 
44
  == Installation ==
45
 
46
- 1. Make sure you are using WordPress 4.9 or later and that your server is running PHP 5.6 or later (same requirement as WordPress itself)
47
  1. If you tried other multilingual plugins, deactivate them before activating Polylang, otherwise, you may get unexpected results!
48
  1. Install and activate the plugin as usual from the 'Plugins' menu in WordPress.
49
  1. Go to the languages settings page and create the languages you need
@@ -78,6 +78,27 @@ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-
78
 
79
  == Changelog ==
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  = 2.8.4 (2020-11-03) =
82
 
83
  * Pro: Remove useless bulk translate action for ACF fields groups
2
  Contributors: Chouby, manooweb, raaaahman, marianne38, sebastienserre
3
  Donate link: https://polylang.pro
4
  Tags: multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
+ Requires at least: 5.1
6
+ Tested up to: 5.6
7
  Requires PHP: 5.6
8
+ Stable tag: 2.9
9
  License: GPLv3 or later
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
11
 
43
 
44
  == Installation ==
45
 
46
+ 1. Make sure you are using WordPress 5.1 or later and that your server is running PHP 5.6 or later (same requirement as WordPress itself)
47
  1. If you tried other multilingual plugins, deactivate them before activating Polylang, otherwise, you may get unexpected results!
48
  1. Install and activate the plugin as usual from the 'Plugins' menu in WordPress.
49
  1. Go to the languages settings page and create the languages you need
78
 
79
  == Changelog ==
80
 
81
+ = 2.9 (2020-12-07) =
82
+
83
+ * Add compatibility with WordPress 5.6
84
+ * Pro: Add locale fallback used when the theme or plugins translations are not available
85
+ * Pro: Fix SSO and browser preferred language redirect when using multiple domains
86
+ * Pro: Fix post slugs for German and Danish in the REST API
87
+ * Pro: Fix a fatal error in ACF integration when saving url modifications with multiple domains
88
+ * Pro: Fix a deprecated notice fired by ACF since the version 5.9.2
89
+ * Pro: Fix ACF relationship fields not reloaded when changing the language in the classic editor
90
+ * Update plugin updater to version 1.8
91
+ * Add Lower Sorbian to the list of predefined language
92
+ * Options are now translated on backend when using the admin language filter
93
+ * Keep previous translations when modifying an option value
94
+ * Add navigation markup to the language switcher widget
95
+ * Fix canonical redirect for taxonomy terms
96
+ * Fix a fatal error when deleting a post with a translation group corrupted in the database
97
+ * Fix a fatal error when switching to plain permalinks and using multiple domains
98
+ * Fix a conflict with WP Sweep which could corrupt languages
99
+ * Fix title displayed instead of meta description with Yoast SEO > 14.0
100
+ * Fix PHP Notice: Undefined index: wp_the_query in /frontend/choose-lang-content.php on line 92
101
+
102
  = 2.8.4 (2020-11-03) =
103
 
104
  * Pro: Remove useless bulk translate action for ACF fields groups
settings/languages.php CHANGED
@@ -247,6 +247,13 @@ return array(
247
  'w3c' => 'de-DE',
248
  'facebook' => 'de_DE',
249
  ),
 
 
 
 
 
 
 
250
  'dzo' => array(
251
  'code' => 'dz',
252
  'locale' => 'dzo',
247
  'w3c' => 'de-DE',
248
  'facebook' => 'de_DE',
249
  ),
250
+ 'dsb' => array(
251
+ 'code' => 'dsb',
252
+ 'locale' => 'dsb',
253
+ 'name' => 'Dolnoserbšćina',
254
+ 'dir' => 'ltr',
255
+ 'flag' => 'de',
256
+ ),
257
  'dzo' => array(
258
  'code' => 'dz',
259
  'locale' => 'dzo',
settings/settings-browser.php CHANGED
@@ -85,17 +85,19 @@ class PLL_Settings_Browser extends PLL_Settings_Module {
85
  ?>
86
  <script type='text/javascript'>
87
  //<![CDATA[
88
- ( function( $ ){
89
- $( "input[name='force_lang']" ).change( function() {
90
- var value = $( this ).val();
91
- if ( 3 > value ) {
92
- $( "#pll-module-browser" ).<?php echo $func; // phpcs:ignore WordPress.Security.EscapeOutput ?>.children( "td" ).children( ".row-actions" ).html( '<?php echo $link; // phpcs:ignore WordPress.Security.EscapeOutput ?>' );
93
- }
94
- else {
95
- $( "#pll-module-browser" ).removeClass( "active" ).addClass( "inactive" ).children( "td" ).children( ".row-actions" ).html( '<?php echo $deactivated; // phpcs:ignore WordPress.Security.EscapeOutput ?>' );
96
- }
97
- } );
98
- } )( jQuery );
 
 
99
  // ]]>
100
  </script>
101
  <?php
85
  ?>
86
  <script type='text/javascript'>
87
  //<![CDATA[
88
+ jQuery(
89
+ function( $ ){
90
+ $( "input[name='force_lang']" ).on( 'change', function() {
91
+ var value = $( this ).val();
92
+ if ( 3 > value ) {
93
+ $( "#pll-module-browser" ).<?php echo $func; // phpcs:ignore WordPress.Security.EscapeOutput ?>.children( "td" ).children( ".row-actions" ).html( '<?php echo $link; // phpcs:ignore WordPress.Security.EscapeOutput ?>' );
94
+ }
95
+ else {
96
+ $( "#pll-module-browser" ).removeClass( "active" ).addClass( "inactive" ).children( "td" ).children( ".row-actions" ).html( '<?php echo $deactivated; // phpcs:ignore WordPress.Security.EscapeOutput ?>' );
97
+ }
98
+ } );
99
+ }
100
+ );
101
  // ]]>
102
  </script>
103
  <?php
settings/settings-module.php CHANGED
@@ -216,7 +216,7 @@ class PLL_Settings_Module {
216
  * @param array $options Raw options
217
  * @return array Options
218
  */
219
- protected function update( $options ) {
220
  return array(); // It's responsibility of the child class to decide what is saved
221
  }
222
 
216
  * @param array $options Raw options
217
  * @return array Options
218
  */
219
+ protected function update( $options ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
220
  return array(); // It's responsibility of the child class to decide what is saved
221
  }
222
 
settings/settings.php CHANGED
@@ -305,7 +305,7 @@ class PLL_Settings extends PLL_Admin_Base {
305
  // Handle user input
306
  $action = isset( $_REQUEST['pll_action'] ) ? sanitize_key( $_REQUEST['pll_action'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
307
  if ( 'edit' === $action && ! empty( $_GET['lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
308
- // phpcs:ignore WordPress.Security.NonceVerification, WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable
309
  $edit_lang = $this->model->get_language( (int) $_GET['lang'] );
310
  } else {
311
  $this->handle_actions( $action );
@@ -387,7 +387,7 @@ class PLL_Settings extends PLL_Admin_Base {
387
  *
388
  * @since 1.7.10
389
  * @since 2.3 The languages arrays use associative keys instead of numerical keys
390
- * @see settings/languages.php
391
  *
392
  * @param array $languages
393
  */
305
  // Handle user input
306
  $action = isset( $_REQUEST['pll_action'] ) ? sanitize_key( $_REQUEST['pll_action'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
307
  if ( 'edit' === $action && ! empty( $_GET['lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
308
+ // phpcs:ignore WordPress.Security.NonceVerification, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
309
  $edit_lang = $this->model->get_language( (int) $_GET['lang'] );
310
  } else {
311
  $this->handle_actions( $action );
387
  *
388
  * @since 1.7.10
389
  * @since 2.3 The languages arrays use associative keys instead of numerical keys
390
+ * @see https://github.com/polylang/polylang/blob/2.8.2/settings/languages.php the list of predefined languages
391
  *
392
  * @param array $languages
393
  */
settings/table-settings.php CHANGED
@@ -186,5 +186,5 @@ class PLL_Table_Settings extends WP_List_Table {
186
  *
187
  * @param string $which 'top' or 'bottom'
188
  */
189
- protected function display_tablenav( $which ) {}
190
  }
186
  *
187
  * @param string $which 'top' or 'bottom'
188
  */
189
+ protected function display_tablenav( $which ) {} // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
190
  }
vendor/composer/autoload_classmap.php CHANGED
@@ -36,11 +36,13 @@ return array(
36
  'PLL_Choose_Lang_Content' => $baseDir . '/frontend/choose-lang-content.php',
37
  'PLL_Choose_Lang_Domain' => $baseDir . '/frontend/choose-lang-domain.php',
38
  'PLL_Choose_Lang_Url' => $baseDir . '/frontend/choose-lang-url.php',
 
39
  'PLL_Domain_Mapping' => $baseDir . '/integrations/domain-mapping/domain-mapping.php',
40
  'PLL_Duplicate_Post' => $baseDir . '/integrations/duplicate-post/duplicate-post.php',
41
  'PLL_Featured_Content' => $baseDir . '/integrations/jetpack/featured-content.php',
42
  'PLL_Filters' => $baseDir . '/include/filters.php',
43
  'PLL_Filters_Links' => $baseDir . '/include/filters-links.php',
 
44
  'PLL_Frontend' => $baseDir . '/frontend/frontend.php',
45
  'PLL_Frontend_Auto_Translate' => $baseDir . '/frontend/frontend-auto-translate.php',
46
  'PLL_Frontend_Filters' => $baseDir . '/frontend/frontend-filters.php',
@@ -97,6 +99,7 @@ return array(
97
  'PLL_Table_Languages' => $baseDir . '/settings/table-languages.php',
98
  'PLL_Table_Settings' => $baseDir . '/settings/table-settings.php',
99
  'PLL_Table_String' => $baseDir . '/settings/table-string.php',
 
100
  'PLL_Translated_Object' => $baseDir . '/include/translated-object.php',
101
  'PLL_Translated_Post' => $baseDir . '/include/translated-post.php',
102
  'PLL_Translated_Term' => $baseDir . '/include/translated-term.php',
36
  'PLL_Choose_Lang_Content' => $baseDir . '/frontend/choose-lang-content.php',
37
  'PLL_Choose_Lang_Domain' => $baseDir . '/frontend/choose-lang-domain.php',
38
  'PLL_Choose_Lang_Url' => $baseDir . '/frontend/choose-lang-url.php',
39
+ 'PLL_Cookie' => $baseDir . '/include/cookie.php',
40
  'PLL_Domain_Mapping' => $baseDir . '/integrations/domain-mapping/domain-mapping.php',
41
  'PLL_Duplicate_Post' => $baseDir . '/integrations/duplicate-post/duplicate-post.php',
42
  'PLL_Featured_Content' => $baseDir . '/integrations/jetpack/featured-content.php',
43
  'PLL_Filters' => $baseDir . '/include/filters.php',
44
  'PLL_Filters_Links' => $baseDir . '/include/filters-links.php',
45
+ 'PLL_Filters_Sanitization' => $baseDir . '/include/filters-sanitization.php',
46
  'PLL_Frontend' => $baseDir . '/frontend/frontend.php',
47
  'PLL_Frontend_Auto_Translate' => $baseDir . '/frontend/frontend-auto-translate.php',
48
  'PLL_Frontend_Filters' => $baseDir . '/frontend/frontend-filters.php',
99
  'PLL_Table_Languages' => $baseDir . '/settings/table-languages.php',
100
  'PLL_Table_Settings' => $baseDir . '/settings/table-settings.php',
101
  'PLL_Table_String' => $baseDir . '/settings/table-string.php',
102
+ 'PLL_Translate_Option' => $baseDir . '/include/translate-option.php',
103
  'PLL_Translated_Object' => $baseDir . '/include/translated-object.php',
104
  'PLL_Translated_Post' => $baseDir . '/include/translated-post.php',
105
  'PLL_Translated_Term' => $baseDir . '/include/translated-term.php',
vendor/composer/autoload_static.php CHANGED
@@ -37,11 +37,13 @@ class ComposerStaticInit90bd76dc7672e476bcd63c4a62ea320a
37
  'PLL_Choose_Lang_Content' => __DIR__ . '/../..' . '/frontend/choose-lang-content.php',
38
  'PLL_Choose_Lang_Domain' => __DIR__ . '/../..' . '/frontend/choose-lang-domain.php',
39
  'PLL_Choose_Lang_Url' => __DIR__ . '/../..' . '/frontend/choose-lang-url.php',
 
40
  'PLL_Domain_Mapping' => __DIR__ . '/../..' . '/integrations/domain-mapping/domain-mapping.php',
41
  'PLL_Duplicate_Post' => __DIR__ . '/../..' . '/integrations/duplicate-post/duplicate-post.php',
42
  'PLL_Featured_Content' => __DIR__ . '/../..' . '/integrations/jetpack/featured-content.php',
43
  'PLL_Filters' => __DIR__ . '/../..' . '/include/filters.php',
44
  'PLL_Filters_Links' => __DIR__ . '/../..' . '/include/filters-links.php',
 
45
  'PLL_Frontend' => __DIR__ . '/../..' . '/frontend/frontend.php',
46
  'PLL_Frontend_Auto_Translate' => __DIR__ . '/../..' . '/frontend/frontend-auto-translate.php',
47
  'PLL_Frontend_Filters' => __DIR__ . '/../..' . '/frontend/frontend-filters.php',
@@ -98,6 +100,7 @@ class ComposerStaticInit90bd76dc7672e476bcd63c4a62ea320a
98
  'PLL_Table_Languages' => __DIR__ . '/../..' . '/settings/table-languages.php',
99
  'PLL_Table_Settings' => __DIR__ . '/../..' . '/settings/table-settings.php',
100
  'PLL_Table_String' => __DIR__ . '/../..' . '/settings/table-string.php',
 
101
  'PLL_Translated_Object' => __DIR__ . '/../..' . '/include/translated-object.php',
102
  'PLL_Translated_Post' => __DIR__ . '/../..' . '/include/translated-post.php',
103
  'PLL_Translated_Term' => __DIR__ . '/../..' . '/include/translated-term.php',
37
  'PLL_Choose_Lang_Content' => __DIR__ . '/../..' . '/frontend/choose-lang-content.php',
38
  'PLL_Choose_Lang_Domain' => __DIR__ . '/../..' . '/frontend/choose-lang-domain.php',
39
  'PLL_Choose_Lang_Url' => __DIR__ . '/../..' . '/frontend/choose-lang-url.php',
40
+ 'PLL_Cookie' => __DIR__ . '/../..' . '/include/cookie.php',
41
  'PLL_Domain_Mapping' => __DIR__ . '/../..' . '/integrations/domain-mapping/domain-mapping.php',
42
  'PLL_Duplicate_Post' => __DIR__ . '/../..' . '/integrations/duplicate-post/duplicate-post.php',
43
  'PLL_Featured_Content' => __DIR__ . '/../..' . '/integrations/jetpack/featured-content.php',
44
  'PLL_Filters' => __DIR__ . '/../..' . '/include/filters.php',
45
  'PLL_Filters_Links' => __DIR__ . '/../..' . '/include/filters-links.php',
46
+ 'PLL_Filters_Sanitization' => __DIR__ . '/../..' . '/include/filters-sanitization.php',
47
  'PLL_Frontend' => __DIR__ . '/../..' . '/frontend/frontend.php',
48
  'PLL_Frontend_Auto_Translate' => __DIR__ . '/../..' . '/frontend/frontend-auto-translate.php',
49
  'PLL_Frontend_Filters' => __DIR__ . '/../..' . '/frontend/frontend-filters.php',
100
  'PLL_Table_Languages' => __DIR__ . '/../..' . '/settings/table-languages.php',
101
  'PLL_Table_Settings' => __DIR__ . '/../..' . '/settings/table-settings.php',
102
  'PLL_Table_String' => __DIR__ . '/../..' . '/settings/table-string.php',
103
+ 'PLL_Translate_Option' => __DIR__ . '/../..' . '/include/translate-option.php',
104
  'PLL_Translated_Object' => __DIR__ . '/../..' . '/include/translated-object.php',
105
  'PLL_Translated_Post' => __DIR__ . '/../..' . '/include/translated-post.php',
106
  'PLL_Translated_Term' => __DIR__ . '/../..' . '/include/translated-term.php',