Polylang - Version 2.3.9

Version Description

(2018-08-14) =

  • Add a notice to inform about Polylang for WooCommerce
  • Deprecate PLL_Pointer
  • Fix bulk editing pages with no language breaking hierarchy #281
  • Fix an edge case where rewrite rules could be messed on a multisite
  • MU Domain Mapping: fix secondary domain redirected to primary domain
Download this release

Release Info

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

Code changes from version 2.3.8 to 2.3.9

Files changed (47) hide show
  1. admin/admin-base.php +2 -0
  2. admin/admin-filters-columns.php +27 -27
  3. admin/admin-filters-post-base.php +6 -7
  4. admin/admin-filters-post.php +5 -2
  5. admin/admin-filters-term.php +1 -1
  6. admin/admin-links.php +8 -8
  7. admin/admin-model.php +1 -0
  8. admin/admin-notices.php +192 -0
  9. admin/admin-strings.php +10 -10
  10. admin/admin.php +14 -14
  11. admin/view-translations-post.php +2 -2
  12. css/admin.css +12 -0
  13. css/admin.min.css +1 -1
  14. frontend/choose-lang-content.php +17 -17
  15. frontend/choose-lang-domain.php +2 -2
  16. frontend/choose-lang-url.php +7 -7
  17. frontend/choose-lang.php +28 -28
  18. frontend/frontend-filters-search.php +25 -22
  19. frontend/frontend.php +1 -1
  20. include/base.php +1 -0
  21. include/filters.php +1 -1
  22. include/language.php +1 -1
  23. include/model.php +15 -15
  24. include/nav-menu.php +7 -7
  25. include/pointer.php +10 -7
  26. include/query.php +1 -0
  27. include/static-pages.php +6 -6
  28. include/switcher.php +1 -2
  29. include/widget-calendar.php +24 -14
  30. install/install.php +13 -12
  31. lingotek/lingotek.php +19 -32
  32. modules/plugins/plugins-compat.php +3 -3
  33. modules/plugins/wp-import.php +20 -20
  34. modules/share-slug/settings-share-slug.php +4 -4
  35. modules/sync/settings-sync.php +6 -6
  36. modules/translate-slugs/settings-translate-slugs.php +3 -3
  37. modules/wpml/settings-wpml.php +2 -2
  38. modules/wpml/wpml-api.php +1 -0
  39. modules/wpml/wpml-legacy-api.php +3 -1
  40. polylang.php +2 -2
  41. readme.txt +9 -1
  42. settings/flags.php +1 -1
  43. settings/settings-media.php +1 -1
  44. settings/settings-tools.php +4 -4
  45. settings/settings-url.php +2 -1
  46. settings/view-tab-lang.php +3 -3
  47. settings/view-tab-settings.php +2 -2
admin/admin-base.php CHANGED
@@ -46,6 +46,7 @@ class PLL_Admin_Base extends PLL_Base {
46
  return;
47
  }
48
 
 
49
  $this->links = new PLL_Admin_Links( $this ); // FIXME needed here ?
50
  $this->static_pages = new PLL_Admin_Static_Pages( $this ); // FIXME needed here ?
51
  $this->filters_links = new PLL_Filters_Links( $this ); // FIXME needed here ?
