Polylang - Version 3.0

Version Description

(2021-03-08) =

  • Add compatibility with WordPress 5.7
  • Remove upgrades from Polylang older than 1.8
  • Remove deprecated class PLL_Pointer
  • Pro: Hide the license keys
  • Pro: Fix redirect to the home page of a deactivated language
  • Pro: Fix synchronization of post status not working
  • Pro: Fix language switcher block not working in a post retrieved in REST API
  • Pro: Fix PO export of strings with line breaks
  • Pro: Fix file block title customization lost
  • Add a dialog box to ask a confirmation about a language change in classic and block editors
  • Improve browser language detection #591
  • Improve robustness and documentation of code
  • Fix media library after the language has been chnaged in the editor metabox
  • Fix duplicated title attribute on flag link in posts list
  • Fix legacy block editor language metabox compatibility with WordPress 5.6
  • Fix uploaded theme and plugin files in media library
  • Fix site title not translated in email change confirmation email
  • Fix remaining deprecated jQuery notices #741
  • Fix compatibility with GN publisher
  • Fix compatibility with Woodmart theme search form
  • Fix compatibility issue with 3rd party ajax requests since jQuery 3.3 #744
  • Fix CSS conflict with WooCommerce Bookings
  • Fix browser error when displaying an embed and using a cache plugin #757
  • Fix post type archive title and metadesc not translated in Yoast SEO
  • Fix PHP notice in REST API
Download this release

Release Info

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

Code changes from version 2.9.2 to 3.0

Files changed (163) hide show
  1. admin/admin-base.php +97 -35
  2. admin/admin-block-editor.php +17 -7
  3. admin/admin-classic-editor.php +99 -59
  4. admin/admin-filters-columns.php +60 -26
  5. admin/admin-filters-media.php +9 -4
  6. admin/admin-filters-post-base.php +22 -7
  7. admin/admin-filters-post.php +48 -9
  8. admin/admin-filters-term.php +106 -42
  9. admin/admin-filters-widgets-options.php +55 -0
  10. admin/admin-filters.php +10 -97
  11. admin/admin-links.php +42 -42
  12. admin/admin-model.php +103 -89
  13. admin/admin-nav-menu.php +13 -5
  14. admin/admin-notices.php +30 -2
  15. admin/admin-static-pages.php +20 -74
  16. admin/admin-strings.php +21 -8
  17. admin/admin.php +32 -50
  18. css/admin.css +0 -5
  19. css/admin.min.css +0 -1
  20. css/build/admin.css +457 -0
  21. css/build/admin.min.css +1 -0
  22. css/build/dialog.css +82 -0
  23. css/build/dialog.min.css +1 -0
  24. css/build/selectmenu.css +231 -0
  25. css/build/selectmenu.min.css +1 -0
  26. css/build/wizard.css +951 -0
  27. css/build/wizard.min.css +1 -0
  28. css/dialog.css +82 -0
  29. css/selectmenu.css +29 -9
  30. css/selectmenu.min.css +0 -1
  31. frontend/accept-language.php +112 -0
  32. frontend/accept-languages-collection.php +149 -0
  33. frontend/choose-lang-content.php +19 -12
  34. frontend/choose-lang-domain.php +6 -0
  35. frontend/choose-lang-url.php +12 -1
  36. frontend/choose-lang.php +72 -83
  37. frontend/frontend-auto-translate.php +14 -3
  38. frontend/frontend-filters-links.php +70 -45
  39. frontend/frontend-filters-search.php +24 -7
  40. frontend/frontend-filters.php +10 -6
  41. frontend/frontend-links.php +22 -11
  42. frontend/frontend-nav-menu.php +25 -18
  43. frontend/frontend-static-pages.php +36 -21
  44. frontend/frontend.php +71 -33
  45. include/api.php +127 -107
  46. include/base.php +30 -55
  47. include/cache.php +16 -1
  48. include/class-polylang.php +9 -2
  49. include/cookie.php +1 -0
  50. include/crud-posts.php +51 -24
  51. include/crud-terms.php +68 -30
  52. include/filters-links.php +49 -19
  53. include/filters-widgets-options.php +105 -0
  54. include/filters.php +63 -35
  55. include/functions.php +2 -2
  56. include/language.php +262 -112
  57. include/license.php +65 -9
  58. include/links-abstract-domain.php +5 -5
  59. include/links-default.php +13 -8
  60. include/links-directory.php +26 -17
  61. include/links-domain.php +15 -8
  62. include/links-model.php +115 -29
  63. include/links-permalinks.php +43 -9
  64. include/links-subdomain.php +15 -9
  65. include/links.php +22 -4
  66. include/mo.php +18 -11
  67. include/model.php +133 -169
  68. include/nav-menu.php +42 -16
  69. include/olt-manager.php +44 -32
  70. include/pointer.php +0 -116
  71. include/query.php +47 -11
  72. include/rest-request.php +37 -17
  73. include/static-pages.php +39 -5
  74. include/switcher.php +113 -62
  75. include/translate-option.php +24 -19
  76. include/translated-object.php +158 -79
  77. include/translated-post.php +33 -21
  78. include/translated-term.php +31 -22
  79. include/walker-dropdown.php +21 -12
  80. include/walker-list.php +21 -12
  81. include/widget-languages.php +6 -2
  82. install/install-base.php +13 -0
  83. install/install.php +11 -1
  84. install/t15s.php +5 -0
  85. install/upgrade.php +42 -488
  86. integrations/cache/cache-compat.php +8 -3
  87. integrations/custom-field-template/cft.php +2 -2
  88. integrations/wp-importer/wp-import.php +5 -0
  89. integrations/wp-offload-media/as3cf.php +5 -0
  90. integrations/wpseo/wpseo.php +3 -2
  91. js/admin.js +8 -2
  92. js/admin.min.js +0 -1
  93. js/block-editor.js +148 -99
  94. js/block-editor.min.js +0 -1
  95. js/build/admin.js +416 -0
  96. js/build/admin.min.js +1 -0
  97. js/build/block-editor.js +350 -0
  98. js/build/block-editor.min.js +1 -0
  99. js/build/classic-editor.js +436 -0
  100. js/build/classic-editor.min.js +1 -0
  101. js/build/languages-step.js +303 -0
  102. js/build/languages-step.min.js +1 -0
  103. js/build/nav-menu.js +105 -0
  104. js/build/nav-menu.min.js +1 -0
  105. js/build/post.js +178 -0
  106. js/build/post.min.js +1 -0
  107. js/build/term.js +222 -0
  108. js/build/term.min.js +1 -0
  109. js/build/user.js +34 -0
  110. js/build/user.min.js +1 -0
  111. js/build/widgets.js +133 -0
  112. js/build/widgets.min.js +1 -0
  113. js/classic-editor.js +196 -71
  114. js/classic-editor.min.js +0 -1
  115. js/lib/confirmation-modal.js +95 -0
  116. js/nav-menu.js +4 -3
  117. js/nav-menu.min.js +0 -1
  118. js/post.js +2 -1
  119. js/post.min.js +0 -1
  120. js/term.js +2 -1
  121. js/term.min.js +0 -1
  122. js/user.js +1 -1
  123. js/user.min.js +0 -1
  124. js/widgets.min.js +0 -1
  125. modules/lingotek/lingotek.php +7 -0
  126. modules/share-slug/settings-share-slug.php +2 -0
  127. modules/site-health/admin-site-health.php +3 -3
  128. modules/sitemaps/abstract-sitemaps.php +37 -0
  129. modules/sitemaps/load.php +5 -1
  130. modules/sitemaps/multilingual-sitemaps-provider.php +6 -3
  131. modules/sitemaps/sitemaps-domain.php +71 -0
  132. modules/sitemaps/sitemaps.php +14 -65
  133. modules/sync/admin-sync.php +31 -23
  134. modules/sync/settings-sync.php +4 -4
  135. modules/sync/sync-metas.php +57 -20
  136. modules/sync/sync-post-metas.php +11 -6
  137. modules/sync/sync-tax.php +47 -27
  138. modules/sync/sync.php +58 -23
  139. modules/wizard/css/wizard.css +2 -1
  140. modules/wizard/css/wizard.min.css +0 -1
  141. modules/wizard/js/languages-step.min.js +0 -1
  142. modules/wizard/wizard.php +115 -49
  143. modules/wpml/wpml-api.php +13 -2
  144. modules/wpml/wpml-compat.php +22 -3
  145. modules/wpml/wpml-config.php +83 -52
  146. modules/wpml/wpml-legacy-api.php +6 -4
  147. polylang.php +2 -2
  148. readme.txt +30 -2
  149. settings/settings-browser.php +2 -0
  150. settings/settings-cpt.php +4 -4
  151. settings/settings-licenses.php +6 -4
  152. settings/settings-module.php +14 -8
  153. settings/settings-url.php +19 -1
  154. settings/settings.php +40 -21
  155. settings/table-languages.php +23 -21
  156. settings/table-settings.php +15 -11
  157. settings/table-string.php +44 -13
  158. settings/view-tab-lang.php +1 -1
  159. uninstall.php +1 -20
  160. vendor/autoload.php +1 -1
  161. vendor/composer/autoload_classmap.php +6 -1
  162. vendor/composer/autoload_real.php +4 -4
  163. vendor/composer/autoload_static.php +8 -3
admin/admin-base.php CHANGED
@@ -4,20 +4,58 @@
4
  */
5
 
6
  /**
7
- * Base class for both admin
8
  *
9
  * @since 1.8
10
  */
11
- class PLL_Admin_Base extends PLL_Base {
12
- public $filter_lang, $curlang, $pref_lang;
 
 
 
 
 
13
 
14
  /**
15
- * Loads the polylang text domain
16
- * Setups actions needed on all admin pages
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  *
18
  * @since 1.8
19
  *
20
- * @param object $links_model
21
  */
22
  public function __construct( &$links_model ) {
23
  parent::__construct( $links_model );
@@ -64,6 +102,8 @@ class PLL_Admin_Base extends PLL_Base {
64
  * Adds the link to the languages panel in the WordPress admin menu
65
  *
66
  * @since 0.1
 
 
67
  */
68
  public function add_menus() {
69
  global $admin_page_hooks;
@@ -93,7 +133,7 @@ class PLL_Admin_Base extends PLL_Base {
93
  $page = 'lang' === $tab ? 'mlang' : "mlang_$tab";
94
  if ( empty( $parent ) ) {
95
  $parent = $page;
96
- add_menu_page( $title, __( 'Languages', 'polylang' ), 'manage_options', $page, null, 'dashicons-translation' );
97
  $admin_page_hooks[ $page ] = 'languages'; // Hack to avoid the localization of the hook name. See: https://core.trac.wordpress.org/ticket/18857
98
  }
99
 
@@ -105,6 +145,8 @@ class PLL_Admin_Base extends PLL_Base {
105
  * Setup js scripts & css styles ( only on the relevant pages )
106
  *
107
  * @since 0.6
 
 
108
  */
109
  public function admin_enqueue_scripts() {
110
  $screen = get_current_screen();
@@ -131,12 +173,12 @@ class PLL_Admin_Base extends PLL_Base {
131
 
132
  // Classic editor.
133
  if ( ! method_exists( $screen, 'is_block_editor' ) || ! $screen->is_block_editor() ) {
134
- $scripts['classic-editor'] = array( array( 'post', 'media', 'async-upload' ), array( 'jquery', 'wp-ajax-response', 'post' ), 0, 1 );
135
  }
136
 
137
  // Block editor with legacy metabox in WP 5.0+.
138
  if ( method_exists( $screen, 'is_block_editor' ) && $screen->is_block_editor() && ! pll_use_block_editor_plugin() ) {
139
- $scripts['block-editor'] = array( array( 'post' ), array( 'jquery', 'wp-ajax-response', 'wp-api-fetch' ), 0, 1 );
140
  }
141
  }
142
 
@@ -146,11 +188,12 @@ class PLL_Admin_Base extends PLL_Base {
146
 
147
  foreach ( $scripts as $script => $v ) {
148
  if ( in_array( $screen->base, $v[0] ) && ( $v[2] || $this->model->get_languages_list() ) ) {
149
- wp_enqueue_script( 'pll_' . $script, plugins_url( '/js/' . $script . $suffix . '.js', POLYLANG_FILE ), $v[1], POLYLANG_VERSION, $v[3] );
150
  }
151
  }
152
 
153
- wp_enqueue_style( 'polylang_admin', plugins_url( '/css/admin' . $suffix . '.css', POLYLANG_FILE ), array(), POLYLANG_VERSION );
 
154
 
155
  $this->localize_scripts();
156
  }
@@ -159,11 +202,13 @@ class PLL_Admin_Base extends PLL_Base {
159
  * Enqueue scripts to the WP Customizer.
160
  *
161
  * @since 2.4.0
 
 
162
  */
163
  public function customize_controls_enqueue_scripts() {
164
  if ( $this->model->get_languages_list() ) {
165
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
166
- wp_enqueue_script( 'pll_widgets', plugins_url( '/js/widgets' . $suffix . '.js', POLYLANG_FILE ), array( 'jquery' ), POLYLANG_VERSION, true );
167
  $this->localize_scripts();
168
  }
169
  }
@@ -172,6 +217,8 @@ class PLL_Admin_Base extends PLL_Base {
172
  * Localize scripts.
173
  *
174
  * @since 2.4.0
 
 
175
  */
176
  public function localize_scripts() {
177
  if ( wp_script_is( 'pll_widgets', 'enqueued' ) ) {
@@ -198,6 +245,8 @@ class PLL_Admin_Base extends PLL_Base {
198
  * see: https://wordpress.org/support/topic/invalid-url-during-wordpress-new-dashboard-widget-operation
199
  *
200
  * @since 1.4
 
 
201
  */
202
  public function admin_print_footer_scripts() {
203
  global $post_ID, $tag_ID;
@@ -217,27 +266,35 @@ class PLL_Admin_Base extends PLL_Base {
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
  }
@@ -253,6 +310,8 @@ class PLL_Admin_Base extends PLL_Base {
253
  * Sets the admin current language, used to filter the content
254
  *
255
  * @since 2.0
 
 
256
  */
257
  public function set_current_language() {
258
  $this->curlang = $this->filter_lang;
@@ -298,6 +357,8 @@ class PLL_Admin_Base extends PLL_Base {
298
  * Defines the backend language and the admin language filter based on user preferences
299
  *
300
  * @since 1.2.3
 
 
301
  */
302
  public function init_user() {
303
  // Language for admin language filter: may be empty
@@ -313,12 +374,12 @@ class PLL_Admin_Base extends PLL_Base {
313
  $this->pref_lang = empty( $this->filter_lang ) ? $this->model->get_language( $this->options['default_lang'] ) : $this->filter_lang;
314
 
315
  /**
316
- * Filter the preferred language on admin side
317
- * The preferred language is used for example to determine the language of a new post
318
  *
319
  * @since 1.2.3
320
  *
321
- * @param object $pref_lang preferred language
322
  */
323
  $this->pref_lang = apply_filters( 'pll_admin_preferred_language', $this->pref_lang );
324
 
@@ -348,11 +409,12 @@ class PLL_Admin_Base extends PLL_Base {
348
  }
349
 
350
  /**
351
- * Adds the languages list in admin bar for the admin languages filter
352
  *
353
  * @since 0.9
354
  *
355
- * @param object $wp_admin_bar
 
356
  */
357
  public function admin_bar_menu( $wp_admin_bar ) {
358
  $all_item = (object) array(
4
  */
5
 
6
  /**
7
+ * Setup features available on all admin pages.
8
  *
9
  * @since 1.8
10
  */
11
+ abstract class PLL_Admin_Base extends PLL_Base {
12
+ /**
13
+ * Current language (used to filter the content).
14
+ *
15
+ * @var PLL_Language
16
+ */
17
+ public $curlang;
18
 
19
  /**
20
+ * Language selected in the admin language filter.
21
+ *
22
+ * @var PLL_Language
23
+ */
24
+ public $filter_lang;
25
+
26
+ /**
27
+ * Preferred language to assign to new contents.
28
+ *
29
+ * @var PLL_Language
30
+ */
31
+ public $pref_lang;
32
+
33
+ /**
34
+ * @var PLL_Filters_Links
35
+ */
36
+ public $filters_links;
37
+
38
+ /**
39
+ * @var PLL_Admin_Links
40
+ */
41
+ public $links;
42
+
43
+ /**
44
+ * @var PLL_Admin_Notices
45
+ */
46
+ public $notices;
47
+
48
+ /**
49
+ * @var PLL_Admin_Static_Pages
50
+ */
51
+ public $static_pages;
52
+
53
+ /**
54
+ * Setups actions needed on all admin pages.
55
  *
56
  * @since 1.8
57
  *
58
+ * @param PLL_Links_Model $links_model Reference to the links model.
59
  */
60
  public function __construct( &$links_model ) {
61
  parent::__construct( $links_model );
102
  * Adds the link to the languages panel in the WordPress admin menu
103
  *
104
  * @since 0.1
105
+ *
106
+ * @return void
107
  */
108
  public function add_menus() {
109
  global $admin_page_hooks;
133
  $page = 'lang' === $tab ? 'mlang' : "mlang_$tab";
134
  if ( empty( $parent ) ) {
135
  $parent = $page;
136
+ add_menu_page( $title, __( 'Languages', 'polylang' ), 'manage_options', $page, '__return_null', 'dashicons-translation' );
137
  $admin_page_hooks[ $page ] = 'languages'; // Hack to avoid the localization of the hook name. See: https://core.trac.wordpress.org/ticket/18857
138
  }
139
 
145
  * Setup js scripts & css styles ( only on the relevant pages )
146
  *
147
  * @since 0.6
148
+ *
149
+ * @return void
150
  */
151
  public function admin_enqueue_scripts() {
152
  $screen = get_current_screen();
173
 
174
  // Classic editor.
175
  if ( ! method_exists( $screen, 'is_block_editor' ) || ! $screen->is_block_editor() ) {
176
+ $scripts['classic-editor'] = array( array( 'post', 'media', 'async-upload' ), array( 'jquery', 'wp-ajax-response', 'post', 'jquery-ui-dialog' ), 0, 1 );
177
  }
178
 
179
  // Block editor with legacy metabox in WP 5.0+.
180
  if ( method_exists( $screen, 'is_block_editor' ) && $screen->is_block_editor() && ! pll_use_block_editor_plugin() ) {
181
+ $scripts['block-editor'] = array( array( 'post' ), array( 'jquery', 'wp-ajax-response', 'wp-api-fetch', 'jquery-ui-dialog' ), 0, 1 );
182
  }
183
  }
184
 
188
 
189
  foreach ( $scripts as $script => $v ) {
190
  if ( in_array( $screen->base, $v[0] ) && ( $v[2] || $this->model->get_languages_list() ) ) {
191
+ wp_enqueue_script( 'pll_' . $script, plugins_url( '/js/build/' . $script . $suffix . '.js', POLYLANG_BASENAME ), $v[1], POLYLANG_VERSION, $v[3] );
192
  }
193
  }
194
 
195
+ wp_register_style( 'polylang_admin', plugins_url( '/css/build/admin' . $suffix . '.css', POLYLANG_BASENAME ), array( 'wp-jquery-ui-dialog' ), POLYLANG_VERSION );
196
+ wp_enqueue_style( 'polylang_dialog', plugins_url( '/css/build/dialog' . $suffix . '.css', POLYLANG_BASENAME ), array( 'polylang_admin' ), POLYLANG_VERSION );
197
 
198
  $this->localize_scripts();
199
  }
202
  * Enqueue scripts to the WP Customizer.
203
  *
204
  * @since 2.4.0
205
+ *
206
+ * @return void
207
  */
208
  public function customize_controls_enqueue_scripts() {
209
  if ( $this->model->get_languages_list() ) {
210
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
211
+ wp_enqueue_script( 'pll_widgets', plugins_url( '/js/build/widgets' . $suffix . '.js', POLYLANG_BASENAME ), array( 'jquery' ), POLYLANG_VERSION, true );
212
  $this->localize_scripts();
213
  }
214
  }
217
  * Localize scripts.
218
  *
219
  * @since 2.4.0
220
+ *
221
+ * @return void
222
  */
223
  public function localize_scripts() {
224
  if ( wp_script_is( 'pll_widgets', 'enqueued' ) ) {
245
  * see: https://wordpress.org/support/topic/invalid-url-during-wordpress-new-dashboard-widget-operation
246
  *
247
  * @since 1.4
248
+ *
249
+ * @return void
250
  */
251
  public function admin_print_footer_scripts() {
252
  global $post_ID, $tag_ID;
266
  <script type="text/javascript">
267
  if (typeof jQuery != 'undefined') {
268
  jQuery(
269
+ function( $ ){
270
+ $.ajaxPrefilter( function ( options, originalOptions, jqXHR ) {
271
  if ( -1 != options.url.indexOf( ajaxurl ) || -1 != ajaxurl.indexOf( options.url ) ) {
272
+
273
+ function addStringParameters() {
274
+ if ( 'undefined' === typeof options.data || '' === options.data ) {
275
+ options.data = '<?php echo $str; // phpcs:ignore WordPress.Security.EscapeOutput ?>';
276
+ } else {
277
+ options.data = options.data + '&<?php echo $str; // phpcs:ignore WordPress.Security.EscapeOutput ?>';
278
+ }
279
+ }
280
+
281
+ /*
282
+ * options.processData set to true is the default jQuery process where the data is converted in a query string by using jQuery.param().
283
+ * This step is done before applying filters. Thus here the options.data is already a string in this case.
284
+ * @See https://github.com/jquery/jquery/blob/3.5.1/src/ajax.js#L563-L569 jQuery ajax function.
285
+ */
286
+ if ( options.processData ) {
287
+ addStringParameters();
288
  } else {
289
+ /*
290
+ * If options.processData is set to false data could be undefined or pass as a string.
291
+ * So data as to be processed as if options.processData is set to true.
292
+ */
293
+ if ( 'undefined' === typeof options.data || 'string' === typeof options.data ) {
294
+ addStringParameters();
 
 
 
 
 
 
 
295
  } else {
296
+ // Otherwise options.data is probably an object.
297
+ options.data = Object.assign( options.data, <?php echo $arr; // phpcs:ignore WordPress.Security.EscapeOutput ?> );
298
  }
299
  }
300
  }
310
  * Sets the admin current language, used to filter the content
311
  *
312
  * @since 2.0
313
+ *
314
+ * @return void
315
  */
316
  public function set_current_language() {
317
  $this->curlang = $this->filter_lang;
357
  * Defines the backend language and the admin language filter based on user preferences
358
  *
359
  * @since 1.2.3
360
+ *
361
+ * @return void
362
  */
363
  public function init_user() {
364
  // Language for admin language filter: may be empty
374
  $this->pref_lang = empty( $this->filter_lang ) ? $this->model->get_language( $this->options['default_lang'] ) : $this->filter_lang;
375
 
376
  /**
377
+ * Filters the preferred language on admin side.
378
+ * The preferred language is used for example to determine the language of a new post.
379
  *
380
  * @since 1.2.3
381
  *
382
+ * @param PLL_Language $pref_lang Preferred language.
383
  */
384
  $this->pref_lang = apply_filters( 'pll_admin_preferred_language', $this->pref_lang );
385
 
409
  }
410
 
411
  /**
412
+ * Adds the languages list in admin bar for the admin languages filter.
413
  *
414
  * @since 0.9
415
  *
416
+ * @param WP_Admin_Bar $wp_admin_bar WP_Admin_Bar global object.
417
+ * @return void
418
  */
419
  public function admin_bar_menu( $wp_admin_bar ) {
420
  $all_item = (object) array(
admin/admin-block-editor.php CHANGED
@@ -9,7 +9,17 @@
9
  * @since 2.5
10
  */
11
  class PLL_Admin_Block_Editor {
12
- public $model;
 
 
 
 
 
 
 
 
 
 
13
 
14
  /**
15
  * Constructor: setups filters and actions
@@ -26,15 +36,15 @@ class PLL_Admin_Block_Editor {
26
  }
27
 
28
  /**
29
- * Filter the preload REST requests by the current language of the post
30
- * Necessary otherwise subsequent REST requests all filtered by the language
31
- * would not hit the preloaded requests
32
  *
33
  * @since 2.5
34
  *
35
- * @param array $preload_paths Array of paths to preload.
36
- * @param object $post The post resource data.
37
- * @return array
38
  */
39
  public function preload_paths( $preload_paths, $post ) {
40
  if ( $this->model->is_translated_post_type( $post->post_type ) ) {
9
  * @since 2.5
10
  */
11
  class PLL_Admin_Block_Editor {
12
+ /**
13
+ * @var PLL_Model
14
+ */
15
+ protected $model;
16
+
17
+ /**
18
+ * Preferred language to assign to a new post.
19
+ *
20
+ * @var PLL_Language
21
+ */
22
+ protected $pref_lang;
23
 
24
  /**
25
  * Constructor: setups filters and actions
36
  }
37
 
38
  /**
39
+ * Filters the preload REST requests by the current language of the post.
40
+ * Necessary otherwise subsequent REST requests, all filtered by the language,
41
+ * would not hit the preloaded requests.
42
  *
43
  * @since 2.5
44
  *
45
+ * @param (string|string[])[] $preload_paths Array of paths to preload.
46
+ * @param WP_Post $post The post resource data.
47
+ * @return (string|string[])[]
48
  */
49
  public function preload_paths( $preload_paths, $post ) {
50
  if ( $this->model->is_translated_post_type( $post->post_type ) ) {
admin/admin-classic-editor.php CHANGED
@@ -9,7 +9,29 @@
9
  * @since 2.4
10
  */
11
  class PLL_Admin_Classic_Editor {
12
- public $model, $links, $curlang, $pref_lang;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  /**
15
  * Constructor: setups filters and actions
@@ -44,6 +66,7 @@ class PLL_Admin_Classic_Editor {
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 ) ) {
@@ -65,6 +88,8 @@ class PLL_Admin_Classic_Editor {
65
  * Displays the Languages metabox in the 'Edit Post' and 'Edit Page' panels
66
  *
67
  * @since 0.1
 
 
68
  */
69
  public function post_language() {
70
  global $post_ID;
@@ -127,6 +152,8 @@ class PLL_Admin_Classic_Editor {
127
  * Ajax response for changing the language in the post metabox
128
  *
129
  * @since 0.2
 
 
130
  */
131
  public function post_lang_choice() {
132
  check_ajax_referer( 'pll_language', '_pll_nonce' );
@@ -140,11 +167,16 @@ class PLL_Admin_Classic_Editor {
140
  $lang = $this->model->get_language( sanitize_key( $_POST['lang'] ) );
141
  $post_type = sanitize_key( $_POST['post_type'] );
142
 
143
- if ( ! post_type_exists( $post_type ) ) {
144
  wp_die( 0 );
145
  }
146
 
147
  $post_type_object = get_post_type_object( $post_type );
 
 
 
 
 
148
  if ( ! current_user_can( $post_type_object->cap->edit_post, $post_ID ) ) {
149
  wp_die( -1 );
150
  }
@@ -157,12 +189,10 @@ class PLL_Admin_Classic_Editor {
157
  $this->model->post->save_translations( $post_ID, $translations );
158
 
159
  ob_start();
160
- if ( $lang ) {
161
- if ( 'attachment' === $post_type ) {
162
- include __DIR__ . '/view-translations-media.php';
163
- } else {
164
- include __DIR__ . '/view-translations-post.php';
165
- }
166
  }
167
  $x = new WP_Ajax_Response( array( 'what' => 'translations', 'data' => ob_get_contents() ) );
168
  ob_end_clean();
@@ -174,30 +204,32 @@ class PLL_Admin_Classic_Editor {
174
  foreach ( array_map( 'sanitize_key', $_POST['taxonomies'] ) as $taxname ) {
175
  $taxonomy = get_taxonomy( $taxname );
176
 
177
- ob_start();
178
- $popular_ids = wp_popular_terms_checklist( $taxonomy->name );
179
- $supplemental['populars'] = ob_get_contents();
180
- ob_end_clean();
181
-
182
- ob_start();
183
- // Use $post_ID to remember checked terms in case we come back to the original language
184
- wp_terms_checklist( $post_ID, array( 'taxonomy' => $taxonomy->name, 'popular_cats' => $popular_ids ) );
185
- $supplemental['all'] = ob_get_contents();
186
- ob_end_clean();
187
-
188
- $supplemental['dropdown'] = wp_dropdown_categories(
189
- array(
190
- 'taxonomy' => $taxonomy->name,
191
- 'hide_empty' => 0,
192
- 'name' => 'new' . $taxonomy->name . '_parent',
193
- 'orderby' => 'name',
194
- 'hierarchical' => 1,
195
- 'show_option_none' => '&mdash; ' . $taxonomy->labels->parent_item . ' &mdash;',
196
- 'echo' => 0,
197
- )
198
- );
199
-
200
- $x->Add( array( 'what' => 'taxonomy', 'data' => $taxonomy->name, 'supplemental' => $supplemental ) );
 
 
201
  }
202
  }
203
 
@@ -205,21 +237,23 @@ class PLL_Admin_Classic_Editor {
205
  if ( in_array( $post_type, get_post_types( array( 'hierarchical' => true ) ) ) ) {
206
  $post = get_post( $post_ID );
207
 
208
- // Args and filter from 'page_attributes_meta_box' in wp-admin/includes/meta-boxes.php of WP 4.2.1
209
- $dropdown_args = array(
210
- 'post_type' => $post->post_type,
211
- 'exclude_tree' => $post->ID,
212
- 'selected' => $post->post_parent,
213
- 'name' => 'parent_id',
214
- 'show_option_none' => __( '(no parent)', 'polylang' ),
215
- 'sort_column' => 'menu_order, post_title',
216
- 'echo' => 0,
217
- );
 
218
 
219
- /** This filter is documented in wp-admin/includes/meta-boxes.php */
220
- $dropdown_args = apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post ); // Since WP 3.3
221
 
222
- $x->Add( array( 'what' => 'pages', 'data' => wp_dropdown_pages( $dropdown_args ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput
 
223
  }
224
 
225
  // Flag
@@ -235,6 +269,8 @@ class PLL_Admin_Classic_Editor {
235
  * Ajax response for input in translation autocomplete input box
236
  *
237
  * @since 1.5
 
 
238
  */
239
  public function ajax_posts_not_translated() {
240
  check_ajax_referer( 'pll_language', '_pll_nonce' );
@@ -270,27 +306,30 @@ class PLL_Admin_Classic_Editor {
270
  // Add current translation in list
271
  if ( $post_id = $this->model->post->get_translation( (int) $_GET['pll_post_id'], $translation_language ) ) {
272
  $post = get_post( $post_id );
273
- array_unshift(
274
- $return,
275
- array(
276
- 'id' => $post_id,
277
- 'value' => $post->post_title,
278
- 'link' => $this->links->edit_post_translation_link( $post_id ),
279
- )
280
- );
 
 
 
281
  }
282
 
283
  wp_die( wp_json_encode( $return ) );
284
  }
285
 
286
  /**
287
- * Filters the pages by language in the parent dropdown list in the page attributes metabox
288
  *
289
  * @since 0.6
290
  *
291
- * @param array $dropdown_args Arguments passed to wp_dropdown_pages
292
- * @param object $post
293
- * @return array Modified arguments
294
  */
295
  public function page_attributes_dropdown_pages_args( $dropdown_args, $post ) {
296
  $dropdown_args['lang'] = isset( $_POST['lang'] ) ? $this->model->get_language( sanitize_key( $_POST['lang'] ) ) : $this->model->post->get_language( $post->ID ); // phpcs:ignore WordPress.Security.NonceVerification
@@ -302,11 +341,12 @@ class PLL_Admin_Classic_Editor {
302
  }
303
 
304
  /**
305
- * Displays a notice if the user has not sufficient rights to overwrite synchronized taxonomies and metas
306
  *
307
  * @since 2.6
308
  *
309
- * @param object $post Post currently being edited
 
310
  */
311
  public function edit_form_top( $post ) {
312
  if ( ! $this->model->post->current_user_can_synchronize( $post->ID ) ) {
9
  * @since 2.4
10
  */
11
  class PLL_Admin_Classic_Editor {
12
+ /**
13
+ * @var PLL_Model
14
+ */
15
+ public $model;
16
+
17
+ /**
18
+ * @var PLL_Admin_Links
19
+ */
20
+ public $links;
21
+
22
+ /**
23
+ * Current language (used to filter the content).
24
+ *
25
+ * @var PLL_Language
26
+ */
27
+ public $curlang;
28
+
29
+ /**
30
+ * Preferred language to assign to new contents.
31
+ *
32
+ * @var PLL_Language
33
+ */
34
+ public $pref_lang;
35
 
36
  /**
37
  * Constructor: setups filters and actions
66
  * @since 0.1
67
  *
68
  * @param string $post_type Current post type
69
+ * @return void
70
  */
71
  public function add_meta_boxes( $post_type ) {
72
  if ( $this->model->is_translated_post_type( $post_type ) ) {
88
  * Displays the Languages metabox in the 'Edit Post' and 'Edit Page' panels
89
  *
90
  * @since 0.1
91
+ *
92
+ * @return void
93
  */
94
  public function post_language() {
95
  global $post_ID;
152
  * Ajax response for changing the language in the post metabox
153
  *
154
  * @since 0.2
155
+ *
156
+ * @return void
157
  */
158
  public function post_lang_choice() {
159
  check_ajax_referer( 'pll_language', '_pll_nonce' );
167
  $lang = $this->model->get_language( sanitize_key( $_POST['lang'] ) );
168
  $post_type = sanitize_key( $_POST['post_type'] );
169
 
170
+ if ( empty( $lang ) || ! post_type_exists( $post_type ) ) {
171
  wp_die( 0 );
172
  }
173
 
174
  $post_type_object = get_post_type_object( $post_type );
175
+
176
+ if ( empty( $post_type_object ) ) {
177
+ wp_die( 0 );
178
+ }
179
+
180
  if ( ! current_user_can( $post_type_object->cap->edit_post, $post_ID ) ) {
181
  wp_die( -1 );
182
  }
189
  $this->model->post->save_translations( $post_ID, $translations );
190
 
191
  ob_start();
192
+ if ( 'attachment' === $post_type ) {
193
+ include __DIR__ . '/view-translations-media.php';
194
+ } else {
195
+ include __DIR__ . '/view-translations-post.php';
 
 
196
  }
197
  $x = new WP_Ajax_Response( array( 'what' => 'translations', 'data' => ob_get_contents() ) );
198
  ob_end_clean();
204
  foreach ( array_map( 'sanitize_key', $_POST['taxonomies'] ) as $taxname ) {
205
  $taxonomy = get_taxonomy( $taxname );
206
 
207
+ if ( ! empty( $taxonomy ) ) {
208
+ ob_start();
209
+ $popular_ids = wp_popular_terms_checklist( $taxonomy->name );
210
+ $supplemental['populars'] = ob_get_contents();
211
+ ob_end_clean();
212
+
213
+ ob_start();
214
+ // Use $post_ID to remember checked terms in case we come back to the original language
215
+ wp_terms_checklist( $post_ID, array( 'taxonomy' => $taxonomy->name, 'popular_cats' => $popular_ids ) );
216
+ $supplemental['all'] = ob_get_contents();
217
+ ob_end_clean();
218
+
219
+ $supplemental['dropdown'] = wp_dropdown_categories(
220
+ array(
221
+ 'taxonomy' => $taxonomy->name,
222
+ 'hide_empty' => 0,
223
+ 'name' => 'new' . $taxonomy->name . '_parent',
224
+ 'orderby' => 'name',
225
+ 'hierarchical' => 1,
226
+ 'show_option_none' => '&mdash; ' . $taxonomy->labels->parent_item . ' &mdash;',
227
+ 'echo' => 0,
228
+ )
229
+ );
230
+
231
+ $x->Add( array( 'what' => 'taxonomy', 'data' => $taxonomy->name, 'supplemental' => $supplemental ) );
232
+ }
233
  }
234
  }
235
 
237
  if ( in_array( $post_type, get_post_types( array( 'hierarchical' => true ) ) ) ) {
238
  $post = get_post( $post_ID );
239
 
240
+ if ( ! empty( $post ) ) {
241
+ // Args and filter from 'page_attributes_meta_box' in wp-admin/includes/meta-boxes.php of WP 4.2.1
242
+ $dropdown_args = array(
243
+ 'post_type' => $post->post_type,
244
+ 'exclude_tree' => $post->ID,
245
+ 'selected' => $post->post_parent,
246
+ 'name' => 'parent_id',
247
+ 'show_option_none' => __( '(no parent)', 'polylang' ),
248
+ 'sort_column' => 'menu_order, post_title',
249
+ 'echo' => 0,
250
+ );
251
 
252
+ /** This filter is documented in wp-admin/includes/meta-boxes.php */
253
+ $dropdown_args = apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post ); // Since WP 3.3
254
 
255
+ $x->Add( array( 'what' => 'pages', 'data' => wp_dropdown_pages( $dropdown_args ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput
256
+ }
257
  }
258
 
259
  // Flag
269
  * Ajax response for input in translation autocomplete input box
270
  *
271
  * @since 1.5
272
+ *
273
+ * @return void
274
  */
275
  public function ajax_posts_not_translated() {
276
  check_ajax_referer( 'pll_language', '_pll_nonce' );
306
  // Add current translation in list
307
  if ( $post_id = $this->model->post->get_translation( (int) $_GET['pll_post_id'], $translation_language ) ) {
308
  $post = get_post( $post_id );
309
+
310
+ if ( ! empty( $post ) ) {
311
+ array_unshift(
312
+ $return,
313
+ array(
314
+ 'id' => $post_id,
315
+ 'value' => $post->post_title,
316
+ 'link' => $this->links->edit_post_translation_link( $post_id ),
317
+ )
318
+ );
319
+ }
320
  }
321
 
322
  wp_die( wp_json_encode( $return ) );
323
  }
324
 
325
  /**
326
+ * Filters the pages by language in the parent dropdown list in the page attributes metabox.
327
  *
328
  * @since 0.6
329
  *
330
+ * @param array $dropdown_args Arguments passed to wp_dropdown_pages().
331
+ * @param WP_Post $post The page being edited.
332
+ * @return array Modified arguments.
333
  */
334
  public function page_attributes_dropdown_pages_args( $dropdown_args, $post ) {
335
  $dropdown_args['lang'] = isset( $_POST['lang'] ) ? $this->model->get_language( sanitize_key( $_POST['lang'] ) ) : $this->model->post->get_language( $post->ID ); // phpcs:ignore WordPress.Security.NonceVerification
341
  }
342
 
343
  /**
344
+ * Displays a notice if the user has not sufficient rights to overwrite synchronized taxonomies and metas.
345
  *
346
  * @since 2.6
347
  *
348
+ * @param WP_Post $post the post currently being edited.
349
+ * @return void
350
  */
351
  public function edit_form_top( $post ) {
352
  if ( ! $this->model->post->current_user_can_synchronize( $post->ID ) ) {
admin/admin-filters-columns.php CHANGED
@@ -10,7 +10,22 @@
10
  * @since 1.2
11
  */
12
  class PLL_Admin_Filters_Columns {
13
- public $links, $model, $filter_lang;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  /**
16
  * Constructor: setups filters and actions
@@ -51,13 +66,13 @@ class PLL_Admin_Filters_Columns {
51
  }
52
 
53
  /**
54
- * Adds languages and translations columns in posts, pages, media, categories and tags tables
55
  *
56
  * @since 0.8.2
57
  *
58
- * @param array $columns List of table columns
59
- * @param string $before The column before which we want to add our languages
60
- * @return array modified list of columns
61
  */
62
  protected function add_column( $columns, $before ) {
63
  if ( $n = array_search( $before, array_keys( $columns ) ) ) {
@@ -90,12 +105,12 @@ class PLL_Admin_Filters_Columns {
90
  }
91
 
92
  /**
93
- * Hide the column for the filtered language
94
  *
95
  * @since 2.7
96
  *
97
- * @param array $hidden Array of hidden columns
98
- * @return array
99
  */
100
  public function hidden_columns( $hidden ) {
101
  if ( ! empty( $this->filter_lang ) ) {
@@ -105,12 +120,12 @@ class PLL_Admin_Filters_Columns {
105
  }
106
 
107
  /**
108
- * Adds the language and translations columns ( before the comments column ) in the posts, pages and media library tables
109
  *
110
  * @since 0.1
111
  *
112
- * @param array $columns list of posts table columns
113
- * @return array modified list of columns
114
  */
115
  public function add_post_column( $columns ) {
116
  return $this->add_column( $columns, 'comments' );
@@ -124,6 +139,7 @@ class PLL_Admin_Filters_Columns {
124
  *
125
  * @param string $column Column name
126
  * @param int $post_id
 
127
  */
128
  public function post_column( $column, $post_id ) {
129
  $inline = wp_doing_ajax() && isset( $_REQUEST['action'], $_POST['inline_lang_choice'] ) && 'inline-save' === $_REQUEST['action']; // phpcs:ignore WordPress.Security.NonceVerification
@@ -135,6 +151,10 @@ class PLL_Admin_Filters_Columns {
135
 
136
  $language = $this->model->get_language( substr( $column, 9 ) );
137
 
 
 
 
 
138
  // Hidden field containing the post language for quick edit
139
  if ( $column == $this->get_first_language_column() ) {
140
  printf( '<div class="hidden" id="lang_%d">%s</div>', intval( $post_id ), esc_html( $lang->slug ) );
@@ -156,14 +176,19 @@ class PLL_Admin_Filters_Columns {
156
  /* translators: accessibility text, %s is a native language name */
157
  $s = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
158
  }
159
- printf(
160
- '<a class="%1$s" title="%2$s" href="%3$s"><span class="screen-reader-text">%4$s</span>%5$s</a>',
161
- esc_attr( $class ),
162
- esc_attr( get_post( $id )->post_title ),
163
- esc_url( $link ),
164
- esc_html( $s ),
165
- $flag // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
166
- );
 
 
 
 
 
167
  } elseif ( $id === $post_id ) {
168
  printf(
169
  '<span class="pll_column_flag" style=""><span class="screen-reader-text">%1$s</span>%2$s</span>',
@@ -214,12 +239,12 @@ class PLL_Admin_Filters_Columns {
214
  }
215
 
216
  /**
217
- * Adds the language column ( before the posts column ) in the 'Categories' or 'Post Tags' table
218
  *
219
  * @since 0.1
220
  *
221
- * @param array $columns list of terms table columns
222
- * @return array modified list of columns
223
  */
224
  public function add_term_column( $columns ) {
225
  return $this->add_column( $columns, 'posts' );
@@ -233,6 +258,7 @@ class PLL_Admin_Filters_Columns {
233
  * @param string $out
234
  * @param string $column Column name
235
  * @param int $term_id
 
236
  */
237
  public function term_column( $out, $column, $term_id ) {
238
  $inline = wp_doing_ajax() && isset( $_REQUEST['action'], $_POST['inline_lang_choice'] ) && 'inline-save-tax' === $_REQUEST['action']; // phpcs:ignore WordPress.Security.NonceVerification
@@ -256,13 +282,17 @@ class PLL_Admin_Filters_Columns {
256
  $taxonomy = $GLOBALS['taxonomy'];
257
  }
258
 
259
- if ( ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
260
  return $out;
261
  }
262
 
263
  $term_id = (int) $term_id;
264
  $language = $this->model->get_language( substr( $column, 9 ) );
265
 
 
 
 
 
266
  if ( $column == $this->get_first_language_column() ) {
267
  $out = sprintf( '<div class="hidden" id="lang_%d">%s</div>', intval( $term_id ), esc_html( $lang->slug ) );
268
 
@@ -316,6 +346,8 @@ class PLL_Admin_Filters_Columns {
316
  * Update rows of translated posts when the language is modified in quick edit
317
  *
318
  * @since 1.7
 
 
319
  */
320
  public function ajax_update_post_rows() {
321
  check_ajax_referer( 'inlineeditnonce', '_pll_nonce' );
@@ -330,7 +362,7 @@ class PLL_Admin_Filters_Columns {
330
  wp_die( 0 );
331
  }
332
 
333
- global $wp_list_table;
334
  $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => sanitize_key( $_POST['screen'] ) ) );
335
 
336
  $x = new WP_Ajax_Response();
@@ -358,6 +390,8 @@ class PLL_Admin_Filters_Columns {
358
  * Update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit
359
  *
360
  * @since 1.7
 
 
361
  */
362
  public function ajax_update_term_rows() {
363
  check_ajax_referer( 'pll_language', '_pll_nonce' );
@@ -372,7 +406,7 @@ class PLL_Admin_Filters_Columns {
372
  wp_die( 0 );
373
  }
374
 
375
- global $wp_list_table;
376
  $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => sanitize_key( $_POST['screen'] ) ) );
377
 
378
  $x = new WP_Ajax_Response();
@@ -402,7 +436,7 @@ class PLL_Admin_Filters_Columns {
402
  *
403
  * @since 2.8
404
  *
405
- * @param object $language PLL_Language object.
406
  * @return string
407
  */
408
  protected function get_flag_html( $language ) {
10
  * @since 1.2
11
  */
12
  class PLL_Admin_Filters_Columns {
13
+ /**
14
+ * @var PLL_Model
15
+ */
16
+ public $model;
17
+
18
+ /**
19
+ * @var PLL_Admin_Links
20
+ */
21
+ public $links;
22
+
23
+ /**
24
+ * Language selected in the admin language filter.
25
+ *
26
+ * @var PLL_Language
27
+ */
28
+ public $filter_lang;
29
 
30
  /**
31
  * Constructor: setups filters and actions
66
  }
67
 
68
  /**
69
+ * Adds languages and translations columns in posts, pages, media, categories and tags tables.
70
  *
71
  * @since 0.8.2
72
  *
73
+ * @param string[] $columns List of table columns.
74
+ * @param string $before The column before which we want to add our languages.
75
+ * @return string[] Modified list of columns.
76
  */
77
  protected function add_column( $columns, $before ) {
78
  if ( $n = array_search( $before, array_keys( $columns ) ) ) {
105
  }
106
 
107
  /**
108
+ * Hides the column for the filtered language.
109
  *
110
  * @since 2.7
111
  *
112
+ * @param string[] $hidden Array of hidden columns.
113
+ * @return string[]
114
  */
115
  public function hidden_columns( $hidden ) {
116
  if ( ! empty( $this->filter_lang ) ) {
120
  }
121
 
122
  /**
123
+ * Adds the language and translations columns ( before the comments column ) in the posts, pages and media library tables.
124
  *
125
  * @since 0.1
126
  *
127
+ * @param string[] $columns List of posts table columns.
128
+ * @return string[] Modified list of columns.
129
  */
130
  public function add_post_column( $columns ) {
131
  return $this->add_column( $columns, 'comments' );
139
  *
140
  * @param string $column Column name
141
  * @param int $post_id
142
+ * @return void
143
  */
144
  public function post_column( $column, $post_id ) {
145
  $inline = wp_doing_ajax() && isset( $_REQUEST['action'], $_POST['inline_lang_choice'] ) && 'inline-save' === $_REQUEST['action']; // phpcs:ignore WordPress.Security.NonceVerification
151
 
152
  $language = $this->model->get_language( substr( $column, 9 ) );
153
 
154
+ if ( empty( $language ) ) {
155
+ return;
156
+ }
157
+
158
  // Hidden field containing the post language for quick edit
159
  if ( $column == $this->get_first_language_column() ) {
160
  printf( '<div class="hidden" id="lang_%d">%s</div>', intval( $post_id ), esc_html( $lang->slug ) );
176
  /* translators: accessibility text, %s is a native language name */
177
  $s = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
178
  }
179
+
180
+ $post = get_post( $id );
181
+
182
+ if ( ! empty( $post ) ) {
183
+ printf(
184
+ '<a class="%1$s" title="%2$s" href="%3$s"><span class="screen-reader-text">%4$s</span>%5$s</a>',
185
+ esc_attr( $class ),
186
+ esc_attr( $post->post_title ),
187
+ esc_url( $link ),
188
+ esc_html( $s ),
189
+ $flag // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
190
+ );
191
+ }
192
  } elseif ( $id === $post_id ) {
193
  printf(
194
  '<span class="pll_column_flag" style=""><span class="screen-reader-text">%1$s</span>%2$s</span>',
239
  }
240
 
241
  /**
242
+ * Adds the language column ( before the posts column ) in the 'Categories' or 'Post Tags' table.
243
  *
244
  * @since 0.1
245
  *
246
+ * @param string[] $columns List of terms table columns.
247
+ * @return string[] modified List of columns.
248
  */
249
  public function add_term_column( $columns ) {
250
  return $this->add_column( $columns, 'posts' );
258
  * @param string $out
259
  * @param string $column Column name
260
  * @param int $term_id
261
+ * @return string
262
  */
263
  public function term_column( $out, $column, $term_id ) {
264
  $inline = wp_doing_ajax() && isset( $_REQUEST['action'], $_POST['inline_lang_choice'] ) && 'inline-save-tax' === $_REQUEST['action']; // phpcs:ignore WordPress.Security.NonceVerification
282
  $taxonomy = $GLOBALS['taxonomy'];
283
  }
284
 
285
+ if ( ! isset( $taxonomy, $post_type ) || ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
286
  return $out;
287
  }
288
 
289
  $term_id = (int) $term_id;
290
  $language = $this->model->get_language( substr( $column, 9 ) );
291
 
292
+ if ( empty( $language ) ) {
293
+ return $out;
294
+ }
295
+
296
  if ( $column == $this->get_first_language_column() ) {
297
  $out = sprintf( '<div class="hidden" id="lang_%d">%s</div>', intval( $term_id ), esc_html( $lang->slug ) );
298
 
346
  * Update rows of translated posts when the language is modified in quick edit
347
  *
348
  * @since 1.7
349
+ *
350
+ * @return void
351
  */
352
  public function ajax_update_post_rows() {
353
  check_ajax_referer( 'inlineeditnonce', '_pll_nonce' );
362
  wp_die( 0 );
363
  }
364
 
365
+ /** @var WP_Posts_List_Table $wp_list_table */
366
  $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => sanitize_key( $_POST['screen'] ) ) );
367
 
368
  $x = new WP_Ajax_Response();
390
  * Update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit
391
  *
392
  * @since 1.7
393
+ *
394
+ * @return void
395
  */
396
  public function ajax_update_term_rows() {
397
  check_ajax_referer( 'pll_language', '_pll_nonce' );
406
  wp_die( 0 );
407
  }
408
 
409
+ /** @var WP_Terms_List_Table $wp_list_table */
410
  $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => sanitize_key( $_POST['screen'] ) ) );
411
 
412
  $x = new WP_Ajax_Response();
436
  *
437
  * @since 2.8
438
  *
439
+ * @param PLL_Language $language PLL_Language object.
440
  * @return string
441
  */
442
  protected function get_flag_html( $language ) {
admin/admin-filters-media.php CHANGED
@@ -10,6 +10,9 @@
10
  * @since 1.2
11
  */
12
  class PLL_Admin_Filters_Media extends PLL_Admin_Filters_Post_Base {
 
 
 
13
  public $posts;
14
 
15
  /**
@@ -37,14 +40,14 @@ class PLL_Admin_Filters_Media extends PLL_Admin_Filters_Post_Base {
37
  }
38
 
39
  /**
40
- * Adds the language field and translations tables in the 'Edit Media' panel
41
  * Needs WP 3.5+
42
  *
43
  * @since 0.9
44
  *
45
- * @param array $fields list of form fields
46
- * @param object $post
47
- * @return array modified list of form fields
48
  */
49
  public function attachment_fields_to_edit( $fields, $post ) {
50
  if ( 'post.php' == $GLOBALS['pagenow'] ) {
@@ -76,6 +79,8 @@ class PLL_Admin_Filters_Media extends PLL_Admin_Filters_Post_Base {
76
  * Creates a media translation
77
  *
78
  * @since 0.9
 
 
79
  */
80
  public function translate_media() {
81
  if ( isset( $_GET['from_media'], $_GET['new_lang'] ) ) {
10
  * @since 1.2
11
  */
12
  class PLL_Admin_Filters_Media extends PLL_Admin_Filters_Post_Base {
13
+ /**
14
+ * @var PLL_CRUD_Posts
15
+ */
16
  public $posts;
17
 
18
  /**
40
  }
41
 
42
  /**
43
+ * Adds the language field and translations tables in the 'Edit Media' panel.
44
  * Needs WP 3.5+
45
  *
46
  * @since 0.9
47
  *
48
+ * @param array $fields List of form fields.
49
+ * @param WP_Post $post The attachment being edited.
50
+ * @return array Modified list of form fields.
51
  */
52
  public function attachment_fields_to_edit( $fields, $post ) {
53
  if ( 'post.php' == $GLOBALS['pagenow'] ) {
79
  * Creates a media translation
80
  *
81
  * @since 0.9
82
+ *
83
+ * @return void
84
  */
85
  public function translate_media() {
86
  if ( isset( $_GET['from_media'], $_GET['new_lang'] ) ) {
admin/admin-filters-post-base.php CHANGED
@@ -9,7 +9,22 @@
9
  * @since 1.5
10
  */
11
  abstract class PLL_Admin_Filters_Post_Base {
12
- public $links, $model, $pref_lang;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  /**
15
  * Constructor: setups filters and actions
@@ -25,21 +40,21 @@ abstract class PLL_Admin_Filters_Post_Base {
25
  }
26
 
27
  /**
28
- * Save translations from language metabox
29
  *
30
  * @since 1.5
31
  *
32
- * @param int $post_id
33
- * @param array $arr
34
- * @return array
35
  */
36
  protected function save_translations( $post_id, $arr ) {
37
- // Security check as 'wp_insert_post' can be called from outside WP admin
38
  check_admin_referer( 'pll_language', '_pll_nonce' );
39
 
40
  $translations = array();
41
 
42
- // Save translations after checking the translated post is in the right language
43
  foreach ( $arr as $lang => $tr_id ) {
44
  $translations[ $lang ] = ( $tr_id && $this->model->post->get_language( (int) $tr_id )->slug == $lang ) ? (int) $tr_id : 0;
45
  }
9
  * @since 1.5
10
  */
11
  abstract class PLL_Admin_Filters_Post_Base {
12
+ /**
13
+ * @var PLL_Model
14
+ */
15
+ public $model;
16
+
17
+ /**
18
+ * @var PLL_Links
19
+ */
20
+ public $links;
21
+
22
+ /**
23
+ * Language selected in the admin language filter.
24
+ *
25
+ * @var PLL_Language
26
+ */
27
+ public $filter_lang;
28
 
29
  /**
30
  * Constructor: setups filters and actions
40
  }
41
 
42
  /**
43
+ * Save translations from the languages metabox.
44
  *
45
  * @since 1.5
46
  *
47
+ * @param int $post_id Post id of the post being saved.
48
+ * @param int[] $arr An array with language codes as key and post id as value.
49
+ * @return int[] The array of translated post ids.
50
  */
51
  protected function save_translations( $post_id, $arr ) {
52
+ // Security check as 'wp_insert_post' can be called from outside WP admin.
53
  check_admin_referer( 'pll_language', '_pll_nonce' );
54
 
55
  $translations = array();
56
 
57
+ // Save translations after checking the translated post is in the right language.
58
  foreach ( $arr as $lang => $tr_id ) {
59
  $translations[ $lang ] = ( $tr_id && $this->model->post->get_language( (int) $tr_id )->slug == $lang ) ? (int) $tr_id : 0;
60
  }
admin/admin-filters-post.php CHANGED
@@ -9,6 +9,11 @@
9
  * @since 1.2
10
  */
11
  class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
 
 
 
 
 
12
  public $curlang;
13
 
14
  /**
@@ -43,10 +48,16 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
43
  * to filter the parent dropdown per post language in quick edit
44
  *
45
  * @since 1.7
 
 
46
  */
47
  public function admin_enqueue_scripts() {
48
  $screen = get_current_screen();
49
 
 
 
 
 
50
  // Hierarchical taxonomies
51
  if ( 'edit' == $screen->base && $taxonomies = get_object_taxonomies( $screen->post_type, 'object' ) ) {
52
  // Get translated hierarchical taxonomies
@@ -61,9 +72,11 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
61
  $terms = get_terms( $hierarchical_taxonomies, array( 'get' => 'all' ) );
62
  $term_languages = array();
63
 
64
- foreach ( $terms as $term ) {
65
- if ( $lang = $this->model->term->get_language( $term->term_id ) ) {
66
- $term_languages[ $lang->slug ][ $term->taxonomy ][] = $term->term_id;
 
 
67
  }
68
  }
69
 
@@ -95,11 +108,12 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
95
  }
96
 
97
  /**
98
- * Filters posts, pages and media by language
99
  *
100
  * @since 0.1
101
  *
102
- * @param object $query a WP_Query object
 
103
  */
104
  public function parse_query( $query ) {
105
  $pll_query = new PLL_Query( $query, $this->model );
@@ -110,14 +124,25 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
110
  * Save language and translation when editing a post (post.php)
111
  *
112
  * @since 2.3
 
 
113
  */
114
  public function edit_post() {
115
  if ( isset( $_POST['post_lang_choice'], $_POST['post_ID'] ) && $post_id = (int) $_POST['post_ID'] ) { // phpcs:ignore WordPress.Security.NonceVerification
116
  check_admin_referer( 'pll_language', '_pll_nonce' );
117
 
118
  $post = get_post( $post_id );
 
 
 
 
 
119
  $post_type_object = get_post_type_object( $post->post_type );
120
 
 
 
 
 
121
  if ( current_user_can( $post_type_object->cap->edit_post, $post_id ) ) {
122
  $this->model->post->set_language( $post_id, $this->model->get_language( sanitize_key( $_POST['post_lang_choice'] ) ) );
123
 
@@ -129,18 +154,28 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
129
  }
130
 
131
  /**
132
- * Save language when inline editing or bulk editing a post
133
- * Fix translations if necessary
134
  *
135
  * @since 2.3
136
  *
137
- * @param int $post_id Post ID
138
- * @param object $lang Language
 
139
  */
140
  protected function inline_save_language( $post_id, $lang ) {
141
  $post = get_post( $post_id );
 
 
 
 
 
142
  $post_type_object = get_post_type_object( $post->post_type );
143
 
 
 
 
 
144
  if ( current_user_can( $post_type_object->cap->edit_post, $post_id ) ) {
145
  $old_lang = $this->model->post->get_language( $post_id ); // Stores the old language
146
  $this->model->post->set_language( $post_id, $lang ); // set new language
@@ -166,6 +201,8 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
166
  * Save language when bulk editing a post
167
  *
168
  * @since 2.3
 
 
169
  */
170
  public function bulk_edit_posts() {
171
  if ( isset( $_GET['bulk_edit'], $_GET['inline_lang_choice'], $_REQUEST['post'] ) && -1 !== $_GET['inline_lang_choice'] ) { // phpcs:ignore WordPress.Security.NonceVerification
@@ -184,6 +221,8 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
184
  * Save language when inline editing a post
185
  *
186
  * @since 2.3
 
 
187
  */
188
  public function inline_edit_post() {
189
  check_admin_referer( 'inlineeditnonce', '_inline_edit' );
9
  * @since 1.2
10
  */
11
  class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
12
+ /**
13
+ * Current language (used to filter the content).
14
+ *
15
+ * @var PLL_Language
16
+ */
17
  public $curlang;
18
 
19
  /**
48
  * to filter the parent dropdown per post language in quick edit
49
  *
50
  * @since 1.7
51
+ *
52
+ * @return void
53
  */
54
  public function admin_enqueue_scripts() {
55
  $screen = get_current_screen();
56
 
57
+ if ( empty( $screen ) ) {
58
+ return;
59
+ }
60
+
61
  // Hierarchical taxonomies
62
  if ( 'edit' == $screen->base && $taxonomies = get_object_taxonomies( $screen->post_type, 'object' ) ) {
63
  // Get translated hierarchical taxonomies
72
  $terms = get_terms( $hierarchical_taxonomies, array( 'get' => 'all' ) );
73
  $term_languages = array();
74
 
75
+ if ( is_array( $terms ) ) {
76
+ foreach ( $terms as $term ) {
77
+ if ( $lang = $this->model->term->get_language( $term->term_id ) ) {
78
+ $term_languages[ $lang->slug ][ $term->taxonomy ][] = $term->term_id;
79
+ }
80
  }
81
  }
82
 
108
  }
109
 
110
  /**
111
+ * Filters posts, pages and media by language.
112
  *
113
  * @since 0.1
114
  *
115
+ * @param WP_Query $query WP_Query object.
116
+ * @return void
117
  */
118
  public function parse_query( $query ) {
119
  $pll_query = new PLL_Query( $query, $this->model );
124
  * Save language and translation when editing a post (post.php)
125
  *
126
  * @since 2.3
127
+ *
128
+ * @return void
129
  */
130
  public function edit_post() {
131
  if ( isset( $_POST['post_lang_choice'], $_POST['post_ID'] ) && $post_id = (int) $_POST['post_ID'] ) { // phpcs:ignore WordPress.Security.NonceVerification
132
  check_admin_referer( 'pll_language', '_pll_nonce' );
133
 
134
  $post = get_post( $post_id );
135
+
136
+ if ( empty( $post ) ) {
137
+ return;
138
+ }
139
+
140
  $post_type_object = get_post_type_object( $post->post_type );
141
 
142
+ if ( empty( $post_type_object ) ) {
143
+ return;
144
+ }
145
+
146
  if ( current_user_can( $post_type_object->cap->edit_post, $post_id ) ) {
147
  $this->model->post->set_language( $post_id, $this->model->get_language( sanitize_key( $_POST['post_lang_choice'] ) ) );
148
 
154
  }
155
 
156
  /**
157
+ * Saves a post language when inline editing or bulk editing.
158
+ * Fixes the translations if necessary.
159
  *
160
  * @since 2.3
161
  *
162
+ * @param int $post_id Post ID.
163
+ * @param PLL_Language $lang Language.
164
+ * @return void
165
  */
166
  protected function inline_save_language( $post_id, $lang ) {
167
  $post = get_post( $post_id );
168
+
169
+ if ( empty( $post ) ) {
170
+ return;
171
+ }
172
+
173
  $post_type_object = get_post_type_object( $post->post_type );
174
 
175
+ if ( empty( $post_type_object ) ) {
176
+ return;
177
+ }
178
+
179
  if ( current_user_can( $post_type_object->cap->edit_post, $post_id ) ) {
180
  $old_lang = $this->model->post->get_language( $post_id ); // Stores the old language
181
  $this->model->post->set_language( $post_id, $lang ); // set new language
201
  * Save language when bulk editing a post
202
  *
203
  * @since 2.3
204
+ *
205
+ * @return void
206
  */
207
  public function bulk_edit_posts() {
208
  if ( isset( $_GET['bulk_edit'], $_GET['inline_lang_choice'], $_REQUEST['post'] ) && -1 !== $_GET['inline_lang_choice'] ) { // phpcs:ignore WordPress.Security.NonceVerification
221
  * Save language when inline editing a post
222
  *
223
  * @since 2.3
224
+ *
225
+ * @return void
226
  */
227
  public function inline_edit_post() {
228
  check_admin_referer( 'inlineeditnonce', '_inline_edit' );
admin/admin-filters-term.php CHANGED
@@ -9,9 +9,50 @@
9
  * @since 1.2
10
  */
11
  class PLL_Admin_Filters_Term {
12
- public $links, $model, $options, $pref_lang;
13
- protected $pre_term_name; // Used to store the term name before creating a slug if needed
14
- protected $post_id; // Used to store the current post_id when bulk editing posts
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  /**
17
  * Constructor: setups filters and actions
@@ -56,6 +97,8 @@ class PLL_Admin_Filters_Term {
56
  * Adds the language field in the 'Categories' and 'Post Tags' panels
57
  *
58
  * @since 0.1
 
 
59
  */
60
  public function add_term_form() {
61
  if ( isset( $_GET['taxonomy'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
@@ -70,7 +113,7 @@ class PLL_Admin_Filters_Term {
70
  $post_type = $GLOBALS['post_type'];
71
  }
72
 
73
- if ( empty( $taxonomy ) || ! taxonomy_exists( $taxonomy ) || ! post_type_exists( $post_type ) ) {
74
  return;
75
  }
76
 
@@ -117,11 +160,12 @@ class PLL_Admin_Filters_Term {
117
  }
118
 
119
  /**
120
- * Adds the language field and translations tables in the 'Edit Category' and 'Edit Tag' panels
121
  *
122
  * @since 0.1
123
  *
124
- * @param object $tag
 
125
  */
126
  public function edit_term_form( $tag ) {
127
  if ( isset( $_REQUEST['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
@@ -132,7 +176,7 @@ class PLL_Admin_Filters_Term {
132
  $post_type = $GLOBALS['post_type'];
133
  }
134
 
135
- if ( ! post_type_exists( $post_type ) ) {
136
  return;
137
  }
138
 
@@ -153,7 +197,7 @@ class PLL_Admin_Filters_Term {
153
  array(
154
  'name' => 'term_lang_choice',
155
  'value' => 'term_id',
156
- 'selected' => $lang ? $lang->term_id : '',
157
  'disabled' => $disabled,
158
  'flag' => true,
159
  )
@@ -177,9 +221,7 @@ class PLL_Admin_Filters_Term {
177
  );
178
 
179
  echo '<tr id="term-translations" class="form-field">';
180
- if ( $lang ) {
181
- include __DIR__ . '/view-translations-term.php';
182
- }
183
  echo '</tr>' . "\n";
184
  }
185
 
@@ -199,7 +241,7 @@ class PLL_Admin_Filters_Term {
199
  if ( isset( $taxonomy, $_GET['from_tag'], $_GET['new_lang'] ) && taxonomy_exists( $taxonomy ) ) { // phpcs:ignore WordPress.Security.NonceVerification
200
  $term = get_term( (int) $_GET['from_tag'], $taxonomy ); // phpcs:ignore WordPress.Security.NonceVerification
201
 
202
- if ( $term && $id = $term->parent ) {
203
  $lang = $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
204
  if ( $parent = $this->model->term->get_translation( $id, $lang ) ) {
205
  return str_replace( '"' . $parent . '"', '"' . $parent . '" selected="selected"', $output );
@@ -215,6 +257,7 @@ class PLL_Admin_Filters_Term {
215
  * @since 1.7
216
  *
217
  * @param int $post_id
 
218
  */
219
  public function pre_post_update( $post_id ) {
220
  if ( isset( $_GET['bulk_edit'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
@@ -229,6 +272,7 @@ class PLL_Admin_Filters_Term {
229
  *
230
  * @param int $term_id
231
  * @param string $taxonomy
 
232
  */
233
  protected function save_language( $term_id, $taxonomy ) {
234
  global $wpdb;
@@ -323,20 +367,20 @@ class PLL_Admin_Filters_Term {
323
  }
324
 
325
  /**
326
- * Save translations from our form
327
  *
328
  * @since 1.5
329
  *
330
- * @param int $term_id
331
- * @return array
332
  */
333
  protected function save_translations( $term_id ) {
334
- // Security check as 'wp_update_term' can be called from outside WP admin
335
  check_admin_referer( 'pll_language', '_pll_nonce' );
336
 
337
  $translations = array();
338
 
339
- // Save translations after checking the translated term is in the right language ( as well as cast id to int )
340
  if ( isset( $_POST['term_tr_lang'] ) ) {
341
  foreach ( array_map( 'absint', $_POST['term_tr_lang'] ) as $lang => $tr_id ) {
342
  $tr_lang = $this->model->term->get_language( $tr_id );
@@ -358,6 +402,7 @@ class PLL_Admin_Filters_Term {
358
  * @param int $term_id
359
  * @param int $tt_id term taxonomy id
360
  * @param string $taxonomy
 
361
  */
362
  public function save_term( $term_id, $tt_id, $taxonomy ) {
363
  // Does nothing except on taxonomies which are filterable
@@ -365,10 +410,15 @@ class PLL_Admin_Filters_Term {
365
  return;
366
  }
367
 
 
 
 
 
 
 
368
  // Capability check
369
  // As 'wp_update_term' can be called from outside WP admin
370
  // 2nd test for creating tags when creating / editing a post
371
- $tax = get_taxonomy( $taxonomy );
372
  if ( current_user_can( $tax->cap->edit_terms ) || ( isset( $_POST['tax_input'][ $taxonomy ] ) && current_user_can( $tax->cap->assign_terms ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
373
  $this->save_language( $term_id, $taxonomy );
374
 
@@ -434,6 +484,8 @@ class PLL_Admin_Filters_Term {
434
  * Ajax response for edit term form
435
  *
436
  * @since 0.2
 
 
437
  */
438
  public function term_lang_choice() {
439
  check_ajax_referer( 'pll_language', '_pll_nonce' );
@@ -447,14 +499,12 @@ class PLL_Admin_Filters_Term {
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 ) ) {
451
  wp_die( 0 );
452
  }
453
 
454
  ob_start();
455
- if ( $lang ) {
456
- include __DIR__ . '/view-translations-term.php';
457
- }
458
  $x = new WP_Ajax_Response( array( 'what' => 'translations', 'data' => ob_get_contents() ) );
459
  ob_end_clean();
460
 
@@ -478,7 +528,7 @@ class PLL_Admin_Filters_Term {
478
  // Tests copied from edit_tags.php
479
  else {
480
  $tax = get_taxonomy( $taxonomy );
481
- if ( ! is_null( $tax->labels->popular_items ) ) {
482
  $args = array( 'taxonomy' => $taxonomy, 'echo' => false );
483
  if ( current_user_can( $tax->cap->edit_terms ) ) {
484
  $args = array_merge( $args, array( 'link' => 'edit' ) );
@@ -501,6 +551,8 @@ class PLL_Admin_Filters_Term {
501
  * Ajax response for input in translation autocomplete input box
502
  *
503
  * @since 1.5
 
 
504
  */
505
  public function ajax_terms_not_translated() {
506
  check_ajax_referer( 'pll_language', '_pll_nonce' );
@@ -509,6 +561,7 @@ class PLL_Admin_Filters_Term {
509
  wp_die( 0 );
510
  }
511
 
 
512
  $s = wp_unslash( $_GET['term'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
513
  $post_type = sanitize_key( $_GET['post_type'] );
514
  $taxonomy = sanitize_key( $_GET['taxonomy'] );
@@ -530,31 +583,36 @@ class PLL_Admin_Filters_Term {
530
  }
531
 
532
  // It is more efficient to use one common query for all languages as soon as there are more than 2.
533
- foreach ( get_terms( $taxonomy, 'hide_empty=0&lang=0&name__like=' . $s ) as $term ) {
534
- $lang = $this->model->term->get_language( $term->term_id );
 
 
535
 
536
- if ( $lang && $lang->slug == $translation_language->slug && ! $this->model->term->get_translation( $term->term_id, $term_language ) ) {
537
- $terms[] = $term;
 
538
  }
539
  }
540
 
541
  // Format the ajax response.
542
  foreach ( $terms as $term ) {
543
- $return[] = array(
544
- 'id' => $term->term_id,
545
- 'value' => rtrim( // Trim the seperator added at the end by WP.
546
- get_term_parents_list(
547
- $term->term_id,
548
- $term->taxonomy,
549
- array(
550
- 'separator' => ' > ',
551
- 'link' => false,
552
- )
 
 
 
553
  ),
554
- ' >'
555
- ),
556
- 'link' => $this->links->edit_term_translation_link( $term->term_id, $term->taxonomy, $post_type ),
557
- );
558
  }
559
 
560
  wp_die( wp_json_encode( $return ) );
@@ -583,6 +641,7 @@ class PLL_Admin_Filters_Term {
583
  *
584
  * @param int $old_value
585
  * @param int $value
 
586
  */
587
  public function update_option_default_category( $old_value, $value ) {
588
  $default_cat_lang = $this->model->term->get_language( $value );
@@ -610,6 +669,7 @@ class PLL_Admin_Filters_Term {
610
  * @param int $new_term_id
611
  * @param int $term_taxonomy_id
612
  * @param string $taxonomy
 
613
  */
614
  public function split_shared_term( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
615
  if ( ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
@@ -622,8 +682,12 @@ class PLL_Admin_Filters_Term {
622
  return;
623
  }
624
 
625
- $avoid_recursion = true;
626
  $lang = $this->model->term->get_language( $term_id );
 
 
 
 
 
627
  $translations = array();
628
 
629
  foreach ( $this->model->term->get_translations( $term_id ) as $key => $tr_id ) {
9
  * @since 1.2
10
  */
11
  class PLL_Admin_Filters_Term {
12
+ /**
13
+ * Stores the plugin options.
14
+ *
15
+ * @var array
16
+ */
17
+ public $options;
18
+
19
+ /**
20
+ * @var PLL_Model
21
+ */
22
+ public $model;
23
+
24
+ /**
25
+ * @var PLL_Admin_Links
26
+ */
27
+ public $links;
28
+
29
+ /**
30
+ * Language selected in the admin language filter.
31
+ *
32
+ * @var PLL_Language
33
+ */
34
+ public $filter_lang;
35
+
36
+ /**
37
+ * Preferred language to assign to the new terms.
38
+ *
39
+ * @var PLL_Language
40
+ */
41
+ public $pref_lang;
42
+
43
+ /**
44
+ * Stores the term name before creating a slug if needed.
45
+ *
46
+ * @var string
47
+ */
48
+ protected $pre_term_name;
49
+
50
+ /**
51
+ * Stores the current post_id when bulk editing posts.
52
+ *
53
+ * @var int
54
+ */
55
+ protected $post_id;
56
 
57
  /**
58
  * Constructor: setups filters and actions
97
  * Adds the language field in the 'Categories' and 'Post Tags' panels
98
  *
99
  * @since 0.1
100
+ *
101
+ * @return void
102
  */
103
  public function add_term_form() {
104
  if ( isset( $_GET['taxonomy'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
113
  $post_type = $GLOBALS['post_type'];
114
  }
115
 
116
+ if ( ! isset( $taxonomy, $post_type ) || ! taxonomy_exists( $taxonomy ) || ! post_type_exists( $post_type ) ) {
117
  return;
118
  }
119
 
160
  }
161
 
162
  /**
163
+ * Adds the language field and translations tables in the 'Edit Category' and 'Edit Tag' panels.
164
  *
165
  * @since 0.1
166
  *
167
+ * @param WP_Term $tag The term being edited.
168
+ * @return void
169
  */
170
  public function edit_term_form( $tag ) {
171
  if ( isset( $_REQUEST['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
176
  $post_type = $GLOBALS['post_type'];
177
  }
178
 
179
+ if ( ! isset( $post_type ) || ! post_type_exists( $post_type ) ) {
180
  return;
181
  }
182
 
197
  array(
198
  'name' => 'term_lang_choice',
199
  'value' => 'term_id',
200
+ 'selected' => $lang->term_id,
201
  'disabled' => $disabled,
202
  'flag' => true,
203
  )
221
  );
222
 
223
  echo '<tr id="term-translations" class="form-field">';
224
+ include __DIR__ . '/view-translations-term.php';
 
 
225
  echo '</tr>' . "\n";
226
  }
227
 
241
  if ( isset( $taxonomy, $_GET['from_tag'], $_GET['new_lang'] ) && taxonomy_exists( $taxonomy ) ) { // phpcs:ignore WordPress.Security.NonceVerification
242
  $term = get_term( (int) $_GET['from_tag'], $taxonomy ); // phpcs:ignore WordPress.Security.NonceVerification
243
 
244
+ if ( $term instanceof WP_Term && $id = $term->parent ) {
245
  $lang = $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
246
  if ( $parent = $this->model->term->get_translation( $id, $lang ) ) {
247
  return str_replace( '"' . $parent . '"', '"' . $parent . '" selected="selected"', $output );
257
  * @since 1.7
258
  *
259
  * @param int $post_id
260
+ * @return void
261
  */
262
  public function pre_post_update( $post_id ) {
263
  if ( isset( $_GET['bulk_edit'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
272
  *
273
  * @param int $term_id
274
  * @param string $taxonomy
275
+ * @return void
276
  */
277
  protected function save_language( $term_id, $taxonomy ) {
278
  global $wpdb;
367
  }
368
 
369
  /**
370
+ * Save translations from our form.
371
  *
372
  * @since 1.5
373
  *
374
+ * @param int $term_id The term id of teh term being saved.
375
+ * @return int[] The array of translated term ids.
376
  */
377
  protected function save_translations( $term_id ) {
378
+ // Security check as 'wp_update_term' can be called from outside WP admin.
379
  check_admin_referer( 'pll_language', '_pll_nonce' );
380
 
381
  $translations = array();
382
 
383
+ // Save translations after checking the translated term is in the right language ( as well as cast id to int ).
384
  if ( isset( $_POST['term_tr_lang'] ) ) {
385
  foreach ( array_map( 'absint', $_POST['term_tr_lang'] ) as $lang => $tr_id ) {
386
  $tr_lang = $this->model->term->get_language( $tr_id );
402
  * @param int $term_id
403
  * @param int $tt_id term taxonomy id
404
  * @param string $taxonomy
405
+ * @return void
406
  */
407
  public function save_term( $term_id, $tt_id, $taxonomy ) {
408
  // Does nothing except on taxonomies which are filterable
410
  return;
411
  }
412
 
413
+ $tax = get_taxonomy( $taxonomy );
414
+
415
+ if ( empty( $tax ) ) {
416
+ return;
417
+ }
418
+
419
  // Capability check
420
  // As 'wp_update_term' can be called from outside WP admin
421
  // 2nd test for creating tags when creating / editing a post
 
422
  if ( current_user_can( $tax->cap->edit_terms ) || ( isset( $_POST['tax_input'][ $taxonomy ] ) && current_user_can( $tax->cap->assign_terms ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
423
  $this->save_language( $term_id, $taxonomy );
424
 
484
  * Ajax response for edit term form
485
  *
486
  * @since 0.2
487
+ *
488
+ * @return void
489
  */
490
  public function term_lang_choice() {
491
  check_ajax_referer( 'pll_language', '_pll_nonce' );
499
  $taxonomy = sanitize_key( $_POST['taxonomy'] );
500
  $post_type = sanitize_key( $_POST['post_type'] );
501
 
502
+ if ( empty( $lang ) || ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
503
  wp_die( 0 );
504
  }
505
 
506
  ob_start();
507
+ include __DIR__ . '/view-translations-term.php';
 
 
508
  $x = new WP_Ajax_Response( array( 'what' => 'translations', 'data' => ob_get_contents() ) );
509
  ob_end_clean();
510
 
528
  // Tests copied from edit_tags.php
529
  else {
530
  $tax = get_taxonomy( $taxonomy );
531
+ if ( ! empty( $tax ) && ! is_null( $tax->labels->popular_items ) ) {
532
  $args = array( 'taxonomy' => $taxonomy, 'echo' => false );
533
  if ( current_user_can( $tax->cap->edit_terms ) ) {
534
  $args = array_merge( $args, array( 'link' => 'edit' ) );
551
  * Ajax response for input in translation autocomplete input box
552
  *
553
  * @since 1.5
554
+ *
555
+ * @return void
556
  */
557
  public function ajax_terms_not_translated() {
558
  check_ajax_referer( 'pll_language', '_pll_nonce' );
561
  wp_die( 0 );
562
  }
563
 
564
+ /** @var string */
565
  $s = wp_unslash( $_GET['term'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
566
  $post_type = sanitize_key( $_GET['post_type'] );
567
  $taxonomy = sanitize_key( $_GET['taxonomy'] );
583
  }
584
 
585
  // It is more efficient to use one common query for all languages as soon as there are more than 2.
586
+ $all_terms = get_terms( $taxonomy, 'hide_empty=0&lang=0&name__like=' . $s );
587
+ if ( is_array( $all_terms ) ) {
588
+ foreach ( $all_terms as $term ) {
589
+ $lang = $this->model->term->get_language( $term->term_id );
590
 
591
+ if ( $lang && $lang->slug == $translation_language->slug && ! $this->model->term->get_translation( $term->term_id, $term_language ) ) {
592
+ $terms[] = $term;
593
+ }
594
  }
595
  }
596
 
597
  // Format the ajax response.
598
  foreach ( $terms as $term ) {
599
+ if ( $term instanceof WP_Term ) {
600
+ $return[] = array(
601
+ 'id' => $term->term_id,
602
+ 'value' => rtrim( // Trim the seperator added at the end by WP.
603
+ get_term_parents_list(
604
+ $term->term_id,
605
+ $term->taxonomy,
606
+ array(
607
+ 'separator' => ' > ',
608
+ 'link' => false,
609
+ )
610
+ ),
611
+ ' >'
612
  ),
613
+ 'link' => $this->links->edit_term_translation_link( $term->term_id, $term->taxonomy, $post_type ),
614
+ );
615
+ }
 
616
  }
617
 
618
  wp_die( wp_json_encode( $return ) );
641
  *
642
  * @param int $old_value
643
  * @param int $value
644
+ * @return void
645
  */
646
  public function update_option_default_category( $old_value, $value ) {
647
  $default_cat_lang = $this->model->term->get_language( $value );
669
  * @param int $new_term_id
670
  * @param int $term_taxonomy_id
671
  * @param string $taxonomy
672
+ * @return void
673
  */
674
  public function split_shared_term( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
675
  if ( ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
682
  return;
683
  }
684
 
 
685
  $lang = $this->model->term->get_language( $term_id );
686
+ if ( empty( $lang ) ) {
687
+ return;
688
+ }
689
+
690
+ $avoid_recursion = true;
691
  $translations = array();
692
 
693
  foreach ( $this->model->term->get_translations( $term_id ) as $key => $tr_id ) {
admin/admin-filters-widgets-options.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Polylang
4
+ */
5
+
6
+ /**
7
+ * Class PLL_Widgets_Filters
8
+ *
9
+ * @since 3.0
10
+ *
11
+ * Adds new options to {@see https://developer.wordpress.org/reference/classes/wp_widget/ WP_Widget} and saves them.
12
+ */
13
+ class PLL_Admin_Filters_Widgets_Options extends PLL_Filters_Widgets_Options {
14
+ /**
15
+ * Modifies the widgets forms to add our language dropdown list.
16
+ *
17
+ * @since 0.3
18
+ * @since 3.0 Moved from PLL_Admin_Filters
19
+ *
20
+ * @param WP_Widget $widget Widget instance.
21
+ * @param null $return Not used.
22
+ * @param array $instance Widget settings.
23
+ * @return void
24
+ */
25
+ public function in_widget_form( $widget, $return, $instance ) {
26
+ $screen = get_current_screen();
27
+
28
+ // Test the Widgets screen and the Customizer to avoid displaying the option in page builders
29
+ // Saving the widget reloads the form. And curiously the action is in $_REQUEST but neither in $_POST, nor in $_GET.
30
+ if ( ( isset( $screen ) && in_array( $screen->base, array( 'widgets', 'appearance_page_gutenberg-widgets' ) ) ) || ( isset( $_REQUEST['action'] ) && 'save-widget' === $_REQUEST['action'] ) || isset( $GLOBALS['wp_customize'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
31
+ parent::in_widget_form( $widget, $return, $instance );
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Called when widget options are saved.
37
+ * Saves the language associated to the widget.
38
+ *
39
+ * @since 3.0
40
+ *
41
+ * @param array $instance The current Widget's options.
42
+ * @param array $new_instance The new Widget's options.
43
+ * @param array $old_instance Not used.
44
+ * @param WP_Widget $widget The Widget object.
45
+ * @return array The processed Widget options.
46
+ */
47
+ public function widget_update_callback( $instance, $new_instance, $old_instance, $widget ) {
48
+ $key = $this->get_language_key( $widget );
49
+ if ( ! empty( $_POST[ $key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
50
+ $new_instance[ $key ] = sanitize_key( $_POST[ $key ] ); // phpcs:ignore WordPress.Security.NonceVerification
51
+ }
52
+ return parent::widget_update_callback( $instance, $new_instance, $old_instance, $widget );
53
+ }
54
+
55
+ }
admin/admin-filters.php CHANGED
@@ -20,19 +20,11 @@ class PLL_Admin_Filters extends PLL_Filters {
20
  public function __construct( &$polylang ) {
21
  parent::__construct( $polylang );
22
 
23
- // Widgets languages filter
24
- add_action( 'in_widget_form', array( $this, 'in_widget_form' ), 10, 3 );
25
- add_filter( 'widget_update_callback', array( $this, 'widget_update_callback' ), 10, 4 );
26
-
27
  // Language management for users
28
  add_action( 'personal_options_update', array( $this, 'personal_options_update' ) );
29
  add_action( 'edit_user_profile_update', array( $this, 'personal_options_update' ) );
30
  add_action( 'personal_options', array( $this, 'personal_options' ) );
31
 
32
- // Upgrades languages files after a core upgrade ( timing is important )
33
- // Backward compatibility WP < 4.0 *AND* Polylang < 1.6
34
- add_action( '_core_updated_successfully', array( $this, 'upgrade_languages' ), 1 ); // since WP 3.3
35
-
36
  // Upgrades plugins and themes translations files
37
  add_filter( 'themes_update_check_locales', array( $this, 'update_check_locales' ) );
38
  add_filter( 'plugins_update_check_locales', array( $this, 'update_check_locales' ) );
@@ -43,75 +35,13 @@ class PLL_Admin_Filters extends PLL_Filters {
43
  add_filter( 'display_post_states', array( $this, 'display_post_states' ), 10, 2 );
44
  }
45
 
46
- /**
47
- * Modifies the widgets forms to add our language dropdown list
48
- *
49
- * @since 0.3
50
- *
51
- * @param object $widget Widget instance
52
- * @param null $return Not used
53
- * @param array $instance Widget settings
54
- */
55
- public function in_widget_form( $widget, $return, $instance ) {
56
- $screen = get_current_screen();
57
-
58
- // Test the Widgets screen and the Customizer to avoid displaying the option in page builders
59
- // Saving the widget reloads the form. And curiously the action is in $_REQUEST but neither in $_POST, nor in $_GET.
60
- if ( ( isset( $screen ) && 'widgets' === $screen->base ) || ( isset( $_REQUEST['action'] ) && 'save-widget' === $_REQUEST['action'] ) || isset( $GLOBALS['wp_customize'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
61
- $dropdown = new PLL_Walker_Dropdown();
62
-
63
- $dropdown_html = $dropdown->walk(
64
- array_merge(
65
- array( (object) array( 'slug' => 0, 'name' => __( 'All languages', 'polylang' ) ) ),
66
- $this->model->get_languages_list()
67
- ),
68
- -1,
69
- array(
70
- 'name' => $widget->id . '_lang_choice',
71
- 'class' => 'tags-input pll-lang-choice',
72
- 'selected' => empty( $instance['pll_lang'] ) ? '' : $instance['pll_lang'],
73
- )
74
- );
75
-
76
- printf(
77
- '<p><label for="%1$s">%2$s %3$s</label></p>',
78
- esc_attr( $widget->id . '_lang_choice' ),
79
- esc_html__( 'The widget is displayed for:', 'polylang' ),
80
- $dropdown_html // phpcs:ignore WordPress.Security.EscapeOutput
81
- );
82
- }
83
- }
84
-
85
- /**
86
- * Called when widget options are saved
87
- * saves the language associated to the widget
88
- *
89
- * @since 0.3
90
- *
91
- * @param array $instance Widget options
92
- * @param array $new_instance Not used
93
- * @param array $old_instance Not used
94
- * @param object $widget WP_Widget object
95
- * @return array Widget options
96
- */
97
- public function widget_update_callback( $instance, $new_instance, $old_instance, $widget ) {
98
- $key = $widget->id . '_lang_choice';
99
-
100
- if ( ! empty( $_POST[ $key ] ) && $lang = $this->model->get_language( sanitize_key( $_POST[ $key ] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
101
- $instance['pll_lang'] = $lang->slug;
102
- } else {
103
- unset( $instance['pll_lang'] );
104
- }
105
-
106
- return $instance;
107
- }
108
-
109
  /**
110
  * Updates language user preference set in user profile
111
  *
112
  * @since 0.4
113
  *
114
  * @param int $user_id
 
115
  */
116
  public function personal_options_update( $user_id ) {
117
  // Biography translations
@@ -126,11 +56,12 @@ class PLL_Admin_Filters extends PLL_Filters {
126
  }
127
 
128
  /**
129
- * Outputs hidden information to modify the biography form with js
130
  *
131
  * @since 0.4
132
  *
133
- * @param object $profileuser
 
134
  */
135
  public function personal_options( $profileuser ) {
136
  foreach ( $this->model->get_languages_list() as $lang ) {
@@ -148,31 +79,13 @@ class PLL_Admin_Filters extends PLL_Filters {
148
  }
149
  }
150
 
151
- /**
152
- * Upgrades languages files after a core upgrade
153
- * only for backward compatibility WP < 4.0 *AND* Polylang < 1.6
154
- *
155
- * @since 0.6
156
- *
157
- * @param string $version new WP version
158
- */
159
- public function upgrade_languages( $version ) {
160
- // $GLOBALS['wp_version'] is the old WP version
161
- if ( version_compare( $version, '4.0', '>=' ) && version_compare( $GLOBALS['wp_version'], '4.0', '<' ) ) {
162
-
163
- /** This filter is documented in wp-admin/includes/update-core.php */
164
- apply_filters( 'update_feedback', __( 'Upgrading language files&#8230;', 'polylang' ) );
165
- PLL_Upgrade::download_language_packs();
166
- }
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' ) ) );
@@ -194,13 +107,13 @@ class PLL_Admin_Filters extends PLL_Filters {
194
  }
195
 
196
  /**
197
- * Add post state for translations of the privacy policy page
198
  *
199
  * @since 2.7
200
  *
201
- * @param array $post_states An array of post display states.
202
- * @param object $post The current post object.
203
- * @return array
204
  */
205
  public function display_post_states( $post_states, $post ) {
206
  $page_for_privacy_policy = get_option( 'wp_page_for_privacy_policy' );
20
  public function __construct( &$polylang ) {
21
  parent::__construct( $polylang );
22
 
 
 
 
 
23
  // Language management for users
24
  add_action( 'personal_options_update', array( $this, 'personal_options_update' ) );
25
  add_action( 'edit_user_profile_update', array( $this, 'personal_options_update' ) );
26
  add_action( 'personal_options', array( $this, 'personal_options' ) );
27
 
 
 
 
 
28
  // Upgrades plugins and themes translations files
29
  add_filter( 'themes_update_check_locales', array( $this, 'update_check_locales' ) );
30
  add_filter( 'plugins_update_check_locales', array( $this, 'update_check_locales' ) );
35
  add_filter( 'display_post_states', array( $this, 'display_post_states' ), 10, 2 );
36
  }
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  /**
39
  * Updates language user preference set in user profile
40
  *
41
  * @since 0.4
42
  *
43
  * @param int $user_id
44
+ * @return void
45
  */
46
  public function personal_options_update( $user_id ) {
47
  // Biography translations
56
  }
57
 
58
  /**
59
+ * Outputs hidden information to modify the biography form with js.
60
  *
61
  * @since 0.4
62
  *
63
+ * @param WP_User $profileuser The current WP_User object.
64
+ * @return void
65
  */
66
  public function personal_options( $profileuser ) {
67
  foreach ( $this->model->get_languages_list() as $lang ) {
79
  }
80
  }
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  /**
83
  * Allows to update translations files for plugins and themes.
84
  *
85
  * @since 1.6
86
  *
87
+ * @param string[] $locales List of locales to update for plugins and themes.
88
+ * @return string[]
89
  */
90
  public function update_check_locales( $locales ) {
91
  return array_merge( $locales, $this->model->get_languages_list( array( 'fields' => 'locale' ) ) );
107
  }
108
 
109
  /**
110
+ * Adds post state for translations of the privacy policy page.
111
  *
112
  * @since 2.7
113
  *
114
+ * @param string[] $post_states An array of post display states.
115
+ * @param WP_Post $post The current post object.
116
+ * @return string[]
117
  */
118
  public function display_post_states( $post_states, $post ) {
119
  $page_for_privacy_policy = get_option( 'wp_page_for_privacy_policy' );
admin/admin-links.php CHANGED
@@ -4,19 +4,19 @@
4
  */
5
 
6
  /**
7
- * Manages links related functions
8
  *
9
  * @since 1.8
10
  */
11
  class PLL_Admin_Links extends PLL_Links {
12
 
13
  /**
14
- * Returns html markup for a new translation link
15
  *
16
  * @since 2.6
17
  *
18
- * @param string $link The new translation link.
19
- * @param object $language The language of the new translation.
20
  * @return string
21
  */
22
  protected function new_translation_link( $link, $language ) {
@@ -38,12 +38,12 @@ class PLL_Admin_Links extends PLL_Links {
38
  }
39
 
40
  /**
41
- * Returns html markup for a translation link
42
  *
43
  * @since 2.6
44
  *
45
- * @param string $link The translation link.
46
- * @param object $language The language of the translation.
47
  * @return string
48
  */
49
  public function edit_translation_link( $link, $language ) {
@@ -56,20 +56,20 @@ class PLL_Admin_Links extends PLL_Links {
56
  }
57
 
58
  /**
59
- * Get the link to create a new post translation
60
  *
61
  * @since 1.5
62
  *
63
- * @param int $post_id The source post id.
64
- * @param object $language The language of the new translation.
65
- * @param string $context Optional. Defaults to 'display' which encodes '&' to '&amp;'.
66
- * Otherwise, preserves '&'.
67
  * @return string
68
  */
69
  public function get_new_post_translation_link( $post_id, $language, $context = 'display' ) {
70
  $post_type = get_post_type( $post_id );
71
  $post_type_object = get_post_type_object( get_post_type( $post_id ) );
72
- if ( ! current_user_can( $post_type_object->cap->create_posts ) ) {
73
  return '';
74
  }
75
 
@@ -113,24 +113,24 @@ class PLL_Admin_Links extends PLL_Links {
113
  }
114
 
115
  /**
116
- * Filter the new post translation link
117
  *
118
  * @since 1.8
119
  *
120
- * @param string $link the new post translation link
121
- * @param object $language the language of the new translation
122
- * @param int $post_id the source post id
123
  */
124
  return apply_filters( 'pll_get_new_post_translation_link', $link, $language, $post_id );
125
  }
126
 
127
  /**
128
- * Returns html markup for a new post translation link
129
  *
130
  * @since 1.8
131
  *
132
- * @param int $post_id
133
- * @param object $language
134
  * @return string
135
  */
136
  public function new_post_translation_link( $post_id, $language ) {
@@ -139,11 +139,11 @@ class PLL_Admin_Links extends PLL_Links {
139
  }
140
 
141
  /**
142
- * Returns html markup for a translation link
143
  *
144
  * @since 1.4
145
  *
146
- * @param int $post_id translation post id
147
  * @return string
148
  */
149
  public function edit_post_translation_link( $post_id ) {
@@ -153,14 +153,14 @@ class PLL_Admin_Links extends PLL_Links {
153
  }
154
 
155
  /**
156
- * Get the link to create a new term translation
157
  *
158
  * @since 1.5
159
  *
160
- * @param int $term_id
161
- * @param string $taxonomy
162
- * @param string $post_type
163
- * @param object $language
164
  * @return string
165
  */
166
  public function get_new_term_translation_link( $term_id, $taxonomy, $post_type, $language ) {
@@ -179,28 +179,28 @@ class PLL_Admin_Links extends PLL_Links {
179
  $link = add_query_arg( $args, admin_url( 'edit-tags.php' ) );
180
 
181
  /**
182
- * Filter the new term translation link
183
  *
184
  * @since 1.8
185
  *
186
- * @param string $link the new term translation link
187
- * @param object $language the language of the new translation
188
- * @param int $term_id the source term id
189
- * @param string $taxonomy
190
- * @param string $post_type
191
  */
192
  return apply_filters( 'pll_get_new_term_translation_link', $link, $language, $term_id, $taxonomy, $post_type );
193
  }
194
 
195
  /**
196
- * Returns html markup for a new term translation
197
  *
198
  * @since 1.8
199
  *
200
- * @param int $term_id
201
- * @param string $taxonomy
202
- * @param string $post_type
203
- * @param object $language
204
  * @return string
205
  */
206
  public function new_term_translation_link( $term_id, $taxonomy, $post_type, $language ) {
@@ -209,13 +209,13 @@ class PLL_Admin_Links extends PLL_Links {
209
  }
210
 
211
  /**
212
- * Returns html markup for a term translation link
213
  *
214
  * @since 1.4
215
  *
216
- * @param object $term_id translation term id
217
- * @param string $taxonomy
218
- * @param string $post_type
219
  * @return string
220
  */
221
  public function edit_term_translation_link( $term_id, $taxonomy, $post_type ) {
4
  */
5
 
6
  /**
7
+ * Manages links related functions.
8
  *
9
  * @since 1.8
10
  */
11
  class PLL_Admin_Links extends PLL_Links {
12
 
13
  /**
14
+ * Returns the html markup for a new translation link.
15
  *
16
  * @since 2.6
17
  *
18
+ * @param string $link The new translation link.
19
+ * @param PLL_Language $language The language of the new translation.
20
  * @return string
21
  */
22
  protected function new_translation_link( $link, $language ) {
38
  }
39
 
40
  /**
41
+ * Returns the html markup for a translation link.
42
  *
43
  * @since 2.6
44
  *
45
+ * @param string $link The translation link.
46
+ * @param PLL_Language $language The language of the translation.
47
  * @return string
48
  */
49
  public function edit_translation_link( $link, $language ) {
56
  }
57
 
58
  /**
59
+ * Get the link to create a new post translation.
60
  *
61
  * @since 1.5
62
  *
63
+ * @param int $post_id The source post id.
64
+ * @param PLL_Language $language The language of the new translation.
65
+ * @param string $context Optional. Defaults to 'display' which encodes '&' to '&amp;'.
66
+ * Otherwise, preserves '&'.
67
  * @return string
68
  */
69
  public function get_new_post_translation_link( $post_id, $language, $context = 'display' ) {
70
  $post_type = get_post_type( $post_id );
71
  $post_type_object = get_post_type_object( get_post_type( $post_id ) );
72
+ if ( empty( $post_type_object ) || ! current_user_can( $post_type_object->cap->create_posts ) ) {
73
  return '';
74
  }
75
 
113
  }
114
 
115
  /**
116
+ * Filters the new post translation link.
117
  *
118
  * @since 1.8
119
  *
120
+ * @param string $link The new post translation link.
121
+ * @param PLL_Language $language The language of the new translation.
122
+ * @param int $post_id The source post id.
123
  */
124
  return apply_filters( 'pll_get_new_post_translation_link', $link, $language, $post_id );
125
  }
126
 
127
  /**
128
+ * Returns the html markup for a new post translation link.
129
  *
130
  * @since 1.8
131
  *
132
+ * @param int $post_id The source post id.
133
+ * @param PLL_Language $language The language of the new translation.
134
  * @return string
135
  */
136
  public function new_post_translation_link( $post_id, $language ) {
139
  }
140
 
141
  /**
142
+ * Returns the html markup for a post translation link.
143
  *
144
  * @since 1.4
145
  *
146
+ * @param int $post_id The translation post id.
147
  * @return string
148
  */
149
  public function edit_post_translation_link( $post_id ) {
153
  }
154
 
155
  /**
156
+ * Get the link to create a new term translation.
157
  *
158
  * @since 1.5
159
  *
160
+ * @param int $term_id Source term id.
161
+ * @param string $taxonomy Taxonomy name.
162
+ * @param string $post_type Post type name.
163
+ * @param PLL_Language $language The language of the new translation.
164
  * @return string
165
  */
166
  public function get_new_term_translation_link( $term_id, $taxonomy, $post_type, $language ) {
179
  $link = add_query_arg( $args, admin_url( 'edit-tags.php' ) );
180
 
181
  /**
182
+ * Filters the new term translation link.
183
  *
184
  * @since 1.8
185
  *
186
+ * @param string $link The new term translation link.
187
+ * @param PLL_Language $language The language of the new translation.
188
+ * @param int $term_id The source term id.
189
+ * @param string $taxonomy Taxonomy name.
190
+ * @param string $post_type Post type name.
191
  */
192
  return apply_filters( 'pll_get_new_term_translation_link', $link, $language, $term_id, $taxonomy, $post_type );
193
  }
194
 
195
  /**
196
+ * Returns the html markup for a new term translation.
197
  *
198
  * @since 1.8
199
  *
200
+ * @param int $term_id Source term id.
201
+ * @param string $taxonomy Taxonomy name.
202
+ * @param string $post_type Post type name.
203
+ * @param PLL_Language $language The language of the new translation.
204
  * @return string
205
  */
206
  public function new_term_translation_link( $term_id, $taxonomy, $post_type, $language ) {
209
  }
210
 
211
  /**
212
+ * Returns the html markup for a term translation link.
213
  *
214
  * @since 1.4
215
  *
216
+ * @param int $term_id Translation term id.
217
+ * @param string $taxonomy Taxonomy name.
218
+ * @param string $post_type Post type name.
219
  * @return string
220
  */
221
  public function edit_term_translation_link( $term_id, $taxonomy, $post_type ) {
admin/admin-model.php CHANGED
@@ -4,7 +4,7 @@
4
  */
5
 
6
  /**
7
- * Extends the PLL_Model class with methods needed only in Polylang settings pages
8
  *
9
  * @since 1.2
10
  */
@@ -12,23 +12,20 @@ class PLL_Admin_Model extends PLL_Model {
12
 
13
  /**
14
  * Adds a new language
15
- * Creates a default category for this language
16
- *
17
- * List of arguments that $args must contain:
18
- * name -> language name ( used only for display )
19
- * slug -> language code ( ideally 2-letters ISO 639-1 language code )
20
- * locale -> WordPress locale. If something wrong is used for the locale, the .mo files will not be loaded...
21
- * rtl -> 1 if rtl language, 0 otherwise
22
- * term_group -> language order when displayed
23
- *
24
- * Optional arguments that $args can contain:
25
- * no_default_cat -> if set, no default category will be created for this language
26
- * flag -> country code, see flags.php
27
  *
28
  * @since 1.2
29
  *
30
- * @param array $args
31
- * @return bool true if success / false if failed
 
 
 
 
 
 
 
 
32
  */
33
  public function add_language( $args ) {
34
  $errors = $this->validate_lang( $args );
@@ -67,11 +64,11 @@ class PLL_Admin_Model extends PLL_Model {
67
  $mo->export_to_db( $this->get_language( $args['slug'] ) );
68
 
69
  /**
70
- * Fires when a language is added
71
  *
72
  * @since 1.9
73
  *
74
- * @param array $args arguments used to create the language
75
  */
76
  do_action( 'pll_add_language', $args );
77
 
@@ -81,11 +78,12 @@ class PLL_Admin_Model extends PLL_Model {
81
  }
82
 
83
  /**
84
- * Delete a language
85
  *
86
  * @since 1.2
87
  *
88
- * @param int $lang_id language term_id
 
89
  */
90
  public function delete_language( $lang_id ) {
91
  $lang = $this->get_language( (int) $lang_id );
@@ -142,7 +140,7 @@ class PLL_Admin_Model extends PLL_Model {
142
 
143
  // Delete the string translations
144
  $post = wpcom_vip_get_page_by_title( 'polylang_mo_' . $lang->term_id, OBJECT, 'polylang_mo' );
145
- if ( ! empty( $post ) ) {
146
  wp_delete_post( $post->ID );
147
  }
148
 
@@ -162,23 +160,20 @@ class PLL_Admin_Model extends PLL_Model {
162
  }
163
 
164
  /**
165
- * Update language properties
166
- *
167
- * List of arguments that $args must contain:
168
- * lang_id -> term_id of the language to modify
169
- * name -> language name ( used only for display )
170
- * slug -> language code ( ideally 2-letters ISO 639-1 language code
171
- * locale -> WordPress locale. If something wrong is used for the locale, the .mo files will not be loaded...
172
- * rtl -> 1 if rtl language, 0 otherwise
173
- * term_group -> language order when displayed
174
- *
175
- * Optional arguments that $args can contain:
176
- * flag -> country code, see flags.php
177
  *
178
  * @since 1.2
179
  *
180
- * @param array $args
181
- * @return bool true if success / false if failed
 
 
 
 
 
 
 
 
182
  */
183
  public function update_language( $args ) {
184
  $lang = $this->get_language( (int) $args['lang_id'] );
@@ -243,11 +238,11 @@ class PLL_Admin_Model extends PLL_Model {
243
  wp_update_term( (int) $lang->tl_term_id, 'term_language', array( 'slug' => 'pll_' . $slug, 'name' => $args['name'] ) );
244
 
245
  /**
246
- * Fires when a language is added
247
  *
248
  * @since 1.9
249
  *
250
- * @param array $args arguments used to modify the language
251
  */
252
  do_action( 'pll_update_language', $args );
253
 
@@ -257,15 +252,15 @@ class PLL_Admin_Model extends PLL_Model {
257
  }
258
 
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
  *
266
- * @param array $args
267
- * @param object $lang optional the language currently updated, the language is created if not set
268
- * @return bool true if success / false if failed
269
  */
270
  protected function validate_lang( $args, $lang = null ) {
271
  $errors = new WP_Error();
@@ -282,7 +277,7 @@ class PLL_Admin_Model extends PLL_Model {
282
 
283
  // Validate slug is unique
284
  foreach ( $this->get_languages_list() as $language ) {
285
- if ( $language->slug === $args['slug'] && ( null === $lang || ( isset( $lang ) && $lang->term_id != $language->term_id ) ) ) {
286
  $errors->add( 'pll_non_unique_slug', __( 'The language code must be unique', 'polylang' ) );
287
  }
288
  }
@@ -310,21 +305,27 @@ class PLL_Admin_Model extends PLL_Model {
310
  }
311
 
312
  /**
313
- * Used to set the language of posts or terms in mass
314
  *
315
  * @since 1.2
316
  *
317
- * @param string $type either 'post' or 'term'
318
- * @param array $ids array of post ids or term ids
319
- * @param object|string $lang object or slug
 
320
  */
321
  public function set_language_in_mass( $type, $ids, $lang ) {
322
  global $wpdb;
323
 
324
- $ids = array_map( 'intval', $ids );
325
- $lang = $this->get_language( $lang );
 
 
 
 
326
  $tt_id = 'term' === $type ? $lang->tl_term_taxonomy_id : $lang->term_taxonomy_id;
327
  $values = array();
 
328
 
329
  foreach ( $ids as $id ) {
330
  $values[] = $wpdb->prepare( '( %d, %d )', $id, $tt_id );
@@ -353,12 +354,13 @@ class PLL_Admin_Model extends PLL_Model {
353
  }
354
 
355
  /**
356
- * Used to create a translations groups in mass
357
  *
358
  * @since 1.6.3
359
  *
360
- * @param string $type either 'post' or 'term'
361
- * @param array $translations array of translations arrays
 
362
  */
363
  public function set_translation_in_mass( $type, $translations ) {
364
  global $wpdb;
@@ -405,13 +407,15 @@ class PLL_Admin_Model extends PLL_Model {
405
  $terms = get_terms( $taxonomy, array( 'hide_empty' => false ) );
406
  $trs = array();
407
 
408
- // Prepare objects relationships
409
- foreach ( $terms as $term ) {
410
- $t = maybe_unserialize( $term->description );
411
- if ( in_array( $t, $translations ) ) {
412
- foreach ( $t as $object_id ) {
413
- if ( ! empty( $object_id ) ) {
414
- $trs[] = $wpdb->prepare( '( %d, %d )', $object_id, $term->term_taxonomy_id );
 
 
415
  }
416
  }
417
  }
@@ -428,25 +432,30 @@ class PLL_Admin_Model extends PLL_Model {
428
  }
429
 
430
  /**
431
- * Returns untranslated posts and terms ids ( used in settings )
432
  *
433
  * @since 0.9
434
- * @since 2.2.6 Add the $limit argument
 
 
 
 
435
  *
436
- * @param in $limit Max number of posts or terms to return. Defaults to -1 (no limit).
437
- * @return array Array made of an array of post ids and an array of term ids
 
438
  */
439
  public function get_objects_with_no_lang( $limit = -1 ) {
440
  global $wpdb;
441
 
442
  /**
443
- * Filters the max number of posts or terms to return when searching objects with no language
444
  * This filter can be used to decrease the memory usage in case the number of objects
445
  * without language is too big. Using a negative value is equivalent to have no limit.
446
  *
447
  * @since 2.2.6
448
  *
449
- * @param int $limit Max number of posts or terms to retrieve from the database
450
  */
451
  $limit = (int) apply_filters( 'get_objects_with_no_lang_limit', $limit );
452
 
@@ -484,22 +493,24 @@ class PLL_Admin_Model extends PLL_Model {
484
  // PHPCS:enable
485
 
486
  /**
487
- * Filter the list of untranslated posts ids and terms ids
488
  *
489
  * @since 0.9
490
  *
491
- * @param bool|array $objects false if no ids found, list of post and/or term ids otherwise
492
  */
493
  return apply_filters( 'pll_get_objects_with_no_lang', empty( $posts ) && empty( $terms ) ? false : array( 'posts' => $posts, 'terms' => $terms ) );
494
  }
495
 
496
  /**
497
- * Used to delete translations or update the translations when a language slug has been modified in settings
 
498
  *
499
  * @since 0.5
500
  *
501
- * @param string $old_slug the old language slug
502
- * @param string $new_slug optional, the new language slug, if not set it means the correspondent has been deleted
 
503
  */
504
  public function update_translations( $old_slug, $new_slug = '' ) {
505
  global $wpdb;
@@ -510,24 +521,26 @@ class PLL_Admin_Model extends PLL_Model {
510
  $dt = array();
511
  $ut = array();
512
 
513
- foreach ( $terms as $term ) {
514
- $term_ids[ $term->taxonomy ][] = $term->term_id;
515
- $tr = maybe_unserialize( $term->description );
516
- if ( ! empty( $tr[ $old_slug ] ) ) {
517
- if ( $new_slug ) {
518
- $tr[ $new_slug ] = $tr[ $old_slug ]; // Suppress this for delete
519
- } else {
520
- $dr['id'][] = (int) $tr[ $old_slug ];
521
- $dr['tt'][] = (int) $term->term_taxonomy_id;
522
- }
523
- unset( $tr[ $old_slug ] );
524
-
525
- if ( empty( $tr ) || 1 == count( $tr ) ) {
526
- $dt['t'][] = (int) $term->term_id;
527
- $dt['tt'][] = (int) $term->term_taxonomy_id;
528
- } else {
529
- $ut['case'][] = $wpdb->prepare( 'WHEN %d THEN %s', $term->term_id, maybe_serialize( $tr ) );
530
- $ut['in'][] = (int) $term->term_id;
 
 
531
  }
532
  }
533
  }
@@ -569,11 +582,12 @@ class PLL_Admin_Model extends PLL_Model {
569
 
570
  /**
571
  * Updates the default language
572
- * taking care to update the default category & the nav menu locations
573
  *
574
  * @since 1.8
575
  *
576
- * @param string $slug new language slug
 
577
  */
578
  public function update_default_lang( $slug ) {
579
  // The nav menus stored in theme locations should be in the default language
4
  */
5
 
6
  /**
7
+ * Extends the PLL_Model class with methods needed only in Polylang settings pages.
8
  *
9
  * @since 1.2
10
  */
12
 
13
  /**
14
  * Adds a new language
15
+ * and creates a default category for this language.
 
 
 
 
 
 
 
 
 
 
 
16
  *
17
  * @since 1.2
18
  *
19
+ * @param array $args {
20
+ * @type string $name Language name ( used only for display ).
21
+ * @type string $slug Language code ( ideally 2-letters ISO 639-1 language code ).
22
+ * @type string $locale WordPress locale. If something wrong is used for the locale, the .mo files will not be loaded...
23
+ * @type int $rtl 1 if rtl language, 0 otherwise.
24
+ * @type int $term_group Language order when displayed.
25
+ * @type string $no_default_cat Optional, if set, no default category will be created for this language.
26
+ * @type string $flag Optional, country code, @see flags.php.
27
+ * }
28
+ * @return WP_Error|true true if success / WP_Error if failed.
29
  */
30
  public function add_language( $args ) {
31
  $errors = $this->validate_lang( $args );
64
  $mo->export_to_db( $this->get_language( $args['slug'] ) );
65
 
66
  /**
67
+ * Fires when a language is added.
68
  *
69
  * @since 1.9
70
  *
71
+ * @param array $args Arguments used to create the language. @see PLL_Admin_Model::add_language().
72
  */
73
  do_action( 'pll_add_language', $args );
74
 
78
  }
79
 
80
  /**
81
+ * Delete a language.
82
  *
83
  * @since 1.2
84
  *
85
+ * @param int $lang_id Language term_id.
86
+ * @return bool
87
  */
88
  public function delete_language( $lang_id ) {
89
  $lang = $this->get_language( (int) $lang_id );
140
 
141
  // Delete the string translations
142
  $post = wpcom_vip_get_page_by_title( 'polylang_mo_' . $lang->term_id, OBJECT, 'polylang_mo' );
143
+ if ( $post instanceof WP_Post ) {
144
  wp_delete_post( $post->ID );
145
  }
146
 
160
  }
161
 
162
  /**
163
+ * Updates language properties.
 
 
 
 
 
 
 
 
 
 
 
164
  *
165
  * @since 1.2
166
  *
167
+ * @param array $args {
168
+ * @type int $lang_id Id of the language to modify.
169
+ * @type string $name Language name ( used only for display ).
170
+ * @type string $slug Language code ( ideally 2-letters ISO 639-1 language code ).
171
+ * @type string $locale WordPress locale. If something wrong is used for the locale, the .mo files will not be loaded...
172
+ * @type int $rtl 1 if rtl language, 0 otherwise.
173
+ * @type int $term_group Language order when displayed.
174
+ * @type string $flag Optional, country code, @see flags.php.
175
+ * }
176
+ * @return WP_Error|true true if success / WP_Error if failed.
177
  */
178
  public function update_language( $args ) {
179
  $lang = $this->get_language( (int) $args['lang_id'] );
238
  wp_update_term( (int) $lang->tl_term_id, 'term_language', array( 'slug' => 'pll_' . $slug, 'name' => $args['name'] ) );
239
 
240
  /**
241
+ * Fires when a language is updated.
242
  *
243
  * @since 1.9
244
  *
245
+ * @param array $args Arguments used to modify the language. @see PLL_Admin_Model::update_language().
246
  */
247
  do_action( 'pll_update_language', $args );
248
 
252
  }
253
 
254
  /**
255
+ * Validates data entered when creating or updating a language.
256
  *
257
+ * @see PLL_Admin_Model::add_language().
258
  *
259
  * @since 0.4
260
  *
261
+ * @param array $args Parameters of {@see PLL_Admin_Model::add_language() or @see PLL_Admin_Model::update_language()}.
262
+ * @param PLL_Language $lang Optional the language currently updated, the language is created if not set.
263
+ * @return WP_Error
264
  */
265
  protected function validate_lang( $args, $lang = null ) {
266
  $errors = new WP_Error();
277
 
278
  // Validate slug is unique
279
  foreach ( $this->get_languages_list() as $language ) {
280
+ if ( $language->slug === $args['slug'] && ( null === $lang || $lang->term_id !== $language->term_id ) ) {
281
  $errors->add( 'pll_non_unique_slug', __( 'The language code must be unique', 'polylang' ) );
282
  }
283
  }
305
  }
306
 
307
  /**
308
+ * Assigns a language to posts or terms in mass.
309
  *
310
  * @since 1.2
311
  *
312
+ * @param string $type Either 'post' or 'term'.
313
+ * @param int[] $ids Array of post ids or term ids.
314
+ * @param PLL_Language|string $lang Language to assign to the posts or terms.
315
+ * @return void
316
  */
317
  public function set_language_in_mass( $type, $ids, $lang ) {
318
  global $wpdb;
319
 
320
+ $lang = $this->get_language( $lang );
321
+
322
+ if ( empty( $lang ) ) {
323
+ return;
324
+ }
325
+
326
  $tt_id = 'term' === $type ? $lang->tl_term_taxonomy_id : $lang->term_taxonomy_id;
327
  $values = array();
328
+ $ids = array_map( 'intval', $ids );
329
 
330
  foreach ( $ids as $id ) {
331
  $values[] = $wpdb->prepare( '( %d, %d )', $id, $tt_id );
354
  }
355
 
356
  /**
357
+ * Creates translations groups in mass.
358
  *
359
  * @since 1.6.3
360
  *
361
+ * @param string $type Either 'post' or 'term'
362
+ * @param array $translations Array of translations arrays.
363
+ * @return void
364
  */
365
  public function set_translation_in_mass( $type, $translations ) {
366
  global $wpdb;
407
  $terms = get_terms( $taxonomy, array( 'hide_empty' => false ) );
408
  $trs = array();
409
 
410
+ // Prepare objects relationships.
411
+ if ( is_array( $terms ) ) {
412
+ foreach ( $terms as $term ) {
413
+ $t = maybe_unserialize( $term->description );
414
+ if ( in_array( $t, $translations ) ) {
415
+ foreach ( $t as $object_id ) {
416
+ if ( ! empty( $object_id ) ) {
417
+ $trs[] = $wpdb->prepare( '( %d, %d )', $object_id, $term->term_taxonomy_id );
418
+ }
419
  }
420
  }
421
  }
432
  }
433
 
434
  /**
435
+ * Returns untranslated posts and terms ids ( used in settings ).
436
  *
437
  * @since 0.9
438
+ * @since 2.2.6 Add the $limit argument.
439
+ *
440
+ * @param int $limit Max number of posts or terms to return. Defaults to -1 (no limit).
441
+ * @return array {
442
+ * Objects without language.
443
  *
444
+ * @type int[] $posts Array of post ids.
445
+ * @type int[] $terms Array of term ids.
446
+ * }
447
  */
448
  public function get_objects_with_no_lang( $limit = -1 ) {
449
  global $wpdb;
450
 
451
  /**
452
+ * Filters the max number of posts or terms to return when searching objects with no language.
453
  * This filter can be used to decrease the memory usage in case the number of objects
454
  * without language is too big. Using a negative value is equivalent to have no limit.
455
  *
456
  * @since 2.2.6
457
  *
458
+ * @param int $limit Max number of posts or terms to retrieve from the database.
459
  */
460
  $limit = (int) apply_filters( 'get_objects_with_no_lang_limit', $limit );
461
 
493
  // PHPCS:enable
494
 
495
  /**
496
+ * Filters the list of untranslated posts ids and terms ids
497
  *
498
  * @since 0.9
499
  *
500
+ * @param array|false $objects false if no ids found, list of post and/or term ids otherwise.
501
  */
502
  return apply_filters( 'pll_get_objects_with_no_lang', empty( $posts ) && empty( $terms ) ? false : array( 'posts' => $posts, 'terms' => $terms ) );
503
  }
504
 
505
  /**
506
+ * Updates the translations when a language slug has been modified in settings
507
+ * or deletes them when a language is removed.
508
  *
509
  * @since 0.5
510
  *
511
+ * @param string $old_slug The old language slug.
512
+ * @param string $new_slug Optional, the new language slug, if not set it means that the language has been deleted.
513
+ * @return void
514
  */
515
  public function update_translations( $old_slug, $new_slug = '' ) {
516
  global $wpdb;
521
  $dt = array();
522
  $ut = array();
523
 
524
+ if ( is_array( $terms ) ) {
525
+ foreach ( $terms as $term ) {
526
+ $term_ids[ $term->taxonomy ][] = $term->term_id;
527
+ $tr = maybe_unserialize( $term->description );
528
+ if ( ! empty( $tr[ $old_slug ] ) ) {
529
+ if ( $new_slug ) {
530
+ $tr[ $new_slug ] = $tr[ $old_slug ]; // Suppress this for delete
531
+ } else {
532
+ $dr['id'][] = (int) $tr[ $old_slug ];
533
+ $dr['tt'][] = (int) $term->term_taxonomy_id;
534
+ }
535
+ unset( $tr[ $old_slug ] );
536
+
537
+ if ( empty( $tr ) || 1 == count( $tr ) ) {
538
+ $dt['t'][] = (int) $term->term_id;
539
+ $dt['tt'][] = (int) $term->term_taxonomy_id;
540
+ } else {
541
+ $ut['case'][] = $wpdb->prepare( 'WHEN %d THEN %s', $term->term_id, maybe_serialize( $tr ) );
542
+ $ut['in'][] = (int) $term->term_id;
543
+ }
544
  }
545
  }
546
  }
582
 
583
  /**
584
  * Updates the default language
585
+ * taking care to update the default category & the nav menu locations.
586
  *
587
  * @since 1.8
588
  *
589
+ * @param string $slug New language slug.
590
+ * @return void
591
  */
592
  public function update_default_lang( $slug ) {
593
  // The nav menus stored in theme locations should be in the default language
admin/admin-nav-menu.php CHANGED
@@ -33,6 +33,8 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
33
  * adds the language switcher metabox and create new nav menu locations
34
  *
35
  * @since 1.1
 
 
36
  */
37
  public function admin_init() {
38
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
@@ -55,6 +57,8 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
55
  * Thanks to John Morris for his very interesting post http://www.johnmorrisonline.com/how-to-add-a-fully-functional-custom-meta-box-to-wordpress-navigation-menus/
56
  *
57
  * @since 1.1
 
 
58
  */
59
  public function lang_switch() {
60
  global $_nav_menu_placeholder, $nav_menu_selected_id;
@@ -87,15 +91,17 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
87
  * Prepares javascript to modify the language switcher menu item
88
  *
89
  * @since 1.1
 
 
90
  */
91
  public function admin_enqueue_scripts() {
92
  $screen = get_current_screen();
93
- if ( 'nav-menus' != $screen->base ) {
94
  return;
95
  }
96
 
97
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
98
- wp_enqueue_script( 'pll_nav_menu', plugins_url( '/js/nav-menu' . $suffix . '.js', POLYLANG_FILE ), array( 'jquery' ), POLYLANG_VERSION );
99
 
100
  $data = array(
101
  'strings' => PLL_Switcher::get_switcher_options( 'menu', 'string' ), // The strings for the options
@@ -130,6 +136,7 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
130
  *
131
  * @param int $menu_id not used
132
  * @param int $menu_item_db_id
 
133
  */
134
  public function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0 ) {
135
  if ( empty( $_POST['menu-item-url'][ $menu_item_db_id ] ) || '#pll_switcher' !== $_POST['menu-item-url'][ $menu_item_db_id ] ) { // phpcs:ignore WordPress.Security.NonceVerification
@@ -179,12 +186,12 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
179
  }
180
 
181
  /**
182
- * Assign menu languages and translations based on ( temporary ) locations
183
  *
184
  * @since 1.1
185
  *
186
- * @param array $mods theme mods
187
- * @return unmodified $mods
188
  */
189
  public function pre_update_option_theme_mods( $mods ) {
190
  if ( current_user_can( 'edit_theme_options' ) && isset( $mods['nav_menu_locations'] ) ) {
@@ -253,6 +260,7 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
253
  * @since 1.7.3
254
  *
255
  * @param int $term_id nav menu id
 
256
  */
257
  public function delete_nav_menu( $term_id ) {
258
  if ( isset( $this->options['nav_menus'] ) ) {
33
  * adds the language switcher metabox and create new nav menu locations
34
  *
35
  * @since 1.1
36
+ *
37
+ * @return void
38
  */
39
  public function admin_init() {
40
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
57
  * Thanks to John Morris for his very interesting post http://www.johnmorrisonline.com/how-to-add-a-fully-functional-custom-meta-box-to-wordpress-navigation-menus/
58
  *
59
  * @since 1.1
60
+ *
61
+ * @return void
62
  */
63
  public function lang_switch() {
64
  global $_nav_menu_placeholder, $nav_menu_selected_id;
91
  * Prepares javascript to modify the language switcher menu item
92
  *
93
  * @since 1.1
94
+ *
95
+ * @return void
96
  */
97
  public function admin_enqueue_scripts() {
98
  $screen = get_current_screen();
99
+ if ( empty( $screen ) || 'nav-menus' !== $screen->base ) {
100
  return;
101
  }
102
 
103
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
104
+ wp_enqueue_script( 'pll_nav_menu', plugins_url( '/js/build/nav-menu' . $suffix . '.js', POLYLANG_BASENAME ), array( 'jquery' ), POLYLANG_VERSION );
105
 
106
  $data = array(
107
  'strings' => PLL_Switcher::get_switcher_options( 'menu', 'string' ), // The strings for the options
136
  *
137
  * @param int $menu_id not used
138
  * @param int $menu_item_db_id
139
+ * @return void
140
  */
141
  public function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0 ) {
142
  if ( empty( $_POST['menu-item-url'][ $menu_item_db_id ] ) || '#pll_switcher' !== $_POST['menu-item-url'][ $menu_item_db_id ] ) { // phpcs:ignore WordPress.Security.NonceVerification
186
  }
187
 
188
  /**
189
+ * Assign menu languages and translations based on ( temporary ) locations.
190
  *
191
  * @since 1.1
192
  *
193
+ * @param mixed $mods Theme mods.
194
+ * @return mixed
195
  */
196
  public function pre_update_option_theme_mods( $mods ) {
197
  if ( current_user_can( 'edit_theme_options' ) && isset( $mods['nav_menu_locations'] ) ) {
260
  * @since 1.7.3
261
  *
262
  * @param int $term_id nav menu id
263
+ * @return void
264
  */
265
  public function delete_nav_menu( $term_id ) {
266
  if ( isset( $this->options['nav_menus'] ) ) {
admin/admin-notices.php CHANGED
@@ -12,6 +12,18 @@
12
  * @since 2.7 Dismissed notices are stored in an option instead of a user meta
13
  */
14
  class PLL_Admin_Notices {
 
 
 
 
 
 
 
 
 
 
 
 
15
  private static $notices = array();
16
 
17
  /**
@@ -36,17 +48,18 @@ class PLL_Admin_Notices {
36
  *
37
  * @param string $name Notice name
38
  * @param string $html Content of the notice
 
39
  */
40
  public static function add_notice( $name, $html ) {
41
  self::$notices[ $name ] = $html;
42
  }
43
 
44
  /**
45
- * Get custom notices
46
  *
47
  * @since 2.3.9
48
  *
49
- * @return array
50
  */
51
  public static function get_notices() {
52
  return self::$notices;
@@ -89,6 +102,11 @@ class PLL_Admin_Notices {
89
  */
90
  protected function can_display_notice( $notice ) {
91
  $screen = get_current_screen();
 
 
 
 
 
92
  $screen_id = sanitize_title( __( 'Languages', 'polylang' ) );
93
 
94
  /**
@@ -121,6 +139,7 @@ class PLL_Admin_Notices {
121
  * @since 2.3.9
122
  *
123
  * @param string $notice
 
124
  */
125
  public static function dismiss( $notice ) {
126
  $dismissed = get_option( 'pll_dismissed_notices', array() );
@@ -135,6 +154,8 @@ class PLL_Admin_Notices {
135
  * Handle a click on the dismiss button
136
  *
137
  * @since 2.3.9
 
 
138
  */
139
  public function hide_notice() {
140
  if ( isset( $_GET['pll-hide-notice'], $_GET['_pll_notice_nonce'] ) ) {
@@ -150,6 +171,8 @@ class PLL_Admin_Notices {
150
  * Displays notices
151
  *
152
  * @since 2.3.9
 
 
153
  */
154
  public function display_notices() {
155
  if ( current_user_can( 'manage_options' ) ) {
@@ -184,6 +207,7 @@ class PLL_Admin_Notices {
184
  * @since 2.3.9
185
  *
186
  * @param string $name Notice name
 
187
  */
188
  public function dismiss_button( $name ) {
189
  printf(
@@ -198,6 +222,8 @@ class PLL_Admin_Notices {
198
  * Displays a notice if WooCommerce is activated without Polylang for WooCommerce
199
  *
200
  * @since 2.3.9
 
 
201
  */
202
  private function pllwc_notice() {
203
  ?>
@@ -221,6 +247,8 @@ class PLL_Admin_Notices {
221
  * Displays a notice asking for a review
222
  *
223
  * @since 2.3.9
 
 
224
  */
225
  private function review_notice() {
226
  ?>
12
  * @since 2.7 Dismissed notices are stored in an option instead of a user meta
13
  */
14
  class PLL_Admin_Notices {
15
+ /**
16
+ * Stores the plugin options.
17
+ *
18
+ * @var array
19
+ */
20
+ protected $options;
21
+
22
+ /**
23
+ * Stores custom notices.
24
+ *
25
+ * @var string[]
26
+ */
27
  private static $notices = array();
28
 
29
  /**
48
  *
49
  * @param string $name Notice name
50
  * @param string $html Content of the notice
51
+ * @return void
52
  */
53
  public static function add_notice( $name, $html ) {
54
  self::$notices[ $name ] = $html;
55
  }
56
 
57
  /**
58
+ * Get custom notices.
59
  *
60
  * @since 2.3.9
61
  *
62
+ * @return string[]
63
  */
64
  public static function get_notices() {
65
  return self::$notices;
102
  */
103
  protected function can_display_notice( $notice ) {
104
  $screen = get_current_screen();
105
+
106
+ if ( empty( $screen ) ) {
107
+ return false;
108
+ }
109
+
110
  $screen_id = sanitize_title( __( 'Languages', 'polylang' ) );
111
 
112
  /**
139
  * @since 2.3.9
140
  *
141
  * @param string $notice
142
+ * @return void
143
  */
144
  public static function dismiss( $notice ) {
145
  $dismissed = get_option( 'pll_dismissed_notices', array() );
154
  * Handle a click on the dismiss button
155
  *
156
  * @since 2.3.9
157
+ *
158
+ * @return void
159
  */
160
  public function hide_notice() {
161
  if ( isset( $_GET['pll-hide-notice'], $_GET['_pll_notice_nonce'] ) ) {
171
  * Displays notices
172
  *
173
  * @since 2.3.9
174
+ *
175
+ * @return void
176
  */
177
  public function display_notices() {
178
  if ( current_user_can( 'manage_options' ) ) {
207
  * @since 2.3.9
208
  *
209
  * @param string $name Notice name
210
+ * @return void
211
  */
212
  public function dismiss_button( $name ) {
213
  printf(
222
  * Displays a notice if WooCommerce is activated without Polylang for WooCommerce
223
  *
224
  * @since 2.3.9
225
+ *
226
+ * @return void
227
  */
228
  private function pllwc_notice() {
229
  ?>
247
  * Displays a notice asking for a review
248
  *
249
  * @since 2.3.9
250
+ *
251
+ * @return void
252
  */
253
  private function review_notice() {
254
  ?>
admin/admin-static-pages.php CHANGED
@@ -9,6 +9,9 @@
9
  * @since 1.8
10
  */
11
  class PLL_Admin_Static_Pages extends PLL_Static_Pages {
 
 
 
12
  protected $links;
13
 
14
  /**
@@ -33,10 +36,6 @@ class PLL_Admin_Static_Pages extends PLL_Static_Pages {
33
  // Refresh language cache when a static front page has been translated
34
  add_action( 'pll_save_post', array( $this, 'pll_save_post' ), 10, 3 );
35
 
36
- // Checks if chosen page on front is translated
37
- add_filter( 'pre_update_option_page_on_front', array( $this, 'update_page_on_front' ), 10, 2 );
38
- add_filter( 'customize_validate_page_on_front', array( $this, 'customize_validate_page_on_front' ), 10, 2 );
39
-
40
  // Prevents WP resetting the option
41
  add_filter( 'pre_update_option_show_on_front', array( $this, 'update_show_on_front' ), 10, 2 );
42
 
@@ -65,13 +64,14 @@ class PLL_Admin_Static_Pages extends PLL_Static_Pages {
65
  }
66
 
67
  /**
68
- * Removes the editor for translations of the pages for posts
69
- * Removes the page template select dropdown in page attributes metabox too
70
  *
71
  * @since 2.2.2
72
  *
73
- * @param string $post_type Current post type
74
- * @param object $post Current post
 
75
  */
76
  public function add_meta_boxes( $post_type, $post ) {
77
  if ( 'page' === $post_type ) {
@@ -85,13 +85,13 @@ class PLL_Admin_Static_Pages extends PLL_Static_Pages {
85
  }
86
 
87
  /**
88
- * Add post state for translations of the front page and posts page
89
  *
90
  * @since 1.8
91
  *
92
- * @param array $post_states An array of post display states.
93
- * @param object $post The current post object.
94
- * @return array
95
  */
96
  public function display_post_states( $post_states, $post ) {
97
  if ( in_array( $post->ID, $this->model->get_languages_list( array( 'fields' => 'page_on_front' ) ) ) ) {
@@ -106,13 +106,14 @@ class PLL_Admin_Static_Pages extends PLL_Static_Pages {
106
  }
107
 
108
  /**
109
- * Refresh language cache when a static front page has been translated
110
  *
111
  * @since 1.8
112
  *
113
- * @param int $post_id Not used
114
- * @param object $post Not used
115
- * @param array $translations
 
116
  */
117
  public function pll_save_post( $post_id, $post, $translations ) {
118
  if ( in_array( $this->page_on_front, $translations ) ) {
@@ -120,63 +121,6 @@ class PLL_Admin_Static_Pages extends PLL_Static_Pages {
120
  }
121
  }
122
 
123
- /**
124
- * Checks if a page is translated in all languages
125
- *
126
- * @since 2.2
127
- *
128
- * @param int $page_id
129
- * @return bool
130
- */
131
- protected function is_page_translated( $page_id ) {
132
- if ( $page_id ) {
133
- $translations = count( $this->model->post->get_translations( $page_id ) );
134
- $languages = count( $this->model->get_languages_list() );
135
-
136
- if ( $languages > 1 && $translations != $languages ) {
137
- return false;
138
- }
139
- }
140
-
141
- return true;
142
- }
143
-
144
- /**
145
- * Prevents choosing an untranslated static front page
146
- * Displays an error message
147
- *
148
- * @since 1.6
149
- *
150
- * @param int $page_id New page on front page id
151
- * @param int $old_id Old page on front page_id
152
- * @return int
153
- */
154
- public function update_page_on_front( $page_id, $old_id ) {
155
- if ( ! $this->is_page_translated( $page_id ) ) {
156
- $page_id = $old_id;
157
- add_settings_error( 'reading', 'pll_page_on_front_error', __( 'The chosen static front page must be translated in all languages.', 'polylang' ) );
158
- }
159
-
160
- return $page_id;
161
- }
162
-
163
- /**
164
- * Displays an error message in the customizer when choosing an untranslated static front page
165
- *
166
- * @since 2.2
167
- *
168
- * @param object $validity WP_Error object
169
- * @param int $page_id New page on front page id
170
- * @return object
171
- */
172
- public function customize_validate_page_on_front( $validity, $page_id ) {
173
- if ( ! $this->is_page_translated( $page_id ) ) {
174
- return new WP_Error( 'pll_page_on_front_error', __( 'The chosen static front page must be translated in all languages.', 'polylang' ) );
175
- }
176
-
177
- return $validity;
178
- }
179
-
180
  /**
181
  * Prevents WP resetting the option if the admin language filter is active for a language with no pages
182
  *
@@ -199,11 +143,13 @@ class PLL_Admin_Static_Pages extends PLL_Static_Pages {
199
  * The notice is not dismissible and displayed on the Languages pages and the list of pages.
200
  *
201
  * @since 2.6
 
 
202
  */
203
  public function notice_must_translate() {
204
  $screen = get_current_screen();
205
 
206
- if ( 'toplevel_page_mlang' === $screen->id || 'edit-page' === $screen->id ) {
207
  $message = $this->get_must_translate_message();
208
 
209
  if ( ! empty( $message ) ) {
9
  * @since 1.8
10
  */
11
  class PLL_Admin_Static_Pages extends PLL_Static_Pages {
12
+ /**
13
+ * @var PLL_Admin_Links
14
+ */
15
  protected $links;
16
 
17
  /**
36
  // Refresh language cache when a static front page has been translated
37
  add_action( 'pll_save_post', array( $this, 'pll_save_post' ), 10, 3 );
38
 
 
 
 
 
39
  // Prevents WP resetting the option
40
  add_filter( 'pre_update_option_show_on_front', array( $this, 'update_show_on_front' ), 10, 2 );
41
 
64
  }
65
 
66
  /**
67
+ * Removes the editor for the translations of the pages for posts.
68
+ * Removes the page template select dropdown in page attributes metabox too.
69
  *
70
  * @since 2.2.2
71
  *
72
+ * @param string $post_type Current post type.
73
+ * @param WP_Post $post Current post.
74
+ * @return void
75
  */
76
  public function add_meta_boxes( $post_type, $post ) {
77
  if ( 'page' === $post_type ) {
85
  }
86
 
87
  /**
88
+ * Adds post state for translations of the front page and posts page.
89
  *
90
  * @since 1.8
91
  *
92
+ * @param string[] $post_states An array of post display states.
93
+ * @param WP_Post $post The current post object.
94
+ * @return string[]
95
  */
96
  public function display_post_states( $post_states, $post ) {
97
  if ( in_array( $post->ID, $this->model->get_languages_list( array( 'fields' => 'page_on_front' ) ) ) ) {
106
  }
107
 
108
  /**
109
+ * Refreshes the language cache when a static front page has been translated.
110
  *
111
  * @since 1.8
112
  *
113
+ * @param int $post_id Not used.
114
+ * @param WP_Post $post Not used.
115
+ * @param int[] $translations Translations of the post being saved.
116
+ * @return void
117
  */
118
  public function pll_save_post( $post_id, $post, $translations ) {
119
  if ( in_array( $this->page_on_front, $translations ) ) {
121
  }
122
  }
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  /**
125
  * Prevents WP resetting the option if the admin language filter is active for a language with no pages
126
  *
143
  * The notice is not dismissible and displayed on the Languages pages and the list of pages.
144
  *
145
  * @since 2.6
146
+ *
147
+ * @return void
148
  */
149
  public function notice_must_translate() {
150
  $screen = get_current_screen();
151
 
152
+ if ( ! empty( $screen ) && ( 'toplevel_page_mlang' === $screen->id || 'edit-page' === $screen->id ) ) {
153
  $message = $this->get_must_translate_message();
154
 
155
  if ( ! empty( $message ) ) {
admin/admin-strings.php CHANGED
@@ -9,13 +9,31 @@
9
  * @since 1.6
10
  */
11
  class PLL_Admin_Strings {
12
- protected static $strings = array(); // strings to translate
13
- protected static $default_strings; // default strings to register
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  /**
16
  * Add filters
17
  *
18
  * @since 1.6
 
 
19
  */
20
  public static function init() {
21
  // default strings translations sanitization
@@ -31,14 +49,9 @@ class PLL_Admin_Strings {
31
  * @param string $string The string to register
32
  * @param string $context Optional, the group in which the string is registered, defaults to 'polylang'
33
  * @param bool $multiline Optional, whether the string table should display a multiline textarea or a single line input, defaults to single line
 
34
  */
35
  public static function register_string( $name, $string, $context = 'Polylang', $multiline = false ) {
36
- // Backward compatibility with Polylang older than 1.1
37
- if ( is_bool( $context ) ) {
38
- $multiline = $context;
39
- $context = 'Polylang';
40
- }
41
-
42
  if ( $string && is_scalar( $string ) ) {
43
  self::$strings[ md5( $string ) ] = compact( 'name', 'string', 'context', 'multiline' );
44
  }
9
  * @since 1.6
10
  */
11
  class PLL_Admin_Strings {
12
+ /**
13
+ * Stores the strings to translate.
14
+ *
15
+ * @var array {
16
+ * @type string $name A unique name for the string.
17
+ * @type string $string The actual string to translate.
18
+ * @type string $context The group in which the string is registered.
19
+ * @type bool $multiline Whether the string table should display a multiline textarea or a single line input.
20
+ * }
21
+ */
22
+ protected static $strings = array();
23
+
24
+ /**
25
+ * The strings to register by default.
26
+ *
27
+ * @var string[]
28
+ */
29
+ protected static $default_strings;
30
 
31
  /**
32
  * Add filters
33
  *
34
  * @since 1.6
35
+ *
36
+ * @return void
37
  */
38
  public static function init() {
39
  // default strings translations sanitization
49
  * @param string $string The string to register
50
  * @param string $context Optional, the group in which the string is registered, defaults to 'polylang'
51
  * @param bool $multiline Optional, whether the string table should display a multiline textarea or a single line input, defaults to single line
52
+ * @return void
53
  */
54
  public static function register_string( $name, $string, $context = 'Polylang', $multiline = false ) {
 
 
 
 
 
 
55
  if ( $string && is_scalar( $string ) ) {
56
  self::$strings[ md5( $string ) ] = compact( 'name', 'string', 'context', 'multiline' );
57
  }
admin/admin.php CHANGED
@@ -4,79 +4,37 @@
4
  */
5
 
6
  /**
7
- * Admin side controller
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
@@ -84,12 +42,31 @@ class PLL_Admin extends PLL_Admin_Base {
84
  public $filters_sanitization;
85
 
86
  /**
87
- * Loads the polylang text domain
88
- * Setups filters and action needed on all admin pages and on plugins page
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  *
90
  * @since 1.2
91
  *
92
- * @param object $links_model
93
  */
94
  public function __construct( &$links_model ) {
95
  parent::__construct( $links_model );
@@ -116,12 +93,12 @@ class PLL_Admin extends PLL_Admin_Base {
116
  }
117
 
118
  /**
119
- * Adds a 'settings' link in the plugins table
120
  *
121
  * @since 0.1
122
  *
123
- * @param array $links list of links associated to the plugin
124
- * @return array modified list of links
125
  */
126
  public function plugin_action_links( $links ) {
127
  array_unshift( $links, '<a href="admin.php?page=mlang">' . __( 'Settings', 'polylang' ) . '</a>' );
@@ -135,6 +112,7 @@ class PLL_Admin extends PLL_Admin_Base {
135
  *
136
  * @param array $plugin_data Not used
137
  * @param object $r Plugin update data
 
138
  */
139
  public function plugin_update_message( $plugin_data, $r ) {
140
  if ( isset( $r->upgrade_notice ) ) {
@@ -147,9 +125,12 @@ class PLL_Admin extends PLL_Admin_Base {
147
  *
148
  * @since 1.2
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,6 +153,7 @@ class PLL_Admin extends PLL_Admin_Base {
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.
4
  */
5
 
6
  /**
7
+ * Main Polylang class for admin (except Polylang pages), accessible from @see PLL().
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  *
9
  * @since 1.2
10
  */
11
  class PLL_Admin extends PLL_Admin_Base {
12
  /**
 
 
13
  * @var PLL_Admin_Filters
14
  */
15
  public $filters;
16
 
17
  /**
 
 
18
  * @var PLL_Admin_Filters_Columns
19
  */
20
  public $filters_columns;
21
 
22
  /**
 
 
23
  * @var PLL_Admin_Filters_Post
24
  */
25
  public $filters_post;
26
 
27
  /**
28
+ * @var PLL_Admin_Filters_Term
 
 
29
  */
30
  public $filters_term;
31
 
32
  /**
 
 
 
 
 
 
 
 
 
33
  * @var PLL_Admin_Filters_Media
34
  */
35
  public $filters_media;
36
 
37
  /**
 
 
38
  * @since 2.9
39
  *
40
  * @var PLL_Filters_Sanitization
42
  public $filters_sanitization;
43
 
44
  /**
45
+ * @var PLL_Admin_Block_Editor
46
+ */
47
+ public $block_editor;
48
+
49
+ /**
50
+ * @var PLL_Admin_Classic_Editor
51
+ */
52
+ public $classic_editor;
53
+
54
+ /**
55
+ * @var PLL_Admin_Nav_Menu
56
+ */
57
+ public $nav_menu;
58
+
59
+ /**
60
+ * @var PLL_Admin_Filters_Widgets_Options
61
+ */
62
+ public $filters_widgets;
63
+
64
+ /**
65
+ * Setups filters and action needed on all admin pages and on plugins page.
66
  *
67
  * @since 1.2
68
  *
69
+ * @param PLL_Links_Model $links_model Reference to the links model.
70
  */
71
  public function __construct( &$links_model ) {
72
  parent::__construct( $links_model );
93
  }
94
 
95
  /**
96
+ * Adds a 'settings' link for our plugin in the plugins list table.
97
  *
98
  * @since 0.1
99
  *
100
+ * @param string[] $links List of links associated to the plugin.
101
+ * @return string[] Modified list of links.
102
  */
103
  public function plugin_action_links( $links ) {
104
  array_unshift( $links, '<a href="admin.php?page=mlang">' . __( 'Settings', 'polylang' ) . '</a>' );
112
  *
113
  * @param array $plugin_data Not used
114
  * @param object $r Plugin update data
115
+ * @return void
116
  */
117
  public function plugin_update_message( $plugin_data, $r ) {
118
  if ( isset( $r->upgrade_notice ) ) {
125
  *
126
  * @since 1.2
127
  * @since 2.7 instantiate a PLL_Bulk_Translate instance.
128
+ * @return void
129
  */
130
  public function add_filters() {
131
  $this->filters_sanitization = new PLL_Filters_Sanitization( $this->get_locale_for_sanitization() );
132
+ $this->filters_widgets = new PLL_Admin_Filters_Widgets_Options( $this );
133
+
134
  // All these are separated just for convenience and maintainability
135
  $classes = array( 'Filters', 'Filters_Columns', 'Filters_Post', 'Filters_Term', 'Nav_Menu', 'Classic_Editor', 'Block_Editor' );
136
 
153
  $this->$obj = new $class( $this );
154
  }
155
  }
156
+
157
  /**
158
  * Retrieve the locale according to the current language instead of the language
159
  * of the admin interface.
css/admin.css CHANGED
@@ -376,11 +376,6 @@
376
  }
377
 
378
  /* strings translations table */
379
- .stringstranslations .column-context,
380
- .stringstranslations .column-name {
381
- display: none; /* backward compatibility WP < 4.3 */
382
- }
383
-
384
  .translation label {
385
  display: block;
386
  width: 95%;
376
  }
377
 
378
  /* strings translations table */
 
 
 
 
 
379
  .translation label {
380
  display: block;
381
  width: 95%;
css/admin.min.css DELETED
@@ -1 +0,0 @@
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/build/admin.css ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* languages admin panel */
2
+ #add-lang select {
3
+ width: 95%;
4
+ }
5
+
6
+ .column-locale,
7
+ .languages .column-slug {
8
+ width : 15%
9
+ }
10
+
11
+ .column-default_lang {
12
+ width : 5%;
13
+ }
14
+
15
+ .column-term_group,
16
+ .column-flag, .column-count {
17
+ width : 10%;
18
+ }
19
+
20
+ .icon-default-lang:before {
21
+ font-family: 'dashicons';
22
+ content: "\f155";
23
+ }
24
+ .pll-icon:before{
25
+ display: inline-block;
26
+ text-align: left;
27
+ width: 15px;
28
+ }
29
+ .pll-circle:before{
30
+ content: "\25cf";
31
+ }
32
+
33
+ .form-field input[type="radio"] {
34
+ width: auto;
35
+ margin-right: 2px;
36
+ }
37
+
38
+ /* about Polylang metabox */
39
+ #pll-about-box p,
40
+ #pll-recommended p {
41
+ text-align: justify;
42
+ }
43
+
44
+ #pll-about-box input {
45
+ margin: 0;
46
+ padding: 0;
47
+ float: right;
48
+ }
49
+
50
+ /* strings translation table */
51
+ .stringstranslations .column-name,
52
+ .stringstranslations .column-context {
53
+ width: 10%;
54
+ }
55
+
56
+ .stringstranslations .column-string {
57
+ width: 33%;
58
+ }
59
+
60
+ .translation label {
61
+ display: inline-block;
62
+ width: 23%;
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 */
85
+ .pll-settings {
86
+ margin-top: 20px;
87
+ }
88
+
89
+ .pll-settings .plugin-title {
90
+ width: 25%;
91
+ }
92
+
93
+ #wpbody-content .pll-settings .pll-configure tr {
94
+ display: table-row;
95
+ }
96
+
97
+ #wpbody-content .pll-settings .pll-configure td {
98
+ display: table-cell;
99
+ }
100
+
101
+ #wpbody-content .pll-settings .pll-configure > td {
102
+ padding: 20px 20px 20px 40px;
103
+ }
104
+
105
+ .pll-configure legend {
106
+ font-size: 14px;
107
+ font-weight: 600;
108
+ margin-bottom: 0.5em;
109
+ }
110
+
111
+ .pll-configure td .description {
112
+ margin-top: 2px;
113
+ margin-bottom: 0.5em;
114
+ }
115
+
116
+ .pll-configure p.submit {
117
+ margin-top: 20px;
118
+ }
119
+
120
+ .pll-configure .button {
121
+ margin-right: 20px;
122
+ }
123
+
124
+ .pll-configure fieldset {
125
+ margin-bottom: 1.5em;
126
+ }
127
+
128
+ .pll-inline-block-list {
129
+ margin: 0;
130
+ }
131
+
132
+ .pll-inline-block-list li {
133
+ display: inline-block;
134
+ margin: 0;
135
+ width: 250px;
136
+ }
137
+
138
+ /* settings URL modifications */
139
+ #pll-domains-table td {
140
+ padding: 2px 2px 2px 1.5em;
141
+ -webkit-box-shadow: none;
142
+ box-shadow: none;
143
+ border: none;
144
+ }
145
+
146
+ .pll-settings-url-col {
147
+ display: inline-block;
148
+ width: 49%;
149
+ vertical-align: top;
150
+ }
151
+
152
+ /* settings Activation keys */
153
+ #pll-licenses-table td {
154
+ vertical-align: top;
155
+ }
156
+
157
+ #pll-licenses-table label {
158
+ font-size: 1em;
159
+ font-weight: 600;
160
+ }
161
+
162
+ .pll-configure .pll-deactivate-license {
163
+ margin: 0 0 0 20px;
164
+ }
165
+
166
+ /* language columns in edit.php and edit-tags.php */
167
+ .wp-list-table th[class*='column-language_'],
168
+ .wp-list-table td[class*='column-language_'] {
169
+ width: 1.5em;
170
+ box-sizing: content-box; /* Override ACF 5.9.0 styles */
171
+ }
172
+
173
+ /* Text direction in post.php and edit-tags.php */
174
+ .pll-dir-rtl textarea,
175
+ .pll-dir-rtl input[type="text"] {
176
+ direction: rtl;
177
+ }
178
+
179
+ .pll-dir-ltr textarea,
180
+ .pll-dir-ltr input[type="text"] {
181
+ direction: ltr;
182
+ }
183
+
184
+ .pll-dir-ltr .tr_lang,
185
+ .pll-dir-rtl .tr_lang {
186
+ direction: inherit;
187
+ }
188
+
189
+ /* languages metabox in post.php */
190
+ #post-translations p {
191
+ float: left;
192
+ }
193
+
194
+ #post-translations table {
195
+ table-layout: fixed;
196
+ width: 100%;
197
+ clear: both;
198
+ }
199
+
200
+ #post-translations a {
201
+ text-decoration: none;
202
+ }
203
+
204
+ #post-translations .pll-language-column,
205
+ #post-translations .pll-column-icon {
206
+ width: 20px;
207
+ }
208
+
209
+ #post-translations .tr_lang {
210
+ width: 100%;
211
+ }
212
+
213
+ #post-translations td {
214
+ padding: 2px;
215
+ }
216
+
217
+ #post-translations .spinner,
218
+ #term-translations .spinner {
219
+ float: none;
220
+ margin: 0;
221
+ background-position: center;
222
+ width: auto;
223
+ }
224
+
225
+ .pll-column-icon {
226
+ text-align: center;
227
+ }
228
+
229
+ #select-post-language .pll-select-flag {
230
+ padding: 4px;
231
+ margin-right: 32px;
232
+ }
233
+
234
+ /* specific cases for media */
235
+ #select-media-language .pll-select-flag {
236
+ padding: 4px;
237
+ margin-right: 10px;
238
+ }
239
+
240
+ .pll-media-edit-column {
241
+ float: right;
242
+ }
243
+
244
+ /* language and translations in edit-tags.php */
245
+ .pll-translation-flag { /* also for media */
246
+ margin-right: 14px;
247
+ }
248
+
249
+ #select-add-term-language .pll-select-flag {
250
+ padding: 11px;
251
+ margin-right: 13px;
252
+ }
253
+
254
+ #select-edit-term-language .pll-select-flag {
255
+ padding: 11px;
256
+ margin-right: 4px;
257
+ }
258
+
259
+ #term-translations p {
260
+ /* same style as label */
261
+ font-weight: 400;
262
+ font-style: normal;
263
+ padding: 2px;
264
+ color: #23282d;
265
+ }
266
+
267
+ #add-term-translations,
268
+ #edit-term-translations {
269
+ width: 95%;
270
+ }
271
+
272
+ #term-translations .pll-language-column {
273
+ line-height: 28px;
274
+ width: 20%;
275
+ }
276
+
277
+ #term-translations .pll-edit-column,
278
+ #add-term-translations .pll-language-column {
279
+ width: 20px;
280
+ }
281
+
282
+ #edit-term-translations .pll-language-column {
283
+ padding: 15px 10px;
284
+ font-weight: normal;
285
+ }
286
+
287
+ /* icon fonts */
288
+ .pll_icon_add:before {
289
+ content: "\f132";
290
+ }
291
+
292
+ .pll_icon_edit:before {
293
+ content: "\f464";
294
+ }
295
+
296
+ [class^="pll_icon_"] {
297
+ font: 20px/1 'dashicons';
298
+ vertical-align: middle;
299
+ }
300
+
301
+ /* admin bar */
302
+ #wpadminbar #wp-admin-bar-languages .ab-item img {
303
+ margin: 0 8px 0 2px;
304
+ }
305
+
306
+ #wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon {
307
+ float: none;
308
+ top: 4px;
309
+ }
310
+
311
+ #wpadminbar #wp-admin-bar-languages .ab-icon:before {
312
+ content: "\f326";
313
+ top: 1px;
314
+ }
315
+
316
+ /* Notices */
317
+ .pll-notice.notice {
318
+ padding-right: 38px;
319
+ position: relative;
320
+ }
321
+
322
+ .pll-notice a.notice-dismiss {
323
+ text-decoration: none;
324
+ }
325
+
326
+ .pll-notice .button {
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 {
352
+ padding: 20px;
353
+ }
354
+
355
+ #wpbody-content .pll-settings #cb {
356
+ padding: 20px 9px;
357
+ }
358
+
359
+ /* settings URL modifications */
360
+ .pll-inline-block {
361
+ width: auto;
362
+ }
363
+
364
+ .pll-settings-url-col {
365
+ display: block;
366
+ width: 100%;
367
+ }
368
+
369
+ /* settings licenses */
370
+ #wpbody-content .pll-settings #pll-licenses-table td {
371
+ display: block;
372
+ }
373
+
374
+ .pll-configure .pll-deactivate-license {
375
+ margin: 10px 0 5px;
376
+ }
377
+
378
+ /* strings translations table */
379
+ .translation label {
380
+ display: block;
381
+ width: 95%;
382
+ padding-left: 0;
383
+ }
384
+
385
+ .translation input,
386
+ .translation textarea {
387
+ width: 95%;
388
+ }
389
+
390
+ /* Metaboxes holder in Strings translations screen */
391
+ .languages_page_mlang_strings .metabox-holder > div {
392
+ flex-direction: column;
393
+ }
394
+ .languages_page_mlang_strings .metabox-holder > div > div:nth-child(2n) {
395
+ margin-left: 0;
396
+ }
397
+
398
+ /* hide selected language flag and translations language name */
399
+ #select-add-term-language .pll-select-flag,
400
+ #select-edit-term-language .pll-select-flag,
401
+ #edit-term-translations .pll-language-name {
402
+ display: none;
403
+ }
404
+
405
+ #edit-term-translations {
406
+ width: 100%;
407
+ }
408
+
409
+ #add-term-translations .pll-language-column {
410
+ line-height: 38px;
411
+ }
412
+
413
+ #edit-term-translations td {
414
+ padding: 8px 10px;
415
+ }
416
+
417
+ #edit-term-translations .pll-language-column,
418
+ #edit-term-translations .pll-edit-column {
419
+ width: 20px;
420
+ }
421
+
422
+ /* translations tables should be kept as table */
423
+ .term-translations .pll-language-column,
424
+ .term-translations .pll-edit-column,
425
+ .term-translations .pll-translation-column {
426
+ display: table-cell;
427
+ }
428
+
429
+ .term-translations .hidden {
430
+ display: none;
431
+ }
432
+
433
+ /* admin bar */
434
+ #wpadminbar #wp-admin-bar-languages {
435
+ display: block; /*shows our menu on mobile devices */
436
+ }
437
+
438
+ #wpadminbar #wp-admin-bar-languages > .ab-item {
439
+ width: 50px;
440
+ text-align: center;
441
+ }
442
+
443
+ #wpadminbar #wp-admin-bar-languages > .ab-item .ab-icon:before {
444
+ font: 32px/1 'dashicons';
445
+ top: -1px;
446
+ }
447
+
448
+ #wpadminbar #wp-admin-bar-languages > .ab-item img {
449
+ margin: 19px 0;
450
+ }
451
+
452
+ #wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon {
453
+ margin-right: 6px;
454
+ font-size: 20px !important;
455
+ line-height: 20px !important;
456
+ }
457
+ }
css/build/admin.min.css ADDED
@@ -0,0 +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{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:50%;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:none}.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}.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/build/dialog.css ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* By default Polylang dialog box use WordPress jQuery UI dialog styles.
2
+ However WooCommerce loads its own jQuery UI dialog styles and we need to override them by ours
3
+ to revert to the default WordPress ones.
4
+ */
5
+ .pll-confirmation-modal.ui-widget,
6
+ .pll-confirmation-modal.ui-widget .ui-widget,
7
+ .pll-confirmation-modal .ui-widget {
8
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
9
+ font-size: 13px;
10
+ }
11
+ .pll-confirmation-modal.ui-dialog {
12
+ padding: 0;
13
+ z-index: 100102;
14
+ background: #fff;
15
+ border: 0;
16
+ color: #444;
17
+ }
18
+ .ui-dialog.pll-confirmation-modal .ui-dialog-titlebar {
19
+ background: #fcfcfc;
20
+ border-radius: 0;
21
+ border: 0;
22
+ border-bottom: 1px solid #dfdfdf;
23
+ height: 36px;
24
+ font-size: 18px;
25
+ font-weight: 600;
26
+ line-height: 2;
27
+ padding: 0 36px 0 16px;
28
+ color: #444;
29
+ position: static;
30
+ }
31
+ .ui-dialog.pll-confirmation-modal .ui-dialog-title {
32
+ float: none;
33
+ width: auto;
34
+ margin: 0;
35
+ }
36
+ .pll-confirmation-modal .ui-widget-header .ui-icon {
37
+ background: none;
38
+ position: static;
39
+ }
40
+ .pll-confirmation-modal .ui-button.ui-dialog-titlebar-close {
41
+ padding: 0;
42
+ margin: 0;
43
+ top: 0;
44
+ right: 0;
45
+ width: 36px;
46
+ height: 36px;
47
+ }
48
+ .ui-dialog.pll-confirmation-modal .ui-dialog-content {
49
+ border: 0;
50
+ padding: 16px;
51
+ color: #444;
52
+ position: static;
53
+ box-sizing: border-box;
54
+ }
55
+ .ui-dialog.pll-confirmation-modal .ui-dialog-buttonpane{
56
+ margin: 0;
57
+ padding: 16px;
58
+ border: 0;
59
+ background: #fcfcfc;
60
+ border-top: 1px solid #dfdfdf;
61
+ }
62
+ .ui-dialog.pll-confirmation-modal .ui-dialog-buttonpane .ui-button{
63
+ margin: 0 0 0 16px;
64
+ padding: 0 10px 1px;
65
+ background: #f7f7f7;
66
+ border: 1px solid #cccccc;
67
+ border-radius: 3px;
68
+ position: static;
69
+ line-height: 2;
70
+ vertical-align: top;
71
+ }
72
+ .ui-dialog.pll-confirmation-modal .ui-button:hover,
73
+ .ui-dialog.pll-confirmation-modal .ui-button:focus {
74
+ background: #fafafa;
75
+ border-color: #999;
76
+ color: #23282d;
77
+ }
78
+ .pll-confirmation-modal + .ui-widget-overlay {
79
+ background: #000;
80
+ opacity: 0.7;
81
+ z-index: 100101;
82
+ }
css/build/dialog.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .pll-confirmation-modal.ui-widget,.pll-confirmation-modal .ui-widget,.pll-confirmation-modal.ui-widget .ui-widget{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px}.pll-confirmation-modal.ui-dialog{padding:0;z-index:100102;background:#fff;border:0;color:#444}.ui-dialog.pll-confirmation-modal .ui-dialog-titlebar{background:#fcfcfc;border-radius:0;border:0;border-bottom:1px solid #dfdfdf;height:36px;font-size:18px;font-weight:600;line-height:2;padding:0 36px 0 16px;color:#444;position:static}.ui-dialog.pll-confirmation-modal .ui-dialog-title{float:none;width:auto;margin:0}.pll-confirmation-modal .ui-widget-header .ui-icon{background:none;position:static}.pll-confirmation-modal .ui-button.ui-dialog-titlebar-close{padding:0;margin:0;top:0;right:0;width:36px;height:36px}.ui-dialog.pll-confirmation-modal .ui-dialog-content{border:0;padding:16px;color:#444;position:static;box-sizing:border-box}.ui-dialog.pll-confirmation-modal .ui-dialog-buttonpane{margin:0;padding:16px;border:0;background:#fcfcfc;border-top:1px solid #dfdfdf}.ui-dialog.pll-confirmation-modal .ui-dialog-buttonpane .ui-button{margin:0 0 0 16px;padding:0 10px 1px;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;position:static;line-height:2;vertical-align:top}.ui-dialog.pll-confirmation-modal .ui-button:focus,.ui-dialog.pll-confirmation-modal .ui-button:hover{background:#fafafa;border-color:#999;color:#23282d}.pll-confirmation-modal+.ui-widget-overlay{background:#000;opacity:.7;z-index:100101}
css/build/selectmenu.css ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Greatly modified version of the jquery-ui.css */
2
+
3
+ .ui-widget-overlay {
4
+ position: fixed;
5
+ top: 0;
6
+ left: 0;
7
+ width: 100%;
8
+ height: 100%;
9
+ }
10
+
11
+ .ui-menu {
12
+ list-style: none;
13
+ padding: 0;
14
+ margin: 0;
15
+ display: block;
16
+ outline: none;
17
+ }
18
+
19
+ .ui-menu .ui-menu {
20
+ position: absolute;
21
+ }
22
+
23
+ .ui-menu .ui-menu-item {
24
+ position: relative;
25
+ margin: 0;
26
+ padding: 3px 1em 3px .4em;
27
+ cursor: pointer;
28
+ min-height: 0; /* support: IE7 */
29
+ /* support: IE10, see #8844 */
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
+ }
59
+
60
+ /* left-aligned */
61
+ .ui-selectmenu-text .ui-icon,
62
+ .ui-menu .ui-icon {
63
+ position: absolute;
64
+ top: 0;
65
+ bottom: 0;
66
+ left: .3em;
67
+ margin: auto 0;
68
+ }
69
+
70
+ .rtl .ui-selectmenu-text .ui-icon,
71
+ .rtl .ui-menu .ui-icon {
72
+ right: .3em;
73
+ left: auto;
74
+ }
75
+
76
+ /* right-aligned */
77
+ .ui-menu .ui-menu-icon {
78
+ left: auto;
79
+ right: 0;
80
+ }
81
+
82
+ .ui-selectmenu-menu {
83
+ padding: 0;
84
+ margin: 0;
85
+ position: absolute;
86
+ top: 0;
87
+ left: 0;
88
+ display: none;
89
+ }
90
+
91
+ .ui-selectmenu-menu .ui-menu {
92
+ overflow: auto;
93
+ /* Support: IE7 */
94
+ overflow-x: hidden;
95
+ padding-bottom: 1px;
96
+ }
97
+
98
+ .ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup {
99
+ font-size: 1em;
100
+ font-weight: bold;
101
+ line-height: 23px;
102
+ padding: 2px 0.4em;
103
+ margin: 0.5em 0 0 0;
104
+ height: auto;
105
+ border: 0;
106
+ }
107
+
108
+ .ui-selectmenu-open {
109
+ display: block;
110
+ }
111
+
112
+ .ui-selectmenu-button.ui-button {
113
+ display: inline-block;
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
+ text-align: left;
119
+ white-space: nowrap;
120
+ vertical-align: top;
121
+ padding: 0;
122
+ }
123
+
124
+ .ui-selectmenu-button span.ui-icon {
125
+ right: 0.5em;
126
+ left: auto;
127
+ position: absolute;
128
+ top: 26%;
129
+ width: 16px;
130
+ height: 16px;
131
+ text-indent: 0; /* due to text-indent for jquery ui-dialog in wizard */
132
+ background: none;
133
+ }
134
+
135
+ .rtl .ui-selectmenu-button span.ui-icon {
136
+ left: 0.5em;
137
+ right: auto;
138
+ }
139
+
140
+
141
+ .ui-selectmenu-button span.ui-selectmenu-text {
142
+ text-align: left;
143
+ padding: 0.1em 2.1em 0.2em 2em;
144
+ display: block;
145
+ line-height: 23px;
146
+ overflow: hidden;
147
+ text-overflow: ellipsis;
148
+ white-space: nowrap;
149
+ margin: 0;
150
+ }
151
+
152
+ .rtl .ui-selectmenu-button span.ui-selectmenu-text {
153
+ text-align: right;
154
+ padding: 0.2em 2em 0.2em 2.1em;
155
+ }
156
+
157
+ .ui-widget-content,
158
+ .ui-state-default,
159
+ .ui-button.ui-selectmenu-button-closed, /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
160
+ .ui-button.ui-selectmenu-button-open, /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
161
+ .ui-widget-content .ui-state-default,
162
+ .ui-widget-header .ui-state-default {
163
+ background: #fff;
164
+ border: 1px solid #ddd;
165
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.07) inset;
166
+ color: #32373c;
167
+ }
168
+ /* Override to have same styles as WP form styles since WordPress 5.4 */
169
+ .toplevel_page_mlang .ui-selectmenu-button.ui-state-default,
170
+ .toplevel_page_mlang .ui-selectmenu-button.ui-selectmenu-button-closed, /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
171
+ .toplevel_page_mlang .ui-selectmenu-button.ui-selectmenu-button-open{ /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
172
+ box-shadow: 0 0 0 transparent;
173
+ border-radius: 4px;
174
+ border: 1px solid #7e8993;
175
+ }
176
+
177
+ /* From this line and below: override WooCommerce bookings plugin styles which overrides default WordPress styles */
178
+ .pll-selectmenu-menu .ui-widget,
179
+ .pll-selectmenu-button.ui-widget {
180
+ font-size: 13px;
181
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
182
+ }
183
+
184
+ .toplevel_page_mlang .ui-button.ui-selectmenu-button:focus{
185
+ color: #016087; /* Same color as WordPress focused select HTML tag */
186
+ border-color: #007cba;
187
+ box-shadow: 0 0 0 1px #007cba;
188
+ outline: 2px solid transparent;
189
+ background: #fff; /* Override bookings plugin styles which overrides default WordPress styles */
190
+ }
191
+
192
+ .toplevel_page_mlang .ui-menu-item,
193
+ .toplevel_page_mlang .ui-widget-content .ui-state-hover,
194
+ .toplevel_page_mlang .ui-widget-content .ui-state-focus,
195
+ .toplevel_page_mlang .ui-widget-content .ui-state-active {
196
+ color: #016087; /* Same color as option in a WordPress focused select HTML tag */
197
+ margin: 0;
198
+ }
199
+
200
+ .pll-selectmenu-menu .ui-widget-content .ui-state-hover,
201
+ .pll-selectmenu-menu .ui-widget-content .ui-state-focus,
202
+ .pll-selectmenu-menu .ui-widget-content .ui-state-active { /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
203
+ background: #d5d5d5;
204
+ border: 0;
205
+ }
206
+
207
+ .ui-selectmenu-button.ui-state-focus {
208
+ border: 1px solid #5b9dd9;
209
+ box-shadow: 0 0 2px rgba(30, 140, 190, 0.8);
210
+ }
211
+
212
+ .ui-icon-triangle-1-s:before {
213
+ content: "";
214
+ 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%;
215
+ background-size: 16px 16px;
216
+ box-sizing: border-box;
217
+ position: absolute;
218
+ width: 16px;
219
+ height: 16px;
220
+ }
221
+
222
+ .pll-selectmenu-button.ui-button:hover,
223
+ .pll-wizard .ui-button:hover,
224
+ .pll-wizard .ui-button:focus {
225
+ background: #fff; /* To override jQuery ui-dialog styles provided by WordPress */
226
+ }
227
+
228
+ .ui-widget-content {
229
+ max-height: 231px;
230
+ box-shadow: 0 2px 6px rgba(100, 100, 100, 0.3);
231
+ }
css/build/selectmenu.min.css ADDED
@@ -0,0 +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:none}.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;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-button.ui-button{display:inline-block;overflow:hidden;position:relative;text-decoration:none;box-sizing:border-box;text-align:left;white-space:nowrap;vertical-align:top;padding:0}.ui-selectmenu-button span.ui-icon{right:.5em;left:auto;position:absolute;top:26%;width:16px;height:16px;text-indent:0;background:none}.rtl .ui-selectmenu-button span.ui-icon{left:.5em;right:auto}.ui-selectmenu-button span.ui-selectmenu-text{text-align:left;padding:.1em 2.1em .2em 2em;display:block;line-height:23px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin:0}.rtl .ui-selectmenu-button span.ui-selectmenu-text{text-align:right;padding:.2em 2em .2em 2.1em}.ui-button.ui-selectmenu-button-closed,.ui-button.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:inset 0 1px 2px rgba(0,0,0,.07);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}.pll-selectmenu-button.ui-widget,.pll-selectmenu-menu .ui-widget{font-size:13px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif}.toplevel_page_mlang .ui-button.ui-selectmenu-button:focus{color:#016087;border-color:#007cba;box-shadow:0 0 0 1px #007cba;outline:2px solid transparent;background:#fff}.toplevel_page_mlang .ui-menu-item,.toplevel_page_mlang .ui-widget-content .ui-state-active,.toplevel_page_mlang .ui-widget-content .ui-state-focus,.toplevel_page_mlang .ui-widget-content .ui-state-hover{color:#016087;margin:0}.pll-selectmenu-menu .ui-widget-content .ui-state-active,.pll-selectmenu-menu .ui-widget-content .ui-state-focus,.pll-selectmenu-menu .ui-widget-content .ui-state-hover{background:#d5d5d5;border:0}.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-selectmenu-button.ui-button:hover,.pll-wizard .ui-button:focus,.pll-wizard .ui-button:hover{background:#fff}.ui-widget-content{max-height:231px;box-shadow:0 2px 6px hsla(0,0%,39.2%,.3)}
css/build/wizard.css ADDED
@@ -0,0 +1,951 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @charset "UTF-8";
2
+ body {
3
+ margin: 65px auto 24px;
4
+ box-shadow: none;
5
+ background: #f1f1f1;
6
+ padding: 0;
7
+ border: 0; /* fix-pro #856 override WP install.css */
8
+ }
9
+
10
+ #pll-logo {
11
+ border: 0;
12
+ margin: 0 0 24px;
13
+ padding: 0;
14
+ text-align: center;
15
+ font-family: sans-serif;
16
+ font-size: 64px;
17
+ text-transform: uppercase;
18
+ color: #000;
19
+ line-height: normal;
20
+ }
21
+ #pll-logo a {
22
+ display: flex;
23
+ justify-content: center;
24
+ color: #000;
25
+ text-decoration: none;
26
+ }
27
+
28
+ #pll-logo img {
29
+ max-width: 100%;
30
+ margin-right: 16px;
31
+ }
32
+ .rtl #pll-logo img {
33
+ margin-right: 0;
34
+ margin-left: 16px;
35
+ }
36
+
37
+ .pll-wizard-footer {
38
+ text-align: center
39
+ }
40
+
41
+ .pll-wizard .select2-container {
42
+ text-align: left;
43
+ width: auto
44
+ }
45
+
46
+ .pll-wizard .hidden {
47
+ display: none
48
+ }
49
+
50
+ .pll-wizard-content {
51
+ box-shadow: 0 1px 3px rgba(0, 0, 0, .13);
52
+ padding: 2em;
53
+ margin: 0 0 20px;
54
+ background: #fff;
55
+ overflow: hidden;
56
+ zoom: 1;
57
+ text-align: left;
58
+ }
59
+ .rtl .pll-wizard-content{
60
+ text-align: right;
61
+ }
62
+
63
+ .pll-wizard-content h1,
64
+ .pll-wizard-content h2,
65
+ .pll-wizard-content h3,
66
+ .pll-wizard-content table {
67
+ margin: 0 0 20px;
68
+ border: 0;
69
+ padding: 0;
70
+ color: #666;
71
+ clear: none;
72
+ font-weight: 500
73
+ }
74
+
75
+ .pll-wizard-content p {
76
+ margin: 20px 0;
77
+ font-size: 1em;
78
+ line-height: 1.75em;
79
+ color: #666
80
+ }
81
+
82
+ .pll-wizard-content table {
83
+ font-size: 1em;
84
+ line-height: 1.75em;
85
+ color: #666;
86
+ width: 100%;
87
+ margin-top: 20px;
88
+ }
89
+ .pll-wizard-content table td span{
90
+ display: inline-block;
91
+ }
92
+
93
+ .pll-wizard-content table caption {
94
+ caption-side: bottom;
95
+ font-style: italic;
96
+ text-align: right;
97
+ }
98
+ .rtl .pll-wizard-content table caption {
99
+ text-align: left;
100
+ }
101
+
102
+ .pll-wizard-content table caption .icon-default-lang{
103
+ font-style: normal;
104
+ }
105
+
106
+ .pll-wizard-content a {
107
+ color: #a03f3f;
108
+ }
109
+
110
+ .pll-wizard-content a:focus,
111
+ .pll-wizard-content a:hover,
112
+ .pll-wizard-footer-links:hover {
113
+ color: #dd5454
114
+ }
115
+
116
+ .pll-wizard-content .pll-wizard-next-steps {
117
+ overflow: hidden;
118
+ margin: 0 0 24px;
119
+ padding-bottom: 2px
120
+ }
121
+
122
+ .pll-wizard-content .pll-wizard-next-steps h2 {
123
+ margin-bottom: 12px
124
+ }
125
+
126
+ .pll-wizard-content .pll-wizard-next-steps .pll-wizard-next-steps-first {
127
+ float: left;
128
+ width: 50%;
129
+ box-sizing: border-box
130
+ }
131
+
132
+ .pll-wizard-content .pll-wizard-next-steps .pll-wizard-next-steps-last {
133
+ float: right;
134
+ width: 50%;
135
+ box-sizing: border-box
136
+ }
137
+
138
+ .pll-wizard-content .pll-wizard-next-steps ul {
139
+ padding: 0 2em 0 0;
140
+ list-style: none outside;
141
+ margin: 0
142
+ }
143
+
144
+ .pll-wizard-content .pll-wizard-next-steps ul li a {
145
+ display: block;
146
+ padding: 0 0 .75em
147
+ }
148
+
149
+ .pll-wizard-content .pll-wizard-next-steps ul li a::before {
150
+ color: #82878c;
151
+ font: normal 20px/1 dashicons;
152
+ speak: none;
153
+ display: inline-block;
154
+ padding: 0 10px 0 0;
155
+ top: 1px;
156
+ position: relative;
157
+ text-decoration: none!important;
158
+ vertical-align: top
159
+ }
160
+
161
+ .pll-wizard-steps {
162
+ padding: 0 0 24px;
163
+ margin: 0;
164
+ list-style: none outside;
165
+ overflow: hidden;
166
+ color: #ccc;
167
+ width: 100%;
168
+ display: -webkit-inline-box;
169
+ display: -webkit-inline-flex;
170
+ display: inline-flex
171
+ }
172
+
173
+ .pll-wizard-steps li {
174
+ width: 100%;
175
+ float: left;
176
+ padding: 0 0 .8em;
177
+ margin: 0;
178
+ text-align: center;
179
+ position: relative;
180
+ border-bottom: 4px solid #ccc;
181
+ line-height: 1.4em
182
+ }
183
+
184
+ .pll-wizard-steps li a {
185
+ color: #a03f3f;
186
+ text-decoration: none;
187
+ padding: 1.5em;
188
+ margin: -1.5em;
189
+ position: relative;
190
+ z-index: 1
191
+ }
192
+
193
+ .pll-wizard-steps li a:focus,
194
+ .pll-wizard-steps li a:hover {
195
+ color: #dd5454;
196
+ text-decoration: underline
197
+ }
198
+
199
+ .pll-wizard-steps li::before {
200
+ content: "";
201
+ border: 4px solid #ccc;
202
+ border-radius: 100%;
203
+ width: 4px;
204
+ height: 4px;
205
+ position: absolute;
206
+ bottom: 0;
207
+ left: 50%;
208
+ margin-left: -6px;
209
+ margin-bottom: -8px;
210
+ background: #fff
211
+ }
212
+
213
+ .pll-wizard-steps li.active {
214
+ border-color: #a03f3f;
215
+ color: #a03f3f;
216
+ font-weight: 700
217
+ }
218
+
219
+ .pll-wizard-steps li.active::before {
220
+ border-color: #a03f3f
221
+ }
222
+
223
+ .pll-wizard-steps li.done {
224
+ border-color: #a03f3f;
225
+ color: #a03f3f
226
+ }
227
+
228
+ .pll-wizard-steps li.done::before {
229
+ border-color: #a03f3f;
230
+ background: #a03f3f
231
+ }
232
+
233
+ .pll-wizard .pll-wizard-actions {
234
+ overflow: hidden;
235
+ margin: 20px 0 0;
236
+ position: relative
237
+ }
238
+
239
+ .pll-wizard .pll-wizard-actions .button {
240
+ font-size: 16px;
241
+ font-weight: 300;
242
+ padding: 1em 2em;
243
+ line-height: 1em;
244
+ margin-right: .5em;
245
+ margin-bottom: 2px;
246
+ margin-top: 10px;
247
+ height: auto;
248
+ border-radius: 4px;
249
+ box-shadow: none;
250
+ min-width: auto;
251
+ border-color: #a03f3f;
252
+ color: #a03f3f;
253
+ }
254
+
255
+ .pll-wizard .pll-wizard-content .button {
256
+ border-color: #a03f3f;
257
+ color: #a03f3f;
258
+ }
259
+
260
+ .pll-wizard .pll-wizard-content .button-primary,
261
+ .pll-wizard .pll-wizard-actions .button-primary {
262
+ background-color: #a03f3f;
263
+ border-color: #a03f3f;
264
+ color: #fff;
265
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 0 #a03f3f;
266
+ text-shadow: 0 -1px 1px #a03f3f, 1px 0 1px #a03f3f, 0 1px 1px #a03f3f, -1px 0 1px #a03f3f;
267
+ margin: 0;
268
+ opacity: 1
269
+ }
270
+
271
+ .pll-wizard .pll-wizard-content .button-small .dashicons {
272
+ font-size: 15px;
273
+ height: auto;
274
+ vertical-align: middle;
275
+ }
276
+
277
+ .pll-wizard .button-primary:active,
278
+ .pll-wizard .button-primary:focus,
279
+ .pll-wizard input[type="checkbox"]:focus + label.button-primary,
280
+ .pll-wizard .button-primary:hover {
281
+ background: #dd5454;
282
+ border-color: #dd5454;
283
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 0 #dd5454
284
+ }
285
+ .pll-wizard .pll-wizard-actions .button-primary[disabled],
286
+ .pll-wizard .pll-wizard-actions .button-primary:disabled,
287
+ .pll-wizard .pll-wizard-actions .button-primary.disabled {
288
+ cursor: wait;
289
+ background-color: #bb5454 !important;
290
+ border-color: #bb5454 !important;
291
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 0 #bb5454 !important;
292
+ text-shadow: 0 -1px 1px #bb5454, 1px 0 1px #bb5454, 0 1px 1px #bb5454, -1px 0 1px #bb5454 !important;
293
+ color: #ffa3a3 !important;
294
+ }
295
+ .pll-wizard-content p:last-child {
296
+ margin-bottom: 0
297
+ }
298
+
299
+ .pll-wizard-footer-links {
300
+ font-size: .85em;
301
+ color: #7b7b7b;
302
+ margin: 1.18em auto;
303
+ display: inline-block;
304
+ text-align: center
305
+ }
306
+
307
+ .pll-wizard-services {
308
+ border: 1px solid #eee;
309
+ padding: 0;
310
+ margin: 0 0 1em;
311
+ list-style: none outside;
312
+ border-radius: 4px;
313
+ overflow: hidden
314
+ }
315
+
316
+ .pll-wizard-services p {
317
+ margin: 0 0 1em 0;
318
+ padding: 0;
319
+ font-size: 1em;
320
+ line-height: 1.5em
321
+ }
322
+
323
+ .pll-wizard-service-item {
324
+ display: -webkit-box;
325
+ display: -webkit-flex;
326
+ display: flex;
327
+ -webkit-flex-wrap: nowrap;
328
+ flex-wrap: nowrap;
329
+ -webkit-box-pack: justify;
330
+ -webkit-justify-content: space-between;
331
+ justify-content: space-between;
332
+ padding: 0;
333
+ border-bottom: 1px solid #eee;
334
+ color: #666;
335
+ -webkit-box-align: center;
336
+ -webkit-align-items: center;
337
+ align-items: center
338
+ }
339
+
340
+ .media-step .pll-wizard-service-item{
341
+ border: 0;
342
+ }
343
+
344
+ .media-step .pll-wizard-service-item:last-child{
345
+ display: block;
346
+ }
347
+
348
+ .media-step .pll-wizard-service-item .pll-wizard-service-enable{
349
+ padding-bottom: 0;
350
+ }
351
+
352
+ .pll-wizard-service-item:last-child {
353
+ border-bottom: 0
354
+ }
355
+
356
+ .pll-wizard-service-item .pll-wizard-service-name {
357
+ -webkit-flex-basis: 0;
358
+ flex-basis: 0;
359
+ min-width: 160px;
360
+ text-align: center;
361
+ font-weight: 700;
362
+ padding: 2em 0;
363
+ -webkit-align-self: stretch;
364
+ align-self: stretch;
365
+ display: -webkit-box;
366
+ display: -webkit-flex;
367
+ display: flex;
368
+ -webkit-box-align: baseline;
369
+ -webkit-align-items: baseline;
370
+ align-items: baseline
371
+ }
372
+
373
+ .pll-wizard-service-item .pll-wizard-service-name img {
374
+ max-width: 75px
375
+ }
376
+
377
+ .pll-wizard-service-item .pll-wizard-service-description {
378
+ -webkit-box-flex: 1;
379
+ -webkit-flex-grow: 1;
380
+ flex-grow: 1;
381
+ padding: 20px
382
+ }
383
+
384
+ .pll-wizard-service-item .pll-wizard-service-example {
385
+ padding: 0 20px 20px
386
+ }
387
+
388
+ .pll-wizard-service-item .pll-wizard-service-example p{
389
+ text-align: right;
390
+ }
391
+ .rtl .pll-wizard-service-item .pll-wizard-service-example p{
392
+ text-align: left;
393
+ }
394
+
395
+ .pll-wizard-service-item .pll-wizard-service-description p {
396
+ margin-bottom: 1em
397
+ }
398
+
399
+ .pll-wizard-service-item .pll-wizard-service-description p:last-child {
400
+ margin-bottom: 0
401
+ }
402
+
403
+ .pll-wizard-service-item .pll-wizard-service-description .pll-wizard-service-settings-description {
404
+ display: block;
405
+ font-style: italic;
406
+ color: #999
407
+ }
408
+
409
+ .pll-wizard-service-item .pll-wizard-service-enable {
410
+ -webkit-flex-basis: 0;
411
+ flex-basis: 0;
412
+ min-width: 75px;
413
+ text-align: center;
414
+ cursor: pointer;
415
+ padding: 2em 0;
416
+ position: relative;
417
+ max-height: 1.5em;
418
+ -webkit-align-self: flex-start;
419
+ align-self: flex-start;
420
+ -webkit-box-ordinal-group: 4;
421
+ -webkit-order: 3;
422
+ order: 3
423
+ }
424
+
425
+ .pll-wizard-service-item .pll-wizard-service-toggle {
426
+ position: relative
427
+ }
428
+
429
+ .pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] {
430
+ position:absolute;
431
+ opacity: 0;
432
+ }
433
+
434
+ .pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label {
435
+ position: relative;
436
+ display: inline-block;
437
+ width: 44px;
438
+ height: 20px;
439
+ border-radius: 10em;
440
+ cursor: pointer;
441
+ text-indent: -9999px;
442
+ }
443
+
444
+ .pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:focus + label {
445
+ border:1px dashed #777;
446
+ }
447
+
448
+ .pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label::before,
449
+ .pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label::after {
450
+ content: '';
451
+ position: absolute;
452
+ }
453
+
454
+ .pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label::before {
455
+ left: 0;
456
+ top: 0;
457
+ width: 44px;
458
+ height: 20px;
459
+ background: #ddd;
460
+ border-radius: 10em;
461
+ transition: background-color .2s;
462
+ }
463
+
464
+ .pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox] + label::after {
465
+ width: 16px;
466
+ height: 16px;
467
+ transition: all .2s;
468
+ border-radius: 50%;
469
+ background: #fff;
470
+ margin: 2px;
471
+ top: 0;
472
+ left: 0;
473
+ }
474
+
475
+ .pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:checked + label::before {
476
+ background:#a03f3f;
477
+ }
478
+
479
+ .pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:checked + label::after {
480
+ right: 0;
481
+ left:auto;
482
+ }
483
+
484
+ .pll-wizard-service-item .pll-wizard-service-settings {
485
+ display: none;
486
+ margin-top: .75em;
487
+ margin-bottom: 0;
488
+ cursor: default
489
+ }
490
+
491
+ .pll-wizard-service-item .pll-wizard-service-settings.hide {
492
+ display: none
493
+ }
494
+
495
+ .pll-wizard-service-item.checked .pll-wizard-service-settings {
496
+ display: inline-block
497
+ }
498
+
499
+ .pll-wizard-service-item.checked .pll-wizard-service-settings.hide {
500
+ display: none
501
+ }
502
+
503
+ .pll-wizard-service-item.closed {
504
+ border-bottom: 0
505
+ }
506
+
507
+ .step {
508
+ text-align: center
509
+ }
510
+
511
+ .pll-wizard .button .dashicons{
512
+ vertical-align: middle;
513
+ }
514
+ .rtl .dashicons-arrow-right-alt2:before {
515
+ content: "\f341";
516
+ }
517
+ .pll-wizard .pll-wizard-actions .button:active,
518
+ .pll-wizard .pll-wizard-actions .button:focus,
519
+ .pll-wizard .pll-wizard-actions .button:hover {
520
+ box-shadow: none
521
+ }
522
+
523
+ .pll-wizard-next-steps {
524
+ border: 1px solid #eee;
525
+ border-radius: 4px;
526
+ list-style: none;
527
+ padding: 0
528
+ }
529
+
530
+ .pll-wizard-next-steps li {
531
+ padding: 0
532
+ }
533
+
534
+ .pll-wizard-next-steps .pll-wizard-next-step-item {
535
+ display: -webkit-box;
536
+ display: -webkit-flex;
537
+ display: flex;
538
+ border-top: 1px solid #eee
539
+ }
540
+
541
+ .pll-wizard-next-steps .pll-wizard-next-step-item.no-border,
542
+ .pll-wizard-next-steps .pll-wizard-next-step-item:first-child {
543
+ border-top: 0
544
+ }
545
+
546
+ .pll-wizard-next-steps .pll-wizard-next-step-description {
547
+ -webkit-box-flex: 1;
548
+ -webkit-flex-grow: 1;
549
+ flex-grow: 1;
550
+ margin: 1.5em
551
+ }
552
+
553
+ .pll-wizard-next-steps .pll-wizard-next-step-action {
554
+ -webkit-box-flex: 0;
555
+ -webkit-flex-grow: 0;
556
+ flex-grow: 0;
557
+ display: -webkit-box;
558
+ display: -webkit-flex;
559
+ display: flex;
560
+ -webkit-box-align: center;
561
+ -webkit-align-items: center;
562
+ align-items: center
563
+ }
564
+
565
+ .pll-wizard-next-steps .pll-wizard-next-step-action .button {
566
+ margin: 1em 1.5em
567
+ }
568
+
569
+ .pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-next-step-description,
570
+ .pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-actions,
571
+ .pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-next-step-action .button{
572
+ margin-top: 0;
573
+ }
574
+
575
+
576
+ .pll-wizard-next-steps p.next-step-heading {
577
+ margin: 0;
578
+ font-size: .95em;
579
+ font-weight: 400;
580
+ font-variant: all-petite-caps
581
+ }
582
+
583
+ .pll-wizard-next-steps p.next-step-extra-info {
584
+ margin: 0
585
+ }
586
+
587
+ .pll-wizard-next-steps h3.next-step-description {
588
+ margin: 0;
589
+ font-size: 16px;
590
+ font-weight: 600;
591
+ }
592
+
593
+ .pll-wizard-next-steps .pll-wizard-additional-steps {
594
+ border-top: 1px solid #eee;
595
+ }
596
+
597
+ .pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-next-step-description {
598
+ margin-bottom: 0
599
+ }
600
+
601
+ .pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions {
602
+ margin: 0 0 1.5em 0;
603
+ }
604
+
605
+ .pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button {
606
+ font-size: 15px;
607
+ margin: 1em 0 1em 1.5em;
608
+ }
609
+ .rtl .pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button {
610
+ margin: 1em 1.5em 1em 0;
611
+ }
612
+
613
+ .pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button::last-child {
614
+ margin-right: 1.5em;
615
+ }
616
+
617
+ .pll-wizard-content img{
618
+ max-width: 100%;
619
+ margin-right: 0.5em;
620
+ }
621
+ .rtl .pll-wizard-content img{
622
+ margin-left: 0.5em;
623
+ }
624
+
625
+ .pll-wizard-content .form-field label{
626
+ margin-bottom: 5px;
627
+ display: block;
628
+ }
629
+
630
+ .pll-wizard-content .form-field select{
631
+ padding: 3px;
632
+ }
633
+
634
+ .pll-wizard-content .languages-step select,
635
+ .pll-wizard-content .untranslated-contents-step select{
636
+ width: 100%;
637
+ }
638
+
639
+ .languages-step .form-field .button{
640
+ margin-left: 15px;
641
+ }
642
+ .languages-step .form-field .button > span{
643
+ margin-right: 0.3em;
644
+ }
645
+ .rtl .languages-step .form-field .button{
646
+ margin-left: 0;
647
+ margin-right: 15px;
648
+ }
649
+ .rtl .languages-step .form-field .button > span{
650
+ margin-left: 0.3em;
651
+ margin-right: 0;
652
+ }
653
+
654
+ .pll-wizard-content .languages-step .select-language-field{
655
+ display: flex;
656
+ }
657
+
658
+ .pll-wizard-content #languages{
659
+ display: none;
660
+ }
661
+ .pll-wizard-content #languages tr th:first-child{
662
+ width: 80%;
663
+ }
664
+ .pll-wizard-content #languages .dashicons{
665
+ color: #a03f3f;
666
+ }
667
+ .pll-wizard-content #languages img{
668
+ margin-right: 5px;
669
+ }
670
+ .pll-wizard-content .error{
671
+ color: #a03f3f;
672
+ font-weight: bold;
673
+ }
674
+ .pll-wizard-content #messages .error{
675
+ background: #fccfcf;
676
+ padding: 0.5rem;
677
+ border: 1px solid #a03f3f;
678
+ margin-bottom: 0.5rem;
679
+ }
680
+
681
+ .pll-wizard-content #slide-toggle{
682
+ position:absolute;
683
+ opacity: 0;
684
+ }
685
+
686
+ .pll-wizard-content #slide-toggle + label{
687
+ position:relative;
688
+ }
689
+ .pll-wizard-content #slide-toggle + label + span{
690
+ display: block;
691
+ }
692
+
693
+ .pll-wizard-content #slide-toggle + label .dashicons{
694
+ margin-right: 0.3em;
695
+ }
696
+ .rtl .pll-wizard-content #slide-toggle + label .dashicons{
697
+ margin-left: 0.3em;
698
+ margin-right: 0;
699
+ }
700
+ .pll-wizard-content #slide-toggle ~ #screenshot > img {
701
+ max-height: 500px;
702
+ margin-top: 10px;
703
+ -webkit-transition: all .5s cubic-bezier(0, 1, 0.5, 1);
704
+ transition: all .5s cubic-bezier(0, 1, 0.5, 1);
705
+ }
706
+ .pll-wizard-content #slide-toggle:checked ~ #screenshot > img {
707
+ max-height: 0;
708
+ }
709
+ .hide {
710
+ display: none;
711
+ }
712
+
713
+ input[type="text"].field-in-error,
714
+ input[type="password"].field-in-error,
715
+ input[type="checkbox"].field-in-error,
716
+ input[type="color"].field-in-error,
717
+ input[type="date"].field-in-error,
718
+ input[type="datetime"].field-in-error,
719
+ input[type="datetime-local"].field-in-error,
720
+ input[type="email"].field-in-error,
721
+ input[type="month"].field-in-error,
722
+ input[type="number"].field-in-error,
723
+ input[type="search"].field-in-error,
724
+ input[type="radio"].field-in-error,
725
+ input[type="tel"].field-in-error,
726
+ input[type="text"].field-in-error,
727
+ input[type="time"].field-in-error,
728
+ input[type="url"].field-in-error,
729
+ input[type="week"].field-in-error,
730
+ select.field-in-error,
731
+ textarea.field-in-error,
732
+ span.field-in-error,
733
+ .field-in-error{
734
+ border-color: #a03f3f;
735
+ }
736
+
737
+ input[type="text"].field-in-error:focus,
738
+ input[type="password"].field-in-error:focus,
739
+ input[type="checkbox"].field-in-error:focus,
740
+ input[type="color"].field-in-error:focus,
741
+ input[type="date"].field-in-error:focus,
742
+ input[type="datetime"].field-in-error:focus,
743
+ input[type="datetime-local"].field-in-error:focus,
744
+ input[type="email"].field-in-error:focus,
745
+ input[type="month"].field-in-error:focus,
746
+ input[type="number"].field-in-error:focus,
747
+ input[type="search"].field-in-error:focus,
748
+ input[type="radio"].field-in-error:focus,
749
+ input[type="tel"].field-in-error:focus,
750
+ input[type="text"].field-in-error:focus,
751
+ input[type="time"].field-in-error:focus,
752
+ input[type="url"].field-in-error:focus,
753
+ input[type="week"].field-in-error:focus,
754
+ select.field-in-error:focus,
755
+ textarea.field-in-error:focus,
756
+ span.field-in-error:focus,
757
+ .field-in-error:focus{
758
+ border: 1px solid #a03f3f;
759
+ box-shadow: 0 0 2px rgba(160, 63, 63, 0.8);
760
+ outline-color: #a03f3f;
761
+ outline-style: auto;
762
+ outline-width: thin;
763
+ }
764
+
765
+ /* override install styles by returning back to forms styles */
766
+ .form-table input.regular-text{
767
+ width: 25em;
768
+ }
769
+ .form-table input.field-in-error{
770
+ border-color: #a03f3f;
771
+ }
772
+ #pll-licenses-table td{
773
+ padding: 10px 9px;
774
+ }
775
+ #pll-licenses-table .license-valid td p{
776
+ min-width: 35em;
777
+ }
778
+ #pll-licenses-table .pll-deactivate-license{
779
+ margin: 0 0 0 20px;
780
+ }
781
+ .rtl #pll-licenses-table .pll-deactivate-license{
782
+ margin: 0 10px 0 0;
783
+ }
784
+ .pll-wizard-content .documentation {
785
+ padding: 24px 24px 0;
786
+ margin: 0 0 24px;
787
+ overflow: hidden;
788
+ background: #f5f5f5
789
+ }
790
+
791
+ .pll-wizard-content .documentation p {
792
+ padding: 0;
793
+ margin: 0 0 12px;
794
+ }
795
+ .documentation-container {
796
+ display: -webkit-box;
797
+ display: -webkit-flex;
798
+ display: flex;
799
+ justify-content: flex-end;
800
+ }
801
+
802
+ .documentation-container .documentation-button-container {
803
+ -webkit-box-flex: 0;
804
+ -webkit-flex-grow: 0;
805
+ flex-grow: 0;
806
+ }
807
+
808
+ .wc-setup .wc-setup-actions .button.documentation-button {
809
+ height: 42px;
810
+ padding: 0 1em;
811
+ margin: 0;
812
+ }
813
+ #dialog{
814
+ display: none;
815
+ }
816
+ .pll-wizard .ui-dialog.ui-widget-content{
817
+ max-height: none;
818
+ }
819
+ .pll-wizard .ui-dialog-title::before{
820
+ content: "\f534";
821
+ font-family: dashicons;
822
+ display: inline-block;
823
+ line-height: 1;
824
+ font-weight: 400;
825
+ font-style: normal;
826
+ speak: none;
827
+ text-decoration: inherit;
828
+ text-transform: none;
829
+ text-rendering: auto;
830
+ -webkit-font-smoothing: antialiased;
831
+ -moz-osx-font-smoothing: grayscale;
832
+ width: 20px;
833
+ height: 20px;
834
+ font-size: 20px;
835
+ vertical-align: middle;
836
+ text-align: center;
837
+ margin: 0 5px 5px 0;
838
+ transition: color 0.1s ease-in;
839
+ }
840
+ .rtl.pll-wizard .ui-dialog-title::before{
841
+ margin-right: 0;
842
+ margin-left: 5px;
843
+ }
844
+ .pll-wizard .ui-dialog ul{
845
+ list-style: disc;
846
+ padding-left: 20px;
847
+ }
848
+ .rtl.pll-wizard .ui-dialog ul{
849
+ padding-left: 0;
850
+ padding-right: 20px;
851
+ }
852
+ .pll-wizard li{
853
+ margin-bottom: 0;
854
+ }
855
+ #translations{
856
+ border-collapse: collapse;
857
+ }
858
+ #translations tbody:nth-child(odd){
859
+ background-color: #f9f9f9;
860
+ }
861
+ #translations.striped > tbody > :nth-child(odd) {
862
+ background-color: transparent; /* Override common WordPress style */
863
+ }
864
+ .pll-wizard-content mark{
865
+ background: transparent none;
866
+ }
867
+ .pll-wizard-content mark{
868
+ color: #7ad03a;
869
+ }
870
+ @media screen and (max-width: 782px) {
871
+ /* Override WordPress button css rules */
872
+ .languages-step .form-field .button{
873
+ font-size: 13px;
874
+ line-height: 26px;
875
+ height: 28px;
876
+ padding: 0 10px 1px;
877
+ vertical-align: top;
878
+ }
879
+
880
+ #pll-licenses-table .pll-deactivate-license{
881
+ margin: 10px 0 5px;
882
+ }
883
+ }
884
+ @media only screen and (max-width:620px) {
885
+ /* Override dialog width rule */
886
+ .ui-dialog{
887
+ width: 100% !important;
888
+ }
889
+
890
+ }
891
+ @media only screen and (max-width:500px) {
892
+ #pll-logo a,
893
+ .select-language-field{
894
+ flex-direction: column;
895
+ }
896
+ .select-language-field .action-buttons{
897
+ display: flex;
898
+ justify-content: flex-end;
899
+ }
900
+ .languages-step .form-field .button{
901
+ margin: 5px 0 0;
902
+ }
903
+ }
904
+ @media only screen and (max-width:400px) {
905
+ #pll-logo {
906
+ font-size: 56px;
907
+ }
908
+ .pll-wizard-steps {
909
+ display: none
910
+ }
911
+ .pll-wizard-service-item {
912
+ -webkit-flex-wrap: wrap;
913
+ flex-wrap: wrap
914
+ }
915
+ .pll-wizard-service-item .pll-wizard-service-enable {
916
+ -webkit-box-ordinal-group: 3;
917
+ -webkit-order: 2;
918
+ order: 2;
919
+ padding: 20px 0 0
920
+ }
921
+ .pll-wizard-service-item .pll-wizard-service-description {
922
+ -webkit-box-ordinal-group: 4;
923
+ -webkit-order: 3;
924
+ order: 3
925
+ }
926
+ .pll-wizard-service-item .pll-wizard-service-name {
927
+ padding: 20px 20px 0;
928
+ text-align: left;
929
+ -webkit-box-pack: justify!important;
930
+ -webkit-justify-content: space-between!important;
931
+ justify-content: space-between!important
932
+ }
933
+ .pll-wizard-service-item .pll-wizard-service-name img {
934
+ margin: 0
935
+ }
936
+ .pll-wizard-next-steps .pll-wizard-next-step-item {
937
+ -webkit-flex-wrap: wrap;
938
+ flex-wrap: wrap
939
+ }
940
+ .pll-wizard-next-steps .pll-wizard-next-step-item .pll-wizard-next-step-description {
941
+ margin-bottom: 0
942
+ }
943
+ .pll-wizard-next-steps .pll-wizard-next-step-item .pll-wizard-next-step-action p {
944
+ margin: 0
945
+ }
946
+ }
947
+ @media only screen and (max-width:360px) {
948
+ #pll-logo {
949
+ font-size: 48px;
950
+ }
951
+ }
css/build/wizard.min.css ADDED
@@ -0,0 +1 @@
 
1
+ body{margin:65px auto 24px;box-shadow:none;background:#f1f1f1}#pll-logo,body{padding:0;border:0}#pll-logo{margin:0 0 24px;text-align:center;font-family:sans-serif;font-size:64px;text-transform:uppercase;color:#000;line-height:normal}#pll-logo a{display:flex;justify-content:center;color:#000;text-decoration:none}#pll-logo img{max-width:100%;margin-right:16px}.rtl #pll-logo img{margin-right:0;margin-left:16px}.pll-wizard-footer{text-align:center}.pll-wizard .select2-container{text-align:left;width:auto}.pll-wizard .hidden{display:none}.pll-wizard-content{box-shadow:0 1px 3px rgba(0,0,0,.13);padding:2em;margin:0 0 20px;background:#fff;overflow:hidden;zoom:1;text-align:left}.rtl .pll-wizard-content{text-align:right}.pll-wizard-content h1,.pll-wizard-content h2,.pll-wizard-content h3,.pll-wizard-content table{margin:0 0 20px;border:0;padding:0;color:#666;clear:none;font-weight:500}.pll-wizard-content p{margin:20px 0;font-size:1em;line-height:1.75em;color:#666}.pll-wizard-content table{font-size:1em;line-height:1.75em;color:#666;width:100%;margin-top:20px}.pll-wizard-content table td span{display:inline-block}.pll-wizard-content table caption{caption-side:bottom;font-style:italic;text-align:right}.rtl .pll-wizard-content table caption{text-align:left}.pll-wizard-content table caption .icon-default-lang{font-style:normal}.pll-wizard-content a{color:#a03f3f}.pll-wizard-content a:focus,.pll-wizard-content a:hover,.pll-wizard-footer-links:hover{color:#dd5454}.pll-wizard-content .pll-wizard-next-steps{overflow:hidden;margin:0 0 24px;padding-bottom:2px}.pll-wizard-content .pll-wizard-next-steps h2{margin-bottom:12px}.pll-wizard-content .pll-wizard-next-steps .pll-wizard-next-steps-first{float:left;width:50%;box-sizing:border-box}.pll-wizard-content .pll-wizard-next-steps .pll-wizard-next-steps-last{float:right;width:50%;box-sizing:border-box}.pll-wizard-content .pll-wizard-next-steps ul{padding:0 2em 0 0;list-style:none outside;margin:0}.pll-wizard-content .pll-wizard-next-steps ul li a{display:block;padding:0 0 .75em}.pll-wizard-content .pll-wizard-next-steps ul li a:before{color:#82878c;font:normal 20px/1 dashicons;speak:none;display:inline-block;padding:0 10px 0 0;top:1px;position:relative;text-decoration:none!important;vertical-align:top}.pll-wizard-steps{padding:0 0 24px;margin:0;list-style:none outside;overflow:hidden;color:#ccc;width:100%;display:-webkit-inline-box;display:-webkit-inline-flex;display:inline-flex}.pll-wizard-steps li{width:100%;float:left;padding:0 0 .8em;margin:0;text-align:center;position:relative;border-bottom:4px solid #ccc;line-height:1.4em}.pll-wizard-steps li a{color:#a03f3f;text-decoration:none;padding:1.5em;margin:-1.5em;position:relative;z-index:1}.pll-wizard-steps li a:focus,.pll-wizard-steps li a:hover{color:#dd5454;text-decoration:underline}.pll-wizard-steps li:before{content:"";border:4px solid #ccc;border-radius:100%;width:4px;height:4px;position:absolute;bottom:0;left:50%;margin-left:-6px;margin-bottom:-8px;background:#fff}.pll-wizard-steps li.active{border-color:#a03f3f;color:#a03f3f;font-weight:700}.pll-wizard-steps li.active:before{border-color:#a03f3f}.pll-wizard-steps li.done{border-color:#a03f3f;color:#a03f3f}.pll-wizard-steps li.done:before{border-color:#a03f3f;background:#a03f3f}.pll-wizard .pll-wizard-actions{overflow:hidden;margin:20px 0 0;position:relative}.pll-wizard .pll-wizard-actions .button{font-size:16px;font-weight:300;padding:1em 2em;line-height:1em;margin-right:.5em;margin-bottom:2px;margin-top:10px;height:auto;border-radius:4px;box-shadow:none;min-width:auto;border-color:#a03f3f;color:#a03f3f}.pll-wizard .pll-wizard-content .button{border-color:#a03f3f;color:#a03f3f}.pll-wizard .pll-wizard-actions .button-primary,.pll-wizard .pll-wizard-content .button-primary{background-color:#a03f3f;border-color:#a03f3f;color:#fff;box-shadow:inset 0 1px 0 hsla(0,0%,100%,.25),0 1px 0 #a03f3f;text-shadow:0 -1px 1px #a03f3f,1px 0 1px #a03f3f,0 1px 1px #a03f3f,-1px 0 1px #a03f3f;margin:0;opacity:1}.pll-wizard .pll-wizard-content .button-small .dashicons{font-size:15px;height:auto;vertical-align:middle}.pll-wizard .button-primary:active,.pll-wizard .button-primary:focus,.pll-wizard .button-primary:hover,.pll-wizard input[type=checkbox]:focus+label.button-primary{background:#dd5454;border-color:#dd5454;box-shadow:inset 0 1px 0 hsla(0,0%,100%,.25),0 1px 0 #dd5454}.pll-wizard .pll-wizard-actions .button-primary.disabled,.pll-wizard .pll-wizard-actions .button-primary:disabled,.pll-wizard .pll-wizard-actions .button-primary[disabled]{cursor:wait;background-color:#bb5454!important;border-color:#bb5454!important;box-shadow:inset 0 1px 0 hsla(0,0%,100%,.25),0 1px 0 #bb5454!important;text-shadow:0 -1px 1px #bb5454,1px 0 1px #bb5454,0 1px 1px #bb5454,-1px 0 1px #bb5454!important;color:#ffa3a3!important}.pll-wizard-content p:last-child{margin-bottom:0}.pll-wizard-footer-links{font-size:.85em;color:#7b7b7b;margin:1.18em auto;display:inline-block;text-align:center}.pll-wizard-services{border:1px solid #eee;padding:0;margin:0 0 1em;list-style:none outside;border-radius:4px;overflow:hidden}.pll-wizard-services p{margin:0 0 1em;padding:0;font-size:1em;line-height:1.5em}.pll-wizard-service-item{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;padding:0;border-bottom:1px solid #eee;color:#666;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.media-step .pll-wizard-service-item{border:0}.media-step .pll-wizard-service-item:last-child{display:block}.media-step .pll-wizard-service-item .pll-wizard-service-enable{padding-bottom:0}.pll-wizard-service-item:last-child{border-bottom:0}.pll-wizard-service-item .pll-wizard-service-name{-webkit-flex-basis:0;flex-basis:0;min-width:160px;text-align:center;font-weight:700;padding:2em 0;-webkit-align-self:stretch;align-self:stretch;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:baseline;-webkit-align-items:baseline;align-items:baseline}.pll-wizard-service-item .pll-wizard-service-name img{max-width:75px}.pll-wizard-service-item .pll-wizard-service-description{-webkit-box-flex:1;-webkit-flex-grow:1;flex-grow:1;padding:20px}.pll-wizard-service-item .pll-wizard-service-example{padding:0 20px 20px}.pll-wizard-service-item .pll-wizard-service-example p{text-align:right}.rtl .pll-wizard-service-item .pll-wizard-service-example p{text-align:left}.pll-wizard-service-item .pll-wizard-service-description p{margin-bottom:1em}.pll-wizard-service-item .pll-wizard-service-description p:last-child{margin-bottom:0}.pll-wizard-service-item .pll-wizard-service-description .pll-wizard-service-settings-description{display:block;font-style:italic;color:#999}.pll-wizard-service-item .pll-wizard-service-enable{-webkit-flex-basis:0;flex-basis:0;min-width:75px;text-align:center;cursor:pointer;padding:2em 0;position:relative;max-height:1.5em;-webkit-align-self:flex-start;align-self:flex-start;-webkit-box-ordinal-group:4;-webkit-order:3;order:3}.pll-wizard-service-item .pll-wizard-service-toggle{position:relative}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]{position:absolute;opacity:0}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]+label{position:relative;display:inline-block;width:44px;height:20px;border-radius:10em;cursor:pointer;text-indent:-9999px}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:focus+label{border:1px dashed #777}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]+label:after,.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]+label:before{content:"";position:absolute}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]+label:before{left:0;top:0;width:44px;height:20px;background:#ddd;border-radius:10em;transition:background-color .2s}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]+label:after{width:16px;height:16px;transition:all .2s;border-radius:50%;background:#fff;margin:2px;top:0;left:0}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:checked+label:before{background:#a03f3f}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:checked+label:after{right:0;left:auto}.pll-wizard-service-item .pll-wizard-service-settings{display:none;margin-top:.75em;margin-bottom:0;cursor:default}.pll-wizard-service-item .pll-wizard-service-settings.hide{display:none}.pll-wizard-service-item.checked .pll-wizard-service-settings{display:inline-block}.pll-wizard-service-item.checked .pll-wizard-service-settings.hide{display:none}.pll-wizard-service-item.closed{border-bottom:0}.step{text-align:center}.pll-wizard .button .dashicons{vertical-align:middle}.rtl .dashicons-arrow-right-alt2:before{content:"\f341"}.pll-wizard .pll-wizard-actions .button:active,.pll-wizard .pll-wizard-actions .button:focus,.pll-wizard .pll-wizard-actions .button:hover{box-shadow:none}.pll-wizard-next-steps{border:1px solid #eee;border-radius:4px;list-style:none;padding:0}.pll-wizard-next-steps li{padding:0}.pll-wizard-next-steps .pll-wizard-next-step-item{display:-webkit-box;display:-webkit-flex;display:flex;border-top:1px solid #eee}.pll-wizard-next-steps .pll-wizard-next-step-item.no-border,.pll-wizard-next-steps .pll-wizard-next-step-item:first-child{border-top:0}.pll-wizard-next-steps .pll-wizard-next-step-description{-webkit-box-flex:1;-webkit-flex-grow:1;flex-grow:1;margin:1.5em}.pll-wizard-next-steps .pll-wizard-next-step-action{-webkit-box-flex:0;-webkit-flex-grow:0;flex-grow:0;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.pll-wizard-next-steps .pll-wizard-next-step-action .button{margin:1em 1.5em}.pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-actions,.pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-next-step-action .button,.pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-next-step-description{margin-top:0}.pll-wizard-next-steps p.next-step-heading{margin:0;font-size:.95em;font-weight:400;font-variant:all-petite-caps}.pll-wizard-next-steps p.next-step-extra-info{margin:0}.pll-wizard-next-steps h3.next-step-description{margin:0;font-size:16px;font-weight:600}.pll-wizard-next-steps .pll-wizard-additional-steps{border-top:1px solid #eee}.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-next-step-description{margin-bottom:0}.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions{margin:0 0 1.5em}.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button{font-size:15px;margin:1em 0 1em 1.5em}.rtl .pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button{margin:1em 1.5em 1em 0}.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button::last-child{margin-right:1.5em}.pll-wizard-content img{max-width:100%;margin-right:.5em}.rtl .pll-wizard-content img{margin-left:.5em}.pll-wizard-content .form-field label{margin-bottom:5px;display:block}.pll-wizard-content .form-field select{padding:3px}.pll-wizard-content .languages-step select,.pll-wizard-content .untranslated-contents-step select{width:100%}.languages-step .form-field .button{margin-left:15px}.languages-step .form-field .button>span{margin-right:.3em}.rtl .languages-step .form-field .button{margin-left:0;margin-right:15px}.rtl .languages-step .form-field .button>span{margin-left:.3em;margin-right:0}.pll-wizard-content .languages-step .select-language-field{display:flex}.pll-wizard-content #languages{display:none}.pll-wizard-content #languages tr th:first-child{width:80%}.pll-wizard-content #languages .dashicons{color:#a03f3f}.pll-wizard-content #languages img{margin-right:5px}.pll-wizard-content .error{color:#a03f3f;font-weight:700}.pll-wizard-content #messages .error{background:#fccfcf;padding:.5rem;border:1px solid #a03f3f;margin-bottom:.5rem}.pll-wizard-content #slide-toggle{position:absolute;opacity:0}.pll-wizard-content #slide-toggle+label{position:relative}.pll-wizard-content #slide-toggle+label+span{display:block}.pll-wizard-content #slide-toggle+label .dashicons{margin-right:.3em}.rtl .pll-wizard-content #slide-toggle+label .dashicons{margin-left:.3em;margin-right:0}.pll-wizard-content #slide-toggle~#screenshot>img{max-height:500px;margin-top:10px;-webkit-transition:all .5s cubic-bezier(0,1,.5,1);transition:all .5s cubic-bezier(0,1,.5,1)}.pll-wizard-content #slide-toggle:checked~#screenshot>img{max-height:0}.hide{display:none}.field-in-error,input[type=checkbox].field-in-error,input[type=color].field-in-error,input[type=date].field-in-error,input[type=datetime-local].field-in-error,input[type=datetime].field-in-error,input[type=email].field-in-error,input[type=month].field-in-error,input[type=number].field-in-error,input[type=password].field-in-error,input[type=radio].field-in-error,input[type=search].field-in-error,input[type=tel].field-in-error,input[type=text].field-in-error,input[type=time].field-in-error,input[type=url].field-in-error,input[type=week].field-in-error,select.field-in-error,span.field-in-error,textarea.field-in-error{border-color:#a03f3f}.field-in-error:focus,input[type=checkbox].field-in-error:focus,input[type=color].field-in-error:focus,input[type=date].field-in-error:focus,input[type=datetime-local].field-in-error:focus,input[type=datetime].field-in-error:focus,input[type=email].field-in-error:focus,input[type=month].field-in-error:focus,input[type=number].field-in-error:focus,input[type=password].field-in-error:focus,input[type=radio].field-in-error:focus,input[type=search].field-in-error:focus,input[type=tel].field-in-error:focus,input[type=text].field-in-error:focus,input[type=time].field-in-error:focus,input[type=url].field-in-error:focus,input[type=week].field-in-error:focus,select.field-in-error:focus,span.field-in-error:focus,textarea.field-in-error:focus{border:1px solid #a03f3f;box-shadow:0 0 2px rgba(160,63,63,.8);outline-color:#a03f3f;outline-style:auto;outline-width:thin}.form-table input.regular-text{width:25em}.form-table input.field-in-error{border-color:#a03f3f}#pll-licenses-table td{padding:10px 9px}#pll-licenses-table .license-valid td p{min-width:35em}#pll-licenses-table .pll-deactivate-license{margin:0 0 0 20px}.rtl #pll-licenses-table .pll-deactivate-license{margin:0 10px 0 0}.pll-wizard-content .documentation{padding:24px 24px 0;margin:0 0 24px;overflow:hidden;background:#f5f5f5}.pll-wizard-content .documentation p{padding:0;margin:0 0 12px}.documentation-container{display:-webkit-box;display:-webkit-flex;display:flex;justify-content:flex-end}.documentation-container .documentation-button-container{-webkit-box-flex:0;-webkit-flex-grow:0;flex-grow:0}.wc-setup .wc-setup-actions .button.documentation-button{height:42px;padding:0 1em;margin:0}#dialog{display:none}.pll-wizard .ui-dialog.ui-widget-content{max-height:none}.pll-wizard .ui-dialog-title:before{content:"\f534";font-family:dashicons;display:inline-block;line-height:1;font-weight:400;font-style:normal;speak:none;text-decoration:inherit;text-transform:none;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:20px;height:20px;font-size:20px;vertical-align:middle;text-align:center;margin:0 5px 5px 0;transition:color .1s ease-in}.rtl.pll-wizard .ui-dialog-title:before{margin-right:0;margin-left:5px}.pll-wizard .ui-dialog ul{list-style:disc;padding-left:20px}.rtl.pll-wizard .ui-dialog ul{padding-left:0;padding-right:20px}.pll-wizard li{margin-bottom:0}#translations{border-collapse:collapse}#translations tbody:nth-child(odd){background-color:#f9f9f9}#translations.striped>tbody>:nth-child(odd){background-color:transparent}.pll-wizard-content mark{background:transparent none;color:#7ad03a}@media screen and (max-width:782px){.languages-step .form-field .button{font-size:13px;line-height:26px;height:28px;padding:0 10px 1px;vertical-align:top}#pll-licenses-table .pll-deactivate-license{margin:10px 0 5px}}@media only screen and (max-width:620px){.ui-dialog{width:100%!important}}@media only screen and (max-width:500px){#pll-logo a,.select-language-field{flex-direction:column}.select-language-field .action-buttons{display:flex;justify-content:flex-end}.languages-step .form-field .button{margin:5px 0 0}}@media only screen and (max-width:400px){#pll-logo{font-size:56px}.pll-wizard-steps{display:none}.pll-wizard-service-item{-webkit-flex-wrap:wrap;flex-wrap:wrap}.pll-wizard-service-item .pll-wizard-service-enable{-webkit-box-ordinal-group:3;-webkit-order:2;order:2;padding:20px 0 0}.pll-wizard-service-item .pll-wizard-service-description{-webkit-box-ordinal-group:4;-webkit-order:3;order:3}.pll-wizard-service-item .pll-wizard-service-name{padding:20px 20px 0;text-align:left;-webkit-box-pack:justify!important;-webkit-justify-content:space-between!important;justify-content:space-between!important}.pll-wizard-service-item .pll-wizard-service-name img{margin:0}.pll-wizard-next-steps .pll-wizard-next-step-item{-webkit-flex-wrap:wrap;flex-wrap:wrap}.pll-wizard-next-steps .pll-wizard-next-step-item .pll-wizard-next-step-description{margin-bottom:0}.pll-wizard-next-steps .pll-wizard-next-step-item .pll-wizard-next-step-action p{margin:0}}@media only screen and (max-width:360px){#pll-logo{font-size:48px}}
css/dialog.css ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* By default Polylang dialog box use WordPress jQuery UI dialog styles.
2
+ However WooCommerce loads its own jQuery UI dialog styles and we need to override them by ours
3
+ to revert to the default WordPress ones.
4
+ */
5
+ .pll-confirmation-modal.ui-widget,
6
+ .pll-confirmation-modal.ui-widget .ui-widget,
7
+ .pll-confirmation-modal .ui-widget {
8
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
9
+ font-size: 13px;
10
+ }
11
+ .pll-confirmation-modal.ui-dialog {
12
+ padding: 0;
13
+ z-index: 100102;
14
+ background: #fff;
15
+ border: 0;
16
+ color: #444;
17
+ }
18
+ .ui-dialog.pll-confirmation-modal .ui-dialog-titlebar {
19
+ background: #fcfcfc;
20
+ border-radius: 0;
21
+ border: 0;
22
+ border-bottom: 1px solid #dfdfdf;
23
+ height: 36px;
24
+ font-size: 18px;
25
+ font-weight: 600;
26
+ line-height: 2;
27
+ padding: 0 36px 0 16px;
28
+ color: #444;
29
+ position: static;
30
+ }
31
+ .ui-dialog.pll-confirmation-modal .ui-dialog-title {
32
+ float: none;
33
+ width: auto;
34
+ margin: 0;
35
+ }
36
+ .pll-confirmation-modal .ui-widget-header .ui-icon {
37
+ background: none;
38
+ position: static;
39
+ }
40
+ .pll-confirmation-modal .ui-button.ui-dialog-titlebar-close {
41
+ padding: 0;
42
+ margin: 0;
43
+ top: 0;
44
+ right: 0;
45
+ width: 36px;
46
+ height: 36px;
47
+ }
48
+ .ui-dialog.pll-confirmation-modal .ui-dialog-content {
49
+ border: 0;
50
+ padding: 16px;
51
+ color: #444;
52
+ position: static;
53
+ box-sizing: border-box;
54
+ }
55
+ .ui-dialog.pll-confirmation-modal .ui-dialog-buttonpane{
56
+ margin: 0;
57
+ padding: 16px;
58
+ border: 0;
59
+ background: #fcfcfc;
60
+ border-top: 1px solid #dfdfdf;
61
+ }
62
+ .ui-dialog.pll-confirmation-modal .ui-dialog-buttonpane .ui-button{
63
+ margin: 0 0 0 16px;
64
+ padding: 0 10px 1px;
65
+ background: #f7f7f7;
66
+ border: 1px solid #cccccc;
67
+ border-radius: 3px;
68
+ position: static;
69
+ line-height: 2;
70
+ vertical-align: top;
71
+ }
72
+ .ui-dialog.pll-confirmation-modal .ui-button:hover,
73
+ .ui-dialog.pll-confirmation-modal .ui-button:focus {
74
+ background: #fafafa;
75
+ border-color: #999;
76
+ color: #23282d;
77
+ }
78
+ .pll-confirmation-modal + .ui-widget-overlay {
79
+ background: #000;
80
+ opacity: 0.7;
81
+ z-index: 100101;
82
+ }
css/selectmenu.css CHANGED
@@ -109,12 +109,16 @@
109
  display: block;
110
  }
111
 
112
- .ui-selectmenu-button {
113
  display: inline-block;
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 {
@@ -125,6 +129,7 @@
125
  width: 16px;
126
  height: 16px;
127
  text-indent: 0; /* due to text-indent for jquery ui-dialog in wizard */
 
128
  }
129
 
130
  .rtl .ui-selectmenu-button span.ui-icon {
@@ -135,12 +140,13 @@
135
 
136
  .ui-selectmenu-button span.ui-selectmenu-text {
137
  text-align: left;
138
- padding: 0.2em 2.1em 0.2em 2em;
139
  display: block;
140
  line-height: 23px;
141
  overflow: hidden;
142
  text-overflow: ellipsis;
143
  white-space: nowrap;
 
144
  }
145
 
146
  .rtl .ui-selectmenu-button span.ui-selectmenu-text {
@@ -150,8 +156,8 @@
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;
@@ -168,21 +174,34 @@
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 {
@@ -200,6 +219,7 @@
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 */
109
  display: block;
110
  }
111
 
112
+ .ui-selectmenu-button.ui-button {
113
  display: inline-block;
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
+ text-align: left;
119
+ white-space: nowrap;
120
+ vertical-align: top;
121
+ padding: 0;
122
  }
123
 
124
  .ui-selectmenu-button span.ui-icon {
129
  width: 16px;
130
  height: 16px;
131
  text-indent: 0; /* due to text-indent for jquery ui-dialog in wizard */
132
+ background: none;
133
  }
134
 
135
  .rtl .ui-selectmenu-button span.ui-icon {
140
 
141
  .ui-selectmenu-button span.ui-selectmenu-text {
142
  text-align: left;
143
+ padding: 0.1em 2.1em 0.2em 2em;
144
  display: block;
145
  line-height: 23px;
146
  overflow: hidden;
147
  text-overflow: ellipsis;
148
  white-space: nowrap;
149
+ margin: 0;
150
  }
151
 
152
  .rtl .ui-selectmenu-button span.ui-selectmenu-text {
156
 
157
  .ui-widget-content,
158
  .ui-state-default,
159
+ .ui-button.ui-selectmenu-button-closed, /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
160
+ .ui-button.ui-selectmenu-button-open, /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
161
  .ui-widget-content .ui-state-default,
162
  .ui-widget-header .ui-state-default {
163
  background: #fff;
174
  border: 1px solid #7e8993;
175
  }
176
 
177
+ /* From this line and below: override WooCommerce bookings plugin styles which overrides default WordPress styles */
178
+ .pll-selectmenu-menu .ui-widget,
179
+ .pll-selectmenu-button.ui-widget {
180
+ font-size: 13px;
181
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
182
+ }
183
+
184
+ .toplevel_page_mlang .ui-button.ui-selectmenu-button:focus{
185
  color: #016087; /* Same color as WordPress focused select HTML tag */
186
  border-color: #007cba;
187
  box-shadow: 0 0 0 1px #007cba;
188
  outline: 2px solid transparent;
189
+ background: #fff; /* Override bookings plugin styles which overrides default WordPress styles */
190
  }
191
 
192
+ .toplevel_page_mlang .ui-menu-item,
193
+ .toplevel_page_mlang .ui-widget-content .ui-state-hover,
194
+ .toplevel_page_mlang .ui-widget-content .ui-state-focus,
195
+ .toplevel_page_mlang .ui-widget-content .ui-state-active {
196
  color: #016087; /* Same color as option in a WordPress focused select HTML tag */
197
+ margin: 0;
198
  }
199
 
200
+ .pll-selectmenu-menu .ui-widget-content .ui-state-hover,
201
+ .pll-selectmenu-menu .ui-widget-content .ui-state-focus,
202
+ .pll-selectmenu-menu .ui-widget-content .ui-state-active { /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
203
  background: #d5d5d5;
204
+ border: 0;
205
  }
206
 
207
  .ui-selectmenu-button.ui-state-focus {
219
  height: 16px;
220
  }
221
 
222
+ .pll-selectmenu-button.ui-button:hover,
223
  .pll-wizard .ui-button:hover,
224
  .pll-wizard .ui-button:focus {
225
  background: #fff; /* To override jQuery ui-dialog styles provided by WordPress */
css/selectmenu.min.css DELETED
@@ -1 +0,0 @@
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/accept-language.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Polylang
4
+ */
5
+
6
+ /**
7
+ * Class Accept_Language.
8
+ *
9
+ * Represents an Accept-Language HTTP Header, as defined in RFC 2616 Section 14.4 {@see https://tools.ietf.org/html/rfc2616.html#section-14.4}.
10
+ *
11
+ * @since 3.0
12
+ */
13
+ class PLL_Accept_Language {
14
+ const SUBTAG_PATTERNS = array(
15
+ 'language' => '(\b[a-z]{2,3}|[a-z]{4}|[a-z]{5-8}\b)',
16
+ 'language-extension' => '(?:-(\b[a-z]{3}){1,3}\b)?',
17
+ 'script' => '(?:-(\b[a-z]{4})\b)?',
18
+ 'region' => '(?:-(\b[a-z]{2}|[0-9]{3})\b)?',
19
+ 'variant' => '(?:-(\b[0-9][a-z]{1,3}|[a-z][a-z0-9]{4,7})\b)?',
20
+ 'extension' => '(?:-(\b[a-wy-z]-[a-z0-9]{2,8})\b)?',
21
+ 'private-use' => '(?:-(\bx-[a-z0-9]{1,8})\b)?',
22
+ );
23
+
24
+ /**
25
+ * @var string[] {
26
+ * @type string $language Usually 2 or three letters (ISO 639).
27
+ * @type string $language-extension Up to three groups of 3 letters.
28
+ * @type string $script Four letters.
29
+ * @type string $region Either two letters of three digits.
30
+ * @type string $variant Either one digit followed by 1 to 3 letters, or a letter followed by 2 to 7 alphanumerical characters.
31
+ * @type string $extension One letter that cannot be an 'x', followed by 2 to 8 alphanumerical characters.
32
+ * @type string $private-use Starts by 'x-', followed by 1 to 8 alphanumerical characters.
33
+ * }
34
+ */
35
+ protected $subtags;
36
+
37
+ /**
38
+ * @var float
39
+ */
40
+ protected $quality;
41
+
42
+ /**
43
+ * PLL_Accept_Language constructor.
44
+ *
45
+ * @since 3.0
46
+ *
47
+ * @param string[] $subtags With subtag name as keys and subtag values as names.
48
+ * @param float $quality
49
+ */
50
+ public function __construct( $subtags, $quality = 1.0 ) {
51
+ $this->subtags = $subtags;
52
+ $this->quality = $quality;
53
+ }
54
+
55
+ /**
56
+ * Creates a new instance from an array resulting of a PHP {@see preg_match()} or {@see preg_match_all()} call.
57
+ *
58
+ * @since 3.0
59
+ *
60
+ * @param string[] $matches Expects first entry to be full match, following entries to be subtags and last entry to be quality factor.
61
+ * @return PLL_Accept_Language
62
+ */
63
+ public static function from_array( $matches ) {
64
+ $subtags = array_combine(
65
+ array_keys( array_slice( self::SUBTAG_PATTERNS, 0, count( $matches ) - 1 ) ),
66
+ array_slice( $matches, 1, count( self::SUBTAG_PATTERNS ) )
67
+ );
68
+ $quality = count( $matches ) === 9 ? floatval( $matches[8] ) : 1.0;
69
+
70
+ return new PLL_Accept_Language( $subtags, $quality );
71
+ }
72
+
73
+ /**
74
+ * Returns the full language tag.
75
+ *
76
+ * @since 3.0
77
+ *
78
+ * @return string
79
+ */
80
+ public function __toString() {
81
+ $subtags = array_filter(
82
+ $this->subtags,
83
+ function ( $subtag ) {
84
+ return ! empty( trim( $subtag ) );
85
+ }
86
+ );
87
+ return implode( '-', $subtags );
88
+ }
89
+
90
+ /**
91
+ * Returns the quality factor as negotiated by the browser agent.
92
+ *
93
+ * @since 3.0
94
+ *
95
+ * @return float
96
+ */
97
+ public function get_quality() {
98
+ return $this->quality;
99
+ }
100
+
101
+ /**
102
+ * Returns a subtag from the language tag.
103
+ *
104
+ * @since 3.0
105
+ *
106
+ * @param string $name A valid subtag name, {@see PLL_Accept_Language::SUBTAG_PATTERNS} for available subtag names.
107
+ * @return string
108
+ */
109
+ public function get_subtag( $name ) {
110
+ return isset( $this->subtags[ $name ] ) ? $this->subtags[ $name ] : '';
111
+ }
112
+ }
frontend/accept-languages-collection.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Polylang
4
+ */
5
+
6
+ /**
7
+ * Class PLL_Accept_Languages_Collection.
8
+ *
9
+ * Represents a collection of values parsed from an Accept-Language HTTP header.
10
+ *
11
+ * @since 3.0
12
+ */
13
+ class PLL_Accept_Languages_Collection {
14
+ /**
15
+ * @var PLL_Accept_Language[]
16
+ */
17
+ protected $accept_languages = array();
18
+
19
+ /**
20
+ * Parse Accept-Language HTTP header according to IETF BCP 47.
21
+ *
22
+ * @since 3.0
23
+ *
24
+ * @param string $http_header Value of the Accept-Language HTTP Header. Formatted as stated BCP 47 for language tags {@see https://tools.ietf.org/html/bcp47}.
25
+ * @return PLL_Accept_Languages_Collection
26
+ */
27
+ public static function from_accept_language_header( $http_header ) {
28
+ $lang_parse = array();
29
+ // Break up string into pieces ( languages and q factors ).
30
+ $language_pattern = implode( '', PLL_Accept_Language::SUBTAG_PATTERNS );
31
+ $quality_pattern = '\s*;\s*q\s*=\s*((?>1|0)(?>\.[0-9]+)?)';
32
+ $full_pattern = "/{$language_pattern}(?:{$quality_pattern})?/i";
33
+
34
+ preg_match_all(
35
+ $full_pattern,
36
+ $http_header,
37
+ $lang_parse,
38
+ PREG_SET_ORDER
39
+ );
40
+
41
+ return new PLL_Accept_Languages_Collection(
42
+ array_map(
43
+ array( PLL_Accept_Language::class, 'from_array' ),
44
+ $lang_parse
45
+ )
46
+ );
47
+ }
48
+
49
+ /**
50
+ * PLL_Accept_Languages_Collection constructor.
51
+ *
52
+ * @since 3.0
53
+ *
54
+ * @param PLL_Accept_Language[] $accept_languages
55
+ */
56
+ public function __construct( $accept_languages = array() ) {
57
+ $this->accept_languages = $accept_languages;
58
+ }
59
+
60
+ /**
61
+ * Bubble sort ( need a stable sort for Android, so can't use a PHP sort function ).
62
+ *
63
+ * @since 3.0
64
+ *
65
+ * @return void
66
+ */
67
+ public function bubble_sort() {
68
+ $k = $this->accept_languages;
69
+ $v = array_map(
70
+ function ( $accept_lang ) {
71
+ return $accept_lang->get_quality();
72
+ },
73
+ $this->accept_languages
74
+ );
75
+
76
+ if ( $n = count( $k ) ) {
77
+ // Set default to 1 for any without q factor.
78
+ foreach ( $v as $key => $val ) {
79
+ if ( '' === $val || (float) $val > 1 ) {
80
+ $v[ $key ] = 1;
81
+ }
82
+ }
83
+
84
+ if ( $n > 1 ) {
85
+ for ( $i = 2; $i <= $n; $i++ ) {
86
+ for ( $j = 0; $j <= $n - 2; $j++ ) {
87
+ if ( $v[ $j ] < $v[ $j + 1 ] ) {
88
+ // Swap values.
89
+ $temp = $v[ $j ];
90
+ $v[ $j ] = $v[ $j + 1 ];
91
+ $v[ $j + 1 ] = $temp;
92
+ // Swap keys.
93
+ $temp = $k[ $j ];
94
+ $k[ $j ] = $k[ $j + 1 ];
95
+ $k[ $j + 1 ] = $temp;
96
+ }
97
+ }
98
+ }
99
+ }
100
+ $this->accept_languages = array_filter(
101
+ $k,
102
+ function ( $accept_lang ) {
103
+ return $accept_lang->get_quality() > 0;
104
+ }
105
+ );
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Looks through sorted list and use first one that matches our language list.
111
+ *
112
+ * @since 3.0
113
+ *
114
+ * @param PLL_Language[] $languages
115
+ * @return string|false A language slug if there's a match, false otherwise.
116
+ */
117
+ public function find_best_match( $languages = array() ) {
118
+ foreach ( $this->accept_languages as $accept_lang ) {
119
+ // First loop to match the exact locale.
120
+ foreach ( $languages as $language ) {
121
+ if ( 0 === strcasecmp( $accept_lang, $language->get_locale( 'display' ) ) ) {
122
+ return $language->slug;
123
+ }
124
+ }
125
+
126
+ // In order of priority.
127
+ $subsets = array();
128
+ if ( ! empty( $accept_lang->get_subtag( 'region' ) ) ) {
129
+ $subsets[] = $accept_lang->get_subtag( 'language' ) . '-' . $accept_lang->get_subtag( 'region' );
130
+ $subsets[] = $accept_lang->get_subtag( 'region' );
131
+ }
132
+ if ( ! empty( $accept_lang->get_subtag( 'variant' ) ) ) {
133
+ $subsets[] = $accept_lang->get_subtag( 'language' ) . '-' . $accept_lang->get_subtag( 'variant' );
134
+ }
135
+ $subsets[] = $accept_lang->get_subtag( 'language' );
136
+
137
+ // More loops to match the subsets.
138
+ foreach ( $languages as $language ) {
139
+ foreach ( $subsets as $subset ) {
140
+
141
+ if ( 0 === stripos( $subset, $language->slug ) || 0 === stripos( $language->get_locale( 'display' ), $subset ) ) {
142
+ return $language->slug;
143
+ }
144
+ }
145
+ }
146
+ }
147
+ return false;
148
+ }
149
+ }
frontend/choose-lang-content.php CHANGED
@@ -15,6 +15,8 @@ class PLL_Choose_Lang_Content extends PLL_Choose_Lang {
15
  * Defers the language choice to the 'wp' action (when the content is known)
16
  *
17
  * @since 1.8
 
 
18
  */
19
  public function init() {
20
  parent::init();
@@ -29,11 +31,12 @@ class PLL_Choose_Lang_Content extends PLL_Choose_Lang {
29
  }
30
 
31
  /**
32
- * Overwrites parent::set_language to remove the 'wp' action if the language is set before
33
  *
34
  * @since 1.2
35
  *
36
- * @param object $curlang current language
 
37
  */
38
  protected function set_language( $curlang ) {
39
  parent::set_language( $curlang );
@@ -64,29 +67,31 @@ class PLL_Choose_Lang_Content extends PLL_Choose_Lang {
64
 
65
  else {
66
  foreach ( $this->model->get_translated_taxonomies() as $taxonomy ) {
67
- if ( $var = get_query_var( get_taxonomy( $taxonomy )->query_var ) ) {
 
68
  $lang = $this->model->term->get_language( $var, $taxonomy );
69
  }
70
  }
71
  }
72
 
73
  /**
74
- * Filter the language before it is set from the content
75
  *
76
  * @since 0.9
77
  *
78
- * @param bool|object $lang language object or false if none was found
79
  */
80
  return apply_filters( 'pll_get_current_language', isset( $lang ) ? $lang : false );
81
  }
82
 
83
  /**
84
- * Sets the language for home page
85
- * Add the lang query var when querying archives with no language code
86
  *
87
  * @since 1.2
88
  *
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'] ) {
@@ -122,21 +127,23 @@ class PLL_Choose_Lang_Content extends PLL_Choose_Lang {
122
  * Sets the language from content
123
  *
124
  * @since 1.2
 
 
125
  */
126
  public function wp() {
127
  // Nothing to do if the language has already been set ( although normally the filter has been removed )
128
- if ( ! $this->curlang && $curlang = $this->get_language_from_content() ) {
129
  parent::set_language( $curlang );
130
  }
131
  }
132
 
133
  /**
134
- * If no language found by get_language_from_content, return the preferred one
135
  *
136
  * @since 0.9
137
  *
138
- * @param object|bool $lang Language found in get_language_from_content
139
- * @return object Language
140
  */
141
  public function pll_get_current_language( $lang ) {
142
  return ! $lang ? $this->get_preferred_language() : $lang;
15
  * Defers the language choice to the 'wp' action (when the content is known)
16
  *
17
  * @since 1.8
18
+ *
19
+ * @return void
20
  */
21
  public function init() {
22
  parent::init();
31
  }
32
 
33
  /**
34
+ * Overwrites parent::set_language to remove the 'wp' action if the language is set before.
35
  *
36
  * @since 1.2
37
  *
38
+ * @param PLL_Language $curlang Current language.
39
+ * @return void
40
  */
41
  protected function set_language( $curlang ) {
42
  parent::set_language( $curlang );
67
 
68
  else {
69
  foreach ( $this->model->get_translated_taxonomies() as $taxonomy ) {
70
+ $tax_object = get_taxonomy( $taxonomy );
71
+ if ( ! empty( $tax_object ) && $var = get_query_var( $tax_object->query_var ) ) {
72
  $lang = $this->model->term->get_language( $var, $taxonomy );
73
  }
74
  }
75
  }
76
 
77
  /**
78
+ * Filters the language before it is set from the content.
79
  *
80
  * @since 0.9
81
  *
82
+ * @param PLL_Language|false $lang Language object or false if none was found.
83
  */
84
  return apply_filters( 'pll_get_current_language', isset( $lang ) ? $lang : false );
85
  }
86
 
87
  /**
88
+ * Sets the language for the home page.
89
+ * Adds the lang query var when querying archives with no language code.
90
  *
91
  * @since 1.2
92
  *
93
+ * @param WP_Query $query Instance of WP_Query.
94
+ * @return void
95
  */
96
  public function parse_main_query( $query ) {
97
  if ( empty( $GLOBALS['wp_the_query'] ) || $query !== $GLOBALS['wp_the_query'] ) {
127
  * Sets the language from content
128
  *
129
  * @since 1.2
130
+ *
131
+ * @return void
132
  */
133
  public function wp() {
134
  // Nothing to do if the language has already been set ( although normally the filter has been removed )
135
+ if ( empty( $this->curlang ) && $curlang = $this->get_language_from_content() ) {
136
  parent::set_language( $curlang );
137
  }
138
  }
139
 
140
  /**
141
+ * If no language is found by {@see PLL_Choose_Lang_Content::get_language_from_content()}, returns the preferred one.
142
  *
143
  * @since 0.9
144
  *
145
+ * @param PLL_Language|false $lang Language found by {@see PLL_Choose_Lang_Content::get_language_from_content()}.
146
+ * @return PLL_Language
147
  */
148
  public function pll_get_current_language( $lang ) {
149
  return ! $lang ? $this->get_preferred_language() : $lang;
frontend/choose-lang-domain.php CHANGED
@@ -14,6 +14,8 @@ class PLL_Choose_Lang_Domain extends PLL_Choose_Lang_Url {
14
  * Don't set any language cookie
15
  *
16
  * @since 1.5
 
 
17
  */
18
  public function maybe_setcookie() {}
19
 
@@ -21,6 +23,8 @@ class PLL_Choose_Lang_Domain extends PLL_Choose_Lang_Url {
21
  * Don't redirect according to browser preferences
22
  *
23
  * @since 1.5
 
 
24
  */
25
  public function get_preferred_language() {
26
  return $this->model->get_language( $this->links_model->get_language_from_url() );
@@ -30,6 +34,8 @@ class PLL_Choose_Lang_Domain extends PLL_Choose_Lang_Url {
30
  * Adds query vars to query for home pages in all languages
31
  *
32
  * @since 1.5
 
 
33
  */
34
  public function home_requested() {
35
  $this->set_curlang_in_query( $GLOBALS['wp_query'] );
14
  * Don't set any language cookie
15
  *
16
  * @since 1.5
17
+ *
18
+ * @return void
19
  */
20
  public function maybe_setcookie() {}
21
 
23
  * Don't redirect according to browser preferences
24
  *
25
  * @since 1.5
26
+ *
27
+ * @return PLL_Language
28
  */
29
  public function get_preferred_language() {
30
  return $this->model->get_language( $this->links_model->get_language_from_url() );
34
  * Adds query vars to query for home pages in all languages
35
  *
36
  * @since 1.5
37
+ *
38
+ * @return void
39
  */
40
  public function home_requested() {
41
  $this->set_curlang_in_query( $GLOBALS['wp_query'] );
frontend/choose-lang-url.php CHANGED
@@ -11,12 +11,21 @@
11
  * @since 1.2
12
  */
13
  class PLL_Choose_Lang_Url extends PLL_Choose_Lang {
14
- protected $index = 'index.php'; // Need this before $wp_rewrite is created, also hardcoded in wp-includes/rewrite.php
 
 
 
 
 
 
 
15
 
16
  /**
17
  * Sets the language
18
  *
19
  * @since 1.8
 
 
20
  */
21
  public function init() {
22
  parent::init();
@@ -32,6 +41,8 @@ class PLL_Choose_Lang_Url extends PLL_Choose_Lang {
32
  * Finds the language according to information found in the url
33
  *
34
  * @since 1.2
 
 
35
  */
36
  public function set_language_from_url() {
37
  $host = str_replace( 'www.', '', wp_parse_url( $this->links_model->home, PHP_URL_HOST ) ); // Remove www. for the comparison
11
  * @since 1.2
12
  */
13
  class PLL_Choose_Lang_Url extends PLL_Choose_Lang {
14
+ /**
15
+ * The name of the index file which is the entry point to all requests.
16
+ * We need this before the global $wp_rewrite is created.
17
+ * Also hardcoded in WP_Rewrite.
18
+ *
19
+ * @var string
20
+ */
21
+ protected $index = 'index.php';
22
 
23
  /**
24
  * Sets the language
25
  *
26
  * @since 1.8
27
+ *
28
+ * @return void
29
  */
30
  public function init() {
31
  parent::init();
41
  * Finds the language according to information found in the url
42
  *
43
  * @since 1.2
44
+ *
45
+ * @return void
46
  */
47
  public function set_language_from_url() {
48
  $host = str_replace( 'www.', '', wp_parse_url( $this->links_model->home, PHP_URL_HOST ) ); // Remove www. for the comparison
frontend/choose-lang.php CHANGED
@@ -9,8 +9,39 @@
9
  * @since 1.2
10
  */
11
  abstract class PLL_Choose_Lang {
12
- public $links_model, $model, $options;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  public $curlang;
 
 
 
 
 
 
 
 
14
 
15
  /**
16
  * Constructor
@@ -33,6 +64,8 @@ abstract class PLL_Choose_Lang {
33
  * Any child class must call this method if it overrides it
34
  *
35
  * @since 1.8
 
 
36
  */
37
  public function init() {
38
  if ( Polylang::is_ajax_on_front() || ! wp_using_themes() ) {
@@ -45,13 +78,13 @@ abstract class PLL_Choose_Lang {
45
  }
46
 
47
  /**
48
- * Writes language cookie
49
- * Loads user defined translations
50
- * Fires the action 'pll_language_defined'
51
  *
52
  * @since 1.2
53
  *
54
- * @param object $curlang current language
 
55
  */
56
  protected function set_language( $curlang ) {
57
  // Don't set the language a second time
@@ -67,12 +100,12 @@ abstract class PLL_Choose_Lang {
67
  wp_styles()->text_direction = $GLOBALS['text_direction'];
68
 
69
  /**
70
- * Fires when the current language is defined
71
  *
72
  * @since 0.9.5
73
  *
74
- * @param string $slug current language code
75
- * @param object $curlang current language object
76
  */
77
  do_action( 'pll_language_defined', $this->curlang->slug, $this->curlang );
78
  }
@@ -82,6 +115,8 @@ abstract class PLL_Choose_Lang {
82
  * Setting PLL_COOKIE to false will disable cookie although it will break some functionalities
83
  *
84
  * @since 1.5
 
 
85
  */
86
  public function maybe_setcookie() {
87
  // Don't set cookie in javascript when a cache plugin is active.
@@ -95,85 +130,32 @@ abstract class PLL_Choose_Lang {
95
  }
96
 
97
  /**
98
- * Get the preferred language according to the browser preferences
99
- * Code adapted from http://www.thefutureoftheweb.com/blog/use-accept-language-header
100
  *
101
  * @since 1.8
102
  *
103
- * @return string|bool the preferred language slug or false
104
  */
105
  public function get_preferred_browser_language() {
106
- $accept_langs = array();
107
-
108
  if ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) {
109
- // Break up string into pieces ( languages and q factors )
110
- preg_match_all(
111
- '/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*((?>1|0)(?>\.[0-9]+)?))?/i',
112
- sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ),
113
- $lang_parse
114
- );
115
-
116
- $k = $lang_parse[1];
117
- $v = $lang_parse[4];
118
-
119
- if ( $n = count( $k ) ) {
120
- // Set default to 1 for any without q factor
121
- foreach ( $v as $key => $val ) {
122
- if ( '' === $val || (float) $val > 1 ) {
123
- $v[ $key ] = 1;
124
- }
125
- }
126
-
127
- // Bubble sort ( need a stable sort for Android, so can't use a PHP sort function )
128
- if ( $n > 1 ) {
129
- for ( $i = 2; $i <= $n; $i++ ) {
130
- for ( $j = 0; $j <= $n - 2; $j++ ) {
131
- if ( $v[ $j ] < $v[ $j + 1 ] ) {
132
- // Swap values
133
- $temp = $v[ $j ];
134
- $v[ $j ] = $v[ $j + 1 ];
135
- $v[ $j + 1 ] = $temp;
136
- // Swap keys
137
- $temp = $k[ $j ];
138
- $k[ $j ] = $k[ $j + 1 ];
139
- $k[ $j + 1 ] = $temp;
140
- }
141
- }
142
- }
143
- }
144
- $accept_langs = array_combine( $k, $v );
145
- }
146
- }
147
 
148
- $accept_langs = array_filter( $accept_langs ); // Remove languages marked as unacceptable (q=0).
149
 
150
- $languages = $this->model->get_languages_list( array( 'hide_empty' => true ) ); // Hides languages with no post
151
 
152
- /**
153
- * Filter the list of languages to use to match the browser preferences
154
- *
155
- * @since 1.9.3
156
- *
157
- * @param array $languages array of PLL_Language objects
158
- */
159
- $languages = apply_filters( 'pll_languages_for_browser_preferences', $languages );
160
-
161
- // Looks through sorted list and use first one that matches our language list
162
- foreach ( array_keys( $accept_langs ) as $accept_lang ) {
163
- // First loop to match the exact locale
164
- foreach ( $languages as $language ) {
165
- if ( 0 === strcasecmp( $accept_lang, $language->get_locale( 'display' ) ) ) {
166
- return $language->slug;
167
- }
168
- }
169
 
170
- // Second loop to match the language set
171
- foreach ( $languages as $language ) {
172
- if ( 0 === stripos( $accept_lang, $language->slug ) || 0 === stripos( $language->get_locale( 'display' ), $accept_lang ) ) {
173
- return $language->slug;
174
- }
175
- }
176
  }
 
177
  return false;
178
  }
179
 
@@ -221,6 +203,8 @@ abstract class PLL_Choose_Lang {
221
  * Sets the language when home page is requested
222
  *
223
  * @since 1.2
 
 
224
  */
225
  protected function home_language() {
226
  // Test referer in case PLL_COOKIE is set to false. Since WP 3.6.1, wp_get_referer() validates the host which is exactly what we want
@@ -237,6 +221,8 @@ abstract class PLL_Choose_Lang {
237
  * Performs a redirection to the home page in the current language if needed
238
  *
239
  * @since 0.9
 
 
240
  */
241
  public function home_requested() {
242
  // We are already on the right page
@@ -285,18 +271,20 @@ abstract class PLL_Choose_Lang {
285
  * @since 0.8.4
286
  *
287
  * @param int $post_id the post being commented
 
288
  */
289
  public function pre_comment_on_post( $post_id ) {
290
  $this->set_language( $this->model->post->get_language( $post_id ) );
291
  }
292
 
293
  /**
294
- * Modifies some main query vars for home page and page for posts
295
- * to enable one home page ( and one page for posts ) per language
296
  *
297
  * @since 1.2
298
  *
299
- * @param object $query instance of WP_Query
 
300
  */
301
  public function parse_main_query( $query ) {
302
  if ( ! $query->is_main_query() ) {
@@ -308,8 +296,8 @@ abstract class PLL_Choose_Lang {
308
  *
309
  * @since 1.8
310
  *
311
- * @param bool|object $lang false or language object
312
- * @param object $query WP_Query object
313
  */
314
  if ( $lang = apply_filters( 'pll_set_language_from_query', false, $query ) ) {
315
  $this->set_language( $lang );
@@ -328,11 +316,12 @@ abstract class PLL_Choose_Lang {
328
  }
329
 
330
  /**
331
- * Sets the current language in the query
332
  *
333
  * @since 2.2
334
  *
335
- * @param object $query
 
336
  */
337
  protected function set_curlang_in_query( &$query ) {
338
  $pll_query = new PLL_Query( $query, $this->model );
9
  * @since 1.2
10
  */
11
  abstract class PLL_Choose_Lang {
12
+ /**
13
+ * Stores the plugin options.
14
+ *
15
+ * @var array
16
+ */
17
+ public $options;
18
+
19
+ /**
20
+ * @var PLL_Model
21
+ */
22
+ public $model;
23
+
24
+ /**
25
+ * Instance of a child class of PLL_Links_Model.
26
+ *
27
+ * @var PLL_Links_Model
28
+ */
29
+ public $links_model;
30
+
31
+ /**
32
+ * Current language.
33
+ *
34
+ * @var PLL_Language
35
+ */
36
  public $curlang;
37
+ /**
38
+ * @var PLL_Accept_Language
39
+ */
40
+ private $lang_parse;
41
+ /**
42
+ * @var PLL_Accept_Languages_Collection
43
+ */
44
+ private $accept_langs;
45
 
46
  /**
47
  * Constructor
64
  * Any child class must call this method if it overrides it
65
  *
66
  * @since 1.8
67
+ *
68
+ * @return void
69
  */
70
  public function init() {
71
  if ( Polylang::is_ajax_on_front() || ! wp_using_themes() ) {
78
  }
79
 
80
  /**
81
+ * Sets the current language
82
+ * and fires the action 'pll_language_defined'.
 
83
  *
84
  * @since 1.2
85
  *
86
+ * @param PLL_Language $curlang Current language.
87
+ * @return void
88
  */
89
  protected function set_language( $curlang ) {
90
  // Don't set the language a second time
100
  wp_styles()->text_direction = $GLOBALS['text_direction'];
101
 
102
  /**
103
+ * Fires when the current language is defined.
104
  *
105
  * @since 0.9.5
106
  *
107
+ * @param string $slug Current language code.
108
+ * @param PLL_Language $curlang Current language object.
109
  */
110
  do_action( 'pll_language_defined', $this->curlang->slug, $this->curlang );
111
  }
115
  * Setting PLL_COOKIE to false will disable cookie although it will break some functionalities
116
  *
117
  * @since 1.5
118
+ *
119
+ * @return void
120
  */
121
  public function maybe_setcookie() {
122
  // Don't set cookie in javascript when a cache plugin is active.
130
  }
131
 
132
  /**
133
+ * Get the preferred language according to the browser preferences.
 
134
  *
135
  * @since 1.8
136
  *
137
+ * @return string|bool The preferred language slug or false.
138
  */
139
  public function get_preferred_browser_language() {
 
 
140
  if ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) {
141
+ $accept_langs = PLL_Accept_Languages_Collection::from_accept_language_header( sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
+ $accept_langs->bubble_sort();
144
 
145
+ $languages = $this->model->get_languages_list( array( 'hide_empty' => true ) ); // Hides languages with no post.
146
 
147
+ /**
148
+ * Filters the list of languages to use to match the browser preferences.
149
+ *
150
+ * @since 1.9.3
151
+ *
152
+ * @param array $languages Array of PLL_Language objects.
153
+ */
154
+ $languages = apply_filters( 'pll_languages_for_browser_preferences', $languages );
 
 
 
 
 
 
 
 
 
155
 
156
+ return $accept_langs->find_best_match( $languages );
 
 
 
 
 
157
  }
158
+
159
  return false;
160
  }
161
 
203
  * Sets the language when home page is requested
204
  *
205
  * @since 1.2
206
+ *
207
+ * @return void
208
  */
209
  protected function home_language() {
210
  // Test referer in case PLL_COOKIE is set to false. Since WP 3.6.1, wp_get_referer() validates the host which is exactly what we want
221
  * Performs a redirection to the home page in the current language if needed
222
  *
223
  * @since 0.9
224
+ *
225
+ * @return void
226
  */
227
  public function home_requested() {
228
  // We are already on the right page
271
  * @since 0.8.4
272
  *
273
  * @param int $post_id the post being commented
274
+ * @return void
275
  */
276
  public function pre_comment_on_post( $post_id ) {
277
  $this->set_language( $this->model->post->get_language( $post_id ) );
278
  }
279
 
280
  /**
281
+ * Modifies some main query vars for the home page and the page for posts
282
+ * to enable one home page (and one page for posts) per language.
283
  *
284
  * @since 1.2
285
  *
286
+ * @param WP_Query $query Instance of WP_Query.
287
+ * @return void
288
  */
289
  public function parse_main_query( $query ) {
290
  if ( ! $query->is_main_query() ) {
296
  *
297
  * @since 1.8
298
  *
299
+ * @param PLL_Language|false $lang Language object or false.
300
+ * @param WP_Query $query WP_Query object.
301
  */
302
  if ( $lang = apply_filters( 'pll_set_language_from_query', false, $query ) ) {
303
  $this->set_language( $lang );
316
  }
317
 
318
  /**
319
+ * Sets the current language in the query.
320
  *
321
  * @since 2.2
322
  *
323
+ * @param WP_Query $query Instance of WP_Query.
324
+ * @return void
325
  */
326
  protected function set_curlang_in_query( &$query ) {
327
  $pll_query = new PLL_Query( $query, $this->model );
frontend/frontend-auto-translate.php CHANGED
@@ -10,7 +10,17 @@
10
  * @since 1.1
11
  */
12
  class PLL_Frontend_Auto_Translate {
13
- public $model, $curlang;
 
 
 
 
 
 
 
 
 
 
14
 
15
  /**
16
  * Constructor
@@ -56,7 +66,8 @@ class PLL_Frontend_Auto_Translate {
56
  *
57
  * @since 1.1
58
  *
59
- * @param object $query WP_Query object
 
60
  */
61
  public function pre_get_posts( $query ) {
62
  global $wpdb;
@@ -133,7 +144,7 @@ class PLL_Frontend_Auto_Translate {
133
  foreach ( array_intersect( $this->model->get_translated_taxonomies(), get_taxonomies( array( '_builtin' => false ) ) ) as $taxonomy ) {
134
  $tax = get_taxonomy( $taxonomy );
135
  $arr = array();
136
- if ( ! empty( $qv[ $tax->query_var ] ) ) {
137
  $sep = strpos( $qv[ $tax->query_var ], ',' ) !== false ? ',' : '+'; // Two possible separators
138
  foreach ( explode( $sep, $qv[ $tax->query_var ] ) as $slug ) {
139
  $arr[] = $this->get_translated_term_by( 'slug', $slug, $taxonomy );
10
  * @since 1.1
11
  */
12
  class PLL_Frontend_Auto_Translate {
13
+ /**
14
+ * @var PLL_Model
15
+ */
16
+ public $model;
17
+
18
+ /**
19
+ * Current language.
20
+ *
21
+ * @var PLL_Language
22
+ */
23
+ public $curlang;
24
 
25
  /**
26
  * Constructor
66
  *
67
  * @since 1.1
68
  *
69
+ * @param WP_Query $query WP_Query object
70
+ * @return void
71
  */
72
  public function pre_get_posts( $query ) {
73
  global $wpdb;
144
  foreach ( array_intersect( $this->model->get_translated_taxonomies(), get_taxonomies( array( '_builtin' => false ) ) ) as $taxonomy ) {
145
  $tax = get_taxonomy( $taxonomy );
146
  $arr = array();
147
+ if ( ! empty( $tax ) && ! empty( $qv[ $tax->query_var ] ) ) {
148
  $sep = strpos( $qv[ $tax->query_var ], ',' ) !== false ? ',' : '+'; // Two possible separators
149
  foreach ( explode( $sep, $qv[ $tax->query_var ] ) as $slug ) {
150
  $arr[] = $this->get_translated_term_by( 'slug', $slug, $taxonomy );
frontend/frontend-filters-links.php CHANGED
@@ -9,6 +9,12 @@
9
  * @since 1.8
10
  */
11
  class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
 
 
 
 
 
 
12
  /**
13
  * Our internal non persistent cache object
14
  *
@@ -125,13 +131,13 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
125
 
126
  /**
127
  * Modifies custom posts links
128
- * and caches the result
129
  *
130
  * @since 1.6
131
  *
132
- * @param string $link post link
133
- * @param object $post post object
134
- * @return string modified post link
135
  */
136
  public function post_type_link( $link, $post ) {
137
  $cache_key = "post:{$post->ID}:{$link}";
@@ -144,14 +150,14 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
144
 
145
  /**
146
  * Modifies filtered taxonomies ( post format like ) and translated taxonomies links
147
- * and caches the result
148
  *
149
  * @since 0.7
150
  *
151
- * @param string $link
152
- * @param object $term term object
153
- * @param string $tax taxonomy name
154
- * @return string modified link
155
  */
156
  public function term_link( $link, $term, $tax ) {
157
  $cache_key = "term:{$term->term_id}:{$link}";
@@ -178,7 +184,7 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
178
  *
179
  * @param string $link Post permalink.
180
  * @param int $post_id Post id.
181
- * @return Post permalink with the correct domain.
182
  */
183
  public function shortlink( $link, $post_id ) {
184
  $post_type = get_post_type( $post_id );
@@ -189,6 +195,8 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
189
  * Outputs references to translated pages ( if exists ) in the html head section
190
  *
191
  * @since 0.1
 
 
192
  */
193
  public function wp_head() {
194
  // Don't output anything on paged archives: see https://wordpress.org/support/topic/hreflang-on-page2
@@ -345,30 +353,30 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
345
  }
346
 
347
  /**
348
- * If the language code is not in agreement with the language of the content
349
- * redirects incoming links to the proper URL to avoid duplicate content
350
  *
351
  * @since 0.9.6
352
  *
353
- * @param string $requested_url optional
354
- * @param bool $do_redirect optional, whether to perform the redirection or not
355
- * @return string if redirect is not performed
356
  */
357
  public function check_canonical_url( $requested_url = '', $do_redirect = true ) {
358
- global $wp_query, $post, $is_IIS;
359
-
360
- // Don't redirect in same cases as WP
361
- if ( is_trackback() || is_search() || is_admin() || is_preview() || is_robots() || ( $is_IIS && ! iis7_supports_permalinks() ) ) {
362
  return;
363
  }
364
 
365
- // Don't redirect mysite.com/?attachment_id= to mysite.com/en/?attachment_id=
366
  if ( 1 == $this->options['force_lang'] && is_attachment() && isset( $_GET['attachment_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
367
  return;
368
  }
369
 
370
- // If the default language code is not hidden and the static front page url contains the page name
371
- // the customizer lands here and the code below would redirect to the list of posts
 
 
372
  if ( is_customize_preview() ) {
373
  return;
374
  }
@@ -378,15 +386,16 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
378
  }
379
 
380
  if ( is_single() || is_page() ) {
381
- if ( isset( $post->ID ) && $this->model->is_translated_post_type( $post->post_type ) ) {
 
382
  $language = $this->model->post->get_language( (int) $post->ID );
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
  }
@@ -394,28 +403,30 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
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'] ) {
@@ -445,19 +456,24 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
445
  }
446
 
447
  /**
448
- * Filters the canonical url detected by Polylang
449
  *
450
  * @since 1.6
451
  *
452
- * @param bool|string $redirect_url false or the url to redirect to
453
- * @param object $language the language detected
454
  */
455
  $redirect_url = apply_filters( 'pll_check_canonical_url', $redirect_url, $language );
456
 
457
  // The language is not correctly set so let's redirect to the correct url for this object
458
- if ( $do_redirect && $redirect_url && $requested_url != $redirect_url ) {
459
- wp_safe_redirect( $redirect_url, 301, POLYLANG );
460
- exit;
 
 
 
 
 
461
  }
462
 
463
  return $redirect_url;
@@ -472,9 +488,7 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
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;
@@ -485,7 +499,7 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
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 ) {
@@ -517,7 +531,7 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
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 ) {
@@ -526,4 +540,15 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
526
 
527
  return key( $queried_terms );
528
  }
 
 
 
 
 
 
 
 
 
 
 
529
  }
9
  * @since 1.8
10
  */
11
  class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
12
+
13
+ /**
14
+ * @var PLL_Frontend_Links
15
+ */
16
+ public $links;
17
+
18
  /**
19
  * Our internal non persistent cache object
20
  *
131
 
132
  /**
133
  * Modifies custom posts links
134
+ * and caches the result.
135
  *
136
  * @since 1.6
137
  *
138
+ * @param string $link Post link.
139
+ * @param WP_Post $post Post object.
140
+ * @return string Modified post link.
141
  */
142
  public function post_type_link( $link, $post ) {
143
  $cache_key = "post:{$post->ID}:{$link}";
150
 
151
  /**
152
  * Modifies filtered taxonomies ( post format like ) and translated taxonomies links
153
+ * and caches the result.
154
  *
155
  * @since 0.7
156
  *
157
+ * @param string $link Term link.
158
+ * @param WP_Term $term Term object.
159
+ * @param string $tax Taxonomy name.
160
+ * @return string Modified link.
161
  */
162
  public function term_link( $link, $term, $tax ) {
163
  $cache_key = "term:{$term->term_id}:{$link}";
184
  *
185
  * @param string $link Post permalink.
186
  * @param int $post_id Post id.
187
+ * @return string Post permalink with the correct domain.
188
  */
189
  public function shortlink( $link, $post_id ) {
190
  $post_type = get_post_type( $post_id );
195
  * Outputs references to translated pages ( if exists ) in the html head section
196
  *
197
  * @since 0.1
198
+ *
199
+ * @return void
200
  */
201
  public function wp_head() {
202
  // Don't output anything on paged archives: see https://wordpress.org/support/topic/hreflang-on-page2
353
  }
354
 
355
  /**
356
+ * If the language code is not in agreement with the language of the content,
357
+ * redirects incoming links to the proper URL to avoid duplicate content.
358
  *
359
  * @since 0.9.6
360
  *
361
+ * @param string $requested_url Optional, defaults to requested url.
362
+ * @param bool $do_redirect Optional, whether to perform the redirect or not.
363
+ * @return string|void Returns if redirect is not performed.
364
  */
365
  public function check_canonical_url( $requested_url = '', $do_redirect = true ) {
366
+ // Don't redirect in same cases as WP.
367
+ if ( is_trackback() || is_search() || is_admin() || is_preview() || is_robots() || ( $GLOBALS['is_IIS'] && ! iis7_supports_permalinks() ) ) {
 
 
368
  return;
369
  }
370
 
371
+ // Don't redirect mysite.com/?attachment_id= to mysite.com/en/?attachment_id=.
372
  if ( 1 == $this->options['force_lang'] && is_attachment() && isset( $_GET['attachment_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
373
  return;
374
  }
375
 
376
+ /*
377
+ * If the default language code is not hidden and the static front page url contains the page name,
378
+ * the customizer lands here and the code below would redirect to the list of posts.
379
+ */
380
  if ( is_customize_preview() ) {
381
  return;
382
  }
386
  }
387
 
388
  if ( is_single() || is_page() ) {
389
+ $post = get_post();
390
+ if ( $post instanceof WP_Post && $this->model->is_translated_post_type( $post->post_type ) ) {
391
  $language = $this->model->post->get_language( (int) $post->ID );
392
  }
393
  }
394
 
395
+ elseif ( $this->links_model->using_permalinks && is_category() && ! empty( $this->wp_query()->query['cat'] ) ) {
396
  // When we receive a plain permaling with a cat query var, we need to redirect to the pretty permalink.
397
+ if ( $this->model->is_translated_taxonomy( $this->get_queried_taxonomy( $this->wp_query()->tax_query ) ) ) {
398
+ $term_id = $this->get_queried_term_id( $this->wp_query()->tax_query );
399
  $language = $this->model->term->get_language( $term_id );
400
  $redirect_url = $this->maybe_add_page_to_redirect_url( get_term_link( $term_id ) );
401
  }
403
 
404
  elseif ( is_category() || is_tag() || is_tax() ) {
405
  // We need to switch the language when there is no language provided in a pretty permalink.
406
+ $obj = get_queried_object();
407
  if ( ! empty( $obj ) && $this->model->is_translated_taxonomy( $obj->taxonomy ) ) {
408
  $language = $this->model->term->get_language( (int) $obj->term_id );
409
  }
410
  }
411
 
412
+ elseif ( is_404() && ! empty( $this->wp_query()->tax_query ) ) {
413
  // When a wrong language is passed through a pretty permalink, we just need to switch the language.
414
+ if ( $this->model->is_translated_taxonomy( $this->get_queried_taxonomy( $this->wp_query()->tax_query ) ) ) {
415
+ $term_id = $this->get_queried_term_id( $this->wp_query()->tax_query );
416
  $language = $this->model->term->get_language( $term_id );
417
  }
418
  }
419
 
420
+ elseif ( $this->links_model->using_permalinks && $this->wp_query()->is_posts_page && ! empty( $this->wp_query()->query['page_id'] ) && $id = get_query_var( 'page_id' ) ) {
421
  $language = $this->model->post->get_language( (int) $id );
422
  $redirect_url = $this->maybe_add_page_to_redirect_url( get_permalink( $id ) );
423
  }
424
 
425
+ elseif ( $this->wp_query()->is_posts_page ) {
426
+ $obj = get_queried_object();
427
+ if ( $obj instanceof WP_Post ) {
428
+ $language = $this->model->post->get_language( (int) $obj->ID );
429
+ }
430
  }
431
 
432
  if ( 3 === $this->options['force_lang'] ) {
456
  }
457
 
458
  /**
459
+ * Filters the canonical url detected by Polylang.
460
  *
461
  * @since 1.6
462
  *
463
+ * @param string|false $redirect_url False or the url to redirect to.
464
+ * @param PLL_Language $language The language detected.
465
  */
466
  $redirect_url = apply_filters( 'pll_check_canonical_url', $redirect_url, $language );
467
 
468
  // The language is not correctly set so let's redirect to the correct url for this object
469
+ if ( $do_redirect ) {
470
+ // Protect against chained redirects.
471
+ if ( $redirect_url && $requested_url != $redirect_url && $redirect_url === $this->check_canonical_url( $redirect_url, false ) ) {
472
+ wp_safe_redirect( $redirect_url, 301, POLYLANG );
473
+ exit;
474
+ } else {
475
+ return;
476
+ }
477
  }
478
 
479
  return $redirect_url;
488
  * @return string
489
  */
490
  protected function maybe_add_page_to_redirect_url( $redirect_url ) {
491
+ if ( ! empty( $this->wp_query()->query['paged'] ) && $page = get_query_var( 'paged' ) ) {
 
 
492
  $redirect_url = $this->links_model->add_paged_to_link( $redirect_url, $page );
493
  }
494
  return $redirect_url;
499
  *
500
  * @since 2.9
501
  *
502
+ * @param WP_Tax_Query $tax_query An instance of WP_Tax_Query.
503
  * @return int
504
  */
505
  protected function get_queried_term_id( $tax_query ) {
531
  *
532
  * @since 2.9
533
  *
534
+ * @param WP_Tax_Query $tax_query An instance of WP_Tax_Query.
535
  * @return string A taxonomy slug
536
  */
537
  protected function get_queried_taxonomy( $tax_query ) {
540
 
541
  return key( $queried_terms );
542
  }
543
+
544
+ /**
545
+ * Returns the Global WordPress WP_Query object.
546
+ *
547
+ * @since 3.0
548
+ *
549
+ * @return WP_Query
550
+ */
551
+ protected function wp_query() {
552
+ return $GLOBALS['wp_query'];
553
+ }
554
  }
frontend/frontend-filters-search.php CHANGED
@@ -9,7 +9,19 @@
9
  * @since 1.2
10
  */
11
  class PLL_Frontend_Filters_Search {
12
- public $links_model, $curlang;
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  /**
15
  * Constructor
@@ -37,13 +49,14 @@ class PLL_Frontend_Filters_Search {
37
  }
38
 
39
  /**
40
- * Adds the language information in the search form
 
41
  * Does not work if searchform.php ( prior to WP 3.6 ) is used or if the search form is hardcoded in another template file
42
  *
43
  * @since 0.1
44
  *
45
- * @param string $form Search form
46
- * @return string Modified search form
47
  */
48
  public function get_search_form( $form ) {
49
  if ( $form ) {
@@ -51,10 +64,9 @@ class PLL_Frontend_Filters_Search {
51
  // Take care to modify only the url in the <form> tag.
52
  preg_match( '#<form.+>#', $form, $matches );
53
  $old = reset( $matches );
54
- $new = preg_replace( '#' . esc_url( $this->links_model->home ) . '\/?#', esc_url( $this->curlang->search_url ), $old );
55
  $form = str_replace( $old, $new, $form );
56
- }
57
- else {
58
  $form = str_replace( '</form>', '<input type="hidden" name="lang" value="' . esc_attr( $this->curlang->slug ) . '" /></form>', $form );
59
  }
60
  }
@@ -66,6 +78,8 @@ class PLL_Frontend_Filters_Search {
66
  * Adds the language information in admin bar search form
67
  *
68
  * @since 1.2
 
 
69
  */
70
  public function add_admin_bar_menus() {
71
  remove_action( 'admin_bar_menu', 'wp_admin_bar_search_menu', 4 );
@@ -79,6 +93,7 @@ class PLL_Frontend_Filters_Search {
79
  * @since 0.9
80
  *
81
  * @param WP_Admin_Bar $wp_admin_bar
 
82
  */
83
  public function admin_bar_search_menu( $wp_admin_bar ) {
84
  $form = '<form action="' . esc_url( home_url( '/' ) ) . '" method="get" id="adminbarsearch">';
@@ -104,6 +119,8 @@ class PLL_Frontend_Filters_Search {
104
  * Allows modifying the search form if it does not pass get_search_form
105
  *
106
  * @since 0.1
 
 
107
  */
108
  public function wp_print_footer_scripts() {
109
  // Don't use directly e[0] just in case there is somewhere else an element named 's'
9
  * @since 1.2
10
  */
11
  class PLL_Frontend_Filters_Search {
12
+ /**
13
+ * Instance of a child class of PLL_Links_Model.
14
+ *
15
+ * @var PLL_Links_Model
16
+ */
17
+ public $links_model;
18
+
19
+ /**
20
+ * Current language.
21
+ *
22
+ * @var PLL_Language
23
+ */
24
+ public $curlang;
25
 
26
  /**
27
  * Constructor
49
  }
50
 
51
  /**
52
+ * Adds the language information in the search form.
53
+ *
54
  * Does not work if searchform.php ( prior to WP 3.6 ) is used or if the search form is hardcoded in another template file
55
  *
56
  * @since 0.1
57
  *
58
+ * @param string $form The search form HTML.
59
+ * @return string Modified search form.
60
  */
61
  public function get_search_form( $form ) {
62
  if ( $form ) {
64
  // Take care to modify only the url in the <form> tag.
65
  preg_match( '#<form.+>#', $form, $matches );
66
  $old = reset( $matches );
67
+ $new = preg_replace( '#action="(.+)"#', 'action="' . esc_url( $this->curlang->search_url ) . '"', $old );
68
  $form = str_replace( $old, $new, $form );
69
+ } else {
 
70
  $form = str_replace( '</form>', '<input type="hidden" name="lang" value="' . esc_attr( $this->curlang->slug ) . '" /></form>', $form );
71
  }
72
  }
78
  * Adds the language information in admin bar search form
79
  *
80
  * @since 1.2
81
+ *
82
+ * @return void
83
  */
84
  public function add_admin_bar_menus() {
85
  remove_action( 'admin_bar_menu', 'wp_admin_bar_search_menu', 4 );
93
  * @since 0.9
94
  *
95
  * @param WP_Admin_Bar $wp_admin_bar
96
+ * @return void
97
  */
98
  public function admin_bar_search_menu( $wp_admin_bar ) {
99
  $form = '<form action="' . esc_url( home_url( '/' ) ) . '" method="get" id="adminbarsearch">';
119
  * Allows modifying the search form if it does not pass get_search_form
120
  *
121
  * @since 0.1
122
+ *
123
+ * @return void
124
  */
125
  public function wp_print_footer_scripts() {
126
  // Don't use directly e[0] just in case there is somewhere else an element named 's'
frontend/frontend-filters.php CHANGED
@@ -9,6 +9,11 @@
9
  * @since 1.2
10
  */
11
  class PLL_Frontend_Filters extends PLL_Filters {
 
 
 
 
 
12
  public $cache;
13
 
14
  /**
@@ -49,8 +54,7 @@ class PLL_Frontend_Filters extends PLL_Filters {
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' ) );
55
  }
56
  }
@@ -67,18 +71,18 @@ class PLL_Frontend_Filters extends PLL_Filters {
67
  }
68
 
69
  /**
70
- * Filters sticky posts by current language
71
  *
72
  * @since 0.8
73
  *
74
- * @param array $posts list of sticky posts ids
75
- * @return array modified list of sticky posts ids
76
  */
77
  public function option_sticky_posts( $posts ) {
78
  global $wpdb;
79
 
80
  // Do not filter sticky posts on REST requests as $this->curlang is *not* the 'lang' parameter set in the request
81
- if ( ! defined( 'REST_REQUEST' ) && $this->curlang && ! empty( $posts ) ) {
82
  $_posts = wp_cache_get( 'sticky_posts', 'options' ); // This option is usually cached in 'all_options' by WP
83
 
84
  if ( empty( $_posts ) || ! is_array( $_posts[ $this->curlang->term_taxonomy_id ] ) ) {
9
  * @since 1.2
10
  */
11
  class PLL_Frontend_Filters extends PLL_Filters {
12
+ /**
13
+ * Internal non persistent cache object.
14
+ *
15
+ * @var PLL_Cache
16
+ */
17
  public $cache;
18
 
19
  /**
54
  // Translates biography
55
  add_filter( 'get_user_metadata', array( $this, 'get_user_metadata' ), 10, 4 );
56
 
57
+ if ( Polylang::is_ajax_on_front() ) {
 
58
  add_filter( 'load_textdomain_mofile', array( $this, 'load_textdomain_mofile' ) );
59
  }
60
  }
71
  }
72
 
73
  /**
74
+ * Filters sticky posts by current language.
75
  *
76
  * @since 0.8
77
  *
78
+ * @param int[] $posts List of sticky posts ids.
79
+ * @return int[] Modified list of sticky posts ids
80
  */
81
  public function option_sticky_posts( $posts ) {
82
  global $wpdb;
83
 
84
  // Do not filter sticky posts on REST requests as $this->curlang is *not* the 'lang' parameter set in the request
85
+ if ( ! defined( 'REST_REQUEST' ) && ! empty( $this->curlang ) && ! empty( $posts ) ) {
86
  $_posts = wp_cache_get( 'sticky_posts', 'options' ); // This option is usually cached in 'all_options' by WP
87
 
88
  if ( empty( $_posts ) || ! is_array( $_posts[ $this->curlang->term_taxonomy_id ] ) ) {
frontend/frontend-links.php CHANGED
@@ -9,8 +9,19 @@
9
  * @since 1.2
10
  */
11
  class PLL_Frontend_Links extends PLL_Links {
 
 
 
 
 
12
  public $curlang;
13
- public $cache; // Our internal non persistent cache object
 
 
 
 
 
 
14
 
15
  /**
16
  * Constructor
@@ -28,11 +39,11 @@ class PLL_Frontend_Links extends PLL_Links {
28
  }
29
 
30
  /**
31
- * Returns the url of the translation ( if exists ) of the current page
32
  *
33
  * @since 0.1
34
  *
35
- * @param object $language
36
  * @return string
37
  */
38
  public function get_translation_url( $language ) {
@@ -47,14 +58,14 @@ class PLL_Frontend_Links extends PLL_Links {
47
  $queried_object_id = $wp_query->get_queried_object_id();
48
 
49
  /**
50
- * Filter the translation url before Polylang attempts to find one
51
- * Internally used by Polylang for the static front page and posts page
52
  *
53
  * @since 1.8
54
  *
55
- * @param string $url Empty or the url of the translation of teh current page
56
- * @param object $language Language of the translation
57
- * @param int $queried_object_id Queried object id
58
  */
59
  if ( ! $url = apply_filters( 'pll_pre_translation_url', '', $language, $queried_object_id ) ) {
60
  $qv = $wp_query->query_vars;
@@ -199,12 +210,12 @@ class PLL_Frontend_Links extends PLL_Links {
199
  }
200
 
201
  /**
202
- * Returns the home url in the right language
203
  *
204
  * @since 0.1
205
  *
206
- * @param object $language Optional, defaults to current language
207
- * @param bool $is_search Optional, whether we need the home url for a search form, defaults to false
208
  */
209
  public function get_home_url( $language = '', $is_search = false ) {
210
  if ( empty( $language ) ) {
9
  * @since 1.2
10
  */
11
  class PLL_Frontend_Links extends PLL_Links {
12
+ /**
13
+ * Current language.
14
+ *
15
+ * @var PLL_Language
16
+ */
17
  public $curlang;
18
+
19
+ /**
20
+ * Internal non persistent cache object.
21
+ *
22
+ * @var PLL_Cache
23
+ */
24
+ public $cache;
25
 
26
  /**
27
  * Constructor
39
  }
40
 
41
  /**
42
+ * Returns the url of the translation (if it exists) of the current page.
43
  *
44
  * @since 0.1
45
  *
46
+ * @param PLL_Language $language Language object.
47
  * @return string
48
  */
49
  public function get_translation_url( $language ) {
58
  $queried_object_id = $wp_query->get_queried_object_id();
59
 
60
  /**
61
+ * Filters the translation url before Polylang attempts to find one.
62
+ * Internally used by Polylang for the static front page and posts page.
63
  *
64
  * @since 1.8
65
  *
66
+ * @param string $url Empty or the url of the translation of teh current page.
67
+ * @param PLL_Language $language Language of the translation.
68
+ * @param int $queried_object_id Queried object id.
69
  */
70
  if ( ! $url = apply_filters( 'pll_pre_translation_url', '', $language, $queried_object_id ) ) {
71
  $qv = $wp_query->query_vars;
210
  }
211
 
212
  /**
213
+ * Returns the home url in the right language.
214
  *
215
  * @since 0.1
216
  *
217
+ * @param PLL_Language|string $language Optional, defaults to current language.
218
+ * @param bool $is_search Optional, whether we need the home url for a search form, defaults to false.
219
  */
220
  public function get_home_url( $language = '', $is_search = false ) {
221
  if ( empty( $language ) ) {
frontend/frontend-nav-menu.php CHANGED
@@ -9,6 +9,11 @@
9
  * @since 1.2
10
  */
11
  class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
 
 
 
 
 
12
  public $curlang;
13
 
14
  /**
@@ -40,12 +45,12 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
40
  }
41
 
42
  /**
43
- * Sort menu items by menu order
44
  *
45
  * @since 1.7.9
46
  *
47
- * @param object $a The first object to compare
48
- * @param object $b The second object to compare
49
  * @return int -1 or 1 if $a is considered to be respectively less than or greater than $b.
50
  */
51
  protected function usort_menu_items( $a, $b ) {
@@ -76,13 +81,13 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
76
  }
77
 
78
  /**
79
- * Splits the one item of backend in several items on frontend
80
- * take care to menu_order as it is used later in wp_nav_menu
81
  *
82
  * @since 1.1.1
83
  *
84
- * @param array $items menu items
85
- * @return array modified items
86
  */
87
  public function wp_get_nav_menu_items( $items ) {
88
  if ( doing_action( 'customize_register' ) ) { // needed since WP 4.3, doing_action available since WP 3.9
@@ -104,6 +109,8 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
104
 
105
  $switcher = new PLL_Switcher();
106
  $args = array_merge( array( 'raw' => 1 ), $options );
 
 
107
  $the_languages = $switcher->the_languages( PLL()->links, $args );
108
 
109
  // parent item for dropdown
@@ -141,12 +148,12 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
141
  }
142
 
143
  /**
144
- * Returns the ancestors of a menu item
145
  *
146
  * @since 1.1.1
147
  *
148
- * @param object $item
149
- * @return array ancestors ids
150
  */
151
  public function get_ancestors( $item ) {
152
  $ids = array();
@@ -158,12 +165,12 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
158
  }
159
 
160
  /**
161
- * Removes current-menu and current-menu-ancestor classes to lang switcher when not on the home page
162
  *
163
  * @since 1.1.1
164
  *
165
- * @param array $items
166
- * @return array modified menu items
167
  */
168
  public function wp_nav_menu_objects( $items ) {
169
  $r_ids = $k_ids = array();
@@ -192,14 +199,14 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
192
  }
193
 
194
  /**
195
- * Adds hreflang attribute for the language switcher menu items
196
- * available since WP 3.6
197
  *
198
  * @since 1.1
199
  *
200
- * @param array $atts
201
- * @param object $item
202
- * @return array modified $atts
203
  */
204
  public function nav_menu_link_attributes( $atts, $item ) {
205
  if ( isset( $item->lang ) ) {
9
  * @since 1.2
10
  */
11
  class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
12
+ /**
13
+ * Current language.
14
+ *
15
+ * @var PLL_Language
16
+ */
17
  public $curlang;
18
 
19
  /**
45
  }
46
 
47
  /**
48
+ * Sorts menu items by menu order.
49
  *
50
  * @since 1.7.9
51
  *
52
+ * @param stdClass $a The first object to compare.
53
+ * @param stdClass $b The second object to compare.
54
  * @return int -1 or 1 if $a is considered to be respectively less than or greater than $b.
55
  */
56
  protected function usort_menu_items( $a, $b ) {
81
  }
82
 
83
  /**
84
+ * Splits the one language switcher menu item of backend in several menu items on frontend.
85
+ * Takes care to menu_order as it is used later in wp_nav_menu().
86
  *
87
  * @since 1.1.1
88
  *
89
+ * @param stdClass[] $items Menu items.
90
+ * @return stdClass[] Modified menu items.
91
  */
92
  public function wp_get_nav_menu_items( $items ) {
93
  if ( doing_action( 'customize_register' ) ) { // needed since WP 4.3, doing_action available since WP 3.9
109
 
110
  $switcher = new PLL_Switcher();
111
  $args = array_merge( array( 'raw' => 1 ), $options );
112
+
113
+ /** @var array */
114
  $the_languages = $switcher->the_languages( PLL()->links, $args );
115
 
116
  // parent item for dropdown
148
  }
149
 
150
  /**
151
+ * Returns the ancestors of a menu item.
152
  *
153
  * @since 1.1.1
154
  *
155
+ * @param stdClass $item Menu item.
156
+ * @return int[] Ancestors ids.
157
  */
158
  public function get_ancestors( $item ) {
159
  $ids = array();
165
  }
166
 
167
  /**
168
+ * Removes current-menu and current-menu-ancestor classes to lang switcher when not on the home page.
169
  *
170
  * @since 1.1.1
171
  *
172
+ * @param stdClass[] $items An array of menu items.
173
+ * @return stdClass[]
174
  */
175
  public function wp_nav_menu_objects( $items ) {
176
  $r_ids = $k_ids = array();
199
  }
200
 
201
  /**
202
+ * Adds hreflang attribute for the language switcher menu items.
203
+ * available since WP 3.6.
204
  *
205
  * @since 1.1
206
  *
207
+ * @param string[] $atts HTML attributes applied to the menu item's `<a>` element.
208
+ * @param stdClass $item Menu item.
209
+ * @return string[] Modified attributes.
210
  */
211
  public function nav_menu_link_attributes( $atts, $item ) {
212
  if ( isset( $item->lang ) ) {
frontend/frontend-static-pages.php CHANGED
@@ -9,6 +9,17 @@
9
  * @since 1.8
10
  */
11
  class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  /**
14
  * Constructor: setups filters and actions
@@ -40,6 +51,8 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
40
  * Init the filters
41
  *
42
  * @since 1.8
 
 
43
  */
44
  public function pll_language_defined() {
45
  // Translates our page on front and page for posts properties
@@ -60,6 +73,8 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
60
  * Translates the page_id query var when the site root page is requested
61
  *
62
  * @since 1.8
 
 
63
  */
64
  public function pll_home_requested() {
65
  set_query_var( 'page_id', $this->curlang->page_on_front );
@@ -87,9 +102,8 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
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();
93
 
94
  // Don't forget additional query vars
95
  $query = wp_parse_url( $redirect_url, PHP_URL_QUERY );
@@ -106,14 +120,14 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
106
  }
107
 
108
  /**
109
- * Translates the url of the page on front and page for posts
110
  *
111
  * @since 1.8
112
  *
113
- * @param string $url not used
114
- * @param object $language language in which we want the translation
115
- * @param int $queried_object_id id of the queried object
116
- * @return string
117
  */
118
  public function pll_pre_translation_url( $url, $language, $queried_object_id ) {
119
  if ( ! empty( $queried_object_id ) ) {
@@ -148,7 +162,7 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
148
  *
149
  * @since 2.3
150
  *
151
- * @param object $query
152
  * @return bool
153
  */
154
  protected function is_front_page( $query ) {
@@ -161,9 +175,9 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
161
  *
162
  * @since 1.8
163
  *
164
- * @param bool|object $lang
165
- * @param object $query
166
- * @return bool|object
167
  */
168
  public function page_on_front_query( $lang, $query ) {
169
  if ( ! empty( $lang ) || ! $this->page_on_front ) {
@@ -224,9 +238,9 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
224
  *
225
  * @since 1.8
226
  *
227
- * @param bool|object $lang
228
- * @param object $query
229
- * @return bool|object
230
  */
231
  public function page_for_posts_query( $lang, $query ) {
232
  if ( empty( $lang ) && $this->page_for_posts ) {
@@ -246,14 +260,15 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
246
  }
247
 
248
  /**
249
- * Get queried page_id ( if exists )
250
- * If permalinks are used, WordPress does set and use $query->queried_object_id and sets $query->query_vars['page_id'] to 0
251
- * and does set and use $query->query_vars['page_id'] if permalinks are not used :(
 
252
  *
253
  * @since 1.5
254
  *
255
- * @param object $query instance of WP_Query
256
- * @return int page_id
257
  */
258
  protected function get_page_id( $query ) {
259
  if ( ! empty( $query->query_vars['pagename'] ) && isset( $query->queried_object_id ) ) {
@@ -264,6 +279,6 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
264
  return $query->query_vars['page_id'];
265
  }
266
 
267
- return 0; // No page queried
268
  }
269
  }
9
  * @since 1.8
10
  */
11
  class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
12
+ /**
13
+ * Instance of a child class of PLL_Links_Model.
14
+ *
15
+ * @var PLL_Links_Model
16
+ */
17
+ protected $links_model;
18
+
19
+ /**
20
+ * @var PLL_Frontend_Links
21
+ */
22
+ protected $links;
23
 
24
  /**
25
  * Constructor: setups filters and actions
51
  * Init the filters
52
  *
53
  * @since 1.8
54
+ *
55
+ * @return void
56
  */
57
  public function pll_language_defined() {
58
  // Translates our page on front and page for posts properties
73
  * Translates the page_id query var when the site root page is requested
74
  *
75
  * @since 1.8
76
+ *
77
+ * @return void
78
  */
79
  public function pll_home_requested() {
80
  set_query_var( 'page_id', $this->curlang->page_on_front );
102
  * @return bool|string modified url, false if redirection is canceled
103
  */
104
  public function redirect_canonical( $redirect_url ) {
105
+ if ( is_page() && ! is_feed() && get_queried_object_id() == $this->curlang->page_on_front ) {
106
+ $url = is_paged() ? $this->links_model->add_paged_to_link( $this->links->get_home_url(), get_query_var( 'page' ) ) : $this->links->get_home_url();
 
107
 
108
  // Don't forget additional query vars
109
  $query = wp_parse_url( $redirect_url, PHP_URL_QUERY );
120
  }
121
 
122
  /**
123
+ * Translates the url of the page on front and page for posts.
124
  *
125
  * @since 1.8
126
  *
127
+ * @param string $url Not used.
128
+ * @param PLL_Language $language Language in which we want the translation.
129
+ * @param int $queried_object_id Id of the queried object.
130
+ * @return string The translation url.
131
  */
132
  public function pll_pre_translation_url( $url, $language, $queried_object_id ) {
133
  if ( ! empty( $queried_object_id ) ) {
162
  *
163
  * @since 2.3
164
  *
165
+ * @param WP_Query $query The WP_Query object.
166
  * @return bool
167
  */
168
  protected function is_front_page( $query ) {
175
  *
176
  * @since 1.8
177
  *
178
+ * @param PLL_Language|false $lang The current language, false if it is not set yet.
179
+ * @param WP_Query $query The main WP query.
180
+ * @return PLL_Language|false
181
  */
182
  public function page_on_front_query( $lang, $query ) {
183
  if ( ! empty( $lang ) || ! $this->page_on_front ) {
238
  *
239
  * @since 1.8
240
  *
241
+ * @param PLL_Language|false $lang The current language, false if it is not set yet.
242
+ * @param WP_Query $query The main WP query.
243
+ * @return PLL_Language|false
244
  */
245
  public function page_for_posts_query( $lang, $query ) {
246
  if ( empty( $lang ) && $this->page_for_posts ) {
260
  }
261
 
262
  /**
263
+ * Get the queried page_id (if it exists ).
264
+ *
265
+ * If permalinks are used, WordPress does set and use `$query->queried_object_id` and sets `$query->query_vars['page_id']` to 0,
266
+ * and does set and use `$query->query_vars['page_id']` if permalinks are not used :(.
267
  *
268
  * @since 1.5
269
  *
270
+ * @param WP_Query $query Instance of WP_Query.
271
+ * @return int The page_id.
272
  */
273
  protected function get_page_id( $query ) {
274
  if ( ! empty( $query->query_vars['pagename'] ) && isset( $query->queried_object_id ) ) {
279
  return $query->query_vars['page_id'];
280
  }
281
 
282
+ return 0; // No page queried.
283
  }
284
  }
frontend/frontend.php CHANGED
@@ -4,37 +4,66 @@
4
  */
5
 
6
  /**
7
- * Frontend controller
8
- * accessible as $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 => reference to PLL_Links object
15
- * static_pages => reference to PLL_Frontend_Static_Pages object
16
- * choose_lang => reference to PLL_Choose_Lang object
17
- * curlang => current language
18
- * filters => reference to PLL_Frontend_Filters object
19
- * filters_links => reference to PLL_Frontend_Filters_Links object
20
- * filters_search => reference to PLL_Frontend_Filters_Search object
21
- * posts => reference to PLL_CRUD_Posts object
22
- * terms => reference to PLL_CRUD_Terms object
23
- * nav_menu => reference to PLL_Frontend_Nav_Menu object
24
- * auto_translate => optional, reference to PLL_Auto_Translate object
25
  *
26
  * @since 1.2
27
  */
28
  class PLL_Frontend extends PLL_Base {
 
 
 
 
 
29
  public $curlang;
30
- public $links, $choose_lang, $filters, $filters_search, $nav_menu, $auto_translate;
31
 
32
  /**
33
- * Constructor
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  *
35
  * @since 1.2
36
  *
37
- * @param object $links_model
38
  */
39
  public function __construct( &$links_model ) {
40
  parent::__construct( $links_model );
@@ -82,6 +111,8 @@ class PLL_Frontend extends PLL_Base {
82
  * Setups filters and nav menus once the language has been defined
83
  *
84
  * @since 1.2
 
 
85
  */
86
  public function pll_language_defined() {
87
  // Filters
@@ -96,11 +127,12 @@ class PLL_Frontend extends PLL_Base {
96
  }
97
 
98
  /**
99
- * When querying multiple taxonomies, makes sure that the language is not the queried object
100
  *
101
  * @since 1.8
102
  *
103
- * @param object $query WP_Query object
 
104
  */
105
  public function parse_tax_query( $query ) {
106
  $pll_query = new PLL_Query( $query, $this->model );
@@ -112,11 +144,12 @@ class PLL_Frontend extends PLL_Base {
112
  }
113
 
114
  /**
115
- * Modifies some query vars to "hide" that the language is a taxonomy and avoid conflicts
116
  *
117
  * @since 1.2
118
  *
119
- * @param object $query WP_Query object
 
120
  */
121
  public function parse_query( $query ) {
122
  $qv = $query->query_vars;
@@ -157,26 +190,31 @@ class PLL_Frontend extends PLL_Base {
157
  * Auto translate posts and terms ids
158
  *
159
  * @since 1.2
 
 
160
  */
161
  public function auto_translate() {
162
  $this->auto_translate = new PLL_Frontend_Auto_Translate( $this );
163
  }
164
 
165
  /**
166
- * Resets some variables when switching blog
167
- * Overrides parent method
168
  *
169
  * @since 1.5.1
170
  *
171
- * @param int $new_blog
172
- * @param int $old_blog
 
173
  */
174
- public function switch_blog( $new_blog, $old_blog ) {
175
- // Need to check that some languages are defined when user is logged in, has several blogs, some without any languages
176
- if ( parent::switch_blog( $new_blog, $old_blog ) && did_action( 'pll_language_defined' ) && $this->model->get_languages_list() ) {
 
 
177
  static $restore_curlang;
178
  if ( empty( $restore_curlang ) ) {
179
- $restore_curlang = $this->curlang->slug; // To always remember the current language through blogs
180
  }
181
 
182
  $lang = $this->model->get_language( $restore_curlang );
4
  */
5
 
6
  /**
7
+ * Main Polylang class when on frontend, accessible from @see PLL().
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  *
9
  * @since 1.2
10
  */
11
  class PLL_Frontend extends PLL_Base {
12
+ /**
13
+ * Current language.
14
+ *
15
+ * @var PLL_Language
16
+ */
17
  public $curlang;
 
18
 
19
  /**
20
+ * @var PLL_Frontend_Auto_Translate
21
+ */
22
+ public $auto_translate;
23
+
24
+ /**
25
+ * The class selecting the current language.
26
+ *
27
+ * @var PLL_Choose_Lang
28
+ */
29
+ public $choose_lang;
30
+
31
+ /**
32
+ * @var PLL_Frontend_Filters
33
+ */
34
+ public $filters;
35
+
36
+ /**
37
+ * @var PLL_Frontend_Filters_Links
38
+ */
39
+ public $filters_links;
40
+
41
+ /**
42
+ * @var PLL_Frontend_Filters_Search
43
+ */
44
+ public $filters_search;
45
+
46
+ /**
47
+ * @var PLL_Frontend_Links
48
+ */
49
+ public $links;
50
+
51
+ /**
52
+ * @var PLL_Frontend_Nav_Menu
53
+ */
54
+ public $nav_menu;
55
+
56
+ /**
57
+ * @var PLL_Frontend_Static_Pages
58
+ */
59
+ public $static_pages;
60
+
61
+ /**
62
+ * Constructor.
63
  *
64
  * @since 1.2
65
  *
66
+ * @param PLL_Links_Model $links_model Reference to the links model.
67
  */
68
  public function __construct( &$links_model ) {
69
  parent::__construct( $links_model );
111
  * Setups filters and nav menus once the language has been defined
112
  *
113
  * @since 1.2
114
+ *
115
+ * @return void
116
  */
117
  public function pll_language_defined() {
118
  // Filters
127
  }
128
 
129
  /**
130
+ * When querying multiple taxonomies, makes sure that the language is not the queried object.
131
  *
132
  * @since 1.8
133
  *
134
+ * @param WP_Query $query WP_Query object.
135
+ * @return void
136
  */
137
  public function parse_tax_query( $query ) {
138
  $pll_query = new PLL_Query( $query, $this->model );
144
  }
145
 
146
  /**
147
+ * Modifies some query vars to "hide" that the language is a taxonomy and avoid conflicts.
148
  *
149
  * @since 1.2
150
  *
151
+ * @param WP_Query $query WP_Query object.
152
+ * @return void
153
  */
154
  public function parse_query( $query ) {
155
  $qv = $query->query_vars;
190
  * Auto translate posts and terms ids
191
  *
192
  * @since 1.2
193
+ *
194
+ * @return void
195
  */
196
  public function auto_translate() {
197
  $this->auto_translate = new PLL_Frontend_Auto_Translate( $this );
198
  }
199
 
200
  /**
201
+ * Resets some variables when the blog is switched.
202
+ * Overrides the parent method.
203
  *
204
  * @since 1.5.1
205
  *
206
+ * @param int $new_blog_id New blog ID.
207
+ * @param int $prev_blog_id Previous blog ID.
208
+ * @return void
209
  */
210
+ public function switch_blog( $new_blog_id, $prev_blog_id ) {
211
+ parent::switch_blog( $new_blog_id, $prev_blog_id );
212
+
213
+ // Need to check that some languages are defined when user is logged in, has several blogs, some without any languages.
214
+ if ( $this->is_active_on_new_blog( $new_blog_id, $prev_blog_id ) && did_action( 'pll_language_defined' ) && $this->model->get_languages_list() ) {
215
  static $restore_curlang;
216
  if ( empty( $restore_curlang ) ) {
217
+ $restore_curlang = $this->curlang->slug; // To always remember the current language through blogs.
218
  }
219
 
220
  $lang = $this->model->get_language( $restore_curlang );
include/api.php CHANGED
@@ -4,48 +4,44 @@
4
  */
5
 
6
  /**
7
- * Template tag: displays the language switcher
8
- *
9
- * List of parameters accepted in $args:
10
- *
11
- * - dropdown => displays a dropdown if set to 1, defaults to 0
12
- * - echo => echoes the switcher if set to 1 ( default )
13
- * - hide_if_empty => hides languages with no posts ( or pages ) if set to 1 ( default )
14
- * - show_flags => shows flags if set to 1, defaults to 0
15
- * - show_names => shows languages names if set to 1 ( default )
16
- * - display_names_as => whether to display the language name or its slug, valid options are 'slug' and 'name',
17
- * defaults to name
18
- * - force_home => forces linking to the home page is set to 1, defaults to 0
19
- * - hide_if_no_translation => hides the link if there is no translation if set to 1, defaults to 0
20
- * - hide_current => hides the current language if set to 1, defaults to 0
21
- * - post_id => if not null, link to translations of post defined by post_id, defaults to null
22
- * - raw => set this to true to build your own custom language switcher, defaults to 0
23
- * - item_spacing => whether to preserve or discard whitespace between list items, valid options are
24
- * 'preserve' and 'discard', defaults to preserve
25
  *
26
  * @api
27
  * @since 0.5
28
  *
29
- * @param array $args optional
30
- * @return null|string|array null if displaying, array if raw is requested, string otherwise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  */
32
- function pll_the_languages( $args = '' ) {
33
- if ( PLL() instanceof PLL_Frontend ) {
34
- $switcher = new PLL_Switcher();
35
- return $switcher->the_languages( PLL()->links, $args );
36
- }
37
- return '';
38
  }
39
 
40
  /**
41
- * Returns the current language on frontend
42
- * Returns the language set in admin language filter on backend ( false if set to all languages )
43
  *
44
  * @api
45
  * @since 0.8.1
46
  *
47
- * @param string $field Optional, the language field to return ( see PLL_Language ), defaults to 'slug', pass OBJECT constant to get the language object.
48
- * @return string|PLL_Language|bool The requested field for the current language
49
  */
50
  function pll_current_language( $field = 'slug' ) {
51
  if ( OBJECT === $field ) {
@@ -55,13 +51,13 @@ function pll_current_language( $field = 'slug' ) {
55
  }
56
 
57
  /**
58
- * Returns the default language
59
  *
60
  * @api
61
  * @since 1.0
62
  *
63
- * @param string $field Optional, the language field to return ( see PLL_Language ), defaults to 'slug', pass OBJECT constant to get the language object.
64
- * @return string|PLL_Language|bool The requested field for the default language
65
  */
66
  function pll_default_language( $field = 'slug' ) {
67
  if ( isset( PLL()->options['default_lang'] ) ) {
@@ -77,40 +73,40 @@ function pll_default_language( $field = 'slug' ) {
77
  }
78
 
79
  /**
80
- * Among the post and its translations, returns the id of the post which is in the language represented by $slug
81
  *
82
  * @api
83
  * @since 0.5
84
  *
85
- * @param int $post_id post id
86
- * @param string $slug optional language code, defaults to current language
87
- * @return int|false|null post id of the translation if exists, false otherwise, null if the current language is not defined yet
88
  */
89
- function pll_get_post( $post_id, $slug = '' ) {
90
- return ( $slug = $slug ? $slug : pll_current_language() ) ? PLL()->model->post->get( $post_id, $slug ) : null;
91
  }
92
 
93
  /**
94
- * Among the term and its translations, returns the id of the term which is in the language represented by $slug
95
  *
96
  * @api
97
  * @since 0.5
98
  *
99
- * @param int $term_id term id
100
- * @param string $slug optional language code, defaults to current language
101
- * @return int|false|null term id of the translation if exists, false otherwise, null if the current language is not defined yet
102
  */
103
- function pll_get_term( $term_id, $slug = '' ) {
104
- return ( $slug = $slug ? $slug : pll_current_language() ) ? PLL()->model->term->get( $term_id, $slug ) : null;
105
  }
106
 
107
  /**
108
- * Returns the home url in the current language
109
  *
110
  * @api
111
  * @since 0.8
112
  *
113
- * @param string $lang language code ( optional on frontend )
114
  * @return string
115
  */
116
  function pll_home_url( $lang = '' ) {
@@ -122,15 +118,17 @@ function pll_home_url( $lang = '' ) {
122
  }
123
 
124
  /**
125
- * Registers a string for translation in the "strings translation" panel
126
  *
127
  * @api
128
  * @since 0.6
129
  *
130
- * @param string $name a unique name for the string
131
- * @param string $string the string to register
132
- * @param string $context optional the group in which the string is registered, defaults to 'polylang'
133
- * @param bool $multiline optional whether the string table should display a multiline textarea or a single line input, defaults to single line
 
 
134
  */
135
  function pll_register_string( $name, $string, $context = 'Polylang', $multiline = false ) {
136
  if ( PLL() instanceof PLL_Admin_Base ) {
@@ -139,13 +137,13 @@ function pll_register_string( $name, $string, $context = 'Polylang', $multiline
139
  }
140
 
141
  /**
142
- * Translates a string ( previously registered with pll_register_string )
143
  *
144
  * @api
145
  * @since 0.6
146
  *
147
- * @param string $string the string to translate
148
- * @return string the string translation in the current language
149
  */
150
  function pll__( $string ) {
151
  return is_scalar( $string ) ? __( $string, 'pll_string' ) : $string; // PHPCS:ignore WordPress.WP.I18n
@@ -157,8 +155,8 @@ function pll__( $string ) {
157
  * @api
158
  * @since 2.1
159
  *
160
- * @param string $string the string to translate
161
- * @return string translation in the current language
162
  */
163
  function pll_esc_html__( $string ) {
164
  return esc_html( pll__( $string ) );
@@ -170,8 +168,8 @@ function pll_esc_html__( $string ) {
170
  * @api
171
  * @since 2.1
172
  *
173
- * @param string $string The string to translate
174
- * @return string
175
  */
176
  function pll_esc_attr__( $string ) {
177
  return esc_attr( pll__( $string ) );
@@ -184,7 +182,8 @@ function pll_esc_attr__( $string ) {
184
  * @api
185
  * @since 0.6
186
  *
187
- * @param string $string The string to translate
 
188
  */
189
  function pll_e( $string ) {
190
  echo pll__( $string ); // phpcs:ignore
@@ -196,7 +195,8 @@ function pll_e( $string ) {
196
  * @api
197
  * @since 2.1
198
  *
199
- * @param string $string The string to translate
 
200
  */
201
  function pll_esc_html_e( $string ) {
202
  echo pll_esc_html__( $string ); // phpcs:ignore WordPress.Security.EscapeOutput
@@ -208,21 +208,22 @@ function pll_esc_html_e( $string ) {
208
  * @api
209
  * @since 2.1
210
  *
211
- * @param string $string The string to translate
 
212
  */
213
  function pll_esc_attr_e( $string ) {
214
  echo pll_esc_attr__( $string ); // phpcs:ignore WordPress.Security.EscapeOutput
215
  }
216
 
217
  /**
218
- * Translates a string ( previously registered with pll_register_string )
219
  *
220
  * @api
221
  * @since 1.5.4
222
  *
223
- * @param string $string the string to translate
224
- * @param string $lang language code
225
- * @return string the string translation in the requested language
226
  */
227
  function pll_translate_string( $string, $lang ) {
228
  if ( PLL() instanceof PLL_Frontend && pll_current_language() == $lang ) {
@@ -233,7 +234,7 @@ function pll_translate_string( $string, $lang ) {
233
  return $string;
234
  }
235
 
236
- static $cache; // Cache object to avoid loading the same translations object several times
237
 
238
  if ( empty( $cache ) ) {
239
  $cache = new PLL_Cache();
@@ -249,12 +250,12 @@ function pll_translate_string( $string, $lang ) {
249
  }
250
 
251
  /**
252
- * Returns true if Polylang manages languages and translations for this post type
253
  *
254
  * @api
255
  * @since 1.0.1
256
  *
257
- * @param string $post_type Post type name
258
  * @return bool
259
  */
260
  function pll_is_translated_post_type( $post_type ) {
@@ -262,12 +263,12 @@ function pll_is_translated_post_type( $post_type ) {
262
  }
263
 
264
  /**
265
- * Returns true if Polylang manages languages and translations for this taxonomy
266
  *
267
  * @api
268
  * @since 1.0.1
269
  *
270
- * @param string $tax Taxonomy name
271
  * @return bool
272
  */
273
  function pll_is_translated_taxonomy( $tax ) {
@@ -275,18 +276,18 @@ function pll_is_translated_taxonomy( $tax ) {
275
  }
276
 
277
  /**
278
- * Returns the list of available languages
279
- *
280
- * List of parameters accepted in $args:
281
- *
282
- * hide_empty => hides languages with no posts if set to true ( defaults to false )
283
- * fields => return only that field if set ( see PLL_Language for a list of fields )
284
  *
285
  * @api
286
  * @since 1.5
287
  *
288
- * @param array $args list of parameters
289
- * @return array
 
 
 
 
 
290
  */
291
  function pll_languages_list( $args = array() ) {
292
  $args = wp_parse_args( $args, array( 'fields' => 'slug' ) );
@@ -294,38 +295,41 @@ function pll_languages_list( $args = array() ) {
294
  }
295
 
296
  /**
297
- * Set the post language
298
  *
299
  * @api
300
  * @since 1.5
301
  *
302
- * @param int $id post id
303
- * @param string $lang language code
 
304
  */
305
  function pll_set_post_language( $id, $lang ) {
306
  PLL()->model->post->set_language( $id, $lang );
307
  }
308
 
309
  /**
310
- * Set the term language
311
  *
312
  * @api
313
  * @since 1.5
314
  *
315
- * @param int $id term id
316
- * @param string $lang language code
 
317
  */
318
  function pll_set_term_language( $id, $lang ) {
319
  PLL()->model->term->set_language( $id, $lang );
320
  }
321
 
322
  /**
323
- * Save posts translations
324
  *
325
  * @api
326
  * @since 1.5
327
  *
328
- * @param array $arr an associative array of translations with language code as key and post id as value
 
329
  */
330
  function pll_save_post_translations( $arr ) {
331
  PLL()->model->post->save_translations( reset( $arr ), $arr );
@@ -337,74 +341,88 @@ function pll_save_post_translations( $arr ) {
337
  * @api
338
  * @since 1.5
339
  *
340
- * @param array $arr an associative array of translations with language code as key and term id as value
 
341
  */
342
  function pll_save_term_translations( $arr ) {
343
  PLL()->model->term->save_translations( reset( $arr ), $arr );
344
  }
345
 
346
  /**
347
- * Returns the post language
348
  *
349
  * @api
350
  * @since 1.5.4
351
  *
352
- * @param int $post_id
353
- * @param string $field Optional, the language field to return ( see PLL_Language ), defaults to 'slug'
354
- * @return bool|string The requested field for the post language, false if no language is associated to that post
355
  */
356
  function pll_get_post_language( $post_id, $field = 'slug' ) {
357
  return ( $lang = PLL()->model->post->get_language( $post_id ) ) ? $lang->$field : false;
358
  }
359
 
360
  /**
361
- * Returns the term language
362
  *
363
  * @api
364
  * @since 1.5.4
365
  *
366
- * @param int $term_id
367
- * @param string $field Optional, the language field to return ( see PLL_Language ), defaults to 'slug'
368
- * @return bool|string The requested field for the term language, false if no language is associated to that term
369
  */
370
  function pll_get_term_language( $term_id, $field = 'slug' ) {
371
  return ( $lang = PLL()->model->term->get_language( $term_id ) ) ? $lang->$field : false;
372
  }
373
 
374
  /**
375
- * Returns an array of translations of a post
376
  *
377
  * @api
378
  * @since 1.8
379
  *
380
- * @param int $post_id
381
- * @return array an associative array of translations with language code as key and translation post_id as value
382
  */
383
  function pll_get_post_translations( $post_id ) {
384
  return PLL()->model->post->get_translations( $post_id );
385
  }
386
 
387
  /**
388
- * Returns an array of translations of a term
389
  *
390
  * @api
391
  * @since 1.8
392
  *
393
- * @param int $term_id
394
- * @return array an associative array of translations with language code as key and translation term_id as value
395
  */
396
  function pll_get_term_translations( $term_id ) {
397
  return PLL()->model->term->get_translations( $term_id );
398
  }
399
 
400
  /**
401
- * Count posts in a language
402
  *
403
  * @api
404
  * @since 1.5
405
  *
406
  * @param string $lang Language code.
407
- * @param array $args WP_Query arguments ( accepted keys: post_type, m, year, monthnum, day, author, author_name, post_format, post_status ).
 
 
 
 
 
 
 
 
 
 
 
 
 
408
  * @return int Posts count.
409
  */
410
  function pll_count_posts( $lang, $args = array() ) {
@@ -412,11 +430,13 @@ function pll_count_posts( $lang, $args = array() ) {
412
  }
413
 
414
  /**
415
- * Allows to access the Polylang instance
416
- * It is always preferable to use API functions
417
- * Internal methods may be changed without prior notice
418
  *
419
  * @since 1.8
 
 
420
  */
421
  function PLL() { // PHPCS:ignore WordPress.NamingConventions.ValidFunctionName
422
  return $GLOBALS['polylang'];
4
  */
5
 
6
  /**
7
+ * Template tag: displays the language switcher.
8
+ * The function does nothing if used outside the frontend.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  *
10
  * @api
11
  * @since 0.5
12
  *
13
+ * @param array $args {
14
+ * Optional array of arguments.
15
+ *
16
+ * @type int $dropdown The list is displayed as dropdown if set to 1, defaults to 0.
17
+ * @type int $echo Echoes the list if set to 1, defaults to 1.
18
+ * @type int $hide_if_empty Hides languages with no posts ( or pages ) if set to 1, defaults to 1.
19
+ * @type int $show_flags Displays flags if set to 1, defaults to 0.
20
+ * @type int $show_names Shows language names if set to 1, defaults to 1.
21
+ * @type string $display_names_as Whether to display the language name or its slug, valid options are 'slug' and 'name', defaults to name.
22
+ * @type int $force_home Will always link to the homepage in the translated language if set to 1, defaults to 0.
23
+ * @type int $hide_if_no_translation Hides the link if there is no translation if set to 1, defaults to 0.
24
+ * @type int $hide_current Hides the current language if set to 1, defaults to 0.
25
+ * @type int $post_id Returns links to the translations of the post defined by post_id if set, defaults to not set.
26
+ * @type int $raw Return a raw array instead of html markup if set to 1, defaults to 0.
27
+ * @type string $item_spacing Whether to preserve or discard whitespace between list items, valid options are 'preserve' and 'discard', defaults to 'preserve'.
28
+ * }
29
+ * @return string|array Either the html markup of the switcher or the raw elements to build a custom language switcher.
30
  */
31
+ function pll_the_languages( $args = array() ) {
32
+ $switcher = new PLL_Switcher();
33
+ return $switcher->the_languages( PLL()->links, $args );
 
 
 
34
  }
35
 
36
  /**
37
+ * Returns the current language on frontend.
38
+ * Returns the language set in admin language filter on backend ( false if set to all languages ).
39
  *
40
  * @api
41
  * @since 0.8.1
42
  *
43
+ * @param string $field Optional, the language field to return ( @see PLL_Language ), defaults to 'slug'. Pass OBJECT constant to get the language object.
44
+ * @return string|PLL_Language|false The requested field for the current language.
45
  */
46
  function pll_current_language( $field = 'slug' ) {
47
  if ( OBJECT === $field ) {
51
  }
52
 
53
  /**
54
+ * Returns the default language.
55
  *
56
  * @api
57
  * @since 1.0
58
  *
59
+ * @param string $field Optional, the language field to return ( @see PLL_Language ), defaults to 'slug'. Pass OBJECT constant to get the language object.
60
+ * @return string|PLL_Language|false The requested field for the default language.
61
  */
62
  function pll_default_language( $field = 'slug' ) {
63
  if ( isset( PLL()->options['default_lang'] ) ) {
73
  }
74
 
75
  /**
76
+ * Among the post and its translations, returns the id of the post which is in the language represented by $lang.
77
  *
78
  * @api
79
  * @since 0.5
80
  *
81
+ * @param int $post_id Post id.
82
+ * @param string $lang Optional language code, defaults to the current language.
83
+ * @return int|false|null Post id of the translation if it exists, false otherwise, null if the current language is not defined yet.
84
  */
85
+ function pll_get_post( $post_id, $lang = '' ) {
86
+ return ( $lang = $lang ? $lang : pll_current_language() ) ? PLL()->model->post->get( $post_id, $lang ) : null;
87
  }
88
 
89
  /**
90
+ * Among the term and its translations, returns the id of the term which is in the language represented by $lang.
91
  *
92
  * @api
93
  * @since 0.5
94
  *
95
+ * @param int $term_id Term id.
96
+ * @param string $lang Optional language code, defaults to the current language.
97
+ * @return int|false|null Term id of the translation if it exists, false otherwise, null if the current language is not defined yet.
98
  */
99
+ function pll_get_term( $term_id, $lang = '' ) {
100
+ return ( $lang = $lang ? $lang : pll_current_language() ) ? PLL()->model->term->get( $term_id, $lang ) : null;
101
  }
102
 
103
  /**
104
+ * Returns the home url in a language.
105
  *
106
  * @api
107
  * @since 0.8
108
  *
109
+ * @param string $lang Optional language code, defaults to the current language.
110
  * @return string
111
  */
112
  function pll_home_url( $lang = '' ) {
118
  }
119
 
120
  /**
121
+ * Registers a string for translation in the "strings translation" panel.
122
  *
123
  * @api
124
  * @since 0.6
125
  *
126
+ * @param string $name A unique name for the string.
127
+ * @param string $string The string to register.
128
+ * @param string $context Optional, the group in which the string is registered, defaults to 'polylang'.
129
+ * @param bool $multiline Optional, true if the string table should display a multiline textarea,
130
+ * false if should display a single line input, defaults to false.
131
+ * @return void
132
  */
133
  function pll_register_string( $name, $string, $context = 'Polylang', $multiline = false ) {
134
  if ( PLL() instanceof PLL_Admin_Base ) {
137
  }
138
 
139
  /**
140
+ * Translates a string ( previously registered with pll_register_string ).
141
  *
142
  * @api
143
  * @since 0.6
144
  *
145
+ * @param string $string The string to translate.
146
+ * @return string The string translated in the current language.
147
  */
148
  function pll__( $string ) {
149
  return is_scalar( $string ) ? __( $string, 'pll_string' ) : $string; // PHPCS:ignore WordPress.WP.I18n
155
  * @api
156
  * @since 2.1
157
  *
158
+ * @param string $string The string to translate.
159
+ * @return string The string translated in the current language.
160
  */
161
  function pll_esc_html__( $string ) {
162
  return esc_html( pll__( $string ) );
168
  * @api
169
  * @since 2.1
170
  *
171
+ * @param string $string The string to translate.
172
+ * @return string The string translated in the current language.
173
  */
174
  function pll_esc_attr__( $string ) {
175
  return esc_attr( pll__( $string ) );
182
  * @api
183
  * @since 0.6
184
  *
185
+ * @param string $string The string to translate.
186
+ * @return void
187
  */
188
  function pll_e( $string ) {
189
  echo pll__( $string ); // phpcs:ignore
195
  * @api
196
  * @since 2.1
197
  *
198
+ * @param string $string The string to translate.
199
+ * @return void
200
  */
201
  function pll_esc_html_e( $string ) {
202
  echo pll_esc_html__( $string ); // phpcs:ignore WordPress.Security.EscapeOutput
208
  * @api
209
  * @since 2.1
210
  *
211
+ * @param string $string The string to translate.
212
+ * @return void
213
  */
214
  function pll_esc_attr_e( $string ) {
215
  echo pll_esc_attr__( $string ); // phpcs:ignore WordPress.Security.EscapeOutput
216
  }
217
 
218
  /**
219
+ * Translates a string ( previously registered with pll_register_string ).
220
  *
221
  * @api
222
  * @since 1.5.4
223
  *
224
+ * @param string $string The string to translate.
225
+ * @param string $lang Language code.
226
+ * @return string The string translated in the requested language.
227
  */
228
  function pll_translate_string( $string, $lang ) {
229
  if ( PLL() instanceof PLL_Frontend && pll_current_language() == $lang ) {
234
  return $string;
235
  }
236
 
237
+ static $cache; // Cache object to avoid loading the same translations object several times.
238
 
239
  if ( empty( $cache ) ) {
240
  $cache = new PLL_Cache();
250
  }
251
 
252
  /**
253
+ * Returns true if Polylang manages languages and translations for this post type.
254
  *
255
  * @api
256
  * @since 1.0.1
257
  *
258
+ * @param string $post_type Post type name.
259
  * @return bool
260
  */
261
  function pll_is_translated_post_type( $post_type ) {
263
  }
264
 
265
  /**
266
+ * Returns true if Polylang manages languages and translations for this taxonomy.
267
  *
268
  * @api
269
  * @since 1.0.1
270
  *
271
+ * @param string $tax Taxonomy name.
272
  * @return bool
273
  */
274
  function pll_is_translated_taxonomy( $tax ) {
276
  }
277
 
278
  /**
279
+ * Returns the list of available languages.
 
 
 
 
 
280
  *
281
  * @api
282
  * @since 1.5
283
  *
284
+ * @param array $args {
285
+ * Optional array of arguments.
286
+ *
287
+ * @type bool $hide_empty Hides languages with no posts if set to true ( defaults to false ).
288
+ * @type string $fields Return only that field if set ( @see PLL_Language for a list of fields ), defaults to 'slug'.
289
+ * }
290
+ * @return string[]
291
  */
292
  function pll_languages_list( $args = array() ) {
293
  $args = wp_parse_args( $args, array( 'fields' => 'slug' ) );
295
  }
296
 
297
  /**
298
+ * Sets the post language.
299
  *
300
  * @api
301
  * @since 1.5
302
  *
303
+ * @param int $id Post id.
304
+ * @param string $lang Language code.
305
+ * @return void
306
  */
307
  function pll_set_post_language( $id, $lang ) {
308
  PLL()->model->post->set_language( $id, $lang );
309
  }
310
 
311
  /**
312
+ * Sets the term language.
313
  *
314
  * @api
315
  * @since 1.5
316
  *
317
+ * @param int $id Term id.
318
+ * @param string $lang Language code.
319
+ * @return void
320
  */
321
  function pll_set_term_language( $id, $lang ) {
322
  PLL()->model->term->set_language( $id, $lang );
323
  }
324
 
325
  /**
326
+ * Save posts translations.
327
  *
328
  * @api
329
  * @since 1.5
330
  *
331
+ * @param int[] $arr An associative array of translations with language code as key and post id as value.
332
+ * @return void
333
  */
334
  function pll_save_post_translations( $arr ) {
335
  PLL()->model->post->save_translations( reset( $arr ), $arr );
341
  * @api
342
  * @since 1.5
343
  *
344
+ * @param int[] $arr An associative array of translations with language code as key and term id as value.
345
+ * @return void
346
  */
347
  function pll_save_term_translations( $arr ) {
348
  PLL()->model->term->save_translations( reset( $arr ), $arr );
349
  }
350
 
351
  /**
352
+ * Returns the post language.
353
  *
354
  * @api
355
  * @since 1.5.4
356
  *
357
+ * @param int $post_id Post id.
358
+ * @param string $field Optional, the language field to return ( @see PLL_Language ), defaults to 'slug'.
359
+ * @return string|false The requested field for the post language, false if no language is associated to that post.
360
  */
361
  function pll_get_post_language( $post_id, $field = 'slug' ) {
362
  return ( $lang = PLL()->model->post->get_language( $post_id ) ) ? $lang->$field : false;
363
  }
364
 
365
  /**
366
+ * Returns the term language.
367
  *
368
  * @api
369
  * @since 1.5.4
370
  *
371
+ * @param int $term_id Term id.
372
+ * @param string $field Optional, the language field to return ( @see PLL_Language ), defaults to 'slug'.
373
+ * @return string|false The requested field for the term language, false if no language is associated to that term.
374
  */
375
  function pll_get_term_language( $term_id, $field = 'slug' ) {
376
  return ( $lang = PLL()->model->term->get_language( $term_id ) ) ? $lang->$field : false;
377
  }
378
 
379
  /**
380
+ * Returns an array of translations of a post.
381
  *
382
  * @api
383
  * @since 1.8
384
  *
385
+ * @param int $post_id Post id.
386
+ * @return int[] An associative array of translations with language code as key and translation post id as value.
387
  */
388
  function pll_get_post_translations( $post_id ) {
389
  return PLL()->model->post->get_translations( $post_id );
390
  }
391
 
392
  /**
393
+ * Returns an array of translations of a term.
394
  *
395
  * @api
396
  * @since 1.8
397
  *
398
+ * @param int $term_id Term id.
399
+ * @return int[] An associative array of translations with language code as key and translation term id as value.
400
  */
401
  function pll_get_term_translations( $term_id ) {
402
  return PLL()->model->term->get_translations( $term_id );
403
  }
404
 
405
  /**
406
+ * Counts posts in a language.
407
  *
408
  * @api
409
  * @since 1.5
410
  *
411
  * @param string $lang Language code.
412
+ * @param array $args {
413
+ * Optional arguments.
414
+ * Accepted keys:
415
+ *
416
+ * @type string $post_type Post type.
417
+ * @type int $m YearMonth ( ex: 201307 ).
418
+ * @type int $year 4 digit year.
419
+ * @type int $monthnum Month number (from 1 to 12).
420
+ * @type int $day Day of the month (from 1 to 31).
421
+ * @type int $author Author id.
422
+ * @type string $author_name Author nicename.
423
+ * @type string $post_format Post format.
424
+ * @type string $post_status Post status.
425
+ * }
426
  * @return int Posts count.
427
  */
428
  function pll_count_posts( $lang, $args = array() ) {
430
  }
431
 
432
  /**
433
+ * Allows to access the Polylang instance.
434
+ * However, it is always preferable to use API functions
435
+ * as internal methods may be changed without prior notice.
436
  *
437
  * @since 1.8
438
+ *
439
+ * @return PLL_Frontend|PLL_Admin|PLL_Settings|PLL_REST_Request
440
  */
441
  function PLL() { // PHPCS:ignore WordPress.NamingConventions.ValidFunctionName
442
  return $GLOBALS['polylang'];
include/base.php CHANGED
@@ -17,8 +17,6 @@ abstract class PLL_Base {
17
  public $options;
18
 
19
  /**
20
- * Instance of PLL_Model.
21
- *
22
  * @var PLL_Model
23
  */
24
  public $model;
@@ -45,11 +43,11 @@ abstract class PLL_Base {
45
  public $terms;
46
 
47
  /**
48
- * Constructor
49
  *
50
  * @since 1.2
51
  *
52
- * @param object $links_model
53
  */
54
  public function __construct( &$links_model ) {
55
  $this->links_model = &$links_model;
@@ -63,6 +61,7 @@ abstract class PLL_Base {
63
  // User defined strings translations
64
  add_action( 'pll_language_defined', array( $this, 'load_strings_translations' ), 5 );
65
  add_action( 'change_locale', array( $this, 'load_strings_translations' ) ); // Since WP 4.7
 
66
 
67
  // Switch_to_blog
68
  add_action( 'switch_blog', array( $this, 'switch_blog' ), 10, 2 );
@@ -73,6 +72,8 @@ abstract class PLL_Base {
73
  * only when at least one language is defined.
74
  *
75
  * @since 2.6
 
 
76
  */
77
  public function init() {
78
  if ( $this->model->get_languages_list() ) {
@@ -91,6 +92,8 @@ abstract class PLL_Base {
91
  * Registers our widgets
92
  *
93
  * @since 0.1
 
 
94
  */
95
  public function widgets_init() {
96
  register_widget( 'PLL_Widget_Languages' );
@@ -109,10 +112,11 @@ abstract class PLL_Base {
109
  * @since 2.1.3 $locale parameter added.
110
  *
111
  * @param string $locale Locale. Defaults to current locale.
 
112
  */
113
  public function load_strings_translations( $locale = '' ) {
114
  if ( empty( $locale ) ) {
115
- $locale = get_locale();
116
  }
117
 
118
  $language = $this->model->get_language( $locale );
@@ -127,69 +131,40 @@ abstract class PLL_Base {
127
  }
128
 
129
  /**
130
- * Resets some variables when switching blog
131
- * Applies only if Polylang is active on the new blog
132
  *
133
  * @since 1.5.1
134
  *
135
- * @param int $new_blog
136
- * @param int $old_blog
137
- * @return bool not used by WP but by child class
138
  */
139
- public function switch_blog( $new_blog, $old_blog ) {
140
- $plugins = ( $sitewide_plugins = get_site_option( 'active_sitewide_plugins' ) ) && is_array( $sitewide_plugins ) ? array_keys( $sitewide_plugins ) : array();
141
- $plugins = array_merge( $plugins, get_option( 'active_plugins', array() ) );
142
-
143
- // 2nd test needed when Polylang is not networked activated
144
- // 3rd test needed when Polylang is networked activated and a new site is created
145
- if ( $new_blog != $old_blog && in_array( POLYLANG_BASENAME, $plugins ) && get_option( 'polylang' ) ) {
146
- $this->options = get_option( 'polylang' ); // Needed for menus
147
  remove_action( 'pre_option_rewrite_rules', array( $this->links_model, 'prepare_rewrite_rules' ) );
148
  $this->links_model = $this->model->get_links_model();
149
- return true;
150
  }
151
- return false;
152
  }
153
 
154
  /**
155
- * Some backward compatibility with Polylang < 1.2
156
- * Allows for example to call $polylang->get_languages_list() instead of $polylang->model->get_languages_list()
157
- * This works but should be slower than the direct call, thus an error is triggered in debug mode
158
  *
159
- * @since 1.2
160
  *
161
- * @param string $func function name
162
- * @param array $args function arguments
 
163
  */
164
- public function __call( $func, $args ) {
165
- foreach ( $this as $prop => &$obj ) {
166
- if ( is_object( $obj ) && method_exists( $obj, $func ) ) {
167
- if ( WP_DEBUG ) {
168
- $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
169
- $i = 1 + empty( $debug[1]['line'] ); // The file and line are in $debug[2] if the function was called using call_user_func
170
- trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
171
- sprintf(
172
- '%1$s was called incorrectly in %3$s on line %4$s: the call to $polylang->%1$s() has been deprecated in Polylang 1.2, use PLL()->%2$s->%1$s() instead.' . "\nError handler",
173
- esc_html( $func ),
174
- esc_html( $prop ),
175
- esc_html( $debug[ $i ]['file'] ),
176
- absint( $debug[ $i ]['line'] )
177
- )
178
- );
179
- }
180
- return call_user_func_array( array( $obj, $func ), $args );
181
- }
182
- }
183
 
184
- $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
185
- trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
186
- sprintf(
187
- 'Call to undefined function PLL()->%1$s() in %2$s on line %3$s' . "\nError handler",
188
- esc_html( $func ),
189
- esc_html( $debug[0]['file'] ),
190
- absint( $debug[0]['line'] )
191
- ),
192
- E_USER_ERROR
193
- );
194
  }
195
  }
17
  public $options;
18
 
19
  /**
 
 
20
  * @var PLL_Model
21
  */
22
  public $model;
43
  public $terms;
44
 
45
  /**
46
+ * Constructor.
47
  *
48
  * @since 1.2
49
  *
50
+ * @param PLL_Links_Model $links_model Links Model.
51
  */
52
  public function __construct( &$links_model ) {
53
  $this->links_model = &$links_model;
61
  // User defined strings translations
62
  add_action( 'pll_language_defined', array( $this, 'load_strings_translations' ), 5 );
63
  add_action( 'change_locale', array( $this, 'load_strings_translations' ) ); // Since WP 4.7
64
+ add_action( 'personal_options_update', array( $this, 'load_strings_translations' ), 1, 0 ); // Before WP, for confirmation request when changing the user email.
65
 
66
  // Switch_to_blog
67
  add_action( 'switch_blog', array( $this, 'switch_blog' ), 10, 2 );
72
  * only when at least one language is defined.
73
  *
74
  * @since 2.6
75
+ *
76
+ * @return void
77
  */
78
  public function init() {
79
  if ( $this->model->get_languages_list() ) {
92
  * Registers our widgets
93
  *
94
  * @since 0.1
95
+ *
96
+ * @return void
97
  */
98
  public function widgets_init() {
99
  register_widget( 'PLL_Widget_Languages' );
112
  * @since 2.1.3 $locale parameter added.
113
  *
114
  * @param string $locale Locale. Defaults to current locale.
115
+ * @return void
116
  */
117
  public function load_strings_translations( $locale = '' ) {
118
  if ( empty( $locale ) ) {
119
+ $locale = ( is_admin() && ! Polylang::is_ajax_on_front() ) ? get_user_locale() : get_locale();
120
  }
121
 
122
  $language = $this->model->get_language( $locale );
131
  }
132
 
133
  /**
134
+ * Resets some variables when the blog is switched.
135
+ * Applied only if Polylang is active on the new blog.
136
  *
137
  * @since 1.5.1
138
  *
139
+ * @param int $new_blog_id New blog ID.
140
+ * @param int $prev_blog_id Previous blog ID.
141
+ * @return void
142
  */
143
+ public function switch_blog( $new_blog_id, $prev_blog_id ) {
144
+ if ( $this->is_active_on_new_blog( $new_blog_id, $prev_blog_id ) ) {
145
+ $this->options = get_option( 'polylang' ); // Needed for menus.
 
 
 
 
 
146
  remove_action( 'pre_option_rewrite_rules', array( $this->links_model, 'prepare_rewrite_rules' ) );
147
  $this->links_model = $this->model->get_links_model();
 
148
  }
 
149
  }
150
 
151
  /**
152
+ * Checks if Polylang is active on the new blog when the blog is switched.
 
 
153
  *
154
+ * @since 3.0
155
  *
156
+ * @param int $new_blog_id New blog ID.
157
+ * @param int $prev_blog_id Previous blog ID.
158
+ * @return bool
159
  */
160
+ protected function is_active_on_new_blog( $new_blog_id, $prev_blog_id ) {
161
+ $plugins = ( $sitewide_plugins = get_site_option( 'active_sitewide_plugins' ) ) && is_array( $sitewide_plugins ) ? array_keys( $sitewide_plugins ) : array();
162
+ $plugins = array_merge( $plugins, get_option( 'active_plugins', array() ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
+ /*
165
+ * The 2nd test is needed when Polylang is not networked activated.
166
+ * The 3rd test is needed when Polylang is networked activated and a new site is created.
167
+ */
168
+ return $new_blog_id !== $prev_blog_id && in_array( POLYLANG_BASENAME, $plugins ) && get_option( 'polylang' );
 
 
 
 
 
169
  }
170
  }
include/cache.php CHANGED
@@ -10,7 +10,19 @@
10
  * @since 1.7
11
  */
12
  class PLL_Cache {
13
- protected $blog_id, $cache;
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  /**
16
  * Constructor
@@ -28,6 +40,7 @@ class PLL_Cache {
28
  * @since 1.7
29
  *
30
  * @param int $new_blog
 
31
  */
32
  public function switch_blog( $new_blog ) {
33
  $this->blog_id = $new_blog;
@@ -40,6 +53,7 @@ class PLL_Cache {
40
  *
41
  * @param string $key
42
  * @param mixed $data
 
43
  */
44
  public function set( $key, $data ) {
45
  $this->cache[ $this->blog_id ][ $key ] = $data;
@@ -63,6 +77,7 @@ class PLL_Cache {
63
  * @since 1.7
64
  *
65
  * @param string $key
 
66
  */
67
  public function clean( $key = '' ) {
68
  if ( empty( $key ) ) {
10
  * @since 1.7
11
  */
12
  class PLL_Cache {
13
+ /**
14
+ * Current site id.
15
+ *
16
+ * @var int
17
+ */
18
+ protected $blog_id;
19
+
20
+ /**
21
+ * The cache container.
22
+ *
23
+ * @var array
24
+ */
25
+ protected $cache;
26
 
27
  /**
28
  * Constructor
40
  * @since 1.7
41
  *
42
  * @param int $new_blog
43
+ * @return void
44
  */
45
  public function switch_blog( $new_blog ) {
46
  $this->blog_id = $new_blog;
53
  *
54
  * @param string $key
55
  * @param mixed $data
56
+ * @return void
57
  */
58
  public function set( $key, $data ) {
59
  $this->cache[ $this->blog_id ][ $key ] = $data;
77
  * @since 1.7
78
  *
79
  * @param string $key
80
+ * @return void
81
  */
82
  public function clean( $key = '' ) {
83
  if ( empty( $key ) ) {
include/class-polylang.php CHANGED
@@ -128,6 +128,8 @@ class Polylang {
128
  * May be overridden by a plugin if set before plugins_loaded, 1
129
  *
130
  * @since 1.6
 
 
131
  */
132
  public static function define_constants() {
133
  // Cookie name. no cookie will be used if set to false
@@ -156,6 +158,8 @@ class Polylang {
156
  * setups models and separate admin and frontend
157
  *
158
  * @since 1.2
 
 
159
  */
160
  public function init() {
161
  global $polylang;
@@ -237,8 +241,11 @@ 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 ) {
241
- require_once $load_script; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
 
 
 
242
  }
243
 
244
  $polylang->init();
128
  * May be overridden by a plugin if set before plugins_loaded, 1
129
  *
130
  * @since 1.6
131
+ *
132
+ * @return void
133
  */
134
  public static function define_constants() {
135
  // Cookie name. no cookie will be used if set to false
158
  * setups models and separate admin and frontend
159
  *
160
  * @since 1.2
161
+ *
162
+ * @return void
163
  */
164
  public function init() {
165
  global $polylang;
241
  require_once __DIR__ . '/api.php'; // Loads the API
242
 
243
  // Loads the modules.
244
+ $load_scripts = glob( POLYLANG_DIR . '/modules/*/load.php', GLOB_NOSORT );
245
+ if ( is_array( $load_scripts ) ) {
246
+ foreach ( $load_scripts as $load_script ) {
247
+ require_once $load_script; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
248
+ }
249
  }
250
 
251
  $polylang->init();
include/cookie.php CHANGED
@@ -55,6 +55,7 @@ class PLL_Cookie {
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 );
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
+ * @return void
59
  */
60
  public static function set( $lang, $args = array() ) {
61
  $args = self::parse_args( $args );
include/crud-posts.php CHANGED
@@ -4,12 +4,30 @@
4
  */
5
 
6
  /**
7
- * Adds actions and filters related to languages when creating, updating or deleting posts
8
- * Actions a filters used when reaing posts are handled separately
9
  *
10
  * @since 2.4
11
  */
12
  class PLL_CRUD_Posts {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  /**
15
  * Constructor
@@ -42,6 +60,7 @@ class PLL_CRUD_Posts {
42
  * @since 1.5
43
  *
44
  * @param int $post_id
 
45
  */
46
  public function set_default_language( $post_id ) {
47
  if ( ! $this->model->post->get_language( $post_id ) ) {
@@ -64,16 +83,17 @@ class PLL_CRUD_Posts {
64
  }
65
 
66
  /**
67
- * Called when a post ( or page ) is saved, published or updated
68
  *
69
  * @since 0.1
70
- * @since 2.3 Does not save the language and translations anymore, unless the post has no language yet
71
  *
72
- * @param int $post_id
73
- * @param object $post
 
74
  */
75
  public function save_post( $post_id, $post ) {
76
- // Does nothing except on post types which are filterable
77
  if ( $this->model->is_translated_post_type( $post->post_type ) ) {
78
  if ( $id = wp_is_post_revision( $post_id ) ) {
79
  $post_id = $id;
@@ -86,27 +106,28 @@ class PLL_CRUD_Posts {
86
  }
87
 
88
  /**
89
- * Fires after the post language and translations are saved
90
  *
91
  * @since 1.2
92
  *
93
- * @param int $post_id Post id
94
- * @param object $post Post object
95
- * @param array $translations The list of translations post ids
96
  */
97
  do_action( 'pll_save_post', $post_id, $post, $this->model->post->get_translations( $post_id ) );
98
  }
99
  }
100
 
101
  /**
102
- * Make sure saved terms are in the right language (especially tags with same name in different languages)
103
  *
104
  * @since 2.3
105
  *
106
- * @param int $object_id Object ID.
107
- * @param array $terms An array of object terms.
108
- * @param array $tt_ids An array of term taxonomy IDs.
109
- * @param string $taxonomy Taxonomy slug.
 
110
  */
111
  public function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy ) {
112
  static $avoid_recursion;
@@ -198,6 +219,7 @@ class PLL_CRUD_Posts {
198
  * @since 0.1
199
  *
200
  * @param int $post_id
 
201
  */
202
  public function delete_post( $post_id ) {
203
  if ( ! wp_is_post_revision( $post_id ) ) {
@@ -248,27 +270,32 @@ class PLL_CRUD_Posts {
248
  */
249
  public function create_media_translation( $post_id, $lang ) {
250
  if ( empty( $post_id ) ) {
251
- return $post_id;
252
  }
253
 
254
- $post = get_post( $post_id );
255
 
256
  if ( empty( $post ) ) {
257
- return $post;
258
  }
259
 
260
  $lang = $this->model->get_language( $lang ); // Make sure we get a valid language slug.
261
 
 
 
 
 
262
  // Create a new attachment ( translate attachment parent if exists ).
263
  add_filter( 'pll_enable_duplicate_media', '__return_false', 99 ); // Avoid a conflict with automatic duplicate at upload.
264
- $post->ID = null; // Will force the creation
265
- $post->post_parent = ( $post->post_parent && $tr_parent = $this->model->post->get_translation( $post->post_parent, $lang->slug ) ) ? $tr_parent : 0;
266
- $post->tax_input = array( 'language' => array( $lang->slug ) ); // Assigns the language.
267
- $tr_id = wp_insert_attachment( wp_slash( (array) $post ) );
268
  remove_filter( 'pll_enable_duplicate_media', '__return_false', 99 ); // Restore automatic duplicate at upload.
269
 
270
  // Copy metadata.
271
- if ( $data = wp_get_attachment_metadata( $post_id, true ) ) { // Unfiltered.
 
272
  wp_update_attachment_metadata( $tr_id, wp_slash( $data ) ); // Directly uses update_post_meta, so expects slashed.
273
  }
274
 
4
  */
5
 
6
  /**
7
+ * Adds actions and filters related to languages when creating, updating or deleting posts.
8
+ * Actions and filters triggered when reading posts are handled separately.
9
  *
10
  * @since 2.4
11
  */
12
  class PLL_CRUD_Posts {
13
+ /**
14
+ * @var PLL_Model
15
+ */
16
+ protected $model;
17
+
18
+ /**
19
+ * Preferred language to assign to a new post.
20
+ *
21
+ * @var PLL_Language
22
+ */
23
+ protected $pref_lang;
24
+
25
+ /**
26
+ * Current language.
27
+ *
28
+ * @var PLL_Language
29
+ */
30
+ protected $curlang;
31
 
32
  /**
33
  * Constructor
60
  * @since 1.5
61
  *
62
  * @param int $post_id
63
+ * @return void
64
  */
65
  public function set_default_language( $post_id ) {
66
  if ( ! $this->model->post->get_language( $post_id ) ) {
83
  }
84
 
85
  /**
86
+ * Called when a post ( or page ) is saved, published or updated.
87
  *
88
  * @since 0.1
89
+ * @since 2.3 Does not save the language and translations anymore, unless the post has no language yet.
90
  *
91
+ * @param int $post_id Post id of the post being saved.
92
+ * @param WP_Post $post The post being saved.
93
+ * @return void
94
  */
95
  public function save_post( $post_id, $post ) {
96
+ // Does nothing except on post types which are filterable.
97
  if ( $this->model->is_translated_post_type( $post->post_type ) ) {
98
  if ( $id = wp_is_post_revision( $post_id ) ) {
99
  $post_id = $id;
106
  }
107
 
108
  /**
109
+ * Fires after the post language and translations are saved.
110
  *
111
  * @since 1.2
112
  *
113
+ * @param int $post_id Post id.
114
+ * @param WP_Post $post Post object.
115
+ * @param int[] $translations The list of translations post ids.
116
  */
117
  do_action( 'pll_save_post', $post_id, $post, $this->model->post->get_translations( $post_id ) );
118
  }
119
  }
120
 
121
  /**
122
+ * Makes sure that saved terms are in the right language (especially tags with same name in different languages).
123
  *
124
  * @since 2.3
125
  *
126
+ * @param int $object_id Object ID.
127
+ * @param WP_Term[] $terms An array of object terms.
128
+ * @param int[] $tt_ids An array of term taxonomy IDs.
129
+ * @param string $taxonomy Taxonomy slug.
130
+ * @return void
131
  */
132
  public function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy ) {
133
  static $avoid_recursion;
219
  * @since 0.1
220
  *
221
  * @param int $post_id
222
+ * @return void
223
  */
224
  public function delete_post( $post_id ) {
225
  if ( ! wp_is_post_revision( $post_id ) ) {
270
  */
271
  public function create_media_translation( $post_id, $lang ) {
272
  if ( empty( $post_id ) ) {
273
+ return 0;
274
  }
275
 
276
+ $post = get_post( $post_id, ARRAY_A );
277
 
278
  if ( empty( $post ) ) {
279
+ return 0;
280
  }
281
 
282
  $lang = $this->model->get_language( $lang ); // Make sure we get a valid language slug.
283
 
284
+ if ( empty( $lang ) ) {
285
+ return 0;
286
+ }
287
+
288
  // Create a new attachment ( translate attachment parent if exists ).
289
  add_filter( 'pll_enable_duplicate_media', '__return_false', 99 ); // Avoid a conflict with automatic duplicate at upload.
290
+ unset( $post['ID'] ); // Will force the creation.
291
+ $post['post_parent'] = ( $post['post_parent'] && $tr_parent = $this->model->post->get_translation( $post['post_parent'], $lang->slug ) ) ? $tr_parent : 0;
292
+ $post['tax_input'] = array( 'language' => array( $lang->slug ) ); // Assigns the language.
293
+ $tr_id = wp_insert_attachment( wp_slash( $post ) );
294
  remove_filter( 'pll_enable_duplicate_media', '__return_false', 99 ); // Restore automatic duplicate at upload.
295
 
296
  // Copy metadata.
297
+ $data = wp_get_attachment_metadata( $post_id, true ); // Unfiltered.
298
+ if ( is_array( $data ) ) {
299
  wp_update_attachment_metadata( $tr_id, wp_slash( $data ) ); // Directly uses update_post_meta, so expects slashed.
300
  }
301
 
include/crud-terms.php CHANGED
@@ -10,7 +10,37 @@
10
  * @since 2.4
11
  */
12
  class PLL_CRUD_Terms {
13
- public $model, $curlang, $filter_lang, $pref_lang;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  private $tax_query_lang;
15
 
16
  /**
@@ -49,6 +79,7 @@ class PLL_CRUD_Terms {
49
  *
50
  * @param int $term_id
51
  * @param string $taxonomy
 
52
  */
53
  protected function set_default_language( $term_id, $taxonomy ) {
54
  if ( ! $this->model->term->get_language( $term_id ) ) {
@@ -69,14 +100,15 @@ class PLL_CRUD_Terms {
69
  }
70
 
71
  /**
72
- * Called when a category or post tag is created or edited
73
- * Does nothing except on taxonomies which are filterable
74
  *
75
  * @since 0.1
76
  *
77
- * @param int $term_id
78
- * @param int $tt_id Term taxonomy id
79
- * @param string $taxonomy
 
80
  */
81
  public function save_term( $term_id, $tt_id, $taxonomy ) {
82
  if ( $this->model->is_translated_taxonomy( $taxonomy ) ) {
@@ -88,28 +120,30 @@ class PLL_CRUD_Terms {
88
  }
89
 
90
  /**
91
- * Fires after the term language and translations are saved
92
  *
93
  * @since 1.2
94
  *
95
- * @param int $term_id term id
96
- * @param string $taxonomy taxonomy name
97
- * @param array $translations the list of translations term ids
98
  */
99
  do_action( 'pll_save_term', $term_id, $taxonomy, $this->model->term->get_translations( $term_id ) );
100
  }
101
  }
102
 
103
  /**
104
- * Get the language(s) to filter get_terms
105
  *
106
  * @since 1.7.6
107
  *
108
- * @param array $taxonomies queried taxonomies
109
- * @param array $args get_terms arguments
110
- * @return object|string|bool the language(s) to use in the filter, false otherwise
111
  */
112
  protected function get_queried_language( $taxonomies, $args ) {
 
 
113
  // Does nothing except on taxonomies which are filterable
114
  // Since WP 4.7, make sure not to filter wp_get_object_terms()
115
  if ( ! $this->model->is_translated_taxonomy( $taxonomies ) || ! empty( $args['object_ids'] ) ) {
@@ -122,7 +156,7 @@ class PLL_CRUD_Terms {
122
  }
123
 
124
  // On tags page, everything should be filtered according to the admin language filter except the parent dropdown
125
- if ( 'edit-tags.php' === $GLOBALS['pagenow'] && empty( $args['class'] ) ) {
126
  return $this->filter_lang;
127
  }
128
 
@@ -130,17 +164,17 @@ class PLL_CRUD_Terms {
130
  }
131
 
132
  /**
133
- * Adds language dependent cache domain when querying terms
134
- * Useful as the 'lang' parameter is not included in cache key by WordPress
135
  *
136
  * @since 1.3
137
  *
138
- * @param array $args
139
- * @param array $taxonomies
140
- * @return array modified arguments
141
  */
142
  public function get_terms_args( $args, $taxonomies ) {
143
- // Don't break _get_term_hierarchy()
144
  if ( 'all' === $args['get'] && 'id' === $args['orderby'] && 'id=>parent' === $args['fields'] ) {
145
  $args['lang'] = '';
146
  }
@@ -162,10 +196,10 @@ class PLL_CRUD_Terms {
162
  *
163
  * @since 0.2
164
  *
165
- * @param array $clauses list of sql clauses
166
- * @param array $taxonomies list of taxonomies
167
- * @param array $args get_terms arguments
168
- * @return array modified sql clauses
169
  */
170
  public function terms_clauses( $clauses, $taxonomies, $args ) {
171
  $lang = $this->get_queried_language( $taxonomies, $args );
@@ -173,22 +207,25 @@ class PLL_CRUD_Terms {
173
  }
174
 
175
  /**
176
- * Sets the WP_Term_Query language when doing a WP_Query
177
- * Needed since WP 4.9
178
  *
179
  * @since 2.3.2
180
  *
181
- * @param object $query WP_Query object
 
182
  */
183
  public function set_tax_query_lang( $query ) {
184
  $this->tax_query_lang = isset( $query->query_vars['lang'] ) ? $query->query_vars['lang'] : '';
185
  }
186
 
187
  /**
188
- * Removes the WP_Term_Query language filter for WP_Query
189
- * Needed since WP 4.9
190
  *
191
  * @since 2.3.2
 
 
192
  */
193
  public function unset_tax_query_lang() {
194
  unset( $this->tax_query_lang );
@@ -201,6 +238,7 @@ class PLL_CRUD_Terms {
201
  * @since 0.1
202
  *
203
  * @param int $term_id
 
204
  */
205
  public function delete_term( $term_id ) {
206
  $this->model->term->delete_translation( $term_id );
10
  * @since 2.4
11
  */
12
  class PLL_CRUD_Terms {
13
+ /**
14
+ * @var PLL_Model
15
+ */
16
+ public $model;
17
+
18
+ /**
19
+ * Current language (used to filter the content).
20
+ *
21
+ * @var PLL_Language
22
+ */
23
+ public $curlang;
24
+
25
+ /**
26
+ * Language selected in the admin language filter.
27
+ *
28
+ * @var PLL_Language
29
+ */
30
+ public $filter_lang;
31
+
32
+ /**
33
+ * Preferred language to assign to new contents.
34
+ *
35
+ * @var PLL_Language
36
+ */
37
+ public $pref_lang;
38
+
39
+ /**
40
+ * Stores the 'lang' query var from WP_Query.
41
+ *
42
+ * @var string
43
+ */
44
  private $tax_query_lang;
45
 
46
  /**
79
  *
80
  * @param int $term_id
81
  * @param string $taxonomy
82
+ * @return void
83
  */
84
  protected function set_default_language( $term_id, $taxonomy ) {
85
  if ( ! $this->model->term->get_language( $term_id ) ) {
100
  }
101
 
102
  /**
103
+ * Called when a category or post tag is created or edited.
104
+ * Does nothing except on taxonomies which are filterable.
105
  *
106
  * @since 0.1
107
  *
108
+ * @param int $term_id Term id of the term being saved.
109
+ * @param int $tt_id Term taxonomy id.
110
+ * @param string $taxonomy Taxonomy name.
111
+ * @return void
112
  */
113
  public function save_term( $term_id, $tt_id, $taxonomy ) {
114
  if ( $this->model->is_translated_taxonomy( $taxonomy ) ) {
120
  }
121
 
122
  /**
123
+ * Fires after the term language and translations are saved.
124
  *
125
  * @since 1.2
126
  *
127
+ * @param int $term_id Term id.
128
+ * @param string $taxonomy Taxonomy name.
129
+ * @param int[] $translations The list of translations term ids.
130
  */
131
  do_action( 'pll_save_term', $term_id, $taxonomy, $this->model->term->get_translations( $term_id ) );
132
  }
133
  }
134
 
135
  /**
136
+ * Get the language(s) to filter WP_Term_Query.
137
  *
138
  * @since 1.7.6
139
  *
140
+ * @param string[] $taxonomies Queried taxonomies.
141
+ * @param array $args WP_Term_Query arguments.
142
+ * @return PLL_Language|string|false The language(s) to use in the filter, false otherwise.
143
  */
144
  protected function get_queried_language( $taxonomies, $args ) {
145
+ global $pagenow;
146
+
147
  // Does nothing except on taxonomies which are filterable
148
  // Since WP 4.7, make sure not to filter wp_get_object_terms()
149
  if ( ! $this->model->is_translated_taxonomy( $taxonomies ) || ! empty( $args['object_ids'] ) ) {
156
  }
157
 
158
  // On tags page, everything should be filtered according to the admin language filter except the parent dropdown
159
+ if ( 'edit-tags.php' === $pagenow && empty( $args['class'] ) ) {
160
  return $this->filter_lang;
161
  }
162
 
164
  }
165
 
166
  /**
167
+ * Adds language dependent cache domain when querying terms.
168
+ * Useful as the 'lang' parameter is not included in cache key by WordPress.
169
  *
170
  * @since 1.3
171
  *
172
+ * @param array $args WP_Term_Query arguments.
173
+ * @param string[] $taxonomies Queried taxonomies.
174
+ * @return array Modified arguments.
175
  */
176
  public function get_terms_args( $args, $taxonomies ) {
177
+ // Don't break _get_term_hierarchy().
178
  if ( 'all' === $args['get'] && 'id' === $args['orderby'] && 'id=>parent' === $args['fields'] ) {
179
  $args['lang'] = '';
180
  }
196
  *
197
  * @since 0.2
198
  *
199
+ * @param string[] $clauses List of sql clauses.
200
+ * @param string[] $taxonomies List of taxonomies.
201
+ * @param array $args WP_Term_Query arguments.
202
+ * @return string[] Modified sql clauses.
203
  */
204
  public function terms_clauses( $clauses, $taxonomies, $args ) {
205
  $lang = $this->get_queried_language( $taxonomies, $args );
207
  }
208
 
209
  /**
210
+ * Sets the WP_Term_Query language when doing a WP_Query.
211
+ * Needed since WP 4.9.
212
  *
213
  * @since 2.3.2
214
  *
215
+ * @param WP_Query $query WP_Query object.
216
+ * @return void
217
  */
218
  public function set_tax_query_lang( $query ) {
219
  $this->tax_query_lang = isset( $query->query_vars['lang'] ) ? $query->query_vars['lang'] : '';
220
  }
221
 
222
  /**
223
+ * Removes the WP_Term_Query language filter for WP_Query.
224
+ * Needed since WP 4.9.
225
  *
226
  * @since 2.3.2
227
+ *
228
+ * @return void
229
  */
230
  public function unset_tax_query_lang() {
231
  unset( $this->tax_query_lang );
238
  * @since 0.1
239
  *
240
  * @param int $term_id
241
+ * @return void
242
  */
243
  public function delete_term( $term_id ) {
244
  $this->model->term->delete_translation( $term_id );
include/filters-links.php CHANGED
@@ -9,7 +9,36 @@
9
  * @since 1.8
10
  */
11
  class PLL_Filters_Links {
12
- public $links, $links_model, $model, $options, $curlang;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  /**
15
  * Constructor
@@ -75,13 +104,13 @@ class PLL_Filters_Links {
75
  }
76
 
77
  /**
78
- * Modifies custom posts links
79
  *
80
  * @since 1.6
81
  *
82
- * @param string $link post link
83
- * @param object $post post object
84
- * @return string modified post link
85
  */
86
  public function post_type_link( $link, $post ) {
87
  // /!\ WP does not use pretty permalinks for preview
@@ -90,13 +119,13 @@ class PLL_Filters_Links {
90
  $link = $this->options['force_lang'] ? $this->links_model->switch_language_in_link( $link, $lang ) : $link;
91
 
92
  /**
93
- * Filter a post or custom post type link
94
  *
95
  * @since 1.6
96
  *
97
- * @param string $link the post link
98
- * @param object $lang the current language
99
- * @param object $post the post object
100
  */
101
  $link = apply_filters( 'pll_post_type_link', $link, $lang, $post );
102
  }
@@ -105,14 +134,14 @@ class PLL_Filters_Links {
105
  }
106
 
107
  /**
108
- * Modifies term link
109
  *
110
  * @since 0.7
111
  *
112
- * @param string $link term link
113
- * @param object $term term object
114
- * @param string $tax taxonomy name
115
- * @return string modified term link
116
  */
117
  public function term_link( $link, $term, $tax ) {
118
  if ( $this->model->is_translated_taxonomy( $tax ) ) {
@@ -124,16 +153,17 @@ class PLL_Filters_Links {
124
  *
125
  * @since 1.6
126
  *
127
- * @param string $link the term link
128
- * @param object $lang the current language
129
- * @param object $term the term object
130
  */
131
  return apply_filters( 'pll_term_link', $link, $lang, $term );
132
  }
133
 
134
- // in case someone calls get_term_link for the 'language' taxonomy
135
  if ( 'language' === $tax ) {
136
- return $this->links_model->home_url( $term );
 
137
  }
138
 
139
  return $link;
9
  * @since 1.8
10
  */
11
  class PLL_Filters_Links {
12
+ /**
13
+ * Stores the plugin options.
14
+ *
15
+ * @var array
16
+ */
17
+ public $options;
18
+
19
+ /**
20
+ * @var PLL_Model
21
+ */
22
+ public $model;
23
+
24
+ /**
25
+ * Instance of a child class of PLL_Links_Model.
26
+ *
27
+ * @var PLL_Links_Model
28
+ */
29
+ public $links_model;
30
+
31
+ /**
32
+ * @var PLL_Links
33
+ */
34
+ public $links;
35
+
36
+ /**
37
+ * Current language.
38
+ *
39
+ * @var PLL_Language
40
+ */
41
+ public $curlang;
42
 
43
  /**
44
  * Constructor
104
  }
105
 
106
  /**
107
+ * Modifies custom posts links.
108
  *
109
  * @since 1.6
110
  *
111
+ * @param string $link Post link.
112
+ * @param WP_Post $post Post object.
113
+ * @return string Modified post link.
114
  */
115
  public function post_type_link( $link, $post ) {
116
  // /!\ WP does not use pretty permalinks for preview
119
  $link = $this->options['force_lang'] ? $this->links_model->switch_language_in_link( $link, $lang ) : $link;
120
 
121
  /**
122
+ * Filters a post or custom post type link.
123
  *
124
  * @since 1.6
125
  *
126
+ * @param string $link The post link.
127
+ * @param PLL_Language $lang The current language.
128
+ * @param WP_Post $post The post object.
129
  */
130
  $link = apply_filters( 'pll_post_type_link', $link, $lang, $post );
131
  }
134
  }
135
 
136
  /**
137
+ * Modifies term links.
138
  *
139
  * @since 0.7
140
  *
141
+ * @param string $link Term link.
142
+ * @param WP_Term $term Term object.
143
+ * @param string $tax Taxonomy name;
144
+ * @return string Modified term link.
145
  */
146
  public function term_link( $link, $term, $tax ) {
147
  if ( $this->model->is_translated_taxonomy( $tax ) ) {
153
  *
154
  * @since 1.6
155
  *
156
+ * @param string $link The term link.
157
+ * @param PLL_Language $lang The current language.
158
+ * @param WP_Term $term The term object.
159
  */
160
  return apply_filters( 'pll_term_link', $link, $lang, $term );
161
  }
162
 
163
+ // In case someone calls get_term_link for the 'language' taxonomy.
164
  if ( 'language' === $tax ) {
165
+ $lang = $this->model->get_language( $term->term_id );
166
+ return $this->links_model->home_url( $lang );
167
  }
168
 
169
  return $link;
include/filters-widgets-options.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Polylang
4
+ */
5
+
6
+ /**
7
+ * Class PLL_Widgets_Filters
8
+ *
9
+ * @since 3.0
10
+ *
11
+ * Add new options to {@see https://developer.wordpress.org/reference/classes/wp_widget/ WP_Widget} and saves them.
12
+ */
13
+ class PLL_Filters_Widgets_Options {
14
+
15
+ /**
16
+ * @var PLL_Model
17
+ */
18
+ public $model;
19
+
20
+ /**
21
+ * PLL_Widgets_Filters constructor.
22
+ *
23
+ * @since 3.0 Moved actions from PLL_Admin_Filters.
24
+ *
25
+ * @param PLL_Base $polylang
26
+ * @return void
27
+ */
28
+ public function __construct( $polylang ) {
29
+ $this->model = $polylang->model;
30
+
31
+ add_action( 'in_widget_form', array( $this, 'in_widget_form' ), 10, 3 );
32
+ add_filter( 'widget_update_callback', array( $this, 'widget_update_callback' ), 10, 4 );
33
+ }
34
+
35
+ /**
36
+ * Add the language filter field to the widgets options form.
37
+ *
38
+ * @since 3.0 Moved PLL_Admin_Filters
39
+ *
40
+ * @param WP_Widget $widget
41
+ * @param null $return
42
+ * @param array $instance
43
+ * @return void
44
+ */
45
+ public function in_widget_form( $widget, $return, $instance ) {
46
+ $dropdown = new PLL_Walker_Dropdown();
47
+
48
+ $dropdown_html = $dropdown->walk(
49
+ array_merge(
50
+ array( (object) array('slug' => 0, 'name' => __( 'All languages', 'polylang' )) ),
51
+ $this->model->get_languages_list()
52
+ ),
53
+ -1,
54
+ array(
55
+ 'name' => $this->get_language_key( $widget ),
56
+ 'class' => 'tags-input pll-lang-choice',
57
+ 'selected' => empty( $instance['pll_lang'] ) ? '' : $instance['pll_lang'],
58
+ )
59
+ );
60
+
61
+ printf(
62
+ '<p><label for="%1$s">%2$s %3$s</label></p>',
63
+ esc_attr( $this->get_language_key( $widget ) ),
64
+ esc_html__( 'The widget is displayed for:', 'polylang' ),
65
+ $dropdown_html // phpcs:ignore WordPress.Security.EscapeOutput
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Called when widget options are saved.
71
+ * Saves the language associated to the widget.
72
+ *
73
+ * @since 0.3
74
+ * @since 3.0 Moved from PLL_Admin_Filters
75
+ *
76
+ * @param array $instance The current Widget's options.
77
+ * @param array $new_instance The new Widget's options.
78
+ * @param array $old_instance Not used.
79
+ * @param WP_Widget $widget WP_Widget object.
80
+ * @return array Widget options.
81
+ */
82
+ public function widget_update_callback( $instance, $new_instance, $old_instance, $widget ) {
83
+ $key = $this->get_language_key( $widget );
84
+
85
+ if ( ! empty( $new_instance[ $key ] ) && $lang = $this->model->get_language( $new_instance[ $key ] ) ) {
86
+ $instance['pll_lang'] = $lang->slug;
87
+ } else {
88
+ unset( $instance['pll_lang'] );
89
+ }
90
+
91
+ return $instance;
92
+ }
93
+
94
+ /**
95
+ * Returns the key used by Polylang to pass language data.
96
+ *
97
+ * @since 3.0
98
+ *
99
+ * @param WP_Widget $widget
100
+ * @return string
101
+ */
102
+ protected function get_language_key( $widget ) {
103
+ return $widget->id . '_lang_choice';
104
+ }
105
+ }
include/filters.php CHANGED
@@ -9,7 +9,31 @@
9
  * @since 1.4
10
  */
11
  class PLL_Filters {
12
- public $links_model, $model, $options, $curlang;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  /**
15
  * Constructor: setups filters
@@ -64,28 +88,30 @@ class PLL_Filters {
64
  * Deletes the cache for multilingual sticky posts.
65
  *
66
  * @since 2.8.4
 
 
67
  */
68
  public function delete_sticky_posts_cache() {
69
  wp_cache_delete( 'sticky_posts', 'options' );
70
  }
71
 
72
  /**
73
- * Get the language to filter a comments query
74
  *
75
  * @since 2.0
76
  *
77
- * @param object $query
78
- * @return object|bool the language(s) to use in the filter, false otherwise
79
  */
80
  protected function get_comments_queried_language( $query ) {
81
- // Don't filter comments if comment ids or post ids are specified
82
  $plucked = wp_array_slice_assoc( $query->query_vars, array( 'comment__in', 'parent', 'post_id', 'post__in', 'post_parent' ) );
83
  $fields = array_filter( $plucked );
84
  if ( ! empty( $fields ) ) {
85
  return false;
86
  }
87
 
88
- // Don't filter comments if a non translated post type is specified
89
  if ( ! empty( $query->query_vars['post_type'] ) && ! $this->model->is_translated_post_type( $query->query_vars['post_type'] ) ) {
90
  return false;
91
  }
@@ -94,30 +120,32 @@ class PLL_Filters {
94
  }
95
 
96
  /**
97
- * Adds language dependent cache domain when querying comments
98
- * Useful as the 'lang' parameter is not included in cache key by WordPress
99
- * Needed since WP 4.6 as comments have been added to persistent cache. See #36906, #37419
100
  *
101
  * @since 2.0
102
  *
103
- * @param object $query
 
104
  */
105
  public function parse_comment_query( $query ) {
106
- if ( $lang = $this->get_comments_queried_language( $query ) ) {
107
- $key = '_' . ( is_array( $lang ) ? implode( ',', $lang ) : $this->model->get_language( $lang )->slug );
 
108
  $query->query_vars['cache_domain'] = empty( $query->query_vars['cache_domain'] ) ? 'pll' . $key : $query->query_vars['cache_domain'] . $key;
109
  }
110
  }
111
 
112
  /**
113
- * Filters the comments according to the current language
114
- * Used by the recent comments widget and admin language filter
115
  *
116
  * @since 0.2
117
  *
118
- * @param array $clauses sql clauses
119
- * @param object $query WP_Comment_Query object
120
- * @return array modified $clauses
121
  */
122
  public function comments_clauses( $clauses, $query ) {
123
  global $wpdb;
@@ -125,7 +153,7 @@ class PLL_Filters {
125
  $lang = $this->get_comments_queried_language( $query );
126
 
127
  if ( ! empty( $lang ) ) {
128
- // If this clause is not already added by WP
129
  if ( ! strpos( $clauses['join'], '.ID' ) ) {
130
  $clauses['join'] .= " JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
131
  }
@@ -137,13 +165,13 @@ class PLL_Filters {
137
  }
138
 
139
  /**
140
- * Filters get_pages per language
141
  *
142
  * @since 1.4
143
  *
144
- * @param array $pages an array of pages already queried
145
- * @param array $args get_pages arguments
146
- * @return array modified list of pages
147
  */
148
  public function get_pages( $pages, $args ) {
149
  if ( isset( $args['lang'] ) && empty( $args['lang'] ) ) {
@@ -206,34 +234,34 @@ class PLL_Filters {
206
  }
207
 
208
  /**
209
- * Modifies the sql request for get_adjacent_post to filter by the current language
210
  *
211
  * @since 0.1
212
  *
213
  * @param string $sql The JOIN clause in the SQL.
214
  * @param bool $in_same_term Whether post should be in a same taxonomy term.
215
- * @param array $excluded_terms Array of excluded term IDs.
216
  * @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true.
217
  * @param WP_Post $post WP_Post object.
218
- * @return string modified JOIN clause
219
  */
220
- public function posts_join( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) {
221
  return $this->model->is_translated_post_type( $post->post_type ) && ! empty( $this->curlang ) ? $sql . $this->model->post->join_clause( 'p' ) : $sql;
222
  }
223
 
224
  /**
225
- * Modifies the sql request for wp_get_archives and get_adjacent_post to filter by the current language
226
  *
227
  * @since 0.1
228
  *
229
  * @param string $sql The WHERE clause in the SQL.
230
  * @param bool $in_same_term Whether post should be in a same taxonomy term.
231
- * @param array $excluded_terms Array of excluded term IDs.
232
  * @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true.
233
  * @param WP_Post $post WP_Post object.
234
- * @return string modified WHERE clause
235
  */
236
- public function posts_where( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) {
237
  return $this->model->is_translated_post_type( $post->post_type ) && ! empty( $this->curlang ) ? $sql . $this->model->post->where_clause( $this->curlang ) : $sql;
238
  }
239
 
@@ -278,13 +306,13 @@ class PLL_Filters {
278
  }
279
 
280
  /**
281
- * Translates the site title in emails sent to the user (change email, reset password)
282
- * It is necessary to filter the email because WP evaluates the site title before calling switch_to_locale()
283
  *
284
  * @since 2.1.3
285
  *
286
- * @param array $email
287
- * @return array
288
  */
289
  public function translate_user_email( $email ) {
290
  $blog_name = wp_specialchars_decode( pll__( get_option( 'blogname' ) ), ENT_QUOTES );
@@ -333,7 +361,7 @@ class PLL_Filters {
333
  * @since 2.3.6
334
  *
335
  * @param array $exporters Personal data exporters
336
- * @retun array
337
  */
338
  public function register_personal_data_exporter( $exporters ) {
339
  $exporters[] = array(
9
  * @since 1.4
10
  */
11
  class PLL_Filters {
12
+ /**
13
+ * Stores the plugin options.
14
+ *
15
+ * @var array
16
+ */
17
+ public $options;
18
+
19
+ /**
20
+ * @var PLL_Model
21
+ */
22
+ public $model;
23
+
24
+ /**
25
+ * Instance of a child class of PLL_Links_Model.
26
+ *
27
+ * @var PLL_Links_Model
28
+ */
29
+ public $links_model;
30
+
31
+ /**
32
+ * Current language.
33
+ *
34
+ * @var PLL_Language
35
+ */
36
+ public $curlang;
37
 
38
  /**
39
  * Constructor: setups filters
88
  * Deletes the cache for multilingual sticky posts.
89
  *
90
  * @since 2.8.4
91
+ *
92
+ * @return void
93
  */
94
  public function delete_sticky_posts_cache() {
95
  wp_cache_delete( 'sticky_posts', 'options' );
96
  }
97
 
98
  /**
99
+ * Get the language to filter a comments query.
100
  *
101
  * @since 2.0
102
  *
103
+ * @param WP_Comment_Query $query WP_Comment_Query object.
104
+ * @return PLL_Language|false The language to use in the filter, false otherwise.
105
  */
106
  protected function get_comments_queried_language( $query ) {
107
+ // Don't filter comments if comment ids or post ids are specified.
108
  $plucked = wp_array_slice_assoc( $query->query_vars, array( 'comment__in', 'parent', 'post_id', 'post__in', 'post_parent' ) );
109
  $fields = array_filter( $plucked );
110
  if ( ! empty( $fields ) ) {
111
  return false;
112
  }
113
 
114
+ // Don't filter comments if a non translated post type is specified.
115
  if ( ! empty( $query->query_vars['post_type'] ) && ! $this->model->is_translated_post_type( $query->query_vars['post_type'] ) ) {
116
  return false;
117
  }
120
  }
121
 
122
  /**
123
+ * Adds a language dependent cache domain when querying comments.
124
+ * Useful as the 'lang' parameter is not included in cache key by WordPress.
125
+ * Needed since WP 4.6 as comments have been added to persistent cache. See #36906, #37419.
126
  *
127
  * @since 2.0
128
  *
129
+ * @param WP_Comment_Query $query WP_Comment_Query object.
130
+ * @return void
131
  */
132
  public function parse_comment_query( $query ) {
133
+ $lang = $this->get_comments_queried_language( $query );
134
+ if ( $lang ) {
135
+ $key = '_' . $lang->slug;
136
  $query->query_vars['cache_domain'] = empty( $query->query_vars['cache_domain'] ) ? 'pll' . $key : $query->query_vars['cache_domain'] . $key;
137
  }
138
  }
139
 
140
  /**
141
+ * Filters the comments according to the current language.
142
+ * Used by the recent comments widget and admin language filter.
143
  *
144
  * @since 0.2
145
  *
146
+ * @param string[] $clauses SQL clauses.
147
+ * @param WP_Comment_Query $query WP_Comment_Query object.
148
+ * @return string[] Modified $clauses.
149
  */
150
  public function comments_clauses( $clauses, $query ) {
151
  global $wpdb;
153
  $lang = $this->get_comments_queried_language( $query );
154
 
155
  if ( ! empty( $lang ) ) {
156
+ // If this clause is not already added by WP.
157
  if ( ! strpos( $clauses['join'], '.ID' ) ) {
158
  $clauses['join'] .= " JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
159
  }
165
  }
166
 
167
  /**
168
+ * Filters get_pages() per language.
169
  *
170
  * @since 1.4
171
  *
172
+ * @param WP_Post[] $pages An array of pages already queried.
173
+ * @param array $args Array of get_pages() arguments.
174
+ * @return WP_Post[] Modified list of pages.
175
  */
176
  public function get_pages( $pages, $args ) {
177
  if ( isset( $args['lang'] ) && empty( $args['lang'] ) ) {
234
  }
235
 
236
  /**
237
+ * Modifies the sql request for get_adjacent_post to filter by the current language.
238
  *
239
  * @since 0.1
240
  *
241
  * @param string $sql The JOIN clause in the SQL.
242
  * @param bool $in_same_term Whether post should be in a same taxonomy term.
243
+ * @param int[] $excluded_terms Array of excluded term IDs.
244
  * @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true.
245
  * @param WP_Post $post WP_Post object.
246
+ * @return string Modified JOIN clause.
247
  */
248
+ public function posts_join( $sql, $in_same_term, $excluded_terms, $taxonomy, $post ) {
249
  return $this->model->is_translated_post_type( $post->post_type ) && ! empty( $this->curlang ) ? $sql . $this->model->post->join_clause( 'p' ) : $sql;
250
  }
251
 
252
  /**
253
+ * Modifies the sql request for wp_get_archives and get_adjacent_post to filter by the current language.
254
  *
255
  * @since 0.1
256
  *
257
  * @param string $sql The WHERE clause in the SQL.
258
  * @param bool $in_same_term Whether post should be in a same taxonomy term.
259
+ * @param int[] $excluded_terms Array of excluded term IDs.
260
  * @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true.
261
  * @param WP_Post $post WP_Post object.
262
+ * @return string Modified WHERE clause.
263
  */
264
+ public function posts_where( $sql, $in_same_term, $excluded_terms, $taxonomy, $post ) {
265
  return $this->model->is_translated_post_type( $post->post_type ) && ! empty( $this->curlang ) ? $sql . $this->model->post->where_clause( $this->curlang ) : $sql;
266
  }
267
 
306
  }
307
 
308
  /**
309
+ * Translates the site title in emails sent to the user (change email, reset password).
310
+ * It is necessary to filter the email because WP evaluates the site title before calling switch_to_locale().
311
  *
312
  * @since 2.1.3
313
  *
314
+ * @param string[] $email Email contents.
315
+ * @return string[] Translated email contents.
316
  */
317
  public function translate_user_email( $email ) {
318
  $blog_name = wp_specialchars_decode( pll__( get_option( 'blogname' ) ), ENT_QUOTES );
361
  * @since 2.3.6
362
  *
363
  * @param array $exporters Personal data exporters
364
+ * @return array
365
  */
366
  public function register_personal_data_exporter( $exporters ) {
367
  $exporters[] = array(
include/functions.php CHANGED
@@ -47,7 +47,7 @@ if ( ! function_exists( 'wpcom_vip_get_page_by_path' ) ) {
47
  *
48
  * @since 2.3.8
49
  *
50
- * return bool True if the cache compatibility must be loaded
51
  */
52
  function pll_is_cache_active() {
53
  /**
@@ -95,7 +95,7 @@ function pll_get_requested_url() {
95
  *
96
  * @since 2.6.0
97
  *
98
- * return bool True to use the block editor plugin.
99
  */
100
  function pll_use_block_editor_plugin() {
101
  /**
47
  *
48
  * @since 2.3.8
49
  *
50
+ * @return bool True if the cache compatibility must be loaded
51
  */
52
  function pll_is_cache_active() {
53
  /**
95
  *
96
  * @since 2.6.0
97
  *
98
+ * @return bool True to use the block editor plugin.
99
  */
100
  function pll_use_block_editor_plugin() {
101
  /**
include/language.php CHANGED
@@ -4,84 +4,215 @@
4
  */
5
 
6
  /**
7
- * A language object is made of two terms in 'language' and 'term_language' taxonomies
8
- * manipulating only one object per language instead of two terms should make things easier
9
- *
10
- * Properties:
11
- * term_id => id of term in 'language' taxonomy
12
- * name => language name. Ex: English
13
- * slug => language code used in url. Ex: en
14
- * term_group => order of the language when displayed in a list of languages
15
- * term_taxonomy_id => term taxonomy id in 'language' taxonomy
16
- * taxonomy => 'language'
17
- * description => language locale for backward compatibility
18
- * parent => 0 / not used
19
- * count => number of posts and pages in that language
20
- * tl_term_id => id of the term in 'term_language' taxonomy
21
- * tl_term_taxonomy_id => term taxonomy id in 'term_language' taxonomy
22
- * tl_count => number of terms in that language ( not used by Polylang )
23
- * locale => WordPress language locale. Ex: en_US
24
- * is_rtl => 1 if the language is rtl
25
- * w3c => W3C locale
26
- * flag_code => code of the flag
27
- * flag_url => url of the flag
28
- * flag => html img of the flag
29
- * custom_flag_url => url of the custom flag if exists, internal use only, moves to flag_url on frontend
30
- * custom_flag => html img of the custom flag if exists, internal use only, moves to flag on frontend
31
- * home_url => home url in this language
32
- * search_url => home url to use in search forms
33
- * host => host of this language
34
- * mo_id => id of the post storing strings translations
35
- * page_on_front => id of the page on front in this language ( set from pll_languages_list filter )
36
- * page_for_posts => id of the page for posts in this language ( set from pll_languages_list filter )
37
  *
38
  * @since 1.2
39
  */
40
  class PLL_Language {
41
- public $term_id, $name, $slug, $term_group, $term_taxonomy_id, $taxonomy, $description, $parent, $count;
42
- public $tl_term_id, $tl_term_taxonomy_id, $tl_count;
43
- public $locale, $is_rtl;
44
- public $w3c, $facebook;
45
- public $flag_url, $flag;
46
- public $home_url, $search_url;
47
- public $host, $mo_id;
48
- public $page_on_front, $page_for_posts;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  /**
51
- * Constructor: builds a language object given its two corresponding terms in language and term_language taxonomies
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  *
53
  * @since 1.2
54
  *
55
- * @param object|array $language 'language' term or language object properties stored as an array
56
- * @param object $term_language Corresponding 'term_language' term
57
  */
58
  public function __construct( $language, $term_language = null ) {
59
- // Build the object from all properties stored as an array
60
- if ( empty( $term_language ) ) {
61
  foreach ( $language as $prop => $value ) {
62
  $this->$prop = $value;
63
  }
64
  }
65
 
66
- // Build the object from taxonomies
67
- else {
68
- foreach ( $language as $prop => $value ) {
69
- $this->$prop = in_array( $prop, array( 'term_id', 'term_taxonomy_id', 'count' ) ) ? (int) $language->$prop : $language->$prop;
70
- }
 
 
 
71
 
72
  $this->tl_term_id = (int) $term_language->term_id;
73
  $this->tl_term_taxonomy_id = (int) $term_language->term_taxonomy_id;
74
  $this->tl_count = (int) $term_language->count;
75
 
76
- // The description field can contain any property
77
- // Backward compatibility for is_rtl
78
  $description = maybe_unserialize( $language->description );
79
  foreach ( $description as $prop => $value ) {
80
  'rtl' == $prop ? $this->is_rtl = $value : $this->$prop = $value;
81
  }
82
 
83
- $this->description = &$this->locale; // Backward compatibility with Polylang < 1.2
84
-
85
  $this->mo_id = PLL_MO::get_id( $this );
86
 
87
  $languages = include POLYLANG_DIR . '/settings/languages.php';
@@ -93,64 +224,72 @@ class PLL_Language {
93
  }
94
 
95
  /**
96
- * Get the flag informations
97
- * 'url' => Flag url
98
- * 'src' => Optional, src attribute value if different of the url, for example if base64 encoded
99
- * 'width' => Optional, flag width in pixels
100
- * 'height' => Optional, flag height in pixels
101
  *
102
  * @since 2.6
103
  *
104
  * @param string $code Flag code.
105
- * @return array Flag informations.
 
 
 
 
 
 
 
106
  */
107
  public static function get_flag_informations( $code ) {
108
  $flag = array( 'url' => '' );
109
 
110
- // Polylang builtin flags
111
  if ( ! empty( $code ) && file_exists( POLYLANG_DIR . ( $file = '/flags/' . $code . '.png' ) ) ) {
112
- $flag['url'] = $_url = plugins_url( $file, POLYLANG_FILE );
 
 
 
 
 
 
 
113
  }
114
 
115
  /**
116
- * Filter flag informations
117
- * 'url' => Flag url
118
- * 'src' => Optional, src attribute value if different of the url, for example if base64 encoded
119
- * 'width' => Optional, flag width in pixels
120
- * 'height' => Optional, flag height in pixels
121
  *
122
  * @since 2.4
123
  *
124
- * @param array $flag Information about the flag
125
- * @param string $code Flag code
 
 
 
 
 
 
 
126
  */
127
  $flag = apply_filters( 'pll_flag', $flag, $code );
128
 
 
 
129
  if ( empty( $flag['src'] ) ) {
130
- // If using predefined flags and base64 encoded flags are preferred
131
- if ( isset( $_url ) && $flag['url'] === $_url && ( ! defined( 'PLL_ENCODED_FLAGS' ) || PLL_ENCODED_FLAGS ) ) {
132
- list( $flag['width'], $flag['height'] ) = getimagesize( POLYLANG_DIR . $file );
133
- $file_contents = file_get_contents( POLYLANG_DIR . $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
134
- $flag['src'] = 'data:image/png;base64,' . base64_encode( $file_contents ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
135
- } else {
136
- $flag['src'] = esc_url( set_url_scheme( $flag['url'], 'relative' ) );
137
- }
138
  }
139
 
140
- $flag['url'] = esc_url_raw( $flag['url'] );
141
-
142
  return $flag;
143
  }
144
 
145
  /**
146
- * Sets flag_url and flag properties
147
  *
148
  * @since 1.2
 
 
149
  */
150
  public function set_flag() {
151
  $flags = array( 'flag' => self::get_flag_informations( $this->flag_code ) );
152
 
153
- // Custom flags ?
154
  $directories = array(
155
  PLL_LOCAL_DIR,
156
  get_stylesheet_directory() . '/polylang',
@@ -165,16 +304,19 @@ class PLL_Language {
165
  }
166
 
167
  /**
168
- * Filter the custom flag informations
169
- * 'url' => Flag url
170
- * 'src' => Optional, src attribute value if different of the url, for example if base64 encoded
171
- * 'width' => Optional, flag width in pixels
172
- * 'height' => Optional, flag height in pixels
173
  *
174
- * @since 2.4
 
175
  *
176
- * @param array $flag Information about the custom flag
177
- * @param string $code Flag code
 
 
 
 
 
 
178
  */
179
  $flags['custom_flag'] = apply_filters( 'pll_custom_flag', empty( $flags['custom_flag'] ) ? null : $flags['custom_flag'], $this->flag_code );
180
 
@@ -189,14 +331,14 @@ class PLL_Language {
189
  }
190
 
191
  /**
192
- * Filter the flag title attribute
193
- * Defaults to the language name
194
  *
195
  * @since 0.7
196
  *
197
- * @param string $title the flag title attribute
198
- * @param string $slug the language code
199
- * @param string $locale the language locale
200
  */
201
  $title = apply_filters( 'pll_flag_title', $this->name, $this->slug, $this->locale );
202
 
@@ -204,12 +346,12 @@ class PLL_Language {
204
  $this->{$key . '_url'} = empty( $flag['url'] ) ? '' : $flag['url'];
205
 
206
  /**
207
- * Filter the html markup of a flag
208
  *
209
  * @since 1.0.2
210
  *
211
- * @param string $flag html markup of the flag or empty string
212
- * @param string $slug language code
213
  */
214
  $this->{$key} = apply_filters(
215
  'pll_get_flag',
@@ -220,20 +362,20 @@ class PLL_Language {
220
  }
221
 
222
  /**
223
- * Get HTML code for flag
224
  *
225
  * @since 2.7
226
  *
227
- * @param array $flag flag properties: src, width and height
228
- * @param string $title optional title attribute
229
- * @param string $alt optional alt attribute
 
230
  */
231
  public static function get_flag_html( $flag, $title = '', $alt = '' ) {
232
  if ( empty( $flag['src'] ) ) {
233
  return '';
234
  }
235
 
236
- $title_attr = empty( $title ) ? '' : sprintf( ' title="%s"', esc_attr( $title ) );
237
  $alt_attr = empty( $alt ) ? '' : sprintf( ' alt="%s"', esc_attr( $alt ) );
238
  $width_attr = empty( $flag['width'] ) ? '' : sprintf( ' width="%s"', (int) $flag['width'] );
239
  $height_attr = empty( $flag['height'] ) ? '' : sprintf( ' height="%s"', (int) $flag['height'] );
@@ -252,9 +394,8 @@ class PLL_Language {
252
  }
253
 
254
  return sprintf(
255
- '<img src="%s"%s%s%s%s%s />',
256
  $flag['src'],
257
- $title_attr,
258
  $alt_attr,
259
  $width_attr,
260
  $height_attr,
@@ -266,6 +407,8 @@ class PLL_Language {
266
  * Returns the html of the custom flag if any, or the default flag otherwise.
267
  *
268
  * @since 2.8
 
 
269
  */
270
  public function get_display_flag() {
271
  return empty( $this->custom_flag ) ? $this->flag : $this->custom_flag;
@@ -275,28 +418,33 @@ class PLL_Language {
275
  * Returns the url of the custom flag if any, or the default flag otherwise.
276
  *
277
  * @since 2.8
 
 
278
  */
279
  public function get_display_flag_url() {
280
  return empty( $this->custom_flag_url ) ? $this->flag_url : $this->custom_flag_url;
281
  }
282
 
283
  /**
284
- * Updates post and term count
285
  *
286
  * @since 1.2
 
 
287
  */
288
  public function update_count() {
289
- wp_update_term_count( $this->term_taxonomy_id, 'language' ); // posts count
290
- wp_update_term_count( $this->tl_term_taxonomy_id, 'term_language' ); // terms count
291
  }
292
 
293
  /**
294
- * Set home_url and search_url properties
295
  *
296
  * @since 1.3
297
  *
298
- * @param string $search_url
299
- * @param string $home_url
 
300
  */
301
  public function set_home_url( $search_url, $home_url ) {
302
  $this->search_url = $search_url;
@@ -304,11 +452,13 @@ class PLL_Language {
304
  }
305
 
306
  /**
307
- * Sets the scheme of the home url and the flag urls
308
  *
309
  * This can't be cached across pages.
310
  *
311
  * @since 2.8
 
 
312
  */
313
  public function set_url_scheme() {
314
  $this->home_url = set_url_scheme( $this->home_url );
@@ -322,12 +472,12 @@ class PLL_Language {
322
  }
323
 
324
  /**
325
- * Returns the language locale
326
- * Converts WP locales to W3C valid locales for display
327
  *
328
  * @since 1.8
329
  *
330
- * @param string $filter either 'display' or 'raw', defaults to raw
331
  * @return string
332
  */
333
  public function get_locale( $filter = 'raw' ) {
4
  */
5
 
6
  /**
7
+ * A language object is made of two terms in 'language' and 'term_language' taxonomies.
8
+ * Manipulating only one object per language instead of two terms should make things easier.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  *
10
  * @since 1.2
11
  */
12
  class PLL_Language {
13
+ /**
14
+ * Id of the term in 'language' taxonomy.
15
+ *
16
+ * @var int
17
+ */
18
+ public $term_id;
19
+
20
+ /**
21
+ * Language name. Ex: English.
22
+ *
23
+ * @var string
24
+ */
25
+ public $name;
26
+
27
+ /**
28
+ * Language code used in url. Ex: en.
29
+ *
30
+ * @var string
31
+ */
32
+ public $slug;
33
+
34
+ /**
35
+ * Order of the language when displayed in a list of languages.
36
+ *
37
+ * @var int
38
+ */
39
+ public $term_group;
40
+
41
+ /**
42
+ * Term taxonomy id in 'language' taxonomy.
43
+ *
44
+ * @var int
45
+ */
46
+ public $term_taxonomy_id;
47
+
48
+ /**
49
+ * Number of posts and pages in that language.
50
+ *
51
+ * @var int
52
+ */
53
+ public $count;
54
+
55
+ /**
56
+ * Id of the term in 'term_language' taxonomy.
57
+ *
58
+ * @var int
59
+ */
60
+ public $tl_term_id;
61
+
62
+ /**
63
+ * Term taxonomy id in 'term_language' taxonomy.
64
+ *
65
+ * @var int
66
+ */
67
+ public $tl_term_taxonomy_id;
68
 
69
  /**
70
+ * Number of terms in that language.
71
+ *
72
+ * @var int
73
+ */
74
+ public $tl_count;
75
+
76
+ /**
77
+ * WordPress language locale. Ex: en_US.
78
+ *
79
+ * @var string
80
+ */
81
+ public $locale;
82
+
83
+ /**
84
+ * 1 if the language is rtl, 0 otherwise.
85
+ *
86
+ * @var int
87
+ */
88
+ public $is_rtl;
89
+
90
+ /**
91
+ * W3C locale.
92
+ *
93
+ * @var string.
94
+ */
95
+ public $w3c;
96
+
97
+ /**
98
+ * Facebook locale.
99
+ *
100
+ * @var string.
101
+ */
102
+ public $facebook;
103
+
104
+ /**
105
+ * Home url in this language.
106
+ *
107
+ * @var string
108
+ */
109
+ public $home_url;
110
+
111
+ /**
112
+ * Home url to use in search forms.
113
+ *
114
+ * @var string
115
+ */
116
+ public $search_url;
117
+
118
+ /**
119
+ * Host corresponding to this language.
120
+ *
121
+ * @var string
122
+ */
123
+ public $host;
124
+
125
+ /**
126
+ * Id of the post storing strings translations.
127
+ *
128
+ * @var int
129
+ */
130
+ public $mo_id;
131
+
132
+ /**
133
+ * Id of the page on front in this language ( set from pll_languages_list filter ).
134
+ *
135
+ * @var int
136
+ */
137
+ public $page_on_front;
138
+
139
+ /**
140
+ * Id of the page for posts in this language ( set from pll_languages_list filter ).
141
+ *
142
+ * @var int
143
+ */
144
+ public $page_for_posts;
145
+
146
+ /**
147
+ * Code of the flag.
148
+ *
149
+ * @var string
150
+ */
151
+ public $flag_code;
152
+
153
+ /**
154
+ * Url of the flag.
155
+ *
156
+ * @var string
157
+ */
158
+ public $flag_url;
159
+
160
+ /**
161
+ * Html markup of the flag.
162
+ *
163
+ * @var string
164
+ */
165
+ public $flag;
166
+
167
+ /**
168
+ * Url of the custom flag if it exists.
169
+ *
170
+ * @var string
171
+ */
172
+ protected $custom_flag_url;
173
+
174
+ /**
175
+ * Html markup of the custom flag if it exists.
176
+ *
177
+ * @var string
178
+ */
179
+ protected $custom_flag;
180
+
181
+ /**
182
+ * Constructor: builds a language object given its two corresponding terms in 'language' and 'term_language' taxonomies.
183
  *
184
  * @since 1.2
185
  *
186
+ * @param WP_Term|array $language Term in 'language' taxonomy or language object properties stored as an array.
187
+ * @param WP_Term $term_language Corresponding 'term_language' term.
188
  */
189
  public function __construct( $language, $term_language = null ) {
190
+ // Build the object from all properties stored as an array.
191
+ if ( is_array( $language ) ) {
192
  foreach ( $language as $prop => $value ) {
193
  $this->$prop = $value;
194
  }
195
  }
196
 
197
+ // Build the object from taxonomy terms.
198
+ elseif ( ! empty( $term_language ) ) {
199
+ $this->term_id = (int) $language->term_id;
200
+ $this->name = $language->name;
201
+ $this->slug = $language->slug;
202
+ $this->term_group = (int) $language->term_group;
203
+ $this->term_taxonomy_id = (int) $language->term_taxonomy_id;
204
+ $this->count = (int) $language->count;
205
 
206
  $this->tl_term_id = (int) $term_language->term_id;
207
  $this->tl_term_taxonomy_id = (int) $term_language->term_taxonomy_id;
208
  $this->tl_count = (int) $term_language->count;
209
 
210
+ // The description field can contain any property.
 
211
  $description = maybe_unserialize( $language->description );
212
  foreach ( $description as $prop => $value ) {
213
  'rtl' == $prop ? $this->is_rtl = $value : $this->$prop = $value;
214
  }
215
 
 
 
216
  $this->mo_id = PLL_MO::get_id( $this );
217
 
218
  $languages = include POLYLANG_DIR . '/settings/languages.php';
224
  }
225
 
226
  /**
227
+ * Get the flag informations:
 
 
 
 
228
  *
229
  * @since 2.6
230
  *
231
  * @param string $code Flag code.
232
+ * @return array {
233
+ * Flag informations.
234
+ *
235
+ * @type string $url Flag url.
236
+ * @type string $src Optional, src attribute value if different of the url, for example if base64 encoded.
237
+ * @type int $width Optional, flag width in pixels.
238
+ * @type int $height Optional, flag height in pixels.
239
+ * }
240
  */
241
  public static function get_flag_informations( $code ) {
242
  $flag = array( 'url' => '' );
243
 
244
+ // Polylang builtin flags.
245
  if ( ! empty( $code ) && file_exists( POLYLANG_DIR . ( $file = '/flags/' . $code . '.png' ) ) ) {
246
+ $flag['url'] = plugins_url( $file, POLYLANG_FILE );
247
+
248
+ // If base64 encoded flags are preferred.
249
+ if ( ! defined( 'PLL_ENCODED_FLAGS' ) || PLL_ENCODED_FLAGS ) {
250
+ list( $flag['width'], $flag['height'] ) = getimagesize( POLYLANG_DIR . $file );
251
+ $file_contents = file_get_contents( POLYLANG_DIR . $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
252
+ $flag['src'] = 'data:image/png;base64,' . base64_encode( $file_contents ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
253
+ }
254
  }
255
 
256
  /**
257
+ * Filters flag informations:
 
 
 
 
258
  *
259
  * @since 2.4
260
  *
261
+ * @param array $flag {
262
+ * Information about the flag.
263
+ *
264
+ * @type string $url Flag url.
265
+ * @type string $src Optional, src attribute value if different of the url, for example if base64 encoded.
266
+ * @type int $width Optional, flag width in pixels.
267
+ * @type int $height Optional, flag height in pixels.
268
+ * }
269
+ * @param string $code Flag code.
270
  */
271
  $flag = apply_filters( 'pll_flag', $flag, $code );
272
 
273
+ $flag['url'] = esc_url_raw( $flag['url'] );
274
+
275
  if ( empty( $flag['src'] ) ) {
276
+ $flag['src'] = esc_url( set_url_scheme( $flag['url'], 'relative' ) );
 
 
 
 
 
 
 
277
  }
278
 
 
 
279
  return $flag;
280
  }
281
 
282
  /**
283
+ * Sets flag_url and flag properties.
284
  *
285
  * @since 1.2
286
+ *
287
+ * @return void
288
  */
289
  public function set_flag() {
290
  $flags = array( 'flag' => self::get_flag_informations( $this->flag_code ) );
291
 
292
+ // Custom flags?
293
  $directories = array(
294
  PLL_LOCAL_DIR,
295
  get_stylesheet_directory() . '/polylang',
304
  }
305
 
306
  /**
307
+ * Filters the custom flag informations.
 
 
 
 
308
  *
309
+ * @param array $flag {
310
+ * Information about the custom flag.
311
  *
312
+ * @type string $url Flag url.
313
+ * @type string $src Optional, src attribute value if different of the url, for example if base64 encoded.
314
+ * @type int $width Optional, flag width in pixels.
315
+ * @type int $height Optional, flag height in pixels.
316
+ * }
317
+ * @param string $code Flag code.
318
+ *
319
+ * @since 2.4
320
  */
321
  $flags['custom_flag'] = apply_filters( 'pll_custom_flag', empty( $flags['custom_flag'] ) ? null : $flags['custom_flag'], $this->flag_code );
322
 
331
  }
332
 
333
  /**
334
+ * Filters the flag title attribute.
335
+ * Defaults to the language name.
336
  *
337
  * @since 0.7
338
  *
339
+ * @param string $title The flag title attribute.
340
+ * @param string $slug The language code.
341
+ * @param string $locale The language locale.
342
  */
343
  $title = apply_filters( 'pll_flag_title', $this->name, $this->slug, $this->locale );
344
 
346
  $this->{$key . '_url'} = empty( $flag['url'] ) ? '' : $flag['url'];
347
 
348
  /**
349
+ * Filters the html markup of a flag.
350
  *
351
  * @since 1.0.2
352
  *
353
+ * @param string $flag Html markup of the flag or empty string.
354
+ * @param string $slug Language code.
355
  */
356
  $this->{$key} = apply_filters(
357
  'pll_get_flag',
362
  }
363
 
364
  /**
365
+ * Get HTML code for flag.
366
  *
367
  * @since 2.7
368
  *
369
+ * @param array $flag Flag properties: src, width and height.
370
+ * @param string $title Optional title attribute.
371
+ * @param string $alt Optional alt attribute.
372
+ * @return string
373
  */
374
  public static function get_flag_html( $flag, $title = '', $alt = '' ) {
375
  if ( empty( $flag['src'] ) ) {
376
  return '';
377
  }
378
 
 
379
  $alt_attr = empty( $alt ) ? '' : sprintf( ' alt="%s"', esc_attr( $alt ) );
380
  $width_attr = empty( $flag['width'] ) ? '' : sprintf( ' width="%s"', (int) $flag['width'] );
381
  $height_attr = empty( $flag['height'] ) ? '' : sprintf( ' height="%s"', (int) $flag['height'] );
394
  }
395
 
396
  return sprintf(
397
+ '<img src="%s"%s%s%s%s />',
398
  $flag['src'],
 
399
  $alt_attr,
400
  $width_attr,
401
  $height_attr,
407
  * Returns the html of the custom flag if any, or the default flag otherwise.
408
  *
409
  * @since 2.8
410
+ *
411
+ * @return string
412
  */
413
  public function get_display_flag() {
414
  return empty( $this->custom_flag ) ? $this->flag : $this->custom_flag;
418
  * Returns the url of the custom flag if any, or the default flag otherwise.
419
  *
420
  * @since 2.8
421
+ *
422
+ * @return string
423
  */
424
  public function get_display_flag_url() {
425
  return empty( $this->custom_flag_url ) ? $this->flag_url : $this->custom_flag_url;
426
  }
427
 
428
  /**
429
+ * Updates post and term count.
430
  *
431
  * @since 1.2
432
+ *
433
+ * @return void
434
  */
435
  public function update_count() {
436
+ wp_update_term_count( $this->term_taxonomy_id, 'language' ); // Posts count.
437
+ wp_update_term_count( $this->tl_term_taxonomy_id, 'term_language' ); // Terms count.
438
  }
439
 
440
  /**
441
+ * Set home_url and search_url properties.
442
  *
443
  * @since 1.3
444
  *
445
+ * @param string $search_url Home url to use in search forms.
446
+ * @param string $home_url Home url.
447
+ * @return void
448
  */
449
  public function set_home_url( $search_url, $home_url ) {
450
  $this->search_url = $search_url;
452
  }
453
 
454
  /**
455
+ * Sets the scheme of the home url and the flag urls.
456
  *
457
  * This can't be cached across pages.
458
  *
459
  * @since 2.8
460
+ *
461
+ * @return void
462
  */
463
  public function set_url_scheme() {
464
  $this->home_url = set_url_scheme( $this->home_url );
472
  }
473
 
474
  /**
475
+ * Returns the language locale.
476
+ * Converts WP locales to W3C valid locales for display.
477
  *
478
  * @since 1.8
479
  *
480
+ * @param string $filter Either 'display' or 'raw', defaults to raw.
481
  * @return string
482
  */
483
  public function get_locale( $filter = 'raw' ) {
include/license.php CHANGED
@@ -9,8 +9,60 @@
9
  * @since 1.9
10
  */
11
  class PLL_License {
12
- public $id, $name, $license_key, $license_data;
13
- private $file, $version, $author;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  private $api_url = 'https://polylang.pro';
15
 
16
  /**
@@ -57,6 +109,8 @@ class PLL_License {
57
  * Auto updater
58
  *
59
  * @since 1.9
 
 
60
  */
61
  public function auto_updater() {
62
  $args = array(
@@ -132,6 +186,7 @@ class PLL_License {
132
  * @since 1.9
133
  *
134
  * @param string $request check_license | activate_license | deactivate_license
 
135
  */
136
  private function api_request( $request ) {
137
  $licenses = get_option( 'polylang_licenses' );
@@ -191,11 +246,12 @@ class PLL_License {
191
  $license = $this->license_data;
192
  }
193
 
194
- $class = 'license-null';
 
195
 
196
  $out = sprintf(
197
  '<td><label for="pll-licenses[%1$s]">%2$s</label></td>' .
198
- '<td><input name="licenses[%1$s]" id="pll-licenses[%1$s]" type="text" value="%3$s" class="regular-text code" />',
199
  esc_attr( $this->id ),
200
  esc_attr( $this->name ),
201
  esc_html( $this->license_key )
@@ -220,7 +276,7 @@ class PLL_License {
220
  /* translators: %1$s is a date, %2$s is link start tag, %3$s is link end tag. */
221
  esc_html__( 'Your license key expired on %1$s. Please %2$srenew your license key%3$s.', 'polylang' ),
222
  esc_html( date_i18n( get_option( 'date_format' ), $expiration ) ),
223
- sprintf( '<a href="%s" target="_blank">', esc_url( 'https://polylang.pro/checkout/?edd_license_key=' . $this->license_key ) ),
224
  '</a>'
225
  );
226
  break;
@@ -234,7 +290,7 @@ class PLL_License {
234
  $message = sprintf(
235
  /* translators: %1$s is link start tag, %2$s is link end tag. */
236
  esc_html__( 'Invalid license. Please %1$svisit your account page%2$s and verify it.', 'polylang' ),
237
- sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account' ),
238
  '</a>'
239
  );
240
  break;
@@ -245,7 +301,7 @@ class PLL_License {
245
  /* translators: %1$s is a product name, %2$s is link start tag, %3$s is link end tag. */
246
  esc_html__( 'Your %1$s license key is not active for this URL. Please %2$svisit your account page%3$s to manage your license key URLs.', 'polylang' ),
247
  esc_html( $this->name ),
248
- sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account' ),
249
  '</a>'
250
  );
251
  break;
@@ -259,7 +315,7 @@ class PLL_License {
259
  $message = sprintf(
260
  /* translators: %1$s is link start tag, %2$s is link end tag */
261
  esc_html__( 'Your license key has reached its activation limit. %1$sView possible upgrades%2$s now.', 'polylang' ),
262
- sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account' ),
263
  '</a>'
264
  );
265
  break;
@@ -277,7 +333,7 @@ class PLL_License {
277
  /* translators: %1$s is a date, %2$s is link start tag, %3$s is link end tag. */
278
  esc_html__( 'Your license key will expire soon! Precisely, it will expire on %1$s. %2$sRenew your license key today!%3$s.', 'polylang' ),
279
  esc_html( date_i18n( get_option( 'date_format' ), $expiration ) ),
280
- sprintf( '<a href="%s" target="_blank">', esc_url( 'https://polylang.pro/checkout/?edd_license_key=' . $this->license_key ) ),
281
  '</a>'
282
  );
283
  } else {
9
  * @since 1.9
10
  */
11
  class PLL_License {
12
+ /**
13
+ * Sanitized plugin name.
14
+ *
15
+ * @var string
16
+ */
17
+ public $id;
18
+
19
+ /**
20
+ * Plugin name.
21
+ *
22
+ * @var string
23
+ */
24
+ public $name;
25
+
26
+ /**
27
+ * License key.
28
+ *
29
+ * @var string
30
+ */
31
+ public $license_key;
32
+
33
+ /**
34
+ * License data, obtained from the API request.
35
+ *
36
+ * @var stdClass
37
+ */
38
+ public $license_data;
39
+
40
+ /**
41
+ * Main plugin file.
42
+ *
43
+ * @var string
44
+ */
45
+ private $file;
46
+
47
+ /**
48
+ * Current plugin version.
49
+ *
50
+ * @var string
51
+ */
52
+ private $version;
53
+
54
+ /**
55
+ * Plugin author.
56
+ *
57
+ * @var string
58
+ */
59
+ private $author;
60
+
61
+ /**
62
+ * API url.
63
+ *
64
+ * @var string.
65
+ */
66
  private $api_url = 'https://polylang.pro';
67
 
68
  /**
109
  * Auto updater
110
  *
111
  * @since 1.9
112
+ *
113
+ * @return void
114
  */
115
  public function auto_updater() {
116
  $args = array(
186
  * @since 1.9
187
  *
188
  * @param string $request check_license | activate_license | deactivate_license
189
+ * @return void
190
  */
191
  private function api_request( $request ) {
192
  $licenses = get_option( 'polylang_licenses' );
246
  $license = $this->license_data;
247
  }
248
 
249
+ $class = 'license-null';
250
+ $message = '';
251
 
252
  $out = sprintf(
253
  '<td><label for="pll-licenses[%1$s]">%2$s</label></td>' .
254
+ '<td><input name="licenses[%1$s]" id="pll-licenses[%1$s]" type="password" value="%3$s" class="regular-text code" />',
255
  esc_attr( $this->id ),
256
  esc_attr( $this->name ),
257
  esc_html( $this->license_key )
276
  /* translators: %1$s is a date, %2$s is link start tag, %3$s is link end tag. */
277
  esc_html__( 'Your license key expired on %1$s. Please %2$srenew your license key%3$s.', 'polylang' ),
278
  esc_html( date_i18n( get_option( 'date_format' ), $expiration ) ),
279
+ sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account/' ),
280
  '</a>'
281
  );
282
  break;
290
  $message = sprintf(
291
  /* translators: %1$s is link start tag, %2$s is link end tag. */
292
  esc_html__( 'Invalid license. Please %1$svisit your account page%2$s and verify it.', 'polylang' ),
293
+ sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account/' ),
294
  '</a>'
295
  );
296
  break;
301
  /* translators: %1$s is a product name, %2$s is link start tag, %3$s is link end tag. */
302
  esc_html__( 'Your %1$s license key is not active for this URL. Please %2$svisit your account page%3$s to manage your license key URLs.', 'polylang' ),
303
  esc_html( $this->name ),
304
+ sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account/' ),
305
  '</a>'
306
  );
307
  break;
315
  $message = sprintf(
316
  /* translators: %1$s is link start tag, %2$s is link end tag */
317
  esc_html__( 'Your license key has reached its activation limit. %1$sView possible upgrades%2$s now.', 'polylang' ),
318
+ sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account/' ),
319
  '</a>'
320
  );
321
  break;
333
  /* translators: %1$s is a date, %2$s is link start tag, %3$s is link end tag. */
334
  esc_html__( 'Your license key will expire soon! Precisely, it will expire on %1$s. %2$sRenew your license key today!%3$s.', 'polylang' ),
335
  esc_html( date_i18n( get_option( 'date_format' ), $expiration ) ),
336
+ sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account/' ),
337
  '</a>'
338
  );
339
  } else {
include/links-abstract-domain.php CHANGED
@@ -11,11 +11,11 @@
11
  abstract class PLL_Links_Abstract_Domain extends PLL_Links_Permalinks {
12
 
13
  /**
14
- * Constructor
15
  *
16
  * @since 2.0
17
  *
18
- * @param object $model PLL_Model instance.
19
  */
20
  public function __construct( &$model ) {
21
  parent::__construct( $model );
@@ -48,15 +48,15 @@ abstract class PLL_Links_Abstract_Domain extends PLL_Links_Permalinks {
48
  }
49
 
50
  /**
51
- * Sets the home urls
52
  *
53
  * @since 2.2
54
  *
55
- * @param object $language
56
  */
57
  protected function set_home_url( $language ) {
58
  $home_url = $this->home_url( $language );
59
- $language->set_home_url( $home_url, $home_url ); // Search url and home url are the same
60
  }
61
 
62
  /**
11
  abstract class PLL_Links_Abstract_Domain extends PLL_Links_Permalinks {
12
 
13
  /**
14
+ * Constructor.
15
  *
16
  * @since 2.0
17
  *
18
+ * @param PLL_Model $model Instance of PLL_Model.
19
  */
20
  public function __construct( &$model ) {
21
  parent::__construct( $model );
48
  }
49
 
50
  /**
51
+ * Sets the home urls.
52
  *
53
  * @since 2.2
54
  *
55
+ * @param PLL_Language $language Language object.
56
  */
57
  protected function set_home_url( $language ) {
58
  $home_url = $this->home_url( $language );
59
+ $language->set_home_url( $home_url, $home_url ); // Search url and home url are the same.
60
  }
61
 
62
  /**
include/links-default.php CHANGED
@@ -11,17 +11,22 @@
11
  * @since 1.2
12
  */
13
  class PLL_Links_Default extends PLL_Links_Model {
 
 
 
 
 
14
  public $using_permalinks = false;
15
 
16
  /**
17
- * Adds language information to an url
18
- * links_model interface
19
  *
20
  * @since 1.2
21
  *
22
- * @param string $url url to modify
23
- * @param object $lang language
24
- * @return string modified url
25
  */
26
  public function add_language_to_link( $url, $lang ) {
27
  return empty( $lang ) || ( $this->options['hide_default'] && $this->options['default_lang'] == $lang->slug ) ? $url : add_query_arg( 'lang', $lang->slug, $url );
@@ -86,12 +91,12 @@ class PLL_Links_Default extends PLL_Links_Model {
86
  }
87
 
88
  /**
89
- * Returns the static front page url
90
  *
91
  * @since 1.8
92
  *
93
- * @param object $lang
94
- * @return string
95
  */
96
  public function front_page_url( $lang ) {
97
  if ( $this->options['hide_default'] && $lang->slug == $this->options['default_lang'] ) {
11
  * @since 1.2
12
  */
13
  class PLL_Links_Default extends PLL_Links_Model {
14
+ /**
15
+ * Tells this child class of PLL_Links_Model does not use pretty permalinks.
16
+ *
17
+ * @var bool
18
+ */
19
  public $using_permalinks = false;
20
 
21
  /**
22
+ * Adds the language code in a url.
23
+ * links_model interface.
24
  *
25
  * @since 1.2
26
  *
27
+ * @param string $url The url to modify.
28
+ * @param PLL_Language $lang The language object.
29
+ * @return string Modified url.
30
  */
31
  public function add_language_to_link( $url, $lang ) {
32
  return empty( $lang ) || ( $this->options['hide_default'] && $this->options['default_lang'] == $lang->slug ) ? $url : add_query_arg( 'lang', $lang->slug, $url );
91
  }
92
 
93
  /**
94
+ * Returns the static front page url.
95
  *
96
  * @since 1.8
97
  *
98
+ * @param PLL_Language $lang The language object.
99
+ * @return string The static front page url.
100
  */
101
  public function front_page_url( $lang ) {
102
  if ( $this->options['hide_default'] && $lang->slug == $this->options['default_lang'] ) {
include/links-directory.php CHANGED
@@ -11,14 +11,19 @@
11
  * @since 1.2
12
  */
13
  class PLL_Links_Directory extends PLL_Links_Permalinks {
 
 
 
 
 
14
  protected $home_relative;
15
 
16
  /**
17
- * Constructor
18
  *
19
  * @since 1.2
20
  *
21
- * @param object $model PLL_Model instance
22
  */
23
  public function __construct( &$model ) {
24
  parent::__construct( $model );
@@ -36,6 +41,8 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
36
  * Called only at first object creation to avoid duplicating filters when switching blog
37
  *
38
  * @since 1.6
 
 
39
  */
40
  public function init() {
41
  if ( did_action( 'setup_theme' ) ) {
@@ -49,14 +56,14 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
49
  }
50
 
51
  /**
52
- * Adds the language code in url
53
- * links_model interface
54
  *
55
  * @since 1.2
56
  *
57
- * @param string $url url to modify
58
- * @param object $lang language
59
- * @return string modified url
60
  */
61
  public function add_language_to_link( $url, $lang ) {
62
  if ( ! empty( $lang ) ) {
@@ -126,12 +133,12 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
126
  }
127
 
128
  /**
129
- * Returns the home url
130
- * links_model interface
131
  *
132
  * @since 1.3.1
133
  *
134
- * @param object $lang PLL_Language object
135
  * @return string
136
  */
137
  public function home_url( $lang ) {
@@ -144,6 +151,8 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
144
  * Optionally removes 'language' in permalinks so that we get http://www.myblog/en/ instead of http://www.myblog/language/en/
145
  *
146
  * @since 1.2
 
 
147
  */
148
  public function add_permastruct() {
149
  // Language information always in front of the uri ( 'with_front' => false )
@@ -155,12 +164,12 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
155
  }
156
 
157
  /**
158
- * Prepares rewrite rules filters
159
  *
160
  * @since 0.8.1
161
  *
162
- * @param array $pre not used
163
- * @return unmodified $pre
164
  */
165
  public function prepare_rewrite_rules( $pre ) {
166
  // Don't modify the rules if there is no languages created yet
@@ -180,13 +189,13 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
180
 
181
  /**
182
  * The rewrite rules !
183
- * always make sure the default language is at the end in case the language information is hidden for default language
184
- * thanks to brbrbr http://wordpress.org/support/topic/plugin-polylang-rewrite-rules-not-correct
185
  *
186
  * @since 0.8.1
187
  *
188
- * @param array $rules rewrite rules
189
- * @return array modified rewrite rules
190
  */
191
  public function rewrite_rules( $rules ) {
192
  $filter = str_replace( '_rewrite_rules', '', current_filter() );
11
  * @since 1.2
12
  */
13
  class PLL_Links_Directory extends PLL_Links_Permalinks {
14
+ /**
15
+ * Relative path to the home url.
16
+ *
17
+ * @var string
18
+ */
19
  protected $home_relative;
20
 
21
  /**
22
+ * Constructor.
23
  *
24
  * @since 1.2
25
  *
26
+ * @param PLL_Model $model PLL_Model instance.
27
  */
28
  public function __construct( &$model ) {
29
  parent::__construct( $model );
41
  * Called only at first object creation to avoid duplicating filters when switching blog
42
  *
43
  * @since 1.6
44
+ *
45
+ * @return void
46
  */
47
  public function init() {
48
  if ( did_action( 'setup_theme' ) ) {
56
  }
57
 
58
  /**
59
+ * Adds the language code in a url.
60
+ * links_model interface.
61
  *
62
  * @since 1.2
63
  *
64
+ * @param string $url The url to modify.
65
+ * @param PLL_Language $lang The language object.
66
+ * @return string Modified url.
67
  */
68
  public function add_language_to_link( $url, $lang ) {
69
  if ( ! empty( $lang ) ) {
133
  }
134
 
135
  /**
136
+ * Returns the home url in a given language.
137
+ * links_model interface.
138
  *
139
  * @since 1.3.1
140
  *
141
+ * @param PLL_Language $lang PLL_Language object.
142
  * @return string
143
  */
144
  public function home_url( $lang ) {
151
  * Optionally removes 'language' in permalinks so that we get http://www.myblog/en/ instead of http://www.myblog/language/en/
152
  *
153
  * @since 1.2
154
+ *
155
+ * @return void
156
  */
157
  public function add_permastruct() {
158
  // Language information always in front of the uri ( 'with_front' => false )
164
  }
165
 
166
  /**
167
+ * Prepares the rewrite rules filters.
168
  *
169
  * @since 0.8.1
170
  *
171
+ * @param mixed $pre Not used as the filter is used as an action.
172
+ * @return mixed
173
  */
174
  public function prepare_rewrite_rules( $pre ) {
175
  // Don't modify the rules if there is no languages created yet
189
 
190
  /**
191
  * The rewrite rules !
192
+ * Always make sure that the default language is at the end in case the language information is hidden for default language.
193
+ * Thanks to brbrbr http://wordpress.org/support/topic/plugin-polylang-rewrite-rules-not-correct.
194
  *
195
  * @since 0.8.1
196
  *
197
+ * @param string[] $rules Rewrite rules.
198
+ * @return string[] Modified rewrite rules.
199
  */
200
  public function rewrite_rules( $rules ) {
201
  $filter = str_replace( '_rewrite_rules', '', current_filter() );
include/links-domain.php CHANGED
@@ -12,6 +12,13 @@
12
  */
13
  class PLL_Links_Domain extends PLL_Links_Abstract_Domain {
14
 
 
 
 
 
 
 
 
15
  /**
16
  * Constructor
17
  *
@@ -35,9 +42,9 @@ class PLL_Links_Domain extends PLL_Links_Abstract_Domain {
35
  *
36
  * @since 1.2
37
  *
38
- * @param string $url url to modify
39
- * @param object $lang language
40
- * @return string modified url
41
  */
42
  public function add_language_to_link( $url, $lang ) {
43
  if ( ! empty( $lang ) && ! empty( $this->hosts[ $lang->slug ] ) ) {
@@ -63,12 +70,12 @@ class PLL_Links_Domain extends PLL_Links_Abstract_Domain {
63
  }
64
 
65
  /**
66
- * Returns the home url
67
- * links_model interface
68
  *
69
  * @since 1.3.1
70
  *
71
- * @param object $lang PLL_Language object
72
  * @return string
73
  */
74
  public function home_url( $lang ) {
@@ -76,11 +83,11 @@ class PLL_Links_Domain extends PLL_Links_Abstract_Domain {
76
  }
77
 
78
  /**
79
- * Get hosts managed on the website
80
  *
81
  * @since 1.5
82
  *
83
- * @return array list of hosts
84
  */
85
  public function get_hosts() {
86
  $hosts = array();
12
  */
13
  class PLL_Links_Domain extends PLL_Links_Abstract_Domain {
14
 
15
+ /**
16
+ * An array with language code as keys and the host as values.
17
+ *
18
+ * @var string[]
19
+ */
20
+ protected $hosts;
21
+
22
  /**
23
  * Constructor
24
  *
42
  *
43
  * @since 1.2
44
  *
45
+ * @param string $url The url to modify.
46
+ * @param PLL_Language $lang The language object.
47
+ * @return string Modified url.
48
  */
49
  public function add_language_to_link( $url, $lang ) {
50
  if ( ! empty( $lang ) && ! empty( $this->hosts[ $lang->slug ] ) ) {
70
  }
71
 
72
  /**
73
+ * Returns the home url in a given language.
74
+ * links_model interface.
75
  *
76
  * @since 1.3.1
77
  *
78
+ * @param PLL_Language $lang PLL_Language object.
79
  * @return string
80
  */
81
  public function home_url( $lang ) {
83
  }
84
 
85
  /**
86
+ * Get hosts managed on the website.
87
  *
88
  * @since 1.5
89
  *
90
+ * @return string[] List of hosts.
91
  */
92
  public function get_hosts() {
93
  $hosts = array();
include/links-model.php CHANGED
@@ -4,43 +4,128 @@
4
  */
5
 
6
  /**
7
- * Links model abstract class
8
  *
9
  * @since 1.5
10
  */
11
  abstract class PLL_Links_Model {
12
- public $model, $options;
13
- public $home; // used to store the home url before it is filtered
 
 
 
14
  public $using_permalinks;
15
 
16
  /**
17
- * Constructor
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  *
19
  * @since 1.5
20
  *
21
- * @param object $model PLL_Model instance
22
  */
23
  public function __construct( &$model ) {
24
- $this->model = &$model;
25
  $this->options = &$model->options;
26
 
27
  $this->home = home_url();
28
 
29
- add_filter( 'pll_languages_list', array( $this, 'pll_languages_list' ), 4 ); // after PLL_Static_Pages
30
  add_filter( 'pll_after_languages_cache', array( $this, 'pll_after_languages_cache' ) );
31
 
32
- // adds our domains or subdomains to allowed hosts for safe redirection
33
  add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
34
  }
35
 
36
  /**
37
- * Changes the language code in url
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  *
39
  * @since 1.5
40
  *
41
- * @param string $url url to modify
42
- * @param object $lang language
43
- * @return string modified url
44
  */
45
  public function switch_language_in_link( $url, $lang ) {
46
  $url = $this->remove_language_from_link( $url );
@@ -48,22 +133,22 @@ abstract class PLL_Links_Model {
48
  }
49
 
50
  /**
51
- * Get hosts managed on the website
52
  *
53
  * @since 1.5
54
  *
55
- * @return array list of hosts
56
  */
57
  public function get_hosts() {
58
  return array( wp_parse_url( $this->home, PHP_URL_HOST ) );
59
  }
60
 
61
  /**
62
- * Returns the home url
63
  *
64
  * @since 1.3.1
65
  *
66
- * @param object $lang PLL_Language object
67
  * @return string
68
  */
69
  public function home_url( $lang ) {
@@ -72,11 +157,12 @@ abstract class PLL_Links_Model {
72
  }
73
 
74
  /**
75
- * Sets the home urls
76
  *
77
  * @since 1.8
78
  *
79
- * @param object $language
 
80
  */
81
  protected function set_home_url( $language ) {
82
  // We should always have a default language here, except, temporarily, in PHPUnit tests. The test here protects against PHP notices.
@@ -88,12 +174,12 @@ abstract class PLL_Links_Model {
88
  }
89
 
90
  /**
91
- * Sets the home urls and flags before the languages are persistently cached
92
  *
93
  * @since 1.8
94
  *
95
- * @param array $languages array of PLL_Language objects
96
- * @return array
97
  */
98
  public function pll_languages_list( $languages ) {
99
  foreach ( $languages as $language ) {
@@ -104,17 +190,17 @@ abstract class PLL_Links_Model {
104
  }
105
 
106
  /**
107
- * Sets the home urls when not cached
108
- * Sets the home urls scheme
109
  *
110
  * @since 1.8
111
  *
112
- * @param array $languages array of PLL_Language objects
113
- * @return array
114
  */
115
  public function pll_after_languages_cache( $languages ) {
116
  foreach ( $languages as $language ) {
117
- // Get the home urls when not cached
118
  if ( ( defined( 'PLL_CACHE_LANGUAGES' ) && ! PLL_CACHE_LANGUAGES ) || ( defined( 'PLL_CACHE_HOME_URL' ) && ! PLL_CACHE_HOME_URL ) ) {
119
  $this->set_home_url( $language );
120
  }
@@ -126,12 +212,12 @@ abstract class PLL_Links_Model {
126
  }
127
 
128
  /**
129
- * Adds our domains or subdomains to allowed hosts for safe redirection
130
  *
131
  * @since 1.4.3
132
  *
133
- * @param array $hosts allowed hosts
134
- * @return array
135
  */
136
  public function allowed_redirect_hosts( $hosts ) {
137
  return array_unique( array_merge( $hosts, array_values( $this->get_hosts() ) ) );
4
  */
5
 
6
  /**
7
+ * Links model abstract class.
8
  *
9
  * @since 1.5
10
  */
11
  abstract class PLL_Links_Model {
12
+ /**
13
+ * True if the child class uses pretty permalinks, false otherwise.
14
+ *
15
+ * @var bool
16
+ */
17
  public $using_permalinks;
18
 
19
  /**
20
+ * Stores the plugin options.
21
+ *
22
+ * @var array
23
+ */
24
+ public $options;
25
+
26
+ /**
27
+ * @var PLL_Model
28
+ */
29
+ public $model;
30
+
31
+ /**
32
+ * Stores the home url before it is filtered.
33
+ *
34
+ * @var string
35
+ */
36
+ public $home;
37
+
38
+ /**
39
+ * Constructor.
40
  *
41
  * @since 1.5
42
  *
43
+ * @param PLL_Model $model PLL_Model instance.
44
  */
45
  public function __construct( &$model ) {
46
+ $this->model = &$model;
47
  $this->options = &$model->options;
48
 
49
  $this->home = home_url();
50
 
51
+ add_filter( 'pll_languages_list', array( $this, 'pll_languages_list' ), 4 ); // After PLL_Static_Pages.
52
  add_filter( 'pll_after_languages_cache', array( $this, 'pll_after_languages_cache' ) );
53
 
54
+ // Adds our domains or subdomains to allowed hosts for safe redirection.
55
  add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
56
  }
57
 
58
  /**
59
+ * Adds the language code in url.
60
+ *
61
+ * @since 1.2
62
+ *
63
+ * @param string $url The url to modify.
64
+ * @param PLL_Language $lang The language object.
65
+ * @return string Modified url.
66
+ */
67
+ abstract public function add_language_to_link( $url, $lang );
68
+
69
+ /**
70
+ * Returns the url without language code.
71
+ *
72
+ * @since 1.2
73
+ *
74
+ * @param string $url The url to modify.
75
+ * @return string Modified url.
76
+ */
77
+ abstract public function remove_language_from_link( $url );
78
+
79
+ /**
80
+ * Returns the link to the first page.
81
+ *
82
+ * @since 1.2
83
+ *
84
+ * @param string $url The url to modify.
85
+ * @return string Modified url.
86
+ */
87
+ abstract public function remove_paged_from_link( $url );
88
+
89
+ /**
90
+ * Returns the link to a paged page.
91
+ *
92
+ * @since 1.5
93
+ *
94
+ * @param string $url The url to modify.
95
+ * @param int $page The page number.
96
+ * @return string Modified url.
97
+ */
98
+ abstract public function add_paged_to_link( $url, $page );
99
+
100
+ /**
101
+ * Returns the language based on language code in url.
102
+ *
103
+ * @since 1.2
104
+ * @since 2.0 add $url argument.
105
+ *
106
+ * @param string $url Optional, defaults to thej current url.
107
+ * @return string Language slug.
108
+ */
109
+ abstract public function get_language_from_url( $url = '' );
110
+
111
+ /**
112
+ * Returns the static front page url.
113
+ *
114
+ * @since 1.8
115
+ *
116
+ * @param PLL_Language $lang The language object.
117
+ * @return string The static front page url.
118
+ */
119
+ abstract public function front_page_url( $lang );
120
+
121
+ /**
122
+ * Changes the language code in url.
123
  *
124
  * @since 1.5
125
  *
126
+ * @param string $url The url to modify.
127
+ * @param PLL_Language $lang The language object.
128
+ * @return string Modified url.
129
  */
130
  public function switch_language_in_link( $url, $lang ) {
131
  $url = $this->remove_language_from_link( $url );
133
  }
134
 
135
  /**
136
+ * Get hosts managed on the website.
137
  *
138
  * @since 1.5
139
  *
140
+ * @return string[] The list of hosts.
141
  */
142
  public function get_hosts() {
143
  return array( wp_parse_url( $this->home, PHP_URL_HOST ) );
144
  }
145
 
146
  /**
147
+ * Returns the home url in a given language.
148
  *
149
  * @since 1.3.1
150
  *
151
+ * @param PLL_Language $lang PLL_Language object.
152
  * @return string
153
  */
154
  public function home_url( $lang ) {
157
  }
158
 
159
  /**
160
+ * Sets the home urls in PLL_Language.
161
  *
162
  * @since 1.8
163
  *
164
+ * @param PLL_Language $language PLL_Language object.
165
+ * @return void
166
  */
167
  protected function set_home_url( $language ) {
168
  // We should always have a default language here, except, temporarily, in PHPUnit tests. The test here protects against PHP notices.
174
  }
175
 
176
  /**
177
+ * Sets the home urls and flags before the languages are persistently cached.
178
  *
179
  * @since 1.8
180
  *
181
+ * @param PLL_Language[] $languages Array of PLL_Language objects.
182
+ * @return PLL_Language[] Array of PLL_Language objects with home url and flag.
183
  */
184
  public function pll_languages_list( $languages ) {
185
  foreach ( $languages as $language ) {
190
  }
191
 
192
  /**
193
+ * Sets the home urls when not cached.
194
+ * Sets the home urls scheme.
195
  *
196
  * @since 1.8
197
  *
198
+ * @param PLL_Language[] $languages Array of PLL_Language objects.
199
+ * @return PLL_Language[] Array of PLL_Language objects.
200
  */
201
  public function pll_after_languages_cache( $languages ) {
202
  foreach ( $languages as $language ) {
203
+ // Get the home urls when not cached.
204
  if ( ( defined( 'PLL_CACHE_LANGUAGES' ) && ! PLL_CACHE_LANGUAGES ) || ( defined( 'PLL_CACHE_HOME_URL' ) && ! PLL_CACHE_HOME_URL ) ) {
205
  $this->set_home_url( $language );
206
  }
212
  }
213
 
214
  /**
215
+ * Adds our domains or subdomains to allowed hosts for safe redirect.
216
  *
217
  * @since 1.4.3
218
  *
219
+ * @param string[] $hosts Allowed hosts.
220
+ * @return string[] Modified list of allowed hosts.
221
  */
222
  public function allowed_redirect_hosts( $hosts ) {
223
  return array_unique( array_merge( $hosts, array_values( $this->get_hosts() ) ) );
include/links-permalinks.php CHANGED
@@ -9,17 +9,49 @@
9
  * @since 1.6
10
  */
11
  abstract class PLL_Links_Permalinks extends PLL_Links_Model {
 
 
 
 
 
12
  public $using_permalinks = true;
13
- protected $index = 'index.php'; // Need this before $wp_rewrite is created, also hardcoded in wp-includes/rewrite.php
14
- protected $root, $use_trailing_slashes;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  protected $always_rewrite = array( 'date', 'root', 'comments', 'search', 'author' );
16
 
17
  /**
18
- * Constructor
19
  *
20
  * @since 1.8
21
  *
22
- * @param object $model PLL_Model instance
23
  */
24
  public function __construct( &$model ) {
25
  parent::__construct( $model );
@@ -73,11 +105,11 @@ abstract class PLL_Links_Permalinks extends PLL_Links_Model {
73
  }
74
 
75
  /**
76
- * Returns the home url
77
  *
78
  * @since 1.3.1
79
  *
80
- * @param object $lang PLL_Language object
81
  * @return string
82
  */
83
  public function home_url( $lang ) {
@@ -85,12 +117,12 @@ abstract class PLL_Links_Permalinks extends PLL_Links_Model {
85
  }
86
 
87
  /**
88
- * Returns the static front page url
89
  *
90
  * @since 1.8
91
  *
92
- * @param object $lang
93
- * @return string
94
  */
95
  public function front_page_url( $lang ) {
96
  if ( $this->options['hide_default'] && $lang->slug == $this->options['default_lang'] ) {
@@ -105,6 +137,8 @@ abstract class PLL_Links_Permalinks extends PLL_Links_Model {
105
  * Prepares rewrite rules filters
106
  *
107
  * @since 1.6
 
 
108
  */
109
  public function get_rewrite_rules_filters() {
110
  // Make sure we have the right post types and taxonomies
9
  * @since 1.6
10
  */
11
  abstract class PLL_Links_Permalinks extends PLL_Links_Model {
12
+ /**
13
+ * Tells this child class of PLL_Links_Model is for pretty permalinks.
14
+ *
15
+ * @var bool
16
+ */
17
  public $using_permalinks = true;
18
+
19
+ /**
20
+ * The name of the index file which is the entry point to all requests.
21
+ * We need this before the global $wp_rewrite is created.
22
+ * Also hardcoded in WP_Rewrite.
23
+ *
24
+ * @var string
25
+ */
26
+ protected $index = 'index.php';
27
+
28
+ /**
29
+ * The prefix for all permalink structures.
30
+ *
31
+ * @var string
32
+ */
33
+ protected $root;
34
+
35
+ /**
36
+ * Whether to add trailing slashes.
37
+ *
38
+ * @var bool
39
+ */
40
+ protected $use_trailing_slashes;
41
+
42
+ /**
43
+ * The name of the rewrite rules to always modify.
44
+ *
45
+ * @var string[]
46
+ */
47
  protected $always_rewrite = array( 'date', 'root', 'comments', 'search', 'author' );
48
 
49
  /**
50
+ * Constructor.
51
  *
52
  * @since 1.8
53
  *
54
+ * @param PLL_Model $model PLL_Model instance.
55
  */
56
  public function __construct( &$model ) {
57
  parent::__construct( $model );
105
  }
106
 
107
  /**
108
+ * Returns the home url.
109
  *
110
  * @since 1.3.1
111
  *
112
+ * @param PLL_Language $lang PLL_Language object.
113
  * @return string
114
  */
115
  public function home_url( $lang ) {
117
  }
118
 
119
  /**
120
+ * Returns the static front page url.
121
  *
122
  * @since 1.8
123
  *
124
+ * @param PLL_Language $lang The language object.
125
+ * @return string The static front page url.
126
  */
127
  public function front_page_url( $lang ) {
128
  if ( $this->options['hide_default'] && $lang->slug == $this->options['default_lang'] ) {
137
  * Prepares rewrite rules filters
138
  *
139
  * @since 1.6
140
+ *
141
+ * @return string[]
142
  */
143
  public function get_rewrite_rules_filters() {
144
  // Make sure we have the right post types and taxonomies
include/links-subdomain.php CHANGED
@@ -11,14 +11,20 @@
11
  * @since 1.2
12
  */
13
  class PLL_Links_Subdomain extends PLL_Links_Abstract_Domain {
 
 
 
 
 
 
14
  protected $www;
15
 
16
  /**
17
- * Constructor
18
  *
19
  * @since 1.7.4
20
  *
21
- * @param object $model PLL_Model instance
22
  */
23
  public function __construct( &$model ) {
24
  parent::__construct( $model );
@@ -26,14 +32,14 @@ class PLL_Links_Subdomain extends PLL_Links_Abstract_Domain {
26
  }
27
 
28
  /**
29
- * Adds the language code in url
30
- * links_model interface
31
  *
32
  * @since 1.2
33
  *
34
- * @param string $url url to modify
35
- * @param object $lang language
36
- * @return string modified url
37
  */
38
  public function add_language_to_link( $url, $lang ) {
39
  if ( ! empty( $lang ) && false === strpos( $url, '://' . $lang->slug . '.' ) ) {
@@ -68,11 +74,11 @@ class PLL_Links_Subdomain extends PLL_Links_Abstract_Domain {
68
  }
69
 
70
  /**
71
- * Get hosts managed on the website
72
  *
73
  * @since 1.5
74
  *
75
- * @return array list of hosts
76
  */
77
  public function get_hosts() {
78
  $hosts = array();
11
  * @since 1.2
12
  */
13
  class PLL_Links_Subdomain extends PLL_Links_Abstract_Domain {
14
+ /**
15
+ * Stores whether the home url includes www. or not.
16
+ * Either '://' or '://www.'.
17
+ *
18
+ * @var string
19
+ */
20
  protected $www;
21
 
22
  /**
23
+ * Constructor.
24
  *
25
  * @since 1.7.4
26
  *
27
+ * @param PLL_Model $model Instance of PLL_Model.
28
  */
29
  public function __construct( &$model ) {
30
  parent::__construct( $model );
32
  }
33
 
34
  /**
35
+ * Adds the language code in a url.
36
+ * links_model interface.
37
  *
38
  * @since 1.2
39
  *
40
+ * @param string $url The url to modify.
41
+ * @param PLL_Language $lang The language object.
42
+ * @return string Modified url.
43
  */
44
  public function add_language_to_link( $url, $lang ) {
45
  if ( ! empty( $lang ) && false === strpos( $url, '://' . $lang->slug . '.' ) ) {
74
  }
75
 
76
  /**
77
+ * Get hosts managed on the website.
78
  *
79
  * @since 1.5
80
  *
81
+ * @return string[] The list of hosts.
82
  */
83
  public function get_hosts() {
84
  $hosts = array();
include/links.php CHANGED
@@ -9,7 +9,24 @@
9
  * @since 1.2
10
  */
11
  class PLL_Links {
12
- public $links_model, $model, $options;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  /**
15
  * Constructor
@@ -25,12 +42,13 @@ class PLL_Links {
25
  }
26
 
27
  /**
28
- * Returns the home url in the requested language
29
  *
30
  * @since 1.3
31
  *
32
- * @param object|string $language
33
- * @param bool $is_search optional whether we need the home url for a search form, defaults to false
 
34
  */
35
  public function get_home_url( $language, $is_search = false ) {
36
  $language = is_object( $language ) ? $language : $this->model->get_language( $language );
9
  * @since 1.2
10
  */
11
  class PLL_Links {
12
+ /**
13
+ * Stores the plugin options.
14
+ *
15
+ * @var array
16
+ */
17
+ public $options;
18
+
19
+ /**
20
+ * @var PLL_Model
21
+ */
22
+ public $model;
23
+
24
+ /**
25
+ * Instance of a child class of PLL_Links_Model.
26
+ *
27
+ * @var PLL_Links_Model
28
+ */
29
+ public $links_model;
30
 
31
  /**
32
  * Constructor
42
  }
43
 
44
  /**
45
+ * Returns the home url in the requested language.
46
  *
47
  * @since 1.3
48
  *
49
+ * @param PLL_Language|string $language The language.
50
+ * @param bool $is_search Optional, whether we need the home url for a search form, defaults to false.
51
+ * @return string
52
  */
53
  public function get_home_url( $language, $is_search = false ) {
54
  $language = is_object( $language ) ? $language : $this->model->get_language( $language );
include/mo.php CHANGED
@@ -26,24 +26,27 @@ class PLL_MO extends MO {
26
  }
27
 
28
  /**
29
- * Writes a PLL_MO object into a custom post meta
30
  *
31
  * @since 1.2
32
  *
33
- * @param object $lang The language in which we want to export strings
 
34
  */
35
  public function export_to_db( $lang ) {
36
  $this->add_entry( $this->make_entry( '', '' ) ); // Empty string translation, just in case
37
 
38
- // Would be convenient to store the whole object but it would take a huge space in DB
39
- // So let's keep only the strings in an array
 
 
 
 
40
  $strings = array();
41
  foreach ( $this->entries as $entry ) {
42
- $strings[] = array( $entry->singular, $this->translate( $entry->singular ) );
43
  }
44
 
45
- $strings = wp_slash( $strings ); // Avoid breaking slashed strings in update_post_meta. See https://codex.wordpress.org/Function_Reference/update_post_meta#Character_Escaping
46
-
47
  if ( empty( $lang->mo_id ) ) {
48
  $post = array(
49
  'post_title' => 'polylang_mo_' . $lang->term_id,
@@ -58,11 +61,12 @@ class PLL_MO extends MO {
58
  }
59
 
60
  /**
61
- * Reads a PLL_MO object from a custom post meta
62
  *
63
  * @since 1.2
64
  *
65
- * @param object $lang The language in which we want to get strings
 
66
  */
67
  public function import_from_db( $lang ) {
68
  if ( ! empty( $lang->mo_id ) ) {
@@ -76,11 +80,11 @@ class PLL_MO extends MO {
76
  }
77
 
78
  /**
79
- * Returns the post id of the post storing the strings translations
80
  *
81
  * @since 1.4
82
  *
83
- * @param object $lang
84
  * @return int
85
  */
86
  public static function get_id( $lang ) {
@@ -101,6 +105,8 @@ class PLL_MO extends MO {
101
  * Invalidate the cache when adding a new language
102
  *
103
  * @since 2.0.5
 
 
104
  */
105
  public function clean_cache() {
106
  wp_cache_delete( 'polylang_mo_ids' );
@@ -112,6 +118,7 @@ class PLL_MO extends MO {
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 ] );
26
  }
27
 
28
  /**
29
+ * Writes a PLL_MO object into a custom post meta.
30
  *
31
  * @since 1.2
32
  *
33
+ * @param PLL_Language $lang The language in which we want to export strings.
34
+ * @return void
35
  */
36
  public function export_to_db( $lang ) {
37
  $this->add_entry( $this->make_entry( '', '' ) ); // Empty string translation, just in case
38
 
39
+ /*
40
+ * It would be convenient to store the whole object but it would take a huge space in DB.
41
+ * So let's keep only the strings in an array.
42
+ * The strings are slashed to avoid breaking slashed strings in update_post_meta.
43
+ * @see https://codex.wordpress.org/Function_Reference/update_post_meta#Character_Escaping.
44
+ */
45
  $strings = array();
46
  foreach ( $this->entries as $entry ) {
47
+ $strings[] = wp_slash( array( $entry->singular, $this->translate( $entry->singular ) ) );
48
  }
49
 
 
 
50
  if ( empty( $lang->mo_id ) ) {
51
  $post = array(
52
  'post_title' => 'polylang_mo_' . $lang->term_id,
61
  }
62
 
63
  /**
64
+ * Reads a PLL_MO object from a custom post meta.
65
  *
66
  * @since 1.2
67
  *
68
+ * @param PLL_Language $lang The language in which we want to get strings.
69
+ * @return void
70
  */
71
  public function import_from_db( $lang ) {
72
  if ( ! empty( $lang->mo_id ) ) {
80
  }
81
 
82
  /**
83
+ * Returns the post id of the post storing the strings translations.
84
  *
85
  * @since 1.4
86
  *
87
+ * @param PLL_Language $lang The language object.
88
  * @return int
89
  */
90
  public static function get_id( $lang ) {
105
  * Invalidate the cache when adding a new language
106
  *
107
  * @since 2.0.5
108
+ *
109
+ * @return void
110
  */
111
  public function clean_cache() {
112
  wp_cache_delete( 'polylang_mo_ids' );
118
  * @since 2.9
119
  *
120
  * @param string $string The source string to remove from the translations.
121
+ * @return void
122
  */
123
  public function delete_entry( $string ) {
124
  unset( $this->entries[ $string ] );
include/model.php CHANGED
@@ -9,27 +9,51 @@
9
  * @since 1.2
10
  */
11
  class PLL_Model {
12
- public $cache; // Our internal non persistent cache object
 
 
 
 
 
 
 
 
 
 
 
13
  public $options;
14
- public $post, $term; // Translated objects models
15
 
16
  /**
17
- * Constructor
18
- * setups translated objects sub models
19
- * setups filters and actions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  *
21
  * @since 1.2
22
  *
23
- * @param array $options Polylang options
24
  */
25
  public function __construct( &$options ) {
26
  $this->options = &$options;
27
 
28
  $this->cache = new PLL_Cache();
29
- $this->post = new PLL_Translated_Post( $this ); // translated post sub model
30
- $this->term = new PLL_Translated_Term( $this ); // translated term sub model
31
 
32
- // We need to clean languages cache when editing a language and when modifying the permalink structure
33
  add_action( 'edited_term_taxonomy', array( $this, 'clean_languages_cache' ), 10, 2 );
34
  add_action( 'update_option_permalink_structure', array( $this, 'clean_languages_cache' ) );
35
  add_action( 'update_option_siteurl', array( $this, 'clean_languages_cache' ) );
@@ -37,29 +61,27 @@ class PLL_Model {
37
 
38
  add_filter( 'get_terms_args', array( $this, 'get_terms_args' ) );
39
 
40
- // Just in case someone would like to display the language description ;- )
41
  add_filter( 'language_description', '__return_empty_string' );
42
  }
43
 
44
  /**
45
- * Returns the list of available languages
46
- * caches the list in a db transient ( except flags ), unless PLL_CACHE_LANGUAGES is set to false
47
- * caches the list ( with flags ) in the private property $languages
48
- *
49
- * List of parameters accepted in $args:
50
- *
51
- * hide_empty => hides languages with no posts if set to true ( defaults to false )
52
- * fields => return only that field if set ( see PLL_Language for a list of fields )
53
  *
54
  * @since 0.1
55
  *
56
- * @param array $args
57
- * @return array|string|int list of PLL_Language objects or PLL_Language object properties
 
 
 
58
  */
59
  public function get_languages_list( $args = array() ) {
60
  if ( false === $languages = $this->cache->get( 'languages' ) ) {
61
 
62
- // Create the languages from taxonomies
63
  if ( ( defined( 'PLL_CACHE_LANGUAGES' ) && ! PLL_CACHE_LANGUAGES ) || false === ( $languages = get_transient( 'pll_languages_list' ) ) ) {
64
  $languages = get_terms( 'language', array( 'hide_empty' => false, 'orderby' => 'term_group' ) );
65
  $languages = empty( $languages ) || is_wp_error( $languages ) ? array() : $languages;
@@ -69,36 +91,38 @@ class PLL_Model {
69
  array() : array_combine( wp_list_pluck( $term_languages, 'slug' ), $term_languages );
70
 
71
  if ( ! empty( $languages ) && ! empty( $term_languages ) ) {
72
- // Don't use array_map + create_function to instantiate an autoloaded class as it breaks badly in old versions of PHP
73
  foreach ( $languages as $k => $v ) {
74
  $languages[ $k ] = new PLL_Language( $v, $term_languages[ 'pll_' . $v->slug ] );
75
  }
76
 
77
- // We will need the languages list to allow its access in the filter below
78
  $this->cache->set( 'languages', $languages );
79
 
80
  /**
81
- * Filter the list of languages *before* it is stored in the persistent cache
82
- * /!\ this filter is fired *before* the $polylang object is available
83
  *
84
  * @since 1.7.5
85
  *
86
- * @param array $languages the list of language objects
87
- * @param object $model PLL_Model object
88
  */
89
  $languages = apply_filters( 'pll_languages_list', $languages, $this );
90
 
91
- // Don't store directly objects as it badly break with some hosts ( GoDaddy ) due to race conditions when using object cache
92
- // Thanks to captin411 for catching this!
93
- // See https://wordpress.org/support/topic/fatal-error-pll_model_languages_list?replies=8#post-6782255;
 
 
94
  set_transient( 'pll_languages_list', array_map( 'get_object_vars', $languages ) );
95
  }
96
  else {
97
- $languages = array(); // In case something went wrong
98
  }
99
  }
100
 
101
- // Create the languages directly from arrays stored in transients
102
  else {
103
  foreach ( $languages as $k => $v ) {
104
  $languages[ $k ] = new PLL_Language( $v );
@@ -106,12 +130,12 @@ class PLL_Model {
106
  }
107
 
108
  /**
109
- * Filter the list of languages *after* it is stored in the persistent cache
110
- * /!\ this filter is fired *before* the $polylang object is available
111
  *
112
  * @since 1.8
113
  *
114
- * @param array $languages the list of language objects
115
  */
116
  $languages = apply_filters( 'pll_after_languages_cache', $languages );
117
  $this->cache->set( 'languages', $languages );
@@ -119,7 +143,7 @@ class PLL_Model {
119
 
120
  $args = wp_parse_args( $args, array( 'hide_empty' => false ) );
121
 
122
- // Remove empty languages if requested
123
  if ( $args['hide_empty'] ) {
124
  $languages = wp_list_filter( $languages, array( 'count' => 0 ), 'NOT' );
125
  }
@@ -136,6 +160,7 @@ class PLL_Model {
136
  *
137
  * @param int $term not used
138
  * @param string $taxonomy taxonomy name
 
139
  */
140
  public function clean_languages_cache( $term = 0, $taxonomy = null ) {
141
  if ( empty( $taxonomy ) || 'language' == $taxonomy ) {
@@ -160,12 +185,12 @@ class PLL_Model {
160
  }
161
 
162
  /**
163
- * Returns the language by its term_id, tl_term_id, slug or locale
164
  *
165
  * @since 0.1
166
  *
167
- * @param int|string $value term_id, tl_term_id, slug or locale of the queried language
168
- * @return object|bool PLL_Language object, false if no language found
169
  */
170
  public function get_language( $value ) {
171
  if ( is_object( $value ) ) {
@@ -187,13 +212,13 @@ class PLL_Model {
187
  }
188
 
189
  /**
190
- * Adds terms clauses to get_terms to filter them by languages - used in both frontend and admin
191
  *
192
  * @since 1.2
193
  *
194
- * @param array $clauses the list of sql clauses in terms query
195
- * @param object $lang PLL_Language object
196
- * @return array modified list of clauses
197
  */
198
  public function terms_clauses( $clauses, $lang ) {
199
  if ( ! empty( $lang ) && false === strpos( $clauses['join'], 'pll_tr' ) ) {
@@ -204,14 +229,15 @@ class PLL_Model {
204
  }
205
 
206
  /**
207
- * Returns post types that need to be translated
208
- * the post types list is cached for better better performance
209
- * wait for 'after_setup_theme' to apply the cache to allow themes adding the filter in functions.php
 
210
  *
211
  * @since 1.2
212
  *
213
- * @param bool $filter true if we should return only valid registered post types
214
- * @return array post type names for which Polylang manages languages and translations
215
  */
216
  public function get_translated_post_types( $filter = true ) {
217
  if ( false === $post_types = $this->cache->get( 'post_types' ) ) {
@@ -226,15 +252,15 @@ class PLL_Model {
226
  }
227
 
228
  /**
229
- * Filter the list of post types available for translation.
230
  * The default are post types which have the parameter ‘public’ set to true.
231
  * The filter must be added soon in the WordPress loading process:
232
  * in a function hooked to ‘plugins_loaded’ or directly in functions.php for themes.
233
  *
234
  * @since 0.8
235
  *
236
- * @param array $post_types list of post type names
237
- * @param bool $is_settings true when displaying the list of custom post types in Polylang settings
238
  */
239
  $post_types = apply_filters( 'pll_get_post_types', $post_types, false );
240
 
@@ -247,11 +273,11 @@ class PLL_Model {
247
  }
248
 
249
  /**
250
- * Returns true if Polylang manages languages and translations for this post type
251
  *
252
  * @since 1.2
253
  *
254
- * @param string|array $post_type post type name or array of post type names
255
  * @return bool
256
  */
257
  public function is_translated_post_type( $post_type ) {
@@ -260,12 +286,12 @@ class PLL_Model {
260
  }
261
 
262
  /**
263
- * Return taxonomies that need to be translated
264
  *
265
  * @since 1.2
266
  *
267
- * @param bool $filter true if we should return only valid registered taxonomies
268
- * @return array array of registered taxonomy names for which Polylang manages languages and translations
269
  */
270
  public function get_translated_taxonomies( $filter = true ) {
271
  if ( false === $taxonomies = $this->cache->get( 'taxonomies' ) ) {
@@ -276,15 +302,15 @@ class PLL_Model {
276
  }
277
 
278
  /**
279
- * Filter the list of taxonomies available for translation.
280
  * The default are taxonomies which have the parameter ‘public’ set to true.
281
  * The filter must be added soon in the WordPress loading process:
282
  * in a function hooked to ‘plugins_loaded’ or directly in functions.php for themes.
283
  *
284
  * @since 0.8
285
  *
286
- * @param array $taxonomies list of taxonomy names
287
- * @param bool $is_settings true when displaying the list of custom taxonomies in Polylang settings
288
  */
289
  $taxonomies = apply_filters( 'pll_get_taxonomies', $taxonomies, false );
290
  if ( did_action( 'after_setup_theme' ) ) {
@@ -296,11 +322,11 @@ class PLL_Model {
296
  }
297
 
298
  /**
299
- * Returns true if Polylang manages languages and translations for this taxonomy
300
  *
301
  * @since 1.2
302
  *
303
- * @param string|array $tax taxonomy name or array of taxonomy names
304
  * @return bool
305
  */
306
  public function is_translated_taxonomy( $tax ) {
@@ -309,12 +335,12 @@ class PLL_Model {
309
  }
310
 
311
  /**
312
- * Return taxonomies that need to be filtered ( post_format like )
313
  *
314
  * @since 1.7
315
  *
316
- * @param bool $filter true if we should return only valid registered taxonomies
317
- * @return array array of registered taxonomy names
318
  */
319
  public function get_filtered_taxonomies( $filter = true ) {
320
  if ( did_action( 'after_setup_theme' ) ) {
@@ -325,15 +351,15 @@ class PLL_Model {
325
  $taxonomies = array( 'post_format' => 'post_format' );
326
 
327
  /**
328
- * Filter the list of taxonomies not translatable but filtered by language.
329
  * Includes only the post format by default
330
  * The filter must be added soon in the WordPress loading process:
331
  * in a function hooked to ‘plugins_loaded’ or directly in functions.php for themes.
332
  *
333
  * @since 1.7
334
  *
335
- * @param array $taxonomies list of taxonomy names
336
- * @param bool $is_settings true when displaying the list of custom taxonomies in Polylang settings
337
  */
338
  $taxonomies = apply_filters( 'pll_filtered_taxonomies', $taxonomies, false );
339
  }
@@ -342,11 +368,11 @@ class PLL_Model {
342
  }
343
 
344
  /**
345
- * Returns true if Polylang filters this taxonomy per language
346
  *
347
  * @since 1.7
348
  *
349
- * @param string|array $tax taxonomy name or array of taxonomy names
350
  * @return bool
351
  */
352
  public function is_filtered_taxonomy( $tax ) {
@@ -355,7 +381,7 @@ class PLL_Model {
355
  }
356
 
357
  /**
358
- * Returns the query vars of all filtered taxonomies
359
  *
360
  * @since 1.7
361
  *
@@ -365,7 +391,9 @@ class PLL_Model {
365
  $query_vars = array();
366
  foreach ( $this->get_filtered_taxonomies() as $filtered_tax ) {
367
  $tax = get_taxonomy( $filtered_tax );
368
- $query_vars[] = $tax->query_var;
 
 
369
  }
370
  return $query_vars;
371
  }
@@ -376,6 +404,7 @@ class PLL_Model {
376
  * @since 1.2
377
  *
378
  * @param object|string|int $lang language
 
379
  */
380
  public function create_default_category( $lang ) {
381
  $lang = $this->get_language( $lang );
@@ -409,16 +438,16 @@ class PLL_Model {
409
 
410
  /**
411
  * It is possible to have several terms with the same name in the same taxonomy ( one per language )
412
- * but the native term_exists will return true even if only one exists
413
- * so here the function adds the language parameter
414
  *
415
  * @since 1.4
416
  *
417
- * @param string $term_name the term name
418
- * @param string $taxonomy taxonomy name
419
- * @param int $parent parent term id
420
- * @param string|object $language the language slug or object
421
- * @return null|int the term_id of the found term
422
  */
423
  public function term_exists( $term_name, $taxonomy, $parent, $language ) {
424
  global $wpdb;
@@ -441,15 +470,15 @@ class PLL_Model {
441
  }
442
 
443
  /**
444
- * Checks if a term slug exists in a given language, taxonomy, hierarchy
445
  *
446
  * @since 1.9
447
- * @since 2.8 Moved from PLL_Share_Term_Slug::term_exists() to PLL_Model::term_exists_by_slug()
448
  *
449
- * @param string $slug The term slug to test.
450
- * @param string|object $language The language slug or object.
451
- * @param string $taxonomy Optional taxonomy name.
452
- * @param int $parent Optional parent term id.
453
  * @return null|int The term_id of the found term.
454
  */
455
  public function term_exists_by_slug( $slug, $language, $taxonomy = '', $parent = 0 ) {
@@ -479,8 +508,20 @@ class PLL_Model {
479
  *
480
  * @since 1.2
481
  *
482
- * @param object $lang PLL_Language instance.
483
- * @param array $q WP_Query arguments ( accepted: post_type, m, year, monthnum, day, author, author_name, post_format, post_status ).
 
 
 
 
 
 
 
 
 
 
 
 
484
  * @return int
485
  */
486
  public function count_posts( $lang, $q = array() ) {
@@ -509,7 +550,7 @@ class PLL_Model {
509
  $select = "SELECT pll_tr.term_taxonomy_id, COUNT( * ) AS num_posts FROM {$wpdb->posts}";
510
  $join = $this->post->join_clause();
511
  $where = sprintf( " WHERE post_status = '%s'", esc_sql( $q['post_status'] ) );
512
- $where .= sprintf( " AND {$wpdb->posts}.post_type IN ( '%s' )", join( "', '", esc_sql( $q['post_type'] ) ) );
513
  $where .= $this->post->where_clause( $this->get_languages_list() );
514
  $groupby = ' GROUP BY pll_tr.term_taxonomy_id';
515
 
@@ -571,103 +612,26 @@ class PLL_Model {
571
  }
572
 
573
  /**
574
- * Setup the links model based on options
575
  *
576
  * @since 1.2
577
  *
578
- * @return object implementing "links_model interface"
579
  */
580
  public function get_links_model() {
581
  $c = array( 'Directory', 'Directory', 'Subdomain', 'Domain' );
582
  $class = get_option( 'permalink_structure' ) ? 'PLL_Links_' . $c[ $this->options['force_lang'] ] : 'PLL_Links_Default';
583
 
584
  /**
585
- * Filter the links model class to use
586
- * /!\ this filter is fired *before* the $polylang object is available
587
  *
588
  * @since 2.1.1
589
  *
590
- * @param string $class A class name: PLL_Links_Default, PLL_Links_Directory, PLL_Links_Subdomain, PLL_Links_Domain
591
  */
592
  $class = apply_filters( 'pll_links_model', $class );
593
 
594
  return new $class( $this );
595
  }
596
-
597
- /**
598
- * Some backward compatibility with Polylang < 1.8
599
- * allows for example to call $polylang->model->get_post_languages( $post_id ) instead of $polylang->model->post->get_language( $post_id )
600
- * this works but should be slower than the direct call, thus an error is triggered in debug mode
601
- *
602
- * @since 1.8
603
- *
604
- * @param string $func Function name
605
- * @param array $args Function arguments
606
- */
607
- public function __call( $func, $args ) {
608
- $f = $func;
609
-
610
- switch ( $func ) {
611
- case 'get_object_term':
612
- $o = ( false === strpos( $args[1], 'term' ) ) ? 'post' : 'term';
613
- break;
614
-
615
- case 'save_translations':
616
- case 'delete_translation':
617
- case 'get_translations':
618
- case 'get_translation':
619
- case 'join_clause':
620
- $o = ( 'post' == $args[0] || $this->is_translated_post_type( $args[0] ) ) ? 'post' : ( 'term' == $args[0] || $this->is_translated_taxonomy( $args[0] ) ? 'term' : false );
621
- unset( $args[0] );
622
- break;
623
-
624
- case 'set_post_language':
625
- case 'get_post_language':
626
- case 'set_term_language':
627
- case 'get_term_language':
628
- case 'delete_term_language':
629
- case 'get_post':
630
- case 'get_term':
631
- $str = explode( '_', $func );
632
- $f = empty( $str[2] ) ? $str[0] : $str[0] . '_' . $str[2];
633
- $o = $str[1];
634
- break;
635
-
636
- case 'where_clause':
637
- case 'get_objects_in_language':
638
- $o = $args[1];
639
- unset( $args[1] );
640
- break;
641
- }
642
-
643
- if ( ! empty( $o ) && is_object( $this->$o ) && method_exists( $this->$o, $f ) ) {
644
- if ( WP_DEBUG ) {
645
- $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
646
- $i = 1 + empty( $debug[1]['line'] ); // the file and line are in $debug[2] if the function was called using call_user_func
647
-
648
- trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
649
- sprintf(
650
- '%1$s was called incorrectly in %4$s on line %5$s: the call to $polylang->model->%1$s() has been deprecated in Polylang 1.8, use PLL()->model->%2$s->%3$s() instead.' . "\nError handler",
651
- esc_html( $func ),
652
- esc_html( $o ),
653
- esc_html( $f ),
654
- esc_html( $debug[ $i ]['file'] ),
655
- absint( $debug[ $i ]['line'] )
656
- )
657
- );
658
- }
659
- return call_user_func_array( array( $this->$o, $f ), $args );
660
- }
661
-
662
- $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
663
- trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
664
- sprintf(
665
- 'Call to undefined function PLL()->model->%1$s() in %2$s on line %3$s' . "\nError handler",
666
- esc_html( $func ),
667
- esc_html( $debug[0]['file'] ),
668
- absint( $debug[0]['line'] )
669
- ),
670
- E_USER_ERROR
671
- );
672
- }
673
  }
9
  * @since 1.2
10
  */
11
  class PLL_Model {
12
+ /**
13
+ * Internal non persistent cache object.
14
+ *
15
+ * @var PLL_Cache
16
+ */
17
+ public $cache;
18
+
19
+ /**
20
+ * Stores the plugin options.
21
+ *
22
+ * @var array
23
+ */
24
  public $options;
 
25
 
26
  /**
27
+ * Translated post model.
28
+ *
29
+ * @var PLL_Translated_Post
30
+ */
31
+ public $post;
32
+
33
+ /**
34
+ * Translated term model.
35
+ *
36
+ * @var PLL_Translated_Term
37
+ */
38
+ public $term;
39
+
40
+ /**
41
+ * Constructor.
42
+ * Setups translated objects sub models.
43
+ * Setups filters and actions.
44
  *
45
  * @since 1.2
46
  *
47
+ * @param array $options Polylang options.
48
  */
49
  public function __construct( &$options ) {
50
  $this->options = &$options;
51
 
52
  $this->cache = new PLL_Cache();
53
+ $this->post = new PLL_Translated_Post( $this ); // Translated post sub model.
54
+ $this->term = new PLL_Translated_Term( $this ); // Translated term sub model.
55
 
56
+ // We need to clean languages cache when editing a language and when modifying the permalink structure.
57
  add_action( 'edited_term_taxonomy', array( $this, 'clean_languages_cache' ), 10, 2 );
58
  add_action( 'update_option_permalink_structure', array( $this, 'clean_languages_cache' ) );
59
  add_action( 'update_option_siteurl', array( $this, 'clean_languages_cache' ) );
61
 
62
  add_filter( 'get_terms_args', array( $this, 'get_terms_args' ) );
63
 
64
+ // Just in case someone would like to display the language description ;).
65
  add_filter( 'language_description', '__return_empty_string' );
66
  }
67
 
68
  /**
69
+ * Returns the list of available languages.
70
+ * - Stores the list in a db transient ( except flags ), unless PLL_CACHE_LANGUAGES is set to false.
71
+ * - Caches the list ( with flags ) in a PLL_Cache object.
 
 
 
 
 
72
  *
73
  * @since 0.1
74
  *
75
+ * @param array $args {
76
+ * @type bool $hide_empty Hides languages with no posts if set to true ( defaults to false ).
77
+ * @type string $fields Returns only that field if set; {@see PLL_Language} for a list of fields.
78
+ * }
79
+ * @return array List of PLL_Language objects or PLL_Language object properties.
80
  */
81
  public function get_languages_list( $args = array() ) {
82
  if ( false === $languages = $this->cache->get( 'languages' ) ) {
83
 
84
+ // Create the languages from taxonomies.
85
  if ( ( defined( 'PLL_CACHE_LANGUAGES' ) && ! PLL_CACHE_LANGUAGES ) || false === ( $languages = get_transient( 'pll_languages_list' ) ) ) {
86
  $languages = get_terms( 'language', array( 'hide_empty' => false, 'orderby' => 'term_group' ) );
87
  $languages = empty( $languages ) || is_wp_error( $languages ) ? array() : $languages;
91
  array() : array_combine( wp_list_pluck( $term_languages, 'slug' ), $term_languages );
92
 
93
  if ( ! empty( $languages ) && ! empty( $term_languages ) ) {
94
+ // Don't use array_map + create_function() to instantiate an autoloaded class as it breaks badly in old versions of PHP.
95
  foreach ( $languages as $k => $v ) {
96
  $languages[ $k ] = new PLL_Language( $v, $term_languages[ 'pll_' . $v->slug ] );
97
  }
98
 
99
+ // We will need the languages list to allow its access in the filter below.
100
  $this->cache->set( 'languages', $languages );
101
 
102
  /**
103
+ * Filters the list of languages *before* it is stored in the persistent cache.
104
+ * /!\ This filter is fired *before* the $polylang object is available.
105
  *
106
  * @since 1.7.5
107
  *
108
+ * @param PLL_Language[] $languages The list of language objects.
109
+ * @param PLL_Model $model PLL_Model object.
110
  */
111
  $languages = apply_filters( 'pll_languages_list', $languages, $this );
112
 
113
+ /*
114
+ * Don't store directly objects as it badly break with some hosts ( GoDaddy ) due to race conditions when using object cache.
115
+ * Thanks to captin411 for catching this!
116
+ * @see https://wordpress.org/support/topic/fatal-error-pll_model_languages_list?replies=8#post-6782255
117
+ */
118
  set_transient( 'pll_languages_list', array_map( 'get_object_vars', $languages ) );
119
  }
120
  else {
121
+ $languages = array(); // In case something went wrong.
122
  }
123
  }
124
 
125
+ // Create the languages directly from arrays stored in transients.
126
  else {
127
  foreach ( $languages as $k => $v ) {
128
  $languages[ $k ] = new PLL_Language( $v );
130
  }
131
 
132
  /**
133
+ * Filters the list of languages *after* it is stored in the persistent cache.
134
+ * /!\ This filter is fired *before* the $polylang object is available.
135
  *
136
  * @since 1.8
137
  *
138
+ * @param PLL_Language[] $languages The list of language objects.
139
  */
140
  $languages = apply_filters( 'pll_after_languages_cache', $languages );
141
  $this->cache->set( 'languages', $languages );
143
 
144
  $args = wp_parse_args( $args, array( 'hide_empty' => false ) );
145
 
146
+ // Remove empty languages if requested.
147
  if ( $args['hide_empty'] ) {
148
  $languages = wp_list_filter( $languages, array( 'count' => 0 ), 'NOT' );
149
  }
160
  *
161
  * @param int $term not used
162
  * @param string $taxonomy taxonomy name
163
+ * @return void
164
  */
165
  public function clean_languages_cache( $term = 0, $taxonomy = null ) {
166
  if ( empty( $taxonomy ) || 'language' == $taxonomy ) {
185
  }
186
 
187
  /**
188
+ * Returns the language by its term_id, tl_term_id, slug or locale.
189
  *
190
  * @since 0.1
191
  *
192
+ * @param mixed $value term_id, tl_term_id, slug or locale of the queried language.
193
+ * @return PLL_Language|false Language object, false if no language found.
194
  */
195
  public function get_language( $value ) {
196
  if ( is_object( $value ) ) {
212
  }
213
 
214
  /**
215
+ * Adds terms clauses to the term query to filter them by languages.
216
  *
217
  * @since 1.2
218
  *
219
+ * @param string[] $clauses The list of sql clauses in terms query.
220
+ * @param PLL_Language $lang PLL_Language object.
221
+ * @return string[] Modified list of clauses.
222
  */
223
  public function terms_clauses( $clauses, $lang ) {
224
  if ( ! empty( $lang ) && false === strpos( $clauses['join'], 'pll_tr' ) ) {
229
  }
230
 
231
  /**
232
+ * Returns post types that need to be translated.
233
+ * The post types list is cached for better better performance.
234
+ * The method waits for 'after_setup_theme' to apply the cache
235
+ * to allow themes adding the filter in functions.php.
236
  *
237
  * @since 1.2
238
  *
239
+ * @param bool $filter True if we should return only valid registered post types.
240
+ * @return string[] Post type names for which Polylang manages languages and translations.
241
  */
242
  public function get_translated_post_types( $filter = true ) {
243
  if ( false === $post_types = $this->cache->get( 'post_types' ) ) {
252
  }
253
 
254
  /**
255
+ * Filters the list of post types available for translation.
256
  * The default are post types which have the parameter ‘public’ set to true.
257
  * The filter must be added soon in the WordPress loading process:
258
  * in a function hooked to ‘plugins_loaded’ or directly in functions.php for themes.
259
  *
260
  * @since 0.8
261
  *
262
+ * @param string[] $post_types List of post type names.
263
+ * @param bool $is_settings True when displaying the list of custom post types in Polylang settings.
264
  */
265
  $post_types = apply_filters( 'pll_get_post_types', $post_types, false );
266
 
273
  }
274
 
275
  /**
276
+ * Returns true if Polylang manages languages and translations for this post type.
277
  *
278
  * @since 1.2
279
  *
280
+ * @param string|string[] $post_type Post type name or array of post type names.
281
  * @return bool
282
  */
283
  public function is_translated_post_type( $post_type ) {
286
  }
287
 
288
  /**
289
+ * Returns taxonomies that need to be translated.
290
  *
291
  * @since 1.2
292
  *
293
+ * @param bool $filter True if we should return only valid registered taxonomies.
294
+ * @return string[] Array of registered taxonomy names for which Polylang manages languages and translations.
295
  */
296
  public function get_translated_taxonomies( $filter = true ) {
297
  if ( false === $taxonomies = $this->cache->get( 'taxonomies' ) ) {
302
  }
303
 
304
  /**
305
+ * Filters the list of taxonomies available for translation.
306
  * The default are taxonomies which have the parameter ‘public’ set to true.
307
  * The filter must be added soon in the WordPress loading process:
308
  * in a function hooked to ‘plugins_loaded’ or directly in functions.php for themes.
309
  *
310
  * @since 0.8
311
  *
312
+ * @param string[] $taxonomies List of taxonomy names.
313
+ * @param bool $is_settings True when displaying the list of custom taxonomies in Polylang settings.
314
  */
315
  $taxonomies = apply_filters( 'pll_get_taxonomies', $taxonomies, false );
316
  if ( did_action( 'after_setup_theme' ) ) {
322
  }
323
 
324
  /**
325
+ * Returns true if Polylang manages languages and translations for this taxonomy.
326
  *
327
  * @since 1.2
328
  *
329
+ * @param string|string[] $tax Taxonomy name or array of taxonomy names.
330
  * @return bool
331
  */
332
  public function is_translated_taxonomy( $tax ) {
335
  }
336
 
337
  /**
338
+ * Return staxonomies that need to be filtered ( post_format like ).
339
  *
340
  * @since 1.7
341
  *
342
+ * @param bool $filter True if we should return only valid registered taxonomies.
343
+ * @return string[] Array of registered taxonomy names.
344
  */
345
  public function get_filtered_taxonomies( $filter = true ) {
346
  if ( did_action( 'after_setup_theme' ) ) {
351
  $taxonomies = array( 'post_format' => 'post_format' );
352
 
353
  /**
354
+ * Filters the list of taxonomies not translatable but filtered by language.
355
  * Includes only the post format by default
356
  * The filter must be added soon in the WordPress loading process:
357
  * in a function hooked to ‘plugins_loaded’ or directly in functions.php for themes.
358
  *
359
  * @since 1.7
360
  *
361
+ * @param string[] $taxonomies List of taxonomy names.
362
+ * @param bool $is_settings True when displaying the list of custom taxonomies in Polylang settings.
363
  */
364
  $taxonomies = apply_filters( 'pll_filtered_taxonomies', $taxonomies, false );
365
  }
368
  }
369
 
370
  /**
371
+ * Returns true if Polylang filters this taxonomy per language.
372
  *
373
  * @since 1.7
374
  *
375
+ * @param string|string[] $tax Taxonomy name or array of taxonomy names.
376
  * @return bool
377
  */
378
  public function is_filtered_taxonomy( $tax ) {
381
  }
382
 
383
  /**
384
+ * Returns the query vars of all filtered taxonomies.
385
  *
386
  * @since 1.7
387
  *
391
  $query_vars = array();
392
  foreach ( $this->get_filtered_taxonomies() as $filtered_tax ) {
393
  $tax = get_taxonomy( $filtered_tax );
394
+ if ( ! empty( $tax ) ) {
395
+ $query_vars[] = $tax->query_var;
396
+ }
397
  }
398
  return $query_vars;
399
  }
404
  * @since 1.2
405
  *
406
  * @param object|string|int $lang language
407
+ * @return void
408
  */
409
  public function create_default_category( $lang ) {
410
  $lang = $this->get_language( $lang );
438
 
439
  /**
440
  * It is possible to have several terms with the same name in the same taxonomy ( one per language )
441
+ * but the native term_exists() will return true even if only one exists.
442
+ * So here the function adds the language parameter.
443
  *
444
  * @since 1.4
445
  *
446
+ * @param string $term_name The term name.
447
+ * @param string $taxonomy Taxonomy name.
448
+ * @param int $parent Parent term id.
449
+ * @param string|PLL_Language $language The language slug or object.
450
+ * @return null|int The term_id of the found term.
451
  */
452
  public function term_exists( $term_name, $taxonomy, $parent, $language ) {
453
  global $wpdb;
470
  }
471
 
472
  /**
473
+ * Checks if a term slug exists in a given language, taxonomy, hierarchy.
474
  *
475
  * @since 1.9
476
+ * @since 2.8 Moved from PLL_Share_Term_Slug::term_exists() to PLL_Model::term_exists_by_slug().
477
  *
478
+ * @param string $slug The term slug to test.
479
+ * @param string|PLL_Language $language The language slug or object.
480
+ * @param string $taxonomy Optional taxonomy name.
481
+ * @param int $parent Optional parent term id.
482
  * @return null|int The term_id of the found term.
483
  */
484
  public function term_exists_by_slug( $slug, $language, $taxonomy = '', $parent = 0 ) {
508
  *
509
  * @since 1.2
510
  *
511
+ * @param PLL_Language $lang PLL_Language instance.
512
+ * @param array $q {
513
+ * WP_Query arguments:
514
+ *
515
+ * @type string|string[] $post_type Post type or array of post types.
516
+ * @type int $m Combination YearMonth. Accepts any four-digit year and month.
517
+ * @type int $year Four-digit year.
518
+ * @type int $monthnum Two-digit month.
519
+ * @type int $day Day of the month.
520
+ * @type int $author Author id.
521
+ * @type string $author_name User 'user_nicename'.
522
+ * @type string $post_format Post format.
523
+ * @type string $post_status Post status.
524
+ * }
525
  * @return int
526
  */
527
  public function count_posts( $lang, $q = array() ) {
550
  $select = "SELECT pll_tr.term_taxonomy_id, COUNT( * ) AS num_posts FROM {$wpdb->posts}";
551
  $join = $this->post->join_clause();
552
  $where = sprintf( " WHERE post_status = '%s'", esc_sql( $q['post_status'] ) );
553
+ $where .= sprintf( " AND {$wpdb->posts}.post_type IN ( '%s' )", implode( "', '", esc_sql( $q['post_type'] ) ) );
554
  $where .= $this->post->where_clause( $this->get_languages_list() );
555
  $groupby = ' GROUP BY pll_tr.term_taxonomy_id';
556
 
612
  }
613
 
614
  /**
615
+ * Setup the links model based on options.
616
  *
617
  * @since 1.2
618
  *
619
+ * @return PLL_Links_Model
620
  */
621
  public function get_links_model() {
622
  $c = array( 'Directory', 'Directory', 'Subdomain', 'Domain' );
623
  $class = get_option( 'permalink_structure' ) ? 'PLL_Links_' . $c[ $this->options['force_lang'] ] : 'PLL_Links_Default';
624
 
625
  /**
626
+ * Filters the links model class to use.
627
+ * /!\ this filter is fired *before* the $polylang object is available.
628
  *
629
  * @since 2.1.1
630
  *
631
+ * @param string $class A class name: PLL_Links_Default, PLL_Links_Directory, PLL_Links_Subdomain, PLL_Links_Domain.
632
  */
633
  $class = apply_filters( 'pll_links_model', $class );
634
 
635
  return new $class( $this );
636
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637
  }
include/nav-menu.php CHANGED
@@ -10,7 +10,31 @@
10
  * @since 1.7.7
11
  */
12
  class PLL_Nav_Menu {
13
- public $model, $options;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  /**
16
  * Constructor: setups filters and actions
@@ -39,8 +63,8 @@ class PLL_Nav_Menu {
39
  *
40
  * @since 2.6
41
  *
42
- * @param object $item Menu item.
43
- * @return object
44
  */
45
  public function wp_setup_nav_menu_item( $item ) {
46
  if ( '#pll_switcher' === $item->url ) {
@@ -55,6 +79,8 @@ class PLL_Nav_Menu {
55
  * to do only one time
56
  *
57
  * @since 1.2
 
 
58
  */
59
  public function create_nav_menu_locations() {
60
  static $once;
@@ -79,8 +105,8 @@ class PLL_Nav_Menu {
79
  *
80
  * @since 1.8
81
  *
82
- * @param string $loc nav menu location
83
- * @param object $lang
84
  * @return string
85
  */
86
  public function combine_location( $loc, $lang ) {
@@ -88,14 +114,15 @@ class PLL_Nav_Menu {
88
  }
89
 
90
  /**
91
- * Get nav menu locations and language from a temporary location
92
  *
93
  * @since 1.8
94
  *
95
- * @param string $loc temporary location
96
- * @return array
97
- * 'location' => nav menu location
98
- * 'lang' => language slug
 
99
  */
100
  public function explode_location( $loc ) {
101
  $infos = explode( '___', $loc );
@@ -119,13 +146,14 @@ class PLL_Nav_Menu {
119
  }
120
 
121
  /**
122
- * Filters _wp_auto_add_pages_to_menu by language
123
  *
124
  * @since 0.9.4
125
  *
126
- * @param string $new_status Transition to this post status.
127
- * @param string $old_status Previous post status.
128
- * @param object $post Post data.
 
129
  */
130
  public function auto_add_pages_to_menu( $new_status, $old_status, $post ) {
131
  if ( 'publish' != $new_status || 'publish' == $old_status || 'page' != $post->post_type || ! empty( $post->post_parent ) ) {
@@ -133,8 +161,6 @@ class PLL_Nav_Menu {
133
  }
134
 
135
  if ( ! empty( $this->options['nav_menus'][ $this->theme ] ) ) {
136
- $this->auto_add_menus = array();
137
-
138
  $lang = $this->model->post->get_language( $post->ID );
139
  $lang = empty( $lang ) ? $this->options['default_lang'] : $lang->slug; // If the page has no language yet, the default language will be assigned
140
 
10
  * @since 1.7.7
11
  */
12
  class PLL_Nav_Menu {
13
+ /**
14
+ * Stores the plugin options.
15
+ *
16
+ * @var array
17
+ */
18
+ public $options;
19
+
20
+ /**
21
+ * @var PLL_Model
22
+ */
23
+ public $model;
24
+
25
+ /**
26
+ * Theme name.
27
+ *
28
+ * @var string
29
+ */
30
+ protected $theme;
31
+
32
+ /**
33
+ * Array of menu ids in a given language used when auto add pages to menus.
34
+ *
35
+ * @var int[]
36
+ */
37
+ protected $auto_add_menus = array();
38
 
39
  /**
40
  * Constructor: setups filters and actions
63
  *
64
  * @since 2.6
65
  *
66
+ * @param stdClass $item Menu item.
67
+ * @return stdClass
68
  */
69
  public function wp_setup_nav_menu_item( $item ) {
70
  if ( '#pll_switcher' === $item->url ) {
79
  * to do only one time
80
  *
81
  * @since 1.2
82
+ *
83
+ * @return void
84
  */
85
  public function create_nav_menu_locations() {
86
  static $once;
105
  *
106
  * @since 1.8
107
  *
108
+ * @param string $loc Nav menu location.
109
+ * @param PLL_Language $lang Language object.
110
  * @return string
111
  */
112
  public function combine_location( $loc, $lang ) {
114
  }
115
 
116
  /**
117
+ * Get nav menu locations and language from a temporary location.
118
  *
119
  * @since 1.8
120
  *
121
+ * @param string $loc Temporary location.
122
+ * @return string[] {
123
+ * @type string $location Nav menu location.
124
+ * @type string $lang Language code.
125
+ * }
126
  */
127
  public function explode_location( $loc ) {
128
  $infos = explode( '___', $loc );
146
  }
147
 
148
  /**
149
+ * Filters _wp_auto_add_pages_to_menu by language.
150
  *
151
  * @since 0.9.4
152
  *
153
+ * @param string $new_status Transition to this post status.
154
+ * @param string $old_status Previous post status.
155
+ * @param WP_Post $post Post object.
156
+ * @return void
157
  */
158
  public function auto_add_pages_to_menu( $new_status, $old_status, $post ) {
159
  if ( 'publish' != $new_status || 'publish' == $old_status || 'page' != $post->post_type || ! empty( $post->post_parent ) ) {
161
  }
162
 
163
  if ( ! empty( $this->options['nav_menus'][ $this->theme ] ) ) {
 
 
164
  $lang = $this->model->post->get_language( $post->ID );
165
  $lang = empty( $lang ) ? $this->options['default_lang'] : $lang->slug; // If the page has no language yet, the default language will be assigned
166
 
include/olt-manager.php CHANGED
@@ -13,10 +13,33 @@
13
  * @since 1.2
14
  */
15
  class PLL_OLT_Manager {
16
- protected static $instance; // For singleton
 
 
 
 
 
 
 
 
 
 
 
17
  protected $default_locale;
18
- protected $list_textdomains = array(); // All text domains
19
- public $labels = array(); // Post types and taxonomies labels to translate
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  /**
22
  * Constructor: setups relevant filters
@@ -28,9 +51,8 @@ class PLL_OLT_Manager {
28
  add_filter( 'pre_update_option_active_plugins', array( $this, 'make_polylang_first' ) );
29
  add_filter( 'pre_update_option_active_sitewide_plugins', array( $this, 'make_polylang_first' ) );
30
 
31
- // Overriding load text domain only on front since WP 4.7
32
- // FIXME test get_user_locale for backward compatibility with WP < 4.7
33
- if ( is_admin() && ! Polylang::is_ajax_on_front() && function_exists( 'get_user_locale' ) ) {
34
  return;
35
  }
36
 
@@ -66,6 +88,8 @@ class PLL_OLT_Manager {
66
  * Loads text domains
67
  *
68
  * @since 0.1
 
 
69
  */
70
  public function load_textdomains() {
71
  // Our load_textdomain_mofile filter has done its job. let's remove it before calling load_textdomain
@@ -83,7 +107,7 @@ class PLL_OLT_Manager {
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
 
89
  foreach ( $this->list_textdomains as $textdomain ) {
@@ -139,36 +163,23 @@ class PLL_OLT_Manager {
139
  }
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
-
154
- /**
155
- * Saves all text domains in a table for later usage
156
- * It replaces the 'override_load_textdomain' filter used since 0.1
157
  *
158
  * @since 2.0.4
159
  *
160
- * @param string $mofile translation file name
161
- * @param string $domain text domain name
162
- * @return bool
163
  */
164
  public function load_textdomain_mofile( $mofile, $domain ) {
165
- // On multisite, 2 files are sharing the same domain so we need to distinguish them
166
  if ( 'default' === $domain && false !== strpos( $mofile, '/ms-' ) ) {
167
  $this->list_textdomains['ms-default'] = array( 'mo' => $mofile, 'domain' => $domain );
168
  } else {
169
  $this->list_textdomains[ $domain ] = array( 'mo' => $mofile, 'domain' => $domain );
170
  }
171
- return ''; // Hack to prevent WP loading text domains as we will load them all later
172
  }
173
 
174
  /**
@@ -205,11 +216,12 @@ class PLL_OLT_Manager {
205
  }
206
 
207
  /**
208
- * Translates post types and taxonomies labels once the language is known
209
  *
210
  * @since 0.9
211
  *
212
- * @param object $type either a post type or a taxonomy
 
213
  */
214
  public function translate_labels( $type ) {
215
  // Use static array to avoid translating several times the same ( default ) labels
@@ -232,12 +244,12 @@ class PLL_OLT_Manager {
232
  }
233
 
234
  /**
235
- * Allows Polylang to be the first plugin loaded ;- )
236
  *
237
  * @since 1.2
238
  *
239
- * @param array $plugins list of active plugins
240
- * @return array list of active plugins
241
  */
242
  public function make_polylang_first( $plugins ) {
243
  if ( $key = array_search( POLYLANG_BASENAME, $plugins ) ) {
13
  * @since 1.2
14
  */
15
  class PLL_OLT_Manager {
16
+ /**
17
+ * Singleton instance
18
+ *
19
+ * @var PLL_OLT_Manager
20
+ */
21
+ protected static $instance;
22
+
23
+ /**
24
+ * Stores the default site locale before it is modified.
25
+ *
26
+ * @var string
27
+ */
28
  protected $default_locale;
29
+
30
+ /**
31
+ * Stores all loaded text domains and mo files.
32
+ *
33
+ * @var string[][]
34
+ */
35
+ protected $list_textdomains = array();
36
+
37
+ /**
38
+ * Stores post types an taxonomies labels to translate.
39
+ *
40
+ * @var string[][]
41
+ */
42
+ public $labels = array();
43
 
44
  /**
45
  * Constructor: setups relevant filters
51
  add_filter( 'pre_update_option_active_plugins', array( $this, 'make_polylang_first' ) );
52
  add_filter( 'pre_update_option_active_sitewide_plugins', array( $this, 'make_polylang_first' ) );
53
 
54
+ // Overriding load text domain only on front since WP 4.7.
55
+ if ( is_admin() && ! Polylang::is_ajax_on_front() ) {
 
56
  return;
57
  }
58
 
88
  * Loads text domains
89
  *
90
  * @since 0.1
91
+ *
92
+ * @return void
93
  */
94
  public function load_textdomains() {
95
  // Our load_textdomain_mofile filter has done its job. let's remove it before calling load_textdomain
107
  * See WP_Locale_Switcher::change_locale()
108
  */
109
  if ( ! class_exists( 'WP_Textdomain_Registry' ) && function_exists( '_get_path_to_translation' ) ) {
110
+ _get_path_to_translation( '', true );
111
  }
112
 
113
  foreach ( $this->list_textdomains as $textdomain ) {
163
  }
164
 
165
  /**
166
+ * Saves all text domains in a table for later usage.
167
+ * It replaces the 'override_load_textdomain' filter previously used.
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  *
169
  * @since 2.0.4
170
  *
171
+ * @param string $mofile The translation file name.
172
+ * @param string $domain The text domain name.
173
+ * @return string
174
  */
175
  public function load_textdomain_mofile( $mofile, $domain ) {
176
+ // On multisite, 2 files are sharing the same domain so we need to distinguish them.
177
  if ( 'default' === $domain && false !== strpos( $mofile, '/ms-' ) ) {
178
  $this->list_textdomains['ms-default'] = array( 'mo' => $mofile, 'domain' => $domain );
179
  } else {
180
  $this->list_textdomains[ $domain ] = array( 'mo' => $mofile, 'domain' => $domain );
181
  }
182
+ return ''; // Hack to prevent WP loading text domains as we will load them all later.
183
  }
184
 
185
  /**
216
  }
217
 
218
  /**
219
+ * Translates post types and taxonomies labels once the language is known.
220
  *
221
  * @since 0.9
222
  *
223
+ * @param WP_Post_Type|WP_Taxonomy $type Either a post type or a taxonomy.
224
+ * @return void
225
  */
226
  public function translate_labels( $type ) {
227
  // Use static array to avoid translating several times the same ( default ) labels
244
  }
245
 
246
  /**
247
+ * Allows Polylang to be the first plugin loaded ;-).
248
  *
249
  * @since 1.2
250
  *
251
+ * @param string[] $plugins List of active plugins.
252
+ * @return string[] List of active plugins.
253
  */
254
  public function make_polylang_first( $plugins ) {
255
  if ( $key = array_search( POLYLANG_BASENAME, $plugins ) ) {
include/pointer.php DELETED
@@ -1,116 +0,0 @@
1
- <?php
2
- /**
3
- * @package Polylang
4
- */
5
-
6
- /**
7
- * A class to manage WP pointers
8
- * offers the possibility to have customized buttons
9
- *
10
- * @since 1.7.7
11
- * @deprecated 2.3.9
12
- */
13
- class PLL_Pointer {
14
- protected $args;
15
-
16
- /**
17
- * Constructor
18
- * enqueues the pointer script
19
- *
20
- * List of parameters accepted in $args:
21
- *
22
- * pointer => required, unique identifier of the pointer
23
- * id => required, the pointer will be attached to this html id
24
- * position => optional array, if used both sub parameters are required
25
- * edge => 'top' or 'bottom'
26
- * align => 'right' or 'left'
27
- * width => optional, the width in px
28
- * title => required, title
29
- * content => required, content
30
- * buttons => optional array of arrays, by default the pointer uses the standard dismiss button offered by WP
31
- * label => the label of the button
32
- * link => optional link for the button. By default, the button just dismisses the pointer
33
- *
34
- * @since 1.7.7
35
- *
36
- * @param array $args
37
- */
38
- public function __construct( $args ) {
39
- trigger_error( 'The class PLL_Pointer has been deprecated since Polylang 2.3.9 and will be removed in a future version.', E_USER_ERROR ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
40
-
41
- $this->args = $args;
42
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
43
- }
44
-
45
- /**
46
- * Enqueue javascripts and styles if the pointer has not been dismissed
47
- *
48
- * @since 1.7.7
49
- */
50
- public function enqueue_scripts() {
51
- $dismissed = explode( ',', get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
52
- if ( in_array( $this->args['pointer'], $dismissed ) || ! current_user_can( 'manage_options' ) ) {
53
- return;
54
- }
55
-
56
- // Add pointer javascript
57
- add_action( 'admin_print_footer_scripts', array( $this, 'print_js' ) );
58
-
59
- wp_enqueue_style( 'wp-pointer' );
60
- wp_enqueue_script( 'wp-pointer' );
61
- }
62
-
63
- /**
64
- * Adds the javascript of our pointer to the page
65
- *
66
- * @since 1.7.7
67
- */
68
- public function print_js() {
69
-
70
- // Add optional buttons
71
- if ( ! empty( $this->args['buttons'] ) ) {
72
- $b = "
73
- var widget = pointer.pointer( 'widget' );
74
- var buttons = $( '.wp-pointer-buttons', widget );
75
- $( 'a.close', widget ).remove();"; // removes the WP button
76
-
77
- // All the buttons use the standard WP ajax action to remember the pointer has been dismissed
78
- foreach ( $this->args['buttons'] as $button ) {
79
- $b .= sprintf(
80
- "$( '<a>' ).addClass( '%s' ).html( '%s' ).css( 'margin-left', '10px' ).click( function() {
81
- $.post( ajaxurl, {
82
- pointer: '%s',
83
- action: 'dismiss-wp-pointer'
84
- }, function( response ) {
85
- %s
86
- } );
87
- } ).appendTo( buttons );",
88
- empty( $button['link'] ) ? 'button' : 'button button-primary',
89
- esc_html( $button['label'] ),
90
- $this->args['pointer'],
91
- empty( $button['link'] ) ? "pointer.pointer( 'close' )" : sprintf( "location.href = '%s'", $button['link'] )
92
- );
93
- }
94
- }
95
-
96
- $js = sprintf(
97
- "//<![CDATA[
98
- jQuery( document ).ready( function( $ ) {
99
- var pointer = $( '#%s' ).pointer( {
100
- content: '%s',
101
- %s
102
- %s
103
- } );
104
- pointer.pointer( 'open' );
105
- %s
106
- } );
107
- // ]]>",
108
- $this->args['id'],
109
- sprintf( '<h3>%s</h3><p>%s</p>', esc_html( $this->args['title'] ), esc_html( $this->args['content'] ) ),
110
- empty( $this->args['position'] ) ? '' : sprintf( 'position: {edge: "%s", align: "%s",},', $this->args['position']['edge'], $this->args['position']['align'] ),
111
- empty( $this->args['width'] ) ? '' : sprintf( 'pointerWidth: %d,', $this->args['width'] ),
112
- empty( $b ) ? '' : $b
113
- );
114
- echo '<script type="text/javascript">' . $js . '</script>'; // phpcs:ignore WordPress.Security.EscapeOutput
115
- }
116
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
include/query.php CHANGED
@@ -9,19 +9,53 @@
9
  * @since 2.2
10
  */
11
  class PLL_Query {
 
 
 
 
 
 
 
 
 
 
12
  /**
13
  * Constructor
14
  *
15
  * @since 2.2
16
  *
17
- * @param array $query Reference to the WP_Query object
18
- * @param object $model
19
  */
20
  public function __construct( &$query, &$model ) {
21
  $this->query = &$query;
22
  $this->model = &$model;
23
  }
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  /**
26
  * Check if translated taxonomy is queried
27
  * Compatible with nested queries introduced in WP 4.1
@@ -62,12 +96,13 @@ class PLL_Query {
62
  }
63
 
64
  /**
65
- * Sets the language in query
66
- * Optimized for ( needs ) WP 3.5+
67
  *
68
  * @since 2.2
69
  *
70
- * @param object $lang
 
71
  */
72
  public function set_language( $lang ) {
73
  // Defining directly the tax_query ( rather than setting 'lang' avoids transforming the query by WP )
@@ -98,21 +133,22 @@ class PLL_Query {
98
  }
99
 
100
  /**
101
- * Add the language in query after it has checked that it won't conflict with other query vars
102
  *
103
  * @since 2.2
104
  *
105
- * @param object $lang Language
 
106
  */
107
  public function filter_query( $lang ) {
108
  $qvars = &$this->query->query_vars;
109
 
110
- if ( ! isset( $qvars['lang'] ) ) {
111
  $taxonomies = array_intersect( $this->model->get_translated_taxonomies(), get_taxonomies( array( '_builtin' => false ) ) );
112
 
113
  foreach ( $taxonomies as $tax ) {
114
- $tax = get_taxonomy( $tax );
115
- if ( ! empty( $qvars[ $tax->query_var ] ) ) {
116
  return;
117
  }
118
  }
@@ -128,7 +164,7 @@ class PLL_Query {
128
  if ( $taxonomies && ( empty( $qvars['post_type'] ) || 'any' === $qvars['post_type'] ) ) {
129
  foreach ( $taxonomies as $taxonomy ) {
130
  $tax_object = get_taxonomy( $taxonomy );
131
- if ( $this->model->is_translated_post_type( $tax_object->object_type ) ) {
132
  $this->set_language( $lang );
133
  break;
134
  }
9
  * @since 2.2
10
  */
11
  class PLL_Query {
12
+ /**
13
+ * @var PLL_Model
14
+ */
15
+ public $model;
16
+
17
+ /**
18
+ * @var WP_Query
19
+ */
20
+ public $query;
21
+
22
  /**
23
  * Constructor
24
  *
25
  * @since 2.2
26
  *
27
+ * @param WP_Query $query Reference to the WP_Query object.
28
+ * @param PLL_Model $model Instance of PLL_Model.
29
  */
30
  public function __construct( &$query, &$model ) {
31
  $this->query = &$query;
32
  $this->model = &$model;
33
  }
34
 
35
+ /**
36
+ * Checks if the query already includes a language taxonomy.
37
+ *
38
+ * @since 3.0
39
+ *
40
+ * @param array $qvars WP_Query query vars.
41
+ * @return bool
42
+ */
43
+ protected function is_already_filtered( $qvars ) {
44
+ if ( isset( $qvars['lang'] ) ) {
45
+ return true;
46
+ }
47
+
48
+ if ( ! empty( $qvars['tax_query'] ) && is_array( $qvars['tax_query'] ) ) {
49
+ foreach ( $qvars['tax_query'] as $tax_query ) {
50
+ if ( isset( $tax_query['taxonomy'] ) && 'language' === $tax_query['taxonomy'] ) {
51
+ return true;
52
+ }
53
+ }
54
+ }
55
+
56
+ return false;
57
+ }
58
+
59
  /**
60
  * Check if translated taxonomy is queried
61
  * Compatible with nested queries introduced in WP 4.1
96
  }
97
 
98
  /**
99
+ * Sets the language in query.
100
+ * Optimized for (and requires) WP 3.5+.
101
  *
102
  * @since 2.2
103
  *
104
+ * @param PLL_Language $lang Language object.
105
+ * @return void
106
  */
107
  public function set_language( $lang ) {
108
  // Defining directly the tax_query ( rather than setting 'lang' avoids transforming the query by WP )
133
  }
134
 
135
  /**
136
+ * Adds the language in the query after it has checked that it won't conflict with other query vars.
137
  *
138
  * @since 2.2
139
  *
140
+ * @param PLL_Language $lang Language.
141
+ * @return void
142
  */
143
  public function filter_query( $lang ) {
144
  $qvars = &$this->query->query_vars;
145
 
146
+ if ( ! $this->is_already_filtered( $qvars ) ) {
147
  $taxonomies = array_intersect( $this->model->get_translated_taxonomies(), get_taxonomies( array( '_builtin' => false ) ) );
148
 
149
  foreach ( $taxonomies as $tax ) {
150
+ $tax_object = get_taxonomy( $tax );
151
+ if ( ! empty( $tax_object ) && ! empty( $qvars[ $tax_object->query_var ] ) ) {
152
  return;
153
  }
154
  }
164
  if ( $taxonomies && ( empty( $qvars['post_type'] ) || 'any' === $qvars['post_type'] ) ) {
165
  foreach ( $taxonomies as $taxonomy ) {
166
  $tax_object = get_taxonomy( $taxonomy );
167
+ if ( ! empty( $tax_object ) && $this->model->is_translated_post_type( $tax_object->object_type ) ) {
168
  $this->set_language( $lang );
169
  break;
170
  }
include/rest-request.php CHANGED
@@ -4,29 +4,48 @@
4
  */
5
 
6
  /**
7
- * REST API controller
8
- * accessible as $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 => reference to PLL_Admin_Links object
15
- * static_pages => reference to PLL_Static_Pages object
16
- * filters => reference to PLL_Frontend_Filters object
17
- * filters_links => reference to PLL_Filters_Links object
18
- * posts => reference to PLL_CRUD_Posts object
19
- * terms => reference to PLL_CRUD_Terms object
20
  *
21
  * @since 2.6
22
  */
23
  class PLL_REST_Request extends PLL_Base {
24
- public $links, $static_pages, $posts, $terms, $filters, $filters_links;
25
 
26
  /**
27
- * Setup filters
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  *
29
  * @since 2.6
 
 
30
  */
31
  public function init() {
32
  parent::init();
@@ -38,15 +57,16 @@ class PLL_REST_Request extends PLL_Base {
38
 
39
  $this->filters_links = new PLL_Filters_Links( $this );
40
  $this->filters = new PLL_Filters( $this );
 
41
 
42
- // Static front page and page for posts
43
  if ( 'page' === get_option( 'show_on_front' ) ) {
44
  $this->static_pages = new PLL_Static_Pages( $this );
45
  }
46
 
47
  $this->links = new PLL_Admin_Links( $this );
48
 
49
- $this->nav_menu = new PLL_Nav_Menu( $this ); // For auto added pages to menu
50
  }
51
  }
52
  }
4
  */
5
 
6
  /**
7
+ * Main Polylang class for REST API requrests, accessible from @see PLL().
 
 
 
 
 
 
 
 
 
 
 
 
8
  *
9
  * @since 2.6
10
  */
11
  class PLL_REST_Request extends PLL_Base {
 
12
 
13
  /**
14
+ * @var PLL_Filters
15
+ */
16
+ public $filters;
17
+
18
+ /**
19
+ * @var PLL_Filters_Links
20
+ */
21
+ public $filters_links;
22
+
23
+ /**
24
+ * @var PLL_Admin_Links
25
+ */
26
+ public $links;
27
+
28
+ /**
29
+ * @var PLL_Nav_Menu
30
+ */
31
+ public $nav_menu;
32
+
33
+ /**
34
+ * @var PLL_Static_Pages
35
+ */
36
+ public $static_pages;
37
+
38
+ /**
39
+ * @var PLL_Filters_Widgets_Options
40
+ */
41
+ public $filters_widgets;
42
+
43
+ /**
44
+ * Setup filters.
45
  *
46
  * @since 2.6
47
+ *
48
+ * @return void
49
  */
50
  public function init() {
51
  parent::init();
57
 
58
  $this->filters_links = new PLL_Filters_Links( $this );
59
  $this->filters = new PLL_Filters( $this );
60
+ $this->filters_widgets = new PLL_Filters_Widgets_Options( $this );
61
 
62
+ // Static front page and page for posts.
63
  if ( 'page' === get_option( 'show_on_front' ) ) {
64
  $this->static_pages = new PLL_Static_Pages( $this );
65
  }
66
 
67
  $this->links = new PLL_Admin_Links( $this );
68
 
69
+ $this->nav_menu = new PLL_Nav_Menu( $this ); // For auto added pages to menu.
70
  }
71
  }
72
  }
include/static-pages.php CHANGED
@@ -9,8 +9,38 @@
9
  * @since 1.8
10
  */
11
  class PLL_Static_Pages {
12
- public $model, $options;
13
- public $page_on_front, $page_for_posts;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  /**
16
  * Constructor: setups filters and actions
@@ -45,6 +75,8 @@ class PLL_Static_Pages {
45
  * Stores the page on front and page for posts ids
46
  *
47
  * @since 1.8
 
 
48
  */
49
  public function init() {
50
  if ( 'page' == get_option( 'show_on_front' ) ) {
@@ -75,12 +107,13 @@ class PLL_Static_Pages {
75
  }
76
 
77
  /**
78
- * Adds page_on_front and page_for_posts properties to the language objects
79
  *
80
  * @since 1.8
81
  *
82
- * @param array $languages
83
- * @param object $model
 
84
  */
85
  public static function pll_languages_list( $languages, $model ) {
86
  if ( 'page' === get_option( 'show_on_front' ) ) {
@@ -114,6 +147,7 @@ class PLL_Static_Pages {
114
  *
115
  * @param int $post_id The post ID.
116
  * @param string $url The requested URL.
 
117
  */
118
  public function oembed_request_post_id( $post_id, $url ) {
119
  foreach ( $this->model->get_languages_list() as $lang ) {
9
  * @since 1.8
10
  */
11
  class PLL_Static_Pages {
12
+ /**
13
+ * Id of the page on front.
14
+ *
15
+ * @var int
16
+ */
17
+ public $page_on_front;
18
+
19
+ /**
20
+ * Id of the page for posts.
21
+ *
22
+ * @var int
23
+ */
24
+ public $page_for_posts;
25
+
26
+ /**
27
+ * Stores the plugin options.
28
+ *
29
+ * @var array
30
+ */
31
+ protected $options;
32
+
33
+ /**
34
+ * @var PLL_Model
35
+ */
36
+ protected $model;
37
+
38
+ /**
39
+ * Current language.
40
+ *
41
+ * @var PLL_Language
42
+ */
43
+ protected $curlang;
44
 
45
  /**
46
  * Constructor: setups filters and actions
75
  * Stores the page on front and page for posts ids
76
  *
77
  * @since 1.8
78
+ *
79
+ * @return void
80
  */
81
  public function init() {
82
  if ( 'page' == get_option( 'show_on_front' ) ) {
107
  }
108
 
109
  /**
110
+ * Adds page_on_front and page_for_posts properties to the language objects.
111
  *
112
  * @since 1.8
113
  *
114
+ * @param PLL_Language[] $languages The list of languages.
115
+ * @param PLL_Model $model The instance of PLL_Model.
116
+ * @return PLL_Language[]
117
  */
118
  public static function pll_languages_list( $languages, $model ) {
119
  if ( 'page' === get_option( 'show_on_front' ) ) {
147
  *
148
  * @param int $post_id The post ID.
149
  * @param string $url The requested URL.
150
+ * @return int
151
  */
152
  public function oembed_request_post_id( $post_id, $url ) {
153
  foreach ( $this->model->get_languages_list() as $lang ) {
include/switcher.php CHANGED
@@ -9,6 +9,27 @@
9
  * @since 1.2
10
  */
11
  class PLL_Switcher {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  /**
14
  * Returns options available for the language switcher - menu or widget
@@ -33,29 +54,80 @@ class PLL_Switcher {
33
  }
34
 
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
  *
42
- * @param object $links instance of PLL_Frontend_Links
43
- * @param array $args
44
- * @return array
45
  */
46
- protected function get_elements( $links, $args ) {
47
  $first = true;
48
  $out = array();
49
 
50
- foreach ( $links->model->get_languages_list( array( 'hide_empty' => $args['hide_if_empty'] ) ) as $language ) {
51
  $id = (int) $language->term_id;
52
  $order = (int) $language->term_group;
53
  $slug = $language->slug;
54
  $locale = $language->get_locale( 'display' );
55
  $classes = array( 'lang-item', 'lang-item-' . $id, 'lang-item-' . esc_attr( $slug ) );
56
- $url = null; // Avoids potential notice
57
- $curlang = 0 === $args['admin_render'] ? $links->curlang->slug : $args['admin_current_lang'];
58
- $current_lang = $curlang == $slug;
59
 
60
  if ( $current_lang ) {
61
  if ( $args['hide_current'] && ! ( $args['dropdown'] && ! $args['raw'] ) ) {
@@ -65,11 +137,7 @@ class PLL_Switcher {
65
  }
66
  }
67
 
68
- if ( null !== $args['post_id'] && ( $tr_id = $links->model->post->get( $args['post_id'], $language ) ) && $links->model->post->current_user_can_read( $tr_id ) ) {
69
- $url = get_permalink( $tr_id );
70
- } elseif ( null === $args['post_id'] && 0 === $args['admin_render'] ) {
71
- $url = $links->get_translation_url( $language );
72
- }
73
 
74
  if ( $no_translation = empty( $url ) ) {
75
  $classes[] = 'no-translation';
@@ -80,9 +148,9 @@ class PLL_Switcher {
80
  *
81
  * @since 0.7
82
  *
83
- * @param string $url the link
84
- * @param string $slug language code
85
- * @param string $locale language locale
86
  */
87
  $url = apply_filters( 'pll_the_language_link', $url, $slug, $language->locale );
88
 
@@ -91,7 +159,7 @@ class PLL_Switcher {
91
  continue;
92
  }
93
 
94
- $url = empty( $url ) || $args['force_home'] ? $links->get_home_url( $language ) : $url; // If the page is not translated, link to the home page
95
 
96
  $name = $args['show_names'] || ! $args['show_flags'] || $args['raw'] ? ( 'slug' == $args['display_names_as'] ? $slug : $language->name ) : '';
97
  $flag = $args['raw'] && ! $args['show_flags'] ? $language->get_display_flag_url() : ( $args['show_flags'] ? $language->get_display_flag() : '' );
@@ -109,50 +177,34 @@ class PLL_Switcher {
109
 
110
  /**
111
  * Displays a language switcher
112
- * or returns the raw elements to build a custom language switcher
113
- *
114
- * List of parameters accepted in $args:
115
- *
116
- * dropdown => the list is displayed as dropdown if set, defaults to 0
117
- * echo => echoes the list if set to 1, defaults to 1
118
- * hide_if_empty => hides languages with no posts ( or pages ) if set to 1, defaults to 1
119
- * show_flags => displays flags if set to 1, defaults to 0
120
- * show_names => show language names if set to 1, defaults to 1
121
- * display_names_as => whether to display the language name or its slug, valid options are 'slug' and 'name', defaults to name
122
- * force_home => will always link to home in translated language if set to 1, defaults to 0
123
- * hide_if_no_translation => hide the link if there is no translation if set to 1, defaults to 0
124
- * hide_current => hide the current language if set to 1, defaults to 0
125
- * post_id => returns links to translations of post defined by post_id if set, defaults not set
126
- * raw => return a raw array instead of html markup if set to 1, defaults to 0
127
- * item_spacing => whether to preserve or discard whitespace between list items, valid options are 'preserve' and 'discard', defaults to preserve
128
- * admin_render => allows to force the current language code in an admin context if set, default to 0. Need to set the admin_current_lang argument below
129
- * admin_current_lang => the current language code in an admin context. Need to set the admin_render to 1, defaults not set
130
  *
131
  * @since 0.1
132
  *
133
- * @param object $links instance of PLL_Frontend_Links
134
- * @param array $args
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  * @return string|array either the html markup of the switcher or the raw elements to build a custom language switcher
136
  */
137
- public function the_languages( $links, $args = '' ) {
138
- $defaults = array(
139
- 'dropdown' => 0, // display as list and not as dropdown
140
- 'echo' => 1, // echoes the list
141
- 'hide_if_empty' => 1, // hides languages with no posts ( or pages )
142
- 'menu' => 0, // not for nav menu ( this argument is deprecated since v1.1.1 )
143
- 'show_flags' => 0, // don't show flags
144
- 'show_names' => 1, // show language names
145
- 'display_names_as' => 'name', // valid options are slug and name
146
- 'force_home' => 0, // tries to find a translation
147
- 'hide_if_no_translation' => 0, // don't hide the link if there is no translation
148
- 'hide_current' => 0, // don't hide current language
149
- 'post_id' => null, // if not null, link to translations of post defined by post_id
150
- 'raw' => 0, // set this to true to build your own custom language switcher
151
- 'item_spacing' => 'preserve', // 'preserve' or 'discard' whitespace between list items
152
- 'admin_render' => 0, // make the switcher in an frontend context
153
- 'admin_current_lang' => null, // use when admin_render is set to 1, if not null use it instead of the current language
154
- );
155
- $args = wp_parse_args( $args, $defaults );
156
 
157
  /**
158
  * Filter the arguments of the 'pll_the_languages' template tag
@@ -168,7 +220,7 @@ class PLL_Switcher {
168
  $args['show_names'] = 1;
169
  }
170
 
171
- $elements = $this->get_elements( $links, $args );
172
 
173
  if ( $args['raw'] ) {
174
  return $elements;
@@ -177,9 +229,8 @@ class PLL_Switcher {
177
  if ( $args['dropdown'] ) {
178
  $args['name'] = 'lang_choice_' . $args['dropdown'];
179
  $walker = new PLL_Walker_Dropdown();
180
- $args['selected'] = 0 === $args['admin_render'] ? $links->curlang->slug : $args['admin_current_lang'];
181
- }
182
- else {
183
  $walker = new PLL_Walker_List();
184
  }
185
 
9
  * @since 1.2
10
  */
11
  class PLL_Switcher {
12
+ const DEFAULTS = array(
13
+ 'dropdown' => 0, // Display as list and not as dropdown.
14
+ 'echo' => 1, // Echoes the list.
15
+ 'hide_if_empty' => 1, // Hides languages with no posts (or pages).
16
+ 'show_flags' => 0, // Don't show flags.
17
+ 'show_names' => 1, // Show language names.
18
+ 'display_names_as' => 'name', // Display the language name.
19
+ 'force_home' => 0, // Tries to find a translation.
20
+ 'hide_if_no_translation' => 0, // Don't hide the link if there is no translation.
21
+ 'hide_current' => 0, // Don't hide the current language.
22
+ 'post_id' => null, // Link to the translations of the current page.
23
+ 'raw' => 0, // Build the language switcher.
24
+ 'item_spacing' => 'preserve', // Preserve whitespace between list items.
25
+ 'admin_render' => 0, // Make the switcher in a frontend context.
26
+ 'admin_current_lang' => null, // Use the global current language.
27
+ );
28
+
29
+ /**
30
+ * @var PLL_Links
31
+ */
32
+ protected $links;
33
 
34
  /**
35
  * Returns options available for the language switcher - menu or widget
54
  }
55
 
56
  /**
57
+ * Returns the current language code.
58
+ *
59
+ * @since 3.0
60
+ *
61
+ * @param array $args Arguments passed to {@see PLL_Switcher::the_languages()}.
62
+ * @return string
63
+ */
64
+ protected function get_current_language( $args ) {
65
+ if ( $args['admin_current_lang'] ) {
66
+ return $args['admin_current_lang'];
67
+ }
68
+
69
+ if ( isset( $this->links->curlang ) ) {
70
+ return $this->links->curlang->slug;
71
+ }
72
+
73
+ return $this->links->options['default_lang'];
74
+ }
75
+
76
+ /**
77
+ * Returns the link for a given language.
78
  *
79
+ * @since 3.0
80
+ *
81
+ * @param PLL_Language $language Language.
82
+ * @param array $args Arguments passed to {@see PLL_Switcher::the_languages()}.
83
+ * @return string|null
84
+ */
85
+ protected function get_link( $language, $args ) {
86
+ global $post;
87
+
88
+ // Priority to the post passed in parameters.
89
+ if ( null !== $args['post_id'] ) {
90
+ $tr_id = $this->links->model->post->get( $args['post_id'], $language );
91
+ if ( $tr_id && $this->links->model->post->current_user_can_read( $tr_id ) ) {
92
+ return get_permalink( $tr_id );
93
+ }
94
+ }
95
+
96
+ // If we are on frontend.
97
+ if ( $this->links instanceof PLL_Frontend_Links ) {
98
+ return $this->links->get_translation_url( $language );
99
+ }
100
+
101
+ // For blocks in posts in REST requests.
102
+ if ( $post instanceof WP_Post ) {
103
+ $tr_id = $this->links->model->post->get( $post->ID, $language );
104
+ if ( $tr_id && $this->links->model->post->current_user_can_read( $tr_id ) ) {
105
+ return get_permalink( $tr_id );
106
+ }
107
+ }
108
+
109
+ return null;
110
+ }
111
+
112
+ /**
113
+ * Get the language elements for use in a walker
114
  *
115
  * @since 1.2
116
  *
117
+ * @param array $args Arguments passed to {@see PLL_Switcher::the_languages()}.
118
+ * @return array Language switcher elements.
 
119
  */
120
+ protected function get_elements( $args ) {
121
  $first = true;
122
  $out = array();
123
 
124
+ foreach ( $this->links->model->get_languages_list( array( 'hide_empty' => $args['hide_if_empty'] ) ) as $language ) {
125
  $id = (int) $language->term_id;
126
  $order = (int) $language->term_group;
127
  $slug = $language->slug;
128
  $locale = $language->get_locale( 'display' );
129
  $classes = array( 'lang-item', 'lang-item-' . $id, 'lang-item-' . esc_attr( $slug ) );
130
+ $current_lang = $this->get_current_language( $args ) === $slug;
 
 
131
 
132
  if ( $current_lang ) {
133
  if ( $args['hide_current'] && ! ( $args['dropdown'] && ! $args['raw'] ) ) {
137
  }
138
  }
139
 
140
+ $url = $this->get_link( $language, $args );
 
 
 
 
141
 
142
  if ( $no_translation = empty( $url ) ) {
143
  $classes[] = 'no-translation';
148
  *
149
  * @since 0.7
150
  *
151
+ * @param string|null $url The link, null if no translation was found.
152
+ * @param string $slug The language code.
153
+ * @param string $locale The language locale
154
  */
155
  $url = apply_filters( 'pll_the_language_link', $url, $slug, $language->locale );
156
 
159
  continue;
160
  }
161
 
162
+ $url = empty( $url ) || $args['force_home'] ? $this->links->get_home_url( $language ) : $url; // If the page is not translated, link to the home page
163
 
164
  $name = $args['show_names'] || ! $args['show_flags'] || $args['raw'] ? ( 'slug' == $args['display_names_as'] ? $slug : $language->name ) : '';
165
  $flag = $args['raw'] && ! $args['show_flags'] ? $language->get_display_flag_url() : ( $args['show_flags'] ? $language->get_display_flag() : '' );
177
 
178
  /**
179
  * Displays a language switcher
180
+ * or returns the raw elements to build a custom language switcher.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  *
182
  * @since 0.1
183
  *
184
+ * @param PLL_Links $links Instance of PLL_Links.
185
+ * @param array $args {
186
+ * Optional array of arguments.
187
+ *
188
+ * @type int $dropdown The list is displayed as dropdown if set, defaults to 0.
189
+ * @type int $echo Echoes the list if set to 1, defaults to 1.
190
+ * @type int $hide_if_empty Hides languages with no posts ( or pages ) if set to 1, defaults to 1.
191
+ * @type int $show_flags Displays flags if set to 1, defaults to 0.
192
+ * @type int $show_names Shows language names if set to 1, defaults to 1.
193
+ * @type string $display_names_as Whether to display the language name or its slug, valid options are 'slug' and 'name', defaults to name.
194
+ * @type int $force_home Will always link to home in translated language if set to 1, defaults to 0.
195
+ * @type int $hide_if_no_translation Hides the link if there is no translation if set to 1, defaults to 0.
196
+ * @type int $hide_current Hides the current language if set to 1, defaults to 0.
197
+ * @type int $post_id Returns links to the translations of the post defined by post_id if set, defaults not set.
198
+ * @type int $raw Return a raw array instead of html markup if set to 1, defaults to 0.
199
+ * @type string $item_spacing Whether to preserve or discard whitespace between list items, valid options are 'preserve' and 'discard', defaults to 'preserve'.
200
+ * @type int $admin_render Allows to force the current language code in an admin context if set, default to 0. Need to set the admin_current_lang argument below.
201
+ * @type string $admin_current_lang The current language code in an admin context. Need to set the admin_render to 1, defaults not set.
202
+ * }
203
  * @return string|array either the html markup of the switcher or the raw elements to build a custom language switcher
204
  */
205
+ public function the_languages( $links, $args = array() ) {
206
+ $this->links = $links;
207
+ $args = wp_parse_args( $args, self::DEFAULTS );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
 
209
  /**
210
  * Filter the arguments of the 'pll_the_languages' template tag
220
  $args['show_names'] = 1;
221
  }
222
 
223
+ $elements = $this->get_elements( $args );
224
 
225
  if ( $args['raw'] ) {
226
  return $elements;
229
  if ( $args['dropdown'] ) {
230
  $args['name'] = 'lang_choice_' . $args['dropdown'];
231
  $walker = new PLL_Walker_Dropdown();
232
+ $args['selected'] = $this->get_current_language( $args );
233
+ } else {
 
234
  $walker = new PLL_Walker_List();
235
  }
236
 
include/translate-option.php CHANGED
@@ -31,21 +31,22 @@ class PLL_Translate_Option {
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() ) {
@@ -86,8 +87,8 @@ class PLL_Translate_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 ) {
@@ -136,8 +137,9 @@ class PLL_Translate_Option {
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 ) ) {
@@ -186,11 +188,12 @@ class PLL_Translate_Option {
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
@@ -201,7 +204,7 @@ class PLL_Translate_Option {
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
  }
@@ -214,6 +217,8 @@ class PLL_Translate_Option {
214
  * the new strings with the old translations.
215
  *
216
  * @since 2.9
 
 
217
  */
218
  public function update_option() {
219
  $curlang = pll_current_language();
31
  * @since 2.9
32
  *
33
  * @param string $name Option name.
34
+ * @param array $keys Recursive array of option keys to translate in the form:
35
+ * @example 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
  *
44
+ * Note: only keys are interpreted. Any scalar can be used as values.
45
+ * @param array $args {
46
+ * Optional. Array of arguments for registering the option.
47
+ *
48
+ * @type string $context The group in which the strings will be registered.
49
+ * @type string $sanitize_callback A callback function that sanitizes the option's value.
50
  * }
51
  */
52
  public function __construct( $name, $keys = array(), $args = array() ) {
87
  *
88
  * @since 1.0
89
  *
90
+ * @param mixed $values Either a string to translate or a list of strings to translate.
91
+ * @param array|bool $key Array of option keys to translate.
92
  * @return array|string Translated string(s)
93
  */
94
  protected function translate_string_recursive( $values, $key ) {
137
  *
138
  * @param string $context The group in which the strings will be registered.
139
  * @param string $option Option name.
140
+ * @param mixed $values Option value.
141
  * @param array|bool $key Array of option keys to translate.
142
+ * @return void
143
  */
144
  protected function register_string_recursive( $context, $option, $values, $key ) {
145
  if ( is_object( $values ) ) {
188
  * @param mixed $value The new, unserialized option value.
189
  * @param mixed $old_value The old (filtered) option value.
190
  * @param string $name Option name.
191
+ * @return mixed
192
  */
193
  public function pre_update_option( $value, $old_value, $name ) {
194
  // Stores the unfiltered old option value before it is updated in DB.
195
  remove_filter( 'option_' . $name, array( $this, 'translate' ), 10, 2 );
196
+ $unfiltered_old_value = get_option( $name );
197
  add_filter( 'option_' . $name, array( $this, 'translate' ), 20, 2 );
198
 
199
  // Load strings translations according to the admin language filter
204
  PLL()->load_strings_translations( $locale );
205
 
206
  // Filters out the strings which would be updated to their translations and stores the updated strings.
207
+ $value = $this->check_value_recursive( $unfiltered_old_value, $value, $this->keys );
208
 
209
  return $value;
210
  }
217
  * the new strings with the old translations.
218
  *
219
  * @since 2.9
220
+ *
221
+ * @return void
222
  */
223
  public function update_option() {
224
  $curlang = pll_current_language();
include/translated-object.php CHANGED
@@ -4,41 +4,102 @@
4
  */
5
 
6
  /**
7
- * Setups the objects languages and translations model
8
  *
9
  * @since 1.8
10
  */
11
  abstract class PLL_Translated_Object {
 
 
 
12
  public $model;
13
- protected $object_type, $type, $tax_language, $tax_translations, $tax_tt;
14
 
15
  /**
16
- * Constructor
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  *
18
  * @since 1.8
19
  *
20
- * @param object $model
21
  */
22
  public function __construct( &$model ) {
23
  $this->model = &$model;
24
 
25
- // register our taxonomies as soon as possible
26
- // this is early registration, not ready for rewrite rules as wp_rewrite will be setup later
 
 
27
  $args = array( 'label' => false, 'public' => false, 'query_var' => false, 'rewrite' => false, '_pll' => true );
28
  register_taxonomy( $this->tax_language, $this->object_type, $args );
29
- $args['update_count_callback'] = '_update_generic_term_count'; // count *all* posts to avoid deleting in clean_translations_terms
30
  register_taxonomy( $this->tax_translations, $this->object_type, $args );
31
  }
32
 
33
  /**
34
- * Wrap wp_get_object_terms to cache it and return only one object
35
- * inspired by the function get_the_terms
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  *
37
  * @since 1.2
38
  *
39
- * @param int $object_id post_id or term_id
40
- * @param string $taxonomy Polylang taxonomy depending if we are looking for a post ( or term ) language ( or translation )
41
- * @return bool|object the term associated to the object in the requested taxonomy if exists, false otherwise
42
  */
43
  public function get_object_term( $object_id, $taxonomy ) {
44
  if ( empty( $object_id ) || is_wp_error( $object_id ) ) {
@@ -54,20 +115,22 @@ abstract class PLL_Translated_Object {
54
  $term = get_object_term_cache( $object_id, $taxonomy );
55
 
56
  if ( false === $term ) {
57
- // query language and translations at the same time
58
  $taxonomies = array( $this->tax_language, $this->tax_translations );
59
 
60
- // query terms
61
  $terms = array();
62
- foreach ( wp_get_object_terms( $object_id, $taxonomies, array( 'update_term_meta_cache' => false ) ) as $t ) {
63
- $terms[ $t->taxonomy ] = $t;
64
- if ( $t->taxonomy == $taxonomy ) {
65
- $term = $t;
 
 
 
66
  }
67
  }
68
 
69
- // store it the way WP wants it
70
- // set an empty cache if no term found in the taxonomy
71
  foreach ( $taxonomies as $tax ) {
72
  wp_cache_add( $object_id, empty( $terms[ $tax ] ) ? array() : array( $terms[ $tax ] ), $tax . '_relationships' );
73
  }
@@ -80,66 +143,67 @@ abstract class PLL_Translated_Object {
80
  }
81
 
82
  /**
83
- * Tells whether a translation term must be updated
84
  *
85
  * @since 2.3
86
  *
87
- * @param array $id Post id or term id
88
- * @param array $translations An associative array of translations with language code as key and translation id as value
 
89
  */
90
  protected function should_update_translation_group( $id, $translations ) {
91
- // Don't do anything if no translations have been added to the group
92
- $old_translations = $this->get_translations( $id ); // Includes at least $id itself
93
  return count( array_diff_assoc( $translations, $old_translations ) ) > 0;
94
  }
95
 
96
  /**
97
- * Saves translations for posts or terms
98
  *
99
  * @since 0.5
100
  *
101
- * @param int $id Post id or term id
102
- * @param array $translations An associative array of translations with language code as key and translation id as value
 
103
  */
104
  public function save_translations( $id, $translations ) {
105
  $id = (int) $id;
106
 
107
- if ( ( $lang = $this->get_language( $id ) ) && isset( $translations ) && is_array( $translations ) ) {
108
- // sanitize the translations array
109
  $translations = array_map( 'intval', $translations );
110
- $translations = array_merge( array( $lang->slug => $id ), $translations ); // make sure this object is in translations
111
- $translations = array_diff( $translations, array( 0 ) ); // don't keep non translated languages
112
- $translations = array_intersect_key( $translations, array_flip( $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) ); // keep only valid languages slugs as keys
113
 
114
- // unlink removed translations
115
  $old_translations = $this->get_translations( $id );
116
  foreach ( array_diff_assoc( $old_translations, $translations ) as $object_id ) {
117
  $this->delete_translation( $object_id );
118
  }
119
 
120
- // Check id we need to create or update the translation group
121
  if ( $this->should_update_translation_group( $id, $translations ) ) {
122
  $terms = wp_get_object_terms( $translations, $this->tax_translations );
123
  $term = reset( $terms );
124
 
125
- // create a new term if necessary
126
  if ( empty( $term ) ) {
127
  wp_insert_term( $group = uniqid( 'pll_' ), $this->tax_translations, array( 'description' => maybe_serialize( $translations ) ) );
128
- }
129
- else {
130
- // take care not to overwrite extra data stored in description field, if any
131
  $d = maybe_unserialize( $term->description );
132
- $d = is_array( $d ) ? array_diff_key( $d, $old_translations ) : array(); // remove old translations
133
- $d = array_merge( $d, $translations ); // add new one
134
  wp_update_term( $group = (int) $term->term_id, $this->tax_translations, array( 'description' => maybe_serialize( $d ) ) );
135
  }
136
 
137
- // link all translations to the new term
138
  foreach ( $translations as $p ) {
139
  wp_set_object_terms( $p, $group, $this->tax_translations );
140
  }
141
 
142
- // clean now unused translation groups
143
  foreach ( wp_list_pluck( $terms, 'term_id' ) as $term_id ) {
144
  $term = get_term( $term_id, $this->tax_translations );
145
  if ( empty( $term->count ) ) {
@@ -151,11 +215,12 @@ abstract class PLL_Translated_Object {
151
  }
152
 
153
  /**
154
- * Deletes a translation of a post or term
155
  *
156
  * @since 0.5
157
  *
158
- * @param int $id post id or term id
 
159
  */
160
  public function delete_translation( $id ) {
161
  $id = (int) $id;
@@ -177,23 +242,23 @@ abstract class PLL_Translated_Object {
177
  }
178
 
179
  /**
180
- * Returns an array of translations of a post or term
181
  *
182
  * @since 0.5
183
  *
184
- * @param int $id post id or term id
185
- * @return array an associative array of translations with language code as key and translation id as value
186
  */
187
  public function get_translations( $id ) {
188
  $term = $this->get_object_term( $id, $this->tax_translations );
189
  $translations = empty( $term ) ? array() : maybe_unserialize( $term->description );
190
 
191
- // make sure we return only translations ( thus we allow plugins to store other information in the array )
192
  if ( is_array( $translations ) ) {
193
  $translations = array_intersect_key( $translations, array_flip( $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) );
194
  }
195
 
196
- // make sure to return at least the passed post or term in its translation array
197
  if ( empty( $translations ) && $lang = $this->get_language( $id ) ) {
198
  $translations = array( $lang->slug => $id );
199
  }
@@ -202,13 +267,13 @@ abstract class PLL_Translated_Object {
202
  }
203
 
204
  /**
205
- * Returns the id of the translation of a post or term
206
  *
207
  * @since 0.5
208
  *
209
- * @param int $id post id or term id
210
- * @param object|string $lang object or slug
211
- * @return bool|int post id or term id of the translation, false if there is none
212
  */
213
  public function get_translation( $id, $lang ) {
214
  if ( ! $lang = $this->model->get_language( $lang ) ) {
@@ -225,40 +290,54 @@ abstract class PLL_Translated_Object {
225
  *
226
  * @since 0.1
227
  *
228
- * @param int $id post id or term id
229
- * @param int|string|object $lang language ( term_id or slug or object )
230
- * @return bool|int the translation post id or term id if exists, otherwise the post id or term id, false if the post has no language
231
  */
232
  public function get( $id, $lang ) {
233
  $id = (int) $id;
234
- $obj_lang = $this->get_language( $id ); // FIXME is this necessary?
235
- if ( ! $lang || ! $obj_lang ) {
 
236
  return false;
237
  }
238
 
239
- $lang = $this->model->get_language( $lang );
240
  return $obj_lang->term_id == $lang->term_id ? $id : $this->get_translation( $id, $lang );
241
  }
242
 
243
  /**
244
- * A where clause to add to sql queries when filtering by language is needed directly in query
245
  *
246
  * @since 1.2
247
  *
248
- * @param object|array|string $lang a PLL_Language object or a comma separated list of language slug or an array of language slugs
249
- * @return string where clause
 
 
 
 
 
 
 
 
 
 
250
  */
251
  public function where_clause( $lang ) {
252
  $tt_id = $this->tax_tt;
253
 
254
- // $lang is an object
255
- // generally the case if the query is coming from Polylang
 
 
256
  if ( is_object( $lang ) ) {
257
  return ' AND pll_tr.term_taxonomy_id = ' . absint( $lang->$tt_id );
258
  }
259
 
260
- // $lang is a comma separated list of slugs ( or an array of slugs )
261
- // generally the case is the query is coming from outside with 'lang' parameter
 
 
262
  $slugs = is_array( $lang ) ? $lang : explode( ',', $lang );
263
  $languages = array();
264
  foreach ( $slugs as $slug ) {
@@ -269,13 +348,13 @@ abstract class PLL_Translated_Object {
269
  }
270
 
271
  /**
272
- * Returns ids of objects in a language similarly to get_objects_in_term for a taxonomy
273
- * faster than get_objects_in_term as it avoids a JOIN
274
  *
275
  * @since 1.4
276
  *
277
- * @param object $lang a PLL_Language object
278
- * @return array
279
  */
280
  public function get_objects_in_language( $lang ) {
281
  global $wpdb;
@@ -300,24 +379,24 @@ abstract class PLL_Translated_Object {
300
  }
301
 
302
  /**
303
- * Check if a user can synchronize translations
304
  *
305
  * @since 2.6
306
  *
307
- * @param int $id Object id
308
  * @return bool
309
  */
310
  public function current_user_can_synchronize( $id ) {
311
  /**
312
- * Filters whether a synchronization capability check should take place
313
  *
314
  * @since 2.6
315
  *
316
- * @param $check null to enable the capability check,
317
- * true to always allow the synchronization,
318
- * false to always disallow the synchronization.
319
- * Defaults to true.
320
- * @param $id The synchronization source object id
321
  */
322
  $check = apply_filters( "pll_pre_current_user_can_synchronize_{$this->type}", true, $id );
323
  if ( null !== $check ) {
4
  */
5
 
6
  /**
7
+ * Setups the objects languages and translations model.
8
  *
9
  * @since 1.8
10
  */
11
  abstract class PLL_Translated_Object {
12
+ /**
13
+ * @var PLL_Model
14
+ */
15
  public $model;
 
16
 
17
  /**
18
+ * Object type to use when registering the taxonomies.
19
+ * Left empty for posts.
20
+ *
21
+ * @var string|null
22
+ */
23
+ protected $object_type;
24
+
25
+ /**
26
+ * Object type to use when checking capabilities.
27
+ *
28
+ * @var string
29
+ */
30
+ protected $type;
31
+
32
+ /**
33
+ * Taxonomy name for the languages.
34
+ *
35
+ * @var string
36
+ */
37
+ protected $tax_language;
38
+
39
+ /**
40
+ * Taxonomy name for the translation groups.
41
+ *
42
+ * @var string
43
+ */
44
+ protected $tax_translations;
45
+
46
+ /**
47
+ * PLL_Language property name for the term_taxonomy id.
48
+ *
49
+ * @var string
50
+ */
51
+ protected $tax_tt;
52
+
53
+ /**
54
+ * Constructor.
55
  *
56
  * @since 1.8
57
  *
58
+ * @param PLL_Model $model Instance of PLL_Model.
59
  */
60
  public function __construct( &$model ) {
61
  $this->model = &$model;
62
 
63
+ /*
64
+ * Register our taxonomies as soon as possible.
65
+ * This is early registration, not ready for rewrite rules as $wp_rewrite will be setup later.
66
+ */
67
  $args = array( 'label' => false, 'public' => false, 'query_var' => false, 'rewrite' => false, '_pll' => true );
68
  register_taxonomy( $this->tax_language, $this->object_type, $args );
69
+ $args['update_count_callback'] = '_update_generic_term_count'; // Count *all* objects to avoid deleting in clean_translations_terms.
70
  register_taxonomy( $this->tax_translations, $this->object_type, $args );
71
  }
72
 
73
  /**
74
+ * Stores the language in the database.
75
+ *
76
+ * @since 0.6
77
+ *
78
+ * @param int $id Object id.
79
+ * @param int|string|PLL_Language $lang Language (term_id or slug or object).
80
+ * @return void
81
+ */
82
+ abstract public function set_language( $id, $lang );
83
+
84
+ /**
85
+ * Returns the language of an object.
86
+ *
87
+ * @since 0.1
88
+ *
89
+ * @param int $id Object id.
90
+ * @return PLL_Language|false PLL_Language object, false if no language is associated to that object.
91
+ */
92
+ abstract public function get_language( $id );
93
+
94
+ /**
95
+ * Wrap wp_get_object_terms() to cache it and return only one object.
96
+ * inspired by the WordPress function get_the_terms().
97
  *
98
  * @since 1.2
99
  *
100
+ * @param int $object_id Object id ( typically a post_id or term_id ).
101
+ * @param string $taxonomy Polylang taxonomy depending if we are looking for a post ( or term ) language ( or translation ).
102
+ * @return WP_Term|false The term associated to the object in the requested taxonomy if it exists, false otherwise.
103
  */
104
  public function get_object_term( $object_id, $taxonomy ) {
105
  if ( empty( $object_id ) || is_wp_error( $object_id ) ) {
115
  $term = get_object_term_cache( $object_id, $taxonomy );
116
 
117
  if ( false === $term ) {
118
+ // Query language and translations at the same time.
119
  $taxonomies = array( $this->tax_language, $this->tax_translations );
120
 
121
+ // Query terms.
122
  $terms = array();
123
+ $object_terms = wp_get_object_terms( $object_id, $taxonomies, array( 'update_term_meta_cache' => false ) );
124
+ if ( is_array( $object_terms ) ) {
125
+ foreach ( $object_terms as $t ) {
126
+ $terms[ $t->taxonomy ] = $t;
127
+ if ( $t->taxonomy == $taxonomy ) {
128
+ $term = $t;
129
+ }
130
  }
131
  }
132
 
133
+ // Stores it the way WP expects it. Set an empty cache if no term was found in the taxonomy.
 
134
  foreach ( $taxonomies as $tax ) {
135
  wp_cache_add( $object_id, empty( $terms[ $tax ] ) ? array() : array( $terms[ $tax ] ), $tax . '_relationships' );
136
  }
143
  }
144
 
145
  /**
146
+ * Tells whether a translation term must be updated.
147
  *
148
  * @since 2.3
149
  *
150
+ * @param int $id Object id ( typically a post_id or term_id ).
151
+ * @param int[] $translations An associative array of translations with language code as key and translation id as value.
152
+ * @return bool
153
  */
154
  protected function should_update_translation_group( $id, $translations ) {
155
+ // Don't do anything if no translations have been added to the group.
156
+ $old_translations = $this->get_translations( $id ); // Includes at least $id itself.
157
  return count( array_diff_assoc( $translations, $old_translations ) ) > 0;
158
  }
159
 
160
  /**
161
+ * Saves translations for posts or terms.
162
  *
163
  * @since 0.5
164
  *
165
+ * @param int $id Object id ( typically a post_id or term_id ).
166
+ * @param int[] $translations An associative array of translations with language code as key and translation id as value.
167
+ * @return void
168
  */
169
  public function save_translations( $id, $translations ) {
170
  $id = (int) $id;
171
 
172
+ if ( ( $lang = $this->get_language( $id ) ) && is_array( $translations ) ) {
173
+ // Sanitize the translations array.
174
  $translations = array_map( 'intval', $translations );
175
+ $translations = array_merge( array( $lang->slug => $id ), $translations ); // Make sure this object is in translations.
176
+ $translations = array_diff( $translations, array( 0 ) ); // Don't keep non translated languages.
177
+ $translations = array_intersect_key( $translations, array_flip( $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) ); // Keep only valid languages slugs as keys.
178
 
179
+ // Unlink removed translations.
180
  $old_translations = $this->get_translations( $id );
181
  foreach ( array_diff_assoc( $old_translations, $translations ) as $object_id ) {
182
  $this->delete_translation( $object_id );
183
  }
184
 
185
+ // Check id we need to create or update the translation group.
186
  if ( $this->should_update_translation_group( $id, $translations ) ) {
187
  $terms = wp_get_object_terms( $translations, $this->tax_translations );
188
  $term = reset( $terms );
189
 
190
+ // Create a new term if necessary.
191
  if ( empty( $term ) ) {
192
  wp_insert_term( $group = uniqid( 'pll_' ), $this->tax_translations, array( 'description' => maybe_serialize( $translations ) ) );
193
+ } else {
194
+ // Take care not to overwrite extra data stored in the description field, if any.
 
195
  $d = maybe_unserialize( $term->description );
196
+ $d = is_array( $d ) ? array_diff_key( $d, $old_translations ) : array(); // Remove old translations.
197
+ $d = array_merge( $d, $translations ); // Add new one.
198
  wp_update_term( $group = (int) $term->term_id, $this->tax_translations, array( 'description' => maybe_serialize( $d ) ) );
199
  }
200
 
201
+ // Link all translations to the new term.
202
  foreach ( $translations as $p ) {
203
  wp_set_object_terms( $p, $group, $this->tax_translations );
204
  }
205
 
206
+ // Clean now unused translation groups.
207
  foreach ( wp_list_pluck( $terms, 'term_id' ) as $term_id ) {
208
  $term = get_term( $term_id, $this->tax_translations );
209
  if ( empty( $term->count ) ) {
215
  }
216
 
217
  /**
218
+ * Deletes a translation of a post or term.
219
  *
220
  * @since 0.5
221
  *
222
+ * @param int $id Object id ( typically a post_id or term_id ).
223
+ * @return void
224
  */
225
  public function delete_translation( $id ) {
226
  $id = (int) $id;
242
  }
243
 
244
  /**
245
+ * Returns an array of translations of a post or term.
246
  *
247
  * @since 0.5
248
  *
249
+ * @param int $id Object id ( typically a post_id or term_id ).
250
+ * @return int[] An associative array of translations with language code as key and translation id as value.
251
  */
252
  public function get_translations( $id ) {
253
  $term = $this->get_object_term( $id, $this->tax_translations );
254
  $translations = empty( $term ) ? array() : maybe_unserialize( $term->description );
255
 
256
+ // Make sure we return only translations ( thus we allow plugins to store other information in the array ).
257
  if ( is_array( $translations ) ) {
258
  $translations = array_intersect_key( $translations, array_flip( $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) );
259
  }
260
 
261
+ // Make sure to return at least the passed object in its translation array.
262
  if ( empty( $translations ) && $lang = $this->get_language( $id ) ) {
263
  $translations = array( $lang->slug => $id );
264
  }
267
  }
268
 
269
  /**
270
+ * Returns the id of the translation of a post or term.
271
  *
272
  * @since 0.5
273
  *
274
+ * @param int $id Object id ( typically a post_id or term_id ).
275
+ * @param PLL_Language|string $lang Language ( slug or object ).
276
+ * @return int|false Object id of the translation, false if there is none.
277
  */
278
  public function get_translation( $id, $lang ) {
279
  if ( ! $lang = $this->model->get_language( $lang ) ) {
290
  *
291
  * @since 0.1
292
  *
293
+ * @param int $id Object id ( typically a post_id or term_id ).
294
+ * @param int|string|PLL_Language $lang Language ( term_id or slug or object ).
295
+ * @return int|false The translation object id if exists, otherwise the passed id, false if the passed object has no language.
296
  */
297
  public function get( $id, $lang ) {
298
  $id = (int) $id;
299
+ $lang = $this->model->get_language( $lang );
300
+ $obj_lang = $this->get_language( $id );
301
+ if ( empty( $lang ) || empty( $obj_lang ) ) {
302
  return false;
303
  }
304
 
 
305
  return $obj_lang->term_id == $lang->term_id ? $id : $this->get_translation( $id, $lang );
306
  }
307
 
308
  /**
309
+ * A join clause to add to sql queries when filtering by language is needed directly in query.
310
  *
311
  * @since 1.2
312
  *
313
+ * @param string $alias Optional alias for object table.
314
+ * @return string Join clause.
315
+ */
316
+ abstract public function join_clause( $alias = '' );
317
+
318
+ /**
319
+ * A where clause to add to sql queries when filtering by language is needed directly in query.
320
+ *
321
+ * @since 1.2
322
+ *
323
+ * @param PLL_Language|string|string[] $lang PLL_Language object or a comma separated list of language slug or an array of language slugs.
324
+ * @return string Where clause.
325
  */
326
  public function where_clause( $lang ) {
327
  $tt_id = $this->tax_tt;
328
 
329
+ /*
330
+ * $lang is an object.
331
+ * This is generally the case if the query is coming from Polylang.
332
+ */
333
  if ( is_object( $lang ) ) {
334
  return ' AND pll_tr.term_taxonomy_id = ' . absint( $lang->$tt_id );
335
  }
336
 
337
+ /*
338
+ * $lang is a comma separated list of slugs ( or an array of slugs ).
339
+ * This is generally the case is the query is coming from outside with a 'lang' parameter.
340
+ */
341
  $slugs = is_array( $lang ) ? $lang : explode( ',', $lang );
342
  $languages = array();
343
  foreach ( $slugs as $slug ) {
348
  }
349
 
350
  /**
351
+ * Returns ids of objects in a language similarly to get_objects_in_term() for a taxonomy.
352
+ * It is faster than get_objects_in_term() as it avoids a JOIN.
353
  *
354
  * @since 1.4
355
  *
356
+ * @param PLL_Language $lang PLL_Language object.
357
+ * @return int[] Object ids.
358
  */
359
  public function get_objects_in_language( $lang ) {
360
  global $wpdb;
379
  }
380
 
381
  /**
382
+ * Check if a user can synchronize translations.
383
  *
384
  * @since 2.6
385
  *
386
+ * @param int $id Object id.
387
  * @return bool
388
  */
389
  public function current_user_can_synchronize( $id ) {
390
  /**
391
+ * Filters whether a synchronization capability check should take place.
392
  *
393
  * @since 2.6
394
  *
395
+ * @param bool|null $check Null to enable the capability check,
396
+ * true to always allow the synchronization,
397
+ * false to always disallow the synchronization.
398
+ * Defaults to true.
399
+ * @param int $id The synchronization source object id.
400
  */
401
  $check = apply_filters( "pll_pre_current_user_can_synchronize_{$this->type}", true, $id );
402
  if ( null !== $check ) {
include/translated-post.php CHANGED
@@ -11,11 +11,11 @@
11
  class PLL_Translated_Post extends PLL_Translated_Object {
12
 
13
  /**
14
- * Constructor
15
  *
16
  * @since 1.8
17
  *
18
- * @param object $model
19
  */
20
  public function __construct( &$model ) {
21
  // init properties
@@ -38,17 +38,20 @@ class PLL_Translated_Post extends PLL_Translated_Object {
38
  }
39
 
40
  /**
41
- * Store the post language in the database
42
  *
43
  * @since 0.6
44
  *
45
- * @param int $post_id post id
46
- * @param int|string|object $lang language ( term_id or slug or object )
 
47
  */
48
  public function set_language( $post_id, $lang ) {
49
  $old_lang = $this->get_language( $post_id );
50
  $old_lang = $old_lang ? $old_lang->slug : '';
51
- $lang = $lang ? $this->model->get_language( $lang )->slug : '';
 
 
52
 
53
  if ( $old_lang !== $lang ) {
54
  wp_set_post_terms( (int) $post_id, $lang, 'language' );
@@ -61,7 +64,7 @@ class PLL_Translated_Post extends PLL_Translated_Object {
61
  * @since 0.1
62
  *
63
  * @param int $post_id post id
64
- * @return bool|object PLL_Language object, false if no language is associated to that post
65
  */
66
  public function get_language( $post_id ) {
67
  $lang = $this->get_object_term( $post_id, 'language' );
@@ -69,15 +72,16 @@ class PLL_Translated_Post extends PLL_Translated_Object {
69
  }
70
 
71
  /**
72
- * Deletes a translation
73
  *
74
  * @since 0.5
75
  *
76
- * @param int $id post id
 
77
  */
78
  public function delete_translation( $id ) {
79
  parent::delete_translation( $id );
80
- wp_set_object_terms( $id, null, $this->tax_translations );
81
  }
82
 
83
  /**
@@ -100,6 +104,8 @@ class PLL_Translated_Post extends PLL_Translated_Object {
100
  * Register the language taxonomy
101
  *
102
  * @since 1.2
 
 
103
  */
104
  public function register_taxonomy() {
105
  register_taxonomy(
@@ -128,6 +134,7 @@ class PLL_Translated_Post extends PLL_Translated_Object {
128
  * @since 1.2
129
  *
130
  * @param string $post_type post type name
 
131
  */
132
  public function registered_post_type( $post_type ) {
133
  if ( $this->model->is_translated_post_type( $post_type ) ) {
@@ -137,13 +144,14 @@ class PLL_Translated_Post extends PLL_Translated_Object {
137
  }
138
 
139
  /**
140
- * Forces calling 'update_object_term_cache' when querying posts or pages
141
- * this is especially useful for nav menus with a lot of pages
142
- * without doing this, we would have one query per page in the menu to get the page language for the permalink
143
  *
144
  * @since 1.8
145
  *
146
- * @param object $query reference to the query object
 
147
  */
148
  public function pre_get_posts( $query ) {
149
  if ( ! empty( $query->query['post_type'] ) && $this->model->is_translated_post_type( $query->query['post_type'] ) ) {
@@ -169,6 +177,10 @@ class PLL_Translated_Post extends PLL_Translated_Object {
169
 
170
  if ( 'inherit' === $post->post_status && $post->post_parent ) {
171
  $post = get_post( $post->post_parent );
 
 
 
 
172
  }
173
 
174
  if ( 'inherit' === $post->post_status || in_array( $post->post_status, get_post_stati( array( 'public' => true ) ) ) ) {
@@ -202,15 +214,15 @@ class PLL_Translated_Post extends PLL_Translated_Object {
202
 
203
  /**
204
  * Returns a list of posts in a language ( $lang )
205
- * not translated in another language ( $untranslated_in )
206
  *
207
  * @since 2.6
208
  *
209
- * @param string $type Post type
210
- * @param string $untranslated_in The posts must not be translated in this language
211
- * @param string $lang Language of the search posts
212
- * @param string $search Limit results to posts matching this string
213
- * @return array Array of posts
214
  */
215
  public function get_untranslated( $type, $untranslated_in, $lang, $search = '' ) {
216
  $return = array();
@@ -246,7 +258,7 @@ class PLL_Translated_Post extends PLL_Translated_Object {
246
  $posts = get_posts( $args );
247
 
248
  foreach ( $posts as $post ) {
249
- if ( ! $this->get_translation( $post->ID, $untranslated_in ) && $this->current_user_can_read( $post->ID, 'edit' ) ) {
250
  $return[] = $post;
251
  }
252
  }
11
  class PLL_Translated_Post extends PLL_Translated_Object {
12
 
13
  /**
14
+ * Constructor.
15
  *
16
  * @since 1.8
17
  *
18
+ * @param PLL_Model $model PLL_Model instance.
19
  */
20
  public function __construct( &$model ) {
21
  // init properties
38
  }
39
 
40
  /**
41
+ * Store the post language in the database.
42
  *
43
  * @since 0.6
44
  *
45
+ * @param int $post_id Post id.
46
+ * @param int|string|PLL_Language $lang Language (term_id or slug or object).
47
+ * @return void
48
  */
49
  public function set_language( $post_id, $lang ) {
50
  $old_lang = $this->get_language( $post_id );
51
  $old_lang = $old_lang ? $old_lang->slug : '';
52
+
53
+ $lang = $this->model->get_language( $lang );
54
+ $lang = $lang ? $lang->slug : '';
55
 
56
  if ( $old_lang !== $lang ) {
57
  wp_set_post_terms( (int) $post_id, $lang, 'language' );
64
  * @since 0.1
65
  *
66
  * @param int $post_id post id
67
+ * @return PLL_Language|false PLL_Language object, false if no language is associated to that post
68
  */
69
  public function get_language( $post_id ) {
70
  $lang = $this->get_object_term( $post_id, 'language' );
72
  }
73
 
74
  /**
75
+ * Deletes a translation.
76
  *
77
  * @since 0.5
78
  *
79
+ * @param int $id Post id.
80
+ * @return void
81
  */
82
  public function delete_translation( $id ) {
83
  parent::delete_translation( $id );
84
+ wp_set_object_terms( $id, array(), $this->tax_translations );
85
  }
86
 
87
  /**
104
  * Register the language taxonomy
105
  *
106
  * @since 1.2
107
+ *
108
+ * @return void
109
  */
110
  public function register_taxonomy() {
111
  register_taxonomy(
134
  * @since 1.2
135
  *
136
  * @param string $post_type post type name
137
+ * @return void
138
  */
139
  public function registered_post_type( $post_type ) {
140
  if ( $this->model->is_translated_post_type( $post_type ) ) {
144
  }
145
 
146
  /**
147
+ * Forces calling 'update_object_term_cache' when querying posts or pages.
148
+ * This is especially useful for nav menus with a lot of pages as, without doing this,
149
+ * we would have one query per page in the menu to get the page language for the permalink.
150
  *
151
  * @since 1.8
152
  *
153
+ * @param WP_Query $query Reference to the query object.
154
+ * @return void
155
  */
156
  public function pre_get_posts( $query ) {
157
  if ( ! empty( $query->query['post_type'] ) && $this->model->is_translated_post_type( $query->query['post_type'] ) ) {
177
 
178
  if ( 'inherit' === $post->post_status && $post->post_parent ) {
179
  $post = get_post( $post->post_parent );
180
+
181
+ if ( empty( $post ) ) {
182
+ return false;
183
+ }
184
  }
185
 
186
  if ( 'inherit' === $post->post_status || in_array( $post->post_status, get_post_stati( array( 'public' => true ) ) ) ) {
214
 
215
  /**
216
  * Returns a list of posts in a language ( $lang )
217
+ * not translated in another language ( $untranslated_in ).
218
  *
219
  * @since 2.6
220
  *
221
+ * @param string $type Post type.
222
+ * @param PLL_Language $untranslated_in The language the posts must not be translated in.
223
+ * @param PLL_Language $lang Language of the searched posts.
224
+ * @param string $search Limit the results to the posts matching this string.
225
+ * @return WP_Post[] Array of posts.
226
  */
227
  public function get_untranslated( $type, $untranslated_in, $lang, $search = '' ) {
228
  $return = array();
258
  $posts = get_posts( $args );
259
 
260
  foreach ( $posts as $post ) {
261
+ if ( $post instanceof WP_Post && ! $this->get_translation( $post->ID, $untranslated_in ) && $this->current_user_can_read( $post->ID, 'edit' ) ) {
262
  $return[] = $post;
263
  }
264
  }
include/translated-term.php CHANGED
@@ -28,25 +28,28 @@ class PLL_Translated_Term extends PLL_Translated_Object {
28
 
29
  // Filters to prime terms cache
30
  add_filter( 'get_terms', array( $this, '_prime_terms_cache' ), 10, 2 );
31
- add_filter( 'wp_get_object_terms', array( $this, 'wp_get_object_terms' ), 10, 3 );
32
 
33
  add_action( 'clean_term_cache', array( $this, 'clean_term_cache' ) );
34
  }
35
 
36
  /**
37
- * Stores the term language in the database
38
  *
39
  * @since 0.6
40
  *
41
- * @param int $term_id term id
42
- * @param int|string|object $lang language ( term_id or slug or object )
 
43
  */
44
  public function set_language( $term_id, $lang ) {
45
  $term_id = (int) $term_id;
46
 
47
  $old_lang = $this->get_language( $term_id );
48
  $old_lang = $old_lang ? $old_lang->tl_term_id : '';
49
- $lang = $lang ? $this->model->get_language( $lang )->tl_term_id : '';
 
 
50
 
51
  if ( $old_lang !== $lang ) {
52
  wp_set_object_terms( $term_id, $lang, 'term_language' );
@@ -67,6 +70,7 @@ class PLL_Translated_Term extends PLL_Translated_Object {
67
  * @since 0.5
68
  *
69
  * @param int $term_id term id
 
70
  */
71
  public function delete_language( $term_id ) {
72
  wp_delete_object_term_relationships( $term_id, 'term_language' );
@@ -79,7 +83,7 @@ class PLL_Translated_Term extends PLL_Translated_Object {
79
  *
80
  * @param int|string $value term id or term slug
81
  * @param string $taxonomy optional taxonomy needed when the term slug is passed as first parameter
82
- * @return bool|object PLL_Language object, false if no language is associated to that term
83
  */
84
  public function get_language( $value, $taxonomy = '' ) {
85
  if ( is_numeric( $value ) ) {
@@ -88,7 +92,10 @@ class PLL_Translated_Term extends PLL_Translated_Object {
88
 
89
  // get_term_by still not cached in WP 3.5.1 but internally, the function is always called by term_id
90
  elseif ( is_string( $value ) && $taxonomy ) {
91
- $term_id = get_term_by( 'slug', $value, $taxonomy )->term_id;
 
 
 
92
  }
93
 
94
  // Get the language and make sure it is a PLL_Language object
@@ -96,12 +103,13 @@ class PLL_Translated_Term extends PLL_Translated_Object {
96
  }
97
 
98
  /**
99
- * Tells whether a translation term must updated
100
  *
101
  * @since 2.3
102
  *
103
- * @param array $id Post id or term id
104
- * @param array $translations An associative array of translations with language code as key and translation id as value
 
105
  */
106
  protected function should_update_translation_group( $id, $translations ) {
107
  // Don't do anything if no translations have been added to the group
@@ -121,6 +129,7 @@ class PLL_Translated_Term extends PLL_Translated_Object {
121
  * @since 0.5
122
  *
123
  * @param int $id term id
 
124
  */
125
  public function delete_translation( $id ) {
126
  global $wpdb;
@@ -152,13 +161,13 @@ class PLL_Translated_Term extends PLL_Translated_Object {
152
  }
153
 
154
  /**
155
- * Cache language and translations when terms are queried by get_terms
156
  *
157
  * @since 1.2
158
  *
159
- * @param array $terms queried terms
160
- * @param array $taxonomies queried taxonomies
161
- * @return array unmodified $terms
162
  */
163
  public function _prime_terms_cache( $terms, $taxonomies ) {
164
  $term_ids = array();
@@ -176,17 +185,16 @@ class PLL_Translated_Term extends PLL_Translated_Object {
176
  }
177
 
178
  /**
179
- * When terms are found for posts, add their language and translations to cache
180
  *
181
  * @since 1.2
182
  *
183
- * @param array $terms terms found
184
- * @param array $object_ids not used
185
- * @param array $taxonomies terms taxonomies
186
- * @return array unmodified $terms
187
  */
188
  public function wp_get_object_terms( $terms, $object_ids, $taxonomies ) {
189
- $taxonomies = explode( "', '", trim( $taxonomies, "'" ) );
190
  if ( ! in_array( 'term_translations', $taxonomies ) ) {
191
  $this->_prime_terms_cache( $terms, $taxonomies );
192
  }
@@ -194,11 +202,12 @@ class PLL_Translated_Term extends PLL_Translated_Object {
194
  }
195
 
196
  /**
197
- * When the term cache is cleaned, clean the object term cache too
198
  *
199
  * @since 2.0
200
  *
201
- * @param array $ids An array of term IDs.
 
202
  */
203
  public function clean_term_cache( $ids ) {
204
  clean_object_term_cache( $ids, 'term' );
28
 
29
  // Filters to prime terms cache
30
  add_filter( 'get_terms', array( $this, '_prime_terms_cache' ), 10, 2 );
31
+ add_filter( 'get_object_terms', array( $this, 'wp_get_object_terms' ), 10, 3 );
32
 
33
  add_action( 'clean_term_cache', array( $this, 'clean_term_cache' ) );
34
  }
35
 
36
  /**
37
+ * Stores the term language in the database.
38
  *
39
  * @since 0.6
40
  *
41
+ * @param int $term_id Term id.
42
+ * @param int|string|PLL_Language $lang Language (term_id or slug or object).
43
+ * @return void
44
  */
45
  public function set_language( $term_id, $lang ) {
46
  $term_id = (int) $term_id;
47
 
48
  $old_lang = $this->get_language( $term_id );
49
  $old_lang = $old_lang ? $old_lang->tl_term_id : '';
50
+
51
+ $lang = $this->model->get_language( $lang );
52
+ $lang = $lang ? $lang->tl_term_id : '';
53
 
54
  if ( $old_lang !== $lang ) {
55
  wp_set_object_terms( $term_id, $lang, 'term_language' );
70
  * @since 0.5
71
  *
72
  * @param int $term_id term id
73
+ * @return void
74
  */
75
  public function delete_language( $term_id ) {
76
  wp_delete_object_term_relationships( $term_id, 'term_language' );
83
  *
84
  * @param int|string $value term id or term slug
85
  * @param string $taxonomy optional taxonomy needed when the term slug is passed as first parameter
86
+ * @return PLL_Language|false PLL_Language object, false if no language is associated to that term
87
  */
88
  public function get_language( $value, $taxonomy = '' ) {
89
  if ( is_numeric( $value ) ) {
92
 
93
  // get_term_by still not cached in WP 3.5.1 but internally, the function is always called by term_id
94
  elseif ( is_string( $value ) && $taxonomy ) {
95
+ $term = get_term_by( 'slug', $value, $taxonomy );
96
+ if ( $term instanceof WP_Term ) {
97
+ $term_id = $term->term_id;
98
+ }
99
  }
100
 
101
  // Get the language and make sure it is a PLL_Language object
103
  }
104
 
105
  /**
106
+ * Tells whether a translation term must updated.
107
  *
108
  * @since 2.3
109
  *
110
+ * @param int $id Post id or term id.
111
+ * @param int[] $translations An associative array of translations with language code as key and translation id as value.
112
+ * @return bool
113
  */
114
  protected function should_update_translation_group( $id, $translations ) {
115
  // Don't do anything if no translations have been added to the group
129
  * @since 0.5
130
  *
131
  * @param int $id term id
132
+ * @return void
133
  */
134
  public function delete_translation( $id ) {
135
  global $wpdb;
161
  }
162
 
163
  /**
164
+ * Caches the language and translations when terms are queried by get_terms().
165
  *
166
  * @since 1.2
167
  *
168
+ * @param WP_Term[]|int[] $terms Queried terms.
169
+ * @param string[] $taxonomies Queried taxonomies.
170
+ * @return WP_Term[]|int[] Unmodified $terms.
171
  */
172
  public function _prime_terms_cache( $terms, $taxonomies ) {
173
  $term_ids = array();
185
  }
186
 
187
  /**
188
+ * When terms are found for posts, add their language and translations to cache.
189
  *
190
  * @since 1.2
191
  *
192
+ * @param WP_Term[] $terms Array of terms for the given object or objects.
193
+ * @param int[] $object_ids Array of object IDs for which terms were retrieved.
194
+ * @param string[] $taxonomies Array of taxonomy names from which terms were retrieved.
195
+ * @return WP_Term[] Unmodified $terms.
196
  */
197
  public function wp_get_object_terms( $terms, $object_ids, $taxonomies ) {
 
198
  if ( ! in_array( 'term_translations', $taxonomies ) ) {
199
  $this->_prime_terms_cache( $terms, $taxonomies );
200
  }
202
  }
203
 
204
  /**
205
+ * When the term cache is cleaned, cleans the object term cache too.
206
  *
207
  * @since 2.0
208
  *
209
+ * @param int[] $ids An array of term IDs.
210
+ * @return void
211
  */
212
  public function clean_term_cache( $ids ) {
213
  clean_object_term_cache( $ids, 'term' );
include/walker-dropdown.php CHANGED
@@ -9,6 +9,13 @@
9
  * @since 1.2
10
  */
11
  class PLL_Walker_Dropdown extends Walker {
 
 
 
 
 
 
 
12
  public $db_fields = array( 'parent' => 'parent', 'id' => 'id' );
13
 
14
  /**
@@ -16,11 +23,12 @@ class PLL_Walker_Dropdown extends Walker {
16
  *
17
  * @since 1.2
18
  *
19
- * @param string $output Passed by reference. Used to append additional content.
20
- * @param object $element The data object.
21
- * @param int $depth Depth of the item.
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'];
@@ -38,12 +46,13 @@ class PLL_Walker_Dropdown extends Walker {
38
  *
39
  * @since 1.2
40
  *
41
- * @param object $element Data object.
42
- * @param array $children_elements List of elements to continue traversing.
43
- * @param int $max_depth Max depth to traverse.
44
- * @param int $depth Depth of current element.
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
@@ -75,7 +84,7 @@ class PLL_Walker_Dropdown extends Walker {
75
  public function walk( $elements, $max_depth, ...$args ) { // // phpcs:ignore WordPressVIPMinimum.Classes.DeclarationCompatibility.DeclarationCompatibility
76
  $output = '';
77
 
78
- if ( is_array( $max_depth ) ) {
79
  // Backward compatibility with Polylang < 2.6.7
80
  if ( WP_DEBUG ) {
81
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
9
  * @since 1.2
10
  */
11
  class PLL_Walker_Dropdown extends Walker {
12
+ /**
13
+ * Database fields to use.
14
+ *
15
+ * @see https://developer.wordpress.org/reference/classes/walker/#properties Walker::$db_fields.
16
+ *
17
+ * @var array
18
+ */
19
  public $db_fields = array( 'parent' => 'parent', 'id' => 'id' );
20
 
21
  /**
23
  *
24
  * @since 1.2
25
  *
26
+ * @param string $output Passed by reference. Used to append additional content.
27
+ * @param stdClass $element The data object.
28
+ * @param int $depth Depth of the item.
29
+ * @param array $args An array of additional arguments.
30
+ * @param int $current_object_id ID of the current item.
31
+ * @return void
32
  */
33
  public function start_el( &$output, $element, $depth = 0, $args = array(), $current_object_id = 0 ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
34
  $value = $args['value'];
46
  *
47
  * @since 1.2
48
  *
49
+ * @param stdClass $element Data object.
50
+ * @param array $children_elements List of elements to continue traversing.
51
+ * @param int $max_depth Max depth to traverse.
52
+ * @param int $depth Depth of current element.
53
+ * @param array $args An array of arguments.
54
+ * @param string $output Passed by reference. Used to append additional content.
55
+ * @return void
56
  */
57
  public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
58
  $element = (object) $element; // Make sure we have an object
84
  public function walk( $elements, $max_depth, ...$args ) { // // phpcs:ignore WordPressVIPMinimum.Classes.DeclarationCompatibility.DeclarationCompatibility
85
  $output = '';
86
 
87
+ if ( is_array( $max_depth ) ) { // @phpstan-ignore-line
88
  // Backward compatibility with Polylang < 2.6.7
89
  if ( WP_DEBUG ) {
90
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
include/walker-list.php CHANGED
@@ -9,6 +9,13 @@
9
  * @since 1.2
10
  */
11
  class PLL_Walker_List extends Walker {
 
 
 
 
 
 
 
12
  public $db_fields = array( 'parent' => 'parent', 'id' => 'id' );
13
 
14
  /**
@@ -16,11 +23,12 @@ class PLL_Walker_List extends Walker {
16
  *
17
  * @since 1.2
18
  *
19
- * @param string $output Passed by reference. Used to append additional content.
20
- * @param object $element The data object.
21
- * @param int $depth Depth of the item.
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(
@@ -40,12 +48,13 @@ class PLL_Walker_List extends Walker {
40
  *
41
  * @since 1.2
42
  *
43
- * @param object $element Data object.
44
- * @param array $children_elements List of elements to continue traversing.
45
- * @param int $max_depth Max depth to traverse.
46
- * @param int $depth Depth of current element.
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
@@ -65,7 +74,7 @@ class PLL_Walker_List extends Walker {
65
  * @return string The hierarchical item output.
66
  */
67
  public function walk( $elements, $max_depth, ...$args ) { // phpcs:ignore WordPressVIPMinimum.Classes.DeclarationCompatibility.DeclarationCompatibility
68
- if ( is_array( $max_depth ) ) {
69
  // Backward compatibility with Polylang < 2.6.7
70
  if ( WP_DEBUG ) {
71
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
9
  * @since 1.2
10
  */
11
  class PLL_Walker_List extends Walker {
12
+ /**
13
+ * Database fields to use.
14
+ *
15
+ * @see https://developer.wordpress.org/reference/classes/walker/#properties Walker::$db_fields.
16
+ *
17
+ * @var array
18
+ */
19
  public $db_fields = array( 'parent' => 'parent', 'id' => 'id' );
20
 
21
  /**
23
  *
24
  * @since 1.2
25
  *
26
+ * @param string $output Passed by reference. Used to append additional content.
27
+ * @param stdClass $element The data object.
28
+ * @param int $depth Depth of the item.
29
+ * @param array $args An array of additional arguments.
30
+ * @param int $current_object_id ID of the current item.
31
+ * @return void
32
  */
33
  public function start_el( &$output, $element, $depth = 0, $args = array(), $current_object_id = 0 ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
34
  $output .= sprintf(
48
  *
49
  * @since 1.2
50
  *
51
+ * @param stdClass $element Data object.
52
+ * @param array $children_elements List of elements to continue traversing.
53
+ * @param int $max_depth Max depth to traverse.
54
+ * @param int $depth Depth of current element.
55
+ * @param array $args An array of arguments.
56
+ * @param string $output Passed by reference. Used to append additional content.
57
+ * @return void
58
  */
59
  public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
60
  $element = (object) $element; // Make sure we have an object
74
  * @return string The hierarchical item output.
75
  */
76
  public function walk( $elements, $max_depth, ...$args ) { // phpcs:ignore WordPressVIPMinimum.Classes.DeclarationCompatibility.DeclarationCompatibility
77
+ if ( is_array( $max_depth ) ) { // @phpstan-ignore-line
78
  // Backward compatibility with Polylang < 2.6.7
79
  if ( WP_DEBUG ) {
80
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
include/widget-languages.php CHANGED
@@ -33,6 +33,7 @@ class PLL_Widget_Languages extends WP_Widget {
33
  *
34
  * @param array $args Display arguments including before_title, after_title, before_widget, and after_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.
@@ -99,11 +100,12 @@ class PLL_Widget_Languages extends WP_Widget {
99
  }
100
 
101
  /**
102
- * Displays the widget form
103
  *
104
  * @since 0.4
105
  *
106
- * @param array $instance Current settings
 
107
  */
108
  public function form( $instance ) {
109
  // Default values
@@ -130,5 +132,7 @@ class PLL_Widget_Languages extends WP_Widget {
130
  esc_attr( 'pll-' . $key )
131
  );
132
  }
 
 
133
  }
134
  }
33
  *
34
  * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
35
  * @param array $instance The settings for the particular instance of the widget
36
+ * @return void
37
  */
38
  public function widget( $args, $instance ) {
39
  // Sets a unique id for dropdown.
100
  }
101
 
102
  /**
103
+ * Displays the widget form.
104
  *
105
  * @since 0.4
106
  *
107
+ * @param array $instance Current settings.
108
+ * @return string
109
  */
110
  public function form( $instance ) {
111
  // Default values
132
  esc_attr( 'pll-' . $key )
133
  );
134
  }
135
+
136
+ return ''; // Because the parent class returns a string, however not used.
137
  }
138
  }
install/install-base.php CHANGED
@@ -9,6 +9,11 @@
9
  * @since 1.7
10
  */
11
  class PLL_Install_Base {
 
 
 
 
 
12
  protected $plugin_basename;
13
 
14
  /**
@@ -47,6 +52,7 @@ class PLL_Install_Base {
47
  *
48
  * @param string $what Either 'activate' or 'deactivate'
49
  * @param bool $networkwide
 
50
  */
51
  protected function do_for_all_blogs( $what, $networkwide ) {
52
  // Network
@@ -72,6 +78,7 @@ class PLL_Install_Base {
72
  * @since 1.7
73
  *
74
  * @param bool $networkwide
 
75
  */
76
  public function activate( $networkwide ) {
77
  $this->do_for_all_blogs( 'activate', $networkwide );
@@ -81,6 +88,8 @@ class PLL_Install_Base {
81
  * Plugin activation
82
  *
83
  * @since 0.5
 
 
84
  */
85
  protected function _activate() {
86
  // Can be overriden in child class
@@ -92,6 +101,7 @@ class PLL_Install_Base {
92
  * @since 0.1
93
  *
94
  * @param bool $networkwide
 
95
  */
96
  public function deactivate( $networkwide ) {
97
  $this->do_for_all_blogs( 'deactivate', $networkwide );
@@ -101,6 +111,8 @@ class PLL_Install_Base {
101
  * Plugin deactivation
102
  *
103
  * @since 0.5
 
 
104
  */
105
  protected function _deactivate() {
106
  // Can be overriden in child class
@@ -112,6 +124,7 @@ class PLL_Install_Base {
112
  * @since 2.6.8
113
  *
114
  * @param WP_Site $new_site New site object.
 
115
  */
116
  public function new_site( $new_site ) {
117
  switch_to_blog( $new_site->id );
9
  * @since 1.7
10
  */
11
  class PLL_Install_Base {
12
+ /**
13
+ * The plugin basename.
14
+ *
15
+ * @var string
16
+ */
17
  protected $plugin_basename;
18
 
19
  /**
52
  *
53
  * @param string $what Either 'activate' or 'deactivate'
54
  * @param bool $networkwide
55
+ * @return void
56
  */
57
  protected function do_for_all_blogs( $what, $networkwide ) {
58
  // Network
78
  * @since 1.7
79
  *
80
  * @param bool $networkwide
81
+ * @return void
82
  */
83
  public function activate( $networkwide ) {
84
  $this->do_for_all_blogs( 'activate', $networkwide );
88
  * Plugin activation
89
  *
90
  * @since 0.5
91
+ *
92
+ * @return void
93
  */
94
  protected function _activate() {
95
  // Can be overriden in child class
101
  * @since 0.1
102
  *
103
  * @param bool $networkwide
104
+ * @return void
105
  */
106
  public function deactivate( $networkwide ) {
107
  $this->do_for_all_blogs( 'deactivate', $networkwide );
111
  * Plugin deactivation
112
  *
113
  * @since 0.5
114
+ *
115
+ * @return void
116
  */
117
  protected function _deactivate() {
118
  // Can be overriden in child class
124
  * @since 2.6.8
125
  *
126
  * @param WP_Site $new_site New site object.
127
+ * @return void
128
  */
129
  public function new_site( $new_site ) {
130
  switch_to_blog( $new_site->id );
install/install.php CHANGED
@@ -14,6 +14,8 @@ class PLL_Install extends PLL_Install_Base {
14
  * Checks min PHP and WP version, displays a notice if a requirement is not met.
15
  *
16
  * @since 2.6.7
 
 
17
  */
18
  public function can_activate() {
19
  global $wp_version;
@@ -35,6 +37,8 @@ class PLL_Install extends PLL_Install_Base {
35
  * Displays a notice if PHP min version is not met.
36
  *
37
  * @since 2.6.7
 
 
38
  */
39
  public function php_version_notice() {
40
  load_plugin_textdomain( 'polylang' ); // Plugin i18n.
@@ -55,6 +59,8 @@ class PLL_Install extends PLL_Install_Base {
55
  * Displays a notice if WP min version is not met.
56
  *
57
  * @since 2.6.7
 
 
58
  */
59
  public function wp_version_notice() {
60
  global $wp_version;
@@ -78,7 +84,7 @@ class PLL_Install extends PLL_Install_Base {
78
  *
79
  * @since 1.8
80
  *
81
- * return array
82
  */
83
  public static function get_default_options() {
84
  return array(
@@ -102,6 +108,8 @@ class PLL_Install extends PLL_Install_Base {
102
  * Plugin activation
103
  *
104
  * @since 0.5
 
 
105
  */
106
  protected function _activate() {
107
  if ( $options = get_option( 'polylang' ) ) {
@@ -131,6 +139,8 @@ class PLL_Install extends PLL_Install_Base {
131
  * Plugin deactivation
132
  *
133
  * @since 0.5
 
 
134
  */
135
  protected function _deactivate() {
136
  delete_option( 'rewrite_rules' ); // Don't use flush_rewrite_rules at network activation. See #32471
14
  * Checks min PHP and WP version, displays a notice if a requirement is not met.
15
  *
16
  * @since 2.6.7
17
+ *
18
+ * @return bool
19
  */
20
  public function can_activate() {
21
  global $wp_version;
37
  * Displays a notice if PHP min version is not met.
38
  *
39
  * @since 2.6.7
40
+ *
41
+ * @return void
42
  */
43
  public function php_version_notice() {
44
  load_plugin_textdomain( 'polylang' ); // Plugin i18n.
59
  * Displays a notice if WP min version is not met.
60
  *
61
  * @since 2.6.7
62
+ *
63
+ * @return void
64
  */
65
  public function wp_version_notice() {
66
  global $wp_version;
84
  *
85
  * @since 1.8
86
  *
87
+ * @return array
88
  */
89
  public static function get_default_options() {
90
  return array(
108
  * Plugin activation
109
  *
110
  * @since 0.5
111
+ *
112
+ * @return void
113
  */
114
  protected function _activate() {
115
  if ( $options = get_option( 'polylang' ) ) {
139
  * Plugin deactivation
140
  *
141
  * @since 0.5
142
+ *
143
+ * @return void
144
  */
145
  protected function _deactivate() {
146
  delete_option( 'rewrite_rules' ); // Don't use flush_rewrite_rules at network activation. See #32471
install/t15s.php CHANGED
@@ -89,6 +89,7 @@ class PLL_T15S {
89
  * @since 2.6
90
  *
91
  * @param bool|array $value The transient value.
 
92
  */
93
  public function site_transient_update_plugins( $value ) {
94
  if ( ! $value ) {
@@ -132,6 +133,8 @@ class PLL_T15S {
132
  * Registers actions for clearing translation caches.
133
  *
134
  * @since 2.6
 
 
135
  */
136
  public static function register_clean_translations_cache() {
137
  add_action( 'set_site_transient_update_plugins', array( __CLASS__, 'clean_translations_cache' ) );
@@ -142,6 +145,8 @@ class PLL_T15S {
142
  * Clears existing translation cache.
143
  *
144
  * @since 2.6
 
 
145
  */
146
  public static function clean_translations_cache() {
147
  $translations = get_site_transient( self::TRANSIENT_KEY_PLUGIN );
89
  * @since 2.6
90
  *
91
  * @param bool|array $value The transient value.
92
+ * @return bool|array
93
  */
94
  public function site_transient_update_plugins( $value ) {
95
  if ( ! $value ) {
133
  * Registers actions for clearing translation caches.
134
  *
135
  * @since 2.6
136
+ *
137
+ * @return void
138
  */
139
  public static function register_clean_translations_cache() {
140
  add_action( 'set_site_transient_update_plugins', array( __CLASS__, 'clean_translations_cache' ) );
145
  * Clears existing translation cache.
146
  *
147
  * @since 2.6
148
+ *
149
+ * @return void
150
  */
151
  public static function clean_translations_cache() {
152
  $translations = get_site_transient( self::TRANSIENT_KEY_PLUGIN );
install/upgrade.php CHANGED
@@ -9,6 +9,11 @@
9
  * @since 1.2
10
  */
11
  class PLL_Upgrade {
 
 
 
 
 
12
  public $options;
13
 
14
  /**
@@ -26,6 +31,8 @@ class PLL_Upgrade {
26
  * Check if upgrade is possible otherwise die to avoid activation
27
  *
28
  * @since 1.2
 
 
29
  */
30
  public function can_activate() {
31
  if ( ! $this->can_upgrade() ) {
@@ -63,14 +70,16 @@ class PLL_Upgrade {
63
  * @return bool true if upgrade is possible, false otherwise
64
  */
65
  public function can_upgrade() {
66
- // Don't manage upgrade from version < 0.8
67
- return version_compare( $this->options['version'], '0.8', '>=' );
68
  }
69
 
70
  /**
71
  * Displays a notice when ugrading from a too old version
72
  *
73
  * @since 1.0
 
 
74
  */
75
  public function admin_notices() {
76
  load_plugin_textdomain( 'polylang' );
@@ -80,7 +89,7 @@ class PLL_Upgrade {
80
  sprintf(
81
  /* translators: %1$s and %2$s are Polylang version numbers */
82
  esc_html__( 'Before upgrading to %2$s, please upgrade to %1$s.', 'polylang' ),
83
- '<strong>0.9.8</strong>',
84
  POLYLANG_VERSION // phpcs:ignore WordPress.Security.EscapeOutput
85
  )
86
  );
@@ -90,498 +99,28 @@ class PLL_Upgrade {
90
  * Upgrades the plugin depending on the previous version
91
  *
92
  * @since 1.2
 
 
93
  */
94
  public function _upgrade() {
95
- foreach ( array( '0.9', '1.0', '1.1', '1.2', '1.2.1', '1.2.3', '1.3', '1.4', '1.4.1', '1.4.4', '1.5', '1.6', '1.7.4', '1.8', '2.0.8', '2.1', '2.7', '2.8.1' ) as $version ) {
96
  if ( version_compare( $this->options['version'], $version, '<' ) ) {
97
  call_user_func( array( $this, 'upgrade_' . str_replace( '.', '_', $version ) ) );
98
  }
99
  }
100
 
101
- $delete_pre_1_2_data = get_transient( 'pll_upgrade_1_4' );
102
- if ( false !== $delete_pre_1_2_data && absint( $delete_pre_1_2_data ) < time() ) {
103
- $this->delete_pre_1_2_data();
104
- }
105
-
106
  $this->options['previous_version'] = $this->options['version']; // Remember the previous version of Polylang since v1.7.7
107
  $this->options['version'] = POLYLANG_VERSION;
108
  update_option( 'polylang', $this->options );
109
  }
110
 
111
- /**
112
- * Upgrades if the previous version is < 0.9
113
- *
114
- * @since 1.2
115
- */
116
- protected function upgrade_0_9() {
117
- $this->options['sync'] = defined( 'PLL_SYNC' ) && ! PLL_SYNC ? 0 : 1; // The option replaces PLL_SYNC in 0.9
118
- }
119
-
120
- /**
121
- * Upgrades if the previous version is < 1.0
122
- *
123
- * @since 1.2
124
- */
125
- protected function upgrade_1_0() {
126
- // The option replaces PLL_MEDIA_SUPPORT in 1.0
127
- $this->options['media_support'] = defined( 'PLL_MEDIA_SUPPORT' ) && ! PLL_MEDIA_SUPPORT ? 0 : 1;
128
-
129
- // Split the synchronization options in 1.0
130
- $this->options['sync'] = empty( $this->options['sync'] ) ? array() : array_keys( PLL_Settings_Sync::list_metas_to_sync() );
131
-
132
- // Set default values for post types and taxonomies to translate
133
- $this->options['post_types'] = array_values( get_post_types( array( '_builtin' => false, 'show_ui' => true ) ) );
134
- $this->options['taxonomies'] = array_values( get_taxonomies( array( '_builtin' => false, 'show_ui' => true ) ) );
135
- update_option( 'polylang', $this->options );
136
-
137
- flush_rewrite_rules(); // Rewrite rules have been modified in 1.0
138
- }
139
-
140
- /**
141
- * Upgrades if the previous version is < 1.1
142
- *
143
- * @since 1.2
144
- */
145
- protected function upgrade_1_1() {
146
- // Update strings register with icl_register_string
147
- $strings = get_option( 'polylang_wpml_strings' );
148
- if ( $strings ) {
149
- foreach ( array_keys( $strings ) as $key ) {
150
- $strings[ $key ]['icl'] = 1;
151
- }
152
- update_option( 'polylang_wpml_strings', $strings );
153
- }
154
-
155
- // Move polylang_widgets options
156
- if ( $widgets = get_option( 'polylang_widgets' ) ) {
157
- $this->options['widgets'] = $widgets;
158
- delete_option( 'polylang_widgets' );
159
- }
160
- }
161
-
162
- /**
163
- * Upgrades if the previous version is < 1.2
164
- *
165
- * @since 1.2
166
- */
167
- protected function upgrade_1_2() {
168
- $this->options['domains'] = array(); // Option added in 1.2
169
-
170
- // Need to register the taxonomies
171
- foreach ( array( 'language', 'term_language', 'post_translations', 'term_translations' ) as $taxonomy ) {
172
- register_taxonomy( $taxonomy, null, array( 'label' => false, 'public' => false, 'query_var' => false, 'rewrite' => false ) );
173
- }
174
-
175
- // Abort if the db upgrade has already been done previously
176
- if ( get_terms( 'term_language', array( 'hide_empty' => 0 ) ) ) {
177
- return;
178
- }
179
-
180
- set_time_limit( 0 ); // In case we upgrade a huge site
181
-
182
- // Upgrade old model based on metas to new model based on taxonomies
183
- global $wpdb;
184
- $wpdb->termmeta = $wpdb->prefix . 'termmeta'; // Registers the termmeta table in wpdb
185
- $languages = get_terms( 'language', array( 'hide_empty' => 0 ) ); // Don't use get_languages_list which can't work with the old model
186
- $lang_tt_ids = array();
187
-
188
- foreach ( $languages as $lang ) {
189
- // First update language with new storage for locale and text direction
190
- $text_direction = get_metadata( 'term', $lang->term_id, '_rtl', true );
191
- $desc = maybe_serialize( array( 'locale' => $lang->description, 'rtl' => $text_direction ) );
192
- wp_update_term( (int) $lang->term_id, 'language', array( 'description' => $desc ) );
193
-
194
- // Add language to new 'term_language' taxonomy
195
- $term_lang = wp_insert_term( $lang->name, 'term_language', array( 'slug' => 'pll_' . $lang->slug ) );
196
- $lang_tt_ids[ $lang->term_id ] = $term_lang['term_taxonomy_id']; // Keep the term taxonomy id for future
197
- }
198
-
199
- // Get all terms with a language defined
200
- $terms = $wpdb->get_results( "SELECT term_id, meta_value FROM {$wpdb->termmeta} WHERE meta_key = '_language'" );
201
- foreach ( $terms as $key => $term ) {
202
- $terms[ $key ] = $wpdb->prepare( '( %d, %d )', $term->term_id, $lang_tt_ids[ $term->meta_value ] );
203
- }
204
-
205
- $terms = array_unique( $terms );
206
-
207
- // Assign language to each term
208
- if ( ! empty( $terms ) ) {
209
- // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
210
- $wpdb->query( "INSERT INTO {$wpdb->term_relationships} ( object_id, term_taxonomy_id ) VALUES " . implode( ',', $terms ) );
211
- }
212
-
213
- // Translations
214
- foreach ( array( 'post', 'term' ) as $type ) {
215
- $table = $type . 'meta';
216
- $terms = array();
217
- $slugs = array();
218
- $tts = array();
219
- $trs = array();
220
- $description = array();
221
-
222
- // Get all translated objects
223
- // PHPCS:ignore WordPress.DB.PreparedSQL
224
- $objects = $wpdb->get_col( "SELECT DISTINCT meta_value FROM {$wpdb->$table} WHERE meta_key = '_translations'" );
225
-
226
- if ( empty( $objects ) ) {
227
- continue;
228
- }
229
-
230
- foreach ( $objects as $obj ) {
231
- $term = uniqid( 'pll_' ); // The term name
232
- $terms[] = $wpdb->prepare( '( %s, %s )', $term, $term );
233
- $slugs[] = $wpdb->prepare( '%s', $term );
234
- $translations = maybe_unserialize( maybe_unserialize( $obj ) ); // 2 maybe_unserialize due to an old storage bug
235
- $description[ $term ] = maybe_serialize( $translations );
236
- }
237
-
238
- $terms = array_unique( $terms );
239
-
240
- // Insert terms
241
- if ( ! empty( $terms ) ) {
242
- // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
243
- $wpdb->query( "INSERT INTO {$wpdb->terms} ( slug, name ) VALUES " . implode( ',', $terms ) );
244
- }
245
-
246
- // Get all terms with their term_id
247
- // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
248
- $terms = $wpdb->get_results( "SELECT term_id, slug FROM $wpdb->terms WHERE slug IN ( " . implode( ',', $slugs ) . ' )' );
249
-
250
- // Prepare terms taxonomy relationship
251
- foreach ( $terms as $term ) {
252
- $tts[] = $wpdb->prepare( '( %d, %s, %s )', $term->term_id, $type . '_translations', $description[ $term->slug ] );
253
- }
254
-
255
- $tts = array_unique( $tts );
256
-
257
- // Insert term_taxonomy
258
- if ( ! empty( $tts ) ) {
259
- // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
260
- $wpdb->query( "INSERT INTO {$wpdb->term_taxonomy} ( term_id, taxonomy, description ) VALUES " . implode( ',', $tts ) );
261
- }
262
-
263
- // Get all terms with term_taxonomy_id
264
- $terms = get_terms( $type . '_translations', array( 'hide_empty' => false ) );
265
-
266
- // Prepare objects relationships
267
- foreach ( $terms as $term ) {
268
- $translations = maybe_unserialize( $term->description );
269
- foreach ( $translations as $object_id ) {
270
- if ( ! empty( $object_id ) ) {
271
- $trs[] = $wpdb->prepare( '( %d, %d )', $object_id, $term->term_taxonomy_id );
272
- }
273
- }
274
- }
275
-
276
- $trs = array_unique( $trs );
277
-
278
- // Insert term_relationships
279
- if ( ! empty( $trs ) ) {
280
- // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
281
- $wpdb->query( "INSERT INTO {$wpdb->term_relationships} ( object_id, term_taxonomy_id ) VALUES " . implode( ',', $trs ) );
282
- }
283
- }
284
-
285
- // Upgrade of string translations is now in upgrade_1_2_1
286
- // Upgrade of nav menus is now in upgrade_1_2_3
287
- }
288
-
289
- /**
290
- * Upgrades if the previous version is < 1.2.1
291
- *
292
- * @since 1.2.1
293
- */
294
- protected function upgrade_1_2_1() {
295
- // Strings translations
296
- foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
297
- if ( $strings = get_option( 'polylang_mo' . $lang->term_id ) ) {
298
- $mo = new PLL_MO();
299
- foreach ( $strings as $msg ) {
300
- $mo->add_entry( $mo->make_entry( $msg[0], $msg[1] ) );
301
- }
302
- $mo->export_to_db( $lang );
303
- }
304
- }
305
- }
306
-
307
- /**
308
- * Upgrades if the previous version is < 1.2.3
309
- * Uprades multilingual menus depending on the old version due to multiple changes in menus management
310
- *
311
- * @since 1.2.3
312
- */
313
- public function upgrade_1_2_3() {
314
- // Old version < 1.1
315
- // Multilingal locations and switcher item were stored in a dedicated option
316
- if ( version_compare( $this->options['version'], '1.1', '<' ) ) {
317
- if ( $menu_lang = get_option( 'polylang_nav_menus' ) ) {
318
- $locations = array();
319
-
320
- foreach ( $menu_lang as $location => $arr ) {
321
- if ( ! in_array( $location, array_keys( get_registered_nav_menus() ) ) ) {
322
- continue;
323
- }
324
-
325
- $switch_options = array_slice( $arr, -5, 5 );
326
- $translations = array_diff_key( $arr, $switch_options );
327
- $has_switcher = array_shift( $switch_options );
328
-
329
- foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
330
- // Move nav menus locations
331
- if ( ! empty( $translations[ $lang->slug ] ) ) {
332
- $locations[ $location ][ $lang->slug ] = $translations[ $lang->slug ];
333
- }
334
-
335
- // Create the menu items for the language switcher
336
- if ( ! empty( $has_switcher ) ) {
337
- $menu_item_db_id = wp_update_nav_menu_item(
338
- $translations[ $lang->slug ],
339
- 0,
340
- array(
341
- 'menu-item-title' => __( 'Language switcher', 'polylang' ),
342
- 'menu-item-url' => '#pll_switcher',
343
- 'menu-item-status' => 'publish',
344
- )
345
- );
346
-
347
- update_post_meta( $menu_item_db_id, '_pll_menu_item', $switch_options );
348
- }
349
- }
350
- }
351
-
352
- if ( ! empty( $locations ) ) {
353
- $this->options['nav_menus'][ get_option( 'stylesheet' ) ] = $locations;
354
- }
355
-
356
- delete_option( 'polylang_nav_menus' );
357
- }
358
- }
359
-
360
- elseif ( empty( $this->options['nav_menus'] ) ) {
361
- $menus = get_theme_mod( 'nav_menu_locations' );
362
-
363
- if ( is_array( $menus ) ) {
364
- // If old version < 1.2
365
- // Clean the WP option as it was a bad idea to pollute it
366
- if ( version_compare( $this->options['version'], '1.2', '<' ) ) {
367
- foreach ( $menus as $loc => $menu ) {
368
- if ( strpos( $loc, '#' ) ) {
369
- unset( $menus[ $loc ] );
370
- }
371
- }
372
-
373
- set_theme_mod( 'nav_menu_locations', $menus );
374
- }
375
-
376
- // Get the multilingual locations
377
- foreach ( $menus as $loc => $menu ) {
378
- foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
379
- $arr[ $loc ][ $lang->slug ] = pll_get_term( $menu, $lang );
380
- }
381
- }
382
-
383
- if ( ! empty( $arr ) ) {
384
- $this->options['nav_menus'][ get_option( 'stylesheet' ) ] = $arr;
385
- }
386
- }
387
- }
388
- }
389
-
390
- /**
391
- * Upgrades if the previous version is < 1.3
392
- * Moves the user biographies in default language to the 'description' user meta
393
- *
394
- * @since 1.3
395
- */
396
- protected function upgrade_1_3() {
397
- $usermeta = 'description_' . $this->options['default_lang'];
398
- $query = new WP_User_Query( array( 'blog_id' => $GLOBALS['blog_id'], 'meta_key' => $usermeta ) );
399
-
400
- foreach ( $query->get_results() as $user ) {
401
- $desc = get_user_meta( $user->ID, $usermeta, true );
402
- if ( ! empty( $desc ) ) {
403
- update_user_meta( $user->ID, 'description', $desc );
404
- delete_user_meta( $user->ID, $usermeta );
405
- }
406
- }
407
- }
408
-
409
- /**
410
- * Upgrades if the previous version is < 1.4
411
- * Sets a transient to delete old model data
412
- * Deletes language cache (due to bug correction in home urls in 1.3.1 and added mo_id in 1.4)
413
- *
414
- * @since 1.4
415
- */
416
- protected function upgrade_1_4() {
417
- set_transient( 'pll_upgrade_1_4', time() + 60 * 24 * 60 * 60 ); // 60 days
418
- delete_transient( 'pll_languages_list' );
419
- }
420
-
421
- /**
422
- * Old data were not deleted in 1.2, just in case...
423
- * Delete them at first upgrade at least 60 days after upgrade to 1.4
424
- *
425
- * @since 1.4
426
- */
427
- protected function delete_pre_1_2_data() {
428
- // Suppress data of the old model < 1.2
429
- global $wpdb;
430
- $wpdb->termmeta = $wpdb->prefix . 'termmeta'; // registers the termmeta table in wpdb in case WP < 4.4
431
-
432
- // Do nothing if the termmeta table does not exists
433
- if ( count( $wpdb->get_results( "SHOW TABLES LIKE '$wpdb->termmeta'" ) ) ) {
434
- $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key = '_translations'" );
435
- $wpdb->query( "DELETE FROM $wpdb->termmeta WHERE meta_key = '_language'" );
436
- $wpdb->query( "DELETE FROM $wpdb->termmeta WHERE meta_key = '_rtl'" );
437
- $wpdb->query( "DELETE FROM $wpdb->termmeta WHERE meta_key = '_translations'" );
438
- }
439
-
440
- // Delete the strings translations
441
- $languages = get_terms( 'language', array( 'hide_empty' => false ) );
442
- foreach ( $languages as $lang ) {
443
- delete_option( 'polylang_mo' . $lang->term_id );
444
- }
445
-
446
- delete_transient( 'pll_upgrade_1_4' );
447
- }
448
-
449
- /**
450
- * Upgrades if the previous version is < 1.4.1
451
- * Disables the browser detection when using multiple domains
452
- *
453
- * @since 1.4.1
454
- */
455
- protected function upgrade_1_4_1() {
456
- if ( 3 == $this->options['force_lang'] ) {
457
- $this->options['browser'] = $this->options['hide_default'] = 0;
458
- }
459
- }
460
-
461
- /**
462
- * Upgrades if the previous version is < 1.4.4
463
- * Uprades widgets options for language filter
464
- *
465
- * @since 1.4.4
466
- */
467
- protected function upgrade_1_4_4() {
468
- foreach ( $GLOBALS['wp_registered_widgets'] as $widget ) {
469
- if ( ! empty( $this->options['widgets'][ $widget['id'] ] ) && ! empty( $widget['callback'][0] ) && ! empty( $widget['params'][0]['number'] ) ) {
470
- $obj = $widget['callback'][0];
471
- if ( is_object( $obj ) && method_exists( $obj, 'get_settings' ) && method_exists( $obj, 'save_settings' ) ) {
472
- $settings = $obj->get_settings();
473
- $settings[ $widget['params'][0]['number'] ]['pll_lang'] = $this->options['widgets'][ $widget['id'] ];
474
- $obj->save_settings( $settings );
475
- }
476
- }
477
- }
478
- unset( $this->options['widgets'] );
479
- }
480
-
481
- /**
482
- * Upgrades if the previous version is < 1.5
483
- * Deletes language cache (due to host property added and bug on search url)
484
- *
485
- * @since 1.5
486
- */
487
- protected function upgrade_1_5() {
488
- delete_transient( 'pll_languages_list' );
489
- }
490
-
491
- /**
492
- * Upgrades if the previous version is < 1.6
493
- * Upgrades core language files to get the .po file (only for WP 4.0+)
494
- *
495
- * @since 1.6
496
- */
497
- protected function upgrade_1_6() {
498
- if ( version_compare( $GLOBALS['wp_version'], '4.0', '>=' ) ) {
499
- self::download_language_packs();
500
- }
501
- }
502
-
503
- /**
504
- * Downloads language packs
505
- * Intended to be used only one time (at upgrade to Polylang 1.6 or first upgrade of WP 4.0 or later)
506
- * Adapted from wp_download_language_pack
507
- * Rewritten because wp_download_language_pack checks the existence of .mo and I need to download .po
508
- *
509
- * @since 1.6
510
- */
511
- public static function download_language_packs() {
512
- $languages = pll_languages_list( array( 'fields' => 'locale' ) );
513
-
514
- // Prevents upgrade if the .po file is already here. Let WP manage the upgrades :)
515
- foreach ( $languages as $key => $locale ) {
516
- if ( file_exists( WP_LANG_DIR . "/$locale.po" ) ) {
517
- unset( $languages[ $key ] );
518
- }
519
- }
520
-
521
- if ( empty( $languages ) ) {
522
- return;
523
- }
524
-
525
- require_once ABSPATH . 'wp-admin/includes/translation-install.php';
526
- $translations = wp_get_available_translations();
527
- if ( ! $translations ) {
528
- return;
529
- }
530
-
531
- $translations_to_load = array();
532
-
533
- foreach ( $translations as $translation ) {
534
- if ( in_array( $translation['language'], $languages ) ) {
535
- $translation['type'] = 'core';
536
- $translations_to_load[] = (object) $translation;
537
- }
538
- }
539
-
540
- if ( ! empty( $translations_to_load ) ) {
541
- require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
542
- $upgrader = new Language_Pack_Upgrader( new Automatic_Upgrader_Skin() );
543
- $upgrader->bulk_upgrade( $translations_to_load, array( 'clear_update_cache' => false ) );
544
- }
545
- }
546
-
547
- /**
548
- * Upgrades if the previous version is < 1.7.4
549
- *
550
- * @since 1.7.4
551
- */
552
- protected function upgrade_1_7_4() {
553
- delete_transient( 'pll_languages_list' ); // Deletes language cache (due to flag properties added in 1.7, page on front removed in 1.7.2, home url fixes in 1.7.4)
554
- flush_rewrite_rules(); // Flush rewrite rules due to custom taxonomy rewrite rule bug fix
555
- }
556
-
557
- /**
558
- * Upgrades if the previous version is < 1.8
559
- *
560
- * @since 1.8
561
- */
562
- protected function upgrade_1_8() {
563
- // Adds the flag code in languages stored in DB
564
- $languages = include POLYLANG_DIR . '/settings/languages.php';
565
-
566
- $terms = get_terms( 'language', array( 'hide_empty' => 0 ) );
567
-
568
- foreach ( $terms as $lang ) {
569
- $description = maybe_unserialize( $lang->description );
570
- if ( isset( $languages[ $description['locale'] ] ) ) {
571
- $description['flag_code'] = $languages[ $description['locale'] ]['flag'];
572
- $description = maybe_serialize( $description );
573
- wp_update_term( (int) $lang->term_id, 'language', array( 'description' => $description ) );
574
- }
575
- }
576
-
577
- delete_transient( 'pll_languages_list' );
578
- }
579
-
580
  /**
581
  * Upgrades if the previous version is < 2.0.8
582
  * Changes the user meta 'user_lang' to 'locale' to match WP 4.7 choice
583
  *
584
  * @since 2.0.8
 
 
585
  */
586
  protected function upgrade_2_0_8() {
587
  global $wpdb;
@@ -589,21 +128,32 @@ class PLL_Upgrade {
589
  }
590
 
591
  /**
592
- * Upgrades if the previous version is < 2.1
593
- * Moves strings translations from polylang_mo post_content to post meta _pll_strings_translations
594
  *
595
  * @since 2.1
 
 
596
  */
597
  protected function upgrade_2_1() {
598
- foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
599
- $mo_id = PLL_MO::get_id( $lang );
600
- $meta = get_post_meta( $mo_id, '_pll_strings_translations', true );
 
 
 
 
 
 
 
 
 
601
 
602
- if ( empty( $meta ) ) {
603
- $post = get_post( $mo_id, OBJECT );
604
- $strings = maybe_unserialize( $post->post_content );
605
- if ( is_array( $strings ) ) {
606
- update_post_meta( $mo_id, '_pll_strings_translations', $strings );
607
  }
608
  }
609
  }
@@ -615,6 +165,8 @@ class PLL_Upgrade {
615
  * Dismiss the wizard notice for existing sites
616
  *
617
  * @since 2.7
 
 
618
  */
619
  protected function upgrade_2_7() {
620
  $strings = get_option( 'polylang_wpml_strings' );
@@ -644,6 +196,8 @@ class PLL_Upgrade {
644
  * - bug of flags url returning html fixed in 2.8.1
645
  *
646
  * @since 2.8.1
 
 
647
  */
648
  protected function upgrade_2_8_1() {
649
  delete_transient( 'pll_languages_list' );
9
  * @since 1.2
10
  */
11
  class PLL_Upgrade {
12
+ /**
13
+ * Stores the plugin options.
14
+ *
15
+ * @var array
16
+ */
17
  public $options;
18
 
19
  /**
31
  * Check if upgrade is possible otherwise die to avoid activation
32
  *
33
  * @since 1.2
34
+ *
35
+ * @return void
36
  */
37
  public function can_activate() {
38
  if ( ! $this->can_upgrade() ) {
70
  * @return bool true if upgrade is possible, false otherwise
71
  */
72
  public function can_upgrade() {
73
+ // Don't manage upgrade from version < 1.8
74
+ return version_compare( $this->options['version'], '1.8', '>=' );
75
  }
76
 
77
  /**
78
  * Displays a notice when ugrading from a too old version
79
  *
80
  * @since 1.0
81
+ *
82
+ * @return void
83
  */
84
  public function admin_notices() {
85
  load_plugin_textdomain( 'polylang' );
89
  sprintf(
90
  /* translators: %1$s and %2$s are Polylang version numbers */
91
  esc_html__( 'Before upgrading to %2$s, please upgrade to %1$s.', 'polylang' ),
92
+ '<strong>2.9</strong>',
93
  POLYLANG_VERSION // phpcs:ignore WordPress.Security.EscapeOutput
94
  )
95
  );
99
  * Upgrades the plugin depending on the previous version
100
  *
101
  * @since 1.2
102
+ *
103
+ * @return void
104
  */
105
  public function _upgrade() {
106
+ foreach ( array( '2.0.8', '2.1', '2.7', '2.8.1' ) as $version ) {
107
  if ( version_compare( $this->options['version'], $version, '<' ) ) {
108
  call_user_func( array( $this, 'upgrade_' . str_replace( '.', '_', $version ) ) );
109
  }
110
  }
111
 
 
 
 
 
 
112
  $this->options['previous_version'] = $this->options['version']; // Remember the previous version of Polylang since v1.7.7
113
  $this->options['version'] = POLYLANG_VERSION;
114
  update_option( 'polylang', $this->options );
115
  }
116
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  /**
118
  * Upgrades if the previous version is < 2.0.8
119
  * Changes the user meta 'user_lang' to 'locale' to match WP 4.7 choice
120
  *
121
  * @since 2.0.8
122
+ *
123
+ * @return void
124
  */
125
  protected function upgrade_2_0_8() {
126
  global $wpdb;
128
  }
129
 
130
  /**
131
+ * Upgrades if the previous version is < 2.1.
132
+ * Moves strings translations from polylang_mo post_content to post meta _pll_strings_translations.
133
  *
134
  * @since 2.1
135
+ *
136
+ * @return void
137
  */
138
  protected function upgrade_2_1() {
139
+ $posts = get_posts(
140
+ array(
141
+ 'post_type' => 'polylang_mo',
142
+ 'post_status' => 'any',
143
+ 'numberposts' => -1,
144
+ 'nopaging' => true,
145
+ )
146
+ );
147
+
148
+ if ( is_array( $posts ) ) {
149
+ foreach ( $posts as $post ) {
150
+ $meta = get_post_meta( $post->ID, '_pll_strings_translations', true );
151
 
152
+ if ( empty( $meta ) ) {
153
+ $strings = maybe_unserialize( $post->post_content );
154
+ if ( is_array( $strings ) ) {
155
+ update_post_meta( $post->ID, '_pll_strings_translations', $strings );
156
+ }
157
  }
158
  }
159
  }
165
  * Dismiss the wizard notice for existing sites
166
  *
167
  * @since 2.7
168
+ *
169
+ * @return void
170
  */
171
  protected function upgrade_2_7() {
172
  $strings = get_option( 'polylang_wpml_strings' );
196
  * - bug of flags url returning html fixed in 2.8.1
197
  *
198
  * @since 2.8.1
199
+ *
200
+ * @return void
201
  */
202
  protected function upgrade_2_8_1() {
203
  delete_transient( 'pll_languages_list' );
integrations/cache/cache-compat.php CHANGED
@@ -27,13 +27,18 @@ class PLL_Cache_Compat {
27
  }
28
 
29
  /**
30
- * Currently all tested cache plugins don't send cookies with cached pages
31
- * This makes us impossible know the language of the last browsed page
32
- * This functions allows to create the cookie in javascript as a workaround
33
  *
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 === PLL()->options['force_lang'] ) ? 'None' : 'Lax';
39
 
27
  }
28
 
29
  /**
30
+ * Currently all tested cache plugins don't send cookies with cached pages.
31
+ * This makes us impossible to know the language of the last browsed page.
32
+ * This functions allows to create the cookie in javascript as a workaround.
33
  *
34
  * @since 2.3
35
  */
36
  public function add_cookie_script() {
37
+ // Embeds should not set the cookie.
38
+ if ( is_embed() ) {
39
+ return;
40
+ }
41
+
42
  $domain = ( 2 === PLL()->options['force_lang'] ) ? wp_parse_url( PLL()->links_model->home, PHP_URL_HOST ) : COOKIE_DOMAIN;
43
  $samesite = ( 3 === PLL()->options['force_lang'] ) ? 'None' : 'Lax';
44
 
integrations/custom-field-template/cft.php CHANGED
@@ -23,8 +23,8 @@ class PLL_Cft {
23
  *
24
  * @since 1.0.2
25
  *
26
- * @param string $post_type Unused.
27
- * @param object $post Current post object.
28
  */
29
  public function cft_copy( $post_type, $post ) {
30
  global $custom_field_template;
23
  *
24
  * @since 1.0.2
25
  *
26
+ * @param string $post_type Unused.
27
+ * @param WP_Post $post Current post object.
28
  */
29
  public function cft_copy( $post_type, $post ) {
30
  global $custom_field_template;
integrations/wp-importer/wp-import.php CHANGED
@@ -9,6 +9,11 @@
9
  * @since 1.2
10
  */
11
  class PLL_WP_Import extends WP_Import {
 
 
 
 
 
12
  public $post_translations = array();
13
 
14
  /**
9
  * @since 1.2
10
  */
11
  class PLL_WP_Import extends WP_Import {
12
+ /**
13
+ * Stores post_translations terms.
14
+ *
15
+ * @var array
16
+ */
17
  public $post_translations = array();
18
 
19
  /**
integrations/wp-offload-media/as3cf.php CHANGED
@@ -10,6 +10,11 @@
10
  * @since 2.6
11
  */
12
  class PLL_AS3CF {
 
 
 
 
 
13
  private $is_media_translated;
14
 
15
  /**
10
  * @since 2.6
11
  */
12
  class PLL_AS3CF {
13
+ /**
14
+ * Stores if a media is translated when it is deleted.
15
+ *
16
+ * @var bool[]
17
+ */
18
  private $is_media_translated;
19
 
20
  /**
integrations/wpseo/wpseo.php CHANGED
@@ -84,6 +84,7 @@ class PLL_WPSEO {
84
  }
85
 
86
  if ( ! empty( $keys ) ) {
 
87
  new PLL_Translate_Option( 'wpseo_titles', array_fill_keys( $keys, 1 ), array( 'context' => 'wordpress-seo' ) );
88
  }
89
  }
@@ -195,11 +196,11 @@ class PLL_WPSEO {
195
  }
196
 
197
  /**
198
- * Add filters before the sitemap is evaluated and outputed
199
  *
200
  * @since 2.6
201
  *
202
- * @param object $query Instance of WP_Query being filtered.
203
  */
204
  public function before_sitemap( $query ) {
205
  $type = $query->get( 'sitemap' );
84
  }
85
 
86
  if ( ! empty( $keys ) ) {
87
+ WPSEO_Options::clear_cache();
88
  new PLL_Translate_Option( 'wpseo_titles', array_fill_keys( $keys, 1 ), array( 'context' => 'wordpress-seo' ) );
89
  }
90
  }
196
  }
197
 
198
  /**
199
+ * Add filters before the sitemap is evaluated and outputed.
200
  *
201
  * @since 2.6
202
  *
203
+ * @param WP_Query $query Instance of WP_Query being filtered.
204
  */
205
  public function before_sitemap( $query ) {
206
  $type = $query->get( 'sitemap' );
js/admin.js CHANGED
@@ -111,7 +111,13 @@ jQuery(
111
  */
112
 
113
  // Selectmenu widget options
114
- var selectmenuOptions = { width: defaultSelectmenuWidth };
 
 
 
 
 
 
115
 
116
  // Selectmenu widget callbacks
117
  var selectmenuFlagListCallbacks = {};
@@ -306,7 +312,7 @@ jQuery(
306
  function() {
307
  var $this = $( this ),
308
  $button = $( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
309
- btnText = pll_dismiss_notice || '';
310
 
311
  // Ensure plain text
312
  $button.find( '.screen-reader-text' ).text( btnText );
111
  */
112
 
113
  // Selectmenu widget options
114
+ var selectmenuOptions = {
115
+ width: defaultSelectmenuWidth,
116
+ classes: {
117
+ 'ui-selectmenu-menu': 'pll-selectmenu-menu',
118
+ 'ui-selectmenu-button': 'pll-selectmenu-button',
119
+ }
120
+ };
121
 
122
  // Selectmenu widget callbacks
123
  var selectmenuFlagListCallbacks = {};
312
  function() {
313
  var $this = $( this ),
314
  $button = $( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
315
+ btnText = pll_admin.dismiss_notice || '';
316
 
317
  // Ensure plain text
318
  $button.find( '.screen-reader-text' ).text( btnText );
js/admin.min.js DELETED
@@ -1 +0,0 @@
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
@@ -2,6 +2,11 @@
2
  * @package Polylang
3
  */
4
 
 
 
 
 
 
5
  /**
6
  * Filter REST API requests to add the language in the request
7
  *
@@ -35,126 +40,170 @@ function getCurrentLanguage() {
35
  }
36
 
37
  /**
38
- * Save post after lang choice is done and redirect to the same page for refreshing all the data
 
 
 
 
 
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;
50
  const subscribe = wp.data.subscribe;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
- let unsubscribe = null;
53
-
54
- // Listen if the savePost is done
55
- const savePostIsDone = new Promise(
56
- function( resolve, reject ) {
57
- unsubscribe = subscribe(
58
- function() {
59
- const isSavePostSucceeded = select( 'core/editor' ).didPostSaveRequestSucceed();
60
- const isSavePostFailed = select( 'core/editor' ).didPostSaveRequestFail();
61
- if ( isSavePostSucceeded || isSavePostFailed ) {
62
- if ( isSavePostFailed ) {
63
- reject();
64
- } else {
65
- resolve();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  }
67
- }
 
68
  }
69
  );
70
- }
 
71
  );
72
 
73
- // Specific case for empty posts
74
- if ( location.pathname.match( /post-new.php/gi ) ) {
75
- const title = select( 'core/editor' ).getEditedPostAttribute( 'title' );
76
- const content = select( 'core/editor' ).getEditedPostAttribute( 'content' );
77
- const excerpt = select( 'core/editor' ).getEditedPostAttribute( 'excerpt' );
78
- if ( '' === title && '' === content && '' === excerpt ) {
79
- // Change the new_lang parameter with the new language value for reloading the page
80
- // WPCS location.search is never written in the page, just used to relaoad page ( See line 94 ) with the right value of new_lang
81
- // new_lang input is controlled server side in PHP. The value come from the dropdown list of language returned and escaped server side
82
- if ( -1 != location.search.indexOf( 'new_lang' ) ) {
83
- // use regexp non capturing group to replace new_lang parameter no matter where it is and capture other parameters which can be behind it
84
- window.location.search = window.location.search.replace( /(?:new_lang=[^&]*)(&)?(.*)/, 'new_lang=' + this.value + '$1$2' ); // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment
85
- } else {
86
- window.location.search = window.location.search + ( ( -1 != window.location.search.indexOf( '?' ) ) ? '&' : '?' ) + 'new_lang=' + this.value; // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment
87
- }
88
- }
89
- }
90
 
91
- // For empty posts savePost does nothing
92
- dispatch( 'core/editor' ).savePost();
93
 
94
- savePostIsDone.then(
95
- function() {
96
- // If the post is well saved, we can reload the page
97
- unsubscribe();
98
- window.location.reload();
99
- },
100
- function() {
101
- // If the post save failed
102
- unsubscribe();
 
 
 
 
 
 
103
  }
104
- ).catch(
105
- function() {
106
- // If an exception is thrown
107
- unsubscribe();
108
- }
109
- );
110
- }
111
- );
112
- }
113
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
- /**
116
- * Handles internals of the metabox:
117
- * Language select, autocomplete input field.
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',
129
- lang: $( this ).val(),
130
- post_type: $( '#post_type' ).val(),
131
- post_id: $( '#post_ID' ).val(),
132
- _pll_nonce: $( '#_pll_nonce' ).val()
133
- }
134
 
135
- $.post(
136
- ajaxurl,
137
- data,
138
- function( response ) {
139
- var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
140
- $.each(
141
- res.responses,
142
- function() {
143
- switch ( this.what ) {
144
- case 'translations': // Translations fields
145
- // Data is built and come from server side and is well escaped when necessary
146
- $( '.translations' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
147
- init_translations();
148
- break;
149
- case 'flag': // Flag in front of the select dropdown
150
- // Data is built and come from server side and is well escaped when necessary
151
- $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
152
- break;
153
- }
154
- }
155
- );
156
- }
157
- );
158
  }
159
  );
160
 
2
  * @package Polylang
3
  */
4
 
5
+ import {
6
+ initializeLanguageOldValue,
7
+ initializeConfimationModal
8
+ } from './lib/confirmation-modal';
9
+
10
  /**
11
  * Filter REST API requests to add the language in the request
12
  *
40
  }
41
 
42
  /**
43
+ * Handles internals of the metabox:
44
+ * Language select, autocomplete input field.
45
+ *
46
+ * @since 1.5
47
+ *
48
+ * Save post after lang choice is done and redirect to the same page for refreshing all the data.
49
  *
50
  * @since 2.5
51
+ *
52
+ * Link post saving after refreshing the metabox.
53
+ *
54
+ * @since 3.0
55
  */
56
  jQuery(
57
  function( $ ) {
58
+ // Initialize current language to be able to compare if it changes.
59
+ initializeLanguageOldValue();
60
+
61
+
62
+ // Ajax for changing the post's language in the languages metabox
63
  $( '.post_lang_choice' ).on(
64
  'change',
65
+ function( event ) {
66
  const select = wp.data.select;
67
  const dispatch = wp.data.dispatch;
68
  const subscribe = wp.data.subscribe;
69
+ const emptyPost = isEmptyPost();
70
+
71
+ // Initialize the confirmation dialog box.
72
+ const confirmationModal = initializeConfimationModal();
73
+ const { dialogContainer : dialog } = confirmationModal;
74
+ let { dialogResult } = confirmationModal;
75
+ // The selected option in the dropdown list.
76
+ const selectedOption = event.target;
77
+
78
+ // Specific case for empty posts.
79
+ // Place at the beginning because window.location changing triggers automatically page reloading.
80
+ if ( location.pathname.match( /post-new.php/gi ) && emptyPost ) {
81
+ reloadPageForEmptyPost( selectedOption.value );
82
+ }
83
 
84
+ // Otherwise send an ajax request to refresh the legacy metabox and set the post language with the new language.
85
+ // It needs a confirmation of the user before changing the language.
86
+ // Need to wait the ajax response before triggering the block editor post save action.
87
+ if ( $( this ).data( 'old-value' ) !== selectedOption.value && ! emptyPost ) {
88
+ dialog.dialog( 'open' );
89
+ } else {
90
+ // Update the old language with the new one to be able to compare it in the next changing.
91
+ // Because the page isn't reloaded in this case.
92
+ initializeLanguageOldValue();
93
+ dialogResult = Promise.resolve();
94
+ }
95
+
96
+ dialogResult.then(
97
+ () => {
98
+ var data = { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
99
+ action: 'post_lang_choice',
100
+ lang: selectedOption.value,
101
+ post_type: $( '#post_type' ).val(),
102
+ post_id: $( '#post_ID' ).val(),
103
+ _pll_nonce: $( '#_pll_nonce' ).val()
104
+ }
105
+
106
+ $.post(
107
+ ajaxurl,
108
+ data,
109
+ function( response ) {
110
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
111
+ $.each(
112
+ res.responses,
113
+ function() {
114
+ switch ( this.what ) {
115
+ case 'translations': // Translations fields
116
+ // Data is built and come from server side and is well escaped when necessary
117
+ $( '.translations' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
118
+ init_translations();
119
+ break;
120
+ case 'flag': // Flag in front of the select dropdown
121
+ // Data is built and come from server side and is well escaped when necessary
122
+ $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
123
+ break;
124
+ }
125
  }
126
+ );
127
+ blockEditorSavePostAndReloadPage();
128
  }
129
  );
130
+ },
131
+ () => {} // Do nothing when promise is rejected by clicking the Cancel dialog button.
132
  );
133
 
134
+ function isEmptyPost() {
135
+ const editor = wp.data.select( 'core/editor' );
136
+ const title = editor.getEditedPostAttribute( 'title' ).trim();
137
+ const content = editor.getEditedPostAttribute( 'content' ).trim();
138
+ const excerpt = editor.getEditedPostAttribute( 'excerpt' ).trim();
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
+ return ! title && ! content && ! excerpt;
141
+ }
142
 
143
+ /**
144
+ * Reload the block editor page for empty posts.
145
+ *
146
+ * @param {string} lang The target language code.
147
+ */
148
+ function reloadPageForEmptyPost( lang ) {
149
+ // Change the new_lang parameter with the new language value for reloading the page
150
+ // WPCS location.search is never written in the page, just used to reload page with the right value of new_lang
151
+ // new_lang input is controlled server side in PHP. The value come from the dropdown list of language returned and escaped server side.
152
+ // Notice that window.location changing triggers automatically page reloading.
153
+ if ( -1 != location.search.indexOf( 'new_lang' ) ) {
154
+ // use regexp non capturing group to replace new_lang parameter no matter where it is and capture other parameters which can be behind it
155
+ window.location.search = window.location.search.replace( /(?:new_lang=[^&]*)(&)?(.*)/, 'new_lang=' + lang + '$1$2' ); // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment
156
+ } else {
157
+ window.location.search = window.location.search + ( ( -1 != window.location.search.indexOf( '?' ) ) ? '&' : '?' ) + 'new_lang=' + lang; // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment
158
  }
159
+ };
160
+
161
+ /**
162
+ * Triggers block editor post save and reload the block editor page when everything is ok.
163
+ */
164
+ function blockEditorSavePostAndReloadPage() {
165
+
166
+ let unsubscribe = null;
167
+
168
+ // Listen if the savePost is completely done by subscribing to its events.
169
+ const savePostIsDone = new Promise(
170
+ function( resolve, reject ) {
171
+ unsubscribe = subscribe(
172
+ function() {
173
+ const isSavePostSucceeded = select( 'core/editor' ).didPostSaveRequestSucceed();
174
+ const isSavePostFailed = select( 'core/editor' ).didPostSaveRequestFail();
175
+ if ( isSavePostSucceeded || isSavePostFailed ) {
176
+ if ( isSavePostFailed ) {
177
+ reject();
178
+ } else {
179
+ resolve();
180
+ }
181
+ }
182
+ }
183
+ );
184
+ }
185
+ );
186
 
187
+ // Triggers the post save.
188
+ dispatch( 'core/editor' ).savePost();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
+ // Process
191
+ savePostIsDone.then(
192
+ function() {
193
+ // If the post is well saved, we can reload the page
194
+ window.location.reload();
195
+ },
196
+ function() {
197
+ // If the post save failed
198
+ unsubscribe();
199
+ }
200
+ ).catch(
201
+ function() {
202
+ // If an exception is thrown
203
+ unsubscribe();
204
+ }
205
+ );
206
+ };
 
 
 
 
 
 
207
  }
208
  );
209
 
js/block-editor.min.js DELETED
@@ -1 +0,0 @@
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/build/admin.js ADDED
@@ -0,0 +1,416 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
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' );
20
+ },
21
+ focusout: function() {
22
+ // Tabbing between post title and .row-actions links needs a brief pause, otherwise
23
+ // the .row-actions div gets hidden in transit in some browsers ( ahem, Firefox ).
24
+ transitionTimeout = setTimeout(
25
+ function() {
26
+ focusedRowActions.removeClass( 'visible' );
27
+ },
28
+ 30
29
+ );
30
+ }
31
+ },
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 = {
115
+ width: defaultSelectmenuWidth,
116
+ classes: {
117
+ 'ui-selectmenu-menu': 'pll-selectmenu-menu',
118
+ 'ui-selectmenu-button': 'pll-selectmenu-button',
119
+ }
120
+ };
121
+
122
+ // Selectmenu widget callbacks
123
+ var selectmenuFlagListCallbacks = {};
124
+ // Callbacks when Selectmenu widget create or select event is triggered.
125
+ var createSelectCallback = function( event, ui ) {
126
+ selectmenuRefreshButtonText( event.target );
127
+ }
128
+
129
+ /**
130
+ * Overrides the flag dropdown list with our customized jquery ui selectmenu.
131
+ */
132
+
133
+ // Callbacks when Selectmenu widget change or open event is triggered.
134
+ // 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.
135
+ // jQuery UI 1.11 callback version.
136
+ var changeOpenCallback = function( event, ui ){
137
+ selectmenuRefreshButtonText( $( event.target ).selectmenu( 'refresh' ) );
138
+ }
139
+ // jQueryUI 1.12 callback version.
140
+ var changeOpenCallbackjQueryUI112 = function( event, ui ){
141
+ // Just a refresh of the menu is needed with jQuery UI 1.12 because _renderButtonItem is triggered and then inject correctly the flag.
142
+ $( event.target ).selectmenu( 'refresh' );
143
+ }
144
+ // There is no need of create and select callbacks with jQuery UI 1.12 because overriding _renderButtonItem method do the job.
145
+ if ( isJqueryUImin112 ) {
146
+ selectmenuFlagListCallbacks =
147
+ {
148
+ change: changeOpenCallbackjQueryUI112,
149
+ open: changeOpenCallbackjQueryUI112,
150
+ };
151
+ } else {
152
+ selectmenuFlagListCallbacks = {
153
+ create: createSelectCallback,
154
+ select: createSelectCallback,
155
+ change: changeOpenCallback,
156
+ open: changeOpenCallback,
157
+ };
158
+ }
159
+
160
+ // Create the selectmenu widget only if the field is present.
161
+ if ( flagListExist ) {
162
+ // Create the jQuery UI selectmenu widget for flags list dropdown and return its instance.
163
+ var selectmenuFlagList = initializeSelectmenuWidget( $( '#flag_list' ), Object.assign( {}, selectmenuOptions, selectmenuFlagListCallbacks ) );
164
+ $( '#lang_list' ).on(
165
+ 'languageChanged',
166
+ function( event, flag ) {
167
+ // Refresh the flag field
168
+ selectmenuFlagList.element.val( flag );
169
+ selectmenuFlagList._trigger( 'change' );
170
+ }
171
+ );
172
+ }
173
+
174
+ /**
175
+ * Language choice in predefined languages in Polylang Languages settings page and wizard.
176
+ * Overrides the predefined language dropdown list with our customized jQuery ui selectmenu widget.
177
+ */
178
+
179
+ /**
180
+ * Fill the other language form fields from the language element selected in the language list dropdown.
181
+ *
182
+ * @param {Object} language - language object of the selected element in the language list dropdown.
183
+ */
184
+ function fillLanguageFields( language ) {
185
+ $( '#lang_slug' ).val( language.slug );
186
+ $( '#lang_locale' ).val( language.locale );
187
+ $( 'input[name="rtl"]' ).val( language.rtl );
188
+ $( '#lang_name' ).val( language.name );
189
+ }
190
+
191
+ /**
192
+ * Parse selected language element in the language list dropdown.
193
+ *
194
+ * @param {object} event - jQuery triggered event.
195
+ * @return {object} The language object with its named properties.
196
+ */
197
+ function parseSelectedLanguage( event ) {
198
+ var selectedElement = $('option:selected', event.target);
199
+ var values = selectedElement.val().split(':')
200
+ return {
201
+ slug: values[0],
202
+ locale: values[1],
203
+ rtl: [values[2]],
204
+ flag: values[3],
205
+ 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
206
+ };
207
+ }
208
+
209
+ // Callback when selectmenu widget change event is triggered.
210
+ var changeCallback = function( event, ui ) {
211
+ var language = parseSelectedLanguage( event );
212
+
213
+ fillLanguageFields( language );
214
+
215
+ $( event.target ).trigger( 'languageChanged', language.flag );
216
+ };
217
+
218
+ // Create the jQuery UI selectmenu widget languages list dropdown and return its instance.
219
+ var selectmenuLangListCallbacks = {};
220
+ // For the wizard we need a 100% width. So we override the previous defined value of selectmenuOptions.
221
+ if( $( '#lang_list' ).closest( '.pll-wizard-content' ).length > 0 ) {
222
+ selectmenuOptions = Object.assign( selectmenuOptions, { width: wizardSelectmenuWidth } );
223
+ }
224
+
225
+ // There is no need of create and select callbacks with jQuery UI 1.12 because overrinding _renderButtonItem method do the job.
226
+ if ( isJqueryUImin112 ) {
227
+ selectmenuLangListCallbacks = {
228
+ change: changeCallback,
229
+ };
230
+ } else {
231
+ selectmenuLangListCallbacks = {
232
+ create: createSelectCallback,
233
+ select: createSelectCallback,
234
+ change: changeCallback,
235
+ };
236
+ }
237
+ if ( langListExist ) {
238
+ initializeSelectmenuWidget( $( '#lang_list' ), Object.assign( {}, selectmenuOptions, selectmenuLangListCallbacks ) );
239
+ }
240
+
241
+ // strings translations
242
+ // save translations when pressing enter
243
+ $( '.translation input' ).on(
244
+ 'keydown',
245
+ function( event ){
246
+ if ( 'Enter' === event.key ) {
247
+ event.preventDefault();
248
+ $( '#submit' ).trigger( 'click' );
249
+ }
250
+ }
251
+ );
252
+
253
+ // settings page
254
+ // click on configure link
255
+ $( '#the-list' ).on(
256
+ 'click',
257
+ '.configure>a',
258
+ function(){
259
+ $( '.pll-configure' ).hide().prev().show();
260
+ $( this ).closest( 'tr' ).hide().next().show();
261
+ return false;
262
+ }
263
+ );
264
+
265
+ // cancel
266
+ $( '#the-list' ).on(
267
+ 'click',
268
+ '.cancel',
269
+ function(){
270
+ $( this ).closest( 'tr' ).hide().prev().show();
271
+ }
272
+ );
273
+
274
+ // save settings
275
+ $( '#the-list' ).on(
276
+ 'click',
277
+ '.save',
278
+ function(){
279
+ var tr = $( this ).closest( 'tr' );
280
+ var parts = tr.attr( 'id' ).split( '-' );
281
+
282
+ var data = {
283
+ action: 'pll_save_options',
284
+ pll_ajax_settings: true,
285
+ module: parts[parts.length - 1],
286
+ _pll_nonce: $( '#_pll_nonce' ).val()
287
+ };
288
+
289
+ data = tr.find( ':input' ).serialize() + '&' + $.param( data );
290
+
291
+ $.post(
292
+ ajaxurl,
293
+ data,
294
+ function( response ) {
295
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
296
+ $.each(
297
+ res.responses,
298
+ function() {
299
+ switch ( this.what ) {
300
+ case 'license-update':
301
+ $( '#pll-license-' + this.data ).replaceWith( this.supplemental.html );
302
+ break;
303
+ case 'success':
304
+ tr.hide().prev().show(); // close only if there is no error
305
+ case 'error':
306
+ $( '.settings-error' ).remove(); // remove previous messages if any
307
+ $( 'h1' ).after( this.data );
308
+
309
+ // Make notices dismissible
310
+ // copy paste of common.js from WP 4.2.2
311
+ $( '.notice.is-dismissible' ).each(
312
+ function() {
313
+ var $this = $( this ),
314
+ $button = $( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
315
+ btnText = pll_admin.dismiss_notice || '';
316
+
317
+ // Ensure plain text
318
+ $button.find( '.screen-reader-text' ).text( btnText );
319
+
320
+ // Whitelist because of how the button is built. See above
321
+ $this.append( $button ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
322
+
323
+ $button.on(
324
+ 'click.wp-dismiss-notice',
325
+ function( event ) {
326
+ event.preventDefault();
327
+ $this.fadeTo(
328
+ 100,
329
+ 0,
330
+ function() {
331
+ $( this ).slideUp(
332
+ 100,
333
+ function() {
334
+ $( this ).remove();
335
+ }
336
+ );
337
+ }
338
+ );
339
+ }
340
+ );
341
+ }
342
+ );
343
+ break;
344
+ }
345
+ }
346
+ );
347
+ }
348
+ );
349
+ }
350
+ );
351
+
352
+ // act when pressing enter or esc in configurations
353
+ $( '.pll-configure' ).on(
354
+ 'keydown',
355
+ function( event ){
356
+ if ( 'Enter' === event.key ) {
357
+ event.preventDefault();
358
+ $( this ).find( '.save' ).trigger( 'click' );
359
+ }
360
+
361
+ if ( 'Escape' === event.key ) {
362
+ event.preventDefault();
363
+ $( this ).find( '.cancel' ).trigger( 'click' );
364
+ }
365
+ }
366
+ );
367
+
368
+ // settings URL modifications
369
+ // manages visibility of fields
370
+ $( "input[name='force_lang']" ).on(
371
+ 'change',
372
+ function() {
373
+ function pll_toggle( a, test ) {
374
+ test ? a.show() : a.hide();
375
+ }
376
+
377
+ var value = $( this ).val();
378
+ pll_toggle( $( '#pll-domains-table' ), 3 == value );
379
+ pll_toggle( $( "#pll-hide-default" ), 3 > value );
380
+ pll_toggle( $( "#pll-rewrite" ), 2 > value );
381
+ pll_toggle( $( "#pll-redirect-lang" ), 2 > value );
382
+ }
383
+ );
384
+
385
+ // settings license
386
+ // deactivate button
387
+ $( '.pll-deactivate-license' ).on(
388
+ 'click',
389
+ function() {
390
+ var data = {
391
+ action: 'pll_deactivate_license',
392
+ pll_ajax_settings: true,
393
+ id: $( this ).attr( 'id' ),
394
+ _pll_nonce: $( '#_pll_nonce' ).val()
395
+ };
396
+ $.post(
397
+ ajaxurl,
398
+ data,
399
+ function( response ){
400
+ $( '#pll-license-' + response.id ).replaceWith( response.html );
401
+ }
402
+ );
403
+ }
404
+ );
405
+
406
+ // Manage closing the metabox.
407
+ // close postboxes that should be closed
408
+ $( '.if-js-closed' ).removeClass( 'if-js-closed' ).addClass( 'closed' );
409
+ // postboxes setup
410
+ if ( 'undefined' !== typeof postboxes ) {
411
+ postboxes.add_postbox_toggles( pagenow );
412
+ }
413
+ }
414
+ );
415
+
416
+
js/build/admin.min.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery((function(e){var t;e("table.languages").on({focusin:function(){clearTimeout(t);var n=e(this).find(".row-actions");e(".row-actions").not(this).removeClass("visible"),n.addClass("visible")},focusout:function(){t=setTimeout((function(){focusedRowActions.removeClass("visible")}),30)}},"tr");var n=e.ui.version>="1.12.0",l=e("#flag_list").length,s=e("#lang_list").length,i=function(t,n){var l=e("<li>"),s=e("<div>");return n.disabled&&this._addClass(l,null,"ui-state-disabled"),this._setText(s,n.label),s.prepend(e(n.element).data("flag-html")),s.children("img").addClass("ui-icon"),l.append(s).appendTo(t)},a=function(t){var n=e(t).selectmenu("instance").buttonText;n.prepend(e(t).children(":selected").data("flag-html")),n.children("img").addClass("ui-icon")},c=function(t){var n=e("<span>");return this._setText(n,t.label),this._addClass(n,"ui-selectmenu-text"),n.prepend(e(t.element).data("flag-html")),n.children("img").addClass("ui-icon"),n};function o(e,t){var l=e.selectmenu(t).selectmenu("instance");return l._renderItem=i,n&&(l._renderButtonItem=c,l.refresh()),l}var r={width:"95%",classes:{"ui-selectmenu-menu":"pll-selectmenu-menu","ui-selectmenu-button":"pll-selectmenu-button"}},u={},d=function(e,t){a(e.target)},p=function(t,n){a(e(t.target).selectmenu("refresh"))},g=function(t,n){e(t.target).selectmenu("refresh")};if(u=n?{change:g,open:g}:{create:d,select:d,change:p,open:p},l){var h=o(e("#flag_list"),Object.assign({},r,u));e("#lang_list").on("languageChanged",(function(e,t){h.element.val(t),h._trigger("change")}))}var f=function(t,n){var l=function(t){var n=e("option:selected",t.target),l=n.val().split(":");return{slug:l[0],locale:l[1],rtl:[l[2]],flag:l[3],name:n.text().split(" - ")[0]}}(t);!function(t){e("#lang_slug").val(t.slug),e("#lang_locale").val(t.locale),e('input[name="rtl"]').val(t.rtl),e("#lang_name").val(t.name)}(l),e(t.target).trigger("languageChanged",l.flag)},v={};e("#lang_list").closest(".pll-wizard-content").length>0&&(r=Object.assign(r,{width:"100%"})),v=n?{change:f}:{create:d,select:d,change:f},s&&o(e("#lang_list"),Object.assign({},r,v)),e(".translation input").on("keydown",(function(t){"Enter"===t.key&&(t.preventDefault(),e("#submit").trigger("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("-"),l={action:"pll_save_options",pll_ajax_settings:!0,module:n[n.length-1],_pll_nonce:e("#_pll_nonce").val()};l=t.find(":input").serialize()+"&"+e.param(l),e.post(ajaxurl,l,(function(n){var l=wpAjax.parseAjaxResponse(n,"ajax-response");e.each(l.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>'),l=pll_admin.dismiss_notice||"";n.find(".screen-reader-text").text(l),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").on("keydown",(function(t){"Enter"===t.key&&(t.preventDefault(),e(this).find(".save").trigger("click")),"Escape"===t.key&&(t.preventDefault(),e(this).find(".cancel").trigger("click"))})),e("input[name='force_lang']").on("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)}));
js/build/block-editor.js ADDED
@@ -0,0 +1,350 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******/ "use strict";
2
+
3
+ ;// CONCATENATED MODULE: ./js/lib/confirmation-modal.js
4
+ /**
5
+ * @package Polylang
6
+ */
7
+
8
+ // We can't use underscore or lodash in this common code because it depends of the context classic or block editor.
9
+ // Classic editor underscore is loaded, Block editor lodash is loaded.
10
+ const { __ } = wp.i18n;
11
+
12
+ const languagesList = jQuery( '.post_lang_choice' );
13
+
14
+ // Dialog box for alerting the user about a risky changing.
15
+ const initializeConfimationModal = () => {
16
+ // Create dialog container.
17
+ const dialogContainer = jQuery(
18
+ '<div/>',
19
+ {
20
+ id: 'pll-dialog',
21
+ style: 'display:none;'
22
+ }
23
+ ).text( __( 'Are you sure you want to change the language of the current content?', 'polylang' ) );
24
+
25
+ // Put it after languages list dropdown.
26
+ // PHPCS ignore dialogContainer is a new safe HTML code generated above.
27
+ languagesList.after( dialogContainer ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
28
+
29
+ const dialogResult = new Promise(
30
+ ( confirm, cancel ) => {
31
+ const confirmDialog = ( what ) => { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
32
+ switch ( what ) { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
33
+ case 'yes':
34
+ // Confirm the new language.
35
+ languagesList.data( 'old-value', languagesList.children( ':selected' )[0].value );
36
+ confirm();
37
+ break;
38
+ case 'no':
39
+ // Revert to the old language.
40
+ languagesList.val( languagesList.data( 'old-value' ) );
41
+ cancel( 'Cancel' );
42
+ break;
43
+ }
44
+ dialogContainer.dialog( 'close' ); // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
45
+ } // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
46
+
47
+ // Initialize dialog box in the case a language is selected but not added in the list.
48
+ dialogContainer.dialog(
49
+ {
50
+ autoOpen: false,
51
+ modal: true,
52
+ draggable: false,
53
+ resizable: false,
54
+ title: __( 'Change language', 'polylang' ),
55
+ minWidth: 600,
56
+ maxWidth: '100%',
57
+ classes: {
58
+ 'ui-dialog': 'pll-confirmation-modal',
59
+ },
60
+ open: function( event, ui ) {
61
+ // Change dialog box position for rtl language
62
+ if ( jQuery( 'body' ).hasClass( 'rtl' ) ) {
63
+ jQuery( this ).parent().css(
64
+ {
65
+ right: jQuery( this ).parent().css( 'left' ),
66
+ left: 'auto'
67
+ }
68
+ );
69
+ }
70
+ },
71
+ close: function( event, ui ) {
72
+ // When we're closing the dialog box we need to cancel the language change as we click on Cancel button.
73
+ confirmDialog( 'no' );
74
+ },
75
+ buttons: [
76
+ {
77
+ text: __( 'OK', 'polylang' ),
78
+ click: function( event ) {
79
+ confirmDialog( 'yes' );
80
+ }
81
+ },
82
+ {
83
+ text: __( 'Cancel', 'polylang' ),
84
+ click: function( event ) {
85
+ confirmDialog( 'no' );
86
+ }
87
+ } ]
88
+ }
89
+ );
90
+ }
91
+ );
92
+ return { dialogContainer, dialogResult };
93
+ }
94
+
95
+ const initializeLanguageOldValue = () => {
96
+ // Keep the old language value to be able to compare to the new one and revert to it if necessary.
97
+ languagesList.attr( 'data-old-value', languagesList.children( ':selected' )[0].value );
98
+ };
99
+
100
+ ;// CONCATENATED MODULE: ./js/block-editor.js
101
+ /**
102
+ * @package Polylang
103
+ */
104
+
105
+
106
+
107
+ /**
108
+ * Filter REST API requests to add the language in the request
109
+ *
110
+ * @since 2.5
111
+ */
112
+ wp.apiFetch.use(
113
+ function( options, next ) {
114
+ // If options.url is defined, this is not a REST request but a direct call to post.php for legacy metaboxes.
115
+ if ( 'undefined' === typeof options.url ) {
116
+ if ( 'undefined' === typeof options.data || null === options.data ) {
117
+ // GET
118
+ options.path += ( ( options.path.indexOf( '?' ) >= 0 ) ? '&lang=' : '?lang=' ) + getCurrentLanguage();
119
+ } else {
120
+ // PUT, POST
121
+ options.data.lang = getCurrentLanguage();
122
+ }
123
+ }
124
+ return next( options );
125
+ }
126
+ );
127
+
128
+ /**
129
+ * Get the language from the HTML form
130
+ *
131
+ * @since 2.5
132
+ *
133
+ * @return {Element.value}
134
+ */
135
+ function getCurrentLanguage() {
136
+ return document.querySelector( '[name=post_lang_choice]' ).value;
137
+ }
138
+
139
+ /**
140
+ * Handles internals of the metabox:
141
+ * Language select, autocomplete input field.
142
+ *
143
+ * @since 1.5
144
+ *
145
+ * Save post after lang choice is done and redirect to the same page for refreshing all the data.
146
+ *
147
+ * @since 2.5
148
+ *
149
+ * Link post saving after refreshing the metabox.
150
+ *
151
+ * @since 3.0
152
+ */
153
+ jQuery(
154
+ function( $ ) {
155
+ // Initialize current language to be able to compare if it changes.
156
+ initializeLanguageOldValue();
157
+
158
+
159
+ // Ajax for changing the post's language in the languages metabox
160
+ $( '.post_lang_choice' ).on(
161
+ 'change',
162
+ function( event ) {
163
+ const select = wp.data.select;
164
+ const dispatch = wp.data.dispatch;
165
+ const subscribe = wp.data.subscribe;
166
+ const emptyPost = isEmptyPost();
167
+
168
+ // Initialize the confirmation dialog box.
169
+ const confirmationModal = initializeConfimationModal();
170
+ const { dialogContainer : dialog } = confirmationModal;
171
+ let { dialogResult } = confirmationModal;
172
+ // The selected option in the dropdown list.
173
+ const selectedOption = event.target;
174
+
175
+ // Specific case for empty posts.
176
+ // Place at the beginning because window.location changing triggers automatically page reloading.
177
+ if ( location.pathname.match( /post-new.php/gi ) && emptyPost ) {
178
+ reloadPageForEmptyPost( selectedOption.value );
179
+ }
180
+
181
+ // Otherwise send an ajax request to refresh the legacy metabox and set the post language with the new language.
182
+ // It needs a confirmation of the user before changing the language.
183
+ // Need to wait the ajax response before triggering the block editor post save action.
184
+ if ( $( this ).data( 'old-value' ) !== selectedOption.value && ! emptyPost ) {
185
+ dialog.dialog( 'open' );
186
+ } else {
187
+ // Update the old language with the new one to be able to compare it in the next changing.
188
+ // Because the page isn't reloaded in this case.
189
+ initializeLanguageOldValue();
190
+ dialogResult = Promise.resolve();
191
+ }
192
+
193
+ dialogResult.then(
194
+ () => {
195
+ var data = { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
196
+ action: 'post_lang_choice',
197
+ lang: selectedOption.value,
198
+ post_type: $( '#post_type' ).val(),
199
+ post_id: $( '#post_ID' ).val(),
200
+ _pll_nonce: $( '#_pll_nonce' ).val()
201
+ }
202
+
203
+ $.post(
204
+ ajaxurl,
205
+ data,
206
+ function( response ) {
207
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
208
+ $.each(
209
+ res.responses,
210
+ function() {
211
+ switch ( this.what ) {
212
+ case 'translations': // Translations fields
213
+ // Data is built and come from server side and is well escaped when necessary
214
+ $( '.translations' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
215
+ init_translations();
216
+ break;
217
+ case 'flag': // Flag in front of the select dropdown
218
+ // Data is built and come from server side and is well escaped when necessary
219
+ $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
220
+ break;
221
+ }
222
+ }
223
+ );
224
+ blockEditorSavePostAndReloadPage();
225
+ }
226
+ );
227
+ },
228
+ () => {} // Do nothing when promise is rejected by clicking the Cancel dialog button.
229
+ );
230
+
231
+ function isEmptyPost() {
232
+ const editor = wp.data.select( 'core/editor' );
233
+ const title = editor.getEditedPostAttribute( 'title' ).trim();
234
+ const content = editor.getEditedPostAttribute( 'content' ).trim();
235
+ const excerpt = editor.getEditedPostAttribute( 'excerpt' ).trim();
236
+
237
+ return ! title && ! content && ! excerpt;
238
+ }
239
+
240
+ /**
241
+ * Reload the block editor page for empty posts.
242
+ *
243
+ * @param {string} lang The target language code.
244
+ */
245
+ function reloadPageForEmptyPost( lang ) {
246
+ // Change the new_lang parameter with the new language value for reloading the page
247
+ // WPCS location.search is never written in the page, just used to reload page with the right value of new_lang
248
+ // new_lang input is controlled server side in PHP. The value come from the dropdown list of language returned and escaped server side.
249
+ // Notice that window.location changing triggers automatically page reloading.
250
+ if ( -1 != location.search.indexOf( 'new_lang' ) ) {
251
+ // use regexp non capturing group to replace new_lang parameter no matter where it is and capture other parameters which can be behind it
252
+ window.location.search = window.location.search.replace( /(?:new_lang=[^&]*)(&)?(.*)/, 'new_lang=' + lang + '$1$2' ); // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment
253
+ } else {
254
+ window.location.search = window.location.search + ( ( -1 != window.location.search.indexOf( '?' ) ) ? '&' : '?' ) + 'new_lang=' + lang; // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment
255
+ }
256
+ };
257
+
258
+ /**
259
+ * Triggers block editor post save and reload the block editor page when everything is ok.
260
+ */
261
+ function blockEditorSavePostAndReloadPage() {
262
+
263
+ let unsubscribe = null;
264
+
265
+ // Listen if the savePost is completely done by subscribing to its events.
266
+ const savePostIsDone = new Promise(
267
+ function( resolve, reject ) {
268
+ unsubscribe = subscribe(
269
+ function() {
270
+ const isSavePostSucceeded = select( 'core/editor' ).didPostSaveRequestSucceed();
271
+ const isSavePostFailed = select( 'core/editor' ).didPostSaveRequestFail();
272
+ if ( isSavePostSucceeded || isSavePostFailed ) {
273
+ if ( isSavePostFailed ) {
274
+ reject();
275
+ } else {
276
+ resolve();
277
+ }
278
+ }
279
+ }
280
+ );
281
+ }
282
+ );
283
+
284
+ // Triggers the post save.
285
+ dispatch( 'core/editor' ).savePost();
286
+
287
+ // Process
288
+ savePostIsDone.then(
289
+ function() {
290
+ // If the post is well saved, we can reload the page
291
+ window.location.reload();
292
+ },
293
+ function() {
294
+ // If the post save failed
295
+ unsubscribe();
296
+ }
297
+ ).catch(
298
+ function() {
299
+ // If an exception is thrown
300
+ unsubscribe();
301
+ }
302
+ );
303
+ };
304
+ }
305
+ );
306
+
307
+ // Translations autocomplete input box
308
+ function init_translations() {
309
+ $( '.tr_lang' ).each(
310
+ function(){
311
+ var tr_lang = $( this ).attr( 'id' ).substring( 8 );
312
+ var td = $( this ).parent().parent().siblings( '.pll-edit-column' );
313
+
314
+ $( this ).autocomplete(
315
+ {
316
+ minLength: 0,
317
+
318
+ source: ajaxurl + '?action=pll_posts_not_translated' +
319
+ '&post_language=' + $( '.post_lang_choice' ).val() +
320
+ '&translation_language=' + tr_lang +
321
+ '&post_type=' + $( '#post_type' ).val() +
322
+ '&_pll_nonce=' + $( '#_pll_nonce' ).val(),
323
+
324
+ select: function( event, ui ) {
325
+ $( '#htr_lang_' + tr_lang ).val( ui.item.id );
326
+ // ui.item.link is built and come from server side and is well escaped when necessary
327
+ td.html( ui.item.link ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
328
+ },
329
+ }
330
+ );
331
+
332
+ // When the input box is emptied
333
+ $( this ).on(
334
+ 'blur',
335
+ function() {
336
+ if ( ! $( this ).val() ) {
337
+ $( '#htr_lang_' + tr_lang ).val( 0 );
338
+ // Value is retrieved from HTML already generated server side
339
+ td.html( td.siblings( '.hidden' ).children().clone() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
340
+ }
341
+ }
342
+ );
343
+ }
344
+ );
345
+ }
346
+
347
+ init_translations();
348
+ }
349
+ );
350
+
js/build/block-editor.min.js ADDED
@@ -0,0 +1 @@
 
1
+ "use strict";const{__}=wp.i18n,languagesList=jQuery(".post_lang_choice"),initializeConfimationModal=()=>{const t=jQuery("<div/>",{id:"pll-dialog",style:"display:none;"}).text(__("Are you sure you want to change the language of the current content?","polylang"));languagesList.after(t);const a=new Promise(((a,e)=>{const n=n=>{switch(n){case"yes":languagesList.data("old-value",languagesList.children(":selected")[0].value),a();break;case"no":languagesList.val(languagesList.data("old-value")),e("Cancel")}t.dialog("close")};t.dialog({autoOpen:!1,modal:!0,draggable:!1,resizable:!1,title:__("Change language","polylang"),minWidth:600,maxWidth:"100%",classes:{"ui-dialog":"pll-confirmation-modal"},open:function(t,a){jQuery("body").hasClass("rtl")&&jQuery(this).parent().css({right:jQuery(this).parent().css("left"),left:"auto"})},close:function(t,a){n("no")},buttons:[{text:__("OK","polylang"),click:function(t){n("yes")}},{text:__("Cancel","polylang"),click:function(t){n("no")}}]})}));return{dialogContainer:t,dialogResult:a}},initializeLanguageOldValue=()=>{languagesList.attr("data-old-value",languagesList.children(":selected")[0].value)};function getCurrentLanguage(){return document.querySelector("[name=post_lang_choice]").value}wp.apiFetch.use((function(t,a){return void 0===t.url&&(void 0===t.data||null===t.data?t.path+=(t.path.indexOf("?")>=0?"&lang=":"?lang=")+getCurrentLanguage():t.data.lang=getCurrentLanguage()),a(t)})),jQuery((function(t){function a(){t(".tr_lang").each((function(){var a=t(this).attr("id").substring(8),e=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,l){t("#htr_lang_"+a).val(l.item.id),e.html(l.item.link)}}),t(this).on("blur",(function(){t(this).val()||(t("#htr_lang_"+a).val(0),e.html(e.siblings(".hidden").children().clone()))}))}))}initializeLanguageOldValue(),t(".post_lang_choice").on("change",(function(e){const n=wp.data.select,l=wp.data.dispatch,i=wp.data.subscribe,o=function(){const t=wp.data.select("core/editor"),a=t.getEditedPostAttribute("title").trim(),e=t.getEditedPostAttribute("content").trim(),n=t.getEditedPostAttribute("excerpt").trim();return!a&&!e&&!n}(),s=initializeConfimationModal(),{dialogContainer:c}=s;let{dialogResult:u}=s;const r=e.target;var g;location.pathname.match(/post-new.php/gi)&&o&&(g=r.value,-1!=location.search.indexOf("new_lang")?window.location.search=window.location.search.replace(/(?:new_lang=[^&]*)(&)?(.*)/,"new_lang="+g+"$1$2"):window.location.search=window.location.search+(-1!=window.location.search.indexOf("?")?"&":"?")+"new_lang="+g),t(this).data("old-value")===r.value||o?(initializeLanguageOldValue(),u=Promise.resolve()):c.dialog("open"),u.then((()=>{var e={action:"post_lang_choice",lang:r.value,post_type:t("#post_type").val(),post_id:t("#post_ID").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,e,(function(e){var o=wpAjax.parseAjaxResponse(e,"ajax-response");t.each(o.responses,(function(){switch(this.what){case"translations":t(".translations").html(this.data),a();break;case"flag":t(".pll-select-flag").html(this.data)}})),function(){let t=null;const a=new Promise((function(a,e){t=i((function(){const t=n("core/editor").didPostSaveRequestSucceed(),l=n("core/editor").didPostSaveRequestFail();(t||l)&&(l?e():a())}))}));l("core/editor").savePost(),a.then((function(){window.location.reload()}),(function(){t()})).catch((function(){t()}))}()}))}),(()=>{}))})),a()}));
js/build/classic-editor.js ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******/ "use strict";
2
+
3
+ ;// CONCATENATED MODULE: ./js/lib/confirmation-modal.js
4
+ /**
5
+ * @package Polylang
6
+ */
7
+
8
+ // We can't use underscore or lodash in this common code because it depends of the context classic or block editor.
9
+ // Classic editor underscore is loaded, Block editor lodash is loaded.
10
+ const { __ } = wp.i18n;
11
+
12
+ const languagesList = jQuery( '.post_lang_choice' );
13
+
14
+ // Dialog box for alerting the user about a risky changing.
15
+ const initializeConfimationModal = () => {
16
+ // Create dialog container.
17
+ const dialogContainer = jQuery(
18
+ '<div/>',
19
+ {
20
+ id: 'pll-dialog',
21
+ style: 'display:none;'
22
+ }
23
+ ).text( __( 'Are you sure you want to change the language of the current content?', 'polylang' ) );
24
+
25
+ // Put it after languages list dropdown.
26
+ // PHPCS ignore dialogContainer is a new safe HTML code generated above.
27
+ languagesList.after( dialogContainer ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
28
+
29
+ const dialogResult = new Promise(
30
+ ( confirm, cancel ) => {
31
+ const confirmDialog = ( what ) => { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
32
+ switch ( what ) { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
33
+ case 'yes':
34
+ // Confirm the new language.
35
+ languagesList.data( 'old-value', languagesList.children( ':selected' )[0].value );
36
+ confirm();
37
+ break;
38
+ case 'no':
39
+ // Revert to the old language.
40
+ languagesList.val( languagesList.data( 'old-value' ) );
41
+ cancel( 'Cancel' );
42
+ break;
43
+ }
44
+ dialogContainer.dialog( 'close' ); // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
45
+ } // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
46
+
47
+ // Initialize dialog box in the case a language is selected but not added in the list.
48
+ dialogContainer.dialog(
49
+ {
50
+ autoOpen: false,
51
+ modal: true,
52
+ draggable: false,
53
+ resizable: false,
54
+ title: __( 'Change language', 'polylang' ),
55
+ minWidth: 600,
56
+ maxWidth: '100%',
57
+ classes: {
58
+ 'ui-dialog': 'pll-confirmation-modal',
59
+ },
60
+ open: function( event, ui ) {
61
+ // Change dialog box position for rtl language
62
+ if ( jQuery( 'body' ).hasClass( 'rtl' ) ) {
63
+ jQuery( this ).parent().css(
64
+ {
65
+ right: jQuery( this ).parent().css( 'left' ),
66
+ left: 'auto'
67
+ }
68
+ );
69
+ }
70
+ },
71
+ close: function( event, ui ) {
72
+ // When we're closing the dialog box we need to cancel the language change as we click on Cancel button.
73
+ confirmDialog( 'no' );
74
+ },
75
+ buttons: [
76
+ {
77
+ text: __( 'OK', 'polylang' ),
78
+ click: function( event ) {
79
+ confirmDialog( 'yes' );
80
+ }
81
+ },
82
+ {
83
+ text: __( 'Cancel', 'polylang' ),
84
+ click: function( event ) {
85
+ confirmDialog( 'no' );
86
+ }
87
+ } ]
88
+ }
89
+ );
90
+ }
91
+ );
92
+ return { dialogContainer, dialogResult };
93
+ }
94
+
95
+ const initializeLanguageOldValue = () => {
96
+ // Keep the old language value to be able to compare to the new one and revert to it if necessary.
97
+ languagesList.attr( 'data-old-value', languagesList.children( ':selected' )[0].value );
98
+ };
99
+
100
+ ;// CONCATENATED MODULE: ./js/classic-editor.js
101
+ /**
102
+ * @package Polylang
103
+ */
104
+
105
+
106
+
107
+ // tag suggest in metabox
108
+ jQuery(
109
+ function( $ ) {
110
+ $.ajaxPrefilter(
111
+ function( options, originalOptions, jqXHR ) {
112
+ var lang = $( '.post_lang_choice' ).val();
113
+ if ( 'string' === typeof options.data && -1 !== options.url.indexOf( 'action=ajax-tag-search' ) && lang ) {
114
+ options.data = 'lang=' + lang + '&' + options.data;
115
+ }
116
+ }
117
+ );
118
+ }
119
+ );
120
+
121
+ // overrides tagBox.get
122
+ jQuery(
123
+ function( $ ) {
124
+ // overrides function to add the language
125
+ tagBox.get = function( id ) {
126
+ var tax = id.substr( id.indexOf( '-' ) + 1 );
127
+
128
+ // add the language in the $_POST variable
129
+ var data = {
130
+ action: 'get-tagcloud',
131
+ lang: $( '.post_lang_choice' ).val(),
132
+ tax: tax
133
+ }
134
+
135
+ $.post(
136
+ ajaxurl,
137
+ data,
138
+ function( r, stat ) {
139
+ if ( 0 == r || 'success' != stat ) {
140
+ r = wpAjax.broken;
141
+ }
142
+
143
+ // @see code from WordPress core https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/js/tags-box.js#L291
144
+ // @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
145
+ r = $( '<div />' ).addClass( 'the-tagcloud' ).attr( 'id', 'tagcloud-' + tax ).html( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
146
+ $( 'a', r ).on(
147
+ 'click',
148
+ function(){
149
+ tagBox.flushTags( $( this ).closest( '.inside' ).children( '.tagsdiv' ), this );
150
+ return false;
151
+ }
152
+ );
153
+
154
+ var tagCloud = $( '#tagcloud-' + tax );
155
+ // add an if else condition to allow modifying the tags outputed when switching the language
156
+ var v = tagCloud.css( 'display' );
157
+ if ( v ) {
158
+ // See the comment above when r variable is created.
159
+ $( '#tagcloud-' + tax ).replaceWith( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
160
+ $( '#tagcloud-' + tax ).css( 'display', v );
161
+ }
162
+ else {
163
+ // See the comment above when r variable is created.
164
+ $( '#' + id ).after( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
165
+ }
166
+ }
167
+ );
168
+ }
169
+ }
170
+ );
171
+
172
+ jQuery(
173
+ function( $ ) {
174
+ // collect taxonomies - code partly copied from WordPress
175
+ var taxonomies = new Array();
176
+ $( '.categorydiv' ).each(
177
+ function(){
178
+ var this_id = $( this ).attr( 'id' ), taxonomyParts, taxonomy;
179
+
180
+ taxonomyParts = this_id.split( '-' );
181
+ taxonomyParts.shift();
182
+ taxonomy = taxonomyParts.join( '-' );
183
+ taxonomies.push( taxonomy ); // store the taxonomy for future use
184
+
185
+ // add our hidden field in the new category form - for each hierarchical taxonomy
186
+ // to set the language when creating a new category
187
+ // html code inserted come from html code itself.
188
+ $( '#' + taxonomy + '-add-submit' ).before( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.before
189
+ $( '<input />' ).attr( 'type', 'hidden' )
190
+ .attr( 'id', taxonomy + '-lang' )
191
+ .attr( 'name', 'term_lang_choice' )
192
+ .attr( 'value', $( '.post_lang_choice' ).val() )
193
+ );
194
+ }
195
+ );
196
+
197
+ // Initialize current language to be able to compare if it changes.
198
+ initializeLanguageOldValue();
199
+
200
+ // ajax for changing the post's language in the languages metabox
201
+ $( '.post_lang_choice' ).on(
202
+ 'change',
203
+ function( event ) {
204
+ // Initialize the confirmation dialog box.
205
+ const confirmationModal = initializeConfimationModal();
206
+ const { dialogContainer: dialog } = confirmationModal;
207
+ let { dialogResult } = confirmationModal;
208
+ // The selected option in the dropdown list.
209
+ const selectedOption = event.target;
210
+
211
+ if ( $( this ).data( 'old-value' ) !== selectedOption.value && ! isEmptyPost() ) {
212
+ dialog.dialog( 'open' );
213
+ } else {
214
+ dialogResult = Promise.resolve();
215
+ }
216
+
217
+ // phpcs:disable PEAR.Functions.FunctionCallSignature.EmptyLine
218
+ dialogResult.then(
219
+ () => {
220
+ var lang = selectedOption.options[selectedOption.options.selectedIndex].lang; // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
221
+ var dir = $( '.pll-translation-column > span[lang="' + lang + '"]' ).attr( 'dir' ); // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
222
+
223
+ var data = { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
224
+ action: 'post_lang_choice',
225
+ lang: selectedOption.value,
226
+ post_type: $( '#post_type' ).val(),
227
+ taxonomies: taxonomies,
228
+ post_id: $( '#post_ID' ).val(),
229
+ _pll_nonce: $( '#_pll_nonce' ).val()
230
+ }
231
+
232
+ $.post(
233
+ ajaxurl,
234
+ data,
235
+ function( response ) {
236
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
237
+ $.each(
238
+ res.responses,
239
+ function() {
240
+ switch ( this.what ) {
241
+ case 'translations': // translations fields
242
+ // Data is built and come from server side and is well escaped when necessary
243
+ $( '.translations' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
244
+ init_translations();
245
+ break;
246
+ case 'taxonomy': // categories metabox for posts
247
+ var tax = this.data;
248
+ // @see wp_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L175
249
+ // @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/class-walker-category-checklist.php#L89-L111
250
+ $( '#' + tax + 'checklist' ).html( this.supplemental.all ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
251
+ // @see wp_popular_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L236
252
+ $( '#' + tax + 'checklist-pop' ).html( this.supplemental.populars ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
253
+ // @see wp_dropdown_categories https://github.com/WordPress/WordPress/blob/5.5.1/wp-includes/category-template.php#L336
254
+ // which is called by PLL_Admin_Classic_Editor::post_lang_choice to generate supplemental.dropdown
255
+ $( '#new' + tax + '_parent' ).replaceWith( this.supplemental.dropdown ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
256
+ $( '#' + tax + '-lang' ).val( $( '.post_lang_choice' ).val() ); // hidden field
257
+ break;
258
+ case 'pages': // parent dropdown list for pages
259
+ // @see wp_dropdown_pages https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/post-template.php#L1186-L1208
260
+ // @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/class-walker-page-dropdown.php#L88
261
+ $( '#parent_id' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
262
+ break;
263
+ case 'flag': // flag in front of the select dropdown
264
+ // Data is built and come from server side and is well escaped when necessary
265
+ $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
266
+ break;
267
+ case 'permalink': // Sample permalink
268
+ var div = $( '#edit-slug-box' );
269
+ if ( '-1' != this.data && div.children().length ) {
270
+ // @see get_sample_permalink_html https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/post.php#L1425-L1454
271
+ div.html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
272
+ }
273
+ break;
274
+ }
275
+ }
276
+ );
277
+
278
+ // Update the old language with the new one to be able to compare it in the next changing.
279
+ initializeLanguageOldValue();
280
+ // modifies the language in the tag cloud
281
+ $( '.tagcloud-link' ).each(
282
+ function() {
283
+ var id = $( this ).attr( 'id' );
284
+ tagBox.get( id );
285
+ }
286
+ );
287
+
288
+ // Modifies the text direction
289
+ $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
290
+ $( '#content_ifr' ).contents().find( 'html' ).attr( 'lang', lang ).attr( 'dir', dir );
291
+ $( '#content_ifr' ).contents().find( 'body' ).attr( 'dir', dir );
292
+
293
+ pll.media.resetAllAttachmentsCollections();
294
+ }
295
+ )
296
+ },
297
+ () => {} // Do nothing when promise is rejected by clicking the Cancel dialog button.
298
+ );
299
+ // phpcs:enable PEAR.Functions.FunctionCallSignature.EmptyLine
300
+
301
+ function isEmptyPost() {
302
+ const title = $( 'input#title' ).val();
303
+ const content = $( 'textarea#content' ).val();
304
+ const excerpt = $( 'textarea#excerpt' ).val();
305
+
306
+ return ! title && ! content && ! excerpt;
307
+ }
308
+ }
309
+ );
310
+
311
+ // translations autocomplete input box
312
+ function init_translations() {
313
+ $( '.tr_lang' ).each(
314
+ function(){
315
+ var tr_lang = $( this ).attr( 'id' ).substring( 8 );
316
+ var td = $( this ).parent().parent().siblings( '.pll-edit-column' );
317
+
318
+ $( this ).autocomplete(
319
+ {
320
+ minLength: 0,
321
+ source: ajaxurl + '?action=pll_posts_not_translated' +
322
+ '&post_language=' + $( '.post_lang_choice' ).val() +
323
+ '&translation_language=' + tr_lang +
324
+ '&post_type=' + $( '#post_type' ).val() +
325
+ '&_pll_nonce=' + $( '#_pll_nonce' ).val(),
326
+ select: function( event, ui ) {
327
+ $( '#htr_lang_' + tr_lang ).val( ui.item.id );
328
+ // ui.item.link is built and come from server side and is well escaped when necessary
329
+ td.html( ui.item.link ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
330
+ },
331
+ }
332
+ );
333
+
334
+ // when the input box is emptied
335
+ $( this ).on(
336
+ 'blur',
337
+ function() {
338
+ if ( ! $( this ).val() ) {
339
+ $( '#htr_lang_' + tr_lang ).val( 0 );
340
+ // Value is retrieved from HTML already generated server side
341
+ td.html( td.siblings( '.hidden' ).children().clone() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
342
+ }
343
+ }
344
+ );
345
+ }
346
+ );
347
+ }
348
+
349
+ init_translations();
350
+ }
351
+ );
352
+
353
+ /**
354
+ * @since 3.0
355
+ *
356
+ * @namespace pll
357
+ */
358
+ var pll = window.pll || {};
359
+
360
+ /**
361
+ * @since 3.0
362
+ *
363
+ * @namespace pll.media
364
+ */
365
+ _.extend( pll, { media: {} } );
366
+
367
+ /**
368
+ * @since 3.0
369
+ *
370
+ * @alias pll.media
371
+ * @memberOf pll
372
+ * @namespace
373
+ */
374
+ var media = _.extend(
375
+ pll.media, /** @lends pll.media.prototype */
376
+ {
377
+ /**
378
+ * TODO: Find a way to delete references to Attachments collections that are not used anywhere else.
379
+ *
380
+ * @type {wp.media.model.Attachments}
381
+ */
382
+ attachmentsCollections : [],
383
+
384
+ /**
385
+ * Imitates { @see wp.media.query } but log all Attachments collections created.
386
+ *
387
+ * @param {Object} [props]
388
+ * @return {wp.media.model.Attachments}
389
+ */
390
+ query: function( props ) {
391
+ var attachments = pll.media.query.delegate();
392
+
393
+ pll.media.attachmentsCollections.push( attachments );
394
+
395
+ return attachments;
396
+ },
397
+
398
+ resetAllAttachmentsCollections: function() {
399
+ this.attachmentsCollections.forEach(
400
+ function( attachmentsCollection ) {
401
+ /**
402
+ * First reset the { @see wp.media.model.Attachments } collection.
403
+ * Then, if it is mirroring a { @see wp.media.model.Query } collection,
404
+ * refresh this one too, so it will fetch new data from the server,
405
+ * and then the wp.media.model.Attachments collection will syncrhonize with the new data.
406
+ */
407
+ attachmentsCollection.reset();
408
+ if (attachmentsCollection.mirroring) {
409
+ attachmentsCollection.mirroring._hasMore = true;
410
+ attachmentsCollection.mirroring.reset();
411
+ }
412
+ }
413
+ );
414
+ }
415
+ }
416
+ );
417
+
418
+ /**
419
+ * @since 3.0
420
+ *
421
+ * @memberOf pll.media
422
+ */
423
+ media.query = _.extend(
424
+ media.query, /** @lends pll.media.query prototype */
425
+ {
426
+ /**
427
+ * @type Function References WordPress { @see wp.media.query } constructor
428
+ */
429
+ delegate: wp.media.query
430
+ }
431
+ )
432
+
433
+ // Substitute WordPress media query shortcut with our decorated function.
434
+ wp.media.query = media.query
435
+
436
+
js/build/classic-editor.min.js ADDED
@@ -0,0 +1 @@
 
1
+ "use strict";const{__}=wp.i18n,languagesList=jQuery(".post_lang_choice"),initializeConfimationModal=()=>{const t=jQuery("<div/>",{id:"pll-dialog",style:"display:none;"}).text(__("Are you sure you want to change the language of the current content?","polylang"));languagesList.after(t);const a=new Promise(((a,e)=>{const l=l=>{switch(l){case"yes":languagesList.data("old-value",languagesList.children(":selected")[0].value),a();break;case"no":languagesList.val(languagesList.data("old-value")),e("Cancel")}t.dialog("close")};t.dialog({autoOpen:!1,modal:!0,draggable:!1,resizable:!1,title:__("Change language","polylang"),minWidth:600,maxWidth:"100%",classes:{"ui-dialog":"pll-confirmation-modal"},open:function(t,a){jQuery("body").hasClass("rtl")&&jQuery(this).parent().css({right:jQuery(this).parent().css("left"),left:"auto"})},close:function(t,a){l("no")},buttons:[{text:__("OK","polylang"),click:function(t){l("yes")}},{text:__("Cancel","polylang"),click:function(t){l("no")}}]})}));return{dialogContainer:t,dialogResult:a}},initializeLanguageOldValue=()=>{languagesList.attr("data-old-value",languagesList.children(":selected")[0].value)};jQuery((function(t){t.ajaxPrefilter((function(a,e,l){var n=t(".post_lang_choice").val();"string"==typeof a.data&&-1!==a.url.indexOf("action=ajax-tag-search")&&n&&(a.data="lang="+n+"&"+a.data)}))})),jQuery((function(t){tagBox.get=function(a){var e=a.substr(a.indexOf("-")+1),l={action:"get-tagcloud",lang:t(".post_lang_choice").val(),tax:e};t.post(ajaxurl,l,(function(l,n){0!=l&&"success"==n||(l=wpAjax.broken),l=t("<div />").addClass("the-tagcloud").attr("id","tagcloud-"+e).html(l),t("a",l).on("click",(function(){return tagBox.flushTags(t(this).closest(".inside").children(".tagsdiv"),this),!1}));var i=t("#tagcloud-"+e).css("display");i?(t("#tagcloud-"+e).replaceWith(l),t("#tagcloud-"+e).css("display",i)):t("#"+a).after(l)}))}})),jQuery((function(t){var a=new Array;function e(){t(".tr_lang").each((function(){var a=t(this).attr("id").substring(8),e=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(l,n){t("#htr_lang_"+a).val(n.item.id),e.html(n.item.link)}}),t(this).on("blur",(function(){t(this).val()||(t("#htr_lang_"+a).val(0),e.html(e.siblings(".hidden").children().clone()))}))}))}t(".categorydiv").each((function(){var e,l;(e=t(this).attr("id").split("-")).shift(),l=e.join("-"),a.push(l),t("#"+l+"-add-submit").before(t("<input />").attr("type","hidden").attr("id",l+"-lang").attr("name","term_lang_choice").attr("value",t(".post_lang_choice").val()))})),initializeLanguageOldValue(),t(".post_lang_choice").on("change",(function(l){const n=initializeConfimationModal(),{dialogContainer:i}=n;let{dialogResult:s}=n;const o=l.target;t(this).data("old-value")===o.value||function(){const a=t("input#title").val(),e=t("textarea#content").val(),l=t("textarea#excerpt").val();return!a&&!e&&!l}()?s=Promise.resolve():i.dialog("open"),s.then((()=>{var l=o.options[o.options.selectedIndex].lang,n=t('.pll-translation-column > span[lang="'+l+'"]').attr("dir"),i={action:"post_lang_choice",lang:o.value,post_type:t("#post_type").val(),taxonomies:a,post_id:t("#post_ID").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,i,(function(a){var i=wpAjax.parseAjaxResponse(a,"ajax-response");t.each(i.responses,(function(){switch(this.what){case"translations":t(".translations").html(this.data),e();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 l=t("#edit-slug-box");"-1"!=this.data&&l.children().length&&l.html(this.data)}})),initializeLanguageOldValue(),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-"+n),t("#content_ifr").contents().find("html").attr("lang",l).attr("dir",n),t("#content_ifr").contents().find("body").attr("dir",n),pll.media.resetAllAttachmentsCollections()}))}),(()=>{}))})),e()}));var pll=window.pll||{};_.extend(pll,{media:{}});var media=_.extend(pll.media,{attachmentsCollections:[],query:function(t){var a=pll.media.query.delegate();return pll.media.attachmentsCollections.push(a),a},resetAllAttachmentsCollections:function(){this.attachmentsCollections.forEach((function(t){t.reset(),t.mirroring&&(t.mirroring._hasMore=!0,t.mirroring.reset())}))}});media.query=_.extend(media.query,{delegate:wp.media.query}),wp.media.query=media.query;
js/build/languages-step.js ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
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.
9
+ var languagesTable = $( '#languages' ); // Table element contains languages list to create.
10
+ var languagesListTable = $( '#languages tbody' ); // Table rows with languages list to create.
11
+ var definedLanguagesListTable = $( '#defined-languages tbody' ); // Table rows with already defined languages list.
12
+ var languagesList = $( '#lang_list' ); // Select form element with predefined languages without already created languages.
13
+ var nextStepButton = $( '[name="save_step"]' ); // The button for continuing to the next step.
14
+ var messagesContainer = $( '#messages' ); // Element where to display error messages.
15
+ var languagesMap = new Map(); // Languages map object for managing the languages to create.
16
+ var dialog = $( '#dialog' ); // Dialog box for alerting the language selected has not been added to the list.
17
+
18
+ /**
19
+ * Add a language in the list to create it in Polylang settings
20
+ *
21
+ * @param {object} language The language object
22
+ */
23
+ function addLanguage( language ) {
24
+ // language properties come from the select dropdown which is built server side and well escaped.
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(
41
+ {
42
+ type: 'hidden',
43
+ name: 'languages[]'
44
+ }
45
+ ).val( language.locale );
46
+
47
+ languagesList.val( '' );
48
+ languagesList.selectmenu( 'refresh' ); // Refresh jQuery selectmenu widget after changing the value.
49
+
50
+ languagesMap.set( language.locale, language );
51
+
52
+ // see above how languageLineHtml is built.
53
+ languagesListTable.append( languageLineHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
54
+ // Bind click event on trash icon.
55
+ languagesListTable.on(
56
+ 'click',
57
+ 'span[data-language=' + language.locale + ']',
58
+ function( event ) {
59
+ event.preventDefault();
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' ) );
70
+ // Hide error message.
71
+ hideError();
72
+ }
73
+ );
74
+ // see above how languageFieldHtml is built.
75
+ // Add hidden input field for posting the form.
76
+ languageFields.append( languageFieldHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
77
+
78
+ }
79
+
80
+ /**
81
+ * Display an error message
82
+ *
83
+ * @param {string} message The message to display
84
+ */
85
+ function showError( message ) {
86
+ messagesContainer.empty();
87
+ // html is harcoded and use of jQuery text method which is safe to add message value.
88
+ // In addition message is i18n value which is initialized server side in PLL_Wizard::add_step_languages and correctly escaped.
89
+ messagesContainer.prepend( $( '<p/>' ).addClass( 'error' ).text( message ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
90
+ }
91
+
92
+ /**
93
+ * Hide all error messages and fields in error
94
+ */
95
+ function hideError() {
96
+ messagesContainer.empty();
97
+ addLanguageForm.find( '.error' ).removeClass( 'error field-in-error' );
98
+ }
99
+
100
+ /**
101
+ * Style the field to indicate where the error is
102
+ *
103
+ * @param {object} field The jQuery element which is in error
104
+ */
105
+ function showFieldInError( field ) {
106
+ field.addClass( 'error field-in-error' );
107
+ }
108
+
109
+ /**
110
+ * Focus on a specific element
111
+ *
112
+ * @param {object} field The jQuery element which will be focused
113
+ */
114
+ function focusOnField( field ) {
115
+ field.trigger( 'focus' );
116
+ }
117
+
118
+ /**
119
+ * Disable a specific button
120
+ *
121
+ * @param {object} button
122
+ */
123
+ function disableButton( button ){
124
+ button.prop( 'disabled', true );
125
+ // Because the button is disabled we need to add the value of the button to ensure it will pass in the request.
126
+ addLanguageForm.append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
127
+ $( '<input />' ).prop(
128
+ {
129
+ type: 'hidden',
130
+ name: button.prop( 'name' ),
131
+ value: button.prop( 'value' )
132
+ }
133
+ )
134
+ );
135
+ }
136
+
137
+ /**
138
+ * Remove error when a new selection is done in languages list.
139
+ */
140
+ languagesList.on(
141
+ 'selectmenuchange',
142
+ function() {
143
+ hideError();;
144
+ }
145
+ );
146
+ /**
147
+ * Bind click event on "Add language" button
148
+ */
149
+ $( '#add-language' ).on(
150
+ 'click',
151
+ function( event ) {
152
+ hideError();
153
+ var selectedOption = event.currentTarget.form.lang_list.options[event.currentTarget.form.lang_list.selectedIndex];
154
+ if ( '' !== selectedOption.value && ! languagesMap.has( selectedOption.value ) ) {
155
+ addLanguage(
156
+ {
157
+ locale: selectedOption.value,
158
+ text: selectedOption.innerText,
159
+ name: $( selectedOption ).data( 'language-name' ),
160
+ flagUrl: $( selectedOption ).data( 'flag-html' )
161
+ }
162
+ );
163
+ // Show table of languages.
164
+ languagesTable.show();
165
+ // Put back the focus on the select language field after clicking on "Add language button".
166
+ focusOnField( $( '#lang_list-button' ) );
167
+ } else {
168
+ var message = pll_wizard_params.i18n_no_language_selected;
169
+ if ( languagesMap.has( selectedOption.value ) ) {
170
+ message = pll_wizard_params.i18n_language_already_added;
171
+ }
172
+ showError( message );
173
+ showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
174
+ focusOnField( $( '#lang_list-button' ) );
175
+
176
+ }
177
+ }
178
+ );
179
+
180
+ /**
181
+ * Bind submit event on "add_lang" form
182
+ */
183
+ addLanguageForm.on(
184
+ 'submit',
185
+ function( event ) {
186
+ // Verify if there is at least one language.
187
+ var isLanguagesAlreadyDefined = definedLanguagesListTable.children().length > 0;
188
+ var selectedLanguage = $( '#lang_list' ).val();
189
+ if ( languagesMap.size <= 0 && ! isLanguagesAlreadyDefined ) {
190
+ if ( '' === selectedLanguage ) {
191
+ showError( pll_wizard_params.i18n_no_language_added );
192
+ showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
193
+ focusOnField( $( '#lang_list-button' ) );
194
+ } else {
195
+ showError( pll_wizard_params.i18n_add_language_needed );
196
+ showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
197
+ focusOnField( $( '#add-language' ) ); // Put the focus on the "Add language" button.
198
+ }
199
+ return false;
200
+ }
201
+ // Verify if the language has been added in the list otherwise display a dialog box to confirm what to do.
202
+ if ( '' !== selectedLanguage ) {
203
+ // Verify we don't add a duplicate language before opening the dialog box otherwise display an error message.
204
+ if ( ! languagesMap.has( selectedLanguage ) ) {
205
+ dialog.dialog( 'open' );
206
+ } else {
207
+ showError( pll_wizard_params.i18n_language_already_added );
208
+ showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
209
+ focusOnField( $( '#lang_list-button' ) );
210
+ }
211
+ return false;
212
+ }
213
+ disableButton( nextStepButton );
214
+ }
215
+ );
216
+
217
+ // Is there an error return by PHP ?
218
+ var searchParams = new URLSearchParams( document.location.search );
219
+ if ( searchParams.has( 'activate_error' ) ) {
220
+ // If the error code exists, display it.
221
+ if ( undefined !== pll_wizard_params[ searchParams.get( 'activate_error' ) ] ) {
222
+ showError( pll_wizard_params[ searchParams.get( 'activate_error' ) ] );
223
+ }
224
+ }
225
+
226
+ function confirmDialog( what ) {
227
+ switch ( what ) {
228
+ case 'yes':
229
+ var selectedOption = $( '#lang_list' ).children( ':selected' );
230
+ addLanguage(
231
+ {
232
+ locale: selectedOption[0].value,
233
+ text: selectedOption[0].innerText,
234
+ name: $( selectedOption ).data( 'language-name' ),
235
+ flagUrl: $( selectedOption ).data( 'flag-html' )
236
+ }
237
+ );
238
+ break;
239
+ case 'no':
240
+ // Empty select form field and submit again the form.
241
+ languagesList.val( '' );
242
+ break;
243
+ case 'ignore':
244
+ }
245
+ dialog.dialog( 'close' );
246
+ if ( 'ignore' === what ) {
247
+ focusOnField( $( '#lang_list-button' ) );
248
+ } else {
249
+ addLanguageForm.submit();
250
+ }
251
+ }
252
+
253
+ // Initialize dialog box in the case a language is selected but not added in the list.
254
+ dialog.dialog(
255
+ {
256
+ autoOpen: false,
257
+ modal: true,
258
+ draggable: false,
259
+ resizable: false,
260
+ title: pll_wizard_params.i18n_dialog_title,
261
+ minWidth: 600,
262
+ maxWidth: '100%',
263
+ open: function( event, ui ) {
264
+ // Change dialog box position for rtl language
265
+ if ( $( 'body' ).hasClass( 'rtl' ) ) {
266
+ $( this ).parent().css(
267
+ {
268
+ right: $( this ).parent().css( 'left' ),
269
+ left: 'auto'
270
+ }
271
+ );
272
+ }
273
+ // Display language name and flag information in dialog box.
274
+ $( this ).find( '#dialog-language' ).text( $( '#lang_list' ).children( ':selected' )[0].innerText );
275
+ // language properties come from the select dropdown #lang_list which is built server side and well escaped.
276
+ // see template view-wizard-step-languages.php.
277
+ $( this ).find( '#dialog-language-flag' ).empty().prepend( $( '#lang_list' ).children( ':selected' ).data( 'flag-html' ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
278
+ },
279
+ buttons: [
280
+ {
281
+ text: pll_wizard_params.i18n_dialog_yes_button,
282
+ click: function( event ) {
283
+ confirmDialog( 'yes' );
284
+ }
285
+ },
286
+ {
287
+ text: pll_wizard_params.i18n_dialog_no_button,
288
+ click: function( event ) {
289
+ confirmDialog( 'no' );
290
+ }
291
+ },
292
+ {
293
+ text: pll_wizard_params.i18n_dialog_ignore_button,
294
+ click: function( event ) {
295
+ confirmDialog( 'ignore' );
296
+ }
297
+ }
298
+ ]
299
+ }
300
+ )
301
+ }
302
+ );
303
+
js/build/languages-step.min.js ADDED
@@ -0,0 +1 @@
 
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")}}]})}));
js/build/nav-menu.js ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Handles the options in the language switcher nav menu metabox.
3
+ *
4
+ * @package Polylang
5
+ */
6
+
7
+ jQuery(
8
+ function( $ ) {
9
+ $( '#update-nav-menu' ).on(
10
+ 'click',
11
+ function( e ) {
12
+ if ( e.target && e.target.className && -1 != e.target.className.indexOf( 'item-edit' ) ) {
13
+ $( "input[value='#pll_switcher'][type=text]" ).parent().parent().parent().each(
14
+ function(){
15
+ var item = $( this ).attr( 'id' ).substring( 19 );
16
+ $( this ).children( 'p:not( .field-move )' ).remove(); // remove default fields we don't need
17
+
18
+ // item is a number part of id of parent menu item built by WordPress
19
+ // pll_data is built server side with i18n strings without HTML and data retrieved from post meta
20
+ // the usage of attr method is safe before append call.
21
+ h = $( '<input>' ).attr(
22
+ {
23
+ type: 'hidden',
24
+ id: 'edit-menu-item-title-' + item,
25
+ name: 'menu-item-title[' + item + ']',
26
+ value: pll_data.title
27
+ }
28
+ );
29
+ $( this ).append( h ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
30
+
31
+ h = $( '<input>' ).attr(
32
+ {
33
+ type: 'hidden',
34
+ id: 'edit-menu-item-url-' + item,
35
+ name: 'menu-item-url[' + item + ']',
36
+ value: '#pll_switcher'
37
+ }
38
+ );
39
+ $( this ).append( h ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
40
+
41
+ // a hidden field which exits only if our jQuery code has been executed
42
+ h = $( '<input>' ).attr(
43
+ {
44
+ type: 'hidden',
45
+ id: 'edit-menu-item-pll-detect-' + item,
46
+ name: 'menu-item-pll-detect[' + item + ']',
47
+ value: 1
48
+ }
49
+ );
50
+ $( this ).append( h ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
51
+
52
+ ids = Array( 'hide_if_no_translation', 'hide_current', 'force_home', 'show_flags', 'show_names', 'dropdown' ); // reverse order
53
+
54
+ // add the fields
55
+ for ( var i = 0, idsLength = ids.length; i < idsLength; i++ ) {
56
+ p = $( '<p>' ).attr( 'class', 'description' );
57
+ // p is hardcoded just above by using attr method which is safe.
58
+ $( this ).prepend( p ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
59
+ // item is a number part of id of parent menu item built by WordPress
60
+ // pll_data is built server side with i18n strings without HTML
61
+ label = $( '<label>' ).attr( 'for', 'edit-menu-item-' + ids[ i ] + '-' + item ).text( ' ' + pll_data.strings[ ids[ i ] ] );
62
+ p.append( label ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
63
+ cb = $( '<input>' ).attr(
64
+ {
65
+ type: 'checkbox',
66
+ id: 'edit-menu-item-' + ids[ i ] + '-' + item,
67
+ name: 'menu-item-' + ids[ i ] + '[' + item + ']',
68
+ value: 1
69
+ }
70
+ );
71
+ if ( ( typeof( pll_data.val[ item ] ) != 'undefined' && pll_data.val[ item ][ ids[ i ] ] == 1 ) || ( typeof( pll_data.val[ item ] ) == 'undefined' && ids[ i ] == 'show_names' ) ) { // show_names as default value
72
+ cb.prop( 'checked', true );
73
+ }
74
+ // See reasons above. Checkbox are totaly hardcoded here with safe value
75
+ label.prepend( cb ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
76
+ }
77
+ }
78
+ );
79
+
80
+ // disallow unchecking both show names and show flags
81
+ $( '.menu-item-data-object-id' ).each(
82
+ function() {
83
+ var id = $( this ).val();
84
+ var options = ['names-', 'flags-'];
85
+ $.each(
86
+ options,
87
+ function( i, v ) {
88
+ $( '#edit-menu-item-show_' + v + id ).on(
89
+ 'change',
90
+ function() {
91
+ if ( true != $( this ).prop( 'checked' ) ) {
92
+ $( '#edit-menu-item-show_' + options[ 1 - i ] + id ).prop( 'checked', true );
93
+ }
94
+ }
95
+ );
96
+ }
97
+ );
98
+ }
99
+ );
100
+ }
101
+ }
102
+ );
103
+ }
104
+ );
105
+
js/build/nav-menu.min.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery((function(e){e("#update-nav-menu").on("click",(function(t){t.target&&t.target.className&&-1!=t.target.className.indexOf("item-edit")&&(e("input[value='#pll_switcher'][type=text]").parent().parent().parent().each((function(){var t=e(this).attr("id").substring(19);e(this).children("p:not( .field-move )").remove(),h=e("<input>").attr({type:"hidden",id:"edit-menu-item-title-"+t,name:"menu-item-title["+t+"]",value:pll_data.title}),e(this).append(h),h=e("<input>").attr({type:"hidden",id:"edit-menu-item-url-"+t,name:"menu-item-url["+t+"]",value:"#pll_switcher"}),e(this).append(h),h=e("<input>").attr({type:"hidden",id:"edit-menu-item-pll-detect-"+t,name:"menu-item-pll-detect["+t+"]",value:1}),e(this).append(h),ids=Array("hide_if_no_translation","hide_current","force_home","show_flags","show_names","dropdown");for(var i=0,a=ids.length;i<a;i++)p=e("<p>").attr("class","description"),e(this).prepend(p),label=e("<label>").attr("for","edit-menu-item-"+ids[i]+"-"+t).text(" "+pll_data.strings[ids[i]]),p.append(label),cb=e("<input>").attr({type:"checkbox",id:"edit-menu-item-"+ids[i]+"-"+t,name:"menu-item-"+ids[i]+"["+t+"]",value:1}),(void 0!==pll_data.val[t]&&1==pll_data.val[t][ids[i]]||void 0===pll_data.val[t]&&"show_names"==ids[i])&&cb.prop("checked",!0),label.prepend(cb)})),e(".menu-item-data-object-id").each((function(){var t=e(this).val(),i=["names-","flags-"];e.each(i,(function(a,n){e("#edit-menu-item-show_"+n+t).on("change",(function(){1!=e(this).prop("checked")&&e("#edit-menu-item-show_"+i[1-a]+t).prop("checked",!0)}))}))})))}))}));
js/build/post.js ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @package Polylang
3
+ */
4
+
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.on(
45
+ 'change',
46
+ function() {
47
+ filter_terms( $( this ).val() );
48
+ filter_pages( $( this ).val() );
49
+ }
50
+ );
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Filters the category checklist.
56
+ */
57
+ function filter_terms( lang ) {
58
+ if ( "undefined" != typeof( pll_term_languages ) ) {
59
+ $.each(
60
+ pll_term_languages,
61
+ function( lg, term_tax ) {
62
+ $.each(
63
+ term_tax,
64
+ function( tax, terms ) {
65
+ $.each(
66
+ terms,
67
+ function( i ) {
68
+ id = '#' + tax + '-' + pll_term_languages[ lg ][ tax ][ i ];
69
+ lang == lg ? $( id ).show() : $( id ).hide();
70
+ }
71
+ );
72
+ }
73
+ );
74
+ }
75
+ );
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Filters the parent page dropdown list.
81
+ */
82
+ function filter_pages( lang ) {
83
+ if ( "undefined" != typeof( pll_page_languages ) ) {
84
+ $.each(
85
+ pll_page_languages,
86
+ function( lg, pages ) {
87
+ $.each(
88
+ pages,
89
+ function( i ) {
90
+ v = $( '#post_parent option[value="' + pll_page_languages[ lg ][ i ] + '"]' );
91
+ lang == lg ? v.show() : v.hide();
92
+ }
93
+ );
94
+ }
95
+ );
96
+ }
97
+ }
98
+ }
99
+ );
100
+ }
101
+ );
102
+
103
+ /**
104
+ * Update rows of translated posts when the language is modified in quick edit
105
+ * Acts on ajaxSuccess event
106
+ */
107
+ jQuery(
108
+ function( $ ) {
109
+ $( document ).ajaxSuccess(
110
+ function( event, xhr, settings ) {
111
+ function update_rows( post_id ) {
112
+ // collect old translations
113
+ var translations = new Array();
114
+ $( '.translation_' + post_id ).each(
115
+ function() {
116
+ translations.push( $( this ).parent().parent().attr( 'id' ).substring( 5 ) );
117
+ }
118
+ );
119
+
120
+ var data = {
121
+ action: 'pll_update_post_rows',
122
+ post_id: post_id,
123
+ translations: translations.join( ',' ),
124
+ post_type: $( "input[name='post_type']" ).val(),
125
+ screen: $( "input[name='screen']" ).val(),
126
+ _pll_nonce: $( "input[name='_inline_edit']" ).val() // reuse quick edit nonce
127
+ };
128
+
129
+ // get the modified rows in ajax and update them
130
+ $.post(
131
+ ajaxurl,
132
+ data,
133
+ function( response ) {
134
+ if ( response ) {
135
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
136
+ $.each(
137
+ res.responses,
138
+ function() {
139
+ if ( 'row' == this.what ) {
140
+ // data is built with a call to WP_Posts_List_Table::single_row method
141
+ // which uses internally other WordPress methods which escape correctly values.
142
+ // For Polylang language columns the HTML code is correctly escaped in PLL_Admin_Filters_Columns::post_column method.
143
+ $( "#post-" + this.supplemental.post_id ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
144
+ }
145
+ }
146
+ );
147
+ }
148
+ }
149
+ );
150
+ }
151
+
152
+ if ( 'string' == typeof( settings.data ) ) { // Need to check the type due to block editor sometime sending FormData objects
153
+ var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
154
+ if ( 'undefined' != typeof( data['action'] ) && 'inline-save' == data['action'] ) {
155
+ update_rows( data['post_ID'] );
156
+ }
157
+ }
158
+ }
159
+ );
160
+ }
161
+ );
162
+
163
+ /**
164
+ * Media list table
165
+ * When clicking on attach link, filters find post list per media language
166
+ */
167
+ jQuery(
168
+ function( $ ) {
169
+ $.ajaxPrefilter(
170
+ function ( options, originalOptions, jqXHR ) {
171
+ if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=find_posts' ) ) {
172
+ options.data = 'pll_post_id=' + $( '#affected' ).val() + '&' + options.data;
173
+ }
174
+ }
175
+ );
176
+ }
177
+ );
178
+
js/build/post.min.js ADDED
@@ -0,0 +1 @@
 
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.on("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/build/term.js ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
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() {
126
+ $( '.tr_lang' ).each(
127
+ function(){
128
+ var tr_lang = $( this ).attr( 'id' ).substring( 8 );
129
+ var td = $( this ).parent().parent().siblings( '.pll-edit-column' );
130
+
131
+ $( this ).autocomplete(
132
+ {
133
+ minLength: 0,
134
+ source: ajaxurl + '?action=pll_terms_not_translated' +
135
+ '&term_language=' + $( '#term_lang_choice' ).val() +
136
+ '&term_id=' + $( "input[name='tag_ID']" ).val() +
137
+ '&taxonomy=' + $( "input[name='taxonomy']" ).val() +
138
+ '&translation_language=' + tr_lang +
139
+ '&post_type=' + typenow +
140
+ '&_pll_nonce=' + $( '#_pll_nonce' ).val(),
141
+ select: function( event, ui ) {
142
+ $( '#htr_lang_' + tr_lang ).val( ui.item.id );
143
+ // ui.item.link is built and come from server side and is well escaped when necessary
144
+ td.html( ui.item.link ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
145
+ },
146
+ }
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 );
155
+ // Value is retrieved from HTML already generated server side
156
+ td.html( td.siblings( '.hidden' ).children().clone() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
157
+ }
158
+ }
159
+ );
160
+ }
161
+ );
162
+ }
163
+
164
+ init_translations();
165
+
166
+ // ajax for changing the term's language
167
+ $( '#term_lang_choice' ).on(
168
+ 'change',
169
+ function() {
170
+ var value = $( this ).val();
171
+ var lang = $( this ).children( 'option[value="' + value + '"]' ).attr( 'lang' );
172
+ var dir = $( '.pll-translation-column > span[lang="' + lang + '"]' ).attr( 'dir' );
173
+
174
+ var data = {
175
+ action: 'term_lang_choice',
176
+ lang: value,
177
+ from_tag: $( "input[name='from_tag']" ).val(),
178
+ term_id: $( "input[name='tag_ID']" ).val(),
179
+ taxonomy: $( "input[name='taxonomy']" ).val(),
180
+ post_type: typenow,
181
+ _pll_nonce: $( '#_pll_nonce' ).val()
182
+ };
183
+
184
+ $.post(
185
+ ajaxurl,
186
+ data,
187
+ function( response ) {
188
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
189
+ $.each(
190
+ res.responses,
191
+ function() {
192
+ switch ( this.what ) {
193
+ case 'translations': // translations fields
194
+ // Data is built and come from server side and is well escaped when necessary
195
+ $( "#term-translations" ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
196
+ init_translations();
197
+ break;
198
+ case 'parent': // parent dropdown list for hierarchical taxonomies
199
+ // data correctly escaped in PLL_Admin_Filters_Term::term_lang_choice method which uses wp_dropdown_categories function.
200
+ $( '#parent' ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
201
+ break;
202
+ case 'tag_cloud': // popular items
203
+ // data correctly escaped in PLL_Admin_Filters_Term::term_lang_choice method which uses wp_tag_cloud and wp_generate_tag_cloud functions.
204
+ $( '.tagcloud' ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
205
+ break;
206
+ case 'flag': // flag in front of the select dropdown
207
+ // Data is built and come from server side and is well escaped when necessary
208
+ $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
209
+ break;
210
+ }
211
+ }
212
+ );
213
+
214
+ // Modifies the text direction
215
+ $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
216
+ }
217
+ );
218
+ }
219
+ );
220
+ }
221
+ );
222
+
js/build/term.min.js ADDED
@@ -0,0 +1 @@
 
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").on("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/build/user.js ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Adds one biography input field per language in the user profile.
3
+ *
4
+ * @package Polylang
5
+ */
6
+
7
+ jQuery(
8
+ function( $ ) {
9
+ // biography
10
+ // FIXME there is probably a more efficient way to do this
11
+ var td = $( '#description' ).parent();
12
+ var d = $( '#description' ).clone();
13
+ var span = td.children( '.description' ).clone();
14
+ td.children().remove();
15
+
16
+ $( '.biography' ).each(
17
+ function(){
18
+ lang = $( this ).attr( 'name' ).split( '___' );
19
+ desc = d.clone();
20
+ desc.attr( 'name', 'description_' + lang[0] );
21
+ desc.attr( 'id', 'description_' + lang[0] );
22
+ // Whitelist because description and lang value is already escaped by the side of PHP
23
+ desc.html( $( this ).val() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
24
+ td.append( $( '<div></div>' ).text( lang[1] ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
25
+ td.append( desc ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
26
+ }
27
+ );
28
+
29
+ td.append( '<br />' );
30
+ // Whitelist because description come from html code generated by WordPress
31
+ td.append( span ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
32
+ }
33
+ );
34
+
js/build/user.min.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery((function(e){var n=e("#description").parent(),i=e("#description").clone(),t=n.children(".description").clone();n.children().remove(),e(".biography").each((function(){lang=e(this).attr("name").split("___"),desc=i.clone(),desc.attr("name","description_"+lang[0]),desc.attr("id","description_"+lang[0]),desc.html(e(this).val()),n.append(e("<div></div>").text(lang[1])),n.append(desc)})),n.append("<br />"),n.append(t)}));
js/build/widgets.js ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Adds a flag to the widgets filtered by a language.
3
+ *
4
+ * @package Polylang
5
+ */
6
+
7
+ jQuery(
8
+ function( $ ) {
9
+ var widgets_container, widgets_selector, flags;
10
+
11
+ if ( 'undefined' !== typeof pll_widgets && pll_widgets.hasOwnProperty( 'flags' ) ) {
12
+ flags = pll_widgets.flags;
13
+ }
14
+
15
+ /**
16
+ * Prepend widget titles with a flag once a language is selected.
17
+ *
18
+ * @param {object} widget The widget element.
19
+ * @return {void} Nothing.
20
+ */
21
+ function add_flag( widget ) {
22
+ if ( ! flags ) {
23
+ return;
24
+ }
25
+ widget = $( widget );
26
+ var title = $( '.widget-top .widget-title h3', widget ),
27
+ locale = $( '.pll-lang-choice option:selected', widget ).val(),
28
+ // Icon is HTML built and come from server side and is well escaped when necessary
29
+ icon = ( locale && flags.hasOwnProperty( locale ) ) ? flags[ locale ] : null;
30
+
31
+ if ( icon ) {
32
+ icon += ' &nbsp; ';
33
+ var current = $( '.pll-lang', title );
34
+ if ( current.length ) {
35
+ current.html( icon ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
36
+ } else {
37
+ flag = $( '<span />' ).addClass( 'pll-lang' ).html( icon ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
38
+ // See the comment above about the icon which is safe. So it is also safe to prepend flag which uses icon.
39
+ title.prepend( flag ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
40
+ }
41
+ } else {
42
+ $( '.pll-lang', title ).remove();
43
+ }
44
+ }
45
+
46
+ if ( 'undefined' !== typeof wp.customize ) {
47
+
48
+ widgets_container = $( '#customize-controls' );
49
+ widgets_selector = '.customize-control .widget';
50
+
51
+ /**
52
+ * WP Customizer add control listener.
53
+ *
54
+ * @link https://wordpress.stackexchange.com/questions/256536/callback-after-wordpress-customizer-complete-loading
55
+ *
56
+ * @param {object} control The control type.
57
+ * @return {void} Nothing.
58
+ */
59
+ function customize_add_flag( control ) {
60
+ if ( ! control.extended( wp.customize.Widgets.WidgetControl ) ) {
61
+ return;
62
+ }
63
+
64
+ /*
65
+ * Make sure the widget's contents are embedded; normally this is done
66
+ * when the control is expanded, for DOM performance reasons.
67
+ */
68
+ control.embedWidgetContent();
69
+
70
+ // Now we know for sure the widget is fully embedded.
71
+ add_flag( control.container.find( '.widget' ) );
72
+ }
73
+ wp.customize.control.each( customize_add_flag );
74
+ wp.customize.control.bind( 'add', customize_add_flag );
75
+
76
+ } else {
77
+
78
+ widgets_container = $( '#widgets-right' );
79
+ widgets_selector = '.widget';
80
+
81
+ }
82
+
83
+ // Add flags on load.
84
+ $( widgets_selector, widgets_container ).each(
85
+ function() {
86
+ add_flag( this );
87
+ }
88
+ );
89
+
90
+ // Update flags.
91
+ widgets_container.on(
92
+ 'change',
93
+ '.pll-lang-choice',
94
+ function() {
95
+ add_flag( $( this ).parents( '.widget' ) );
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
+ );
133
+
js/build/widgets.min.js ADDED
@@ -0,0 +1 @@
 
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)}))}))}));
js/classic-editor.js CHANGED
@@ -2,6 +2,11 @@
2
  * @package Polylang
3
  */
4
 
 
 
 
 
 
5
  // tag suggest in metabox
6
  jQuery(
7
  function( $ ) {
@@ -41,7 +46,8 @@ jQuery(
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;
@@ -67,7 +73,7 @@ jQuery(
67
  );
68
 
69
  jQuery(
70
- function ( $ ) {
71
  // collect taxonomies - code partly copied from WordPress
72
  var taxonomies = new Array();
73
  $( '.categorydiv' ).each(
@@ -91,82 +97,117 @@ jQuery(
91
  }
92
  );
93
 
 
 
 
94
  // ajax for changing the post's language in the languages metabox
95
- $( '.post_lang_choice' ).change(
96
- function() {
97
- var value = $( this ).val();
98
- var lang = $( this ).children( 'option[value="' + value + '"]' ).attr( 'lang' );
99
- var dir = $( '.pll-translation-column > span[lang="' + lang + '"]' ).attr( 'dir' );
100
-
101
- var data = {
102
- action: 'post_lang_choice',
103
- lang: value,
104
- post_type: $( '#post_type' ).val(),
105
- taxonomies: taxonomies,
106
- post_id: $( '#post_ID' ).val(),
107
- _pll_nonce: $( '#_pll_nonce' ).val()
 
108
  }
109
 
110
- $.post(
111
- ajaxurl,
112
- data,
113
- function( response ) {
114
- var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
115
- $.each(
116
- res.responses,
117
- function() {
118
- switch ( this.what ) {
119
- case 'translations': // translations fields
120
- // Data is built and come from server side and is well escaped when necessary
121
- $( '.translations' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
122
- init_translations();
123
- break;
124
- case 'taxonomy': // categories metabox for posts
125
- var tax = this.data;
126
- // @see wp_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L175
127
- // @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/class-walker-category-checklist.php#L89-L111
128
- $( '#' + tax + 'checklist' ).html( this.supplemental.all ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
129
- // @see wp_popular_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L236
130
- $( '#' + tax + 'checklist-pop' ).html( this.supplemental.populars ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
131
- // @see wp_dropdown_categories https://github.com/WordPress/WordPress/blob/5.5.1/wp-includes/category-template.php#L336
132
- // which is called by PLL_Admin_Classic_Editor::post_lang_choice to generate supplemental.dropdown
133
- $( '#new' + tax + '_parent' ).replaceWith( this.supplemental.dropdown ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
134
- $( '#' + tax + '-lang' ).val( $( '.post_lang_choice' ).val() ); // hidden field
135
- break;
136
- case 'pages': // parent dropdown list for pages
137
- // @see wp_dropdown_pages https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/post-template.php#L1186-L1208
138
- // @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/class-walker-page-dropdown.php#L88
139
- $( '#parent_id' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
140
- break;
141
- case 'flag': // flag in front of the select dropdown
142
- // Data is built and come from server side and is well escaped when necessary
143
- $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
144
- break;
145
- case 'permalink': // Sample permalink
146
- var div = $( '#edit-slug-box' );
147
- if ( '-1' != this.data && div.children().length ) {
148
- // @see get_sample_permalink_html https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/post.php#L1425-L1454
149
- div.html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  }
151
- break;
152
- }
153
- }
154
- );
155
 
156
- // modifies the language in the tag cloud
157
- $( '.tagcloud-link' ).each(
158
- function() {
159
- var id = $( this ).attr( 'id' );
160
- tagBox.get( id );
161
- }
162
- );
 
 
163
 
164
- // Modifies the text direction
165
- $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
166
- $( '#content_ifr' ).contents().find( 'html' ).attr( 'lang', lang ).attr( 'dir', dir );
167
- $( '#content_ifr' ).contents().find( 'body' ).attr( 'dir', dir );
168
- }
 
 
 
 
 
169
  );
 
 
 
 
 
 
 
 
 
170
  }
171
  );
172
 
@@ -211,3 +252,87 @@ jQuery(
211
  init_translations();
212
  }
213
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  * @package Polylang
3
  */
4
 
5
+ import {
6
+ initializeLanguageOldValue,
7
+ initializeConfimationModal
8
+ } from './lib/confirmation-modal';
9
+
10
  // tag suggest in metabox
11
  jQuery(
12
  function( $ ) {
46
  // @see code from WordPress core https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/js/tags-box.js#L291
47
  // @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
48
  r = $( '<div />' ).addClass( 'the-tagcloud' ).attr( 'id', 'tagcloud-' + tax ).html( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
49
+ $( 'a', r ).on(
50
+ 'click',
51
  function(){
52
  tagBox.flushTags( $( this ).closest( '.inside' ).children( '.tagsdiv' ), this );
53
  return false;
73
  );
74
 
75
  jQuery(
76
+ function( $ ) {
77
  // collect taxonomies - code partly copied from WordPress
78
  var taxonomies = new Array();
79
  $( '.categorydiv' ).each(
97
  }
98
  );
99
 
100
+ // Initialize current language to be able to compare if it changes.
101
+ initializeLanguageOldValue();
102
+
103
  // ajax for changing the post's language in the languages metabox
104
+ $( '.post_lang_choice' ).on(
105
+ 'change',
106
+ function( event ) {
107
+ // Initialize the confirmation dialog box.
108
+ const confirmationModal = initializeConfimationModal();
109
+ const { dialogContainer: dialog } = confirmationModal;
110
+ let { dialogResult } = confirmationModal;
111
+ // The selected option in the dropdown list.
112
+ const selectedOption = event.target;
113
+
114
+ if ( $( this ).data( 'old-value' ) !== selectedOption.value && ! isEmptyPost() ) {
115
+ dialog.dialog( 'open' );
116
+ } else {
117
+ dialogResult = Promise.resolve();
118
  }
119
 
120
+ // phpcs:disable PEAR.Functions.FunctionCallSignature.EmptyLine
121
+ dialogResult.then(
122
+ () => {
123
+ var lang = selectedOption.options[selectedOption.options.selectedIndex].lang; // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
124
+ var dir = $( '.pll-translation-column > span[lang="' + lang + '"]' ).attr( 'dir' ); // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
125
+
126
+ var data = { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
127
+ action: 'post_lang_choice',
128
+ lang: selectedOption.value,
129
+ post_type: $( '#post_type' ).val(),
130
+ taxonomies: taxonomies,
131
+ post_id: $( '#post_ID' ).val(),
132
+ _pll_nonce: $( '#_pll_nonce' ).val()
133
+ }
134
+
135
+ $.post(
136
+ ajaxurl,
137
+ data,
138
+ function( response ) {
139
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
140
+ $.each(
141
+ res.responses,
142
+ function() {
143
+ switch ( this.what ) {
144
+ case 'translations': // translations fields
145
+ // Data is built and come from server side and is well escaped when necessary
146
+ $( '.translations' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
147
+ init_translations();
148
+ break;
149
+ case 'taxonomy': // categories metabox for posts
150
+ var tax = this.data;
151
+ // @see wp_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L175
152
+ // @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/class-walker-category-checklist.php#L89-L111
153
+ $( '#' + tax + 'checklist' ).html( this.supplemental.all ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
154
+ // @see wp_popular_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L236
155
+ $( '#' + tax + 'checklist-pop' ).html( this.supplemental.populars ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
156
+ // @see wp_dropdown_categories https://github.com/WordPress/WordPress/blob/5.5.1/wp-includes/category-template.php#L336
157
+ // which is called by PLL_Admin_Classic_Editor::post_lang_choice to generate supplemental.dropdown
158
+ $( '#new' + tax + '_parent' ).replaceWith( this.supplemental.dropdown ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
159
+ $( '#' + tax + '-lang' ).val( $( '.post_lang_choice' ).val() ); // hidden field
160
+ break;
161
+ case 'pages': // parent dropdown list for pages
162
+ // @see wp_dropdown_pages https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/post-template.php#L1186-L1208
163
+ // @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/class-walker-page-dropdown.php#L88
164
+ $( '#parent_id' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
165
+ break;
166
+ case 'flag': // flag in front of the select dropdown
167
+ // Data is built and come from server side and is well escaped when necessary
168
+ $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
169
+ break;
170
+ case 'permalink': // Sample permalink
171
+ var div = $( '#edit-slug-box' );
172
+ if ( '-1' != this.data && div.children().length ) {
173
+ // @see get_sample_permalink_html https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/post.php#L1425-L1454
174
+ div.html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
175
+ }
176
+ break;
177
  }
178
+ }
179
+ );
 
 
180
 
181
+ // Update the old language with the new one to be able to compare it in the next changing.
182
+ initializeLanguageOldValue();
183
+ // modifies the language in the tag cloud
184
+ $( '.tagcloud-link' ).each(
185
+ function() {
186
+ var id = $( this ).attr( 'id' );
187
+ tagBox.get( id );
188
+ }
189
+ );
190
 
191
+ // Modifies the text direction
192
+ $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
193
+ $( '#content_ifr' ).contents().find( 'html' ).attr( 'lang', lang ).attr( 'dir', dir );
194
+ $( '#content_ifr' ).contents().find( 'body' ).attr( 'dir', dir );
195
+
196
+ pll.media.resetAllAttachmentsCollections();
197
+ }
198
+ )
199
+ },
200
+ () => {} // Do nothing when promise is rejected by clicking the Cancel dialog button.
201
  );
202
+ // phpcs:enable PEAR.Functions.FunctionCallSignature.EmptyLine
203
+
204
+ function isEmptyPost() {
205
+ const title = $( 'input#title' ).val();
206
+ const content = $( 'textarea#content' ).val();
207
+ const excerpt = $( 'textarea#excerpt' ).val();
208
+
209
+ return ! title && ! content && ! excerpt;
210
+ }
211
  }
212
  );
213
 
252
  init_translations();
253
  }
254
  );
255
+
256
+ /**
257
+ * @since 3.0
258
+ *
259
+ * @namespace pll
260
+ */
261
+ var pll = window.pll || {};
262
+
263
+ /**
264
+ * @since 3.0
265
+ *
266
+ * @namespace pll.media
267
+ */
268
+ _.extend( pll, { media: {} } );
269
+
270
+ /**
271
+ * @since 3.0
272
+ *
273
+ * @alias pll.media
274
+ * @memberOf pll
275
+ * @namespace
276
+ */
277
+ var media = _.extend(
278
+ pll.media, /** @lends pll.media.prototype */
279
+ {
280
+ /**
281
+ * TODO: Find a way to delete references to Attachments collections that are not used anywhere else.
282
+ *
283
+ * @type {wp.media.model.Attachments}
284
+ */
285
+ attachmentsCollections : [],
286
+
287
+ /**
288
+ * Imitates { @see wp.media.query } but log all Attachments collections created.
289
+ *
290
+ * @param {Object} [props]
291
+ * @return {wp.media.model.Attachments}
292
+ */
293
+ query: function( props ) {
294
+ var attachments = pll.media.query.delegate();
295
+
296
+ pll.media.attachmentsCollections.push( attachments );
297
+
298
+ return attachments;
299
+ },
300
+
301
+ resetAllAttachmentsCollections: function() {
302
+ this.attachmentsCollections.forEach(
303
+ function( attachmentsCollection ) {
304
+ /**
305
+ * First reset the { @see wp.media.model.Attachments } collection.
306
+ * Then, if it is mirroring a { @see wp.media.model.Query } collection,
307
+ * refresh this one too, so it will fetch new data from the server,
308
+ * and then the wp.media.model.Attachments collection will syncrhonize with the new data.
309
+ */
310
+ attachmentsCollection.reset();
311
+ if (attachmentsCollection.mirroring) {
312
+ attachmentsCollection.mirroring._hasMore = true;
313
+ attachmentsCollection.mirroring.reset();
314
+ }
315
+ }
316
+ );
317
+ }
318
+ }
319
+ );
320
+
321
+ /**
322
+ * @since 3.0
323
+ *
324
+ * @memberOf pll.media
325
+ */
326
+ media.query = _.extend(
327
+ media.query, /** @lends pll.media.query prototype */
328
+ {
329
+ /**
330
+ * @type Function References WordPress { @see wp.media.query } constructor
331
+ */
332
+ delegate: wp.media.query
333
+ }
334
+ )
335
+
336
+ // Substitute WordPress media query shortcut with our decorated function.
337
+ wp.media.query = media.query
338
+
js/classic-editor.min.js DELETED
@@ -1 +0,0 @@
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/lib/confirmation-modal.js ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @package Polylang
3
+ */
4
+
5
+ // We can't use underscore or lodash in this common code because it depends of the context classic or block editor.
6
+ // Classic editor underscore is loaded, Block editor lodash is loaded.
7
+ const { __ } = wp.i18n;
8
+
9
+ const languagesList = jQuery( '.post_lang_choice' );
10
+
11
+ // Dialog box for alerting the user about a risky changing.
12
+ export const initializeConfimationModal = () => {
13
+ // Create dialog container.
14
+ const dialogContainer = jQuery(
15
+ '<div/>',
16
+ {
17
+ id: 'pll-dialog',
18
+ style: 'display:none;'
19
+ }
20
+ ).text( __( 'Are you sure you want to change the language of the current content?', 'polylang' ) );
21
+
22
+ // Put it after languages list dropdown.
23
+ // PHPCS ignore dialogContainer is a new safe HTML code generated above.
24
+ languagesList.after( dialogContainer ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
25
+
26
+ const dialogResult = new Promise(
27
+ ( confirm, cancel ) => {
28
+ const confirmDialog = ( what ) => { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
29
+ switch ( what ) { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
30
+ case 'yes':
31
+ // Confirm the new language.
32
+ languagesList.data( 'old-value', languagesList.children( ':selected' )[0].value );
33
+ confirm();
34
+ break;
35
+ case 'no':
36
+ // Revert to the old language.
37
+ languagesList.val( languagesList.data( 'old-value' ) );
38
+ cancel( 'Cancel' );
39
+ break;
40
+ }
41
+ dialogContainer.dialog( 'close' ); // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
42
+ } // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
43
+
44
+ // Initialize dialog box in the case a language is selected but not added in the list.
45
+ dialogContainer.dialog(
46
+ {
47
+ autoOpen: false,
48
+ modal: true,
49
+ draggable: false,
50
+ resizable: false,
51
+ title: __( 'Change language', 'polylang' ),
52
+ minWidth: 600,
53
+ maxWidth: '100%',
54
+ classes: {
55
+ 'ui-dialog': 'pll-confirmation-modal',
56
+ },
57
+ open: function( event, ui ) {
58
+ // Change dialog box position for rtl language
59
+ if ( jQuery( 'body' ).hasClass( 'rtl' ) ) {
60
+ jQuery( this ).parent().css(
61
+ {
62
+ right: jQuery( this ).parent().css( 'left' ),
63
+ left: 'auto'
64
+ }
65
+ );
66
+ }
67
+ },
68
+ close: function( event, ui ) {
69
+ // When we're closing the dialog box we need to cancel the language change as we click on Cancel button.
70
+ confirmDialog( 'no' );
71
+ },
72
+ buttons: [
73
+ {
74
+ text: __( 'OK', 'polylang' ),
75
+ click: function( event ) {
76
+ confirmDialog( 'yes' );
77
+ }
78
+ },
79
+ {
80
+ text: __( 'Cancel', 'polylang' ),
81
+ click: function( event ) {
82
+ confirmDialog( 'no' );
83
+ }
84
+ } ]
85
+ }
86
+ );
87
+ }
88
+ );
89
+ return { dialogContainer, dialogResult };
90
+ }
91
+
92
+ export const initializeLanguageOldValue = () => {
93
+ // Keep the old language value to be able to compare to the new one and revert to it if necessary.
94
+ languagesList.attr( 'data-old-value', languagesList.children( ':selected' )[0].value );
95
+ };
js/nav-menu.js CHANGED
@@ -4,9 +4,9 @@
4
  * @package Polylang
5
  */
6
 
7
- jQuery( document ).ready(
8
  function( $ ) {
9
- $( '#update-nav-menu' ).bind(
10
  'click',
11
  function( e ) {
12
  if ( e.target && e.target.className && -1 != e.target.className.indexOf( 'item-edit' ) ) {
@@ -85,7 +85,8 @@ jQuery( document ).ready(
85
  $.each(
86
  options,
87
  function( i, v ) {
88
- $( '#edit-menu-item-show_' + v + id ).change(
 
89
  function() {
90
  if ( true != $( this ).prop( 'checked' ) ) {
91
  $( '#edit-menu-item-show_' + options[ 1 - i ] + id ).prop( 'checked', true );
4
  * @package Polylang
5
  */
6
 
7
+ jQuery(
8
  function( $ ) {
9
+ $( '#update-nav-menu' ).on(
10
  'click',
11
  function( e ) {
12
  if ( e.target && e.target.className && -1 != e.target.className.indexOf( 'item-edit' ) ) {
85
  $.each(
86
  options,
87
  function( i, v ) {
88
+ $( '#edit-menu-item-show_' + v + id ).on(
89
+ 'change',
90
  function() {
91
  if ( true != $( this ).prop( 'checked' ) ) {
92
  $( '#edit-menu-item-show_' + options[ 1 - i ] + id ).prop( 'checked', true );
js/nav-menu.min.js DELETED
@@ -1 +0,0 @@
1
- jQuery(document).ready(function(e){e("#update-nav-menu").bind("click",function(t){t.target&&t.target.className&&-1!=t.target.className.indexOf("item-edit")&&(e("input[value='#pll_switcher'][type=text]").parent().parent().parent().each(function(){var t=e(this).attr("id").substring(19);e(this).children("p:not( .field-move )").remove(),h=e("<input>").attr({type:"hidden",id:"edit-menu-item-title-"+t,name:"menu-item-title["+t+"]",value:pll_data.title}),e(this).append(h),h=e("<input>").attr({type:"hidden",id:"edit-menu-item-url-"+t,name:"menu-item-url["+t+"]",value:"#pll_switcher"}),e(this).append(h),h=e("<input>").attr({type:"hidden",id:"edit-menu-item-pll-detect-"+t,name:"menu-item-pll-detect["+t+"]",value:1}),e(this).append(h),ids=Array("hide_if_no_translation","hide_current","force_home","show_flags","show_names","dropdown");for(var i=0,a=ids.length;i<a;i++)p=e("<p>").attr("class","description"),e(this).prepend(p),label=e("<label>").attr("for","edit-menu-item-"+ids[i]+"-"+t).text(" "+pll_data.strings[ids[i]]),p.append(label),cb=e("<input>").attr({type:"checkbox",id:"edit-menu-item-"+ids[i]+"-"+t,name:"menu-item-"+ids[i]+"["+t+"]",value:1}),(void 0!==pll_data.val[t]&&1==pll_data.val[t][ids[i]]||void 0===pll_data.val[t]&&"show_names"==ids[i])&&cb.prop("checked",!0),label.prepend(cb)}),e(".menu-item-data-object-id").each(function(){var t=e(this).val(),i=["names-","flags-"];e.each(i,function(a,n){e("#edit-menu-item-show_"+n+t).change(function(){1!=e(this).prop("checked")&&e("#edit-menu-item-show_"+i[1-a]+t).prop("checked",!0)})})}))})});
 
js/post.js CHANGED
@@ -41,7 +41,8 @@ jQuery(
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() );
41
  filter_pages( lang ); // initial filter for parent dropdown
42
 
43
  // modify category checklist an parent dropdown on language change
44
+ select.on(
45
+ 'change',
46
  function() {
47
  filter_terms( $( this ).val() );
48
  filter_pages( $( this ).val() );
js/post.min.js DELETED
@@ -1 +0,0 @@
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
@@ -164,7 +164,8 @@ jQuery(
164
  init_translations();
165
 
166
  // ajax for changing the term's language
167
- $( '#term_lang_choice' ).change(
 
168
  function() {
169
  var value = $( this ).val();
170
  var lang = $( this ).children( 'option[value="' + value + '"]' ).attr( 'lang' );
164
  init_translations();
165
 
166
  // ajax for changing the term's language
167
+ $( '#term_lang_choice' ).on(
168
+ 'change',
169
  function() {
170
  var value = $( this ).val();
171
  var lang = $( this ).children( 'option[value="' + value + '"]' ).attr( 'lang' );
js/term.min.js DELETED
@@ -1 +0,0 @@
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/user.js CHANGED
@@ -4,7 +4,7 @@
4
  * @package Polylang
5
  */
6
 
7
- jQuery( document ).ready(
8
  function( $ ) {
9
  // biography
10
  // FIXME there is probably a more efficient way to do this
4
  * @package Polylang
5
  */
6
 
7
+ jQuery(
8
  function( $ ) {
9
  // biography
10
  // FIXME there is probably a more efficient way to do this
js/user.min.js DELETED
@@ -1 +0,0 @@
1
- jQuery(document).ready(function(e){var n=e("#description").parent(),d=e("#description").clone(),i=n.children(".description").clone();n.children().remove(),e(".biography").each(function(){lang=e(this).attr("name").split("___"),desc=d.clone(),desc.attr("name","description_"+lang[0]),desc.attr("id","description_"+lang[0]),desc.html(e(this).val()),n.append(e("<div></div>").text(lang[1])),n.append(desc)}),n.append("<br />"),n.append(i)});
 
js/widgets.min.js DELETED
@@ -1 +0,0 @@
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/lingotek/lingotek.php CHANGED
@@ -15,6 +15,8 @@ class PLL_Lingotek {
15
  * Init
16
  *
17
  * @since 1.7.7
 
 
18
  */
19
  public function init() {
20
  // The Lingotek tab
@@ -71,6 +73,8 @@ class PLL_Lingotek {
71
  * Displays the content in the Lingotek tab
72
  *
73
  * @since 1.7.7
 
 
74
  */
75
  public function display_tab() {
76
  PLL_Admin_Notices::dismiss( 'lingotek' );
@@ -154,6 +158,8 @@ class PLL_Lingotek {
154
  * Styles the content of the Lingotek tab
155
  *
156
  * @since 1.7.7
 
 
157
  */
158
  public function print_css() {
159
  ?>
@@ -233,6 +239,7 @@ class PLL_Lingotek {
233
  * @param array $list
234
  * @param array $links
235
  * @param string $img
 
236
  */
237
  protected function box( $title, $desc, $list, $links, $img ) {
238
  ?>
15
  * Init
16
  *
17
  * @since 1.7.7
18
+ *
19
+ * @return void
20
  */
21
  public function init() {
22
  // The Lingotek tab
73
  * Displays the content in the Lingotek tab
74
  *
75
  * @since 1.7.7
76
+ *
77
+ * @return void
78
  */
79
  public function display_tab() {
80
  PLL_Admin_Notices::dismiss( 'lingotek' );
158
  * Styles the content of the Lingotek tab
159
  *
160
  * @since 1.7.7
161
+ *
162
+ * @return void
163
  */
164
  public function print_css() {
165
  ?>
239
  * @param array $list
240
  * @param array $links
241
  * @param string $img
242
+ * @return void
243
  */
244
  protected function box( $title, $desc, $list, $links, $img ) {
245
  ?>
modules/share-slug/settings-share-slug.php CHANGED
@@ -65,6 +65,8 @@ class PLL_Settings_Share_Slug extends PLL_Settings_Module {
65
  * as sharing slugs is not possible when the language is set from the content
66
  *
67
  * @since 1.9
 
 
68
  */
69
  public function print_js() {
70
  wp_enqueue_script( 'jquery' );
65
  * as sharing slugs is not possible when the language is set from the content
66
  *
67
  * @since 1.9
68
+ *
69
+ * @return void
70
  */
71
  public function print_js() {
72
  wp_enqueue_script( 'jquery' );
modules/site-health/admin-site-health.php CHANGED
@@ -53,7 +53,7 @@ class PLL_Admin_Site_Health {
53
  *
54
  * @since 2.8
55
  *
56
- * @return array List of option keys to ignore.
57
  */
58
  protected function exclude_options_keys() {
59
  return array(
@@ -67,7 +67,7 @@ class PLL_Admin_Site_Health {
67
  *
68
  * @since 2.8
69
  *
70
- * @return array List of language keys to ignore.
71
  */
72
  protected function exclude_language_keys() {
73
  return array(
@@ -218,7 +218,7 @@ class PLL_Admin_Site_Health {
218
  *
219
  * @since 2.8
220
  *
221
- * @param object $language Language object.
222
  * @return string
223
  */
224
  protected function get_flag( $language ) {
53
  *
54
  * @since 2.8
55
  *
56
+ * @return string[] List of option keys to ignore.
57
  */
58
  protected function exclude_options_keys() {
59
  return array(
67
  *
68
  * @since 2.8
69
  *
70
+ * @return string[] List of language keys to ignore.
71
  */
72
  protected function exclude_language_keys() {
73
  return array(
218
  *
219
  * @since 2.8
220
  *
221
+ * @param PLL_Language $language Language object.
222
  * @return string
223
  */
224
  protected function get_flag( $language ) {
modules/sitemaps/abstract-sitemaps.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Polylang
4
+ */
5
+
6
+ /**
7
+ * Common class for handling the core sitemaps.
8
+ *
9
+ * The child classes must called the init() method.
10
+ *
11
+ * @since 3.0
12
+ */
13
+ abstract class PLL_Abstract_Sitemaps {
14
+ /**
15
+ * Setups actions and filters.
16
+ *
17
+ * @since 2.8
18
+ *
19
+ * @return void
20
+ */
21
+ public function init() {
22
+ add_filter( 'pll_home_url_white_list', array( $this, 'home_url_white_list' ) );
23
+ }
24
+
25
+ /**
26
+ * Whitelists the home url filter for the sitemaps.
27
+ *
28
+ * @since 2.8
29
+ *
30
+ * @param array $whitelist White list.
31
+ * @return array;
32
+ */
33
+ public function home_url_white_list( $whitelist ) {
34
+ $whitelist[] = array( 'file' => 'class-wp-sitemaps-posts' );
35
+ return $whitelist;
36
+ }
37
+ }
modules/sitemaps/load.php CHANGED
@@ -8,6 +8,10 @@ if ( ! defined( 'ABSPATH' ) ) {
8
  };
9
 
10
  if ( $polylang->model->get_languages_list() ) {
11
- $polylang->sitemaps = new PLL_Sitemaps( $polylang );
 
 
 
 
12
  $polylang->sitemaps->init();
13
  }
8
  };
9
 
10
  if ( $polylang->model->get_languages_list() ) {
11
+ if ( $polylang->links_model instanceof PLL_Links_Abstract_Domain ) {
12
+ $polylang->sitemaps = new PLL_Sitemaps_Domain( $polylang );
13
+ } else {
14
+ $polylang->sitemaps = new PLL_Sitemaps( $polylang );
15
+ }
16
  $polylang->sitemaps->init();
17
  }
modules/sitemaps/multilingual-sitemaps-provider.php CHANGED
@@ -187,9 +187,12 @@ class PLL_Multilingual_Sitemaps_Provider extends WP_Sitemaps_Provider {
187
  $pattern = '#(' . implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) . ')$#';
188
  if ( preg_match( $pattern, $name, $matches ) ) {
189
  $lang = $this->model->get_language( $matches[1] );
190
- $name = preg_replace( '#(-?' . $lang->slug . ')$#', '', $name );
191
- $url = $this->provider->get_sitemap_url( $name, $page );
192
- return $this->links_model->add_language_to_link( $url, $lang );
 
 
 
193
  }
194
 
195
  // If no language is present in $name, we may attempt to get the current sitemap url (e.g. in redirect_canonical() ).
187
  $pattern = '#(' . implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) . ')$#';
188
  if ( preg_match( $pattern, $name, $matches ) ) {
189
  $lang = $this->model->get_language( $matches[1] );
190
+
191
+ if ( ! empty( $lang ) ) {
192
+ $name = preg_replace( '#(-?' . $lang->slug . ')$#', '', $name );
193
+ $url = $this->provider->get_sitemap_url( $name, $page );
194
+ return $this->links_model->add_language_to_link( $url, $lang );
195
+ }
196
  }
197
 
198
  // If no language is present in $name, we may attempt to get the current sitemap url (e.g. in redirect_canonical() ).
modules/sitemaps/sitemaps-domain.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Polylang
4
+ */
5
+
6
+ /**
7
+ * Handles the core sitemaps for subdomains and multiple domains.
8
+ *
9
+ * @since 3.0
10
+ */
11
+ class PLL_Sitemaps_Domain extends PLL_Abstract_Sitemaps {
12
+ /**
13
+ * @var PLL_Links_Abstract_Domain
14
+ */
15
+ protected $links_model;
16
+
17
+ /**
18
+ * Constructor.
19
+ *
20
+ * @since 3.0
21
+ *
22
+ * @param object $polylang Main Polylang object.
23
+ */
24
+ public function __construct( &$polylang ) {
25
+ $this->links_model = &$polylang->links_model;
26
+ }
27
+
28
+ /**
29
+ * Setups actions and filters.
30
+ *
31
+ * @since 3.0
32
+ *
33
+ * @return void
34
+ */
35
+ public function init() {
36
+ parent::init();
37
+
38
+ add_filter( 'wp_sitemaps_index_entry', array( $this, 'index_entry' ) );
39
+ add_filter( 'wp_sitemaps_stylesheet_url', array( $this->links_model, 'site_url' ) );
40
+ add_filter( 'wp_sitemaps_stylesheet_index_url', array( $this->links_model, 'site_url' ) );
41
+ add_filter( 'home_url', array( $this, 'sitemap_url' ) );
42
+ }
43
+
44
+ /**
45
+ * Filters the sitemap index entries for subdomains and multiple domains.
46
+ *
47
+ * @since 2.8
48
+ *
49
+ * @param array $sitemap_entry Sitemap entry for the post.
50
+ * @return array
51
+ */
52
+ public function index_entry( $sitemap_entry ) {
53
+ $sitemap_entry['loc'] = $this->links_model->site_url( $sitemap_entry['loc'] );
54
+ return $sitemap_entry;
55
+ }
56
+
57
+ /**
58
+ * Makes sure that the sitemap urls are always evaluated on the current domain.
59
+ *
60
+ * @since 2.8.4
61
+ *
62
+ * @param string $url A sitemap url.
63
+ * @return string
64
+ */
65
+ public function sitemap_url( $url ) {
66
+ if ( false !== strpos( $url, '/wp-sitemap' ) ) {
67
+ $url = $this->links_model->site_url( $url );
68
+ }
69
+ return $url;
70
+ }
71
+ }
modules/sitemaps/sitemaps.php CHANGED
@@ -4,34 +4,17 @@
4
  */
5
 
6
  /**
7
- * Handles the core sitemaps.
8
  *
9
  * @since 2.8
10
  */
11
- class PLL_Sitemaps {
12
  /**
13
- * A reference to the current language.
14
- *
15
- * @since 2.8
16
- *
17
- * @var PLL_Language
18
- */
19
- protected $curlang;
20
-
21
- /**
22
- * A reference to the PLL_Links_Model instance.
23
- *
24
- * @since 2.8
25
- *
26
  * @var PLL_Links_Model
27
  */
28
  protected $links_model;
29
 
30
  /**
31
- * A reference to the PLL_Model instance.
32
- *
33
- * @since 2.8
34
- *
35
  * @var PLL_Model
36
  */
37
  protected $model;
@@ -51,7 +34,6 @@ class PLL_Sitemaps {
51
  * @param object $polylang Main Polylang object.
52
  */
53
  public function __construct( &$polylang ) {
54
- $this->curlang = &$polylang->curlang;
55
  $this->links_model = &$polylang->links_model;
56
  $this->model = &$polylang->model;
57
  $this->options = &$polylang->options;
@@ -61,20 +43,15 @@ class PLL_Sitemaps {
61
  * Setups actions and filters.
62
  *
63
  * @since 2.8
 
 
64
  */
65
  public function init() {
66
- add_filter( 'pll_home_url_white_list', array( $this, 'home_url_white_list' ) );
67
-
68
- if ( $this->options['force_lang'] < 2 ) {
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' ) );
76
- add_filter( 'home_url', array( $this, 'sitemap_url' ) );
77
- }
78
  }
79
 
80
  /**
@@ -84,7 +61,7 @@ class PLL_Sitemaps {
84
  * @since 2.8
85
  *
86
  * @param string|bool $lang Current language code, false if not set yet.
87
- * @param object $query Main WP query object.
88
  * @return string|bool
89
  */
90
  public function set_language_from_query( $lang, $query ) {
@@ -100,7 +77,7 @@ class PLL_Sitemaps {
100
  * @since 2.8
101
  *
102
  * @param array $whitelist White list.
103
- * @return array;
104
  */
105
  public function home_url_white_list( $whitelist ) {
106
  $whitelist[] = array( 'file' => 'class-wp-sitemaps-posts' );
@@ -112,8 +89,8 @@ class PLL_Sitemaps {
112
  *
113
  * @since 2.8
114
  *
115
- * @param array $rules Rewrite rules.
116
- * @return array
117
  */
118
  public function rewrite_rules( $rules ) {
119
  global $wp_rewrite;
@@ -133,7 +110,7 @@ class PLL_Sitemaps {
133
  $newrules = array();
134
 
135
  foreach ( $rules as $key => $rule ) {
136
- if ( isset( $slug ) && false !== strpos( $rule, 'sitemap=$matches[1]' ) ) {
137
  $newrules[ str_replace( '^wp-sitemap', $slug . 'wp-sitemap', $key ) ] = str_replace(
138
  array( '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]', '?' ),
139
  array( '[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '?lang=$matches[1]&' ),
@@ -160,32 +137,4 @@ class PLL_Sitemaps {
160
  }
161
  return $provider;
162
  }
163
-
164
- /**
165
- * Filters the sitemap index entries for subdomains and multiple domains.
166
- *
167
- * @since 2.8
168
- *
169
- * @param array $sitemap_entry Sitemap entry for the post.
170
- * return array
171
- */
172
- public function index_entry( $sitemap_entry ) {
173
- $sitemap_entry['loc'] = $this->links_model->site_url( $sitemap_entry['loc'] );
174
- return $sitemap_entry;
175
- }
176
-
177
- /**
178
- * Makes sure that the sitemap urls are always evaluated on the current domain.
179
- *
180
- * @since 2.8.4
181
- *
182
- * @param string $url A sitemap url.
183
- * @return string
184
- */
185
- public function sitemap_url( $url ) {
186
- if ( false !== strpos( $url, '/wp-sitemap' ) ) {
187
- $url = $this->links_model->site_url( $url );
188
- }
189
- return $url;
190
- }
191
  }
4
  */
5
 
6
  /**
7
+ * Handles the core sitemaps for sites using a single domain.
8
  *
9
  * @since 2.8
10
  */
11
+ class PLL_Sitemaps extends PLL_Abstract_Sitemaps {
12
  /**
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  * @var PLL_Links_Model
14
  */
15
  protected $links_model;
16
 
17
  /**
 
 
 
 
18
  * @var PLL_Model
19
  */
20
  protected $model;
34
  * @param object $polylang Main Polylang object.
35
  */
36
  public function __construct( &$polylang ) {
 
37
  $this->links_model = &$polylang->links_model;
38
  $this->model = &$polylang->model;
39
  $this->options = &$polylang->options;
43
  * Setups actions and filters.
44
  *
45
  * @since 2.8
46
+ *
47
+ * @return void
48
  */
49
  public function init() {
50
+ parent::init();
51
+
52
+ add_filter( 'pll_set_language_from_query', array( $this, 'set_language_from_query' ), 10, 2 );
53
+ add_filter( 'rewrite_rules_array', array( $this, 'rewrite_rules' ) );
54
+ add_filter( 'wp_sitemaps_add_provider', array( $this, 'replace_provider' ) );
 
 
 
 
 
 
 
55
  }
56
 
57
  /**
61
  * @since 2.8
62
  *
63
  * @param string|bool $lang Current language code, false if not set yet.
64
+ * @param WP_Query $query Main WP query object.
65
  * @return string|bool
66
  */
67
  public function set_language_from_query( $lang, $query ) {
77
  * @since 2.8
78
  *
79
  * @param array $whitelist White list.
80
+ * @return array
81
  */
82
  public function home_url_white_list( $whitelist ) {
83
  $whitelist[] = array( 'file' => 'class-wp-sitemaps-posts' );
89
  *
90
  * @since 2.8
91
  *
92
+ * @param string[] $rules Rewrite rules.
93
+ * @return string[] Modified rewrite rules.
94
  */
95
  public function rewrite_rules( $rules ) {
96
  global $wp_rewrite;
110
  $newrules = array();
111
 
112
  foreach ( $rules as $key => $rule ) {
113
+ if ( false !== strpos( $rule, 'sitemap=$matches[1]' ) ) {
114
  $newrules[ str_replace( '^wp-sitemap', $slug . 'wp-sitemap', $key ) ] = str_replace(
115
  array( '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]', '?' ),
116
  array( '[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '?lang=$matches[1]&' ),
137
  }
138
  return $provider;
139
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  }
modules/sync/admin-sync.php CHANGED
@@ -62,14 +62,16 @@ class PLL_Admin_Sync extends PLL_Sync {
62
  $from_post_id = (int) $_GET['from_post'];
63
  $from_post = get_post( $from_post_id );
64
 
65
- foreach ( array( 'menu_order', 'comment_status', 'ping_status' ) as $property ) {
66
- $data[ $property ] = $from_post->$property;
67
- }
68
-
69
- // Copy the date only if the synchronization is activated
70
- if ( in_array( 'post_date', $this->options['sync'] ) ) {
71
- $data['post_date'] = $from_post->post_date;
72
- $data['post_date_gmt'] = $from_post->post_date_gmt;
 
 
73
  }
74
  }
75
 
@@ -80,6 +82,8 @@ class PLL_Admin_Sync extends PLL_Sync {
80
  * Copy post metas, and taxonomies when using "Add new" ( translation )
81
  *
82
  * @since 2.5
 
 
83
  */
84
  public function new_post_translation() {
85
  global $post;
@@ -108,12 +112,12 @@ class PLL_Admin_Sync extends PLL_Sync {
108
  }
109
 
110
  /**
111
- * Get post fields to synchronize
112
  *
113
  * @since 2.4
114
  *
115
- * @param object $post Post object
116
- * @return array
117
  */
118
  protected function get_fields_to_sync( $post ) {
119
  global $wpdb;
@@ -128,14 +132,17 @@ class PLL_Admin_Sync extends PLL_Sync {
128
  unset( $postarr['post_date_gmt'] );
129
 
130
  $original = get_post( (int) $_GET['from_post'] );
131
- $wpdb->update(
132
- $wpdb->posts,
133
- array(
134
- 'post_date' => $original->post_date,
135
- 'post_date_gmt' => $original->post_date_gmt,
136
- ),
137
- array( 'ID' => $post->ID )
138
- );
 
 
 
139
  }
140
 
141
  if ( isset( $GLOBALS['post_type'] ) ) {
@@ -153,13 +160,13 @@ class PLL_Admin_Sync extends PLL_Sync {
153
  }
154
 
155
  /**
156
- * Synchronizes post fields in translations
157
  *
158
  * @since 1.2
159
  *
160
- * @param int $post_id post id
161
- * @param object $post post object
162
- * @param array $translations post translations
163
  */
164
  public function pll_save_post( $post_id, $post, $translations ) {
165
  parent::pll_save_post( $post_id, $post, $translations );
@@ -186,6 +193,7 @@ class PLL_Admin_Sync extends PLL_Sync {
186
  *
187
  * @param string $func Function name
188
  * @param array $args Function arguments
 
189
  */
190
  public function __call( $func, $args ) {
191
  $obj = substr( $func, 5 );
62
  $from_post_id = (int) $_GET['from_post'];
63
  $from_post = get_post( $from_post_id );
64
 
65
+ if ( $from_post instanceof WP_Post ) {
66
+ foreach ( array( 'menu_order', 'comment_status', 'ping_status' ) as $property ) {
67
+ $data[ $property ] = $from_post->$property;
68
+ }
69
+
70
+ // Copy the date only if the synchronization is activated
71
+ if ( in_array( 'post_date', $this->options['sync'] ) ) {
72
+ $data['post_date'] = $from_post->post_date;
73
+ $data['post_date_gmt'] = $from_post->post_date_gmt;
74
+ }
75
  }
76
  }
77
 
82
  * Copy post metas, and taxonomies when using "Add new" ( translation )
83
  *
84
  * @since 2.5
85
+ *
86
+ * @return void
87
  */
88
  public function new_post_translation() {
89
  global $post;
112
  }
113
 
114
  /**
115
+ * Get post fields to synchronize.
116
  *
117
  * @since 2.4
118
  *
119
+ * @param WP_Post $post Post object.
120
+ * @return array Fields to synchronize.
121
  */
122
  protected function get_fields_to_sync( $post ) {
123
  global $wpdb;
132
  unset( $postarr['post_date_gmt'] );
133
 
134
  $original = get_post( (int) $_GET['from_post'] );
135
+
136
+ if ( $original instanceof WP_Post ) {
137
+ $wpdb->update(
138
+ $wpdb->posts,
139
+ array(
140
+ 'post_date' => $original->post_date,
141
+ 'post_date_gmt' => $original->post_date_gmt,
142
+ ),
143
+ array( 'ID' => $post->ID )
144
+ );
145
+ }
146
  }
147
 
148
  if ( isset( $GLOBALS['post_type'] ) ) {
160
  }
161
 
162
  /**
163
+ * Synchronizes post fields in translations.
164
  *
165
  * @since 1.2
166
  *
167
+ * @param int $post_id Post id.
168
+ * @param WP_Post $post Post object.
169
+ * @param int[] $translations Post translations.
170
  */
171
  public function pll_save_post( $post_id, $post, $translations ) {
172
  parent::pll_save_post( $post_id, $post, $translations );
193
  *
194
  * @param string $func Function name
195
  * @param array $args Function arguments
196
+ * @return mixed|void
197
  */
198
  public function __call( $func, $args ) {
199
  $obj = substr( $func, 5 );
modules/sync/settings-sync.php CHANGED
@@ -79,22 +79,22 @@ class PLL_Settings_Sync extends PLL_Settings_Module {
79
  }
80
 
81
  /**
82
- * Get the row actions
83
  *
84
  * @since 1.8
85
  *
86
- * @return array
87
  */
88
  protected function get_actions() {
89
  return empty( $this->options['sync'] ) ? array( 'configure' ) : array( 'configure', 'deactivate' );
90
  }
91
 
92
  /**
93
- * List the post metas to synchronize
94
  *
95
  * @since 1.0
96
  *
97
- * @return array
98
  */
99
  public static function list_metas_to_sync() {
100
  return array(
79
  }
80
 
81
  /**
82
+ * Get the row actions.
83
  *
84
  * @since 1.8
85
  *
86
+ * @return string[] Row actions.
87
  */
88
  protected function get_actions() {
89
  return empty( $this->options['sync'] ) ? array( 'configure' ) : array( 'configure', 'deactivate' );
90
  }
91
 
92
  /**
93
+ * Get the list of synchronization settings.
94
  *
95
  * @since 1.0
96
  *
97
+ * @return string[] Array synchronization options.
98
  */
99
  public static function list_metas_to_sync() {
100
  return array(
modules/sync/sync-metas.php CHANGED
@@ -9,8 +9,31 @@
9
  * @since 2.3
10
  */
11
  abstract class PLL_Sync_Metas {
 
 
 
12
  public $model;
13
- protected $meta_type, $prev_value, $to_copy;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  /**
16
  * Constructor
@@ -35,6 +58,8 @@ abstract class PLL_Sync_Metas {
35
  * Removes "added_{$this->meta_type}_meta" action
36
  *
37
  * @since 2.3
 
 
38
  */
39
  protected function remove_add_meta_action() {
40
  remove_action( "added_{$this->meta_type}_meta", array( $this, 'add_meta' ), 10, 4 );
@@ -44,6 +69,8 @@ abstract class PLL_Sync_Metas {
44
  * Removes all meta synchronization actions and filters
45
  *
46
  * @since 2.3
 
 
47
  */
48
  protected function remove_all_meta_actions() {
49
  $this->remove_add_meta_action();
@@ -59,6 +86,8 @@ abstract class PLL_Sync_Metas {
59
  * Adds "added_{$this->meta_type}_meta" action
60
  *
61
  * @since 2.3
 
 
62
  */
63
  protected function restore_add_meta_action() {
64
  add_action( "added_{$this->meta_type}_meta", array( $this, 'add_meta' ), 10, 4 );
@@ -68,6 +97,8 @@ abstract class PLL_Sync_Metas {
68
  * Adds meta synchronization actions and filters
69
  *
70
  * @since 2.3
 
 
71
  */
72
  protected function add_all_meta_actions() {
73
  $this->restore_add_meta_action();
@@ -107,28 +138,28 @@ abstract class PLL_Sync_Metas {
107
  }
108
 
109
  /**
110
- * Get the custom fields to copy or synchronize
111
  *
112
  * @since 2.3
113
  *
114
- * @param int $from Id of the post from which we copy informations
115
- * @param int $to Id of the post to which we paste informations
116
- * @param string $lang Language slug
117
- * @param bool $sync True if it is synchronization, false if it is a copy
118
- * @return array List of meta keys
119
  */
120
  protected function get_metas_to_copy( $from, $to, $lang, $sync = false ) {
121
  /**
122
- * Filter the custom fields to copy or synchronize
123
  *
124
  * @since 0.6
125
  * @since 1.9.2 The `$from`, `$to`, `$lang` parameters were added.
126
  *
127
- * @param array $keys List of custom fields names
128
- * @param bool $sync True if it is synchronization, false if it is a copy
129
- * @param int $from Id of the post from which we copy informations
130
- * @param int $to Id of the post to which we paste informations
131
- * @param string $lang Language slug
132
  */
133
  return array_unique( apply_filters( "pll_copy_{$this->meta_type}_metas", array(), $sync, $from, $to, $lang ) );
134
  }
@@ -168,6 +199,7 @@ abstract class PLL_Sync_Metas {
168
  * @param int $id Object ID.
169
  * @param string $meta_key Meta key.
170
  * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
 
171
  */
172
  public function add_meta( $mid, $id, $meta_key, $meta_value ) {
173
  static $avoid_recursion = false;
@@ -219,6 +251,7 @@ abstract class PLL_Sync_Metas {
219
  * @param int $id Object ID.
220
  * @param string $meta_key Meta key.
221
  * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
 
222
  */
223
  public function update_meta( $mid, $id, $meta_key, $meta_value ) {
224
  static $avoid_recursion = false;
@@ -251,12 +284,13 @@ abstract class PLL_Sync_Metas {
251
  }
252
 
253
  /**
254
- * Store metas to synchronize before deleting them
255
  *
256
  * @since 2.3
257
  *
258
- * @param array $mids Not used
259
  * @param int $id Object ID.
 
260
  */
261
  public function store_metas_to_sync( $mids, $id ) {
262
  $tr_ids = $this->model->{$this->meta_type}->get_translations( $id );
@@ -267,14 +301,15 @@ abstract class PLL_Sync_Metas {
267
  }
268
 
269
  /**
270
- * Synchronize deleted meta across translations
271
  *
272
  * @since 2.3
273
  *
274
- * @param array $mids Not used
275
  * @param int $id Object ID.
276
  * @param string $key Meta key.
277
  * @param mixed $value Meta value.
 
278
  */
279
  public function delete_meta( $mids, $id, $key, $value ) {
280
  static $avoid_recursion = false;
@@ -308,6 +343,7 @@ abstract class PLL_Sync_Metas {
308
  * @param int $to Id of the target object
309
  * @param string $lang Language code of the target object
310
  * @param bool $sync Optional, defaults to true. True if it is synchronization, false if it is a copy
 
311
  */
312
  public function copy( $from, $to, $lang, $sync = false ) {
313
  $this->remove_all_meta_actions();
@@ -357,9 +393,10 @@ abstract class PLL_Sync_Metas {
357
  *
358
  * @since 2.3
359
  *
360
- * @param int $object_id Id of the object being asaved
361
- * @param object $obj Not used
362
- * @param array $translations The list of translations object ids
 
363
  */
364
  public function save_object( $object_id, $obj, $translations ) {
365
  foreach ( $translations as $tr_lang => $tr_id ) {
9
  * @since 2.3
10
  */
11
  abstract class PLL_Sync_Metas {
12
+ /**
13
+ * @var PLL_Model
14
+ */
15
  public $model;
16
+
17
+ /**
18
+ * Meta type. Typically 'post' or 'term'.
19
+ *
20
+ * @var string
21
+ */
22
+ protected $meta_type;
23
+
24
+ /**
25
+ * Stores the previous values when updating a meta.
26
+ *
27
+ * @var array
28
+ */
29
+ protected $prev_value;
30
+
31
+ /**
32
+ * Stores the metas to synchronize before deleting them.
33
+ *
34
+ * @var array
35
+ */
36
+ protected $to_copy;
37
 
38
  /**
39
  * Constructor
58
  * Removes "added_{$this->meta_type}_meta" action
59
  *
60
  * @since 2.3
61
+ *
62
+ * @return void
63
  */
64
  protected function remove_add_meta_action() {
65
  remove_action( "added_{$this->meta_type}_meta", array( $this, 'add_meta' ), 10, 4 );
69
  * Removes all meta synchronization actions and filters
70
  *
71
  * @since 2.3
72
+ *
73
+ * @return void
74
  */
75
  protected function remove_all_meta_actions() {
76
  $this->remove_add_meta_action();
86
  * Adds "added_{$this->meta_type}_meta" action
87
  *
88
  * @since 2.3
89
+ *
90
+ * @return void
91
  */
92
  protected function restore_add_meta_action() {
93
  add_action( "added_{$this->meta_type}_meta", array( $this, 'add_meta' ), 10, 4 );
97
  * Adds meta synchronization actions and filters
98
  *
99
  * @since 2.3
100
+ *
101
+ * @return void
102
  */
103
  protected function add_all_meta_actions() {
104
  $this->restore_add_meta_action();
138
  }
139
 
140
  /**
141
+ * Get the custom fields to copy or synchronize.
142
  *
143
  * @since 2.3
144
  *
145
+ * @param int $from Id of the post from which we copy informations.
146
+ * @param int $to Id of the post to which we paste informations.
147
+ * @param string $lang Language slug.
148
+ * @param bool $sync True if it is synchronization, false if it is a copy.
149
+ * @return string[] List of meta keys.
150
  */
151
  protected function get_metas_to_copy( $from, $to, $lang, $sync = false ) {
152
  /**
153
+ * Filters the custom fields to copy or synchronize.
154
  *
155
  * @since 0.6
156
  * @since 1.9.2 The `$from`, `$to`, `$lang` parameters were added.
157
  *
158
+ * @param string[] $keys List of custom fields names.
159
+ * @param bool $sync True if it is synchronization, false if it is a copy.
160
+ * @param int $from Id of the post from which we copy informations.
161
+ * @param int $to Id of the post to which we paste informations.
162
+ * @param string $lang Language slug.
163
  */
164
  return array_unique( apply_filters( "pll_copy_{$this->meta_type}_metas", array(), $sync, $from, $to, $lang ) );
165
  }
199
  * @param int $id Object ID.
200
  * @param string $meta_key Meta key.
201
  * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
202
+ * @return void
203
  */
204
  public function add_meta( $mid, $id, $meta_key, $meta_value ) {
205
  static $avoid_recursion = false;
251
  * @param int $id Object ID.
252
  * @param string $meta_key Meta key.
253
  * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
254
+ * @return void
255
  */
256
  public function update_meta( $mid, $id, $meta_key, $meta_value ) {
257
  static $avoid_recursion = false;
284
  }
285
 
286
  /**
287
+ * Store metas to synchronize before deleting them.
288
  *
289
  * @since 2.3
290
  *
291
+ * @param int[] $mids Not used.
292
  * @param int $id Object ID.
293
+ * @return void
294
  */
295
  public function store_metas_to_sync( $mids, $id ) {
296
  $tr_ids = $this->model->{$this->meta_type}->get_translations( $id );
301
  }
302
 
303
  /**
304
+ * Synchronizes deleted meta across translations.
305
  *
306
  * @since 2.3
307
  *
308
+ * @param int[] $mids Not used.
309
  * @param int $id Object ID.
310
  * @param string $key Meta key.
311
  * @param mixed $value Meta value.
312
+ * @return void
313
  */
314
  public function delete_meta( $mids, $id, $key, $value ) {
315
  static $avoid_recursion = false;
343
  * @param int $to Id of the target object
344
  * @param string $lang Language code of the target object
345
  * @param bool $sync Optional, defaults to true. True if it is synchronization, false if it is a copy
346
+ * @return void
347
  */
348
  public function copy( $from, $to, $lang, $sync = false ) {
349
  $this->remove_all_meta_actions();
393
  *
394
  * @since 2.3
395
  *
396
+ * @param int $object_id Id of the object being saved.
397
+ * @param object $obj Not used.
398
+ * @param int[] $translations The list of translations object ids.
399
+ * @return void
400
  */
401
  public function save_object( $object_id, $obj, $translations ) {
402
  foreach ( $translations as $tr_lang => $tr_id ) {
modules/sync/sync-post-metas.php CHANGED
@@ -9,6 +9,11 @@
9
  * @since 2.3
10
  */
11
  class PLL_Sync_Post_Metas extends PLL_Sync_Metas {
 
 
 
 
 
12
  public $options;
13
 
14
  /**
@@ -29,15 +34,15 @@ class PLL_Sync_Post_Metas extends PLL_Sync_Metas {
29
  }
30
 
31
  /**
32
- * Get the custom fields to copy or synchronize
33
  *
34
  * @since 2.3
35
  *
36
- * @param int $from Id of the post from which we copy informations
37
- * @param int $to Id of the post to which we paste informations
38
- * @param string $lang Language slug
39
- * @param bool $sync True if it is synchronization, false if it is a copy
40
- * @return array List of meta keys
41
  */
42
  protected function get_metas_to_copy( $from, $to, $lang, $sync = false ) {
43
  // Copy or synchronize post metas and allow plugins to do the same
9
  * @since 2.3
10
  */
11
  class PLL_Sync_Post_Metas extends PLL_Sync_Metas {
12
+ /**
13
+ * Stores the plugin options.
14
+ *
15
+ * @var array
16
+ */
17
  public $options;
18
 
19
  /**
34
  }
35
 
36
  /**
37
+ * Get the custom fields to copy or synchronize.
38
  *
39
  * @since 2.3
40
  *
41
+ * @param int $from Id of the post from which we copy informations.
42
+ * @param int $to Id of the post to which we paste informations.
43
+ * @param string $lang Language slug.
44
+ * @param bool $sync True if it is synchronization, false if it is a copy.
45
+ * @return string[] List of meta keys.
46
  */
47
  protected function get_metas_to_copy( $from, $to, $lang, $sync = false ) {
48
  // Copy or synchronize post metas and allow plugins to do the same
modules/sync/sync-tax.php CHANGED
@@ -10,6 +10,18 @@
10
  */
11
  class PLL_Sync_Tax {
12
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  /**
14
  * Constructor
15
  *
@@ -18,7 +30,7 @@ class PLL_Sync_Tax {
18
  * @param object $polylang
19
  */
20
  public function __construct( &$polylang ) {
21
- $this->model = &$polylang->model;
22
  $this->options = &$polylang->options;
23
 
24
  add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
@@ -28,16 +40,16 @@ class PLL_Sync_Tax {
28
  }
29
 
30
  /**
31
- * Get the list of taxonomies to copy or to synchronize
32
  *
33
  * @since 1.7
34
  * @since 2.1 The `$from`, `$to`, `$lang` parameters were added.
35
  *
36
- * @param bool $sync True if it is synchronization, false if it is a copy
37
- * @param int $from Id of the post from which we copy informations, optional, defaults to null
38
- * @param int $to Id of the post to which we paste informations, optional, defaults to null
39
- * @param string $lang Language slug, optional, defaults to null
40
- * @return array List of taxonomy names
41
  */
42
  protected function get_taxonomies_to_copy( $sync, $from = null, $to = null, $lang = null ) {
43
  $taxonomies = ! $sync || in_array( 'taxonomies', $this->options['sync'] ) ? $this->model->get_translated_taxonomies() : array();
@@ -46,16 +58,16 @@ class PLL_Sync_Tax {
46
  }
47
 
48
  /**
49
- * Filter the taxonomies to copy or synchronize
50
  *
51
  * @since 1.7
52
  * @since 2.1 The `$from`, `$to`, `$lang` parameters were added.
53
  *
54
- * @param array $taxonomies List of taxonomy names
55
- * @param bool $sync True if it is synchronization, false if it is a copy
56
- * @param int $from Id of the post from which we copy informations
57
- * @param int $to Id of the post to which we paste informations
58
- * @param string $lang Language slug
59
  */
60
  return array_unique( apply_filters( 'pll_copy_taxonomies', $taxonomies, $sync, $from, $to, $lang ) );
61
  }
@@ -65,11 +77,11 @@ class PLL_Sync_Tax {
65
  *
66
  * @since 2.3
67
  *
68
- * @param array $object_id Object ID
69
- * @param array $terms List of terms ids assigned to the source post
70
- * @param string $taxonomy Taxonomy name
71
- * @param string $lang Language slug
72
- * @return array List of terms ids to assign to the target post
73
  */
74
  protected function maybe_translate_terms( $object_id, $terms, $taxonomy, $lang ) {
75
  if ( is_array( $terms ) && $this->model->is_translated_taxonomy( $taxonomy ) ) {
@@ -104,7 +116,7 @@ class PLL_Sync_Tax {
104
  }
105
 
106
  /**
107
- * Maybe copy taxonomy terms from one post to the other
108
  *
109
  * @since 2.6
110
  *
@@ -114,6 +126,7 @@ class PLL_Sync_Tax {
114
  * @param array $terms An array of object terms.
115
  * @param string $taxonomy Taxonomy slug.
116
  * @param bool $append Whether to append new terms to the old terms.
 
117
  */
118
  protected function copy_object_terms( $object_id, $tr_id, $lang, $terms, $taxonomy, $append ) {
119
  $to_copy = $this->get_taxonomies_to_copy( true, $object_id, $tr_id, $lang );
@@ -139,22 +152,23 @@ class PLL_Sync_Tax {
139
  }
140
 
141
  /**
142
- * When assigning terms to a post, assign translated terms to the translated posts (synchronisation)
143
  *
144
  * @since 2.3
145
  *
146
  * @param int $object_id Object ID.
147
  * @param array $terms An array of object terms.
148
- * @param array $tt_ids An array of term taxonomy IDs.
149
  * @param string $taxonomy Taxonomy slug.
150
  * @param bool $append Whether to append new terms to the old terms.
 
151
  */
152
  public function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append ) {
153
  static $avoid_recursion = false;
154
  $taxonomy_object = get_taxonomy( $taxonomy );
155
 
156
  // Make sure that the taxonomy is registered for a post type
157
- if ( ! $avoid_recursion && array_filter( $taxonomy_object->object_type, 'post_type_exists' ) ) {
158
  $avoid_recursion = true;
159
 
160
  $tr_ids = $this->model->post->get_translations( $object_id );
@@ -193,6 +207,7 @@ class PLL_Sync_Tax {
193
  * @param int $from Id of the source post
194
  * @param int $to Id of the target post
195
  * @param string $lang Language slug
 
196
  */
197
  public function copy( $from, $to, $lang ) {
198
  remove_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 6 );
@@ -201,7 +216,7 @@ class PLL_Sync_Tax {
201
  $taxonomies = array_intersect( get_post_taxonomies( $from ), $this->get_taxonomies_to_copy( false, $from, $to, $lang ) );
202
 
203
  // Update the term cache to reduce the number of queries in the loop
204
- update_object_term_cache( $from, get_post_type( $from ) );
205
 
206
  // Copy
207
  foreach ( $taxonomies as $tax ) {
@@ -219,13 +234,14 @@ class PLL_Sync_Tax {
219
  }
220
 
221
  /**
222
- * When creating a new term, associate it to posts having translations associated to the translated terms
223
  *
224
  * @since 2.3
225
  *
226
- * @param int $term_id Id of the created term
227
- * @param string $taxonomy Taxonomy
228
- * @param array $translations Ids of the translations of the created term
 
229
  */
230
  public function create_term( $term_id, $taxonomy, $translations ) {
231
  if ( doing_action( 'create_term' ) && in_array( $taxonomy, $this->get_taxonomies_to_copy( true ) ) ) {
@@ -274,6 +290,8 @@ class PLL_Sync_Tax {
274
  * to avoid translated terms to be removed from translated posts
275
  *
276
  * @since 2.3.2
 
 
277
  */
278
  public function pre_delete_term() {
279
  remove_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
@@ -283,6 +301,8 @@ class PLL_Sync_Tax {
283
  * Re-activate the synchronization of terms after a term is deleted
284
  *
285
  * @since 2.3.2
 
 
286
  */
287
  public function delete_term() {
288
  add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
10
  */
11
  class PLL_Sync_Tax {
12
 
13
+ /**
14
+ * Stores the plugin options.
15
+ *
16
+ * @var array
17
+ */
18
+ protected $options;
19
+
20
+ /**
21
+ * @var PLL_Model
22
+ */
23
+ protected $model;
24
+
25
  /**
26
  * Constructor
27
  *
30
  * @param object $polylang
31
  */
32
  public function __construct( &$polylang ) {
33
+ $this->model = &$polylang->model;
34
  $this->options = &$polylang->options;
35
 
36
  add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
40
  }
41
 
42
  /**
43
+ * Get the list of taxonomies to copy or to synchronize.
44
  *
45
  * @since 1.7
46
  * @since 2.1 The `$from`, `$to`, `$lang` parameters were added.
47
  *
48
+ * @param bool $sync True if it is synchronization, false if it is a copy.
49
+ * @param int $from Id of the post from which we copy informations, optional, defaults to null.
50
+ * @param int $to Id of the post to which we paste informations, optional, defaults to null.
51
+ * @param string $lang Language slug, optional, defaults to null.
52
+ * @return string[] List of taxonomy names.
53
  */
54
  protected function get_taxonomies_to_copy( $sync, $from = null, $to = null, $lang = null ) {
55
  $taxonomies = ! $sync || in_array( 'taxonomies', $this->options['sync'] ) ? $this->model->get_translated_taxonomies() : array();
58
  }
59
 
60
  /**
61
+ * Filters the taxonomies to copy or synchronize.
62
  *
63
  * @since 1.7
64
  * @since 2.1 The `$from`, `$to`, `$lang` parameters were added.
65
  *
66
+ * @param string[] $taxonomies List of taxonomy names.
67
+ * @param bool $sync True if it is synchronization, false if it is a copy.
68
+ * @param int $from Id of the post from which we copy informations.
69
+ * @param int $to Id of the post to which we paste informations.
70
+ * @param string $lang Language slug.
71
  */
72
  return array_unique( apply_filters( 'pll_copy_taxonomies', $taxonomies, $sync, $from, $to, $lang ) );
73
  }
77
  *
78
  * @since 2.3
79
  *
80
+ * @param int $object_id Object ID.
81
+ * @param int[] $terms List of terms ids assigned to the source post.
82
+ * @param string $taxonomy Taxonomy name.
83
+ * @param string $lang Language slug.
84
+ * @return int[] List of terms ids to assign to the target post.
85
  */
86
  protected function maybe_translate_terms( $object_id, $terms, $taxonomy, $lang ) {
87
  if ( is_array( $terms ) && $this->model->is_translated_taxonomy( $taxonomy ) ) {
116
  }
117
 
118
  /**
119
+ * Maybe copy taxonomy terms from one post to the other.
120
  *
121
  * @since 2.6
122
  *
126
  * @param array $terms An array of object terms.
127
  * @param string $taxonomy Taxonomy slug.
128
  * @param bool $append Whether to append new terms to the old terms.
129
+ * @return void
130
  */
131
  protected function copy_object_terms( $object_id, $tr_id, $lang, $terms, $taxonomy, $append ) {
132
  $to_copy = $this->get_taxonomies_to_copy( true, $object_id, $tr_id, $lang );
152
  }
153
 
154
  /**
155
+ * When assigning terms to a post, assign translated terms to the translated posts (synchronisation).
156
  *
157
  * @since 2.3
158
  *
159
  * @param int $object_id Object ID.
160
  * @param array $terms An array of object terms.
161
+ * @param int[] $tt_ids An array of term taxonomy IDs.
162
  * @param string $taxonomy Taxonomy slug.
163
  * @param bool $append Whether to append new terms to the old terms.
164
+ * @return void
165
  */
166
  public function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append ) {
167
  static $avoid_recursion = false;
168
  $taxonomy_object = get_taxonomy( $taxonomy );
169
 
170
  // Make sure that the taxonomy is registered for a post type
171
+ if ( ! $avoid_recursion && ! empty( $taxonomy_object ) && array_filter( $taxonomy_object->object_type, 'post_type_exists' ) ) {
172
  $avoid_recursion = true;
173
 
174
  $tr_ids = $this->model->post->get_translations( $object_id );
207
  * @param int $from Id of the source post
208
  * @param int $to Id of the target post
209
  * @param string $lang Language slug
210
+ * @return void
211
  */
212
  public function copy( $from, $to, $lang ) {
213
  remove_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 6 );
216
  $taxonomies = array_intersect( get_post_taxonomies( $from ), $this->get_taxonomies_to_copy( false, $from, $to, $lang ) );
217
 
218
  // Update the term cache to reduce the number of queries in the loop
219
+ update_object_term_cache( array( $from ), get_post_type( $from ) );
220
 
221
  // Copy
222
  foreach ( $taxonomies as $tax ) {
234
  }
235
 
236
  /**
237
+ * When creating a new term, associate it to posts having translations associated to the translated terms.
238
  *
239
  * @since 2.3
240
  *
241
+ * @param int $term_id Id of the created term.
242
+ * @param string $taxonomy Taxonomy.
243
+ * @param int[] $translations Ids of the translations of the created term.
244
+ * @return void
245
  */
246
  public function create_term( $term_id, $taxonomy, $translations ) {
247
  if ( doing_action( 'create_term' ) && in_array( $taxonomy, $this->get_taxonomies_to_copy( true ) ) ) {
290
  * to avoid translated terms to be removed from translated posts
291
  *
292
  * @since 2.3.2
293
+ *
294
+ * @return void
295
  */
296
  public function pre_delete_term() {
297
  remove_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
301
  * Re-activate the synchronization of terms after a term is deleted
302
  *
303
  * @since 2.3.2
304
+ *
305
+ * @return void
306
  */
307
  public function delete_term() {
308
  add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
modules/sync/sync.php CHANGED
@@ -9,7 +9,32 @@
9
  * @since 2.4
10
  */
11
  class PLL_Sync {
12
- public $taxonomies, $post_metas, $term_meta;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  /**
15
  * Constructor
@@ -45,11 +70,11 @@ class PLL_Sync {
45
  }
46
 
47
  /**
48
- * Get post fields to synchornize
49
  *
50
  * @since 2.4
51
  *
52
- * @param object $post Post object
53
  * @return array
54
  */
55
  protected function get_fields_to_sync( $post ) {
@@ -119,13 +144,14 @@ class PLL_Sync {
119
  }
120
 
121
  /**
122
- * Synchronizes post fields in translations
123
  *
124
  * @since 2.4
125
  *
126
- * @param int $post_id post id
127
- * @param object $post post object
128
- * @param array $translations post translations
 
129
  */
130
  public function pll_save_post( $post_id, $post, $translations ) {
131
  global $wpdb;
@@ -170,24 +196,32 @@ class PLL_Sync {
170
  * @param int $term_id Term id.
171
  * @param int $tt_id Term taxonomy id, not used.
172
  * @param string $taxonomy Taxonomy name.
 
173
  */
174
  public function sync_term_parent( $term_id, $tt_id, $taxonomy ) {
175
  global $wpdb;
176
 
177
  if ( is_taxonomy_hierarchical( $taxonomy ) && $this->model->is_translated_taxonomy( $taxonomy ) ) {
178
  $term = get_term( $term_id );
179
- $translations = $this->model->term->get_translations( $term_id );
180
-
181
- foreach ( $translations as $lang => $tr_id ) {
182
- if ( ! empty( $tr_id ) && $tr_id !== $term_id ) {
183
- $tr_parent = $this->model->term->get_translation( $term->parent, $lang );
184
- $wpdb->update(
185
- $wpdb->term_taxonomy,
186
- array( 'parent' => isset( $tr_parent ) ? $tr_parent : 0 ),
187
- array( 'term_taxonomy_id' => get_term( (int) $tr_id, $taxonomy )->term_taxonomy_id )
188
- );
189
-
190
- clean_term_cache( $tr_id, $taxonomy ); // OK since WP 3.9
 
 
 
 
 
 
 
191
  }
192
  }
193
  }
@@ -199,19 +233,20 @@ class PLL_Sync {
199
  * @since 1.8
200
  *
201
  * @param int $post_id post id
 
202
  */
203
  public function edit_attachment( $post_id ) {
204
  $this->pll_save_post( $post_id, get_post( $post_id ), $this->model->post->get_translations( $post_id ) );
205
  }
206
 
207
  /**
208
- * Synchronize sticky posts
209
  *
210
  * @since 2.3
211
  *
212
- * @param array $value New option value
213
- * @param array $old_value Old option value
214
- * @return array
215
  */
216
  public function sync_sticky_posts( $value, $old_value ) {
217
  if ( in_array( 'sticky_posts', $this->options['sync'] ) ) {
9
  * @since 2.4
10
  */
11
  class PLL_Sync {
12
+ /**
13
+ * @var PLL_Sync_Tax
14
+ */
15
+ public $taxonomies;
16
+
17
+ /**
18
+ * @var PLL_Sync_Post_Metas
19
+ */
20
+ public $post_metas;
21
+
22
+ /**
23
+ * @var PLL_Sync_Term_Metas
24
+ */
25
+ public $term_metas;
26
+
27
+ /**
28
+ * Stores the plugin options.
29
+ *
30
+ * @var array
31
+ */
32
+ protected $options;
33
+
34
+ /**
35
+ * @var PLL_Model
36
+ */
37
+ protected $model;
38
 
39
  /**
40
  * Constructor
70
  }
71
 
72
  /**
73
+ * Get post fields to synchronize.
74
  *
75
  * @since 2.4
76
  *
77
+ * @param WP_Post $post Post object.
78
  * @return array
79
  */
80
  protected function get_fields_to_sync( $post ) {
144
  }
145
 
146
  /**
147
+ * Synchronizes post fields in translations.
148
  *
149
  * @since 2.4
150
  *
151
+ * @param int $post_id Post id.
152
+ * @param WP_Post $post Post object.
153
+ * @param int[] $translations Post translations.
154
+ * @return void
155
  */
156
  public function pll_save_post( $post_id, $post, $translations ) {
157
  global $wpdb;
196
  * @param int $term_id Term id.
197
  * @param int $tt_id Term taxonomy id, not used.
198
  * @param string $taxonomy Taxonomy name.
199
+ * @return void
200
  */
201
  public function sync_term_parent( $term_id, $tt_id, $taxonomy ) {
202
  global $wpdb;
203
 
204
  if ( is_taxonomy_hierarchical( $taxonomy ) && $this->model->is_translated_taxonomy( $taxonomy ) ) {
205
  $term = get_term( $term_id );
206
+
207
+ if ( $term instanceof WP_Term ) {
208
+ $translations = $this->model->term->get_translations( $term_id );
209
+
210
+ foreach ( $translations as $lang => $tr_id ) {
211
+ if ( ! empty( $tr_id ) && $tr_id !== $term_id ) {
212
+ $tr_parent = $this->model->term->get_translation( $term->parent, $lang );
213
+ $tr_term = get_term( (int) $tr_id, $taxonomy );
214
+
215
+ if ( $tr_term instanceof WP_Term ) {
216
+ $wpdb->update(
217
+ $wpdb->term_taxonomy,
218
+ array( 'parent' => $tr_parent ? $tr_parent : 0 ),
219
+ array( 'term_taxonomy_id' => $tr_term->term_taxonomy_id )
220
+ );
221
+
222
+ clean_term_cache( $tr_id, $taxonomy ); // OK since WP 3.9.
223
+ }
224
+ }
225
  }
226
  }
227
  }
233
  * @since 1.8
234
  *
235
  * @param int $post_id post id
236
+ * @return void
237
  */
238
  public function edit_attachment( $post_id ) {
239
  $this->pll_save_post( $post_id, get_post( $post_id ), $this->model->post->get_translations( $post_id ) );
240
  }
241
 
242
  /**
243
+ * Synchronize sticky posts.
244
  *
245
  * @since 2.3
246
  *
247
+ * @param int[] $value New option value.
248
+ * @param int[] $old_value Old option value.
249
+ * @return int[]
250
  */
251
  public function sync_sticky_posts( $value, $old_value ) {
252
  if ( in_array( 'sticky_posts', $this->options['sync'] ) ) {
modules/wizard/css/wizard.css CHANGED
@@ -3,7 +3,8 @@ body {
3
  margin: 65px auto 24px;
4
  box-shadow: none;
5
  background: #f1f1f1;
6
- padding: 0
 
7
  }
8
 
9
  #pll-logo {
3
  margin: 65px auto 24px;
4
  box-shadow: none;
5
  background: #f1f1f1;
6
+ padding: 0;
7
+ border: 0; /* fix-pro #856 override WP install.css */
8
  }
9
 
10
  #pll-logo {
modules/wizard/css/wizard.min.css DELETED
@@ -1 +0,0 @@
1
- @charset "UTF-8";body{margin:65px auto 24px;box-shadow:none;background:#f1f1f1;padding:0}#pll-logo{border:0;margin:0 0 24px;padding:0;text-align:center;font-family:sans-serif;font-size:64px;text-transform:uppercase;color:#000;line-height:normal}#pll-logo a{display:flex;justify-content:center;color:#000;text-decoration:none}#pll-logo img{max-width:100%;margin-right:16px}.rtl #pll-logo img{margin-right:0;margin-left:16px}.pll-wizard-footer{text-align:center}.pll-wizard .select2-container{text-align:left;width:auto}.pll-wizard .hidden{display:none}.pll-wizard-content{box-shadow:0 1px 3px rgba(0,0,0,.13);padding:2em;margin:0 0 20px;background:#fff;overflow:hidden;zoom:1;text-align:left}.rtl .pll-wizard-content{text-align:right}.pll-wizard-content h1,.pll-wizard-content h2,.pll-wizard-content h3,.pll-wizard-content table{margin:0 0 20px;border:0;padding:0;color:#666;clear:none;font-weight:500}.pll-wizard-content p{margin:20px 0;font-size:1em;line-height:1.75em;color:#666}.pll-wizard-content table{font-size:1em;line-height:1.75em;color:#666;width:100%;margin-top:20px}.pll-wizard-content table td span{display:inline-block}.pll-wizard-content table caption{caption-side:bottom;font-style:italic;text-align:right}.rtl .pll-wizard-content table caption{text-align:left}.pll-wizard-content table caption .icon-default-lang{font-style:normal}.pll-wizard-content a{color:#a03f3f}.pll-wizard-content a:focus,.pll-wizard-content a:hover,.pll-wizard-footer-links:hover{color:#dd5454}.pll-wizard-content .pll-wizard-next-steps{overflow:hidden;margin:0 0 24px;padding-bottom:2px}.pll-wizard-content .pll-wizard-next-steps h2{margin-bottom:12px}.pll-wizard-content .pll-wizard-next-steps .pll-wizard-next-steps-first{float:left;width:50%;box-sizing:border-box}.pll-wizard-content .pll-wizard-next-steps .pll-wizard-next-steps-last{float:right;width:50%;box-sizing:border-box}.pll-wizard-content .pll-wizard-next-steps ul{padding:0 2em 0 0;list-style:none outside;margin:0}.pll-wizard-content .pll-wizard-next-steps ul li a{display:block;padding:0 0 .75em}.pll-wizard-content .pll-wizard-next-steps ul li a::before{color:#82878c;font:normal 20px/1 dashicons;speak:none;display:inline-block;padding:0 10px 0 0;top:1px;position:relative;text-decoration:none!important;vertical-align:top}.pll-wizard-steps{padding:0 0 24px;margin:0;list-style:none outside;overflow:hidden;color:#ccc;width:100%;display:-webkit-inline-box;display:-webkit-inline-flex;display:inline-flex}.pll-wizard-steps li{width:100%;float:left;padding:0 0 .8em;margin:0;text-align:center;position:relative;border-bottom:4px solid #ccc;line-height:1.4em}.pll-wizard-steps li a{color:#a03f3f;text-decoration:none;padding:1.5em;margin:-1.5em;position:relative;z-index:1}.pll-wizard-steps li a:focus,.pll-wizard-steps li a:hover{color:#dd5454;text-decoration:underline}.pll-wizard-steps li::before{content:"";border:4px solid #ccc;border-radius:100%;width:4px;height:4px;position:absolute;bottom:0;left:50%;margin-left:-6px;margin-bottom:-8px;background:#fff}.pll-wizard-steps li.active{border-color:#a03f3f;color:#a03f3f;font-weight:700}.pll-wizard-steps li.active::before{border-color:#a03f3f}.pll-wizard-steps li.done{border-color:#a03f3f;color:#a03f3f}.pll-wizard-steps li.done::before{border-color:#a03f3f;background:#a03f3f}.pll-wizard .pll-wizard-actions{overflow:hidden;margin:20px 0 0;position:relative}.pll-wizard .pll-wizard-actions .button{font-size:16px;font-weight:300;padding:1em 2em;line-height:1em;margin-right:.5em;margin-bottom:2px;margin-top:10px;height:auto;border-radius:4px;box-shadow:none;min-width:auto;border-color:#a03f3f;color:#a03f3f}.pll-wizard .pll-wizard-content .button{border-color:#a03f3f;color:#a03f3f}.pll-wizard .pll-wizard-actions .button-primary,.pll-wizard .pll-wizard-content .button-primary{background-color:#a03f3f;border-color:#a03f3f;color:#fff;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a03f3f;text-shadow:0 -1px 1px #a03f3f,1px 0 1px #a03f3f,0 1px 1px #a03f3f,-1px 0 1px #a03f3f;margin:0;opacity:1}.pll-wizard .pll-wizard-content .button-small .dashicons{font-size:15px;height:auto;vertical-align:middle}.pll-wizard .button-primary:active,.pll-wizard .button-primary:focus,.pll-wizard .button-primary:hover,.pll-wizard input[type=checkbox]:focus+label.button-primary{background:#dd5454;border-color:#dd5454;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #dd5454}.pll-wizard .pll-wizard-actions .button-primary.disabled,.pll-wizard .pll-wizard-actions .button-primary:disabled,.pll-wizard .pll-wizard-actions .button-primary[disabled]{cursor:wait;background-color:#bb5454!important;border-color:#bb5454!important;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #bb5454!important;text-shadow:0 -1px 1px #bb5454,1px 0 1px #bb5454,0 1px 1px #bb5454,-1px 0 1px #bb5454!important;color:#ffa3a3!important}.pll-wizard-content p:last-child{margin-bottom:0}.pll-wizard-footer-links{font-size:.85em;color:#7b7b7b;margin:1.18em auto;display:inline-block;text-align:center}.pll-wizard-services{border:1px solid #eee;padding:0;margin:0 0 1em;list-style:none outside;border-radius:4px;overflow:hidden}.pll-wizard-services p{margin:0 0 1em 0;padding:0;font-size:1em;line-height:1.5em}.pll-wizard-service-item{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;padding:0;border-bottom:1px solid #eee;color:#666;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.media-step .pll-wizard-service-item{border:0}.media-step .pll-wizard-service-item:last-child{display:block}.media-step .pll-wizard-service-item .pll-wizard-service-enable{padding-bottom:0}.pll-wizard-service-item:last-child{border-bottom:0}.pll-wizard-service-item .pll-wizard-service-name{-webkit-flex-basis:0;flex-basis:0;min-width:160px;text-align:center;font-weight:700;padding:2em 0;-webkit-align-self:stretch;align-self:stretch;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:baseline;-webkit-align-items:baseline;align-items:baseline}.pll-wizard-service-item .pll-wizard-service-name img{max-width:75px}.pll-wizard-service-item .pll-wizard-service-description{-webkit-box-flex:1;-webkit-flex-grow:1;flex-grow:1;padding:20px}.pll-wizard-service-item .pll-wizard-service-example{padding:0 20px 20px}.pll-wizard-service-item .pll-wizard-service-example p{text-align:right}.rtl .pll-wizard-service-item .pll-wizard-service-example p{text-align:left}.pll-wizard-service-item .pll-wizard-service-description p{margin-bottom:1em}.pll-wizard-service-item .pll-wizard-service-description p:last-child{margin-bottom:0}.pll-wizard-service-item .pll-wizard-service-description .pll-wizard-service-settings-description{display:block;font-style:italic;color:#999}.pll-wizard-service-item .pll-wizard-service-enable{-webkit-flex-basis:0;flex-basis:0;min-width:75px;text-align:center;cursor:pointer;padding:2em 0;position:relative;max-height:1.5em;-webkit-align-self:flex-start;align-self:flex-start;-webkit-box-ordinal-group:4;-webkit-order:3;order:3}.pll-wizard-service-item .pll-wizard-service-toggle{position:relative}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]{position:absolute;opacity:0}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]+label{position:relative;display:inline-block;width:44px;height:20px;border-radius:10em;cursor:pointer;text-indent:-9999px}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:focus+label{border:1px dashed #777}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]+label::after,.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]+label::before{content:'';position:absolute}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]+label::before{left:0;top:0;width:44px;height:20px;background:#ddd;border-radius:10em;transition:background-color .2s}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]+label::after{width:16px;height:16px;transition:all .2s;border-radius:50%;background:#fff;margin:2px;top:0;left:0}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:checked+label::before{background:#a03f3f}.pll-wizard-service-item .pll-wizard-service-toggle input[type=checkbox]:checked+label::after{right:0;left:auto}.pll-wizard-service-item .pll-wizard-service-settings{display:none;margin-top:.75em;margin-bottom:0;cursor:default}.pll-wizard-service-item .pll-wizard-service-settings.hide{display:none}.pll-wizard-service-item.checked .pll-wizard-service-settings{display:inline-block}.pll-wizard-service-item.checked .pll-wizard-service-settings.hide{display:none}.pll-wizard-service-item.closed{border-bottom:0}.step{text-align:center}.pll-wizard .button .dashicons{vertical-align:middle}.rtl .dashicons-arrow-right-alt2:before{content:"\f341"}.pll-wizard .pll-wizard-actions .button:active,.pll-wizard .pll-wizard-actions .button:focus,.pll-wizard .pll-wizard-actions .button:hover{box-shadow:none}.pll-wizard-next-steps{border:1px solid #eee;border-radius:4px;list-style:none;padding:0}.pll-wizard-next-steps li{padding:0}.pll-wizard-next-steps .pll-wizard-next-step-item{display:-webkit-box;display:-webkit-flex;display:flex;border-top:1px solid #eee}.pll-wizard-next-steps .pll-wizard-next-step-item.no-border,.pll-wizard-next-steps .pll-wizard-next-step-item:first-child{border-top:0}.pll-wizard-next-steps .pll-wizard-next-step-description{-webkit-box-flex:1;-webkit-flex-grow:1;flex-grow:1;margin:1.5em}.pll-wizard-next-steps .pll-wizard-next-step-action{-webkit-box-flex:0;-webkit-flex-grow:0;flex-grow:0;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.pll-wizard-next-steps .pll-wizard-next-step-action .button{margin:1em 1.5em}.pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-actions,.pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-next-step-action .button,.pll-wizard-next-steps .pll-wizard-next-step-item.no-border .pll-wizard-next-step-description{margin-top:0}.pll-wizard-next-steps p.next-step-heading{margin:0;font-size:.95em;font-weight:400;font-variant:all-petite-caps}.pll-wizard-next-steps p.next-step-extra-info{margin:0}.pll-wizard-next-steps h3.next-step-description{margin:0;font-size:16px;font-weight:600}.pll-wizard-next-steps .pll-wizard-additional-steps{border-top:1px solid #eee}.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-next-step-description{margin-bottom:0}.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions{margin:0 0 1.5em 0}.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button{font-size:15px;margin:1em 0 1em 1.5em}.rtl .pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button{margin:1em 1.5em 1em 0}.pll-wizard-next-steps .pll-wizard-additional-steps .pll-wizard-actions .button::last-child{margin-right:1.5em}.pll-wizard-content img{max-width:100%;margin-right:.5em}.rtl .pll-wizard-content img{margin-left:.5em}.pll-wizard-content .form-field label{margin-bottom:5px;display:block}.pll-wizard-content .form-field select{padding:3px}.pll-wizard-content .languages-step select,.pll-wizard-content .untranslated-contents-step select{width:100%}.languages-step .form-field .button{margin-left:15px}.languages-step .form-field .button>span{margin-right:.3em}.rtl .languages-step .form-field .button{margin-left:0;margin-right:15px}.rtl .languages-step .form-field .button>span{margin-left:.3em;margin-right:0}.pll-wizard-content .languages-step .select-language-field{display:flex}.pll-wizard-content #languages{display:none}.pll-wizard-content #languages tr th:first-child{width:80%}.pll-wizard-content #languages .dashicons{color:#a03f3f}.pll-wizard-content #languages img{margin-right:5px}.pll-wizard-content .error{color:#a03f3f;font-weight:700}.pll-wizard-content #messages .error{background:#fccfcf;padding:.5rem;border:1px solid #a03f3f;margin-bottom:.5rem}.pll-wizard-content #slide-toggle{position:absolute;opacity:0}.pll-wizard-content #slide-toggle+label{position:relative}.pll-wizard-content #slide-toggle+label+span{display:block}.pll-wizard-content #slide-toggle+label .dashicons{margin-right:.3em}.rtl .pll-wizard-content #slide-toggle+label .dashicons{margin-left:.3em;margin-right:0}.pll-wizard-content #slide-toggle~#screenshot>img{max-height:500px;margin-top:10px;-webkit-transition:all .5s cubic-bezier(0,1,.5,1);transition:all .5s cubic-bezier(0,1,.5,1)}.pll-wizard-content #slide-toggle:checked~#screenshot>img{max-height:0}.hide{display:none}.field-in-error,input[type=checkbox].field-in-error,input[type=color].field-in-error,input[type=date].field-in-error,input[type=datetime-local].field-in-error,input[type=datetime].field-in-error,input[type=email].field-in-error,input[type=month].field-in-error,input[type=number].field-in-error,input[type=password].field-in-error,input[type=radio].field-in-error,input[type=search].field-in-error,input[type=tel].field-in-error,input[type=text].field-in-error,input[type=time].field-in-error,input[type=url].field-in-error,input[type=week].field-in-error,select.field-in-error,span.field-in-error,textarea.field-in-error{border-color:#a03f3f}.field-in-error:focus,input[type=checkbox].field-in-error:focus,input[type=color].field-in-error:focus,input[type=date].field-in-error:focus,input[type=datetime-local].field-in-error:focus,input[type=datetime].field-in-error:focus,input[type=email].field-in-error:focus,input[type=month].field-in-error:focus,input[type=number].field-in-error:focus,input[type=password].field-in-error:focus,input[type=radio].field-in-error:focus,input[type=search].field-in-error:focus,input[type=tel].field-in-error:focus,input[type=text].field-in-error:focus,input[type=time].field-in-error:focus,input[type=url].field-in-error:focus,input[type=week].field-in-error:focus,select.field-in-error:focus,span.field-in-error:focus,textarea.field-in-error:focus{border:1px solid #a03f3f;box-shadow:0 0 2px rgba(160,63,63,.8);outline-color:#a03f3f;outline-style:auto;outline-width:thin}.form-table input.regular-text{width:25em}.form-table input.field-in-error{border-color:#a03f3f}#pll-licenses-table td{padding:10px 9px}#pll-licenses-table .license-valid td p{min-width:35em}#pll-licenses-table .pll-deactivate-license{margin:0 0 0 20px}.rtl #pll-licenses-table .pll-deactivate-license{margin:0 10px 0 0}.pll-wizard-content .documentation{padding:24px 24px 0;margin:0 0 24px;overflow:hidden;background:#f5f5f5}.pll-wizard-content .documentation p{padding:0;margin:0 0 12px}.documentation-container{display:-webkit-box;display:-webkit-flex;display:flex;justify-content:flex-end}.documentation-container .documentation-button-container{-webkit-box-flex:0;-webkit-flex-grow:0;flex-grow:0}.wc-setup .wc-setup-actions .button.documentation-button{height:42px;padding:0 1em;margin:0}#dialog{display:none}.pll-wizard .ui-dialog.ui-widget-content{max-height:none}.pll-wizard .ui-dialog-title::before{content:"\f534";font-family:dashicons;display:inline-block;line-height:1;font-weight:400;font-style:normal;speak:none;text-decoration:inherit;text-transform:none;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:20px;height:20px;font-size:20px;vertical-align:middle;text-align:center;margin:0 5px 5px 0;transition:color .1s ease-in}.rtl.pll-wizard .ui-dialog-title::before{margin-right:0;margin-left:5px}.pll-wizard .ui-dialog ul{list-style:disc;padding-left:20px}.rtl.pll-wizard .ui-dialog ul{padding-left:0;padding-right:20px}.pll-wizard li{margin-bottom:0}#translations{border-collapse:collapse}#translations tbody:nth-child(odd){background-color:#f9f9f9}#translations.striped>tbody>:nth-child(odd){background-color:transparent}.pll-wizard-content mark{background:transparent none}.pll-wizard-content mark{color:#7ad03a}@media screen and (max-width:782px){.languages-step .form-field .button{font-size:13px;line-height:26px;height:28px;padding:0 10px 1px;vertical-align:top}#pll-licenses-table .pll-deactivate-license{margin:10px 0 5px}}@media only screen and (max-width:620px){.ui-dialog{width:100%!important}}@media only screen and (max-width:500px){#pll-logo a,.select-language-field{flex-direction:column}.select-language-field .action-buttons{display:flex;justify-content:flex-end}.languages-step .form-field .button{margin:5px 0 0}}@media only screen and (max-width:400px){#pll-logo{font-size:56px}.pll-wizard-steps{display:none}.pll-wizard-service-item{-webkit-flex-wrap:wrap;flex-wrap:wrap}.pll-wizard-service-item .pll-wizard-service-enable{-webkit-box-ordinal-group:3;-webkit-order:2;order:2;padding:20px 0 0}.pll-wizard-service-item .pll-wizard-service-description{-webkit-box-ordinal-group:4;-webkit-order:3;order:3}.pll-wizard-service-item .pll-wizard-service-name{padding:20px 20px 0;text-align:left;-webkit-box-pack:justify!important;-webkit-justify-content:space-between!important;justify-content:space-between!important}.pll-wizard-service-item .pll-wizard-service-name img{margin:0}.pll-wizard-next-steps .pll-wizard-next-step-item{-webkit-flex-wrap:wrap;flex-wrap:wrap}.pll-wizard-next-steps .pll-wizard-next-step-item .pll-wizard-next-step-description{margin-bottom:0}.pll-wizard-next-steps .pll-wizard-next-step-item .pll-wizard-next-step-action p{margin:0}}@media only screen and (max-width:360px){#pll-logo{font-size:48px}}
 
modules/wizard/js/languages-step.min.js DELETED
@@ -1 +0,0 @@
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/wizard/wizard.php CHANGED
@@ -10,36 +10,43 @@
10
  */
11
  class PLL_Wizard {
12
  /**
13
- * Reference to PLL_Model object
14
  *
15
- * @var object $model
16
  */
17
  protected $model;
18
 
19
  /**
20
- * Reference to Polylang options array
21
  *
22
- * @var array $options
23
  */
24
  protected $options;
25
 
26
  /**
27
- * List of steps
28
  *
29
  * @var array $steps {
30
- * @type string $name i18n string which names the step.
31
  * @type callable $view The callback function use to display the step content.
32
  * @type callable $handler The callback function use to process the step after it is submitted.
33
- * @type array $scripts List of scripts handle needed by the step.
34
- * @type array $styles The list of styles handle needed by the step.
35
  * }
36
  */
37
  protected $steps = array();
38
 
39
  /**
40
- * List of WordPress CSS file handles
41
  *
42
- * @var array $styles
 
 
 
 
 
 
 
43
  */
44
  protected $styles = array();
45
 
@@ -73,8 +80,10 @@ class PLL_Wizard {
73
  /**
74
  * Save an activation transient when Polylang is activating to redirect to the wizard
75
  *
76
- * @param bool $network_wide if activated for all sites in the network.
77
  * @since 2.7
 
 
 
78
  */
79
  public static function start_wizard( $network_wide ) {
80
  $options = get_option( 'polylang' );
@@ -89,6 +98,8 @@ class PLL_Wizard {
89
  * Redirect to the wizard depending on the context
90
  *
91
  * @since 2.7
 
 
92
  */
93
  public function redirect_to_wizard() {
94
  if ( get_transient( 'pll_activation_redirect' ) ) {
@@ -117,9 +128,10 @@ class PLL_Wizard {
117
  /**
118
  * Add an admin Polylang submenu to access the wizard
119
  *
120
- * @param array $tabs Submenus list.
121
- * @return array Submenus list updated.
122
  * @since 2.7
 
 
 
123
  */
124
  public function settings_tabs( $tabs ) {
125
  $tabs['wizard'] = esc_html__( 'Setup', 'polylang' );
@@ -127,11 +139,12 @@ class PLL_Wizard {
127
  }
128
 
129
  /**
130
- * Return if the media step is displayable
131
  *
132
- * @param array $languages List of language objects.
133
- * @return bool
134
  * @since 2.7
 
 
 
135
  */
136
  public function is_media_step_displayable( $languages ) {
137
  $media = array();
@@ -154,8 +167,9 @@ class PLL_Wizard {
154
  /**
155
  * Check if the licenses step is displayable
156
  *
157
- * @return bool
158
  * @since 2.7
 
 
159
  */
160
  public function is_licenses_step_displayable() {
161
  $licenses = apply_filters( 'pll_settings_licenses', array() );
@@ -166,6 +180,8 @@ class PLL_Wizard {
166
  * Setup the wizard page
167
  *
168
  * @since 2.7
 
 
169
  */
170
  public function setup_wizard_page() {
171
 
@@ -213,15 +229,16 @@ class PLL_Wizard {
213
  /**
214
  * Adds some admin screens where to display the wizard notice
215
  *
 
 
216
  * @param bool $can_display_notice Whether the notice can be displayed.
217
  * @param string $notice The notice name.
218
  * @return bool
219
- * @since 2.7
220
  */
221
  public function can_display_notice( $can_display_notice, $notice ) {
222
  if ( ! $can_display_notice && 'wizard' === $notice ) {
223
  $screen = get_current_screen();
224
- $can_display_notice = in_array(
225
  $screen->base,
226
  array(
227
  'edit',
@@ -237,6 +254,8 @@ class PLL_Wizard {
237
  * Return html code of the wizard notice
238
  *
239
  * @since 2.7
 
 
240
  */
241
  public function wizard_notice() {
242
  ob_start();
@@ -248,6 +267,8 @@ class PLL_Wizard {
248
  * Display the wizard page
249
  *
250
  * @since 2.7
 
 
251
  */
252
  public function display_wizard_page() {
253
  set_current_screen();
@@ -258,10 +279,12 @@ class PLL_Wizard {
258
  * Enqueue scripts and styles for the wizard
259
  *
260
  * @since 2.7
 
 
261
  */
262
  public function enqueue_scripts() {
263
- wp_enqueue_style( 'polylang_admin', plugins_url( '/css/admin' . $this->get_suffix() . '.css', POLYLANG_FILE ), array(), POLYLANG_VERSION );
264
- wp_enqueue_style( 'pll-wizard', plugins_url( '/modules/wizard/css/wizard' . $this->get_suffix() . '.css', POLYLANG_FILE ), array( 'dashicons', 'install', 'common', 'forms' ), POLYLANG_VERSION );
265
 
266
  $this->styles = array( 'polylang_admin', 'pll-wizard' );
267
  }
@@ -269,9 +292,10 @@ class PLL_Wizard {
269
  /**
270
  * Get the suffix to enqueue non minified files in a Debug context
271
  *
 
 
272
  * @return string Empty when SCRIPT_DEBUG equal to true
273
  * otherwise .min
274
- * @since 2.7
275
  */
276
  public function get_suffix() {
277
  return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
@@ -280,10 +304,11 @@ class PLL_Wizard {
280
  /**
281
  * Get the URL for the step's screen.
282
  *
 
 
283
  * @param string $step slug (default: current step).
284
  * @return string URL for the step if it exists.
285
  * Empty string on failure.
286
- * @since 2.7
287
  */
288
  public function get_step_link( $step = '' ) {
289
  if ( ! $step ) {
@@ -303,11 +328,12 @@ class PLL_Wizard {
303
  /**
304
  * Get the URL for the next step's screen.
305
  *
 
 
306
  * @param string $step slug (default: current step).
307
  * @return string URL for next step if a next step exists.
308
  * Admin URL if it's the last step.
309
  * Empty string on failure.
310
- * @since 2.7
311
  */
312
  public function get_next_step_link( $step = '' ) {
313
  if ( ! $step ) {
@@ -330,16 +356,20 @@ class PLL_Wizard {
330
  /**
331
  * Add licenses step to the wizard
332
  *
 
 
333
  * @param array $steps List of steps.
334
  * @return array List of steps updated.
335
- * @since 2.7
336
  */
337
  public function add_step_licenses( $steps ) {
338
  // Add ajax action on deactivate button in licenses step.
339
  add_action( 'wp_ajax_pll_deactivate_license', array( $this, 'deactivate_license' ) );
340
 
341
- wp_enqueue_script( 'pll_admin', plugins_url( '/js/admin' . $this->get_suffix() . '.js', POLYLANG_FILE ), array( 'jquery', 'jquery-ui-selectmenu' ), POLYLANG_VERSION, true );
342
- wp_localize_script( 'pll_admin', 'pll_dismiss_notice', esc_html__( 'Dismiss this notice.', 'polylang' ) );
 
 
 
343
  if ( $this->is_licenses_step_displayable() ) {
344
  $steps['licenses'] = array(
345
  'name' => esc_html__( 'Licenses', 'polylang' ),
@@ -356,6 +386,8 @@ class PLL_Wizard {
356
  * Display the languages step form
357
  *
358
  * @since 2.7
 
 
359
  */
360
  public function display_step_licenses() {
361
  include __DIR__ . '/view-wizard-step-licenses.php';
@@ -365,6 +397,8 @@ class PLL_Wizard {
365
  * Execute the languages step
366
  *
367
  * @since 2.7
 
 
368
  */
369
  public function save_step_licenses() {
370
  check_admin_referer( 'pll-wizard', '_pll_nonce' );
@@ -395,6 +429,8 @@ class PLL_Wizard {
395
  * Ajax method to deactivate a license
396
  *
397
  * @since 2.7
 
 
398
  */
399
  public function deactivate_license() {
400
  check_ajax_referer( 'pll-wizard', '_pll_nonce' );
@@ -423,14 +459,18 @@ class PLL_Wizard {
423
  /**
424
  * Add languages step to the wizard
425
  *
 
 
426
  * @param array $steps List of steps.
427
  * @return array List of steps updated.
428
- * @since 2.7
429
  */
430
  public function add_step_languages( $steps ) {
431
- wp_enqueue_script( 'pll-wizard-language-choice', plugins_url( '/js/admin' . $this->get_suffix() . '.js', POLYLANG_FILE ), array( 'jquery', 'jquery-ui-selectmenu' ), POLYLANG_VERSION, true );
432
- wp_localize_script( 'pll-wizard-language-choice', 'pll_dismiss_notice', esc_html__( 'Dismiss this notice.', 'polylang' ) );
433
- wp_register_script( 'pll-wizard-languages', plugins_url( '/modules/wizard/js/languages-step' . $this->get_suffix() . '.js', POLYLANG_FILE ), array( 'jquery', 'jquery-ui-dialog' ), POLYLANG_VERSION, true );
 
 
 
434
  wp_localize_script(
435
  'pll-wizard-languages',
436
  'pll_wizard_params',
@@ -453,12 +493,12 @@ class PLL_Wizard {
453
  )
454
  );
455
  wp_enqueue_script( 'pll-wizard-languages' );
456
- wp_enqueue_style( 'pll-wizard-selectmenu', plugins_url( '/css/selectmenu' . $this->get_suffix() . '.css', POLYLANG_FILE ), array( 'dashicons', 'install', 'common', 'wp-jquery-ui-dialog' ), POLYLANG_VERSION );
457
  $steps['languages'] = array(
458
  'name' => esc_html__( 'Languages', 'polylang' ),
459
  'view' => array( $this, 'display_step_languages' ),
460
  'handler' => array( $this, 'save_step_languages' ),
461
- 'scripts' => array( 'pll-wizard-languages', 'pll-wizard-language-choice' ),
462
  'styles' => array( 'pll-wizard-selectmenu' ),
463
  );
464
  return $steps;
@@ -468,6 +508,8 @@ class PLL_Wizard {
468
  * Display the languages step form
469
  *
470
  * @since 2.7
 
 
471
  */
472
  public function display_step_languages() {
473
  include __DIR__ . '/view-wizard-step-languages.php';
@@ -477,6 +519,8 @@ class PLL_Wizard {
477
  * Execute the languages step
478
  *
479
  * @since 2.7
 
 
480
  */
481
  public function save_step_languages() {
482
  check_admin_referer( 'pll-wizard', '_pll_nonce' );
@@ -484,7 +528,7 @@ class PLL_Wizard {
484
  $existing_languages = $this->model->get_languages_list();
485
 
486
  $all_languages = include POLYLANG_DIR . '/settings/languages.php';
487
- $languages = isset( $_POST['languages'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['languages'] ) ) : false;
488
  $saved_languages = array();
489
 
490
  // If there is no language added or defined.
@@ -550,12 +594,12 @@ class PLL_Wizard {
550
  }
551
 
552
  /**
553
- * Add media step to the wizard
554
- * Add media step to the wizard
 
555
  *
556
  * @param array $steps List of steps.
557
  * @return array List of steps updated.
558
- * @since 2.7
559
  */
560
  public function add_step_media( $steps ) {
561
  $languages = $this->model->get_languages_list();
@@ -576,6 +620,8 @@ class PLL_Wizard {
576
  * Display the media step form
577
  *
578
  * @since 2.7
 
 
579
  */
580
  public function display_step_media() {
581
  include __DIR__ . '/view-wizard-step-media.php';
@@ -585,6 +631,8 @@ class PLL_Wizard {
585
  * Execute the media step
586
  *
587
  * @since 2.7
 
 
588
  */
589
  public function save_step_media() {
590
  check_admin_referer( 'pll-wizard', '_pll_nonce' );
@@ -602,20 +650,23 @@ class PLL_Wizard {
602
  /**
603
  * Add untranslated contents step to the wizard
604
  *
 
 
605
  * @param array $steps List of steps.
606
  * @return array List of steps updated.
607
- * @since 2.7
608
  */
609
  public function add_step_untranslated_contents( $steps ) {
610
  if ( ! $this->model->get_languages_list() || $this->model->get_objects_with_no_lang( 1 ) ) {
611
- wp_enqueue_script( 'pll-wizard-language-choice', plugins_url( '/js/admin' . $this->get_suffix() . '.js', POLYLANG_FILE ), array( 'jquery', 'jquery-ui-selectmenu' ), POLYLANG_VERSION, true );
612
- wp_localize_script( 'pll-wizard-language-choice', 'pll_dismiss_notice', esc_html__( 'Dismiss this notice.', 'polylang' ) );
613
- wp_enqueue_style( 'pll-wizard-selectmenu', plugins_url( '/css/selectmenu' . $this->get_suffix() . '.css', POLYLANG_FILE ), array( 'dashicons', 'install', 'common' ), POLYLANG_VERSION );
 
 
614
  $steps['untranslated-contents'] = array(
615
  'name' => esc_html__( 'Content', 'polylang' ),
616
  'view' => array( $this, 'display_step_untranslated_contents' ),
617
  'handler' => array( $this, 'save_step_untranslated_contents' ),
618
- 'scripts' => array( 'pll-wizard-language-choice' ),
619
  'styles' => array( 'pll-wizard-selectmenu' ),
620
  );
621
  }
@@ -626,6 +677,8 @@ class PLL_Wizard {
626
  * Display the untranslated contents step form
627
  *
628
  * @since 2.7
 
 
629
  */
630
  public function display_step_untranslated_contents() {
631
  include __DIR__ . '/view-wizard-step-untranslated-contents.php';
@@ -635,6 +688,8 @@ class PLL_Wizard {
635
  * Execute the untranslated contents step
636
  *
637
  * @since 2.7
 
 
638
  */
639
  public function save_step_untranslated_contents() {
640
  check_admin_referer( 'pll-wizard', '_pll_nonce' );
@@ -663,9 +718,10 @@ class PLL_Wizard {
663
  /**
664
  * Add home page step to the wizard
665
  *
 
 
666
  * @param array $steps List of steps.
667
  * @return array List of steps updated.
668
- * @since 2.7
669
  */
670
  public function add_step_home_page( $steps ) {
671
  $languages = $this->model->get_languages_list();
@@ -689,6 +745,8 @@ class PLL_Wizard {
689
  * Display the home page step form
690
  *
691
  * @since 2.7
 
 
692
  */
693
  public function display_step_home_page() {
694
  include __DIR__ . '/view-wizard-step-home-page.php';
@@ -698,6 +756,8 @@ class PLL_Wizard {
698
  * Execute the home page step
699
  *
700
  * @since 2.7
 
 
701
  */
702
  public function save_step_home_page() {
703
  check_admin_referer( 'pll-wizard', '_pll_nonce' );
@@ -731,11 +791,12 @@ class PLL_Wizard {
731
  *
732
  * @since 2.7
733
  *
734
- * @param string $default_language slug of the default language; null if no default language is defined.
735
- * @param int $home_page post_id of the home page if it's defined, false otherwise.
736
- * @param string $home_page_title home page title if it's defined, 'Homepage' otherwise.
737
- * @param string $home_page_language slug of the home page if it's defined, false otherwise.
738
- * @param array $untranslated_languages array of languages which needs to have a home page translated.
 
739
  */
740
  public function create_home_page_translations( $default_language, $home_page, $home_page_title, $home_page_language, $untranslated_languages ) {
741
  $translations = $this->model->post->get_translations( $home_page );
@@ -758,9 +819,10 @@ class PLL_Wizard {
758
  /**
759
  * Add last step to the wizard
760
  *
 
 
761
  * @param array $steps List of steps.
762
  * @return array List of steps updated.
763
- * @since 2.7
764
  */
765
  public function add_step_last( $steps ) {
766
  $steps['last'] = array(
@@ -777,6 +839,8 @@ class PLL_Wizard {
777
  * Display the last step form
778
  *
779
  * @since 2.7
 
 
780
  */
781
  public function display_step_last() {
782
  // We ran the wizard once. So we can dismiss its notice.
@@ -788,6 +852,8 @@ class PLL_Wizard {
788
  * Execute the last step
789
  *
790
  * @since 2.7
 
 
791
  */
792
  public function save_step_last() {
793
  check_admin_referer( 'pll-wizard', '_pll_nonce' );
10
  */
11
  class PLL_Wizard {
12
  /**
13
+ * Reference to the model object
14
  *
15
+ * @var PLL_Admin_Model
16
  */
17
  protected $model;
18
 
19
  /**
20
+ * Reference to the Polylang options array.
21
  *
22
+ * @var array
23
  */
24
  protected $options;
25
 
26
  /**
27
+ * List of steps.
28
  *
29
  * @var array $steps {
30
+ * @type string $name I18n string which names the step.
31
  * @type callable $view The callback function use to display the step content.
32
  * @type callable $handler The callback function use to process the step after it is submitted.
33
+ * @type array $scripts List of scripts handle needed by the step.
34
+ * @type array $styles The list of styles handle needed by the step.
35
  * }
36
  */
37
  protected $steps = array();
38
 
39
  /**
40
+ * The current step.
41
  *
42
+ * @var string
43
+ */
44
+ protected $step;
45
+
46
+ /**
47
+ * List of WordPress CSS file handles.
48
+ *
49
+ * @var string[]
50
  */
51
  protected $styles = array();
52
 
80
  /**
81
  * Save an activation transient when Polylang is activating to redirect to the wizard
82
  *
 
83
  * @since 2.7
84
+ *
85
+ * @param bool $network_wide if activated for all sites in the network.
86
+ * @return void
87
  */
88
  public static function start_wizard( $network_wide ) {
89
  $options = get_option( 'polylang' );
98
  * Redirect to the wizard depending on the context
99
  *
100
  * @since 2.7
101
+ *
102
+ * @return void
103
  */
104
  public function redirect_to_wizard() {
105
  if ( get_transient( 'pll_activation_redirect' ) ) {
128
  /**
129
  * Add an admin Polylang submenu to access the wizard
130
  *
 
 
131
  * @since 2.7
132
+ *
133
+ * @param string[] $tabs Submenus list.
134
+ * @return string[] Submenus list updated.
135
  */
136
  public function settings_tabs( $tabs ) {
137
  $tabs['wizard'] = esc_html__( 'Setup', 'polylang' );
139
  }
140
 
141
  /**
142
+ * Returns true if the media step is displayable, false otherwise.
143
  *
 
 
144
  * @since 2.7
145
+ *
146
+ * @param PLL_Language[] $languages List of language objects.
147
+ * @return bool
148
  */
149
  public function is_media_step_displayable( $languages ) {
150
  $media = array();
167
  /**
168
  * Check if the licenses step is displayable
169
  *
 
170
  * @since 2.7
171
+ *
172
+ * @return bool
173
  */
174
  public function is_licenses_step_displayable() {
175
  $licenses = apply_filters( 'pll_settings_licenses', array() );
180
  * Setup the wizard page
181
  *
182
  * @since 2.7
183
+ *
184
+ * @return void
185
  */
186
  public function setup_wizard_page() {
187
 
229
  /**
230
  * Adds some admin screens where to display the wizard notice
231
  *
232
+ * @since 2.7
233
+ *
234
  * @param bool $can_display_notice Whether the notice can be displayed.
235
  * @param string $notice The notice name.
236
  * @return bool
 
237
  */
238
  public function can_display_notice( $can_display_notice, $notice ) {
239
  if ( ! $can_display_notice && 'wizard' === $notice ) {
240
  $screen = get_current_screen();
241
+ $can_display_notice = ! empty( $screen ) && in_array(
242
  $screen->base,
243
  array(
244
  'edit',
254
  * Return html code of the wizard notice
255
  *
256
  * @since 2.7
257
+ *
258
+ * @return string
259
  */
260
  public function wizard_notice() {
261
  ob_start();
267
  * Display the wizard page
268
  *
269
  * @since 2.7
270
+ *
271
+ * @return void
272
  */
273
  public function display_wizard_page() {
274
  set_current_screen();
279
  * Enqueue scripts and styles for the wizard
280
  *
281
  * @since 2.7
282
+ *
283
+ * @return void
284
  */
285
  public function enqueue_scripts() {
286
+ wp_enqueue_style( 'polylang_admin', plugins_url( '/css/build/admin' . $this->get_suffix() . '.css', POLYLANG_BASENAME ), array(), POLYLANG_VERSION );
287
+ wp_enqueue_style( 'pll-wizard', plugins_url( '/css/build/wizard' . $this->get_suffix() . '.css', POLYLANG_BASENAME ), array( 'dashicons', 'install', 'common', 'forms' ), POLYLANG_VERSION );
288
 
289
  $this->styles = array( 'polylang_admin', 'pll-wizard' );
290
  }
292
  /**
293
  * Get the suffix to enqueue non minified files in a Debug context
294
  *
295
+ * @since 2.7
296
+ *
297
  * @return string Empty when SCRIPT_DEBUG equal to true
298
  * otherwise .min
 
299
  */
300
  public function get_suffix() {
301
  return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
304
  /**
305
  * Get the URL for the step's screen.
306
  *
307
+ * @since 2.7
308
+ *
309
  * @param string $step slug (default: current step).
310
  * @return string URL for the step if it exists.
311
  * Empty string on failure.
 
312
  */
313
  public function get_step_link( $step = '' ) {
314
  if ( ! $step ) {
328
  /**
329
  * Get the URL for the next step's screen.
330
  *
331
+ * @since 2.7
332
+ *
333
  * @param string $step slug (default: current step).
334
  * @return string URL for next step if a next step exists.
335
  * Admin URL if it's the last step.
336
  * Empty string on failure.
 
337
  */
338
  public function get_next_step_link( $step = '' ) {
339
  if ( ! $step ) {
356
  /**
357
  * Add licenses step to the wizard
358
  *
359
+ * @since 2.7
360
+ *
361
  * @param array $steps List of steps.
362
  * @return array List of steps updated.
 
363
  */
364
  public function add_step_licenses( $steps ) {
365
  // Add ajax action on deactivate button in licenses step.
366
  add_action( 'wp_ajax_pll_deactivate_license', array( $this, 'deactivate_license' ) );
367
 
368
+ // Be careful pll_admin script is enqueued here without depedency except jquery because only code useful for deactivate license button is needed.
369
+ // To be really loaded the script need to be passed to the $steps['licenses']['scripts'] array below with the same handle than in wp_enqueue_script().
370
+ wp_enqueue_script( 'pll_admin', plugins_url( '/js/build/admin' . $this->get_suffix() . '.js', POLYLANG_BASENAME ), array( 'jquery' ), POLYLANG_VERSION, true );
371
+ wp_localize_script( 'pll_admin', 'pll_admin', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
372
+
373
  if ( $this->is_licenses_step_displayable() ) {
374
  $steps['licenses'] = array(
375
  'name' => esc_html__( 'Licenses', 'polylang' ),
386
  * Display the languages step form
387
  *
388
  * @since 2.7
389
+ *
390
+ * @return void
391
  */
392
  public function display_step_licenses() {
393
  include __DIR__ . '/view-wizard-step-licenses.php';
397
  * Execute the languages step
398
  *
399
  * @since 2.7
400
+ *
401
+ * @return void
402
  */
403
  public function save_step_licenses() {
404
  check_admin_referer( 'pll-wizard', '_pll_nonce' );
429
  * Ajax method to deactivate a license
430
  *
431
  * @since 2.7
432
+ *
433
+ * @return void
434
  */
435
  public function deactivate_license() {
436
  check_ajax_referer( 'pll-wizard', '_pll_nonce' );
459
  /**
460
  * Add languages step to the wizard
461
  *
462
+ * @since 2.7
463
+ *
464
  * @param array $steps List of steps.
465
  * @return array List of steps updated.
 
466
  */
467
  public function add_step_languages( $steps ) {
468
+ wp_deregister_script( 'pll_admin' ); // Deregister after the licenses step enqueue to update jquery-ui-selectmenu dependency.
469
+ // The wp-ajax-response and postbox dependencies is useless in wizard steps espacially postbox which triggers a javascript error otherwise.
470
+ // To be really loaded the script need to be passed to the $steps['languages']['scripts'] array below with the same handle than in wp_enqueue_script().
471
+ wp_enqueue_script( 'pll_admin', plugins_url( '/js/build/admin' . $this->get_suffix() . '.js', POLYLANG_BASENAME ), array( 'jquery', 'jquery-ui-selectmenu' ), POLYLANG_VERSION, true );
472
+ wp_localize_script( 'pll_admin', 'pll_admin', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
473
+ wp_register_script( 'pll-wizard-languages', plugins_url( '/js/build/languages-step' . $this->get_suffix() . '.js', POLYLANG_BASENAME ), array( 'jquery', 'jquery-ui-dialog' ), POLYLANG_VERSION, true );
474
  wp_localize_script(
475
  'pll-wizard-languages',
476
  'pll_wizard_params',
493
  )
494
  );
495
  wp_enqueue_script( 'pll-wizard-languages' );
496
+ wp_enqueue_style( 'pll-wizard-selectmenu', plugins_url( '/css/build/selectmenu' . $this->get_suffix() . '.css', POLYLANG_BASENAME ), array( 'dashicons', 'install', 'common', 'wp-jquery-ui-dialog' ), POLYLANG_VERSION );
497
  $steps['languages'] = array(
498
  'name' => esc_html__( 'Languages', 'polylang' ),
499
  'view' => array( $this, 'display_step_languages' ),
500
  'handler' => array( $this, 'save_step_languages' ),
501
+ 'scripts' => array( 'pll-wizard-languages', 'pll_admin' ),
502
  'styles' => array( 'pll-wizard-selectmenu' ),
503
  );
504
  return $steps;
508
  * Display the languages step form
509
  *
510
  * @since 2.7
511
+ *
512
+ * @return void
513
  */
514
  public function display_step_languages() {
515
  include __DIR__ . '/view-wizard-step-languages.php';
519
  * Execute the languages step
520
  *
521
  * @since 2.7
522
+ *
523
+ * @return void
524
  */
525
  public function save_step_languages() {
526
  check_admin_referer( 'pll-wizard', '_pll_nonce' );
528
  $existing_languages = $this->model->get_languages_list();
529
 
530
  $all_languages = include POLYLANG_DIR . '/settings/languages.php';
531
+ $languages = isset( $_POST['languages'] ) && is_array( $_POST['languages'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['languages'] ) ) : false;
532
  $saved_languages = array();
533
 
534
  // If there is no language added or defined.
594
  }
595
 
596
  /**
597
+ * Add the media step to the wizard.
598
+ *
599
+ * @since 2.7
600
  *
601
  * @param array $steps List of steps.
602
  * @return array List of steps updated.
 
603
  */
604
  public function add_step_media( $steps ) {
605
  $languages = $this->model->get_languages_list();
620
  * Display the media step form
621
  *
622
  * @since 2.7
623
+ *
624
+ * @return void
625
  */
626
  public function display_step_media() {
627
  include __DIR__ . '/view-wizard-step-media.php';
631
  * Execute the media step
632
  *
633
  * @since 2.7
634
+ *
635
+ * @return void
636
  */
637
  public function save_step_media() {
638
  check_admin_referer( 'pll-wizard', '_pll_nonce' );
650
  /**
651
  * Add untranslated contents step to the wizard
652
  *
653
+ * @since 2.7
654
+ *
655
  * @param array $steps List of steps.
656
  * @return array List of steps updated.
 
657
  */
658
  public function add_step_untranslated_contents( $steps ) {
659
  if ( ! $this->model->get_languages_list() || $this->model->get_objects_with_no_lang( 1 ) ) {
660
+ // Even if pll_admin is already enqueued with the same dependencies by the languages step, it is interesting to keep that it's also useful for the untranslated-contents step.
661
+ // To be really loaded the script need to be passed to the $steps['untranslated-contents']['scripts'] array below with the same handle than in wp_enqueue_script().
662
+ wp_enqueue_script( 'pll_admin', plugins_url( '/js/build/admin' . $this->get_suffix() . '.js', POLYLANG_BASENAME ), array( 'jquery', 'jquery-ui-selectmenu' ), POLYLANG_VERSION, true );
663
+ wp_localize_script( 'pll_admin', 'pll_admin', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
664
+ wp_enqueue_style( 'pll-wizard-selectmenu', plugins_url( '/css/build/selectmenu' . $this->get_suffix() . '.css', POLYLANG_BASENAME ), array( 'dashicons', 'install', 'common' ), POLYLANG_VERSION );
665
  $steps['untranslated-contents'] = array(
666
  'name' => esc_html__( 'Content', 'polylang' ),
667
  'view' => array( $this, 'display_step_untranslated_contents' ),
668
  'handler' => array( $this, 'save_step_untranslated_contents' ),
669
+ 'scripts' => array( 'pll_admin' ),
670
  'styles' => array( 'pll-wizard-selectmenu' ),
671
  );
672
  }
677
  * Display the untranslated contents step form
678
  *
679
  * @since 2.7
680
+ *
681
+ * @return void
682
  */
683
  public function display_step_untranslated_contents() {
684
  include __DIR__ . '/view-wizard-step-untranslated-contents.php';
688
  * Execute the untranslated contents step
689
  *
690
  * @since 2.7
691
+ *
692
+ * @return void
693
  */
694
  public function save_step_untranslated_contents() {
695
  check_admin_referer( 'pll-wizard', '_pll_nonce' );
718
  /**
719
  * Add home page step to the wizard
720
  *
721
+ * @since 2.7
722
+ *
723
  * @param array $steps List of steps.
724
  * @return array List of steps updated.
 
725
  */
726
  public function add_step_home_page( $steps ) {
727
  $languages = $this->model->get_languages_list();
745
  * Display the home page step form
746
  *
747
  * @since 2.7
748
+ *
749
+ * @return void
750
  */
751
  public function display_step_home_page() {
752
  include __DIR__ . '/view-wizard-step-home-page.php';
756
  * Execute the home page step
757
  *
758
  * @since 2.7
759
+ *
760
+ * @return void
761
  */
762
  public function save_step_home_page() {
763
  check_admin_referer( 'pll-wizard', '_pll_nonce' );
791
  *
792
  * @since 2.7
793
  *
794
+ * @param string $default_language Slug of the default language; null if no default language is defined.
795
+ * @param int $home_page Post ID of the home page if it's defined, false otherwise.
796
+ * @param string $home_page_title Home page title if it's defined, 'Homepage' otherwise.
797
+ * @param string $home_page_language Slug of the home page if it's defined, false otherwise.
798
+ * @param string[] $untranslated_languages Array of languages which needs to have a home page translated.
799
+ * @return void
800
  */
801
  public function create_home_page_translations( $default_language, $home_page, $home_page_title, $home_page_language, $untranslated_languages ) {
802
  $translations = $this->model->post->get_translations( $home_page );
819
  /**
820
  * Add last step to the wizard
821
  *
822
+ * @since 2.7
823
+ *
824
  * @param array $steps List of steps.
825
  * @return array List of steps updated.
 
826
  */
827
  public function add_step_last( $steps ) {
828
  $steps['last'] = array(
839
  * Display the last step form
840
  *
841
  * @since 2.7
842
+ *
843
+ * @return void
844
  */
845
  public function display_step_last() {
846
  // We ran the wizard once. So we can dismiss its notice.
852
  * Execute the last step
853
  *
854
  * @since 2.7
855
+ *
856
+ * @return void
857
  */
858
  public function save_step_last() {
859
  check_admin_referer( 'pll-wizard', '_pll_nonce' );
modules/wpml/wpml-api.php CHANGED
@@ -12,6 +12,11 @@
12
  * @since 2.0
13
  */
14
  class PLL_WPML_API {
 
 
 
 
 
15
  private static $original_language = null;
16
 
17
  /**
@@ -143,6 +148,8 @@ class PLL_WPML_API {
143
  * Returns an HTML hidden input field with name=”lang” and as value the current language
144
  *
145
  * @since 2.0
 
 
146
  */
147
  public function wpml_add_language_form_field() {
148
  $lang = pll_current_language();
@@ -184,6 +191,7 @@ class PLL_WPML_API {
184
  *
185
  * @param null|string $lang Language code to switch into, restores the original language if null.
186
  * @param bool|string $cookie Optionally also switches the cookie.
 
187
  */
188
  public static function wpml_switch_language( $lang = null, $cookie = false ) {
189
  if ( null === self::$original_language ) {
@@ -218,8 +226,11 @@ class PLL_WPML_API {
218
  $type = $args['element_type'];
219
  $id = $args['element_id'];
220
  $pll_type = ( 'post' == $type || pll_is_translated_post_type( $type ) ) ? 'post' : ( 'term' == $type || pll_is_translated_taxonomy( $type ) ? 'term' : false );
221
- if ( 'term' === $pll_type && $term = get_term_by( 'term_taxonomy_id', $id ) ) {
222
- $id = $term->term_id;
 
 
 
223
  }
224
  return $pll_type ? call_user_func( "pll_get_{$pll_type}_language", $id ) : $language_code;
225
  }
12
  * @since 2.0
13
  */
14
  class PLL_WPML_API {
15
+ /**
16
+ * Stores the original language when the language is switched.
17
+ *
18
+ * @var PLL_Language
19
+ */
20
  private static $original_language = null;
21
 
22
  /**
148
  * Returns an HTML hidden input field with name=”lang” and as value the current language
149
  *
150
  * @since 2.0
151
+ *
152
+ * @return void
153
  */
154
  public function wpml_add_language_form_field() {
155
  $lang = pll_current_language();
191
  *
192
  * @param null|string $lang Language code to switch into, restores the original language if null.
193
  * @param bool|string $cookie Optionally also switches the cookie.
194
+ * @return void
195
  */
196
  public static function wpml_switch_language( $lang = null, $cookie = false ) {
197
  if ( null === self::$original_language ) {
226
  $type = $args['element_type'];
227
  $id = $args['element_id'];
228
  $pll_type = ( 'post' == $type || pll_is_translated_post_type( $type ) ) ? 'post' : ( 'term' == $type || pll_is_translated_taxonomy( $type ) ? 'term' : false );
229
+ if ( 'term' === $pll_type ) {
230
+ $term = get_term_by( 'term_taxonomy_id', $id );
231
+ if ( $term instanceof WP_Term ) {
232
+ $id = $term->term_id;
233
+ }
234
  }
235
  return $pll_type ? call_user_func( "pll_get_{$pll_type}_language", $id ) : $language_code;
236
  }
modules/wpml/wpml-compat.php CHANGED
@@ -11,8 +11,23 @@
11
  * @since 1.0.2
12
  */
13
  class PLL_WPML_Compat {
14
- protected static $instance; // For singleton
15
- protected static $strings; // Used for cache
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  public $api;
17
 
18
  /**
@@ -37,7 +52,7 @@ class PLL_WPML_Compat {
37
  *
38
  * @since 1.7
39
  *
40
- * @return object
41
  */
42
  public static function instance() {
43
  if ( empty( self::$instance ) ) {
@@ -52,6 +67,8 @@ class PLL_WPML_Compat {
52
  * in 'setup_theme' by Polylang ( based on user info ) and 'plugins_loaded' by WPML ( based on cookie )
53
  *
54
  * @since 0.9.5
 
 
55
  */
56
  public function define_constants() {
57
  if ( ! empty( PLL()->curlang ) ) {
@@ -83,6 +100,7 @@ class PLL_WPML_Compat {
83
  * @param string $context The group in which the string is registered.
84
  * @param string $name A unique name for the string.
85
  * @param string $string The string to register.
 
86
  */
87
  public function register_string( $context, $name, $string ) {
88
  // If a string has already been registered with the same name and context, let's replace it.
@@ -118,6 +136,7 @@ class PLL_WPML_Compat {
118
  *
119
  * @param string $context The group in which the string is registered.
120
  * @param string $name A unique name for the string.
 
121
  */
122
  public function unregister_string( $context, $name ) {
123
  $key = md5( "$context | $name" );
11
  * @since 1.0.2
12
  */
13
  class PLL_WPML_Compat {
14
+ /**
15
+ * Singleton instance
16
+ *
17
+ * @var PLL_WPML_Compat
18
+ */
19
+ protected static $instance;
20
+
21
+ /**
22
+ * Stores the strings registered with the WPML API.
23
+ *
24
+ * @var array
25
+ */
26
+ protected static $strings;
27
+
28
+ /**
29
+ * @var PLL_WPML_API
30
+ */
31
  public $api;
32
 
33
  /**
52
  *
53
  * @since 1.7
54
  *
55
+ * @return PLL_WPML_Compat
56
  */
57
  public static function instance() {
58
  if ( empty( self::$instance ) ) {
67
  * in 'setup_theme' by Polylang ( based on user info ) and 'plugins_loaded' by WPML ( based on cookie )
68
  *
69
  * @since 0.9.5
70
+ *
71
+ * @return void
72
  */
73
  public function define_constants() {
74
  if ( ! empty( PLL()->curlang ) ) {
100
  * @param string $context The group in which the string is registered.
101
  * @param string $name A unique name for the string.
102
  * @param string $string The string to register.
103
+ * @return void
104
  */
105
  public function register_string( $context, $name, $string ) {
106
  // If a string has already been registered with the same name and context, let's replace it.
136
  *
137
  * @param string $context The group in which the string is registered.
138
  * @param string $name A unique name for the string.
139
+ * @return void
140
  */
141
  public function unregister_string( $context, $name ) {
142
  $key = md5( "$context | $name" );
modules/wpml/wpml-config.php CHANGED
@@ -11,8 +11,19 @@
11
  * @since 1.0
12
  */
13
  class PLL_WPML_Config {
14
- protected static $instance; // For singleton
15
- protected $xmls, $options;
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  /**
18
  * Constructor
@@ -43,6 +54,8 @@ class PLL_WPML_Config {
43
  * Finds the wpml-config.xml files to parse and setup filters
44
  *
45
  * @since 1.0
 
 
46
  */
47
  public function init() {
48
  $this->xmls = array();
@@ -80,19 +93,24 @@ class PLL_WPML_Config {
80
  add_filter( 'pll_get_taxonomies', array( $this, 'translate_taxonomies' ), 10, 2 );
81
 
82
  foreach ( $this->xmls as $context => $xml ) {
83
- foreach ( $xml->xpath( 'admin-texts/key' ) as $key ) {
84
- $attributes = $key->attributes();
85
- $name = (string) $attributes['name'];
 
 
86
 
87
- if ( false !== strpos( $name, '*' ) ) {
88
- $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
89
- $names = preg_grep( $pattern, array_keys( wp_load_alloptions() ) );
90
 
91
- foreach ( $names as $_name ) {
92
- $this->register_or_translate_option( $context, $_name, $key );
 
 
 
 
 
93
  }
94
- } else {
95
- $this->register_or_translate_option( $context, $name, $key );
96
  }
97
  }
98
  }
@@ -100,22 +118,25 @@ class PLL_WPML_Config {
100
  }
101
 
102
  /**
103
- * Adds custom fields to the list of metas to copy when creating a new translation
104
  *
105
  * @since 1.0
106
  *
107
- * @param array $metas the list of custom fields to copy or synchronize
108
- * @param bool $sync true for sync, false for copy
109
- * @return array the list of custom fields to copy or synchronize
110
  */
111
  public function copy_post_metas( $metas, $sync ) {
112
  foreach ( $this->xmls as $xml ) {
113
- foreach ( $xml->xpath( 'custom-fields/custom-field' ) as $cf ) {
114
- $attributes = $cf->attributes();
115
- if ( 'copy' == $attributes['action'] || ( ! $sync && in_array( $attributes['action'], array( 'translate', 'copy-once' ) ) ) ) {
116
- $metas[] = (string) $cf;
117
- } else {
118
- $metas = array_diff( $metas, array( (string) $cf ) );
 
 
 
119
  }
120
  }
121
  }
@@ -123,22 +144,25 @@ class PLL_WPML_Config {
123
  }
124
 
125
  /**
126
- * Adds term metas to the list of metas to copy when creating a new translation
127
  *
128
  * @since 2.6
129
  *
130
- * @param array $metas The list of term metas to copy or synchronize.
131
- * @param bool $sync True for sync, false for copy.
132
- * @return array The list of term metas to copy or synchronize.
133
  */
134
  public function copy_term_metas( $metas, $sync ) {
135
  foreach ( $this->xmls as $xml ) {
136
- foreach ( $xml->xpath( 'custom-term-fields/custom-term-field' ) as $cf ) {
137
- $attributes = $cf->attributes();
138
- if ( 'copy' == $attributes['action'] || ( ! $sync && in_array( $attributes['action'], array( 'translate', 'copy-once' ) ) ) ) {
139
- $metas[] = (string) $cf;
140
- } else {
141
- $metas = array_diff( $metas, array( (string) $cf ) );
 
 
 
142
  }
143
  }
144
  }
@@ -146,22 +170,25 @@ class PLL_WPML_Config {
146
  }
147
 
148
  /**
149
- * Language and translation management for custom post types
150
  *
151
  * @since 1.0
152
  *
153
- * @param array $types list of post type names for which Polylang manages language and translations
154
- * @param bool $hide true when displaying the list in Polylang settings
155
- * @return array list of post type names for which Polylang manages language and translations
156
  */
157
  public function translate_types( $types, $hide ) {
158
  foreach ( $this->xmls as $xml ) {
159
- foreach ( $xml->xpath( 'custom-types/custom-type' ) as $pt ) {
160
- $attributes = $pt->attributes();
161
- if ( 1 == $attributes['translate'] && ! $hide ) {
162
- $types[ (string) $pt ] = (string) $pt;
163
- } else {
164
- unset( $types[ (string) $pt ] ); // The theme/plugin author decided what to do with the post type so don't allow the user to change this
 
 
 
165
  }
166
  }
167
  }
@@ -169,22 +196,25 @@ class PLL_WPML_Config {
169
  }
170
 
171
  /**
172
- * Language and translation management for custom taxonomies
173
  *
174
  * @since 1.0
175
  *
176
- * @param array $taxonomies list of taxonomy names for which Polylang manages language and translations
177
- * @param bool $hide true when displaying the list in Polylang settings
178
- * @return array list of taxonomy names for which Polylang manages language and translations
179
  */
180
  public function translate_taxonomies( $taxonomies, $hide ) {
181
  foreach ( $this->xmls as $xml ) {
182
- foreach ( $xml->xpath( 'taxonomies/taxonomy' ) as $tax ) {
183
- $attributes = $tax->attributes();
184
- if ( 1 == $attributes['translate'] && ! $hide ) {
185
- $taxonomies[ (string) $tax ] = (string) $tax;
186
- } else {
187
- unset( $taxonomies[ (string) $tax ] ); // the theme/plugin author decided what to do with the taxonomy so don't allow the user to change this
 
 
 
188
  }
189
  }
190
  }
@@ -199,6 +229,7 @@ class PLL_WPML_Config {
199
  * @param string $context The group in which the strings will be registered.
200
  * @param string $name Option name.
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 );
11
  * @since 1.0
12
  */
13
  class PLL_WPML_Config {
14
+ /**
15
+ * Singleton instance
16
+ *
17
+ * @var PLL_WPML_Config
18
+ */
19
+ protected static $instance;
20
+
21
+ /**
22
+ * The content of all read xml files.
23
+ *
24
+ * @var SimpleXMLElement[]
25
+ */
26
+ protected $xmls;
27
 
28
  /**
29
  * Constructor
54
  * Finds the wpml-config.xml files to parse and setup filters
55
  *
56
  * @since 1.0
57
+ *
58
+ * @return void
59
  */
60
  public function init() {
61
  $this->xmls = array();
93
  add_filter( 'pll_get_taxonomies', array( $this, 'translate_taxonomies' ), 10, 2 );
94
 
95
  foreach ( $this->xmls as $context => $xml ) {
96
+ $keys = $xml->xpath( 'admin-texts/key' );
97
+ if ( is_array( $keys ) ) {
98
+ foreach ( $keys as $key ) {
99
+ $attributes = $key->attributes();
100
+ $name = (string) $attributes['name'];
101
 
102
+ if ( false !== strpos( $name, '*' ) ) {
103
+ $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
104
+ $names = preg_grep( $pattern, array_keys( wp_load_alloptions() ) );
105
 
106
+ if ( is_array( $names ) ) {
107
+ foreach ( $names as $_name ) {
108
+ $this->register_or_translate_option( $context, $_name, $key );
109
+ }
110
+ }
111
+ } else {
112
+ $this->register_or_translate_option( $context, $name, $key );
113
  }
 
 
114
  }
115
  }
116
  }
118
  }
119
 
120
  /**
121
+ * Adds custom fields to the list of metas to copy when creating a new translation.
122
  *
123
  * @since 1.0
124
  *
125
+ * @param string[] $metas The list of custom fields to copy or synchronize.
126
+ * @param bool $sync True for sync, false for copy.
127
+ * @return string[] The list of custom fields to copy or synchronize.
128
  */
129
  public function copy_post_metas( $metas, $sync ) {
130
  foreach ( $this->xmls as $xml ) {
131
+ $cfs = $xml->xpath( 'custom-fields/custom-field' );
132
+ if ( is_array( $cfs ) ) {
133
+ foreach ( $cfs as $cf ) {
134
+ $attributes = $cf->attributes();
135
+ if ( 'copy' == $attributes['action'] || ( ! $sync && in_array( $attributes['action'], array( 'translate', 'copy-once' ) ) ) ) {
136
+ $metas[] = (string) $cf;
137
+ } else {
138
+ $metas = array_diff( $metas, array( (string) $cf ) );
139
+ }
140
  }
141
  }
142
  }
144
  }
145
 
146
  /**
147
+ * Adds term metas to the list of metas to copy when creating a new translation.
148
  *
149
  * @since 2.6
150
  *
151
+ * @param string[] $metas The list of term metas to copy or synchronize.
152
+ * @param bool $sync True for sync, false for copy.
153
+ * @return string[] The list of term metas to copy or synchronize.
154
  */
155
  public function copy_term_metas( $metas, $sync ) {
156
  foreach ( $this->xmls as $xml ) {
157
+ $cfs = $xml->xpath( 'custom-term-fields/custom-term-field' );
158
+ if ( is_array( $cfs ) ) {
159
+ foreach ( $cfs as $cf ) {
160
+ $attributes = $cf->attributes();
161
+ if ( 'copy' == $attributes['action'] || ( ! $sync && in_array( $attributes['action'], array( 'translate', 'copy-once' ) ) ) ) {
162
+ $metas[] = (string) $cf;
163
+ } else {
164
+ $metas = array_diff( $metas, array( (string) $cf ) );
165
+ }
166
  }
167
  }
168
  }
170
  }
171
 
172
  /**
173
+ * Language and translation management for custom post types.
174
  *
175
  * @since 1.0
176
  *
177
+ * @param string[] $types The list of post type names for which Polylang manages language and translations.
178
+ * @param bool $hide True when displaying the list in Polylang settings.
179
+ * @return string[] The list of post type names for which Polylang manages language and translations.
180
  */
181
  public function translate_types( $types, $hide ) {
182
  foreach ( $this->xmls as $xml ) {
183
+ $pts = $xml->xpath( 'custom-types/custom-type' );
184
+ if ( is_array( $pts ) ) {
185
+ foreach ( $pts as $pt ) {
186
+ $attributes = $pt->attributes();
187
+ if ( 1 == $attributes['translate'] && ! $hide ) {
188
+ $types[ (string) $pt ] = (string) $pt;
189
+ } else {
190
+ unset( $types[ (string) $pt ] ); // The theme/plugin author decided what to do with the post type so don't allow the user to change this
191
+ }
192
  }
193
  }
194
  }
196
  }
197
 
198
  /**
199
+ * Language and translation management for custom taxonomies.
200
  *
201
  * @since 1.0
202
  *
203
+ * @param string[] $taxonomies The list of taxonomy names for which Polylang manages language and translations.
204
+ * @param bool $hide True when displaying the list in Polylang settings.
205
+ * @return string[] The list of taxonomy names for which Polylang manages language and translations.
206
  */
207
  public function translate_taxonomies( $taxonomies, $hide ) {
208
  foreach ( $this->xmls as $xml ) {
209
+ $taxos = $xml->xpath( 'taxonomies/taxonomy' );
210
+ if ( is_array( $taxos ) ) {
211
+ foreach ( $taxos as $tax ) {
212
+ $attributes = $tax->attributes();
213
+ if ( 1 == $attributes['translate'] && ! $hide ) {
214
+ $taxonomies[ (string) $tax ] = (string) $tax;
215
+ } else {
216
+ unset( $taxonomies[ (string) $tax ] ); // the theme/plugin author decided what to do with the taxonomy so don't allow the user to change this
217
+ }
218
  }
219
  }
220
  }
229
  * @param string $context The group in which the strings will be registered.
230
  * @param string $name Option name.
231
  * @param object $key XML node.
232
+ * @return void
233
  */
234
  protected function register_or_translate_option( $context, $name, $key ) {
235
  $option_keys = $this->xml_to_array( $key );
modules/wpml/wpml-legacy-api.php CHANGED
@@ -128,7 +128,7 @@ if ( ! function_exists( 'icl_link_to_element' ) ) {
128
  }
129
  } elseif ( taxonomy_exists( $type ) ) {
130
  $link = get_term_link( $id, $type );
131
- if ( empty( $text ) && ( $term = get_term( $id, $type ) ) && ! empty( $term ) && ! is_wp_error( $term ) ) {
132
  $text = $term->name;
133
  }
134
  }
@@ -167,7 +167,7 @@ if ( ! function_exists( 'icl_object_id' ) ) {
167
  * @param string $lang Optional, language code, defaults to current language
168
  * @return int|null The object id of the translation, null if the translation is missing and $return_original_if_missing set to false
169
  */
170
- function icl_object_id( $id, $type = 'post', $return_original_if_missing = false, $lang = false ) {
171
  $lang = $lang ? $lang : pll_current_language();
172
 
173
  if ( 'nav_menu' === $type ) {
@@ -246,6 +246,7 @@ if ( ! function_exists( 'icl_register_string' ) ) {
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 );
@@ -260,6 +261,7 @@ if ( ! function_exists( 'icl_unregister_string' ) ) {
260
  *
261
  * @param string $context the group in which the string is registered, defaults to 'polylang'
262
  * @param string $name a unique name for the string
 
263
  */
264
  function icl_unregister_string( $context, $name ) {
265
  PLL_WPML_Compat::instance()->unregister_string( $context, $name );
@@ -282,7 +284,7 @@ if ( ! function_exists( 'icl_t' ) ) {
282
  * @param string|null $lang optional, return the translation in this language, defaults to current language
283
  * @return string the translated string
284
  */
285
- function icl_t( $context, $name, $string = false, &$has_translation = null, $bool = false, $lang = null ) {
286
  return icl_translate( $context, $name, $string, false, $has_translation, $lang );
287
  }
288
  }
@@ -303,7 +305,7 @@ if ( ! function_exists( 'icl_translate' ) ) {
303
  * @param string|null $lang optional, return the translation in this language, defaults to current language
304
  * @return string the translated string
305
  */
306
- function icl_translate( $context, $name, $string = false, $bool = false, &$has_translation = null, $lang = null ) {
307
  // FIXME WPML can automatically registers the string based on an option
308
  if ( empty( $string ) ) {
309
  $string = PLL_WPML_Compat::instance()->get_string_by_context_and_name( $context, $name );
128
  }
129
  } elseif ( taxonomy_exists( $type ) ) {
130
  $link = get_term_link( $id, $type );
131
+ if ( empty( $text ) && ( $term = get_term( $id, $type ) ) && $term instanceof WP_Term ) {
132
  $text = $term->name;
133
  }
134
  }
167
  * @param string $lang Optional, language code, defaults to current language
168
  * @return int|null The object id of the translation, null if the translation is missing and $return_original_if_missing set to false
169
  */
170
+ function icl_object_id( $id, $type = 'post', $return_original_if_missing = false, $lang = '' ) {
171
  $lang = $lang ? $lang : pll_current_language();
172
 
173
  if ( 'nav_menu' === $type ) {
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
+ * @return void
250
  */
251
  function icl_register_string( $context, $name, $string ) {
252
  PLL_WPML_Compat::instance()->register_string( $context, $name, $string );
261
  *
262
  * @param string $context the group in which the string is registered, defaults to 'polylang'
263
  * @param string $name a unique name for the string
264
+ * @return void
265
  */
266
  function icl_unregister_string( $context, $name ) {
267
  PLL_WPML_Compat::instance()->unregister_string( $context, $name );
284
  * @param string|null $lang optional, return the translation in this language, defaults to current language
285
  * @return string the translated string
286
  */
287
+ function icl_t( $context, $name, $string = '', &$has_translation = null, $bool = false, $lang = null ) {
288
  return icl_translate( $context, $name, $string, false, $has_translation, $lang );
289
  }
290
  }
305
  * @param string|null $lang optional, return the translation in this language, defaults to current language
306
  * @return string the translated string
307
  */
308
+ function icl_translate( $context, $name, $string = '', $bool = false, &$has_translation = null, $lang = null ) {
309
  // FIXME WPML can automatically registers the string based on an option
310
  if ( empty( $string ) ) {
311
  $string = PLL_WPML_Compat::instance()->get_string_by_context_and_name( $context, $name );
polylang.php CHANGED
@@ -10,7 +10,7 @@
10
  * Plugin Name: Polylang
11
  * Plugin URI: https://polylang.pro
12
  * Description: Adds multilingual capability to WordPress
13
- * Version: 2.9.2
14
  * Requires at least: 5.1
15
  * Requires PHP: 5.6
16
  * Author: WP SYNTEX
@@ -53,7 +53,7 @@ if ( defined( 'POLYLANG_VERSION' ) ) {
53
  }
54
  } else {
55
  // Go on loading the plugin
56
- define( 'POLYLANG_VERSION', '2.9.2' );
57
  define( 'PLL_MIN_WP_VERSION', '5.1' );
58
  define( 'PLL_MIN_PHP_VERSION', '5.6' );
59
 
10
  * Plugin Name: Polylang
11
  * Plugin URI: https://polylang.pro
12
  * Description: Adds multilingual capability to WordPress
13
+ * Version: 3.0
14
  * Requires at least: 5.1
15
  * Requires PHP: 5.6
16
  * Author: WP SYNTEX
53
  }
54
  } else {
55
  // Go on loading the plugin
56
+ define( 'POLYLANG_VERSION', '3.0' );
57
  define( 'PLL_MIN_WP_VERSION', '5.1' );
58
  define( 'PLL_MIN_PHP_VERSION', '5.6' );
59
 
readme.txt CHANGED
@@ -3,9 +3,9 @@ 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.2
9
  License: GPLv3 or later
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
11
 
@@ -78,6 +78,34 @@ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-
78
 
79
  == Changelog ==
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  = 2.9.2 (2021-02-02) =
82
 
83
  * Pro: Fix translation of CPTUI plural label and description not working
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.7
7
  Requires PHP: 5.6
8
+ Stable tag: 3.0
9
  License: GPLv3 or later
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
11
 
78
 
79
  == Changelog ==
80
 
81
+ = 3.0 (2021-03-08) =
82
+
83
+ * Add compatibility with WordPress 5.7
84
+ * Remove upgrades from Polylang older than 1.8
85
+ * Remove deprecated class PLL_Pointer
86
+ * Pro: Hide the license keys
87
+ * Pro: Fix redirect to the home page of a deactivated language
88
+ * Pro: Fix synchronization of post status not working
89
+ * Pro: Fix language switcher block not working in a post retrieved in REST API
90
+ * Pro: Fix PO export of strings with line breaks
91
+ * Pro: Fix file block title customization lost
92
+ * Add a dialog box to ask a confirmation about a language change in classic and block editors
93
+ * Improve browser language detection #591
94
+ * Improve robustness and documentation of code
95
+ * Fix media library after the language has been chnaged in the editor metabox
96
+ * Fix duplicated title attribute on flag link in posts list
97
+ * Fix legacy block editor language metabox compatibility with WordPress 5.6
98
+ * Fix uploaded theme and plugin files in media library
99
+ * Fix site title not translated in email change confirmation email
100
+ * Fix remaining deprecated jQuery notices #741
101
+ * Fix compatibility with GN publisher
102
+ * Fix compatibility with Woodmart theme search form
103
+ * Fix compatibility issue with 3rd party ajax requests since jQuery 3.3 #744
104
+ * Fix CSS conflict with WooCommerce Bookings
105
+ * Fix browser error when displaying an embed and using a cache plugin #757
106
+ * Fix post type archive title and metadesc not translated in Yoast SEO
107
+ * Fix PHP notice in REST API
108
+
109
  = 2.9.2 (2021-02-02) =
110
 
111
  * Pro: Fix translation of CPTUI plural label and description not working
settings/settings-browser.php CHANGED
@@ -67,6 +67,8 @@ class PLL_Settings_Browser extends PLL_Settings_Module {
67
  * as the preferred browser language is not used when the language is set from different domains
68
  *
69
  * @since 1.8
 
 
70
  */
71
  public function print_js() {
72
  wp_enqueue_script( 'jquery' );
67
  * as the preferred browser language is not used when the language is set from different domains
68
  *
69
  * @since 1.8
70
+ *
71
+ * @return void
72
  */
73
  public function print_js() {
74
  wp_enqueue_script( 'jquery' );
settings/settings-cpt.php CHANGED
@@ -19,28 +19,28 @@ class PLL_Settings_CPT extends PLL_Settings_Module {
19
  /**
20
  * The list of post types to show in the form.
21
  *
22
- * @var array
23
  */
24
  private $post_types;
25
 
26
  /**
27
  * The list of post types to disable in the form.
28
  *
29
- * @var array
30
  */
31
  private $disabled_post_types;
32
 
33
  /**
34
  * The list of taxonomies to show in the form.
35
  *
36
- * @var array
37
  */
38
  private $taxonomies;
39
 
40
  /**
41
  * The list of taxonomies to disable in the form.
42
  *
43
- * @var array
44
  */
45
  private $disabled_taxonomies;
46
 
19
  /**
20
  * The list of post types to show in the form.
21
  *
22
+ * @var string[]
23
  */
24
  private $post_types;
25
 
26
  /**
27
  * The list of post types to disable in the form.
28
  *
29
+ * @var string[]
30
  */
31
  private $disabled_post_types;
32
 
33
  /**
34
  * The list of taxonomies to show in the form.
35
  *
36
+ * @var string[]
37
  */
38
  private $taxonomies;
39
 
40
  /**
41
  * The list of taxonomies to disable in the form.
42
  *
43
+ * @var string[]
44
  */
45
  private $disabled_taxonomies;
46
 
settings/settings-licenses.php CHANGED
@@ -17,9 +17,9 @@ class PLL_Settings_Licenses extends PLL_Settings_Module {
17
  public $priority = 100;
18
 
19
  /**
20
- * Stores an array of PLL_License instances.
21
  *
22
- * @var array
23
  */
24
  protected $items;
25
 
@@ -77,11 +77,11 @@ class PLL_Settings_Licenses extends PLL_Settings_Module {
77
  }
78
 
79
  /**
80
- * Get the html for a row (one per license key) for display
81
  *
82
  * @since 1.9
83
  *
84
- * @param array $item licence id, name and key
85
  * @return string
86
  */
87
  protected function get_row( $item ) {
@@ -122,6 +122,8 @@ class PLL_Settings_Licenses extends PLL_Settings_Module {
122
  * Ajax method to deactivate a license
123
  *
124
  * @since 1.9
 
 
125
  */
126
  public function deactivate_license() {
127
  check_ajax_referer( 'pll_options', '_pll_nonce' );
17
  public $priority = 100;
18
 
19
  /**
20
+ * Stores an array of objects allowing to manage a license.
21
  *
22
+ * @var PLL_License[]
23
  */
24
  protected $items;
25
 
77
  }
78
 
79
  /**
80
+ * Get the html for a row (one per license key) for display.
81
  *
82
  * @since 1.9
83
  *
84
+ * @param PLL_License $item Object allowing to manage a license.
85
  * @return string
86
  */
87
  protected function get_row( $item ) {
122
  * Ajax method to deactivate a license
123
  *
124
  * @since 1.9
125
+ *
126
+ * @return void
127
  */
128
  public function deactivate_license() {
129
  check_ajax_referer( 'pll_options', '_pll_nonce' );
settings/settings-module.php CHANGED
@@ -17,8 +17,6 @@ class PLL_Settings_Module {
17
  public $options;
18
 
19
  /**
20
- * Instance of PLL_Model.
21
- *
22
  * @var PLL_Model
23
  */
24
  public $model;
@@ -161,6 +159,8 @@ class PLL_Settings_Module {
161
  * Activates the module
162
  *
163
  * @since 1.8
 
 
164
  */
165
  public function activate() {
166
  if ( ! empty( $this->active_option ) ) {
@@ -173,6 +173,8 @@ class PLL_Settings_Module {
173
  * Deactivates the module
174
  *
175
  * @since 1.8
 
 
176
  */
177
  public function deactivate() {
178
  if ( ! empty( $this->active_option ) ) {
@@ -185,6 +187,8 @@ class PLL_Settings_Module {
185
  * Protected method to display a configuration form
186
  *
187
  * @since 1.8
 
 
188
  */
189
  protected function form() {
190
  // Child classes can provide a form
@@ -224,6 +228,8 @@ class PLL_Settings_Module {
224
  * Ajax method to save the options
225
  *
226
  * @since 1.8
 
 
227
  */
228
  public function save_options() {
229
  check_ajax_referer( 'pll_options', '_pll_nonce' );
@@ -263,11 +269,11 @@ class PLL_Settings_Module {
263
  }
264
 
265
  /**
266
- * Get the row actions
267
  *
268
  * @since 1.8
269
  *
270
- * @return array
271
  */
272
  protected function get_actions() {
273
  $actions = array();
@@ -288,11 +294,11 @@ class PLL_Settings_Module {
288
  }
289
 
290
  /**
291
- * Get the actions links
292
  *
293
  * @since 1.8
294
  *
295
- * @return array
296
  */
297
  public function get_action_links() {
298
  return array_intersect_key( $this->action_links, array_flip( $this->get_actions() ) );
@@ -326,11 +332,11 @@ class PLL_Settings_Module {
326
  }
327
 
328
  /**
329
- * Get the buttons
330
  *
331
  * @since 1.9
332
  *
333
- * @return array
334
  */
335
  public function get_buttons() {
336
  return $this->buttons;
17
  public $options;
18
 
19
  /**
 
 
20
  * @var PLL_Model
21
  */
22
  public $model;
159
  * Activates the module
160
  *
161
  * @since 1.8
162
+ *
163
+ * @return void
164
  */
165
  public function activate() {
166
  if ( ! empty( $this->active_option ) ) {
173
  * Deactivates the module
174
  *
175
  * @since 1.8
176
+ *
177
+ * @return void
178
  */
179
  public function deactivate() {
180
  if ( ! empty( $this->active_option ) ) {
187
  * Protected method to display a configuration form
188
  *
189
  * @since 1.8
190
+ *
191
+ * @return void
192
  */
193
  protected function form() {
194
  // Child classes can provide a form
228
  * Ajax method to save the options
229
  *
230
  * @since 1.8
231
+ *
232
+ * @return void
233
  */
234
  public function save_options() {
235
  check_ajax_referer( 'pll_options', '_pll_nonce' );
269
  }
270
 
271
  /**
272
+ * Get the row actions.
273
  *
274
  * @since 1.8
275
  *
276
+ * @return string[]
277
  */
278
  protected function get_actions() {
279
  $actions = array();
294
  }
295
 
296
  /**
297
+ * Get the actions links.
298
  *
299
  * @since 1.8
300
  *
301
+ * @return string[] Action links.
302
  */
303
  public function get_action_links() {
304
  return array_intersect_key( $this->action_links, array_flip( $this->get_actions() ) );
332
  }
333
 
334
  /**
335
+ * Get the buttons.
336
  *
337
  * @since 1.9
338
  *
339
+ * @return string[] An array of html fragment for the buttons.
340
  */
341
  public function get_buttons() {
342
  return $this->buttons;
settings/settings-url.php CHANGED
@@ -16,6 +16,13 @@ class PLL_Settings_Url extends PLL_Settings_Module {
16
  */
17
  public $priority = 10;
18
 
 
 
 
 
 
 
 
19
  /**
20
  * Constructor
21
  *
@@ -34,7 +41,6 @@ class PLL_Settings_Url extends PLL_Settings_Module {
34
  )
35
  );
36
 
37
- $this->links_model = &$polylang->links_model;
38
  $this->page_on_front = &$polylang->static_pages->page_on_front;
39
  }
40
 
@@ -42,6 +48,8 @@ class PLL_Settings_Url extends PLL_Settings_Module {
42
  * Displays the fieldset to choose how the language is set
43
  *
44
  * @since 1.8
 
 
45
  */
46
  protected function force_lang() {
47
  ?>
@@ -107,6 +115,8 @@ class PLL_Settings_Url extends PLL_Settings_Module {
107
  * Displays the fieldset to choose to hide the default language information in url
108
  *
109
  * @since 1.8
 
 
110
  */
111
  protected function hide_default() {
112
  ?>
@@ -126,6 +136,8 @@ class PLL_Settings_Url extends PLL_Settings_Module {
126
  * Displays the fieldset to choose to hide /language/ in url
127
  *
128
  * @since 1.8
 
 
129
  */
130
  protected function rewrite() {
131
  ?>
@@ -158,6 +170,8 @@ class PLL_Settings_Url extends PLL_Settings_Module {
158
  * Displays the fieldset to choose to redirect the home page to language page
159
  *
160
  * @since 1.8
 
 
161
  */
162
  protected function redirect_lang() {
163
  ?>
@@ -190,6 +204,8 @@ class PLL_Settings_Url extends PLL_Settings_Module {
190
  * Displays the settings
191
  *
192
  * @since 1.8
 
 
193
  */
194
  public function form() {
195
  ?>
@@ -230,6 +246,7 @@ class PLL_Settings_Url extends PLL_Settings_Module {
230
  * @since 1.8
231
  *
232
  * @param array $options
 
233
  */
234
  protected function update( $options ) {
235
  $newoptions = array();
@@ -285,6 +302,7 @@ class PLL_Settings_Url extends PLL_Settings_Module {
285
  * @since 1.8
286
  *
287
  * @param array $options new set of options to test
 
288
  */
289
  protected function check_domains( $options ) {
290
  $options = array_merge( $this->options, $options );
16
  */
17
  public $priority = 10;
18
 
19
+ /**
20
+ * The page id of the static front page.
21
+ *
22
+ * @var int
23
+ */
24
+ protected $page_on_front;
25
+
26
  /**
27
  * Constructor
28
  *
41
  )
42
  );
43
 
 
44
  $this->page_on_front = &$polylang->static_pages->page_on_front;
45
  }
46
 
48
  * Displays the fieldset to choose how the language is set
49
  *
50
  * @since 1.8
51
+ *
52
+ * @return void
53
  */
54
  protected function force_lang() {
55
  ?>
115
  * Displays the fieldset to choose to hide the default language information in url
116
  *
117
  * @since 1.8
118
+ *
119
+ * @return void
120
  */
121
  protected function hide_default() {
122
  ?>
136
  * Displays the fieldset to choose to hide /language/ in url
137
  *
138
  * @since 1.8
139
+ *
140
+ * @return void
141
  */
142
  protected function rewrite() {
143
  ?>
170
  * Displays the fieldset to choose to redirect the home page to language page
171
  *
172
  * @since 1.8
173
+ *
174
+ * @return void
175
  */
176
  protected function redirect_lang() {
177
  ?>
204
  * Displays the settings
205
  *
206
  * @since 1.8
207
+ *
208
+ * @return void
209
  */
210
  public function form() {
211
  ?>
246
  * @since 1.8
247
  *
248
  * @param array $options
249
+ * @return array
250
  */
251
  protected function update( $options ) {
252
  $newoptions = array();
302
  * @since 1.8
303
  *
304
  * @param array $options new set of options to test
305
+ * @return void
306
  */
307
  protected function check_domains( $options ) {
308
  $options = array_merge( $this->options, $options );
settings/settings.php CHANGED
@@ -4,34 +4,28 @@
4
  */
5
 
6
  /**
7
- * A class for the Polylang settings pages
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 admin content
18
- * pref_lang => inherited, preferred language used as default when saving posts or terms
19
  *
20
  * @since 1.2
21
  */
22
  class PLL_Settings extends PLL_Admin_Base {
23
 
24
  /**
25
- * Name of the active module
 
 
 
 
 
26
  *
27
- * @var string $active_tab
28
  */
29
  protected $active_tab;
30
 
31
  /**
32
- * Array of modules classes
33
  *
34
- * @var array $modules
35
  */
36
  protected $modules;
37
 
@@ -40,7 +34,7 @@ class PLL_Settings extends PLL_Admin_Base {
40
  *
41
  * @since 1.2
42
  *
43
- * @param object $links_model
44
  */
45
  public function __construct( &$links_model ) {
46
  parent::__construct( $links_model );
@@ -65,6 +59,8 @@ class PLL_Settings extends PLL_Admin_Base {
65
  * Initializes the modules
66
  *
67
  * @since 1.8
 
 
68
  */
69
  public function register_settings_modules() {
70
  $modules = array();
@@ -99,6 +95,8 @@ class PLL_Settings extends PLL_Admin_Base {
99
  * Loads the about metabox
100
  *
101
  * @since 0.8
 
 
102
  */
103
  public function metabox_about() {
104
  include __DIR__ . '/view-about.php';
@@ -108,6 +106,8 @@ class PLL_Settings extends PLL_Admin_Base {
108
  * Adds screen options and the about box in the languages admin panel
109
  *
110
  * @since 0.9.5
 
 
111
  */
112
  public function load_page() {
113
  if ( ! defined( 'PLL_DISPLAY_ABOUT' ) || PLL_DISPLAY_ABOUT ) {
@@ -136,6 +136,8 @@ class PLL_Settings extends PLL_Admin_Base {
136
  * Adds screen options in the strings translations admin panel
137
  *
138
  * @since 2.1
 
 
139
  */
140
  public function load_page_strings() {
141
  add_screen_option(
@@ -169,6 +171,7 @@ class PLL_Settings extends PLL_Admin_Base {
169
  * @since 1.9
170
  *
171
  * @param string $action
 
172
  */
173
  public function handle_actions( $action ) {
174
  switch ( $action ) {
@@ -287,6 +290,8 @@ class PLL_Settings extends PLL_Admin_Base {
287
  * Also manages user input for these pages
288
  *
289
  * @since 0.1
 
 
290
  */
291
  public function languages_page() {
292
  switch ( $this->active_tab ) {
@@ -317,23 +322,26 @@ class PLL_Settings extends PLL_Admin_Base {
317
 
318
  /**
319
  * Enqueues scripts and styles
 
 
320
  */
321
  public function admin_enqueue_scripts() {
322
  parent::admin_enqueue_scripts();
323
 
324
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
325
 
326
- wp_enqueue_script( 'pll_admin', plugins_url( '/js/admin' . $suffix . '.js', POLYLANG_FILE ), array( 'jquery', 'wp-ajax-response', 'postbox', 'jquery-ui-selectmenu' ), POLYLANG_VERSION );
327
- wp_localize_script( 'pll_admin', 'pll_flag_base_url', plugins_url( '/flags/', POLYLANG_FILE ) );
328
- wp_localize_script( 'pll_admin', 'pll_dismiss_notice', esc_html__( 'Dismiss this notice.', 'polylang' ) );
329
 
330
- wp_enqueue_style( 'pll_selectmenu', plugins_url( '/css/selectmenu' . $suffix . '.css', POLYLANG_FILE ), array(), POLYLANG_VERSION );
331
  }
332
 
333
  /**
334
  * Displays a notice when there are objects with no language assigned
335
  *
336
  * @since 1.8
 
 
337
  */
338
  public function notice_objects_with_no_lang() {
339
  if ( ! empty( $this->options['default_lang'] ) && $this->model->get_objects_with_no_lang( 1 ) ) {
@@ -353,6 +361,7 @@ class PLL_Settings extends PLL_Admin_Base {
353
  * @since 1.5
354
  *
355
  * @param array $args query arguments to add to the url
 
356
  */
357
  public static function redirect( $args = array() ) {
358
  if ( $errors = get_settings_errors() ) {
@@ -368,6 +377,16 @@ class PLL_Settings extends PLL_Admin_Base {
368
  * Get the list of predefined languages
369
  *
370
  * @since 2.3
 
 
 
 
 
 
 
 
 
 
371
  */
372
  public static function get_predefined_languages() {
373
  require_once ABSPATH . 'wp-admin/includes/translation-install.php';
4
  */
5
 
6
  /**
7
+ * A class for the Polylang settings pages, accessible from @see PLL().
 
 
 
 
 
 
 
 
 
 
 
8
  *
9
  * @since 1.2
10
  */
11
  class PLL_Settings extends PLL_Admin_Base {
12
 
13
  /**
14
+ * @var PLL_Admin_Model
15
+ */
16
+ public $model;
17
+
18
+ /**
19
+ * Name of the active module.
20
  *
21
+ * @var string
22
  */
23
  protected $active_tab;
24
 
25
  /**
26
+ * Array of modules classes.
27
  *
28
+ * @var PLL_Settings_Module[]
29
  */
30
  protected $modules;
31
 
34
  *
35
  * @since 1.2
36
  *
37
+ * @param PLL_Links_Model $links_model Reference to the links model.
38
  */
39
  public function __construct( &$links_model ) {
40
  parent::__construct( $links_model );
59
  * Initializes the modules
60
  *
61
  * @since 1.8
62
+ *
63
+ * @return void
64
  */
65
  public function register_settings_modules() {
66
  $modules = array();
95
  * Loads the about metabox
96
  *
97
  * @since 0.8
98
+ *
99
+ * @return void
100
  */
101
  public function metabox_about() {
102
  include __DIR__ . '/view-about.php';
106
  * Adds screen options and the about box in the languages admin panel
107
  *
108
  * @since 0.9.5
109
+ *
110
+ * @return void
111
  */
112
  public function load_page() {
113
  if ( ! defined( 'PLL_DISPLAY_ABOUT' ) || PLL_DISPLAY_ABOUT ) {
136
  * Adds screen options in the strings translations admin panel
137
  *
138
  * @since 2.1
139
+ *
140
+ * @return void
141
  */
142
  public function load_page_strings() {
143
  add_screen_option(
171
  * @since 1.9
172
  *
173
  * @param string $action
174
+ * @return void
175
  */
176
  public function handle_actions( $action ) {
177
  switch ( $action ) {
290
  * Also manages user input for these pages
291
  *
292
  * @since 0.1
293
+ *
294
+ * @return void
295
  */
296
  public function languages_page() {
297
  switch ( $this->active_tab ) {
322
 
323
  /**
324
  * Enqueues scripts and styles
325
+ *
326
+ * @return void
327
  */
328
  public function admin_enqueue_scripts() {
329
  parent::admin_enqueue_scripts();
330
 
331
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
332
 
333
+ wp_enqueue_script( 'pll_admin', plugins_url( '/js/build/admin' . $suffix . '.js', POLYLANG_BASENAME ), array( 'jquery', 'wp-ajax-response', 'postbox', 'jquery-ui-selectmenu' ), POLYLANG_VERSION, true );
334
+ wp_localize_script( 'pll_admin', 'pll_admin', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
 
335
 
336
+ wp_enqueue_style( 'pll_selectmenu', plugins_url( '/css/build/selectmenu' . $suffix . '.css', POLYLANG_BASENAME ), array(), POLYLANG_VERSION );
337
  }
338
 
339
  /**
340
  * Displays a notice when there are objects with no language assigned
341
  *
342
  * @since 1.8
343
+ *
344
+ * @return void
345
  */
346
  public function notice_objects_with_no_lang() {
347
  if ( ! empty( $this->options['default_lang'] ) && $this->model->get_objects_with_no_lang( 1 ) ) {
361
  * @since 1.5
362
  *
363
  * @param array $args query arguments to add to the url
364
+ * @return void
365
  */
366
  public static function redirect( $args = array() ) {
367
  if ( $errors = get_settings_errors() ) {
377
  * Get the list of predefined languages
378
  *
379
  * @since 2.3
380
+ *
381
+ * @return string[] {
382
+ * @type string $code ISO 639-1 language code.
383
+ * @type string $locale WordPress locale.
384
+ * @type string $name Native language name.
385
+ * @type string $dir Text direction: 'ltr' or 'rtl'.
386
+ * @type string $flag Flag code, generally the country code.
387
+ * @type string $w3c W3C locale.
388
+ * @type string $facebook Facebook locale.
389
+ * }
390
  */
391
  public static function get_predefined_languages() {
392
  require_once ABSPATH . 'wp-admin/includes/translation-install.php';
settings/table-languages.php CHANGED
@@ -30,11 +30,12 @@ class PLL_Table_Languages extends WP_List_Table {
30
  }
31
 
32
  /**
33
- * Generates content for a single row of the table
34
  *
35
  * @since 1.8
36
  *
37
- * @param object $item The current item
 
38
  */
39
  public function single_row( $item ) {
40
  /**
@@ -42,8 +43,8 @@ class PLL_Table_Languages extends WP_List_Table {
42
  *
43
  * @since 1.8
44
  *
45
- * @param array $classes list of class names
46
- * @param object $item the current item
47
  */
48
  $classes = apply_filters( 'pll_languages_row_classes', array(), $item );
49
  echo '<tr' . ( empty( $classes ) ? '>' : ' class="' . esc_attr( implode( ' ', $classes ) ) . '">' );
@@ -52,13 +53,13 @@ class PLL_Table_Languages extends WP_List_Table {
52
  }
53
 
54
  /**
55
- * Displays the item information in a column ( default case )
56
  *
57
  * @since 0.1
58
  *
59
- * @param object $item
60
- * @param string $column_name
61
- * @return string
62
  */
63
  public function column_default( $item, $column_name ) {
64
  switch ( $column_name ) {
@@ -71,7 +72,7 @@ class PLL_Table_Languages extends WP_List_Table {
71
  return (int) $item->$column_name;
72
 
73
  default:
74
- return $item->$column_name; // flag
75
  }
76
  }
77
 
@@ -81,7 +82,7 @@ class PLL_Table_Languages extends WP_List_Table {
81
  *
82
  * @since 0.1
83
  *
84
- * @param object $item
85
  * @return string
86
  */
87
  public function column_name( $item ) {
@@ -99,7 +100,7 @@ class PLL_Table_Languages extends WP_List_Table {
99
  *
100
  * @since 1.8
101
  *
102
- * @param object $item
103
  * @return string
104
  */
105
  public function column_default_lang( $item ) {
@@ -117,12 +118,12 @@ class PLL_Table_Languages extends WP_List_Table {
117
  );
118
 
119
  /**
120
- * Filter the default language row action in the languages list table
121
  *
122
  * @since 1.8
123
  *
124
- * @param string $s html markup of the action
125
- * @param object $item
126
  */
127
  $s = apply_filters( 'pll_default_lang_row_action', $s, $item );
128
  } else {
@@ -141,7 +142,7 @@ class PLL_Table_Languages extends WP_List_Table {
141
  *
142
  * @since 0.1
143
  *
144
- * @return array the list of column titles
145
  */
146
  public function get_columns() {
147
  return array(
@@ -188,9 +189,9 @@ class PLL_Table_Languages extends WP_List_Table {
188
  *
189
  * @since 1.8
190
  *
191
- * @param object $item The item being acted upon.
192
- * @param string $column_name Current column name.
193
- * @param string $primary Primary column name.
194
  * @return string The row actions output.
195
  */
196
  protected function handle_row_actions( $item, $column_name, $primary ) {
@@ -215,12 +216,12 @@ class PLL_Table_Languages extends WP_List_Table {
215
  );
216
 
217
  /**
218
- * Filter the list of row actions in the languages list table
219
  *
220
  * @since 1.8
221
  *
222
- * @param array $actions list of html markup actions
223
- * @param object $item
224
  */
225
  $actions = apply_filters( 'pll_languages_row_actions', $actions, $item );
226
 
@@ -254,6 +255,7 @@ class PLL_Table_Languages extends WP_List_Table {
254
  * @since 0.1
255
  *
256
  * @param array $data
 
257
  */
258
  public function prepare_items( $data = array() ) {
259
  $per_page = $this->get_items_per_page( 'pll_lang_per_page' );
30
  }
31
 
32
  /**
33
+ * Generates content for a single row of the table.
34
  *
35
  * @since 1.8
36
  *
37
+ * @param PLL_Language $item The language item.
38
+ * @return void
39
  */
40
  public function single_row( $item ) {
41
  /**
43
  *
44
  * @since 1.8
45
  *
46
+ * @param array $classes The list of class names.
47
+ * @param PLL_Language $item The language item.
48
  */
49
  $classes = apply_filters( 'pll_languages_row_classes', array(), $item );
50
  echo '<tr' . ( empty( $classes ) ? '>' : ' class="' . esc_attr( implode( ' ', $classes ) ) . '">' );
53
  }
54
 
55
  /**
56
+ * Displays the item information in a column ( default case ).
57
  *
58
  * @since 0.1
59
  *
60
+ * @param PLL_Language $item The language item.
61
+ * @param string $column_name The column name.
62
+ * @return string|int
63
  */
64
  public function column_default( $item, $column_name ) {
65
  switch ( $column_name ) {
72
  return (int) $item->$column_name;
73
 
74
  default:
75
+ return $item->$column_name; // Flag.
76
  }
77
  }
78
 
82
  *
83
  * @since 0.1
84
  *
85
+ * @param PLL_Language $item The language item.
86
  * @return string
87
  */
88
  public function column_name( $item ) {
100
  *
101
  * @since 1.8
102
  *
103
+ * @param PLL_Language $item The language item.
104
  * @return string
105
  */
106
  public function column_default_lang( $item ) {
118
  );
119
 
120
  /**
121
+ * Filters the default language row action in the languages list table.
122
  *
123
  * @since 1.8
124
  *
125
+ * @param string $s The html markup of the action.
126
+ * @param PLL_Language $item The language item.
127
  */
128
  $s = apply_filters( 'pll_default_lang_row_action', $s, $item );
129
  } else {
142
  *
143
  * @since 0.1
144
  *
145
+ * @return string[] The list of column titles.
146
  */
147
  public function get_columns() {
148
  return array(
189
  *
190
  * @since 1.8
191
  *
192
+ * @param PLL_Language $item The language item being acted upon.
193
+ * @param string $column_name Current column name.
194
+ * @param string $primary Primary column name.
195
  * @return string The row actions output.
196
  */
197
  protected function handle_row_actions( $item, $column_name, $primary ) {
216
  );
217
 
218
  /**
219
+ * Filters the list of row actions in the languages list table.
220
  *
221
  * @since 1.8
222
  *
223
+ * @param array $actions A list of html markup actions.
224
+ * @param PLL_Language $item The language item.
225
  */
226
  $actions = apply_filters( 'pll_languages_row_actions', $actions, $item );
227
 
255
  * @since 0.1
256
  *
257
  * @param array $data
258
+ * @return void
259
  */
260
  public function prepare_items( $data = array() ) {
261
  $per_page = $this->get_items_per_page( 'pll_lang_per_page' );
settings/table-settings.php CHANGED
@@ -38,11 +38,12 @@ class PLL_Table_Settings extends WP_List_Table {
38
  }
39
 
40
  /**
41
- * Displays a single row
42
  *
43
  * @since 1.8
44
  *
45
- * @param object $item PLL_Settings_Module object
 
46
  */
47
  public function single_row( $item ) {
48
  // Classes to reuse css from the plugins list table
@@ -88,11 +89,12 @@ class PLL_Table_Settings extends WP_List_Table {
88
  }
89
 
90
  /**
91
- * Generates the columns for a single row of the table
92
  *
93
  * @since 1.8
94
  *
95
- * @param object $item The current item
 
96
  */
97
  protected function single_row_columns( $item ) {
98
  $column_info = $this->get_column_info();
@@ -118,13 +120,13 @@ class PLL_Table_Settings extends WP_List_Table {
118
  }
119
 
120
  /**
121
- * Displays the item information in a column ( default case )
122
  *
123
  * @since 1.8
124
  *
125
- * @param object $item
126
- * @param string $column_name
127
- * @return string
128
  */
129
  protected function column_default( $item, $column_name ) {
130
  if ( 'plugin-title' == $column_name ) {
@@ -134,11 +136,11 @@ class PLL_Table_Settings extends WP_List_Table {
134
  }
135
 
136
  /**
137
- * Gets the list of columns
138
  *
139
  * @since 1.8
140
  *
141
- * @return array the list of column titles
142
  */
143
  public function get_columns() {
144
  return array(
@@ -164,7 +166,8 @@ class PLL_Table_Settings extends WP_List_Table {
164
  *
165
  * @since 1.8
166
  *
167
- * @param array $items
 
168
  */
169
  public function prepare_items( $items = array() ) {
170
  $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns(), $this->get_primary_column_name() );
@@ -185,6 +188,7 @@ class PLL_Table_Settings extends WP_List_Table {
185
  * @since 2.1
186
  *
187
  * @param string $which 'top' or 'bottom'
 
188
  */
189
  protected function display_tablenav( $which ) {} // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
190
  }
38
  }
39
 
40
  /**
41
+ * Displays a single row.
42
  *
43
  * @since 1.8
44
  *
45
+ * @param PLL_Settings_Module $item Settings module item.
46
+ * @return void
47
  */
48
  public function single_row( $item ) {
49
  // Classes to reuse css from the plugins list table
89
  }
90
 
91
  /**
92
+ * Generates the columns for a single row of the table.
93
  *
94
  * @since 1.8
95
  *
96
+ * @param PLL_Settings_Module $item Settings module item.
97
+ * @return void
98
  */
99
  protected function single_row_columns( $item ) {
100
  $column_info = $this->get_column_info();
120
  }
121
 
122
  /**
123
+ * Displays the item information in a column (default case).
124
  *
125
  * @since 1.8
126
  *
127
+ * @param PLL_Settings_Module $item Settings module item.
128
+ * @param string $column_name Column name.
129
+ * @return string The column name.
130
  */
131
  protected function column_default( $item, $column_name ) {
132
  if ( 'plugin-title' == $column_name ) {
136
  }
137
 
138
  /**
139
+ * Gets the list of columns.
140
  *
141
  * @since 1.8
142
  *
143
+ * @return string[] The list of column titles.
144
  */
145
  public function get_columns() {
146
  return array(
166
  *
167
  * @since 1.8
168
  *
169
+ * @param PLL_Settings_Module[] $items Array of settings module items.
170
+ * @return void
171
  */
172
  public function prepare_items( $items = array() ) {
173
  $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns(), $this->get_primary_column_name() );
188
  * @since 2.1
189
  *
190
  * @param string $which 'top' or 'bottom'
191
+ * @return void
192
  */
193
  protected function display_tablenav( $which ) {} // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
194
  }
settings/table-string.php CHANGED
@@ -14,14 +14,40 @@ if ( ! class_exists( 'WP_List_Table' ) ) {
14
  * @since 0.6
15
  */
16
  class PLL_Table_String extends WP_List_Table {
17
- protected $languages, $strings, $groups, $selected_group;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  /**
20
- * Constructor
 
 
 
 
 
 
 
21
  *
22
  * @since 0.6
23
  *
24
- * @param array $languages list of languages
25
  */
26
  public function __construct( $languages ) {
27
  parent::__construct(
@@ -119,15 +145,15 @@ class PLL_Table_String extends WP_List_Table {
119
  }
120
 
121
  /**
122
- * Gets the list of columns
123
  *
124
  * @since 0.6
125
  *
126
- * @return array the list of column titles
127
  */
128
  public function get_columns() {
129
  return array(
130
- 'cb' => '<input type="checkbox" />', // Checkbox
131
  'string' => esc_html__( 'String', 'polylang' ),
132
  'name' => esc_html__( 'Name', 'polylang' ),
133
  'context' => esc_html__( 'Group', 'polylang' ),
@@ -166,9 +192,9 @@ class PLL_Table_String extends WP_List_Table {
166
  *
167
  * @since 2.6
168
  *
169
- * @param array $mos An array of PLL_MO objects
170
- * @param string $s Searched string
171
- * @return array Found strings
172
  */
173
  protected function search_in_translations( $mos, $s ) {
174
  $founds = array();
@@ -209,6 +235,8 @@ class PLL_Table_String extends WP_List_Table {
209
  * Prepares the list of items for displaying
210
  *
211
  * @since 0.6
 
 
212
  */
213
  public function prepare_items() {
214
  // Is admin language filter active?
@@ -275,11 +303,11 @@ class PLL_Table_String extends WP_List_Table {
275
  }
276
 
277
  /**
278
- * Get the list of possible bulk actions
279
  *
280
  * @since 1.1
281
  *
282
- * @return array
283
  */
284
  public function get_bulk_actions() {
285
  return array( 'delete' => __( 'Delete', 'polylang' ) );
@@ -303,6 +331,7 @@ class PLL_Table_String extends WP_List_Table {
303
  * @since 1.1
304
  *
305
  * @param string $which only 'top' is supported
 
306
  */
307
  public function extra_tablenav( $which ) {
308
  if ( 'top' !== $which ) {
@@ -318,7 +347,7 @@ class PLL_Table_String extends WP_List_Table {
318
  echo '<select id="select-group" name="group">' . "\n";
319
  printf(
320
  '<option value="-1"%s>%s</option>' . "\n",
321
- selected( $this->group_selected, -1, false ),
322
  esc_html__( 'View all groups', 'polylang' )
323
  );
324
 
@@ -341,13 +370,15 @@ class PLL_Table_String extends WP_List_Table {
341
  * Optionaly clean the DB
342
  *
343
  * @since 1.9
 
 
344
  */
345
  public function save_translations() {
346
  check_admin_referer( 'string-translation', '_wpnonce_string-translation' );
347
 
348
  if ( ! empty( $_POST['submit'] ) ) {
349
  foreach ( $this->languages as $language ) {
350
- if ( empty( $_POST['translation'][ $language->slug ] ) ) { // In case the language filter is active ( thanks to John P. Bloch )
351
  continue;
352
  }
353
 
14
  * @since 0.6
15
  */
16
  class PLL_Table_String extends WP_List_Table {
17
+ /**
18
+ * The list of languages.
19
+ *
20
+ * @var PLL_Language[]
21
+ */
22
+ protected $languages;
23
+
24
+ /**
25
+ * Registered strings.
26
+ *
27
+ * @var array
28
+ */
29
+ protected $strings;
30
+
31
+ /**
32
+ * The string groups.
33
+ *
34
+ * @var string[]
35
+ */
36
+ protected $groups;
37
 
38
  /**
39
+ * The selected string group or -1 if none is selected.
40
+ *
41
+ * @var string|int
42
+ */
43
+ protected $selected_group;
44
+
45
+ /**
46
+ * Constructor.
47
  *
48
  * @since 0.6
49
  *
50
+ * @param PLL_Language[] $languages List of languages.
51
  */
52
  public function __construct( $languages ) {
53
  parent::__construct(
145
  }
146
 
147
  /**
148
+ * Gets the list of columns.
149
  *
150
  * @since 0.6
151
  *
152
+ * @return string[] The list of column titles.
153
  */
154
  public function get_columns() {
155
  return array(
156
+ 'cb' => '<input type="checkbox" />', // Checkbox.
157
  'string' => esc_html__( 'String', 'polylang' ),
158
  'name' => esc_html__( 'Name', 'polylang' ),
159
  'context' => esc_html__( 'Group', 'polylang' ),
192
  *
193
  * @since 2.6
194
  *
195
+ * @param PLL_MO[] $mos An array of PLL_MO objects.
196
+ * @param string $s Searched string.
197
+ * @return string[] Found strings.
198
  */
199
  protected function search_in_translations( $mos, $s ) {
200
  $founds = array();
235
  * Prepares the list of items for displaying
236
  *
237
  * @since 0.6
238
+ *
239
+ * @return void
240
  */
241
  public function prepare_items() {
242
  // Is admin language filter active?
303
  }
304
 
305
  /**
306
+ * Get the list of possible bulk actions.
307
  *
308
  * @since 1.1
309
  *
310
+ * @return string[] Array of bulk actions.
311
  */
312
  public function get_bulk_actions() {
313
  return array( 'delete' => __( 'Delete', 'polylang' ) );
331
  * @since 1.1
332
  *
333
  * @param string $which only 'top' is supported
334
+ * @return void
335
  */
336
  public function extra_tablenav( $which ) {
337
  if ( 'top' !== $which ) {
347
  echo '<select id="select-group" name="group">' . "\n";
348
  printf(
349
  '<option value="-1"%s>%s</option>' . "\n",
350
+ selected( $this->selected_group, -1, false ),
351
  esc_html__( 'View all groups', 'polylang' )
352
  );
353
 
370
  * Optionaly clean the DB
371
  *
372
  * @since 1.9
373
+ *
374
+ * @return void
375
  */
376
  public function save_translations() {
377
  check_admin_referer( 'string-translation', '_wpnonce_string-translation' );
378
 
379
  if ( ! empty( $_POST['submit'] ) ) {
380
  foreach ( $this->languages as $language ) {
381
+ if ( empty( $_POST['translation'][ $language->slug ] ) || ! is_array( $_POST['translation'][ $language->slug ] ) ) { // In case the language filter is active ( thanks to John P. Bloch )
382
  continue;
383
  }
384
 
settings/view-tab-lang.php CHANGED
@@ -160,7 +160,7 @@ if ( ! defined( 'ABSPATH' ) ) {
160
  *
161
  * @since 1.7.10
162
  *
163
- * @param object $lang language being edited.
164
  */
165
  do_action( 'pll_language_edit_form_fields', $edit_lang );
166
  } else {
160
  *
161
  * @since 1.7.10
162
  *
163
+ * @param PLL_Language $lang language being edited.
164
  */
165
  do_action( 'pll_language_edit_form_fields', $edit_lang );
166
  } else {
uninstall.php CHANGED
@@ -52,18 +52,6 @@ class PLL_Uninstall {
52
 
53
  do_action( 'pll_uninstall' );
54
 
55
- // Suppress data of the old model < 1.2
56
- // FIXME: to remove when support for v1.1.6 will be dropped
57
- $wpdb->termmeta = $wpdb->prefix . 'termmeta'; // registers the termmeta table in wpdb
58
-
59
- // Do nothing if the termmeta table does not exists
60
- if ( count( $wpdb->get_results( "SHOW TABLES LIKE '$wpdb->termmeta'" ) ) ) {
61
- $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key = '_translations'" );
62
- $wpdb->query( "DELETE FROM $wpdb->termmeta WHERE meta_key = '_language'" );
63
- $wpdb->query( "DELETE FROM $wpdb->termmeta WHERE meta_key = '_rtl'" );
64
- $wpdb->query( "DELETE FROM $wpdb->termmeta WHERE meta_key = '_translations'" );
65
- }
66
-
67
  // Need to register the taxonomies
68
  $pll_taxonomies = array( 'language', 'term_language', 'post_translations', 'term_translations' );
69
  foreach ( $pll_taxonomies as $taxonomy ) {
@@ -96,13 +84,7 @@ class PLL_Uninstall {
96
  wp_delete_post( $id, true );
97
  }
98
 
99
- // Delete the strings translations ( <1.2 )
100
- // FIXME: to remove when support for v1.1.6 will be dropped
101
- foreach ( $languages as $lang ) {
102
- delete_option( 'polylang_mo' . $lang->term_id );
103
- }
104
-
105
- // Delete the strings translations 1.2+
106
  register_post_type( 'polylang_mo', array( 'rewrite' => false, 'query_var' => false ) );
107
  $ids = get_posts(
108
  array(
@@ -146,7 +128,6 @@ class PLL_Uninstall {
146
 
147
  // Delete transients
148
  delete_transient( 'pll_languages_list' );
149
- delete_transient( 'pll_upgrade_1_4' );
150
  }
151
  }
152
 
52
 
53
  do_action( 'pll_uninstall' );
54
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  // Need to register the taxonomies
56
  $pll_taxonomies = array( 'language', 'term_language', 'post_translations', 'term_translations' );
57
  foreach ( $pll_taxonomies as $taxonomy ) {
84
  wp_delete_post( $id, true );
85
  }
86
 
87
+ // Delete the strings translations.
 
 
 
 
 
 
88
  register_post_type( 'polylang_mo', array( 'rewrite' => false, 'query_var' => false ) );
89
  $ids = get_posts(
90
  array(
128
 
129
  // Delete transients
130
  delete_transient( 'pll_languages_list' );
 
131
  }
132
  }
133
 
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
- return ComposerAutoloaderInitb2e9581550b70057025a8e7128ef798f::getLoader();
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInit526218cdfc05347def14cd0ba4a67216::getLoader();
vendor/composer/autoload_classmap.php CHANGED
@@ -7,6 +7,9 @@ $baseDir = dirname($vendorDir);
7
 
8
  return array(
9
  'PLL_AS3CF' => $baseDir . '/integrations/wp-offload-media/as3cf.php',
 
 
 
10
  'PLL_Admin' => $baseDir . '/admin/admin.php',
11
  'PLL_Admin_Base' => $baseDir . '/admin/admin-base.php',
12
  'PLL_Admin_Block_Editor' => $baseDir . '/admin/admin-block-editor.php',
@@ -17,6 +20,7 @@ return array(
17
  'PLL_Admin_Filters_Post' => $baseDir . '/admin/admin-filters-post.php',
18
  'PLL_Admin_Filters_Post_Base' => $baseDir . '/admin/admin-filters-post-base.php',
19
  'PLL_Admin_Filters_Term' => $baseDir . '/admin/admin-filters-term.php',
 
20
  'PLL_Admin_Links' => $baseDir . '/admin/admin-links.php',
21
  'PLL_Admin_Model' => $baseDir . '/admin/admin-model.php',
22
  'PLL_Admin_Nav_Menu' => $baseDir . '/admin/admin-nav-menu.php',
@@ -43,6 +47,7 @@ return array(
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',
@@ -73,7 +78,6 @@ return array(
73
  'PLL_No_Category_Base' => $baseDir . '/integrations/no-category-base/no-category-base.php',
74
  'PLL_OLT_Manager' => $baseDir . '/include/olt-manager.php',
75
  'PLL_Plugin_Updater' => $baseDir . '/install/plugin-updater.php',
76
- 'PLL_Pointer' => $baseDir . '/include/pointer.php',
77
  'PLL_Query' => $baseDir . '/include/query.php',
78
  'PLL_REST_Request' => $baseDir . '/include/rest-request.php',
79
  'PLL_Settings' => $baseDir . '/settings/settings.php',
@@ -88,6 +92,7 @@ return array(
88
  'PLL_Settings_Url' => $baseDir . '/settings/settings-url.php',
89
  'PLL_Settings_WPML' => $baseDir . '/modules/wpml/settings-wpml.php',
90
  'PLL_Sitemaps' => $baseDir . '/modules/sitemaps/sitemaps.php',
 
91
  'PLL_Static_Pages' => $baseDir . '/include/static-pages.php',
92
  'PLL_Switcher' => $baseDir . '/include/switcher.php',
93
  'PLL_Sync' => $baseDir . '/modules/sync/sync.php',
7
 
8
  return array(
9
  'PLL_AS3CF' => $baseDir . '/integrations/wp-offload-media/as3cf.php',
10
+ 'PLL_Abstract_Sitemaps' => $baseDir . '/modules/sitemaps/abstract-sitemaps.php',
11
+ 'PLL_Accept_Language' => $baseDir . '/frontend/accept-language.php',
12
+ 'PLL_Accept_Languages_Collection' => $baseDir . '/frontend/accept-languages-collection.php',
13
  'PLL_Admin' => $baseDir . '/admin/admin.php',
14
  'PLL_Admin_Base' => $baseDir . '/admin/admin-base.php',
15
  'PLL_Admin_Block_Editor' => $baseDir . '/admin/admin-block-editor.php',
20
  'PLL_Admin_Filters_Post' => $baseDir . '/admin/admin-filters-post.php',
21
  'PLL_Admin_Filters_Post_Base' => $baseDir . '/admin/admin-filters-post-base.php',
22
  'PLL_Admin_Filters_Term' => $baseDir . '/admin/admin-filters-term.php',
23
+ 'PLL_Admin_Filters_Widgets_Options' => $baseDir . '/admin/admin-filters-widgets-options.php',
24
  'PLL_Admin_Links' => $baseDir . '/admin/admin-links.php',
25
  'PLL_Admin_Model' => $baseDir . '/admin/admin-model.php',
26
  'PLL_Admin_Nav_Menu' => $baseDir . '/admin/admin-nav-menu.php',
47
  'PLL_Filters' => $baseDir . '/include/filters.php',
48
  'PLL_Filters_Links' => $baseDir . '/include/filters-links.php',
49
  'PLL_Filters_Sanitization' => $baseDir . '/include/filters-sanitization.php',
50
+ 'PLL_Filters_Widgets_Options' => $baseDir . '/include/filters-widgets-options.php',
51
  'PLL_Frontend' => $baseDir . '/frontend/frontend.php',
52
  'PLL_Frontend_Auto_Translate' => $baseDir . '/frontend/frontend-auto-translate.php',
53
  'PLL_Frontend_Filters' => $baseDir . '/frontend/frontend-filters.php',
78
  'PLL_No_Category_Base' => $baseDir . '/integrations/no-category-base/no-category-base.php',
79
  'PLL_OLT_Manager' => $baseDir . '/include/olt-manager.php',
80
  'PLL_Plugin_Updater' => $baseDir . '/install/plugin-updater.php',
 
81
  'PLL_Query' => $baseDir . '/include/query.php',
82
  'PLL_REST_Request' => $baseDir . '/include/rest-request.php',
83
  'PLL_Settings' => $baseDir . '/settings/settings.php',
92
  'PLL_Settings_Url' => $baseDir . '/settings/settings-url.php',
93
  'PLL_Settings_WPML' => $baseDir . '/modules/wpml/settings-wpml.php',
94
  'PLL_Sitemaps' => $baseDir . '/modules/sitemaps/sitemaps.php',
95
+ 'PLL_Sitemaps_Domain' => $baseDir . '/modules/sitemaps/sitemaps-domain.php',
96
  'PLL_Static_Pages' => $baseDir . '/include/static-pages.php',
97
  'PLL_Switcher' => $baseDir . '/include/switcher.php',
98
  'PLL_Sync' => $baseDir . '/modules/sync/sync.php',
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInitb2e9581550b70057025a8e7128ef798f
6
  {
7
  private static $loader;
8
 
@@ -22,15 +22,15 @@ class ComposerAutoloaderInitb2e9581550b70057025a8e7128ef798f
22
  return self::$loader;
23
  }
24
 
25
- spl_autoload_register(array('ComposerAutoloaderInitb2e9581550b70057025a8e7128ef798f', 'loadClassLoader'), true, true);
26
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
27
- spl_autoload_unregister(array('ComposerAutoloaderInitb2e9581550b70057025a8e7128ef798f', 'loadClassLoader'));
28
 
29
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30
  if ($useStaticLoader) {
31
  require_once __DIR__ . '/autoload_static.php';
32
 
33
- call_user_func(\Composer\Autoload\ComposerStaticInitb2e9581550b70057025a8e7128ef798f::getInitializer($loader));
34
  } else {
35
  $map = require __DIR__ . '/autoload_namespaces.php';
36
  foreach ($map as $namespace => $path) {
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInit526218cdfc05347def14cd0ba4a67216
6
  {
7
  private static $loader;
8
 
22
  return self::$loader;
23
  }
24
 
25
+ spl_autoload_register(array('ComposerAutoloaderInit526218cdfc05347def14cd0ba4a67216', 'loadClassLoader'), true, true);
26
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
27
+ spl_autoload_unregister(array('ComposerAutoloaderInit526218cdfc05347def14cd0ba4a67216', 'loadClassLoader'));
28
 
29
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30
  if ($useStaticLoader) {
31
  require_once __DIR__ . '/autoload_static.php';
32
 
33
+ call_user_func(\Composer\Autoload\ComposerStaticInit526218cdfc05347def14cd0ba4a67216::getInitializer($loader));
34
  } else {
35
  $map = require __DIR__ . '/autoload_namespaces.php';
36
  foreach ($map as $namespace => $path) {
vendor/composer/autoload_static.php CHANGED
@@ -4,10 +4,13 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInitb2e9581550b70057025a8e7128ef798f
8
  {
9
  public static $classMap = array (
10
  'PLL_AS3CF' => __DIR__ . '/../..' . '/integrations/wp-offload-media/as3cf.php',
 
 
 
11
  'PLL_Admin' => __DIR__ . '/../..' . '/admin/admin.php',
12
  'PLL_Admin_Base' => __DIR__ . '/../..' . '/admin/admin-base.php',
13
  'PLL_Admin_Block_Editor' => __DIR__ . '/../..' . '/admin/admin-block-editor.php',
@@ -18,6 +21,7 @@ class ComposerStaticInitb2e9581550b70057025a8e7128ef798f
18
  'PLL_Admin_Filters_Post' => __DIR__ . '/../..' . '/admin/admin-filters-post.php',
19
  'PLL_Admin_Filters_Post_Base' => __DIR__ . '/../..' . '/admin/admin-filters-post-base.php',
20
  'PLL_Admin_Filters_Term' => __DIR__ . '/../..' . '/admin/admin-filters-term.php',
 
21
  'PLL_Admin_Links' => __DIR__ . '/../..' . '/admin/admin-links.php',
22
  'PLL_Admin_Model' => __DIR__ . '/../..' . '/admin/admin-model.php',
23
  'PLL_Admin_Nav_Menu' => __DIR__ . '/../..' . '/admin/admin-nav-menu.php',
@@ -44,6 +48,7 @@ class ComposerStaticInitb2e9581550b70057025a8e7128ef798f
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',
@@ -74,7 +79,6 @@ class ComposerStaticInitb2e9581550b70057025a8e7128ef798f
74
  'PLL_No_Category_Base' => __DIR__ . '/../..' . '/integrations/no-category-base/no-category-base.php',
75
  'PLL_OLT_Manager' => __DIR__ . '/../..' . '/include/olt-manager.php',
76
  'PLL_Plugin_Updater' => __DIR__ . '/../..' . '/install/plugin-updater.php',
77
- 'PLL_Pointer' => __DIR__ . '/../..' . '/include/pointer.php',
78
  'PLL_Query' => __DIR__ . '/../..' . '/include/query.php',
79
  'PLL_REST_Request' => __DIR__ . '/../..' . '/include/rest-request.php',
80
  'PLL_Settings' => __DIR__ . '/../..' . '/settings/settings.php',
@@ -89,6 +93,7 @@ class ComposerStaticInitb2e9581550b70057025a8e7128ef798f
89
  'PLL_Settings_Url' => __DIR__ . '/../..' . '/settings/settings-url.php',
90
  'PLL_Settings_WPML' => __DIR__ . '/../..' . '/modules/wpml/settings-wpml.php',
91
  'PLL_Sitemaps' => __DIR__ . '/../..' . '/modules/sitemaps/sitemaps.php',
 
92
  'PLL_Static_Pages' => __DIR__ . '/../..' . '/include/static-pages.php',
93
  'PLL_Switcher' => __DIR__ . '/../..' . '/include/switcher.php',
94
  'PLL_Sync' => __DIR__ . '/../..' . '/modules/sync/sync.php',
@@ -126,7 +131,7 @@ class ComposerStaticInitb2e9581550b70057025a8e7128ef798f
126
  public static function getInitializer(ClassLoader $loader)
127
  {
128
  return \Closure::bind(function () use ($loader) {
129
- $loader->classMap = ComposerStaticInitb2e9581550b70057025a8e7128ef798f::$classMap;
130
 
131
  }, null, ClassLoader::class);
132
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInit526218cdfc05347def14cd0ba4a67216
8
  {
9
  public static $classMap = array (
10
  'PLL_AS3CF' => __DIR__ . '/../..' . '/integrations/wp-offload-media/as3cf.php',
11
+ 'PLL_Abstract_Sitemaps' => __DIR__ . '/../..' . '/modules/sitemaps/abstract-sitemaps.php',
12
+ 'PLL_Accept_Language' => __DIR__ . '/../..' . '/frontend/accept-language.php',
13
+ 'PLL_Accept_Languages_Collection' => __DIR__ . '/../..' . '/frontend/accept-languages-collection.php',
14
  'PLL_Admin' => __DIR__ . '/../..' . '/admin/admin.php',
15
  'PLL_Admin_Base' => __DIR__ . '/../..' . '/admin/admin-base.php',
16
  'PLL_Admin_Block_Editor' => __DIR__ . '/../..' . '/admin/admin-block-editor.php',
21
  'PLL_Admin_Filters_Post' => __DIR__ . '/../..' . '/admin/admin-filters-post.php',
22
  'PLL_Admin_Filters_Post_Base' => __DIR__ . '/../..' . '/admin/admin-filters-post-base.php',
23
  'PLL_Admin_Filters_Term' => __DIR__ . '/../..' . '/admin/admin-filters-term.php',
24
+ 'PLL_Admin_Filters_Widgets_Options' => __DIR__ . '/../..' . '/admin/admin-filters-widgets-options.php',
25
  'PLL_Admin_Links' => __DIR__ . '/../..' . '/admin/admin-links.php',
26
  'PLL_Admin_Model' => __DIR__ . '/../..' . '/admin/admin-model.php',
27
  'PLL_Admin_Nav_Menu' => __DIR__ . '/../..' . '/admin/admin-nav-menu.php',
48
  'PLL_Filters' => __DIR__ . '/../..' . '/include/filters.php',
49
  'PLL_Filters_Links' => __DIR__ . '/../..' . '/include/filters-links.php',
50
  'PLL_Filters_Sanitization' => __DIR__ . '/../..' . '/include/filters-sanitization.php',
51
+ 'PLL_Filters_Widgets_Options' => __DIR__ . '/../..' . '/include/filters-widgets-options.php',
52
  'PLL_Frontend' => __DIR__ . '/../..' . '/frontend/frontend.php',
53
  'PLL_Frontend_Auto_Translate' => __DIR__ . '/../..' . '/frontend/frontend-auto-translate.php',
54
  'PLL_Frontend_Filters' => __DIR__ . '/../..' . '/frontend/frontend-filters.php',
79
  'PLL_No_Category_Base' => __DIR__ . '/../..' . '/integrations/no-category-base/no-category-base.php',
80
  'PLL_OLT_Manager' => __DIR__ . '/../..' . '/include/olt-manager.php',
81
  'PLL_Plugin_Updater' => __DIR__ . '/../..' . '/install/plugin-updater.php',
 
82
  'PLL_Query' => __DIR__ . '/../..' . '/include/query.php',
83
  'PLL_REST_Request' => __DIR__ . '/../..' . '/include/rest-request.php',
84
  'PLL_Settings' => __DIR__ . '/../..' . '/settings/settings.php',
93
  'PLL_Settings_Url' => __DIR__ . '/../..' . '/settings/settings-url.php',
94
  'PLL_Settings_WPML' => __DIR__ . '/../..' . '/modules/wpml/settings-wpml.php',
95
  'PLL_Sitemaps' => __DIR__ . '/../..' . '/modules/sitemaps/sitemaps.php',
96
+ 'PLL_Sitemaps_Domain' => __DIR__ . '/../..' . '/modules/sitemaps/sitemaps-domain.php',
97
  'PLL_Static_Pages' => __DIR__ . '/../..' . '/include/static-pages.php',
98
  'PLL_Switcher' => __DIR__ . '/../..' . '/include/switcher.php',
99
  'PLL_Sync' => __DIR__ . '/../..' . '/modules/sync/sync.php',
131
  public static function getInitializer(ClassLoader $loader)
132
  {
133
  return \Closure::bind(function () use ($loader) {
134
+ $loader->classMap = ComposerStaticInit526218cdfc05347def14cd0ba4a67216::$classMap;
135
 
136
  }, null, ClassLoader::class);
137
  }