@@ -284,6 +285,7 @@ class PLL_Admin_Base extends PLL_Base {
284
  /**
285
  * Avoids parsing a tax query when all languages are requested
286
  * Fixes https://wordpress.org/support/topic/notice-undefined-offset-0-in-wp-includesqueryphp-on-line-3877 introduced in WP 4.1
 
287
  * @see the suggestion of @boonebgorges, https://core.trac.wordpress.org/ticket/31246
288
  *
289
  * @since 1.6.5
46
  return;
47
  }
48
 
49
+ $this->notices = new PLL_Admin_Notices( $this );
50
  $this->links = new PLL_Admin_Links( $this ); // FIXME needed here ?
51
  $this->static_pages = new PLL_Admin_Static_Pages( $this ); // FIXME needed here ?
52
  $this->filters_links = new PLL_Filters_Links( $this ); // FIXME needed here ?
285
  /**
286
  * Avoids parsing a tax query when all languages are requested
287
  * Fixes https://wordpress.org/support/topic/notice-undefined-offset-0-in-wp-includesqueryphp-on-line-3877 introduced in WP 4.1
288
+ *
289
  * @see the suggestion of @boonebgorges, https://core.trac.wordpress.org/ticket/31246
290
  *
291
  * @since 1.6.5
admin/admin-filters-columns.php CHANGED
@@ -1,8 +1,8 @@
1
  <?php
2
 
3
  /**
4
- * adds the language column in posts and terms list tables
5
- * manages quick edit and bulk edit as well
6
  *
7
  * @since 1.2
8
  */
@@ -10,7 +10,7 @@ class PLL_Admin_Filters_Columns {
10
  public $links, $model, $filter_lang;
11
 
12
  /**
13
- * constructor: setups filters and actions
14
  *
15
  * @since 1.2
16
  *
@@ -21,31 +21,31 @@ class PLL_Admin_Filters_Columns {
21
  $this->model = &$polylang->model;
22
  $this->filter_lang = &$polylang->filter_lang;
23
 
24
- // add the language and translations columns in 'All Posts', 'All Pages' and 'Media library' panels
25
  foreach ( $this->model->get_translated_post_types() as $type ) {
26
- // use the latest filter late as some plugins purely overwrite what's done by others :(
27
- // specific case for media
28
  add_filter( 'manage_' . ( 'attachment' == $type ? 'upload' : 'edit-' . $type ) . '_columns', array( $this, 'add_post_column' ), 100 );
29
  add_action( 'manage_' . ( 'attachment' == $type ? 'media' : $type . '_posts' ) . '_custom_column', array( $this, 'post_column' ), 10, 2 );
30
  }
31
 
32
- // quick edit and bulk edit
33
  add_filter( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
34
  add_filter( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
35
 
36
- // adds the language column in the 'Categories' and 'Post Tags' tables
37
  foreach ( $this->model->get_translated_taxonomies() as $tax ) {
38
  add_filter( 'manage_edit-' . $tax . '_columns', array( $this, 'add_term_column' ) );
39
  add_filter( 'manage_' . $tax . '_custom_column', array( $this, 'term_column' ), 10, 3 );
40
  }
41
 
42
- // ajax responses to update list table rows
43
  add_action( 'wp_ajax_pll_update_post_rows', array( $this, 'ajax_update_post_rows' ) );
44
  add_action( 'wp_ajax_pll_update_term_rows', array( $this, 'ajax_update_term_rows' ) );
45
  }
46
 
47
  /**
48
- * adds languages and translations columns in posts, pages, media, categories and tags tables
49
  *
50
  * @since 0.8.2
51
  *
@@ -60,7 +60,7 @@ class PLL_Admin_Filters_Columns {
60
  }
61
 
62
  foreach ( $this->model->get_languages_list() as $language ) {
63
- // don't add the column for the filtered language
64
  if ( empty( $this->filter_lang ) || $language->slug != $this->filter_lang->slug ) {
65
  $columns[ 'language_' . $language->slug ] = $language->flag ? $language->flag . '<span class="screen-reader-text">' . esc_html( $language->name ) . '</span>' : esc_html( $language->slug );
66
  }
@@ -70,7 +70,7 @@ class PLL_Admin_Filters_Columns {
70
  }
71
 
72
  /**
73
- * returns the first language column in the posts, pages and media library tables
74
  *
75
  * @since 0.9
76
  *
@@ -87,7 +87,7 @@ class PLL_Admin_Filters_Columns {
87
  }
88
 
89
  /**
90
- * adds the language and translations columns ( before the comments column ) in the posts, pages and media library tables
91
  *
92
  * @since 0.1
93
  *
@@ -99,7 +99,7 @@ class PLL_Admin_Filters_Columns {
99
  }
100
 
101
  /**
102
- * fills the language and translations columns in the posts, pages and media library tables
103
  * take care that when doing ajax inline edit, the post may not be updated in database yet
104
  *
105
  * @since 0.1
@@ -117,17 +117,17 @@ class PLL_Admin_Filters_Columns {
117
 
118
  $language = $this->model->get_language( substr( $column, 9 ) );
119
 
120
- // hidden field containing the post language for quick edit
121
  if ( $column == $this->get_first_language_column() ) {
122
  printf( '<div class="hidden" id="lang_%d">%s</div>', intval( $post_id ), esc_html( $lang->slug ) );
123
  }
124
 
125
  $post_type_object = get_post_type_object( get_post_type( $post_id ) );
126
 
127
- // link to edit post ( or a translation )
128
  if ( $id = $this->model->post->get( $post_id, $language ) ) {
129
  // get_edit_post_link returns nothing if the user cannot edit the post
130
- // thanks to Solinx. See http://wordpress.org/support/topic/feature-request-incl-code-check-for-capabilities-in-admin-screens
131
  if ( $link = get_edit_post_link( $id ) ) {
132
  if ( $id === $post_id ) {
133
  $class = 'pll_icon_tick';
@@ -150,14 +150,14 @@ class PLL_Admin_Filters_Columns {
150
  );
151
  }
152
  }
153
- // link to add a new translation
154
  else {
155
  echo $this->links->new_post_translation_link( $post_id, $language );
156
  }
157
  }
158
 
159
  /**
160
- * quick edit & bulk edit
161
  *
162
  * @since 0.9
163
  *
@@ -174,7 +174,7 @@ class PLL_Admin_Filters_Columns {
174
  }
175
 
176
  $dropdown = new PLL_Walker_Dropdown();
177
- // the hidden field 'old_lang' allows to pass the old language to ajax request
178
  printf(
179
  '<fieldset class="inline-edit-col-left">
180
  <div class="inline-edit-col">
@@ -192,7 +192,7 @@ class PLL_Admin_Filters_Columns {
192
  }
193
 
194
  /**
195
- * adds the language column ( before the posts column ) in the 'Categories' or 'Post Tags' table
196
  *
197
  * @since 0.1
198
  *
@@ -204,7 +204,7 @@ class PLL_Admin_Filters_Columns {
204
  }
205
 
206
  /**
207
- * fills the language column in the 'Categories' or 'Post Tags' table
208
  *
209
  * @since 0.1
210
  *
@@ -231,13 +231,13 @@ class PLL_Admin_Filters_Columns {
231
  if ( $column == $this->get_first_language_column() ) {
232
  $out = sprintf( '<div class="hidden" id="lang_%d">%s</div>', intval( $term_id ), esc_html( $lang->slug ) );
233
 
234
- // identify the default categories to disable the language dropdown in js
235
  if ( in_array( get_option( 'default_category' ), $this->model->term->get_translations( $term_id ) ) ) {
236
  $out .= sprintf( '<div class="hidden" id="default_cat_%1$d">%1$d</div>', intval( $term_id ) );
237
  }
238
  }
239
 
240
- // link to edit term ( or a translation )
241
  if ( ( $id = $this->model->term->get( $term_id, $language ) ) && $term = get_term( $id, $taxonomy ) ) {
242
  if ( $link = get_edit_term_link( $id, $taxonomy, $post_type ) ) {
243
  if ( $id === $term_id ) {
@@ -262,7 +262,7 @@ class PLL_Admin_Filters_Columns {
262
  }
263
  }
264
 
265
- // link to add a new translation
266
  else {
267
  $out .= $this->links->new_term_translation_link( $term_id, $taxonomy, $post_type, $language );
268
  }
@@ -271,7 +271,7 @@ class PLL_Admin_Filters_Columns {
271
  }
272
 
273
  /**
274
- * update rows of translated posts when the language is modified in quick edit
275
  *
276
  * @since 1.7
277
  */
@@ -305,7 +305,7 @@ class PLL_Admin_Filters_Columns {
305
  }
306
 
307
  /**
308
- * update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit
309
  *
310
  * @since 1.7
311
  */
1
  <?php
2
 
3
  /**
4
+ * Adds the language column in posts and terms list tables
5
+ * Manages quick edit and bulk edit as well
6
  *
7
  * @since 1.2
8
  */
10
  public $links, $model, $filter_lang;
11
 
12
  /**
13
+ * Constructor: setups filters and actions
14
  *
15
  * @since 1.2
16
  *
21
  $this->model = &$polylang->model;
22
  $this->filter_lang = &$polylang->filter_lang;
23
 
24
+ // Add the language and translations columns in 'All Posts', 'All Pages' and 'Media library' panels
25
  foreach ( $this->model->get_translated_post_types() as $type ) {
26
+ // Use the latest filter late as some plugins purely overwrite what's done by others :(
27
+ // Specific case for media
28
  add_filter( 'manage_' . ( 'attachment' == $type ? 'upload' : 'edit-' . $type ) . '_columns', array( $this, 'add_post_column' ), 100 );
29
  add_action( 'manage_' . ( 'attachment' == $type ? 'media' : $type . '_posts' ) . '_custom_column', array( $this, 'post_column' ), 10, 2 );
30
  }
31
 
32
+ // Quick edit and bulk edit
33
  add_filter( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
34
  add_filter( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
35
 
36
+ // Adds the language column in the 'Categories' and 'Post Tags' tables
37
  foreach ( $this->model->get_translated_taxonomies() as $tax ) {
38
  add_filter( 'manage_edit-' . $tax . '_columns', array( $this, 'add_term_column' ) );
39
  add_filter( 'manage_' . $tax . '_custom_column', array( $this, 'term_column' ), 10, 3 );
40
  }
41
 
42
+ // Ajax responses to update list table rows
43
  add_action( 'wp_ajax_pll_update_post_rows', array( $this, 'ajax_update_post_rows' ) );
44
  add_action( 'wp_ajax_pll_update_term_rows', array( $this, 'ajax_update_term_rows' ) );
45
  }
46
 
47
  /**
48
+ * Adds languages and translations columns in posts, pages, media, categories and tags tables
49
  *
50
  * @since 0.8.2
51
  *
60
  }
61
 
62
  foreach ( $this->model->get_languages_list() as $language ) {
63
+ // Don't add the column for the filtered language
64
  if ( empty( $this->filter_lang ) || $language->slug != $this->filter_lang->slug ) {
65
  $columns[ 'language_' . $language->slug ] = $language->flag ? $language->flag . '<span class="screen-reader-text">' . esc_html( $language->name ) . '</span>' : esc_html( $language->slug );
66
  }
70
  }
71
 
72
  /**
73
+ * Returns the first language column in the posts, pages and media library tables
74
  *
75
  * @since 0.9
76
  *
87
  }
88
 
89
  /**
90
+ * Adds the language and translations columns ( before the comments column ) in the posts, pages and media library tables
91
  *
92
  * @since 0.1
93
  *
99
  }
100
 
101
  /**
102
+ * Fills the language and translations columns in the posts, pages and media library tables
103
  * take care that when doing ajax inline edit, the post may not be updated in database yet
104
  *
105
  * @since 0.1
117
 
118
  $language = $this->model->get_language( substr( $column, 9 ) );
119
 
120
+ // Hidden field containing the post language for quick edit
121
  if ( $column == $this->get_first_language_column() ) {
122
  printf( '<div class="hidden" id="lang_%d">%s</div>', intval( $post_id ), esc_html( $lang->slug ) );
123
  }
124
 
125
  $post_type_object = get_post_type_object( get_post_type( $post_id ) );
126
 
127
+ // Link to edit post ( or a translation )
128
  if ( $id = $this->model->post->get( $post_id, $language ) ) {
129
  // get_edit_post_link returns nothing if the user cannot edit the post
130
+ // Thanks to Solinx. See http://wordpress.org/support/topic/feature-request-incl-code-check-for-capabilities-in-admin-screens
131
  if ( $link = get_edit_post_link( $id ) ) {
132
  if ( $id === $post_id ) {
133
  $class = 'pll_icon_tick';
150
  );
151
  }
152
  }
153
+ // Link to add a new translation
154
  else {
155
  echo $this->links->new_post_translation_link( $post_id, $language );
156
  }
157
  }
158
 
159
  /**
160
+ * Quick edit & bulk edit
161
  *
162
  * @since 0.9
163
  *
174
  }
175
 
176
  $dropdown = new PLL_Walker_Dropdown();
177
+ // The hidden field 'old_lang' allows to pass the old language to ajax request
178
  printf(
179
  '<fieldset class="inline-edit-col-left">
180
  <div class="inline-edit-col">
192
  }
193
 
194
  /**
195
+ * Adds the language column ( before the posts column ) in the 'Categories' or 'Post Tags' table
196
  *
197
  * @since 0.1
198
  *
204
  }
205
 
206
  /**
207
+ * Fills the language column in the 'Categories' or 'Post Tags' table
208
  *
209
  * @since 0.1
210
  *
231
  if ( $column == $this->get_first_language_column() ) {
232
  $out = sprintf( '<div class="hidden" id="lang_%d">%s</div>', intval( $term_id ), esc_html( $lang->slug ) );
233
 
234
+ // Identify the default categories to disable the language dropdown in js
235
  if ( in_array( get_option( 'default_category' ), $this->model->term->get_translations( $term_id ) ) ) {
236
  $out .= sprintf( '<div class="hidden" id="default_cat_%1$d">%1$d</div>', intval( $term_id ) );
237
  }
238
  }
239
 
240
+ // Link to edit term ( or a translation )
241
  if ( ( $id = $this->model->term->get( $term_id, $language ) ) && $term = get_term( $id, $taxonomy ) ) {
242
  if ( $link = get_edit_term_link( $id, $taxonomy, $post_type ) ) {
243
  if ( $id === $term_id ) {
262
  }
263
  }
264
 
265
+ // Link to add a new translation
266
  else {
267
  $out .= $this->links->new_term_translation_link( $term_id, $taxonomy, $post_type, $language );
268
  }
271
  }
272
 
273
  /**
274
+ * Update rows of translated posts when the language is modified in quick edit
275
  *
276
  * @since 1.7
277
  */
305
  }
306
 
307
  /**
308
+ * Update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit
309
  *
310
  * @since 1.7
311
  */
admin/admin-filters-post-base.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  /**
4
- * some common code for PLL_Admin_Filters_Post and PLL_Admin_Filters_Media
5
  *
6
  * @since 1.5
7
  */
@@ -9,7 +9,7 @@ abstract class PLL_Admin_Filters_Post_Base {
9
  public $links, $model, $pref_lang;
10
 
11
  /**
12
- * constructor: setups filters and actions
13
  *
14
  * @since 1.2
15
  *
@@ -22,7 +22,7 @@ abstract class PLL_Admin_Filters_Post_Base {
22
  }
23
 
24
  /**
25
- * allows to set a language by default for posts if it has no language yet
26
  *
27
  * @since 1.5
28
  *
@@ -45,7 +45,7 @@ abstract class PLL_Admin_Filters_Post_Base {
45
  }
46
 
47
  /**
48
- * save translations from language metabox
49
  *
50
  * @since 1.5
51
  *
@@ -54,11 +54,10 @@ abstract class PLL_Admin_Filters_Post_Base {
54
  * @return array
55
  */
56
  protected function save_translations( $post_id, $arr ) {
57
- // security check
58
- // as 'wp_insert_post' can be called from outside WP admin
59
  check_admin_referer( 'pll_language', '_pll_nonce' );
60
 
61
- // save translations after checking the translated post is in the right language
62
  foreach ( $arr as $lang => $tr_id ) {
63
  $translations[ $lang ] = ( $tr_id && $this->model->post->get_language( (int) $tr_id )->slug == $lang ) ? (int) $tr_id : 0;
64
  }
1
  <?php
2
 
3
  /**
4
+ * Some common code for PLL_Admin_Filters_Post and PLL_Admin_Filters_Media
5
  *
6
  * @since 1.5
7
  */
9
  public $links, $model, $pref_lang;
10
 
11
  /**
12
+ * Constructor: setups filters and actions
13
  *
14
  * @since 1.2
15
  *
22
  }
23
 
24
  /**
25
+ * Allows to set a language by default for posts if it has no language yet
26
  *
27
  * @since 1.5
28
  *
45
  }
46
 
47
  /**
48
+ * Save translations from language metabox
49
  *
50
  * @since 1.5
51
  *
54
  * @return array
55
  */
56
  protected function save_translations( $post_id, $arr ) {
57
+ // Security check as 'wp_insert_post' can be called from outside WP admin
 
58
  check_admin_referer( 'pll_language', '_pll_nonce' );
59
 
60
+ // Save translations after checking the translated post is in the right language
61
  foreach ( $arr as $lang => $tr_id ) {
62
  $translations[ $lang ] = ( $tr_id && $this->model->post->get_language( (int) $tr_id )->slug == $lang ) ? (int) $tr_id : 0;
63
  }
admin/admin-filters-post.php CHANGED
@@ -541,12 +541,15 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
541
  * @return int
542
  */
543
  public function wp_insert_post_parent( $post_parent, $post_id, $new_postarr, $postarr ) {
544
- if ( isset( $postarr['bulk_edit'] ) ) {
545
  check_admin_referer( 'bulk-posts' );
546
  $lang = -1 == $postarr['inline_lang_choice'] ?
547
  $this->model->post->get_language( $post_id ) :
548
  $this->model->get_language( $postarr['inline_lang_choice'] );
549
- $post_parent = $this->model->post->get_translation( $post_parent, $lang );
 
 
 
550
  }
551
  return $post_parent;
552
  }
541
  * @return int
542
  */
543
  public function wp_insert_post_parent( $post_parent, $post_id, $new_postarr, $postarr ) {
544
+ if ( isset( $postarr['bulk_edit'], $postarr['inline_lang_choice'] ) ) {
545
  check_admin_referer( 'bulk-posts' );
546
  $lang = -1 == $postarr['inline_lang_choice'] ?
547
  $this->model->post->get_language( $post_id ) :
548
  $this->model->get_language( $postarr['inline_lang_choice'] );
549
+ // Dont break the hierarchy in case the post has no language
550
+ if ( ! empty( $lang ) ) {
551
+ $post_parent = $this->model->post->get_translation( $post_parent, $lang );
552
+ }
553
  }
554
  return $post_parent;
555
  }
admin/admin-filters-term.php CHANGED
@@ -186,7 +186,7 @@ class PLL_Admin_Filters_Term {
186
  }
187
 
188
  /**
189
- * stores the current post_id when bulk editing posts for use in save_language and pre_term_slug
190
  *
191
  * @since 1.7
192
  *
186
  }
187
 
188
  /**
189
+ * Stores the current post_id when bulk editing posts for use in save_language and pre_term_slug
190
  *
191
  * @since 1.7
192
  *
admin/admin-links.php CHANGED
@@ -1,14 +1,14 @@
1
  <?php
2
 
3
  /**
4
- * manages links related functions
5
  *
6
  * @since 1.8
7
  */
8
  class PLL_Admin_Links extends PLL_Links {
9
 
10
  /**
11
- * get the link to create a new post translation
12
  *
13
  * @since 1.5
14
  *
@@ -38,7 +38,7 @@ class PLL_Admin_Links extends PLL_Links {
38
  'new_lang' => $language->slug,
39
  );
40
 
41
- // add nonce for media as we will directly publish a new attachment from a click on this link
42
  $link = wp_nonce_url( add_query_arg( $args, admin_url( 'admin.php' ) ), 'translate_media' );
43
  } else {
44
  $args = array(
@@ -63,7 +63,7 @@ class PLL_Admin_Links extends PLL_Links {
63
  }
64
 
65
  /**
66
- * returns html markup for a new post translation link
67
  *
68
  * @since 1.8
69
  *
@@ -82,7 +82,7 @@ class PLL_Admin_Links extends PLL_Links {
82
  }
83
 
84
  /**
85
- * returns html markup for a translation link
86
  *
87
  * @since 1.4
88
  *
@@ -101,7 +101,7 @@ class PLL_Admin_Links extends PLL_Links {
101
  }
102
 
103
  /**
104
- * get the link to create a new term translation
105
  *
106
  * @since 1.5
107
  *
@@ -141,7 +141,7 @@ class PLL_Admin_Links extends PLL_Links {
141
  }
142
 
143
  /**
144
- * returns html markup for a new term translation
145
  *
146
  * @since 1.8
147
  *
@@ -162,7 +162,7 @@ class PLL_Admin_Links extends PLL_Links {
162
  }
163
 
164
  /**
165
- * returns html markup for a term translation link
166
  *
167
  * @since 1.4
168
  *
1
  <?php
2
 
3
  /**
4
+ * Manages links related functions
5
  *
6
  * @since 1.8
7
  */
8
  class PLL_Admin_Links extends PLL_Links {
9
 
10
  /**
11
+ * Get the link to create a new post translation
12
  *
13
  * @since 1.5
14
  *
38
  'new_lang' => $language->slug,
39
  );
40
 
41
+ // Add nonce for media as we will directly publish a new attachment from a click on this link
42
  $link = wp_nonce_url( add_query_arg( $args, admin_url( 'admin.php' ) ), 'translate_media' );
43
  } else {
44
  $args = array(
63
  }
64
 
65
  /**
66
+ * Returns html markup for a new post translation link
67
  *
68
  * @since 1.8
69
  *
82
  }
83
 
84
  /**
85
+ * Returns html markup for a translation link
86
  *
87
  * @since 1.4
88
  *
101
  }
102
 
103
  /**
104
+ * Get the link to create a new term translation
105
  *
106
  * @since 1.5
107
  *
141
  }
142
 
143
  /**
144
+ * Returns html markup for a new term translation
145
  *
146
  * @since 1.8
147
  *
162
  }
163
 
164
  /**
165
+ * Returns html markup for a term translation link
166
  *
167
  * @since 1.4
168
  *
admin/admin-model.php CHANGED
@@ -256,6 +256,7 @@ class PLL_Admin_Model extends PLL_Model {
256
 
257
  /**
258
  * Validates data entered when creating or updating a language
 
259
  * @see PLL_Admin_Model::add_language
260
  *
261
  * @since 0.4
256
 
257
  /**
258
  * Validates data entered when creating or updating a language
259
+ *
260
  * @see PLL_Admin_Model::add_language
261
  *
262
  * @since 0.4
admin/admin-notices.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * A class to manage admin notices
5
+ * displayed only to admin, based on 'manage_options' capability
6
+ * and only on dashboard, plugins and Polylang admin pages
7
+ *
8
+ * @since 2.3.9
9
+ */
10
+ class PLL_Admin_Notices {
11
+ static private $notices = array();
12
+
13
+ /**
14
+ * Constructor
15
+ * Setup actions
16
+ *
17
+ * @since 2.3.9
18
+ *
19
+ * @param object $polylang
20
+ */
21
+ public function __construct( $polylang ) {
22
+ $this->options = &$polylang->options;
23
+
24
+ add_action( 'admin_init', array( $this, 'hide_notice' ) );
25
+ add_action( 'admin_notices', array( $this, 'display_notices' ) );
26
+ }
27
+
28
+ /**
29
+ * Add a custom notice
30
+ *
31
+ * @since 2.3.9
32
+ *
33
+ * @param string $name Notice name
34
+ * @param string $html Content of the notice
35
+ */
36
+ static public function add_notice( $name, $html ) {
37
+ self::$notices[ $name ] = $html;
38
+ }
39
+
40
+ /**
41
+ * Get custom notices
42
+ *
43
+ * @since 2.3.9
44
+ *
45
+ * @return array
46
+ */
47
+ static public function get_notices() {
48
+ return self::$notices;
49
+ }
50
+
51
+ /**
52
+ * Has a notice been dismissed?
53
+ *
54
+ * @since 2.3.9
55
+ *
56
+ * @param string $notice Notice name
57
+ * @return bool
58
+ */
59
+ protected function is_dismissed( $notice ) {
60
+ $dismissed = get_user_meta( get_current_user_id(), 'pll_dismissed_notices', true );
61
+ return is_array( $dismissed ) && in_array( $notice, $dismissed );
62
+ }
63
+
64
+ /**
65
+ * Should we display notices on this screen?
66
+ *
67
+ * @since 2.3.9
68
+ *
69
+ * @return bool
70
+ */
71
+ protected function can_display_notice() {
72
+ $screen = get_current_screen();
73
+ $screen_id = sanitize_title( __( 'Languages', 'polylang' ) );
74
+
75
+ return in_array( $screen->id, array(
76
+ 'dashboard',
77
+ 'plugins',
78
+ 'toplevel_page_mlang',
79
+ $screen_id . '_page_mlang_strings',
80
+ $screen_id . '_page_mlang_settings',
81
+ ) );
82
+ }
83
+
84
+ /**
85
+ * Stores dismissed notices in database
86
+ *
87
+ * @since 2.3.9
88
+ */
89
+ public function hide_notice() {
90
+ if ( isset( $_GET['pll-hide-notice'], $_GET['_pll_notice_nonce'] ) ) {
91
+ $notice = sanitize_key( $_GET['pll-hide-notice'] );
92
+ check_admin_referer( $notice, '_pll_notice_nonce' );
93
+ if ( ! $dismissed = get_user_meta( get_current_user_id(), 'pll_dismissed_notices', true ) ) {
94
+ $dismissed = array();
95
+ }
96
+ $dismissed[] = $notice;
97
+ update_user_meta( get_current_user_id(), 'pll_dismissed_notices', $dismissed );
98
+ wp_safe_redirect( remove_query_arg( array( 'pll-hide-notice', '_pll_notice_nonce' ), wp_get_referer() ) );
99
+ exit;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Displays notices
105
+ *
106
+ * @since 2.3.9
107
+ */
108
+ public function display_notices() {
109
+ if ( current_user_can( 'manage_options' ) && $this->can_display_notice() ) {
110
+ // Core notices
111
+ $this->pllwc_notice();
112
+ $this->review_notice();
113
+
114
+ // Custom notices
115
+ foreach ( $this->get_notices() as $notice => $html ) {
116
+ ?>
117
+ <div class="pll-notice notice notice-info">
118
+ <?php
119
+ $this->dismiss_button( $notice );
120
+ echo wp_kses_post( $html );
121
+ ?>
122
+ </div>
123
+ <?php
124
+ }
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Displays a dismiss button
130
+ *
131
+ * @since 2.3.9
132
+ *
133
+ * @param string $name Notice name
134
+ */
135
+ public function dismiss_button( $name ) {
136
+ printf(
137
+ '<a class="notice-dismiss" href="%s"><span class="screen-reader-text">%s</span></a>',
138
+ esc_url( wp_nonce_url( add_query_arg( 'pll-hide-notice', $name ), $name, '_pll_notice_nonce' ) ),
139
+ __( 'Dismiss this notice.' )
140
+ );
141
+ }
142
+
143
+ /**
144
+ * Displays a notice if WooCommerce is activated without Polylang for WooCommerce
145
+ *
146
+ * @since 2.3.9
147
+ */
148
+ private function pllwc_notice() {
149
+ if ( defined( 'WOOCOMMERCE_VERSION' ) && ! defined( 'PLLWC_VERSION' ) && ! $this->is_dismissed( 'pllwc' ) ) {
150
+ ?>
151
+ <div class="pll-notice notice notice-warning">
152
+ <?php $this->dismiss_button( 'pllwc' ); ?>
153
+ <p>
154
+ <?php
155
+ printf(
156
+ /* translators: %1$s is link start tag, %2$s is link end tag. */
157
+ __( 'We have noticed that you are using Polylang with WooCommerce. We recommend you to use %1$sPolylang for WooCommerce%2$s to ensure the compatibility.', 'polylang' ),
158
+ '<a href="https://polylang.pro/downloads/polylang-for-woocommerce/">',
159
+ '</a>'
160
+ );
161
+ ?>
162
+ </p>
163
+ </div>
164
+ <?php
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Displays a notice asking for a review
170
+ *
171
+ * @since 2.3.9
172
+ */
173
+ private function review_notice() {
174
+ if ( ! defined( 'POLYLANG_PRO' ) && ! $this->is_dismissed( 'review' ) && ! empty( $this->options['first_activation'] ) && time() > $this->options['first_activation'] + 15 * DAY_IN_SECONDS ) {
175
+ ?>
176
+ <div class="pll-notice notice notice-info">
177
+ <?php $this->dismiss_button( 'review' ); ?>
178
+ <p>
179
+ <?php
180
+ printf(
181
+ /* translators: %1$s is link start tag, %2$s is link end tag. */
182
+ __( 'We have noticed that you are using Polylang for some time. We hope that you love it! We would be thrilled if you could %1$sgive us a 5 stars rating%2$s.', 'polylang' ),
183
+ '<a href="https://wordpress.org/support/plugin/polylang/reviews/?rate=5#new-post">',
184
+ '</a>'
185
+ );
186
+ ?>
187
+ </p>
188
+ </div>
189
+ <?php
190
+ }
191
+ }
192
+ }
admin/admin-strings.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  /**
4
- * a fully static class to manage strings translations on admin side
5
  *
6
  * @since 1.6
7
  */
@@ -10,7 +10,7 @@ class PLL_Admin_Strings {
10
  static protected $default_strings; // default strings to register
11
 
12
  /**
13
- * init: add filters
14
  *
15
  * @since 1.6
16
  */
@@ -20,7 +20,7 @@ class PLL_Admin_Strings {
20
  }
21
 
22
  /**
23
- * register strings for translation making sure it is not duplicate or empty
24
  *
25
  * @since 0.6
26
  *
@@ -30,7 +30,7 @@ class PLL_Admin_Strings {
30
  * @param bool $multiline Optional, whether the string table should display a multiline textarea or a single line input, defaults to single line
31
  */
32
  static public function register_string( $name, $string, $context = 'Polylang', $multiline = false ) {
33
- // backward compatibility with Polylang older than 1.1
34
  if ( is_bool( $context ) ) {
35
  $multiline = $context;
36
  $context = 'Polylang';
@@ -42,7 +42,7 @@ class PLL_Admin_Strings {
42
  }
43
 
44
  /**
45
- * get registered strings
46
  *
47
  * @since 0.6.1
48
  *
@@ -65,7 +65,7 @@ class PLL_Admin_Strings {
65
  self::register_string( $string, get_option( $option ), 'WordPress' );
66
  }
67
 
68
- // widgets titles
69
  global $wp_registered_widgets;
70
  $sidebars = wp_get_sidebars_widgets();
71
  foreach ( $sidebars as $sidebar => $widgets ) {
@@ -74,8 +74,8 @@ class PLL_Admin_Strings {
74
  }
75
 
76
  foreach ( $widgets as $widget ) {
77
- // nothing can be done if the widget is created using pre WP2.8 API :(
78
- // there is no object, so we can't access it to get the widget options
79
  if ( ! isset( $wp_registered_widgets[ $widget ]['callback'][0] ) || ! is_object( $wp_registered_widgets[ $widget ]['callback'][0] ) || ! method_exists( $wp_registered_widgets[ $widget ]['callback'][0], 'get_settings' ) ) {
80
  continue;
81
  }
@@ -83,7 +83,7 @@ class PLL_Admin_Strings {
83
  $widget_settings = $wp_registered_widgets[ $widget ]['callback'][0]->get_settings();
84
  $number = $wp_registered_widgets[ $widget ]['params'][0]['number'];
85
 
86
- // don't enable widget translation if the widget is visible in only one language or if there is no title
87
  if ( empty( $widget_settings[ $number ]['pll_lang'] ) ) {
88
  if ( isset( $widget_settings[ $number ]['title'] ) && $title = $widget_settings[ $number ]['title'] ) {
89
  self::register_string( self::$default_strings['widget_title'], $title, 'Widget' );
@@ -109,7 +109,7 @@ class PLL_Admin_Strings {
109
  }
110
 
111
  /**
112
- * performs the sanitization ( before saving in DB ) of default strings translations
113
  *
114
  * @since 1.6
115
  *
1
  <?php
2
 
3
  /**
4
+ * A fully static class to manage strings translations on admin side
5
  *
6
  * @since 1.6
7
  */
10
  static protected $default_strings; // default strings to register
11
 
12
  /**
13
+ * Add filters
14
  *
15
  * @since 1.6
16
  */
20
  }
21
 
22
  /**
23
+ * Register strings for translation making sure it is not duplicate or empty
24
  *
25
  * @since 0.6
26
  *
30
  * @param bool $multiline Optional, whether the string table should display a multiline textarea or a single line input, defaults to single line
31
  */
32
  static public function register_string( $name, $string, $context = 'Polylang', $multiline = false ) {
33
+ // Backward compatibility with Polylang older than 1.1
34
  if ( is_bool( $context ) ) {
35
  $multiline = $context;
36
  $context = 'Polylang';
42
  }
43
 
44
  /**
45
+ * Get registered strings
46
  *
47
  * @since 0.6.1
48
  *
65
  self::register_string( $string, get_option( $option ), 'WordPress' );
66
  }
67
 
68
+ // Widgets titles
69
  global $wp_registered_widgets;
70
  $sidebars = wp_get_sidebars_widgets();
71
  foreach ( $sidebars as $sidebar => $widgets ) {
74
  }
75
 
76
  foreach ( $widgets as $widget ) {
77
+ // Nothing can be done if the widget is created using pre WP2.8 API :(
78
+ // There is no object, so we can't access it to get the widget options
79
  if ( ! isset( $wp_registered_widgets[ $widget ]['callback'][0] ) || ! is_object( $wp_registered_widgets[ $widget ]['callback'][0] ) || ! method_exists( $wp_registered_widgets[ $widget ]['callback'][0], 'get_settings' ) ) {
80
  continue;
81
  }
83
  $widget_settings = $wp_registered_widgets[ $widget ]['callback'][0]->get_settings();
84
  $number = $wp_registered_widgets[ $widget ]['params'][0]['number'];
85
 
86
+ // Don't enable widget translation if the widget is visible in only one language or if there is no title
87
  if ( empty( $widget_settings[ $number ]['pll_lang'] ) ) {
88
  if ( isset( $widget_settings[ $number ]['title'] ) && $title = $widget_settings[ $number ]['title'] ) {
89
  self::register_string( self::$default_strings['widget_title'], $title, 'Widget' );
109
  }
110
 
111
  /**
112
+ * Performs the sanitization ( before saving in DB ) of default strings translations
113
  *
114
  * @since 1.6
115
  *
admin/admin.php CHANGED
@@ -1,10 +1,10 @@
1
  <?php
2
 
3
  /**
4
- * admin side controller
5
  * accessible in $polylang global object
6
  *
7
- * properties:
8
  * options => inherited, reference to Polylang options array
9
  * model => inherited, reference to PLL_Model object
10
  * links_model => inherited, reference to PLL_Links_Model object
@@ -27,8 +27,8 @@ class PLL_Admin extends PLL_Admin_Base {
27
  public $filters, $filters_columns, $filters_post, $filters_term, $nav_menu, $sync, $filters_media;
28
 
29
  /**
30
- * loads the polylang text domain
31
- * setups filters and action needed on all admin pages and on plugins page
32
  *
33
  * @since 1.2
34
  *
@@ -37,29 +37,29 @@ class PLL_Admin extends PLL_Admin_Base {
37
  public function __construct( &$links_model ) {
38
  parent::__construct( $links_model );
39
 
40
- // adds a 'settings' link in the plugins table
41
  add_filter( 'plugin_action_links_' . POLYLANG_BASENAME, array( $this, 'plugin_action_links' ) );
42
  add_action( 'in_plugin_update_message-' . POLYLANG_BASENAME, array( $this, 'plugin_update_message' ), 10, 2 );
43
  }
44
 
45
  /**
46
- * setups filters and action needed on all admin pages and on plugins page
47
- * loads the settings pages or the filters base on the request
48
  *
49
  * @since 1.2
50
  */
51
  public function init() {
52
  parent::init();
53
 
54
- // setup filters for admin pages
55
- // priority 5 to make sure filters are there before customize_register is fired
56
  if ( $this->model->get_languages_list() ) {
57
  add_action( 'wp_loaded', array( $this, 'add_filters' ), 5 );
58
  }
59
  }
60
 
61
  /**
62
- * adds a 'settings' link in the plugins table
63
  *
64
  * @since 0.1
65
  *
@@ -72,7 +72,7 @@ class PLL_Admin extends PLL_Admin_Base {
72
  }
73
 
74
  /**
75
- * adds the upgrade notice in plugins table
76
  *
77
  * @since 1.1.6
78
  *
@@ -86,15 +86,15 @@ class PLL_Admin extends PLL_Admin_Base {
86
  }
87
 
88
  /**
89
- * setup filters for admin pages
90
  *
91
  * @since 1.2
92
  */
93
  public function add_filters() {
94
- // all these are separated just for convenience and maintainability
95
  $classes = array( 'Filters', 'Filters_Columns', 'Filters_Post', 'Filters_Term', 'Nav_Menu', 'Sync' );
96
 
97
- // don't load media filters if option is disabled or if user has no right
98
  if ( $this->options['media_support'] && ( $obj = get_post_type_object( 'attachment' ) ) && ( current_user_can( $obj->cap->edit_posts ) || current_user_can( $obj->cap->create_posts ) ) ) {
99
  $classes[] = 'Filters_Media';
100
  }
1
  <?php
2
 
3
  /**
4
+ * Admin side controller
5
  * accessible in $polylang global object
6
  *
7
+ * Properties:
8
  * options => inherited, reference to Polylang options array
9
  * model => inherited, reference to PLL_Model object
10
  * links_model => inherited, reference to PLL_Links_Model object
27
  public $filters, $filters_columns, $filters_post, $filters_term, $nav_menu, $sync, $filters_media;
28
 
29
  /**
30
+ * Loads the polylang text domain
31
+ * Setups filters and action needed on all admin pages and on plugins page
32
  *
33
  * @since 1.2
34
  *
37
  public function __construct( &$links_model ) {
38
  parent::__construct( $links_model );
39
 
40
+ // Adds a 'settings' link in the plugins table
41
  add_filter( 'plugin_action_links_' . POLYLANG_BASENAME, array( $this, 'plugin_action_links' ) );
42
  add_action( 'in_plugin_update_message-' . POLYLANG_BASENAME, array( $this, 'plugin_update_message' ), 10, 2 );
43
  }
44
 
45
  /**
46
+ * Aetups filters and action needed on all admin pages and on plugins page
47
+ * Loads the settings pages or the filters base on the request
48
  *
49
  * @since 1.2
50
  */
51
  public function init() {
52
  parent::init();
53
 
54
+ // Setup filters for admin pages
55
+ // Priority 5 to make sure filters are there before customize_register is fired
56
  if ( $this->model->get_languages_list() ) {
57
  add_action( 'wp_loaded', array( $this, 'add_filters' ), 5 );
58
  }
59
  }
60
 
61
  /**
62
+ * Adds a 'settings' link in the plugins table
63
  *
64
  * @since 0.1
65
  *
72
  }
73
 
74
  /**
75
+ * Adds the upgrade notice in plugins table
76
  *
77
  * @since 1.1.6
78
  *
86
  }
87
 
88
  /**
89
+ * Setup filters for admin pages
90
  *
91
  * @since 1.2
92
  */
93
  public function add_filters() {
94
+ // All these are separated just for convenience and maintainability
95
  $classes = array( 'Filters', 'Filters_Columns', 'Filters_Post', 'Filters_Term', 'Nav_Menu', 'Sync' );
96
 
97
+ // Don't load media filters if option is disabled or if user has no right
98
  if ( $this->options['media_support'] && ( $obj = get_post_type_object( 'attachment' ) ) && ( current_user_can( $obj->cap->edit_posts ) || current_user_can( $obj->cap->create_posts ) ) ) {
99
  $classes[] = 'Filters_Media';
100
  }
admin/view-translations-post.php CHANGED
@@ -1,11 +1,11 @@
1
  <?php
2
 
3
  /**
4
- * displays the translations fields for posts
5
  */
6
 
7
  if ( ! defined( 'ABSPATH' ) ) {
8
- exit; // don't access directly
9
  };
10
  ?>
11
  <p><strong><?php esc_html_e( 'Translations', 'polylang' ); ?></strong></p>
1
  <?php
2
 
3
  /**
4
+ * Displays the translations fields for posts
5
  */
6
 
7
  if ( ! defined( 'ABSPATH' ) ) {
8
+ exit; // Don't access directly
9
  };
10
  ?>
11
  <p><strong><?php esc_html_e( 'Translations', 'polylang' ); ?></strong></p>
css/admin.css CHANGED
@@ -266,9 +266,11 @@ td[class*='column-language_'] {
266
  .pll_icon_tick:before {
267
  content: "\f147";
268
  }
 
269
  .pll_icon_add:before {
270
  content: "\f132";
271
  }
 
272
  .pll_icon_edit:before {
273
  content: "\f464";
274
  }
@@ -293,6 +295,16 @@ td[class*='column-language_'] {
293
  top: 1px;
294
  }
295
 
 
 
 
 
 
 
 
 
 
 
296
  @media screen and ( max-width: 782px ) {
297
  /* settings */
298
  #wpbody-content .pll-settings .pll-configure > td {
266
  .pll_icon_tick:before {
267
  content: "\f147";
268
  }
269
+
270
  .pll_icon_add:before {
271
  content: "\f132";
272
  }
273
+
274
  .pll_icon_edit:before {
275
  content: "\f464";
276
  }
295
  top: 1px;
296
  }
297
 
298
+ /* Notices */
299
+ .pll-notice.notice {
300
+ padding-right: 38px;
301
+ position: relative;
302
+ }
303
+
304
+ .pll-notice a.notice-dismiss {
305
+ text-decoration: none;
306
+ }
307
+
308
  @media screen and ( max-width: 782px ) {
309
  /* settings */
310
  #wpbody-content .pll-settings .pll-configure > td {
css/admin.min.css CHANGED
@@ -1 +1 @@
1
- #pll-licenses-table td,.translation label{vertical-align:top}#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"}.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%}.translation input,.translation textarea{width:72%}.pll-settings{margin-top:20px}.pll-settings .plugin-title{width:25%}#wpbody-content .pll-settings .pll-configure tr{display:table-row}#wpbody-content .pll-settings .pll-configure td{display:table-cell}#wpbody-content .pll-settings .pll-configure>td{padding:20px 20px 20px 40px}.pll-configure legend{font-size:14px;font-weight:600;margin-bottom:.5em}.pll-configure td .description{margin-top:2px;margin-bottom:.5em}.pll-configure p.submit{margin-top:20px}.pll-configure .button{margin-right:20px}.pll-configure fieldset{margin-bottom:1.5em}.pll-inline-block-list{margin:0}.pll-inline-block-list li{display:inline-block;margin:0;width:250px}#pll-domains-table td{padding:2px 2px 2px 1.5em;-webkit-box-shadow:none;box-shadow:none;border:none}.pll-settings-url-col{display:inline-block;width:49%;vertical-align:top}#pll-licenses-table label{font-size:1em;font-weight:600}.pll-configure .pll-deactivate-license{margin:0 0 0 20px}td[class*=column-language_],th[class*=column-language_]{width:1.5em}.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-size:12px;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_tick:before{content:"\f147"}.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}@media screen and (max-width:782px){#wpbody-content .pll-settings .pll-configure>td{padding:20px}#wpbody-content .pll-settings #cb{padding:20px 9px}.pll-inline-block{width:auto}.pll-settings-url-col{display:block;width:100%}#wpbody-content .pll-settings #pll-licenses-table td{display:block}.pll-configure .pll-deactivate-license{margin:10px 0 5px}.stringstranslations .column-context,.stringstranslations .column-name{display:none}.translation label{display:block;width:95%;padding-left:0}.translation input{width:95%}#edit-term-translations .pll-language-name,#select-add-term-language .pll-select-flag,#select-edit-term-language .pll-select-flag{display:none}#edit-term-translations{width:100%}#add-term-translations .pll-language-column{line-height:38px}#edit-term-translations td{padding:8px 10px}#edit-term-translations .pll-edit-column,#edit-term-translations .pll-language-column{width:20px}.term-translations .pll-edit-column,.term-translations .pll-language-column,.term-translations .pll-translation-column{display:table-cell}.term-translations .hidden{display:none}#wpadminbar #wp-admin-bar-languages{display:block}#wpadminbar #wp-admin-bar-languages>.ab-item{width:50px;text-align:center}#wpadminbar #wp-admin-bar-languages>.ab-item .ab-icon:before{font:32px/1 dashicons;top:-1px}#wpadminbar #wp-admin-bar-languages>.ab-item img{margin:19px 0}#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon{margin-right:6px;font-size:20px!important;line-height:20px!important}}
1
+ #pll-licenses-table td,.translation label{vertical-align:top}#post-translations a,.pll-notice a.notice-dismiss{text-decoration:none}#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"}.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%}.translation input,.translation textarea{width:72%}.pll-settings{margin-top:20px}.pll-settings .plugin-title{width:25%}#wpbody-content .pll-settings .pll-configure tr{display:table-row}#wpbody-content .pll-settings .pll-configure td{display:table-cell}#wpbody-content .pll-settings .pll-configure>td{padding:20px 20px 20px 40px}.pll-configure legend{font-size:14px;font-weight:600;margin-bottom:.5em}.pll-configure td .description{margin-top:2px;margin-bottom:.5em}.pll-configure p.submit{margin-top:20px}.pll-configure .button{margin-right:20px}.pll-configure fieldset{margin-bottom:1.5em}.pll-inline-block-list{margin:0}.pll-inline-block-list li{display:inline-block;margin:0;width:250px}#pll-domains-table td{padding:2px 2px 2px 1.5em;-webkit-box-shadow:none;box-shadow:none;border:none}.pll-settings-url-col{display:inline-block;width:49%;vertical-align:top}#pll-licenses-table label{font-size:1em;font-weight:600}.pll-configure .pll-deactivate-license{margin:0 0 0 20px}td[class*=column-language_],th[class*=column-language_]{width:1.5em}.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 .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-size:12px;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_tick:before{content:"\f147"}.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}@media screen and (max-width:782px){#wpbody-content .pll-settings .pll-configure>td{padding:20px}#wpbody-content .pll-settings #cb{padding:20px 9px}.pll-inline-block{width:auto}.pll-settings-url-col{display:block;width:100%}#wpbody-content .pll-settings #pll-licenses-table td{display:block}.pll-configure .pll-deactivate-license{margin:10px 0 5px}.stringstranslations .column-context,.stringstranslations .column-name{display:none}.translation label{display:block;width:95%;padding-left:0}.translation input{width:95%}#edit-term-translations .pll-language-name,#select-add-term-language .pll-select-flag,#select-edit-term-language .pll-select-flag{display:none}#edit-term-translations{width:100%}#add-term-translations .pll-language-column{line-height:38px}#edit-term-translations td{padding:8px 10px}#edit-term-translations .pll-edit-column,#edit-term-translations .pll-language-column{width:20px}.term-translations .pll-edit-column,.term-translations .pll-language-column,.term-translations .pll-translation-column{display:table-cell}.term-translations .hidden{display:none}#wpadminbar #wp-admin-bar-languages{display:block}#wpadminbar #wp-admin-bar-languages>.ab-item{width:50px;text-align:center}#wpadminbar #wp-admin-bar-languages>.ab-item .ab-icon:before{font:32px/1 dashicons;top:-1px}#wpadminbar #wp-admin-bar-languages>.ab-item img{margin:19px 0}#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon{margin-right:6px;font-size:20px!important;line-height:20px!important}}
frontend/choose-lang-content.php CHANGED
@@ -9,7 +9,7 @@
9
  class PLL_Choose_Lang_Content extends PLL_Choose_lang {
10
 
11
  /**
12
- * defers the language choice to the 'wp' action (when the content is known)
13
  *
14
  * @since 1.8
15
  */
@@ -17,16 +17,16 @@ class PLL_Choose_Lang_Content extends PLL_Choose_lang {
17
  parent::init();
18
 
19
  if ( ! did_action( 'pll_language_defined' ) ) {
20
- // set the languages from content
21
- add_action( 'wp', array( $this, 'wp' ), 5 ); // priority 5 for post types and taxonomies registered in wp hook with default priority
22
 
23
- // if no language found, choose the preferred one
24
  add_filter( 'pll_get_current_language', array( $this, 'pll_get_current_language' ) );
25
  }
26
  }
27
 
28
  /**
29
- * overwrites parent::set_language to remove the 'wp' action if the language is set before
30
  *
31
  * @since 1.2
32
  *
@@ -38,21 +38,21 @@ class PLL_Choose_Lang_Content extends PLL_Choose_lang {
38
  }
39
 
40
  /**
41
- * returns the language based on the queried content
42
  *
43
  * @since 1.2
44
  *
45
  * @return object|bool detected language, false if none was found
46
  */
47
  protected function get_language_from_content() {
48
- // no language set for 404
49
  if ( is_404() || ( is_attachment() && ! $this->options['media_support'] ) ) {
50
  return $this->get_preferred_language();
51
  }
52
 
53
  if ( $var = get_query_var( 'lang' ) ) {
54
  $lang = explode( ',', $var );
55
- $lang = $this->model->get_language( reset( $lang ) ); // choose the first queried language
56
  }
57
 
58
  elseif ( ( is_single() || is_page() || ( is_attachment() && $this->options['media_support'] ) ) && ( ( $var = get_queried_object_id() ) || ( $var = get_query_var( 'p' ) ) || ( $var = get_query_var( 'page_id' ) ) || ( $var = get_query_var( 'attachment_id' ) ) ) ) {
@@ -78,8 +78,8 @@ class PLL_Choose_Lang_Content extends PLL_Choose_lang {
78
  }
79
 
80
  /**
81
- * sets the language for home page
82
- * add the lang query var when querying archives with no language code
83
  *
84
  * @since 1.2
85
  *
@@ -92,8 +92,8 @@ class PLL_Choose_Lang_Content extends PLL_Choose_lang {
92
 
93
  $qv = $query->query_vars;
94
 
95
- // homepage is requested, let's set the language
96
- // take care to avoid posts page for which is_home = 1
97
  if ( empty( $query->query ) && ( is_home() || is_page() ) ) {
98
  $this->home_language();
99
  $this->home_requested();
@@ -106,8 +106,8 @@ class PLL_Choose_Lang_Content extends PLL_Choose_lang {
106
  $query->is_author ||
107
  ( ! empty( $qv['post_type'] ) && $query->is_post_type_archive && $this->model->is_translated_post_type( $qv['post_type'] ) );
108
 
109
- // sets the language in case we hide the default language
110
- // use $query->query['s'] as is_search is not set when search is empty
111
  // http://wordpress.org/support/topic/search-for-empty-string-in-default-language
112
  if ( $this->options['hide_default'] && ! isset( $qv['lang'] ) && ( $is_archive || isset( $query->query['s'] ) || ( count( $query->query ) == 1 && ! empty( $qv['feed'] ) ) ) ) {
113
  $this->set_language( $this->model->get_language( $this->options['default_lang'] ) );
@@ -116,19 +116,19 @@ class PLL_Choose_Lang_Content extends PLL_Choose_lang {
116
  }
117
 
118
  /**
119
- * sets the language from content
120
  *
121
  * @since 1.2
122
  */
123
  public function wp() {
124
- // nothing to do if the language has already been set ( although normally the filter has been removed )
125
  if ( ! $this->curlang && $curlang = $this->get_language_from_content() ) {
126
  parent::set_language( $curlang );
127
  }
128
  }
129
 
130
  /**
131
- * if no language found by get_language_from_content, return the preferred one
132
  *
133
  * @since 0.9
134
  *
9
  class PLL_Choose_Lang_Content extends PLL_Choose_lang {
10
 
11
  /**
12
+ * Defers the language choice to the 'wp' action (when the content is known)
13
  *
14
  * @since 1.8
15
  */
17
  parent::init();
18
 
19
  if ( ! did_action( 'pll_language_defined' ) ) {
20
+ // Set the languages from content
21
+ add_action( 'wp', array( $this, 'wp' ), 5 ); // Priority 5 for post types and taxonomies registered in wp hook with default priority
22
 
23
+ // If no language found, choose the preferred one
24
  add_filter( 'pll_get_current_language', array( $this, 'pll_get_current_language' ) );
25
  }
26
  }
27
 
28
  /**
29
+ * Overwrites parent::set_language to remove the 'wp' action if the language is set before
30
  *
31
  * @since 1.2
32
  *
38
  }
39
 
40
  /**
41
+ * Returns the language based on the queried content
42
  *
43
  * @since 1.2
44
  *
45
  * @return object|bool detected language, false if none was found
46
  */
47
  protected function get_language_from_content() {
48
+ // No language set for 404
49
  if ( is_404() || ( is_attachment() && ! $this->options['media_support'] ) ) {
50
  return $this->get_preferred_language();
51
  }
52
 
53
  if ( $var = get_query_var( 'lang' ) ) {
54
  $lang = explode( ',', $var );
55
+ $lang = $this->model->get_language( reset( $lang ) ); // Choose the first queried language
56
  }
57
 
58
  elseif ( ( is_single() || is_page() || ( is_attachment() && $this->options['media_support'] ) ) && ( ( $var = get_queried_object_id() ) || ( $var = get_query_var( 'p' ) ) || ( $var = get_query_var( 'page_id' ) ) || ( $var = get_query_var( 'attachment_id' ) ) ) ) {
78
  }
79
 
80
  /**
81
+ * Sets the language for home page
82
+ * Add the lang query var when querying archives with no language code
83
  *
84
  * @since 1.2
85
  *
92
 
93
  $qv = $query->query_vars;
94
 
95
+ // Homepage is requested, let's set the language
96
+ // Take care to avoid posts page for which is_home = 1
97
  if ( empty( $query->query ) && ( is_home() || is_page() ) ) {
98
  $this->home_language();
99
  $this->home_requested();
106
  $query->is_author ||
107
  ( ! empty( $qv['post_type'] ) && $query->is_post_type_archive && $this->model->is_translated_post_type( $qv['post_type'] ) );
108
 
109
+ // Sets the language in case we hide the default language
110
+ // Use $query->query['s'] as is_search is not set when search is empty
111
  // http://wordpress.org/support/topic/search-for-empty-string-in-default-language
112
  if ( $this->options['hide_default'] && ! isset( $qv['lang'] ) && ( $is_archive || isset( $query->query['s'] ) || ( count( $query->query ) == 1 && ! empty( $qv['feed'] ) ) ) ) {
113
  $this->set_language( $this->model->get_language( $this->options['default_lang'] ) );
116
  }
117
 
118
  /**
119
+ * Sets the language from content
120
  *
121
  * @since 1.2
122
  */
123
  public function wp() {
124
+ // Nothing to do if the language has already been set ( although normally the filter has been removed )
125
  if ( ! $this->curlang && $curlang = $this->get_language_from_content() ) {
126
  parent::set_language( $curlang );
127
  }
128
  }
129
 
130
  /**
131
+ * If no language found by get_language_from_content, return the preferred one
132
  *
133
  * @since 0.9
134
  *
frontend/choose-lang-domain.php CHANGED
@@ -8,14 +8,14 @@
8
  class PLL_Choose_Lang_Domain extends PLL_Choose_Lang_Url {
9
 
10
  /**
11
- * don't set any language cookie
12
  *
13
  * @since 1.5
14
  */
15
  public function maybe_setcookie() {}
16
 
17
  /**
18
- * don't redirect according to browser preferences
19
  *
20
  * @since 1.5
21
  */
8
  class PLL_Choose_Lang_Domain extends PLL_Choose_Lang_Url {
9
 
10
  /**
11
+ * Don't set any language cookie
12
  *
13
  * @since 1.5
14
  */
15
  public function maybe_setcookie() {}
16
 
17
  /**
18
+ * Don't redirect according to browser preferences
19
  *
20
  * @since 1.5
21
  */
frontend/choose-lang-url.php CHANGED
@@ -8,10 +8,10 @@
8
  * @since 1.2
9
  */
10
  class PLL_Choose_Lang_Url extends PLL_Choose_lang {
11
- protected $index = 'index.php'; // need this before $wp_rewrite is created, also hardcoded in wp-includes/rewrite.php
12
 
13
  /**
14
- * sets the language
15
  *
16
  * @since 1.8
17
  */
@@ -68,7 +68,7 @@ class PLL_Choose_Lang_Url extends PLL_Choose_lang {
68
 
69
 
70
  /**
71
- * adds the current language in query vars
72
  * useful for subdomains and multiple domains
73
  *
74
  * @since 1.8
@@ -80,14 +80,14 @@ class PLL_Choose_Lang_Url extends PLL_Choose_lang {
80
  // FIXME take care not to break untranslated content
81
  // FIXME media ?
82
 
83
- // untranslated post types
84
  if ( isset( $qv['post_type'] ) && ! $this->model->is_translated_post_type( $qv['post_type'] ) ) {
85
  return $qv;
86
  }
87
 
88
- // untranslated taxonomies
89
- $tax_qv = array_filter( wp_list_pluck( get_taxonomies( array(), 'objects' ), 'query_var' ) ); // get all taxonomies query vars
90
- $tax_qv = array_intersect( $tax_qv, array_keys( $qv ) ); // get all queried taxonomies query vars
91
 
92
  if ( ! $this->model->is_translated_taxonomy( array_keys( $tax_qv ) ) ) {
93
  return $qv;
8
  * @since 1.2
9
  */
10
  class PLL_Choose_Lang_Url extends PLL_Choose_lang {
11
+ protected $index = 'index.php'; // Need this before $wp_rewrite is created, also hardcoded in wp-includes/rewrite.php
12
 
13
  /**
14
+ * Sets the language
15
  *
16
  * @since 1.8
17
  */
68
 
69
 
70
  /**
71
+ * Adds the current language in query vars
72
  * useful for subdomains and multiple domains
73
  *
74
  * @since 1.8
80
  // FIXME take care not to break untranslated content
81
  // FIXME media ?
82
 
83
+ // Untranslated post types
84
  if ( isset( $qv['post_type'] ) && ! $this->model->is_translated_post_type( $qv['post_type'] ) ) {
85
  return $qv;
86
  }
87
 
88
+ // Untranslated taxonomies
89
+ $tax_qv = array_filter( wp_list_pluck( get_taxonomies( array(), 'objects' ), 'query_var' ) ); // Get all taxonomies query vars
90
+ $tax_qv = array_intersect( $tax_qv, array_keys( $qv ) ); // Get all queried taxonomies query vars
91
 
92
  if ( ! $this->model->is_translated_taxonomy( array_keys( $tax_qv ) ) ) {
93
  return $qv;
frontend/choose-lang.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  /**
4
- * base class to choose the language
5
  *
6
  * @since 1.2
7
  */
@@ -10,7 +10,7 @@ abstract class PLL_Choose_Lang {
10
  public $curlang;
11
 
12
  /**
13
- * constructor
14
  *
15
  * @since 1.2
16
  *
@@ -25,9 +25,9 @@ abstract class PLL_Choose_Lang {
25
  }
26
 
27
  /**
28
- * sets the language for ajax requests
29
  * and setup actions
30
- * any child class must call this method if it overrides it
31
  *
32
  * @since 1.8
33
  */
@@ -42,22 +42,22 @@ abstract class PLL_Choose_Lang {
42
  }
43
 
44
  /**
45
- * writes language cookie
46
- * loads user defined translations
47
- * fires the action 'pll_language_defined'
48
  *
49
  * @since 1.2
50
  *
51
  * @param object $curlang current language
52
  */
53
  protected function set_language( $curlang ) {
54
- // don't set the language a second time
55
  if ( isset( $this->curlang ) ) {
56
  return;
57
  }
58
 
59
- // final check in case $curlang has an unexpected value
60
- // see https://wordpress.org/support/topic/detect-browser-language-sometimes-setting-null-language
61
  $this->curlang = ( $curlang instanceof PLL_Language ) ? $curlang : $this->model->get_language( $this->options['default_lang'] );
62
 
63
  $GLOBALS['text_direction'] = $this->curlang->is_rtl ? 'rtl' : 'ltr';
@@ -106,8 +106,8 @@ abstract class PLL_Choose_Lang {
106
  }
107
 
108
  /**
109
- * get the preferred language according to the browser preferences
110
- * code adapted from http://www.thefutureoftheweb.com/blog/use-accept-language-header
111
  *
112
  * @since 1.8
113
  *
@@ -117,26 +117,26 @@ abstract class PLL_Choose_Lang {
117
  $accept_langs = array();
118
 
119
  if ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) {
120
- // break up string into pieces ( languages and q factors )
121
  preg_match_all( '/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*( 1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse );
122
 
123
  $k = $lang_parse[1];
124
  $v = $lang_parse[4];
125
 
126
  if ( $n = count( $k ) ) {
127
- // set default to 1 for any without q factor
128
  foreach ( $v as $key => $val ) {
129
  if ( '' === $val ) {
130
  $v[ $key ] = 1;
131
  }
132
  }
133
 
134
- // bubble sort ( need a stable sort for Android, so can't use a PHP sort function )
135
  if ( $n > 1 ) {
136
  for ( $i = 2; $i <= $n; $i++ ) {
137
  for ( $j = 0; $j <= $n - 2; $j++ ) {
138
  if ( $v[ $j ] < $v[ $j + 1 ] ) {
139
- // swap values
140
  $temp = $v[ $j ];
141
  $v[ $j ] = $v[ $j + 1 ];
142
  $v[ $j + 1 ] = $temp;
@@ -183,7 +183,7 @@ abstract class PLL_Choose_Lang {
183
  }
184
 
185
  /**
186
- * returns the language according to browser preference or the default language
187
  *
188
  * @since 0.1
189
  *
@@ -212,7 +212,7 @@ abstract class PLL_Choose_Lang {
212
  }
213
 
214
  /**
215
- * sets the language when home page is requested
216
  *
217
  * @since 1.2
218
  */
@@ -226,14 +226,14 @@ abstract class PLL_Choose_Lang {
226
  }
227
 
228
  /**
229
- * to call when the home page has been requested
230
- * make sure to call this after 'setup_theme' has been fired as we need $wp_query
231
- * performs a redirection to the home page in the current language if needed
232
  *
233
  * @since 0.9
234
  */
235
  public function home_requested() {
236
- // we are already on the right page
237
  if ( $this->options['default_lang'] == $this->curlang->slug && $this->options['hide_default'] ) {
238
  $this->set_curlang_in_query( $GLOBALS['wp_query'] );
239
 
@@ -244,11 +244,11 @@ abstract class PLL_Choose_Lang {
244
  */
245
  do_action( 'pll_home_requested' );
246
  }
247
- // redirect to the home page in the right language
248
- // test to avoid crash if get_home_url returns something wrong
249
  // FIXME why this happens? http://wordpress.org/support/topic/polylang-crashes-1
250
- // don't redirect if $_POST is not empty as it could break other plugins
251
- // don't forget the query string which may be added by plugins
252
  elseif ( is_string( $redirect = $this->curlang->home_url ) && empty( $_POST ) ) {
253
  $redirect = empty( $_SERVER['QUERY_STRING'] ) ? $redirect : $redirect . ( $this->links_model->using_permalinks ? '?' : '&' ) . $_SERVER['QUERY_STRING'];
254
 
@@ -269,7 +269,7 @@ abstract class PLL_Choose_Lang {
269
  }
270
 
271
  /**
272
- * set the language when posting a comment
273
  *
274
  * @since 0.8.4
275
  *
@@ -280,7 +280,7 @@ abstract class PLL_Choose_Lang {
280
  }
281
 
282
  /**
283
- * modifies some main query vars for home page and page for posts
284
  * to enable one home page ( and one page for posts ) per language
285
  *
286
  * @since 1.2
1
  <?php
2
 
3
  /**
4
+ * Base class to choose the language
5
  *
6
  * @since 1.2
7
  */
10
  public $curlang;
11
 
12
  /**
13
+ * Constructor
14
  *
15
  * @since 1.2
16
  *
25
  }
26
 
27
  /**
28
+ * Sets the language for ajax requests
29
  * and setup actions
30
+ * Any child class must call this method if it overrides it
31
  *
32
  * @since 1.8
33
  */
42
  }
43
 
44
  /**
45
+ * Writes language cookie
46
+ * Loads user defined translations
47
+ * Fires the action 'pll_language_defined'
48
  *
49
  * @since 1.2
50
  *
51
  * @param object $curlang current language
52
  */
53
  protected function set_language( $curlang ) {
54
+ // Don't set the language a second time
55
  if ( isset( $this->curlang ) ) {
56
  return;
57
  }
58
 
59
+ // Final check in case $curlang has an unexpected value
60
+ // See https://wordpress.org/support/topic/detect-browser-language-sometimes-setting-null-language
61
  $this->curlang = ( $curlang instanceof PLL_Language ) ? $curlang : $this->model->get_language( $this->options['default_lang'] );
62
 
63
  $GLOBALS['text_direction'] = $this->curlang->is_rtl ? 'rtl' : 'ltr';
106
  }
107
 
108
  /**
109
+ * Get the preferred language according to the browser preferences
110
+ * Code adapted from http://www.thefutureoftheweb.com/blog/use-accept-language-header
111
  *
112
  * @since 1.8
113
  *
117
  $accept_langs = array();
118
 
119
  if ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) {
120
+ // Break up string into pieces ( languages and q factors )
121
  preg_match_all( '/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*( 1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse );
122
 
123
  $k = $lang_parse[1];
124
  $v = $lang_parse[4];
125
 
126
  if ( $n = count( $k ) ) {
127
+ // Set default to 1 for any without q factor
128
  foreach ( $v as $key => $val ) {
129
  if ( '' === $val ) {
130
  $v[ $key ] = 1;
131
  }
132
  }
133
 
134
+ // Bubble sort ( need a stable sort for Android, so can't use a PHP sort function )
135
  if ( $n > 1 ) {
136
  for ( $i = 2; $i <= $n; $i++ ) {
137
  for ( $j = 0; $j <= $n - 2; $j++ ) {
138
  if ( $v[ $j ] < $v[ $j + 1 ] ) {
139
+ // Swap values
140
  $temp = $v[ $j ];
141
  $v[ $j ] = $v[ $j + 1 ];
142
  $v[ $j + 1 ] = $temp;
183
  }
184
 
185
  /**
186
+ * Returns the language according to browser preference or the default language
187
  *
188
  * @since 0.1
189
  *
212
  }
213
 
214
  /**
215
+ * Sets the language when home page is requested
216
  *
217
  * @since 1.2
218
  */
226
  }
227
 
228
  /**
229
+ * To call when the home page has been requested
230
+ * Make sure to call this after 'setup_theme' has been fired as we need $wp_query
231
+ * Performs a redirection to the home page in the current language if needed
232
  *
233
  * @since 0.9
234
  */
235
  public function home_requested() {
236
+ // We are already on the right page
237
  if ( $this->options['default_lang'] == $this->curlang->slug && $this->options['hide_default'] ) {
238
  $this->set_curlang_in_query( $GLOBALS['wp_query'] );
239
 
244
  */
245
  do_action( 'pll_home_requested' );
246
  }
247
+ // Redirect to the home page in the right language
248
+ // Test to avoid crash if get_home_url returns something wrong
249
  // FIXME why this happens? http://wordpress.org/support/topic/polylang-crashes-1
250
+ // Don't redirect if $_POST is not empty as it could break other plugins
251
+ // Don't forget the query string which may be added by plugins
252
  elseif ( is_string( $redirect = $this->curlang->home_url ) && empty( $_POST ) ) {
253
  $redirect = empty( $_SERVER['QUERY_STRING'] ) ? $redirect : $redirect . ( $this->links_model->using_permalinks ? '?' : '&' ) . $_SERVER['QUERY_STRING'];
254
 
269
  }
270
 
271
  /**
272
+ * Set the language when posting a comment
273
  *
274
  * @since 0.8.4
275
  *
280
  }
281
 
282
  /**
283
+ * Modifies some main query vars for home page and page for posts
284
  * to enable one home page ( and one page for posts ) per language
285
  *
286
  * @since 1.2
frontend/frontend-filters-search.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  /**
4
- * filters search forms when using permalinks
5
  *
6
  * @since 1.2
7
  */
@@ -9,7 +9,7 @@ class PLL_Frontend_Filters_Search {
9
  public $links_model, $curlang;
10
 
11
  /**
12
- * constructor
13
  *
14
  * @since 1.2
15
  *
@@ -19,33 +19,33 @@ class PLL_Frontend_Filters_Search {
19
  $this->links_model = &$polylang->links_model;
20
  $this->curlang = &$polylang->curlang;
21
 
22
- // adds the language information in the search form
23
- // low priority in case the search form is created using the same filter as described in http://codex.wordpress.org/Function_Reference/get_search_form
24
  add_filter( 'get_search_form', array( $this, 'get_search_form' ), 99 );
25
 
26
- // adds the language information in admin bar search form
27
  add_action( 'add_admin_bar_menus', array( $this, 'add_admin_bar_menus' ) );
28
 
29
- // adds javascript at the end of the document
30
- // was used for WP < 3.6. kept just in case
31
  if ( defined( 'PLL_SEARCH_FORM_JS' ) && PLL_SEARCH_FORM_JS ) {
32
  add_action( 'wp_footer', array( $this, 'wp_print_footer_scripts' ) );
33
  }
34
  }
35
 
36
  /**
37
- * adds the language information in the search form
38
- * does not work if searchform.php ( prior to WP 3.6 ) is used or if the search form is hardcoded in another template file
39
  *
40
  * @since 0.1
41
  *
42
- * @param string $form search form
43
- * @return string modified search form
44
  */
45
  public function get_search_form( $form ) {
46
  if ( $form ) {
47
  if ( $this->links_model->using_permalinks ) {
48
- // take care to modify only the url in the <form> tag
49
  preg_match( '#<form.+>#', $form, $matches );
50
  $old = reset( $matches );
51
  $new = preg_replace( '#' . esc_url( $this->links_model->home ) . '\/?#', esc_url( $this->curlang->search_url ), $old );
@@ -60,7 +60,7 @@ class PLL_Frontend_Filters_Search {
60
  }
61
 
62
  /**
63
- * adds the language information in admin bar search form
64
  *
65
  * @since 1.2
66
  */
@@ -70,12 +70,12 @@ class PLL_Frontend_Filters_Search {
70
  }
71
 
72
  /**
73
- * rewrites the admin bar search form to pass our get_search form filter. See #21342
74
- * code base is WP 4.3.1
75
  *
76
  * @since 0.9
77
  *
78
- * @param object $wp_admin_bar
79
  */
80
  public function admin_bar_search_menu( $wp_admin_bar ) {
81
  $form = '<form action="' . esc_url( home_url( '/' ) ) . '" method="get" id="adminbarsearch">';
@@ -87,20 +87,23 @@ class PLL_Frontend_Filters_Search {
87
  $wp_admin_bar->add_menu( array(
88
  'parent' => 'top-secondary',
89
  'id' => 'search',
90
- 'title' => $this->get_search_form( $form ), // pass the get_search_form filter
91
- 'meta' => array( 'class' => 'admin-bar-search', 'tabindex' => -1 ),
 
 
 
92
  ) );
93
  }
94
 
95
  /**
96
- * allows modifying the search form if it does not pass get_search_form
97
  *
98
  * @since 0.1
99
  */
100
  public function wp_print_footer_scripts() {
101
- // don't use directly e[0] just in case there is somewhere else an element named 's'
102
- // check before if the hidden input has not already been introduced by get_search_form ( FIXME: is there a way to improve this ) ?
103
- // thanks to AndyDeGroo for improving the code for compatibility with old browsers
104
  // http://wordpress.org/support/topic/development-of-polylang-version-08?replies=6#post-2645559
105
  $lang = esc_js( $this->curlang->slug );
106
  $js = "//<![CDATA[
1
  <?php
2
 
3
  /**
4
+ * Filters search forms when using permalinks
5
  *
6
  * @since 1.2
7
  */
9
  public $links_model, $curlang;
10
 
11
  /**
12
+ * Constructor
13
  *
14
  * @since 1.2
15
  *
19
  $this->links_model = &$polylang->links_model;
20
  $this->curlang = &$polylang->curlang;
21
 
22
+ // Adds the language information in the search form
23
+ // Low priority in case the search form is created using the same filter as described in http://codex.wordpress.org/Function_Reference/get_search_form
24
  add_filter( 'get_search_form', array( $this, 'get_search_form' ), 99 );
25
 
26
+ // Adds the language information in admin bar search form
27
  add_action( 'add_admin_bar_menus', array( $this, 'add_admin_bar_menus' ) );
28
 
29
+ // Adds javascript at the end of the document
30
+ // Was used for WP < 3.6. kept just in case
31
  if ( defined( 'PLL_SEARCH_FORM_JS' ) && PLL_SEARCH_FORM_JS ) {
32
  add_action( 'wp_footer', array( $this, 'wp_print_footer_scripts' ) );
33
  }
34
  }
35
 
36
  /**
37
+ * Adds the language information in the search form
38
+ * Does not work if searchform.php ( prior to WP 3.6 ) is used or if the search form is hardcoded in another template file
39
  *
40
  * @since 0.1
41
  *
42
+ * @param string $form Search form
43
+ * @return string Modified search form
44
  */
45
  public function get_search_form( $form ) {
46
  if ( $form ) {
47
  if ( $this->links_model->using_permalinks ) {
48
+ // Take care to modify only the url in the <form> tag.
49
  preg_match( '#<form.+>#', $form, $matches );
50
  $old = reset( $matches );
51
  $new = preg_replace( '#' . esc_url( $this->links_model->home ) . '\/?#', esc_url( $this->curlang->search_url ), $old );
60
  }
61
 
62
  /**
63
+ * Adds the language information in admin bar search form
64
  *
65
  * @since 1.2
66
  */
70
  }
71
 
72
  /**
73
+ * Rewrites the admin bar search form to pass our get_search form filter. See #21342
74
+ * Code base last checked with WP 4.9.7
75
  *
76
  * @since 0.9
77
  *
78
+ * @param WP_Admin_Bar $wp_admin_bar
79
  */
80
  public function admin_bar_search_menu( $wp_admin_bar ) {
81
  $form = '<form action="' . esc_url( home_url( '/' ) ) . '" method="get" id="adminbarsearch">';
87
  $wp_admin_bar->add_menu( array(
88
  'parent' => 'top-secondary',
89
  'id' => 'search',
90
+ 'title' => $this->get_search_form( $form ), // Pass the get_search_form filter
91
+ 'meta' => array(
92
+ 'class' => 'admin-bar-search',
93
+ 'tabindex' => -1,
94
+ ),
95
  ) );
96
  }
97
 
98
  /**
99
+ * Allows modifying the search form if it does not pass get_search_form
100
  *
101
  * @since 0.1
102
  */
103
  public function wp_print_footer_scripts() {
104
+ // Don't use directly e[0] just in case there is somewhere else an element named 's'
105
+ // Check before if the hidden input has not already been introduced by get_search_form ( FIXME: is there a way to improve this ) ?
106
+ // Thanks to AndyDeGroo for improving the code for compatibility with old browsers
107
  // http://wordpress.org/support/topic/development-of-polylang-version-08?replies=6#post-2645559
108
  $lang = esc_js( $this->curlang->slug );
109
  $js = "//<![CDATA[
frontend/frontend.php CHANGED
@@ -111,7 +111,7 @@ class PLL_Frontend extends PLL_Base {
111
  }
112
 
113
  /**
114
- * mMdifies some query vars to "hide" that the language is a taxonomy and avoid conflicts
115
  *
116
  * @since 1.2
117
  *
111
  }
112
 
113
  /**
114
+ * Modifies some query vars to "hide" that the language is a taxonomy and avoid conflicts
115
  *
116
  * @since 1.2
117
  *
include/base.php CHANGED
@@ -89,6 +89,7 @@ abstract class PLL_Base {
89
  // 3rd test needed when Polylang is networked activated and a new site is created
90
  if ( $new_blog != $old_blog && in_array( POLYLANG_BASENAME, $plugins ) && get_option( 'polylang' ) ) {
91
  $this->options = get_option( 'polylang' ); // Needed for menus
 
92
  $this->links_model = $this->model->get_links_model();
93
  return true;
94
  }
89
  // 3rd test needed when Polylang is networked activated and a new site is created
90
  if ( $new_blog != $old_blog && in_array( POLYLANG_BASENAME, $plugins ) && get_option( 'polylang' ) ) {
91
  $this->options = get_option( 'polylang' ); // Needed for menus
92
+ remove_action( 'pre_option_rewrite_rules', array( $this->links_model, 'prepare_rewrite_rules' ) );
93
  $this->links_model = $this->model->get_links_model();
94
  return true;
95
  }
include/filters.php CHANGED
@@ -300,7 +300,7 @@ class PLL_Filters {
300
  * @return array
301
  */
302
  public function fix_privacy_policy_page_editing( $caps, $cap, $user_id, $args ) {
303
- if ( in_array( $cap, array( 'edit_page', 'edit_post', 'delete_page', 'delete_post' ) ) ) {
304
  $privacy_page = get_option( 'wp_page_for_privacy_policy' );
305
  if ( $privacy_page && array_intersect( $args, $this->model->post->get_translations( $privacy_page ) ) ) {
306
  $caps = array_merge( $caps, map_meta_cap( 'manage_privacy_options', $user_id ) );
300
  * @return array
301
  */
302
  public function fix_privacy_policy_page_editing( $caps, $cap, $user_id, $args ) {
303
+ if ( in_array( $cap, array( 'edit_page', 'edit_post', 'delete_page', 'delete_post' ) ) ) {
304
  $privacy_page = get_option( 'wp_page_for_privacy_policy' );
305
  if ( $privacy_page && array_intersect( $args, $this->model->post->get_translations( $privacy_page ) ) ) {
306
  $caps = array_merge( $caps, map_meta_cap( 'manage_privacy_options', $user_id ) );
include/language.php CHANGED
@@ -90,7 +90,7 @@ class PLL_Language {
90
  }
91
 
92
  /**
93
- * sets flag_url and flag properties
94
  *
95
  * @since 1.2
96
  */
90
  }
91
 
92
  /**
93
+ * Sets flag_url and flag properties
94
  *
95
  * @since 1.2
96
  */
include/model.php CHANGED
@@ -6,9 +6,9 @@
6
  * @since 1.2
7
  */
8
  class PLL_Model {
9
- public $cache; // our internal non persistent cache object
10
  public $options;
11
- public $post, $term; // translated objects models
12
 
13
  /**
14
  * Constructor
@@ -26,7 +26,7 @@ class PLL_Model {
26
  $this->post = new PLL_Translated_Post( $this ); // translated post sub model
27
  $this->term = new PLL_Translated_Term( $this ); // translated term sub model
28
 
29
- // we need to clean languages cache when editing a language and when modifying the permalink structure
30
  add_action( 'edited_term_taxonomy', array( $this, 'clean_languages_cache' ), 10, 2 );
31
  add_action( 'update_option_permalink_structure', array( $this, 'clean_languages_cache' ) );
32
  add_action( 'update_option_siteurl', array( $this, 'clean_languages_cache' ) );
@@ -34,7 +34,7 @@ class PLL_Model {
34
 
35
  add_filter( 'get_terms_args', array( $this, 'get_terms_args' ) );
36
 
37
- // just in case someone would like to display the language description ;- )
38
  add_filter( 'language_description', '__return_empty_string' );
39
  }
40
 
@@ -43,7 +43,7 @@ class PLL_Model {
43
  * caches the list in a db transient ( except flags ), unless PLL_CACHE_LANGUAGES is set to false
44
  * caches the list ( with flags ) in the private property $languages
45
  *
46
- * list of parameters accepted in $args:
47
  *
48
  * hide_empty => hides languages with no posts if set to true ( defaults to false )
49
  * fields => return only that field if set ( see PLL_Language for a list of fields )
@@ -56,7 +56,7 @@ class PLL_Model {
56
  public function get_languages_list( $args = array() ) {
57
  if ( false === $languages = $this->cache->get( 'languages' ) ) {
58
 
59
- // create the languages from taxonomies
60
  if ( ( defined( 'PLL_CACHE_LANGUAGES' ) && ! PLL_CACHE_LANGUAGES ) || false === ( $languages = get_transient( 'pll_languages_list' ) ) ) {
61
  $languages = get_terms( 'language', array( 'hide_empty' => false, 'orderby' => 'term_group' ) );
62
  $languages = empty( $languages ) || is_wp_error( $languages ) ? array() : $languages;
@@ -66,12 +66,12 @@ class PLL_Model {
66
  array() : array_combine( wp_list_pluck( $term_languages, 'slug' ), $term_languages );
67
 
68
  if ( ! empty( $languages ) && ! empty( $term_languages ) ) {
69
- // don't use array_map + create_function to instantiate an autoloaded class as it breaks badly in old versions of PHP
70
  foreach ( $languages as $k => $v ) {
71
  $languages[ $k ] = new PLL_Language( $v, $term_languages[ 'pll_' . $v->slug ] );
72
  }
73
 
74
- // we will need the languages list to allow its access in the filter below
75
  $this->cache->set( 'languages', $languages );
76
 
77
  /**
@@ -85,24 +85,24 @@ class PLL_Model {
85
  */
86
  $languages = apply_filters( 'pll_languages_list', $languages, $this );
87
 
88
- // don't store directly objects as it badly break with some hosts ( GoDaddy ) due to race conditions when using object cache
89
- // thanks to captin411 for catching this!
90
- // see https://wordpress.org/support/topic/fatal-error-pll_model_languages_list?replies=8#post-6782255;
91
  set_transient( 'pll_languages_list', array_map( 'get_object_vars', $languages ) );
92
  }
93
  else {
94
- $languages = array(); // in case something went wrong
95
  }
96
  }
97
 
98
- // create the languages directly from arrays stored in transients
99
  else {
100
  foreach ( $languages as $k => $v ) {
101
  $languages[ $k ] = new PLL_Language( $v );
102
  }
103
  }
104
 
105
- // custom flags
106
  if ( ! PLL_ADMIN ) {
107
  foreach ( $languages as $language ) {
108
  $language->set_custom_flag();
@@ -123,7 +123,7 @@ class PLL_Model {
123
 
124
  $args = wp_parse_args( $args, array( 'hide_empty' => false ) );
125
 
126
- // remove empty languages if requested
127
  if ( $args['hide_empty'] ) {
128
  $languages = wp_list_filter( $languages, array( 'count' => 0 ), 'NOT' );
129
  }
6
  * @since 1.2
7
  */
8
  class PLL_Model {
9
+ public $cache; // Our internal non persistent cache object
10
  public $options;
11
+ public $post, $term; // Translated objects models
12
 
13
  /**
14
  * Constructor
26
  $this->post = new PLL_Translated_Post( $this ); // translated post sub model
27
  $this->term = new PLL_Translated_Term( $this ); // translated term sub model
28
 
29
+ // We need to clean languages cache when editing a language and when modifying the permalink structure
30
  add_action( 'edited_term_taxonomy', array( $this, 'clean_languages_cache' ), 10, 2 );
31
  add_action( 'update_option_permalink_structure', array( $this, 'clean_languages_cache' ) );
32
  add_action( 'update_option_siteurl', array( $this, 'clean_languages_cache' ) );
34
 
35
  add_filter( 'get_terms_args', array( $this, 'get_terms_args' ) );
36
 
37
+ // Just in case someone would like to display the language description ;- )
38
  add_filter( 'language_description', '__return_empty_string' );
39
  }
40
 
43
  * caches the list in a db transient ( except flags ), unless PLL_CACHE_LANGUAGES is set to false
44
  * caches the list ( with flags ) in the private property $languages
45
  *
46
+ * List of parameters accepted in $args:
47
  *
48
  * hide_empty => hides languages with no posts if set to true ( defaults to false )
49
  * fields => return only that field if set ( see PLL_Language for a list of fields )
56
  public function get_languages_list( $args = array() ) {
57
  if ( false === $languages = $this->cache->get( 'languages' ) ) {
58
 
59
+ // Create the languages from taxonomies
60
  if ( ( defined( 'PLL_CACHE_LANGUAGES' ) && ! PLL_CACHE_LANGUAGES ) || false === ( $languages = get_transient( 'pll_languages_list' ) ) ) {
61
  $languages = get_terms( 'language', array( 'hide_empty' => false, 'orderby' => 'term_group' ) );
62
  $languages = empty( $languages ) || is_wp_error( $languages ) ? array() : $languages;
66
  array() : array_combine( wp_list_pluck( $term_languages, 'slug' ), $term_languages );
67
 
68
  if ( ! empty( $languages ) && ! empty( $term_languages ) ) {
69
+ // Don't use array_map + create_function to instantiate an autoloaded class as it breaks badly in old versions of PHP
70
  foreach ( $languages as $k => $v ) {
71
  $languages[ $k ] = new PLL_Language( $v, $term_languages[ 'pll_' . $v->slug ] );
72
  }
73
 
74
+ // We will need the languages list to allow its access in the filter below
75
  $this->cache->set( 'languages', $languages );
76
 
77
  /**
85
  */
86
  $languages = apply_filters( 'pll_languages_list', $languages, $this );
87
 
88
+ // Don't store directly objects as it badly break with some hosts ( GoDaddy ) due to race conditions when using object cache
89
+ // Thanks to captin411 for catching this!
90
+ // See https://wordpress.org/support/topic/fatal-error-pll_model_languages_list?replies=8#post-6782255;
91
  set_transient( 'pll_languages_list', array_map( 'get_object_vars', $languages ) );
92
  }
93
  else {
94
+ $languages = array(); // In case something went wrong
95
  }
96
  }
97
 
98
+ // Create the languages directly from arrays stored in transients
99
  else {
100
  foreach ( $languages as $k => $v ) {
101
  $languages[ $k ] = new PLL_Language( $v );
102
  }
103
  }
104
 
105
+ // Custom flags
106
  if ( ! PLL_ADMIN ) {
107
  foreach ( $languages as $language ) {
108
  $language->set_custom_flag();
123
 
124
  $args = wp_parse_args( $args, array( 'hide_empty' => false ) );
125
 
126
+ // Remove empty languages if requested
127
  if ( $args['hide_empty'] ) {
128
  $languages = wp_list_filter( $languages, array( 'count' => 0 ), 'NOT' );
129
  }
include/nav-menu.php CHANGED
@@ -1,8 +1,8 @@
1
  <?php
2
 
3
  /**
4
- * manages custom menus translations
5
- * common to admin and frontend for the customizer
6
  *
7
  * @since 1.7.7
8
  */
@@ -10,7 +10,7 @@ class PLL_Nav_Menu {
10
  public $model, $options;
11
 
12
  /**
13
- * constructor: setups filters and actions
14
  *
15
  * @since 1.7.7
16
  *
@@ -20,12 +20,12 @@ class PLL_Nav_Menu {
20
  $this->model = &$polylang->model;
21
  $this->options = &$polylang->options;
22
 
23
- // integration with WP customizer
24
  add_action( 'customize_register', array( $this, 'create_nav_menu_locations' ), 5 );
25
  }
26
 
27
  /**
28
- * create temporary nav menu locations ( one per location and per language ) for all non-default language
29
  * to do only one time
30
  *
31
  * @since 1.2
@@ -47,7 +47,7 @@ class PLL_Nav_Menu {
47
  }
48
 
49
  /**
50
- * creates a temporary nav menu location from a location and a language
51
  *
52
  * @since 1.8
53
  *
@@ -60,7 +60,7 @@ class PLL_Nav_Menu {
60
  }
61
 
62
  /**
63
- * get nav menu locations and language from a temporary location
64
  *
65
  * @since 1.8
66
  *
1
  <?php
2
 
3
  /**
4
+ * Manages custom menus translations
5
+ * Common to admin and frontend for the customizer
6
  *
7
  * @since 1.7.7
8
  */
10
  public $model, $options;
11
 
12
  /**
13
+ * Constructor: setups filters and actions
14
  *
15
  * @since 1.7.7
16
  *
20
  $this->model = &$polylang->model;
21
  $this->options = &$polylang->options;
22
 
23
+ // Integration with WP customizer
24
  add_action( 'customize_register', array( $this, 'create_nav_menu_locations' ), 5 );
25
  }
26
 
27
  /**
28
+ * Create temporary nav menu locations ( one per location and per language ) for all non-default language
29
  * to do only one time
30
  *
31
  * @since 1.2
47
  }
48
 
49
  /**
50
+ * Creates a temporary nav menu location from a location and a language
51
  *
52
  * @since 1.8
53
  *
60
  }
61
 
62
  /**
63
+ * Get nav menu locations and language from a temporary location
64
  *
65
  * @since 1.8
66
  *
include/pointer.php CHANGED
@@ -1,19 +1,20 @@
1
  <?php
2
 
3
  /**
4
- * a class to manage WP pointers
5
  * offers the possibility to have customized buttons
6
  *
7
  * @since 1.7.7
 
8
  */
9
  class PLL_Pointer {
10
  protected $args;
11
 
12
  /**
13
- * constructor
14
  * enqueues the pointer script
15
  *
16
- * list of parameters accepted in $args:
17
  *
18
  * pointer => required, unique identifier of the pointer
19
  * id => required, the pointer will be attached to this html id
@@ -32,12 +33,14 @@ class PLL_Pointer {
32
  * @param array $args
33
  */
34
  public function __construct( $args ) {
 
 
35
  $this->args = $args;
36
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
37
  }
38
 
39
  /**
40
- * enqueue javascripts and styles if the pointer has not been dismissed
41
  *
42
  * @since 1.7.7
43
  */
@@ -55,20 +58,20 @@ class PLL_Pointer {
55
  }
56
 
57
  /**
58
- * adds the javascript of our pointer to the page
59
  *
60
  * @since 1.7.7
61
  */
62
  public function print_js() {
63
 
64
- // add optional buttons
65
  if ( ! empty( $this->args['buttons'] ) ) {
66
  $b = "
67
  var widget = pointer.pointer( 'widget' );
68
  var buttons = $( '.wp-pointer-buttons', widget );
69
  $( 'a.close', widget ).remove();"; // removes the WP button
70
 
71
- // all the buttons use the standard WP ajax action to remember the pointer has been dismissed
72
  foreach ( $this->args['buttons'] as $button ) {
73
  $b .= sprintf( "
74
  $( '<a>' ).addClass( '%s' ).html( '%s' ).css( 'margin-left', '10px' ).click( function() {
1
  <?php
2
 
3
  /**
4
+ * A class to manage WP pointers
5
  * offers the possibility to have customized buttons
6
  *
7
  * @since 1.7.7
8
+ * @deprecated 2.3.9
9
  */
10
  class PLL_Pointer {
11
  protected $args;
12
 
13
  /**
14
+ * Constructor
15
  * enqueues the pointer script
16
  *
17
+ * List of parameters accepted in $args:
18
  *
19
  * pointer => required, unique identifier of the pointer
20
  * id => required, the pointer will be attached to this html id
33
  * @param array $args
34
  */
35
  public function __construct( $args ) {
36
+ 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 );
37
+
38
  $this->args = $args;
39
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
40
  }
41
 
42
  /**
43
+ * Enqueue javascripts and styles if the pointer has not been dismissed
44
  *
45
  * @since 1.7.7
46
  */
58
  }
59
 
60
  /**
61
+ * Adds the javascript of our pointer to the page
62
  *
63
  * @since 1.7.7
64
  */
65
  public function print_js() {
66
 
67
+ // Add optional buttons
68
  if ( ! empty( $this->args['buttons'] ) ) {
69
  $b = "
70
  var widget = pointer.pointer( 'widget' );
71
  var buttons = $( '.wp-pointer-buttons', widget );
72
  $( 'a.close', widget ).remove();"; // removes the WP button
73
 
74
+ // All the buttons use the standard WP ajax action to remember the pointer has been dismissed
75
  foreach ( $this->args['buttons'] as $button ) {
76
  $b .= sprintf( "
77
  $( '<a>' ).addClass( '%s' ).html( '%s' ).css( 'margin-left', '10px' ).click( function() {
include/query.php CHANGED
@@ -46,6 +46,7 @@ class PLL_Query {
46
  /**
47
  * Check if translated taxonomy is queried
48
  * Compatible with nested queries introduced in WP 4.1
 
49
  * @see https://wordpress.org/support/topic/tax_query-bug
50
  *
51
  * @since 1.7
46
  /**
47
  * Check if translated taxonomy is queried
48
  * Compatible with nested queries introduced in WP 4.1
49
+ *
50
  * @see https://wordpress.org/support/topic/tax_query-bug
51
  *
52
  * @since 1.7
include/static-pages.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  /**
4
- * base class to manage the static front page and the page for posts
5
  *
6
  * @since 1.8
7
  */
@@ -10,7 +10,7 @@ abstract class PLL_Static_Pages {
10
  public $page_on_front, $page_for_posts;
11
 
12
  /**
13
- * constructor: setups filters and actions
14
  *
15
  * @since 1.8
16
  *
@@ -26,17 +26,17 @@ abstract class PLL_Static_Pages {
26
  // Modifies the page link in case the front page is not in the default language
27
  add_filter( 'page_link', array( $this, 'page_link' ), 20, 2 );
28
 
29
- // clean the languages cache when editing page of front, page for posts
30
  add_action( 'update_option_show_on_front', array( $this->model, 'clean_languages_cache' ) );
31
  add_action( 'update_option_page_on_front', array( $this->model, 'clean_languages_cache' ) );
32
  add_action( 'update_option_page_for_posts', array( $this->model, 'clean_languages_cache' ) );
33
 
34
- // refresh rewrite rules when the page on front is modified
35
  add_action( 'update_option_page_on_front', 'flush_rewrite_rules' );
36
  }
37
 
38
  /**
39
- * stores the page on front and page for posts ids
40
  *
41
  * @since 1.8
42
  */
@@ -69,7 +69,7 @@ abstract class PLL_Static_Pages {
69
  }
70
 
71
  /**
72
- * adds page_on_front and page_for_posts properties to the language objects
73
  *
74
  * @since 1.8
75
  *
1
  <?php
2
 
3
  /**
4
+ * Base class to manage the static front page and the page for posts
5
  *
6
  * @since 1.8
7
  */
10
  public $page_on_front, $page_for_posts;
11
 
12
  /**
13
+ * Constructor: setups filters and actions
14
  *
15
  * @since 1.8
16
  *
26
  // Modifies the page link in case the front page is not in the default language
27
  add_filter( 'page_link', array( $this, 'page_link' ), 20, 2 );
28
 
29
+ // Clean the languages cache when editing page of front, page for posts
30
  add_action( 'update_option_show_on_front', array( $this->model, 'clean_languages_cache' ) );
31
  add_action( 'update_option_page_on_front', array( $this->model, 'clean_languages_cache' ) );
32
  add_action( 'update_option_page_for_posts', array( $this->model, 'clean_languages_cache' ) );
33
 
34
+ // Refresh rewrite rules when the page on front is modified
35
  add_action( 'update_option_page_on_front', 'flush_rewrite_rules' );
36
  }
37
 
38
  /**
39
+ * Stores the page on front and page for posts ids
40
  *
41
  * @since 1.8
42
  */
69
  }
70
 
71
  /**
72
+ * Adds page_on_front and page_for_posts properties to the language objects
73
  *
74
  * @since 1.8
75
  *
include/switcher.php CHANGED
@@ -33,8 +33,7 @@ class PLL_Switcher {
33
  /**
34
  * Get the language elements for use in a walker
35
  *
36
- * List of parameters accepted in $args:
37
- * @see PLL_Switcher::the_languages
38
  *
39
  * @since 1.2
40
  *
33
  /**
34
  * Get the language elements for use in a walker
35
  *
36
+ * @see list of parameters accepted in $args documented for PLL_Switcher::the_languages
 
37
  *
38
  * @since 1.2
39
  *
include/widget-calendar.php CHANGED
@@ -5,40 +5,49 @@ if ( ! class_exists( 'WP_Widget_Calendar' ) ) {
5
  }
6
 
7
  /**
8
- * obliged to rewrite the whole functionality as there is no filter on sql queries and only a filter on final output
9
- * code base last checked with WP 4.4.2
10
- * a request for making a filter on sql queries exists: http://core.trac.wordpress.org/ticket/15202
11
- * method used in 0.4.x: use of the get_calendar filter and overwrite the output of get_calendar function -> not very efficient (add 4 to 5 sql queries)
12
- * method used since 0.5: remove the WP widget and replace it by our own -> our language filter will not work if get_calendar is called directly by a theme
13
  *
14
  * @since 0.5
15
  */
16
  class PLL_Widget_Calendar extends WP_Widget_Calendar {
17
 
18
  /**
19
- * displays the widget
20
- * modified version of the parent function to call our own get_calendar function
21
  *
22
  * @since 0.5
23
  *
24
  * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
25
- * @param array $instance The settings for the particular instance of the widget
26
  */
27
- function widget( $args, $instance ) {
 
 
28
  /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
29
- $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '&nbsp;' : $instance['title'], $instance, $this->id_base );
 
30
  echo $args['before_widget'];
31
  if ( $title ) {
32
  echo $args['before_title'] . $title . $args['after_title'];
33
  }
34
- echo '<div id="calendar_wrap">';
 
 
 
 
35
  empty( PLL()->curlang ) ? get_calendar() : self::get_calendar(); #modified#
36
  echo '</div>';
37
  echo $args['after_widget'];
 
 
38
  }
39
 
40
  /**
41
- * modified version of WP get_calendar function to filter the query
42
  *
43
  * @since 0.5
44
  *
@@ -46,7 +55,7 @@ class PLL_Widget_Calendar extends WP_Widget_Calendar {
46
  * @param bool $echo Optional, default is true. Set to false for return.
47
  * @return string|null String when retrieving, null when displaying.
48
  */
49
- static function get_calendar( $initial = true, $echo = true ) {
50
  global $wpdb, $m, $monthnum, $year, $wp_locale, $posts;
51
 
52
  $join_clause = PLL()->model->post->join_clause(); #added#
@@ -222,6 +231,7 @@ class PLL_Widget_Calendar extends WP_Widget_Calendar {
222
  if ( in_array( $day, $daywithpost ) ) {
223
  // any posts today?
224
  $date_format = date( _x( 'F j, Y', 'daily archives date format' ), strtotime( "{$thisyear}-{$thismonth}-{$day}" ) );
 
225
  $label = sprintf( __( 'Posts published on %s' ), $date_format );
226
  $calendar_output .= sprintf(
227
  '<a href="%s" aria-label="%s">%s</a>',
@@ -250,7 +260,7 @@ class PLL_Widget_Calendar extends WP_Widget_Calendar {
250
 
251
  if ( $echo ) {
252
  /**
253
- * Filter the HTML calendar output.
254
  *
255
  * @since 3.0.0
256
  *
5
  }
6
 
7
  /**
8
+ * Obliged to rewrite the whole functionality as there is no filter on sql queries and only a filter on final output
9
+ * Code base last checked with WP 4.9.7
10
+ * A request for making a filter on sql queries exists: http://core.trac.wordpress.org/ticket/15202
11
+ * Method used in 0.4.x: use of the get_calendar filter and overwrite the output of get_calendar function -> not very efficient (add 4 to 5 sql queries)
12
+ * Method used since 0.5: remove the WP widget and replace it by our own -> our language filter will not work if get_calendar is called directly by a theme
13
  *
14
  * @since 0.5
15
  */
16
  class PLL_Widget_Calendar extends WP_Widget_Calendar {
17
 
18
  /**
19
+ * Outputs the content for the current Calendar widget instance.
20
+ * Modified version of the parent function to call our own get_calendar function.
21
  *
22
  * @since 0.5
23
  *
24
  * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
25
+ * @param array $instance The settings for the particular instance of the widget.
26
  */
27
+ public function widget( $args, $instance ) {
28
+ $title = ! empty( $instance['title'] ) ? $instance['title'] : '';
29
+
30
  /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
31
+ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
32
+
33
  echo $args['before_widget'];
34
  if ( $title ) {
35
  echo $args['before_title'] . $title . $args['after_title'];
36
  }
37
+ if ( 0 === self::$instance ) {
38
+ echo '<div id="calendar_wrap" class="calendar_wrap">';
39
+ } else {
40
+ echo '<div class="calendar_wrap">';
41
+ }
42
  empty( PLL()->curlang ) ? get_calendar() : self::get_calendar(); #modified#
43
  echo '</div>';
44
  echo $args['after_widget'];
45
+
46
+ self::$instance++;
47
  }
48
 
49
  /**
50
+ * Modified version of WP get_calendar function to filter the query
51
  *
52
  * @since 0.5
53
  *
55
  * @param bool $echo Optional, default is true. Set to false for return.
56
  * @return string|null String when retrieving, null when displaying.
57
  */
58
+ static public function get_calendar( $initial = true, $echo = true ) {
59
  global $wpdb, $m, $monthnum, $year, $wp_locale, $posts;
60
 
61
  $join_clause = PLL()->model->post->join_clause(); #added#
231
  if ( in_array( $day, $daywithpost ) ) {
232
  // any posts today?
233
  $date_format = date( _x( 'F j, Y', 'daily archives date format' ), strtotime( "{$thisyear}-{$thismonth}-{$day}" ) );
234
+ /* translators: Post calendar label. 1: Date */
235
  $label = sprintf( __( 'Posts published on %s' ), $date_format );
236
  $calendar_output .= sprintf(
237
  '<a href="%s" aria-label="%s">%s</a>',
260
 
261
  if ( $echo ) {
262
  /**
263
+ * Filters the HTML calendar output.
264
  *
265
  * @since 3.0.0
266
  *
install/install.php CHANGED
@@ -42,18 +42,19 @@ class PLL_Install extends PLL_Install_Base {
42
  */
43
  static public function get_default_options() {
44
  return array(
45
- 'browser' => 1, // Default language for the front page is set by browser preference
46
- 'rewrite' => 1, // Remove /language/ in permalinks ( was the opposite before 0.7.2 )
47
- 'hide_default' => 1, // Remove URL language information for default language ( was the opposite before 2.1.5 )
48
- 'force_lang' => 1, // Add URL language information ( was 0 before 1.7 )
49
- 'redirect_lang' => 0, // Do not redirect the language page to the homepage
50
- 'media_support' => 1, // Support languages and translation for media by default
51
- 'uninstall' => 0, // Do not remove data when uninstalling Polylang
52
- 'sync' => array(), // Synchronisation is disabled by default ( was the opposite before 1.2 )
53
- 'post_types' => array(),
54
- 'taxonomies' => array(),
55
- 'domains' => array(),
56
- 'version' => POLYLANG_VERSION,
 
57
  );
58
  }
59
 
42
  */
43
  static public function get_default_options() {
44
  return array(
45
+ 'browser' => 1, // Default language for the front page is set by browser preference
46
+ 'rewrite' => 1, // Remove /language/ in permalinks ( was the opposite before 0.7.2 )
47
+ 'hide_default' => 1, // Remove URL language information for default language ( was the opposite before 2.1.5 )
48
+ 'force_lang' => 1, // Add URL language information ( was 0 before 1.7 )
49
+ 'redirect_lang' => 0, // Do not redirect the language page to the homepage
50
+ 'media_support' => 1, // Support languages and translation for media by default
51
+ 'uninstall' => 0, // Do not remove data when uninstalling Polylang
52
+ 'sync' => array(), // Synchronisation is disabled by default ( was the opposite before 1.2 )
53
+ 'post_types' => array(),
54
+ 'taxonomies' => array(),
55
+ 'domains' => array(),
56
+ 'version' => POLYLANG_VERSION,
57
+ 'first_activation' => time(),
58
  );
59
  }
60
 
lingotek/lingotek.php CHANGED
@@ -28,42 +28,29 @@ class PLL_Lingotek {
28
  add_action( 'admin_print_styles', array( $this, 'print_css' ) );
29
  }
30
 
31
- // The pointer
32
- $content = __( 'You’ve just upgraded to the latest version of Polylang! Would you like to automatically translate your website for free?', 'polylang' );
 
 
33
 
34
- $buttons = array(
35
- array(
36
- 'label' => __( 'Close' ),
37
- ),
38
- array(
39
- 'label' => __( 'Learn more', 'polylang' ),
40
- 'link' => admin_url( 'admin.php?page=mlang&tab=lingotek' ),
41
- ),
42
- );
43
-
44
- if ( $link = $this->get_activate_link() ) {
45
- $content .= ' ' . __( 'Click on Activate Lingotek to start translating.', 'polylang' );
46
-
47
- $buttons[] = array(
48
- 'label' => __( 'Activate Lingotek', 'polylang' ),
49
- 'link' => str_replace( '&amp;', '&', $link ), // wp_nonce_url escapes the url for html display. Here we want it for js
50
  );
51
- }
52
 
53
- $args = array(
54
- 'pointer' => 'pll_lgt',
55
- 'id' => empty( $options['previous_version'] ) ? 'nav-tab-lingotek' : 'wp-admin-bar-languages',
56
- 'position' => array(
57
- 'edge' => 'top',
58
- 'align' => 'left',
59
- ),
60
- 'width' => 400,
61
- 'title' => __( 'Congratulations!', 'polylang' ),
62
- 'content' => $content,
63
- 'buttons' => $buttons,
64
- );
65
 
66
- new PLL_Pointer( $args );
 
 
 
 
 
 
 
 
67
  }
68
 
69
  /**
28
  add_action( 'admin_print_styles', array( $this, 'print_css' ) );
29
  }
30
 
31
+ // The admin notice
32
+ // Honor old dismissed pointers
33
+ if ( ! in_array( 'pll_lgt', explode( ',', get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) ) ) {
34
+ $content = __( 'You’ve just upgraded to the latest version of Polylang! Would you like to automatically translate your website for free?', 'polylang' );
35
 
36
+ $buttons = sprintf(
37
+ '<a href="%s" class="button button-primary" style="margin-right: 10px">%s</a>',
38
+ admin_url( 'admin.php?page=mlang_lingotek' ),
39
+ __( 'Learn more', 'polylang' )
 
 
 
 
 
 
 
 
 
 
 
 
40
  );
 
41
 
42
+ if ( $link = $this->get_activate_link() ) {
43
+ $content .= ' ' . __( 'Click on Activate Lingotek to start translating.', 'polylang' );
 
 
 
 
 
 
 
 
 
 
44
 
45
+ $buttons = sprintf(
46
+ '<a href="%s" class="button button-primary" style="margin-right: 10px">%s</a>',
47
+ $link,
48
+ __( 'Activate Lingotek', 'polylang' )
49
+ ) . $buttons;
50
+ }
51
+
52
+ PLL_Admin_Notices::add_notice( 'lingotek', '<p>' . $content . '</p><p>' . $buttons . '</p>' );
53
+ }
54
  }
55
 
56
  /**
modules/plugins/plugins-compat.php CHANGED
@@ -376,9 +376,9 @@ class PLL_Plugins_Compat {
376
  wp_redirect( $redirect, $status );
377
  exit;
378
  }
 
 
 
379
  }
380
-
381
- // Otherwise rely on MU Domain Mapping
382
- redirect_to_mapped_domain();
383
  }
384
  }
376
  wp_redirect( $redirect, $status );
377
  exit;
378
  }
379
+ } else {
380
+ // Otherwise rely on MU Domain Mapping
381
+ redirect_to_mapped_domain();
382
  }
 
 
 
383
  }
384
  }
modules/plugins/wp-import.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  /**
4
- * a class to import languages and translations information form a WXR file
5
  *
6
  * @since 1.2
7
  */
@@ -9,14 +9,14 @@ class PLL_WP_Import extends WP_Import {
9
  public $post_translations = array();
10
 
11
  /**
12
- * overrides WP_Import::process_terms to remap terms translations
13
  *
14
  * @since 1.2
15
  */
16
  function process_terms() {
17
  $term_translations = array();
18
 
19
- // store this for future usage as parent function unsets $this->terms
20
  foreach ( $this->terms as $term ) {
21
  if ( 'post_translations' == $term['term_taxonomy'] ) {
22
  $this->post_translations[] = $term;
@@ -28,13 +28,13 @@ class PLL_WP_Import extends WP_Import {
28
 
29
  parent::process_terms();
30
 
31
- // update the languages list if needed
32
- // first reset the core terms cache as WordPress Importer calls wp_suspend_cache_invalidation( true );
33
  wp_cache_set( 'last_changed', microtime(), 'terms' );
34
  PLL()->model->clean_languages_cache();
35
 
36
  if ( ( $options = get_option( 'polylang' ) ) && empty( $options['default_lang'] ) && ( $languages = PLL()->model->get_languages_list() ) ) {
37
- // assign the default language if importer created the first language
38
  $default_lang = reset( $languages );
39
  $options['default_lang'] = $default_lang->slug;
40
  update_option( 'polylang', $options );
@@ -45,15 +45,15 @@ class PLL_WP_Import extends WP_Import {
45
  }
46
 
47
  /**
48
- * overrides WP_Import::process_post to remap posts translations
49
- * also merges strings translations from the WXR file to the existing ones
50
  *
51
  * @since 1.2
52
  */
53
  function process_posts() {
54
  $menu_items = $mo_posts = array();
55
 
56
- // store this for future usage as parent function unset $this->posts
57
  foreach ( $this->posts as $post ) {
58
  if ( 'nav_menu_item' == $post['post_type'] ) {
59
  $menu_items[] = $post;
@@ -65,17 +65,17 @@ class PLL_WP_Import extends WP_Import {
65
  }
66
 
67
  if ( ! empty( $mo_posts ) ) {
68
- new PLL_MO(); // just to register the polylang_mo post type before processing posts
69
  }
70
 
71
  parent::process_posts();
72
 
73
- PLL()->model->clean_languages_cache(); // to update the posts count in ( cached ) languages list
74
 
75
  $this->remap_translations( $this->post_translations, $this->processed_posts );
76
  unset( $this->post_translations );
77
 
78
- // language switcher menu items
79
  foreach ( $menu_items as $item ) {
80
  foreach ( $item['postmeta'] as $meta ) {
81
  if ( '_pll_menu_item' == $meta['key'] ) {
@@ -84,7 +84,7 @@ class PLL_WP_Import extends WP_Import {
84
  }
85
  }
86
 
87
- // merge strings translations
88
  foreach ( $mo_posts as $post ) {
89
  $lang_id = (int) substr( $post['post_title'], 12 );
90
 
@@ -98,13 +98,13 @@ class PLL_WP_Import extends WP_Import {
98
  $mo->export_to_db( $this->processed_terms[ $lang_id ] );
99
  }
100
  }
101
- // delete the now useless imported post
102
  wp_delete_post( $this->processed_posts[ $post['post_id'] ], true );
103
  }
104
  }
105
 
106
  /**
107
- * remaps terms languages
108
  *
109
  * @since 1.2
110
  *
@@ -117,20 +117,20 @@ class PLL_WP_Import extends WP_Import {
117
  $translations = unserialize( $term['term_description'] );
118
  foreach ( $translations as $slug => $old_id ) {
119
  if ( $old_id && ! empty( $this->processed_terms[ $old_id ] ) && $lang = PLL()->model->get_language( $slug ) ) {
120
- // language relationship
121
  $trs[] = $wpdb->prepare( '( %d, %d )', $this->processed_terms[ $old_id ], $lang->tl_term_taxonomy_id );
122
 
123
- // translation relationship
124
  $trs[] = $wpdb->prepare( '( %d, %d )', $this->processed_terms[ $old_id ], get_term( $this->processed_terms[ $term['term_id'] ], 'term_translations' )->term_taxonomy_id );
125
  }
126
  }
127
  }
128
 
129
- // insert term_relationships
130
  if ( ! empty( $trs ) ) {
131
  $trs = array_unique( $trs );
132
 
133
- // make sure we don't attempt to insert already existing term relationships
134
  $existing_trs = $wpdb->get_results( "
135
  SELECT tr.object_id, tr.term_taxonomy_id FROM $wpdb->term_relationships AS tr
136
  INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
@@ -150,7 +150,7 @@ class PLL_WP_Import extends WP_Import {
150
  }
151
 
152
  /**
153
- * remaps translations for both posts and terms
154
  *
155
  * @since 1.2
156
  *
1
  <?php
2
 
3
  /**
4
+ * A class to import languages and translations information form a WXR file
5
  *
6
  * @since 1.2
7
  */
9
  public $post_translations = array();
10
 
11
  /**
12
+ * Overrides WP_Import::process_terms to remap terms translations
13
  *
14
  * @since 1.2
15
  */
16
  function process_terms() {
17
  $term_translations = array();
18
 
19
+ // Store this for future usage as parent function unsets $this->terms
20
  foreach ( $this->terms as $term ) {
21
  if ( 'post_translations' == $term['term_taxonomy'] ) {
22
  $this->post_translations[] = $term;
28
 
29
  parent::process_terms();
30
 
31
+ // Update the languages list if needed
32
+ // First reset the core terms cache as WordPress Importer calls wp_suspend_cache_invalidation( true );
33
  wp_cache_set( 'last_changed', microtime(), 'terms' );
34
  PLL()->model->clean_languages_cache();
35
 
36
  if ( ( $options = get_option( 'polylang' ) ) && empty( $options['default_lang'] ) && ( $languages = PLL()->model->get_languages_list() ) ) {
37
+ // Assign the default language if importer created the first language
38
  $default_lang = reset( $languages );
39
  $options['default_lang'] = $default_lang->slug;
40
  update_option( 'polylang', $options );
45
  }
46
 
47
  /**
48
+ * Overrides WP_Import::process_post to remap posts translations
49
+ * Also merges strings translations from the WXR file to the existing ones
50
  *
51
  * @since 1.2
52
  */
53
  function process_posts() {
54
  $menu_items = $mo_posts = array();
55
 
56
+ // Store this for future usage as parent function unset $this->posts
57
  foreach ( $this->posts as $post ) {
58
  if ( 'nav_menu_item' == $post['post_type'] ) {
59
  $menu_items[] = $post;
65
  }
66
 
67
  if ( ! empty( $mo_posts ) ) {
68
+ new PLL_MO(); // Just to register the polylang_mo post type before processing posts
69
  }
70
 
71
  parent::process_posts();
72
 
73
+ PLL()->model->clean_languages_cache(); // To update the posts count in ( cached ) languages list
74
 
75
  $this->remap_translations( $this->post_translations, $this->processed_posts );
76
  unset( $this->post_translations );
77
 
78
+ // Language switcher menu items
79
  foreach ( $menu_items as $item ) {
80
  foreach ( $item['postmeta'] as $meta ) {
81
  if ( '_pll_menu_item' == $meta['key'] ) {
84
  }
85
  }
86
 
87
+ // Merge strings translations
88
  foreach ( $mo_posts as $post ) {
89
  $lang_id = (int) substr( $post['post_title'], 12 );
90
 
98
  $mo->export_to_db( $this->processed_terms[ $lang_id ] );
99
  }
100
  }
101
+ // Delete the now useless imported post
102
  wp_delete_post( $this->processed_posts[ $post['post_id'] ], true );
103
  }
104
  }
105
 
106
  /**
107
+ * Remaps terms languages
108
  *
109
  * @since 1.2
110
  *
117
  $translations = unserialize( $term['term_description'] );
118
  foreach ( $translations as $slug => $old_id ) {
119
  if ( $old_id && ! empty( $this->processed_terms[ $old_id ] ) && $lang = PLL()->model->get_language( $slug ) ) {
120
+ // Language relationship
121
  $trs[] = $wpdb->prepare( '( %d, %d )', $this->processed_terms[ $old_id ], $lang->tl_term_taxonomy_id );
122
 
123
+ // Translation relationship
124
  $trs[] = $wpdb->prepare( '( %d, %d )', $this->processed_terms[ $old_id ], get_term( $this->processed_terms[ $term['term_id'] ], 'term_translations' )->term_taxonomy_id );
125
  }
126
  }
127
  }
128
 
129
+ // Insert term_relationships
130
  if ( ! empty( $trs ) ) {
131
  $trs = array_unique( $trs );
132
 
133
+ // Make sure we don't attempt to insert already existing term relationships
134
  $existing_trs = $wpdb->get_results( "
135
  SELECT tr.object_id, tr.term_taxonomy_id FROM $wpdb->term_relationships AS tr
136
  INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
150
  }
151
 
152
  /**
153
+ * Remaps translations for both posts and terms
154
  *
155
  * @since 1.2
156
  *
modules/share-slug/settings-share-slug.php CHANGED
@@ -8,7 +8,7 @@
8
  class PLL_Settings_Share_Slug extends PLL_Settings_Module {
9
 
10
  /**
11
- * constructor
12
  *
13
  * @since 1.9
14
  *
@@ -27,7 +27,7 @@ class PLL_Settings_Share_Slug extends PLL_Settings_Module {
27
  }
28
 
29
  /**
30
- * tells if the module is active
31
  *
32
  * @since 1.9
33
  *
@@ -38,7 +38,7 @@ class PLL_Settings_Share_Slug extends PLL_Settings_Module {
38
  }
39
 
40
  /**
41
- * displays upgrade message
42
  *
43
  * @since 1.9
44
  *
@@ -49,7 +49,7 @@ class PLL_Settings_Share_Slug extends PLL_Settings_Module {
49
  }
50
 
51
  /**
52
- * displays the javascript to handle dynamically the change in url modifications
53
  * as sharing slugs is not possible when the language is set from the content
54
  *
55
  * @since 1.9
8
  class PLL_Settings_Share_Slug extends PLL_Settings_Module {
9
 
10
  /**
11
+ * Constructor
12
  *
13
  * @since 1.9
14
  *
27
  }
28
 
29
  /**
30
+ * Tells if the module is active
31
  *
32
  * @since 1.9
33
  *
38
  }
39
 
40
  /**
41
+ * Displays upgrade message
42
  *
43
  * @since 1.9
44
  *
49
  }
50
 
51
  /**
52
+ * Displays the javascript to handle dynamically the change in url modifications
53
  * as sharing slugs is not possible when the language is set from the content
54
  *
55
  * @since 1.9
modules/sync/settings-sync.php CHANGED
@@ -8,7 +8,7 @@
8
  class PLL_Settings_Sync extends PLL_Settings_Module {
9
 
10
  /**
11
- * constructor
12
  *
13
  * @since 1.8
14
  *
@@ -23,7 +23,7 @@ class PLL_Settings_Sync extends PLL_Settings_Module {
23
  }
24
 
25
  /**
26
- * deactivates the module
27
  *
28
  * @since 1.8
29
  */
@@ -33,7 +33,7 @@ class PLL_Settings_Sync extends PLL_Settings_Module {
33
  }
34
 
35
  /**
36
- * displays the settings form
37
  *
38
  * @since 1.8
39
  */
@@ -55,7 +55,7 @@ class PLL_Settings_Sync extends PLL_Settings_Module {
55
  }
56
 
57
  /**
58
- * sanitizes the settings before saving
59
  *
60
  * @since 1.8
61
  *
@@ -67,7 +67,7 @@ class PLL_Settings_Sync extends PLL_Settings_Module {
67
  }
68
 
69
  /**
70
- * get the row actions
71
  *
72
  * @since 1.8
73
  *
@@ -78,7 +78,7 @@ class PLL_Settings_Sync extends PLL_Settings_Module {
78
  }
79
 
80
  /**
81
- * list the post metas to synchronize
82
  *
83
  * @since 1.0
84
  *
8
  class PLL_Settings_Sync extends PLL_Settings_Module {
9
 
10
  /**
11
+ * Constructor
12
  *
13
  * @since 1.8
14
  *
23
  }
24
 
25
  /**
26
+ * Deactivates the module
27
  *
28
  * @since 1.8
29
  */
33
  }
34
 
35
  /**
36
+ * Displays the settings form
37
  *
38
  * @since 1.8
39
  */
55
  }
56
 
57
  /**
58
+ * Sanitizes the settings before saving
59
  *
60
  * @since 1.8
61
  *
67
  }
68
 
69
  /**
70
+ * Get the row actions
71
  *
72
  * @since 1.8
73
  *
78
  }
79
 
80
  /**
81
+ * List the post metas to synchronize
82
  *
83
  * @since 1.0
84
  *
modules/translate-slugs/settings-translate-slugs.php CHANGED
@@ -7,7 +7,7 @@
7
  */
8
  class PLL_Settings_Translate_Slugs extends PLL_Settings_Module {
9
  /**
10
- * constructor
11
  *
12
  * @since 1.9
13
  *
@@ -22,7 +22,7 @@ class PLL_Settings_Translate_Slugs extends PLL_Settings_Module {
22
  }
23
 
24
  /**
25
- * tells if the module is active
26
  *
27
  * @since 1.9
28
  *
@@ -33,7 +33,7 @@ class PLL_Settings_Translate_Slugs extends PLL_Settings_Module {
33
  }
34
 
35
  /**
36
- * displays upgrade message
37
  *
38
  * @since 1.9
39
  *
7
  */
8
  class PLL_Settings_Translate_Slugs extends PLL_Settings_Module {
9
  /**
10
+ * Constructor
11
  *
12
  * @since 1.9
13
  *
22
  }
23
 
24
  /**
25
+ * Tells if the module is active
26
  *
27
  * @since 1.9
28
  *
33
  }
34
 
35
  /**
36
+ * Displays upgrade message
37
  *
38
  * @since 1.9
39
  *
modules/wpml/settings-wpml.php CHANGED
@@ -8,7 +8,7 @@
8
  class PLL_Settings_WPML extends PLL_Settings_Module {
9
 
10
  /**
11
- * constructor
12
  *
13
  * @since 1.8
14
  *
@@ -23,7 +23,7 @@ class PLL_Settings_WPML extends PLL_Settings_Module {
23
  }
24
 
25
  /**
26
- * tells if the module is active
27
  *
28
  * @since 1.8
29
  *
8
  class PLL_Settings_WPML extends PLL_Settings_Module {
9
 
10
  /**
11
+ * Constructor
12
  *
13
  * @since 1.8
14
  *
23
  }
24
 
25
  /**
26
+ * Tells if the module is active
27
  *
28
  * @since 1.8
29
  *
modules/wpml/wpml-api.php CHANGED
@@ -3,6 +3,7 @@
3
  /**
4
  * A class to handle the WPML API based on hooks, introduced since WPML 3.2
5
  * It partly relies on the legacy API
 
6
  * @see https://wpml.org/documentation/support/wpml-coding-api/wpml-hooks-reference/
7
  *
8
  * @since 2.0
3
  /**
4
  * A class to handle the WPML API based on hooks, introduced since WPML 3.2
5
  * It partly relies on the legacy API
6
+ *
7
  * @see https://wpml.org/documentation/support/wpml-coding-api/wpml-hooks-reference/
8
  *
9
  * @since 2.0
modules/wpml/wpml-legacy-api.php CHANGED
@@ -24,7 +24,7 @@ if ( ! function_exists( 'icl_get_languages' ) ) {
24
  * Used for building custom language selectors
25
  * available only on frontend
26
  *
27
- * list of paramaters accepted in $args
28
  *
29
  * skip_missing => whether to skip missing translation or not, 0 or 1, defaults to 0
30
  * orderby => 'id', 'code', 'name', defaults to 'id'
@@ -210,6 +210,7 @@ if ( ! function_exists( 'wpml_get_language_information' ) ) {
210
  /**
211
  * Undocumented function used by the theme Maya
212
  * returns the post language
 
213
  * @see original WPML code at https://wpml.org/forums/topic/canonical-urls-for-wpml-duplicated-posts/#post-52198
214
  *
215
  * @since 1.8
@@ -359,6 +360,7 @@ if ( ! function_exists( 'icl_get_default_language' ) ) {
359
  if ( ! function_exists( 'wpml_get_default_language' ) ) {
360
  /**
361
  * Undocumented function reported to be used by Table Rate Shipping for WooCommerce
 
362
  * @see https://wordpress.org/support/topic/add-wpml-compatibility-function
363
  *
364
  * @since 1.8.2
24
  * Used for building custom language selectors
25
  * available only on frontend
26
  *
27
+ * List of paramaters accepted in $args:
28
  *
29
  * skip_missing => whether to skip missing translation or not, 0 or 1, defaults to 0
30
  * orderby => 'id', 'code', 'name', defaults to 'id'
210
  /**
211
  * Undocumented function used by the theme Maya
212
  * returns the post language
213
+ *
214
  * @see original WPML code at https://wpml.org/forums/topic/canonical-urls-for-wpml-duplicated-posts/#post-52198
215
  *
216
  * @since 1.8
360
  if ( ! function_exists( 'wpml_get_default_language' ) ) {
361
  /**
362
  * Undocumented function reported to be used by Table Rate Shipping for WooCommerce
363
+ *
364
  * @see https://wordpress.org/support/topic/add-wpml-compatibility-function
365
  *
366
  * @since 1.8.2
polylang.php CHANGED
@@ -3,7 +3,7 @@
3
  /**
4
  Plugin Name: Polylang
5
  Plugin URI: https://polylang.pro
6
- Version: 2.3.8
7
  Author: Frédéric Demarle
8
  Author uri: https://polylang.pro
9
  Description: Adds multilingual capability to WordPress
@@ -53,7 +53,7 @@ if ( defined( 'POLYLANG_BASENAME' ) ) {
53
  }
54
  } else {
55
  // Go on loading the plugin
56
- define( 'POLYLANG_VERSION', '2.3.8' );
57
  define( 'PLL_MIN_WP_VERSION', '4.4' );
58
 
59
  define( 'POLYLANG_FILE', __FILE__ ); // this file
3
  /**
4
  Plugin Name: Polylang
5
  Plugin URI: https://polylang.pro
6
+ Version: 2.3.9
7
  Author: Frédéric Demarle
8
  Author uri: https://polylang.pro
9
  Description: Adds multilingual capability to WordPress
53
  }
54
  } else {
55
  // Go on loading the plugin
56
+ define( 'POLYLANG_VERSION', '2.3.9' );
57
  define( 'PLL_MIN_WP_VERSION', '4.4' );
58
 
59
  define( 'POLYLANG_FILE', __FILE__ ); // this file
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://polylang.pro
4
  Tags: multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
  Requires at least: 4.4
6
  Tested up to: 4.9
7
- Stable tag: 2.3.8
8
  License: GPLv2 or later
9
 
10
  Making WordPress multilingual
@@ -76,6 +76,14 @@ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-
76
 
77
  == Changelog ==
78
 
 
 
 
 
 
 
 
 
79
  = 2.3.8 (2018-07-16) =
80
 
81
  * Pro: Duplicate term meta when duplicating a post creates new terms
4
  Tags: multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
  Requires at least: 4.4
6
  Tested up to: 4.9
7
+ Stable tag: 2.3.9
8
  License: GPLv2 or later
9
 
10
  Making WordPress multilingual
76
 
77
  == Changelog ==
78
 
79
+ = 2.3.9 (2018-08-14) =
80
+
81
+ * Add a notice to inform about Polylang for WooCommerce
82
+ * Deprecate PLL_Pointer
83
+ * Fix bulk editing pages with no language breaking hierarchy #281
84
+ * Fix an edge case where rewrite rules could be messed on a multisite
85
+ * MU Domain Mapping: fix secondary domain redirected to primary domain
86
+
87
  = 2.3.8 (2018-07-16) =
88
 
89
  * Pro: Duplicate term meta when duplicating a post creates new terms
settings/flags.php CHANGED
@@ -7,7 +7,7 @@ if ( ! defined( 'ABSPATH' ) ) {
7
  /**
8
  * The list of flags
9
  *
10
- * for each flag:
11
  * the key is the flag file name (without the extension)
12
  * the value is the Country name
13
  */
7
  /**
8
  * The list of flags
9
  *
10
+ * For each flag:
11
  * the key is the flag file name (without the extension)
12
  * the value is the Country name
13
  */
settings/settings-media.php CHANGED
@@ -8,7 +8,7 @@
8
  class PLL_Settings_Media extends PLL_Settings_Module {
9
 
10
  /**
11
- * constructor
12
  *
13
  * @since 1.8
14
  *
8
  class PLL_Settings_Media extends PLL_Settings_Module {
9
 
10
  /**
11
+ * Constructor
12
  *
13
  * @since 1.8
14
  *
settings/settings-tools.php CHANGED
@@ -8,7 +8,7 @@
8
  class PLL_Settings_Tools extends PLL_Settings_Module {
9
 
10
  /**
11
- * constructor
12
  *
13
  * @since 1.8
14
  *
@@ -23,7 +23,7 @@ class PLL_Settings_Tools extends PLL_Settings_Module {
23
  }
24
 
25
  /**
26
- * displays the settings form
27
  *
28
  * @since 1.8
29
  */
@@ -36,7 +36,7 @@ class PLL_Settings_Tools extends PLL_Settings_Module {
36
  }
37
 
38
  /**
39
- * sanitizes the settings before saving
40
  *
41
  * @since 1.8
42
  *
@@ -44,6 +44,6 @@ class PLL_Settings_Tools extends PLL_Settings_Module {
44
  */
45
  protected function update( $options ) {
46
  $newoptions['uninstall'] = isset( $options['uninstall'] ) ? 1 : 0;
47
- return $newoptions; // take care to return only validated options
48
  }
49
  }
8
  class PLL_Settings_Tools extends PLL_Settings_Module {
9
 
10
  /**
11
+ * Constructor
12
  *
13
  * @since 1.8
14
  *
23
  }
24
 
25
  /**
26
+ * Displays the settings form
27
  *
28
  * @since 1.8
29
  */
36
  }
37
 
38
  /**
39
+ * Sanitizes the settings before saving
40
  *
41
  * @since 1.8
42
  *
44
  */
45
  protected function update( $options ) {
46
  $newoptions['uninstall'] = isset( $options['uninstall'] ) ? 1 : 0;
47
+ return $newoptions; // Take care to return only validated options
48
  }
49
  }
settings/settings-url.php CHANGED
@@ -31,7 +31,8 @@ class PLL_Settings_Url extends PLL_Settings_Module {
31
  *
32
  * @since 1.8
33
  */
34
- protected function force_lang() {?>
 
35
  <label>
36
  <?php
37
  printf(
31
  *
32
  * @since 1.8
33
  */
34
+ protected function force_lang() {
35
+ ?>
36
  <label>
37
  <?php
38
  printf(
settings/view-tab-lang.php CHANGED
@@ -1,11 +1,11 @@
1
  <?php
2
 
3
  /**
4
- * displays the languages tab in Polylang settings
5
  */
6
 
7
  if ( ! defined( 'ABSPATH' ) ) {
8
- exit; // don't access directly
9
  };
10
  ?>
11
  <div id="col-container">
@@ -47,7 +47,7 @@ if ( ! defined( 'ABSPATH' ) ) {
47
  <input type="hidden" name="pll_action" value="add" />
48
  <?php
49
  }
50
- ?>
51
  <div class="form-field">
52
  <label for="lang_list"><?php esc_html_e( 'Choose a language', 'polylang' ); ?></label>
53
  <select name="lang_list" id="lang_list">
1
  <?php
2
 
3
  /**
4
+ * Displays the languages tab in Polylang settings
5
  */
6
 
7
  if ( ! defined( 'ABSPATH' ) ) {
8
+ exit; // Don't access directly
9
  };
10
  ?>
11
  <div id="col-container">
47
  <input type="hidden" name="pll_action" value="add" />
48
  <?php
49
  }
50
+ ?>
51
  <div class="form-field">
52
  <label for="lang_list"><?php esc_html_e( 'Choose a language', 'polylang' ); ?></label>
53
  <select name="lang_list" id="lang_list">
settings/view-tab-settings.php CHANGED
@@ -1,11 +1,11 @@
1
  <?php
2
 
3
  /**
4
- * displays the settings tab in Polylang settings
5
  */
6
 
7
  if ( ! defined( 'ABSPATH' ) ) {
8
- exit; // don't access directly
9
  };
10
  ?>
11
  <div class="form-wrap">
1
  <?php
2
 
3
  /**
4
+ * Displays the settings tab in Polylang settings
5
  */
6
 
7
  if ( ! defined( 'ABSPATH' ) ) {
8
+ exit; // Don't access directly
9
  };
10
  ?>
11
  <div class="form-wrap">