Polylang - Version 2.1

Version Description

(2017-01-25) =

  • Minimum WordPress version is now 4.4
  • Pro: Add support for synchronized posts (same post in multiple languages)
  • Pro: Add support for custom post type UI and the Divi Builder
  • Improve support of Yoast SEO (no category base and post type archive breadcrumb title)
  • Move Languages menu at top level instead of submenu of the WordPress settings
  • Copy the original post date when creating a translation and when the date is synchronized (Props Jory Hogeveen) #32
  • Remove hreflang attributes on paged pages and paged posts
  • Add label to widget language dropdown for better accessibility (Props Lawrence Francell) #53 #56
  • Remove constants POLYLANG_URL and PLL_LOCAL_URL
  • wp_get_sidebars_widgets() and is_active_sidebar() are now filtered according to widgets languages #54
  • Add functions pll_esc_html_(), pll_esc_html_e(), pll_esc_attr_() and pll_esc_attr_e() to the API (Props jegbagus) #83
  • Pro: Fix conflict between WooCommerce shop on front and translated shop base slug
  • Pro: Fix $wp_rewrite search base and author_base not translated #68
  • Pro: Fix page preview does not log in the user when using sudomains
  • Fix: avoid setting the language cookie on 404 pages
  • Fix: rewrite rules order modified for custom post types archives
  • Fix: conflict with WP All Import causing our filters to fail in "Add Media" modal when editing a post
  • Fix: auto add pages not working for nav menus assigned to several locations
  • Fix: Jetpack infinite scroll for multiple domains #58 #74
  • Fix: serialize error in Strings translations when balanceTags option is active #63
  • Fix: static front page preview when redirected from the languages page #49
  • Fix: Auto add pages not working for nav menus assigned to several locations
  • Fix: Conflict with Woocommerce Show Single Variation
  • Fix: Parent page not synchronized in Quick edit (introduced in 2.0.8)
  • Fix: WPML API wpml_element_has_translations and wpml_post_language_details
  • Fix: unattached media translations not in language switcher
  • Fix: Conflict with WP Residence advanced search
Download this release

Release Info

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

Code changes from version 2.0.12 to 2.1

admin/admin-base.php CHANGED
@@ -67,7 +67,34 @@ class PLL_Admin_Base extends PLL_Base {
67
  * @since 0.1
68
  */
69
  public function add_menus() {
70
- add_submenu_page( 'options-general.php', $title = __( 'Languages', 'polylang' ), $title, 'manage_options', 'mlang', '__return_null' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  }
72
 
73
  /**
@@ -98,11 +125,11 @@ class PLL_Admin_Base extends PLL_Base {
98
 
99
  foreach ( $scripts as $script => $v ) {
100
  if ( in_array( $screen->base, $v[0] ) && ( $v[2] || $this->model->get_languages_list() ) ) {
101
- wp_enqueue_script( 'pll_' . $script, POLYLANG_URL . '/js/' . $script . $suffix . '.js', $v[1], POLYLANG_VERSION, $v[3] );
102
  }
103
  }
104
 
105
- wp_enqueue_style( 'polylang_admin', POLYLANG_URL . '/css/admin' . $suffix . '.css', array(), POLYLANG_VERSION );
106
  }
107
 
108
  /**
@@ -134,7 +161,7 @@ class PLL_Admin_Base extends PLL_Base {
134
  if (typeof jQuery != 'undefined') {
135
  (function($){
136
  $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
137
- if ( -1 != options.url.indexOf( ajaxurl ) ) {
138
  if ( 'undefined' === typeof options.data ) {
139
  options.data = ( 'get' === options.type.toLowerCase() ) ? '<?php echo $str;?>' : <?php echo $arr;?>;
140
  } else {
67
  * @since 0.1
68
  */
69
  public function add_menus() {
70
+ // Prepare the list of tabs
71
+ $tabs = array( 'lang' => __( 'Languages','polylang' ) );
72
+
73
+ // Only if at least one language has been created
74
+ if ( $this->model->get_languages_list() ) {
75
+ $tabs['strings'] = __( 'Strings translations', 'polylang' );
76
+ }
77
+
78
+ $tabs['settings'] = __( 'Settings', 'polylang' );
79
+
80
+ /**
81
+ * Filter the list of tabs in Polylang settings
82
+ *
83
+ * @since 1.5.1
84
+ *
85
+ * @param array $tabs list of tab names
86
+ */
87
+ $tabs = apply_filters( 'pll_settings_tabs', $tabs );
88
+
89
+ foreach ( $tabs as $tab => $title ) {
90
+ $page = 'lang' === $tab ? 'mlang' : "mlang_$tab";
91
+ if ( empty( $parent ) ) {
92
+ $parent = $page;
93
+ add_menu_page( $title, __( 'Languages','polylang' ), 'manage_options', $page, null , 'dashicons-translation' );
94
+ }
95
+
96
+ add_submenu_page( $parent, $title, $title, 'manage_options', $page , array( $this, 'languages_page' ) );
97
+ }
98
  }
99
 
100
  /**
125
 
126
  foreach ( $scripts as $script => $v ) {
127
  if ( in_array( $screen->base, $v[0] ) && ( $v[2] || $this->model->get_languages_list() ) ) {
128
+ wp_enqueue_script( 'pll_' . $script, plugins_url( '/js/' . $script . $suffix . '.js', POLYLANG_FILE ), $v[1], POLYLANG_VERSION, $v[3] );
129
  }
130
  }
131
 
132
+ wp_enqueue_style( 'polylang_admin', plugins_url( '/css/admin' . $suffix . '.css', POLYLANG_FILE ), array(), POLYLANG_VERSION );
133
  }
134
 
135
  /**
161
  if (typeof jQuery != 'undefined') {
162
  (function($){
163
  $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
164
+ if ( -1 != options.url.indexOf( ajaxurl ) || -1 != ajaxurl.indexOf( options.url ) ) {
165
  if ( 'undefined' === typeof options.data ) {
166
  options.data = ( 'get' === options.type.toLowerCase() ) ? '<?php echo $str;?>' : <?php echo $arr;?>;
167
  } else {
admin/admin-filters-media.php CHANGED
@@ -74,6 +74,11 @@ class PLL_Admin_Filters_Media extends PLL_Admin_Filters_Post_Base {
74
  */
75
  public function create_media_translation( $post_id, $lang ) {
76
  $post = get_post( $post_id );
 
 
 
 
 
77
  $lang = $this->model->get_language( $lang ); // Make sure we get a valid language slug
78
 
79
  // Create a new attachment ( translate attachment parent if exists )
74
  */
75
  public function create_media_translation( $post_id, $lang ) {
76
  $post = get_post( $post_id );
77
+
78
+ if ( empty( $post ) ) {
79
+ return $post;
80
+ }
81
+
82
  $lang = $this->model->get_language( $lang ); // Make sure we get a valid language slug
83
 
84
  // Create a new attachment ( translate attachment parent if exists )
admin/admin-filters-post.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  /**
4
- * manages filters and actions related to posts on admin side
5
  *
6
  * @since 1.2
7
  */
@@ -9,7 +9,7 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
9
  public $options, $curlang;
10
 
11
  /**
12
- * constructor: setups filters and actions
13
  *
14
  * @since 1.2
15
  *
@@ -22,32 +22,32 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
22
 
23
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
24
 
25
- // filters posts, pages and media by language
26
  add_action( 'parse_query', array( $this, 'parse_query' ) );
27
 
28
- // adds the Languages box in the 'Edit Post' and 'Edit Page' panels
29
  add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 10, 2 );
30
 
31
- // ajax response for changing the language in the post metabox
32
  add_action( 'wp_ajax_post_lang_choice', array( $this, 'post_lang_choice' ) );
33
  add_action( 'wp_ajax_pll_posts_not_translated', array( $this, 'ajax_posts_not_translated' ) );
34
 
35
- // adds actions and filters related to languages when creating, saving or deleting posts and pages
36
- add_action( 'save_post', array( $this, 'save_post' ), 21, 3 ); // priority 21 to come after advanced custom fields ( 20 ) and before the event calendar which breaks everything after 25
37
  add_filter( 'wp_insert_post_parent', array( $this, 'wp_insert_post_parent' ), 10, 4 );
38
  add_action( 'before_delete_post', array( $this, 'delete_post' ) );
39
  if ( $this->options['media_support'] ) {
40
- add_action( 'delete_attachment', array( $this, 'delete_post' ) ); // action shared with media
41
  }
42
 
43
- // filters the pages by language in the parent dropdown list in the page attributes metabox
44
  add_filter( 'page_attributes_dropdown_pages_args', array( $this, 'page_attributes_dropdown_pages_args' ), 10, 2 );
45
  }
46
 
47
  /**
48
- * outputs a javascript list of terms ordered by language and hierarchical taxonomies
49
  * to filter the category checklist per post language in quick edit
50
- * outputs a javascript list of pages ordered by language
51
  * to filter the parent dropdown per post language in quick edit
52
  *
53
  * @since 1.7
@@ -55,9 +55,9 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
55
  public function admin_enqueue_scripts() {
56
  $screen = get_current_screen();
57
 
58
- //hierarchical taxonomies
59
  if ( 'edit' == $screen->base && $taxonomies = get_object_taxonomies( $screen->post_type, 'object' ) ) {
60
- // get translated hierarchical taxonomies
61
  foreach ( $taxonomies as $taxonomy ) {
62
  if ( $taxonomy->hierarchical && $taxonomy->show_in_quick_edit && $this->model->is_translated_taxonomy( $taxonomy->name ) ) {
63
  $hierarchical_taxonomies[] = $taxonomy->name;
@@ -73,14 +73,14 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
73
  }
74
  }
75
 
76
- // send all these data to javascript
77
  if ( ! empty( $term_languages ) ) {
78
  wp_localize_script( 'pll_post', 'pll_term_languages', $term_languages );
79
  }
80
  }
81
  }
82
 
83
- // hierarchical post types
84
  if ( 'edit' == $screen->base && is_post_type_hierarchical( $screen->post_type ) ) {
85
  $pages = get_pages();
86
 
@@ -90,7 +90,7 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
90
  }
91
  }
92
 
93
- // send all these data to javascript
94
  if ( ! empty( $page_languages ) ) {
95
  wp_localize_script( 'pll_post', 'pll_page_languages', $page_languages );
96
  }
@@ -98,7 +98,7 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
98
  }
99
 
100
  /**
101
- * filters posts, pages and media by language
102
  *
103
  * @since 0.1
104
  *
@@ -107,7 +107,7 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
107
  public function parse_query( $query ) {
108
  $qvars = &$query->query_vars;
109
 
110
- // do not filter post types such as nav_menu_item
111
  if ( isset( $qvars['post_type'] ) && ! $this->model->is_translated_post_type( $qvars['post_type'] ) ) {
112
  unset( $qvars['lang'] );
113
  return;
@@ -116,10 +116,26 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
116
  // Do not filter the query if the language is already specified in another way
117
  if ( ! isset( $qvars['lang'] ) ) {
118
  $excludes = array(
119
- 'p', 'post_parent', 'attachment', 'attachment_id', 'name', 'pagename', 'page_id',
120
- 'category_name', 'tag', 'cat', 'tag_id', 'category__in', 'category__not_in', 'category__and',
121
- 'post__in', 'post__not_in', 'post_name__in', 'tag__in', 'tag__not_in', 'tag__and',
122
- 'tag_slug__in', 'tag_slug__and', 'post_parent__in', 'post_parent__not_in',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  );
124
 
125
  foreach ( $excludes as $k ) {
@@ -147,7 +163,6 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
147
  }
148
  }
149
 
150
-
151
  if ( isset( $qvars['lang'] ) && 'all' === $qvars['lang'] ) {
152
  unset( $qvars['lang'] );
153
  }
@@ -173,7 +188,7 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
173
  }
174
 
175
  /**
176
- * displays the Languages metabox in the 'Edit Post' and 'Edit Page' panels
177
  *
178
  * @since 0.1
179
  */
@@ -221,14 +236,14 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
221
  }
222
 
223
  /**
224
- * ajax response for changing the language in the post metabox
225
  *
226
  * @since 0.2
227
  */
228
  public function post_lang_choice() {
229
  check_ajax_referer( 'pll_language', '_pll_nonce' );
230
 
231
- global $post_ID; // obliged to use the global variable for wp_popular_terms_checklist
232
  $post_id = $post_ID = (int) $_POST['post_id'];
233
  $lang = $this->model->get_language( $_POST['lang'] );
234
 
@@ -238,7 +253,7 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
238
  wp_die( -1 );
239
  }
240
 
241
- $this->model->post->set_language( $post_ID, $lang ); // save language, useful to set the language when uploading media from post
242
 
243
  ob_start();
244
  if ( $lang ) {
@@ -247,9 +262,9 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
247
  $x = new WP_Ajax_Response( array( 'what' => 'translations', 'data' => ob_get_contents() ) );
248
  ob_end_clean();
249
 
250
- // categories
251
  if ( isset( $_POST['taxonomies'] ) ) {
252
- // not set for pages
253
  foreach ( $_POST['taxonomies'] as $taxname ) {
254
  $taxonomy = get_taxonomy( $taxname );
255
 
@@ -259,7 +274,7 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
259
  ob_end_clean();
260
 
261
  ob_start();
262
- // use $post_ID to remember ckecked terms in case we come back to the original language
263
  wp_terms_checklist( $post_ID, array( 'taxonomy' => $taxonomy->name, 'popular_cats' => $popular_ids ) );
264
  $supplemental['all'] = ob_get_contents();
265
  ob_end_clean();
@@ -278,11 +293,11 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
278
  }
279
  }
280
 
281
- // parent dropdown list ( only for hierarchical post types )
282
  if ( in_array( $post_type, get_post_types( array( 'hierarchical' => true ) ) ) ) {
283
  $post = get_post( $post_ID );
284
 
285
- // args and filter from 'page_attributes_meta_box' in wp-admin/includes/meta-boxes.php of WP 4.2.1
286
  $dropdown_args = array(
287
  'post_type' => $post->post_type,
288
  'exclude_tree' => $post->ID,
@@ -294,12 +309,12 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
294
  );
295
 
296
  /** This filter is documented in wp-admin/includes/meta-boxes.php */
297
- $dropdown_args = apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post ); // since WP 3.3
298
 
299
  $x->Add( array( 'what' => 'pages', 'data' => wp_dropdown_pages( $dropdown_args ) ) );
300
  }
301
 
302
- // flag
303
  $x->Add( array( 'what' => 'flag', 'data' => empty( $lang->flag ) ? esc_html( $lang->slug ) : $lang->flag ) );
304
 
305
  // Sample permalink
@@ -309,7 +324,7 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
309
  }
310
 
311
  /**
312
- * ajax response for input in translation autocomplete input box
313
  *
314
  * @since 1.5
315
  */
@@ -323,12 +338,12 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
323
  $post_language = $this->model->get_language( $_GET['post_language'] );
324
  $translation_language = $this->model->get_language( $_GET['translation_language'] );
325
 
326
- // don't order by title: see https://wordpress.org/support/topic/find-translated-post-when-10-is-not-enough
327
  $args = array(
328
  's' => wp_unslash( $_GET['term'] ),
329
- 'suppress_filters' => 0, // to make the post_fields filter work
330
- 'lang' => 0, // avoid admin language filter
331
- 'numberposts' => 20, // limit to 20 posts
332
  'post_status' => 'any',
333
  'post_type' => $_GET['post_type'],
334
  'tax_query' => array( array(
@@ -363,7 +378,7 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
363
  }
364
  }
365
 
366
- // add current translation in list
367
  if ( $post_id = $this->model->post->get_translation( (int) $_GET['pll_post_id'], $translation_language ) ) {
368
  $post = get_post( $post_id );
369
  array_unshift( $return, array(
@@ -377,44 +392,43 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
377
  }
378
 
379
  /**
380
- * saves language
381
- * checks the terms saved are in the right language
382
  *
383
  * @since 1.5
384
  *
385
- * @param int $post_id
386
  * @param array $post
387
  */
388
  protected function save_language( $post_id, $post ) {
389
- // security checks are necessary to accept language modifications
390
- // as 'wp_insert_post' can be called from outside WP admin
391
 
392
- // edit post
393
- if ( isset( $_POST['post_lang_choice'] ) ) {
394
  check_admin_referer( 'pll_language', '_pll_nonce' );
395
  $this->model->post->set_language( $post_id, $lang = $this->model->get_language( $_POST['post_lang_choice'] ) );
396
  }
397
 
398
- // quick edit and bulk edit
399
  // POST for quick edit, GET for bulk edit
400
  elseif ( isset( $_REQUEST['inline_lang_choice'] ) ) {
401
- // bulk edit does not modify the language
402
  if ( isset( $_GET['bulk_edit'] ) && -1 == $_REQUEST['inline_lang_choice'] ) {
403
  check_admin_referer( 'bulk-posts' );
404
- $lang = $this->model->post->get_language( $post_id ); // get the post language for later use when saving terms
405
  }
406
- // a language is set in the language dropdown
407
  else {
408
  isset( $_GET['bulk_edit'] ) ? check_admin_referer( 'bulk-posts' ) : check_admin_referer( 'inlineeditnonce', '_inline_edit' );
409
 
410
- $old_lang = $this->model->post->get_language( $post_id ); // stores the old language
411
  $this->model->post->set_language( $post_id, $lang = $this->model->get_language( $_REQUEST['inline_lang_choice'] ) ); // set new language
412
 
413
- // checks if the new language already exists in the translation group
414
  if ( $old_lang && $old_lang->slug != $lang->slug ) {
415
  $translations = $this->model->post->get_translations( $post_id );
416
 
417
- // if yes, separate this post from the translation group
418
  if ( array_key_exists( $lang->slug, $translations ) ) {
419
  $this->model->post->delete_translation( $post_id );
420
  }
@@ -427,18 +441,18 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
427
  }
428
  }
429
 
430
- // quick press
431
  // 'post-quickpress-save', 'post-quickpress-publish' = backward compatibility WP < 3.8
432
  elseif ( isset( $_REQUEST['action'] ) && in_array( $_REQUEST['action'], array( 'post-quickpress-save', 'post-quickpress-publish', 'post-quickdraft-save' ) ) ) {
433
  check_admin_referer( 'add-' . $post->post_type );
434
- $this->model->post->set_language( $post_id, $lang = $this->pref_lang ); // default language for Quick draft
435
  }
436
 
437
  else {
438
  $this->set_default_language( $post_id );
439
  }
440
 
441
- // make sure we get save terms in the right language (especially tags with same name in different languages)
442
  if ( ! empty( $lang ) ) {
443
  // FIXME quite a lot of queries in foreach
444
  foreach ( $this->model->get_translated_taxonomies() as $tax ) {
@@ -447,17 +461,17 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
447
  if ( is_array( $terms ) ) {
448
  $newterms = array();
449
  foreach ( $terms as $term ) {
450
- // check if the term is in the correct language or if a translation exist ( mainly for default category )
451
  if ( $newterm = $this->model->term->get( $term->term_id, $lang ) ) {
452
  $newterms[] = (int) $newterm;
453
  }
454
 
455
- // or choose the correct language for tags ( initially defined by name )
456
  elseif ( $newterm = $this->model->term_exists( $term->name, $tax, $term->parent, $lang ) ) {
457
- $newterms[] = (int) $newterm; // cast is important otherwise we get 'numeric' tags
458
  }
459
 
460
- // or create the term in the correct language
461
  elseif ( ! is_wp_error( $term_info = wp_insert_term( $term->name, $tax ) ) ) {
462
  $newterms[] = (int) $term_info['term_id'];
463
  }
@@ -470,17 +484,17 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
470
  }
471
 
472
  /**
473
- * called when a post ( or page ) is saved, published or updated
474
  * saves languages and translations
475
  *
476
  * @since 0.1
477
  *
478
- * @param int $post_id
479
  * @param object $post
480
- * @param bool $update whether it is an update or not
481
  */
482
  public function save_post( $post_id, $post, $update ) {
483
- // does nothing except on post types which are filterable
484
  if ( ! $this->model->is_translated_post_type( $post->post_type ) ) {
485
  return;
486
  }
@@ -489,14 +503,14 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
489
  $post_id = $id;
490
  }
491
 
492
- // capability check
493
- // as 'wp_insert_post' can be called from outside WP admin
494
  $post_type_object = get_post_type_object( $post->post_type );
495
  if ( ( $update && current_user_can( $post_type_object->cap->edit_post, $post_id ) ) || ( ! $update && current_user_can( $post_type_object->cap->create_posts ) ) ) {
496
  $this->save_language( $post_id, $post );
497
 
498
- // Make sure we are saving translations only for the main post currently being edited and not for other possible post types
499
- if ( ! empty( $GLOBALS['post_type'] ) && $post->post_type === $GLOBALS['post_type'] && isset( $_POST['post_tr_lang'] ) ) {
500
  $translations = $this->save_translations( $post_id, $_POST['post_tr_lang'] );
501
  }
502
 
@@ -505,21 +519,21 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
505
  *
506
  * @since 1.2
507
  *
508
- * @param int $post_id post id
509
- * @param object $post post object
510
- * @param array $translations the list of translations post ids
511
  */
512
  do_action( 'pll_save_post', $post_id, $post, empty( $translations ) ? $this->model->post->get_translations( $post_id ) : $translations );
513
  }
514
 
515
- // attempts to set a default language even if no capability
516
  else {
517
  $this->set_default_language( $post_id );
518
  }
519
  }
520
 
521
  /**
522
- * make sure that the post parent is in the correct language when using bulk edit
523
  *
524
  * @since 1.8
525
  *
@@ -541,8 +555,8 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
541
  }
542
 
543
  /**
544
- * called when a post, page or media is deleted
545
- * don't delete translations if this is a post revision thanks to AndyDeGroo who catched this bug
546
  * http://wordpress.org/support/topic/plugin-polylang-quick-edit-still-breaks-translation-linking-of-pages-in-072
547
  *
548
  * @since 0.1
@@ -556,13 +570,13 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
556
  }
557
 
558
  /**
559
- * filters the pages by language in the parent dropdown list in the page attributes metabox
560
  *
561
  * @since 0.6
562
  *
563
- * @param array $dropdown_args arguments passed to wp_dropdown_pages
564
  * @param object $post
565
- * @return array modified arguments
566
  */
567
  public function page_attributes_dropdown_pages_args( $dropdown_args, $post ) {
568
  $dropdown_args['lang'] = isset( $_POST['lang'] ) ? $this->model->get_language( $_POST['lang'] ) : $this->model->post->get_language( $post->ID ); // ajax or not ?
1
  <?php
2
 
3
  /**
4
+ * Manages filters and actions related to posts on admin side
5
  *
6
  * @since 1.2
7
  */
9
  public $options, $curlang;
10
 
11
  /**
12
+ * Constructor: setups filters and actions
13
  *
14
  * @since 1.2
15
  *
22
 
23
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
24
 
25
+ // Filters posts, pages and media by language
26
  add_action( 'parse_query', array( $this, 'parse_query' ) );
27
 
28
+ // Adds the Languages box in the 'Edit Post' and 'Edit Page' panels
29
  add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 10, 2 );
30
 
31
+ // Ajax response for changing the language in the post metabox
32
  add_action( 'wp_ajax_post_lang_choice', array( $this, 'post_lang_choice' ) );
33
  add_action( 'wp_ajax_pll_posts_not_translated', array( $this, 'ajax_posts_not_translated' ) );
34
 
35
+ // Adds actions and filters related to languages when creating, saving or deleting posts and pages
36
+ add_action( 'save_post', array( $this, 'save_post' ), 21, 3 ); // Priority 21 to come after advanced custom fields ( 20 ) and before the event calendar which breaks everything after 25
37
  add_filter( 'wp_insert_post_parent', array( $this, 'wp_insert_post_parent' ), 10, 4 );
38
  add_action( 'before_delete_post', array( $this, 'delete_post' ) );
39
  if ( $this->options['media_support'] ) {
40
+ add_action( 'delete_attachment', array( $this, 'delete_post' ) ); // Action shared with media
41
  }
42
 
43
+ // Filters the pages by language in the parent dropdown list in the page attributes metabox
44
  add_filter( 'page_attributes_dropdown_pages_args', array( $this, 'page_attributes_dropdown_pages_args' ), 10, 2 );
45
  }
46
 
47
  /**
48
+ * Outputs a javascript list of terms ordered by language and hierarchical taxonomies
49
  * to filter the category checklist per post language in quick edit
50
+ * Outputs a javascript list of pages ordered by language
51
  * to filter the parent dropdown per post language in quick edit
52
  *
53
  * @since 1.7
55
  public function admin_enqueue_scripts() {
56
  $screen = get_current_screen();
57
 
58
+ // Hierarchical taxonomies
59
  if ( 'edit' == $screen->base && $taxonomies = get_object_taxonomies( $screen->post_type, 'object' ) ) {
60
+ // Get translated hierarchical taxonomies
61
  foreach ( $taxonomies as $taxonomy ) {
62
  if ( $taxonomy->hierarchical && $taxonomy->show_in_quick_edit && $this->model->is_translated_taxonomy( $taxonomy->name ) ) {
63
  $hierarchical_taxonomies[] = $taxonomy->name;
73
  }
74
  }
75
 
76
+ // Send all these data to javascript
77
  if ( ! empty( $term_languages ) ) {
78
  wp_localize_script( 'pll_post', 'pll_term_languages', $term_languages );
79
  }
80
  }
81
  }
82
 
83
+ // Hierarchical post types
84
  if ( 'edit' == $screen->base && is_post_type_hierarchical( $screen->post_type ) ) {
85
  $pages = get_pages();
86
 
90
  }
91
  }
92
 
93
+ // Send all these data to javascript
94
  if ( ! empty( $page_languages ) ) {
95
  wp_localize_script( 'pll_post', 'pll_page_languages', $page_languages );
96
  }
98
  }
99
 
100
  /**
101
+ * Filters posts, pages and media by language
102
  *
103
  * @since 0.1
104
  *
107
  public function parse_query( $query ) {
108
  $qvars = &$query->query_vars;
109
 
110
+ // Do not filter post types such as nav_menu_item
111
  if ( isset( $qvars['post_type'] ) && ! $this->model->is_translated_post_type( $qvars['post_type'] ) ) {
112
  unset( $qvars['lang'] );
113
  return;
116
  // Do not filter the query if the language is already specified in another way
117
  if ( ! isset( $qvars['lang'] ) ) {
118
  $excludes = array(
119
+ 'p',
120
+ 'post_parent',
121
+ 'attachment',
122
+ 'attachment_id',
123
+ 'name',
124
+ 'pagename',
125
+ 'page_id',
126
+ 'category_name',
127
+ 'tag',
128
+ 'cat',
129
+ 'tag_id',
130
+ 'category__in',
131
+ 'category__and',
132
+ 'post__in',
133
+ 'post_name__in',
134
+ 'tag__in',
135
+ 'tag__and',
136
+ 'tag_slug__in',
137
+ 'tag_slug__and',
138
+ 'post_parent__in',
139
  );
140
 
141
  foreach ( $excludes as $k ) {
163
  }
164
  }
165
 
 
166
  if ( isset( $qvars['lang'] ) && 'all' === $qvars['lang'] ) {
167
  unset( $qvars['lang'] );
168
  }
188
  }
189
 
190
  /**
191
+ * Displays the Languages metabox in the 'Edit Post' and 'Edit Page' panels
192
  *
193
  * @since 0.1
194
  */
236
  }
237
 
238
  /**
239
+ * Ajax response for changing the language in the post metabox
240
  *
241
  * @since 0.2
242
  */
243
  public function post_lang_choice() {
244
  check_ajax_referer( 'pll_language', '_pll_nonce' );
245
 
246
+ global $post_ID; // Obliged to use the global variable for wp_popular_terms_checklist
247
  $post_id = $post_ID = (int) $_POST['post_id'];
248
  $lang = $this->model->get_language( $_POST['lang'] );
249
 
253
  wp_die( -1 );
254
  }
255
 
256
+ $this->model->post->set_language( $post_ID, $lang ); // Save language, useful to set the language when uploading media from post
257
 
258
  ob_start();
259
  if ( $lang ) {
262
  $x = new WP_Ajax_Response( array( 'what' => 'translations', 'data' => ob_get_contents() ) );
263
  ob_end_clean();
264
 
265
+ // Categories
266
  if ( isset( $_POST['taxonomies'] ) ) {
267
+ // Not set for pages
268
  foreach ( $_POST['taxonomies'] as $taxname ) {
269
  $taxonomy = get_taxonomy( $taxname );
270
 
274
  ob_end_clean();
275
 
276
  ob_start();
277
+ // Use $post_ID to remember ckecked terms in case we come back to the original language
278
  wp_terms_checklist( $post_ID, array( 'taxonomy' => $taxonomy->name, 'popular_cats' => $popular_ids ) );
279
  $supplemental['all'] = ob_get_contents();
280
  ob_end_clean();
293
  }
294
  }
295
 
296
+ // Parent dropdown list ( only for hierarchical post types )
297
  if ( in_array( $post_type, get_post_types( array( 'hierarchical' => true ) ) ) ) {
298
  $post = get_post( $post_ID );
299
 
300
+ // Args and filter from 'page_attributes_meta_box' in wp-admin/includes/meta-boxes.php of WP 4.2.1
301
  $dropdown_args = array(
302
  'post_type' => $post->post_type,
303
  'exclude_tree' => $post->ID,
309
  );
310
 
311
  /** This filter is documented in wp-admin/includes/meta-boxes.php */
312
+ $dropdown_args = apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post ); // Since WP 3.3
313
 
314
  $x->Add( array( 'what' => 'pages', 'data' => wp_dropdown_pages( $dropdown_args ) ) );
315
  }
316
 
317
+ // Flag
318
  $x->Add( array( 'what' => 'flag', 'data' => empty( $lang->flag ) ? esc_html( $lang->slug ) : $lang->flag ) );
319
 
320
  // Sample permalink
324
  }
325
 
326
  /**
327
+ * Ajax response for input in translation autocomplete input box
328
  *
329
  * @since 1.5
330
  */
338
  $post_language = $this->model->get_language( $_GET['post_language'] );
339
  $translation_language = $this->model->get_language( $_GET['translation_language'] );
340
 
341
+ // Don't order by title: see https://wordpress.org/support/topic/find-translated-post-when-10-is-not-enough
342
  $args = array(
343
  's' => wp_unslash( $_GET['term'] ),
344
+ 'suppress_filters' => 0, // To make the post_fields filter work
345
+ 'lang' => 0, // Avoid admin language filter
346
+ 'numberposts' => 20, // Limit to 20 posts
347
  'post_status' => 'any',
348
  'post_type' => $_GET['post_type'],
349
  'tax_query' => array( array(
378
  }
379
  }
380
 
381
+ // Add current translation in list
382
  if ( $post_id = $this->model->post->get_translation( (int) $_GET['pll_post_id'], $translation_language ) ) {
383
  $post = get_post( $post_id );
384
  array_unshift( $return, array(
392
  }
393
 
394
  /**
395
+ * Saves language
396
+ * Checks the terms saved are in the right language
397
  *
398
  * @since 1.5
399
  *
400
+ * @param int $post_id
401
  * @param array $post
402
  */
403
  protected function save_language( $post_id, $post ) {
404
+ // Security checks are necessary to accept language modifications as 'wp_insert_post' can be called from outside WP admin
 
405
 
406
+ // Edit post
407
+ if ( isset( $_POST['post_lang_choice'], $_POST['post_ID'] ) && $_POST['post_ID'] == $post_id ) {
408
  check_admin_referer( 'pll_language', '_pll_nonce' );
409
  $this->model->post->set_language( $post_id, $lang = $this->model->get_language( $_POST['post_lang_choice'] ) );
410
  }
411
 
412
+ // Quick edit and bulk edit
413
  // POST for quick edit, GET for bulk edit
414
  elseif ( isset( $_REQUEST['inline_lang_choice'] ) ) {
415
+ // Bulk edit does not modify the language
416
  if ( isset( $_GET['bulk_edit'] ) && -1 == $_REQUEST['inline_lang_choice'] ) {
417
  check_admin_referer( 'bulk-posts' );
418
+ $lang = $this->model->post->get_language( $post_id ); // Get the post language for later use when saving terms
419
  }
420
+ // A language is set in the language dropdown
421
  else {
422
  isset( $_GET['bulk_edit'] ) ? check_admin_referer( 'bulk-posts' ) : check_admin_referer( 'inlineeditnonce', '_inline_edit' );
423
 
424
+ $old_lang = $this->model->post->get_language( $post_id ); // Stores the old language
425
  $this->model->post->set_language( $post_id, $lang = $this->model->get_language( $_REQUEST['inline_lang_choice'] ) ); // set new language
426
 
427
+ // Checks if the new language already exists in the translation group
428
  if ( $old_lang && $old_lang->slug != $lang->slug ) {
429
  $translations = $this->model->post->get_translations( $post_id );
430
 
431
+ // If yes, separate this post from the translation group
432
  if ( array_key_exists( $lang->slug, $translations ) ) {
433
  $this->model->post->delete_translation( $post_id );
434
  }
441
  }
442
  }
443
 
444
+ // Quick press
445
  // 'post-quickpress-save', 'post-quickpress-publish' = backward compatibility WP < 3.8
446
  elseif ( isset( $_REQUEST['action'] ) && in_array( $_REQUEST['action'], array( 'post-quickpress-save', 'post-quickpress-publish', 'post-quickdraft-save' ) ) ) {
447
  check_admin_referer( 'add-' . $post->post_type );
448
+ $this->model->post->set_language( $post_id, $lang = $this->pref_lang ); // Default language for Quick draft
449
  }
450
 
451
  else {
452
  $this->set_default_language( $post_id );
453
  }
454
 
455
+ // Make sure we get save terms in the right language (especially tags with same name in different languages)
456
  if ( ! empty( $lang ) ) {
457
  // FIXME quite a lot of queries in foreach
458
  foreach ( $this->model->get_translated_taxonomies() as $tax ) {
461
  if ( is_array( $terms ) ) {
462
  $newterms = array();
463
  foreach ( $terms as $term ) {
464
+ // Check if the term is in the correct language or if a translation exist ( mainly for default category )
465
  if ( $newterm = $this->model->term->get( $term->term_id, $lang ) ) {
466
  $newterms[] = (int) $newterm;
467
  }
468
 
469
+ // Or choose the correct language for tags ( initially defined by name )
470
  elseif ( $newterm = $this->model->term_exists( $term->name, $tax, $term->parent, $lang ) ) {
471
+ $newterms[] = (int) $newterm; // Cast is important otherwise we get 'numeric' tags
472
  }
473
 
474
+ // Or create the term in the correct language
475
  elseif ( ! is_wp_error( $term_info = wp_insert_term( $term->name, $tax ) ) ) {
476
  $newterms[] = (int) $term_info['term_id'];
477
  }
484
  }
485
 
486
  /**
487
+ * Called when a post ( or page ) is saved, published or updated
488
  * saves languages and translations
489
  *
490
  * @since 0.1
491
  *
492
+ * @param int $post_id
493
  * @param object $post
494
+ * @param bool $update Whether it is an update or not
495
  */
496
  public function save_post( $post_id, $post, $update ) {
497
+ // Does nothing except on post types which are filterable
498
  if ( ! $this->model->is_translated_post_type( $post->post_type ) ) {
499
  return;
500
  }
503
  $post_id = $id;
504
  }
505
 
506
+ // Capability check
507
+ // As 'wp_insert_post' can be called from outside WP admin
508
  $post_type_object = get_post_type_object( $post->post_type );
509
  if ( ( $update && current_user_can( $post_type_object->cap->edit_post, $post_id ) ) || ( ! $update && current_user_can( $post_type_object->cap->create_posts ) ) ) {
510
  $this->save_language( $post_id, $post );
511
 
512
+ // Make sure we are saving translations only for the main post currently being edited
513
+ if ( isset( $_POST['post_tr_lang'], $_POST['post_ID'] ) && $_POST['post_ID'] == $post_id ) {
514
  $translations = $this->save_translations( $post_id, $_POST['post_tr_lang'] );
515
  }
516
 
519
  *
520
  * @since 1.2
521
  *
522
+ * @param int $post_id Post id
523
+ * @param object $post Post object
524
+ * @param array $translations The list of translations post ids
525
  */
526
  do_action( 'pll_save_post', $post_id, $post, empty( $translations ) ? $this->model->post->get_translations( $post_id ) : $translations );
527
  }
528
 
529
+ // Attempts to set a default language even if no capability
530
  else {
531
  $this->set_default_language( $post_id );
532
  }
533
  }
534
 
535
  /**
536
+ * Make sure that the post parent is in the correct language when using bulk edit
537
  *
538
  * @since 1.8
539
  *
555
  }
556
 
557
  /**
558
+ * Called when a post, page or media is deleted
559
+ * Don't delete translations if this is a post revision thanks to AndyDeGroo who catched this bug
560
  * http://wordpress.org/support/topic/plugin-polylang-quick-edit-still-breaks-translation-linking-of-pages-in-072
561
  *
562
  * @since 0.1
570
  }
571
 
572
  /**
573
+ * Filters the pages by language in the parent dropdown list in the page attributes metabox
574
  *
575
  * @since 0.6
576
  *
577
+ * @param array $dropdown_args Arguments passed to wp_dropdown_pages
578
  * @param object $post
579
+ * @return array Modified arguments
580
  */
581
  public function page_attributes_dropdown_pages_args( $dropdown_args, $post ) {
582
  $dropdown_args['lang'] = isset( $_POST['lang'] ) ? $this->model->get_language( $_POST['lang'] ) : $this->model->post->get_language( $post->ID ); // ajax or not ?
admin/admin-filters-term.php CHANGED
@@ -399,13 +399,6 @@ class PLL_Admin_Filters_Term {
399
  public function pre_term_slug( $slug, $taxonomy ) {
400
  $name = sanitize_title( $this->pre_term_name );
401
 
402
- // If the new term has the same name as a language, we *need* to differentiate the term
403
- // See http://core.trac.wordpress.org/ticket/23199
404
- // Backward compatibility with WP < 4.1
405
- if ( version_compare( $GLOBALS['wp_version'], '4.1', '<' ) && term_exists( $name, 'language' ) && ! term_exists( $name, $taxonomy ) && ( ! $slug || $slug == $name ) ) {
406
- $slug = $name . '-' . $taxonomy; // A convenient slug which may be modified later by the user
407
- }
408
-
409
  // If the term already exists in another language
410
  if ( ! $slug && $this->model->is_translated_taxonomy( $taxonomy ) && term_exists( $name, $taxonomy ) ) {
411
  if ( isset( $_POST['term_lang_choice'] ) ) {
@@ -421,8 +414,7 @@ class PLL_Admin_Filters_Term {
421
  // Bulk edit does not modify the language
422
  if ( -1 == $_GET['inline_lang_choice'] ) {
423
  $slug = $name . '-' . $this->model->post->get_language( $this->post_id )->slug;
424
- }
425
- else {
426
  $slug = $name . '-' . $this->model->get_language( $_GET['inline_lang_choice'] )->slug;
427
  }
428
  }
@@ -633,7 +625,7 @@ class PLL_Admin_Filters_Term {
633
  */
634
  public function option_default_category( $value ) {
635
  // Filters the default category in note below the category list table and in settings->writing dropdown
636
- if ( isset( $this->pref_lang) && $tr = $this->model->term->get( $value, $this->pref_lang ) ) {
637
  $value = $tr;
638
  }
639
 
399
  public function pre_term_slug( $slug, $taxonomy ) {
400
  $name = sanitize_title( $this->pre_term_name );
401
 
 
 
 
 
 
 
 
402
  // If the term already exists in another language
403
  if ( ! $slug && $this->model->is_translated_taxonomy( $taxonomy ) && term_exists( $name, $taxonomy ) ) {
404
  if ( isset( $_POST['term_lang_choice'] ) ) {
414
  // Bulk edit does not modify the language
415
  if ( -1 == $_GET['inline_lang_choice'] ) {
416
  $slug = $name . '-' . $this->model->post->get_language( $this->post_id )->slug;
417
+ } else {
 
418
  $slug = $name . '-' . $this->model->get_language( $_GET['inline_lang_choice'] )->slug;
419
  }
420
  }
625
  */
626
  public function option_default_category( $value ) {
627
  // Filters the default category in note below the category list table and in settings->writing dropdown
628
+ if ( isset( $this->pref_lang ) && $tr = $this->model->term->get( $value, $this->pref_lang ) ) {
629
  $value = $tr;
630
  }
631
 
admin/admin-nav-menu.php CHANGED
@@ -25,12 +25,6 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
25
 
26
  // integration in the WP menu interface
27
  add_action( 'admin_init', array( $this, 'admin_init' ) ); // after Polylang upgrade
28
-
29
- // protection against #24802
30
- // backward compatibility with WP < 4.1
31
- if ( version_compare( $GLOBALS['wp_version'], '4.1', '<' ) ) {
32
- add_filter( 'pre_insert_term', array( $this, 'pre_insert_term' ), 10, 2 );
33
- }
34
  }
35
 
36
  /**
@@ -103,7 +97,7 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
103
  }
104
 
105
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
106
- wp_enqueue_script( 'pll_nav_menu', POLYLANG_URL .'/js/nav-menu' . $suffix . '.js', array( 'jquery' ), POLYLANG_VERSION );
107
 
108
  $data['strings'] = PLL_Switcher::get_switcher_options( 'menu', 'string' ); // the strings for the options
109
  $data['title'] = __( 'Language switcher', 'polylang' ); // the title
@@ -296,7 +290,7 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
296
  * @return array Modified options
297
  */
298
  public function nav_menu_options( $options ) {
299
- $options['auto_add'] = array_intersect( $options['auto_add'], array( $this->auto_add_menus ) );
300
  return $options;
301
  }
302
 
@@ -315,33 +309,16 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
315
  }
316
 
317
  if ( ! empty( $this->options['nav_menus'][ $this->theme ] ) ) {
 
 
318
  // get all the menus in the page language
319
  foreach ( $this->options['nav_menus'][ $this->theme ] as $loc ) {
320
  if ( ! empty( $loc[ $lang->slug ] ) ) {
321
- $menus[] = $loc[ $lang->slug ];
322
  }
323
  }
324
 
325
- if ( ! empty( $menus ) ) {
326
- $this->auto_add_menus = implode( ',', $menus );
327
- add_filter( 'option_nav_menu_options', array( $this, 'nav_menu_options' ) );
328
- }
329
  }
330
  }
331
-
332
- /**
333
- * prevents sharing a menu term with a language term by renaming the nav menu before its creation
334
- * to avoid http://core.trac.wordpress.org/ticket/24802
335
- * and http://wordpress.org/support/topic/all-connection-between-elements-lost
336
- * backward compatibility with WP < 4.1
337
- *
338
- * @since 1.1.3
339
- *
340
- * @param string $name term name
341
- * @param string $taxonomy
342
- * @return string modified ( nav menu ) term name if necessary
343
- */
344
- function pre_insert_term( $name, $taxonomy ) {
345
- return ( 'nav_menu' == $taxonomy && in_array( $name, $this->model->get_languages_list( array( 'fields' => 'name' ) ) ) ) ? $name .= '-menu' : $name;
346
- }
347
  }
25
 
26
  // integration in the WP menu interface
27
  add_action( 'admin_init', array( $this, 'admin_init' ) ); // after Polylang upgrade
 
 
 
 
 
 
28
  }
29
 
30
  /**
97
  }
98
 
99
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
100
+ wp_enqueue_script( 'pll_nav_menu', plugins_url( '/js/nav-menu' . $suffix . '.js', POLYLANG_FILE ), array( 'jquery' ), POLYLANG_VERSION );
101
 
102
  $data['strings'] = PLL_Switcher::get_switcher_options( 'menu', 'string' ); // the strings for the options
103
  $data['title'] = __( 'Language switcher', 'polylang' ); // the title
290
  * @return array Modified options
291
  */
292
  public function nav_menu_options( $options ) {
293
+ $options['auto_add'] = array_intersect( $options['auto_add'], $this->auto_add_menus );
294
  return $options;
295
  }
296
 
309
  }
310
 
311
  if ( ! empty( $this->options['nav_menus'][ $this->theme ] ) ) {
312
+ $this->auto_add_menus = array();
313
+
314
  // get all the menus in the page language
315
  foreach ( $this->options['nav_menus'][ $this->theme ] as $loc ) {
316
  if ( ! empty( $loc[ $lang->slug ] ) ) {
317
+ $this->auto_add_menus[] = $loc[ $lang->slug ];
318
  }
319
  }
320
 
321
+ add_filter( 'option_nav_menu_options', array( $this, 'nav_menu_options' ) );
 
 
 
322
  }
323
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  }
admin/view-translations-post.php CHANGED
@@ -34,7 +34,15 @@ if ( ! defined( 'ABSPATH' ) ) {
34
  <tr>
35
  <th class = "pll-language-column"><?php echo $language->flag ? $language->flag : esc_html( $language->slug ); ?></th>
36
  <td class = "hidden"><?php echo $add_link;?></td>
37
- <td class = "pll-edit-column"><?php echo $link;?></td>
 
 
 
 
 
 
 
 
38
  <td class = "pll-translation-column"><?php
39
  printf( '
40
  <label class="screen-reader-text" for="tr_lang_%1$s">%2$s</label>
34
  <tr>
35
  <th class = "pll-language-column"><?php echo $language->flag ? $language->flag : esc_html( $language->slug ); ?></th>
36
  <td class = "hidden"><?php echo $add_link;?></td>
37
+ <td class = "pll-edit-column pll-column-icon"><?php echo $link;?></td><?php
38
+
39
+ /**
40
+ * Fires before the translation colummn is outputed in the language metabox
41
+ * The dynamic portion of the hook name, `$lang`, refers to the language code
42
+ *
43
+ * @since 2.1
44
+ */
45
+ do_action( 'pll_before_post_translation_' . $language->slug ); ?>
46
  <td class = "pll-translation-column"><?php
47
  printf( '
48
  <label class="screen-reader-text" for="tr_lang_%1$s">%2$s</label>
css/admin.css CHANGED
@@ -40,10 +40,6 @@
40
  }
41
 
42
  /* strings translation table */
43
- #string-translation .search-box {
44
- margin: 16px 0 8px 0;
45
- }
46
-
47
  .stringstranslations .column-name,
48
  .stringstranslations .column-context {
49
  width: 10%;
@@ -65,6 +61,10 @@
65
  }
66
 
67
  /* settings */
 
 
 
 
68
  .pll-settings .plugin-title {
69
  width: 25%;
70
  }
@@ -165,7 +165,7 @@ td[class*='column-language_'] {
165
  }
166
 
167
  #post-translations .pll-language-column,
168
- #post-translations .pll-edit-column {
169
  width: 20px;
170
  }
171
 
@@ -185,7 +185,7 @@ td[class*='column-language_'] {
185
  width: auto;
186
  }
187
 
188
- .pll-edit-column {
189
  text-align: center;
190
  }
191
 
40
  }
41
 
42
  /* strings translation table */
 
 
 
 
43
  .stringstranslations .column-name,
44
  .stringstranslations .column-context {
45
  width: 10%;
61
  }
62
 
63
  /* settings */
64
+ .pll-settings {
65
+ margin-top: 20px;
66
+ }
67
+
68
  .pll-settings .plugin-title {
69
  width: 25%;
70
  }
165
  }
166
 
167
  #post-translations .pll-language-column,
168
+ #post-translations .pll-column-icon {
169
  width: 20px;
170
  }
171
 
185
  width: auto;
186
  }
187
 
188
+ .pll-column-icon {
189
  text-align: center;
190
  }
191
 
css/admin.min.css CHANGED
@@ -1 +1 @@
1
- #add-lang select{width:95%}.column-locale,.languages .column-slug{width:15%}.column-default_lang{width:5%}.column-term_group,.column-flag,.column-count{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}#string-translation .search-box{margin:16px 0 8px 0}.stringstranslations .column-name,.stringstranslations .column-context{width:10%}.stringstranslations .column-string{width:33%}.translation label{display:inline-block;width:23%;vertical-align:top}.translation input,.translation textarea{width:72%}.pll-settings .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:0}.pll-settings-url-col{display:inline-block;width:49%;vertical-align:top}#pll-licenses-table td{vertical-align:top}#pll-licenses-table label{font-size:1em;font-weight:600}.pll-configure .pll-deactivate-license{margin:0 0 0 20px}th[class*='column-language_'],td[class*='column-language_']{width:1.5em}#post-translations p{float:left}#post-translations table{table-layout:fixed;width:100%;clear:both}#post-translations a{text-decoration:none}#post-translations .pll-language-column,#post-translations .pll-edit-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-edit-column{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%}#term-translations .pll-edit-column,#add-term-translations .pll-language-column{width:20px}#edit-term-translations .pll-language-column{padding:15px 10px;font-weight:normal}.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%}#select-add-term-language .pll-select-flag,#select-edit-term-language .pll-select-flag,#edit-term-translations .pll-language-name{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-language-column,#edit-term-translations .pll-edit-column{width:20px}.term-translations .pll-language-column,.term-translations .pll-edit-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}#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}#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}}
frontend/choose-lang-domain.php CHANGED
@@ -12,7 +12,7 @@ class PLL_Choose_Lang_Domain extends PLL_Choose_Lang_Url {
12
  *
13
  * @since 1.5
14
  */
15
- protected function maybe_setcookie() {}
16
 
17
  /**
18
  * don't redirect according to browser preferences
12
  *
13
  * @since 1.5
14
  */
15
+ public function maybe_setcookie() {}
16
 
17
  /**
18
  * don't redirect according to browser preferences
frontend/choose-lang.php CHANGED
@@ -38,6 +38,7 @@ abstract class PLL_Choose_Lang {
38
 
39
  add_action( 'pre_comment_on_post', array( $this, 'pre_comment_on_post' ) ); // sets the language of comment
40
  add_action( 'parse_query', array( $this, 'parse_main_query' ), 2 ); // sets the language in special cases
 
41
  }
42
 
43
  /**
@@ -59,8 +60,6 @@ abstract class PLL_Choose_Lang {
59
  // see https://wordpress.org/support/topic/detect-browser-language-sometimes-setting-null-language
60
  $this->curlang = ( $curlang instanceof PLL_Language ) ? $curlang : $this->model->get_language( $this->options['default_lang'] );
61
 
62
- $this->maybe_setcookie();
63
-
64
  $GLOBALS['text_direction'] = $this->curlang->is_rtl ? 'rtl' : 'ltr';
65
 
66
  /**
@@ -80,10 +79,10 @@ abstract class PLL_Choose_Lang {
80
  *
81
  * @since 1.5
82
  */
83
- protected function maybe_setcookie() {
84
  // check headers have not been sent to avoid ugly error
85
  // cookie domain must be set to false for localhost ( default value for COOKIE_DOMAIN ) thanks to Stephen Harris.
86
- if ( ! headers_sent() && PLL_COOKIE !== false && ( ! isset( $_COOKIE[ PLL_COOKIE ] ) || $_COOKIE[ PLL_COOKIE ] != $this->curlang->slug ) ) {
87
 
88
  /**
89
  * Filter the Polylang cookie duration
38
 
39
  add_action( 'pre_comment_on_post', array( $this, 'pre_comment_on_post' ) ); // sets the language of comment
40
  add_action( 'parse_query', array( $this, 'parse_main_query' ), 2 ); // sets the language in special cases
41
+ add_action( 'wp', array( $this, 'maybe_setcookie' ), 7 );
42
  }
43
 
44
  /**
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';
64
 
65
  /**
79
  *
80
  * @since 1.5
81
  */
82
+ public function maybe_setcookie() {
83
  // check headers have not been sent to avoid ugly error
84
  // cookie domain must be set to false for localhost ( default value for COOKIE_DOMAIN ) thanks to Stephen Harris.
85
+ if ( ! headers_sent() && PLL_COOKIE !== false && ! empty( $this->curlang ) && ( ! isset( $_COOKIE[ PLL_COOKIE ] ) || $_COOKIE[ PLL_COOKIE ] != $this->curlang->slug ) && ! is_404() ) {
86
 
87
  /**
88
  * Filter the Polylang cookie duration
frontend/frontend-filters-links.php CHANGED
@@ -190,6 +190,12 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
190
  * @since 0.1
191
  */
192
  public function wp_head() {
 
 
 
 
 
 
193
  // Google recommends to include self link https://support.google.com/webmasters/answer/189077?hl=en
194
  foreach ( $this->model->get_languages_list() as $language ) {
195
  if ( $url = $this->links->get_translation_url( $language ) ) {
@@ -198,8 +204,8 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
198
  }
199
 
200
  // Ouptputs the section only if there are translations ( $urls always contains self link )
201
- // Don't output anything on paged archives: see https://wordpress.org/support/topic/hreflang-on-page2
202
- if ( ! empty( $urls ) && count( $urls ) > 1 && ! is_paged() ) {
203
  // Prepare the list of languages to remove the country code
204
  foreach ( array_keys( $urls ) as $locale ) {
205
  $split = explode( '-', $locale );
@@ -210,13 +216,26 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
210
 
211
  foreach ( $urls as $locale => $url ) {
212
  $lang = $count[ $languages[ $locale ] ] > 1 ? $locale : $languages[ $locale ]; // Output the country code only when necessary
213
- printf( '<link rel="alternate" href="%s" hreflang="%s" />'."\n", esc_url( $url ), esc_attr( $lang ) );
214
  }
215
 
216
  // Adds the site root url when the default language code is not hidden
217
  // See https://wordpress.org/support/topic/implementation-of-hreflangx-default
218
  if ( is_front_page() && ! $this->options['hide_default'] && $this->options['force_lang'] < 3 ) {
219
- printf( '<link rel="alternate" href="%s" hreflang="x-default" />'."\n", esc_url( home_url( '/' ) ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  }
221
  }
222
  }
190
  * @since 0.1
191
  */
192
  public function wp_head() {
193
+ // Don't output anything on paged archives: see https://wordpress.org/support/topic/hreflang-on-page2
194
+ // Don't output anything on paged pages and paged posts
195
+ if ( is_paged() || ( is_singular() && ( $page = get_query_var( 'page' ) ) && $page > 1 ) ) {
196
+ return;
197
+ }
198
+
199
  // Google recommends to include self link https://support.google.com/webmasters/answer/189077?hl=en
200
  foreach ( $this->model->get_languages_list() as $language ) {
201
  if ( $url = $this->links->get_translation_url( $language ) ) {
204
  }
205
 
206
  // Ouptputs the section only if there are translations ( $urls always contains self link )
207
+ if ( ! empty( $urls ) && count( $urls ) > 1 ) {
208
+
209
  // Prepare the list of languages to remove the country code
210
  foreach ( array_keys( $urls ) as $locale ) {
211
  $split = explode( '-', $locale );
216
 
217
  foreach ( $urls as $locale => $url ) {
218
  $lang = $count[ $languages[ $locale ] ] > 1 ? $locale : $languages[ $locale ]; // Output the country code only when necessary
219
+ $hreflangs[ $lang ] = $url;
220
  }
221
 
222
  // Adds the site root url when the default language code is not hidden
223
  // See https://wordpress.org/support/topic/implementation-of-hreflangx-default
224
  if ( is_front_page() && ! $this->options['hide_default'] && $this->options['force_lang'] < 3 ) {
225
+ $hreflangs['x-default'] = home_url( '/' );
226
+ }
227
+
228
+ /**
229
+ * Filters the list of rel hreflang attributes
230
+ *
231
+ * @since 2.1
232
+ *
233
+ * @param array $hreflangs Array of urls with language codes as keys
234
+ */
235
+ $hreflangs = apply_filters( 'pll_rel_hreflang_attributes', $hreflangs );
236
+
237
+ foreach ( $hreflangs as $lang => $url ) {
238
+ printf( '<link rel="alternate" href="%s" hreflang="%s" />'."\n", esc_url( $url ), esc_attr( $lang ) );
239
  }
240
  }
241
  }
frontend/frontend-filters.php CHANGED
@@ -39,6 +39,7 @@ class PLL_Frontend_Filters extends PLL_Filters{
39
 
40
  // Filters the widgets according to the current language
41
  add_filter( 'widget_display_callback', array( $this, 'widget_display_callback' ), 10, 2 );
 
42
 
43
  // Strings translation ( must be applied before WordPress applies its default formatting filters )
44
  foreach ( array( 'widget_text', 'widget_title', 'option_blogname', 'option_blogdescription', 'option_date_format', 'option_time_format' ) as $filter ) {
@@ -174,8 +175,7 @@ class PLL_Frontend_Filters extends PLL_Filters{
174
  * @return string modified JOIN clause
175
  */
176
  public function posts_join( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) {
177
- // FIXME empty( $post ) for backward compatibility with WP < 4.4
178
- return empty( $post ) || $this->model->is_translated_post_type( $post->post_type ) ? $sql . $this->model->post->join_clause( 'p' ) : $sql;
179
  }
180
 
181
  /**
@@ -191,15 +191,7 @@ class PLL_Frontend_Filters extends PLL_Filters{
191
  * @return string modified WHERE clause
192
  */
193
  public function posts_where( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) {
194
- // backward compatibility with WP < 4.4
195
- if ( version_compare( $GLOBALS['wp_version'], '4.4', '<' ) ) {
196
- preg_match( "#post_type = '([^']+)'#", $sql, $matches ); // find the queried post type
197
- $post_type = $matches[1];
198
- } else {
199
- $post_type = $post->post_type;
200
- }
201
-
202
- return ! empty( $post_type ) && $this->model->is_translated_post_type( $post_type ) ? $sql . $this->model->post->where_clause( $this->curlang ) : $sql;
203
  }
204
 
205
  /**
@@ -216,6 +208,43 @@ class PLL_Frontend_Filters extends PLL_Filters{
216
  return ! empty( $instance['pll_lang'] ) && $instance['pll_lang'] != $this->curlang->slug ? false : $instance;
217
  }
218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  /**
220
  * Translates biography
221
  *
39
 
40
  // Filters the widgets according to the current language
41
  add_filter( 'widget_display_callback', array( $this, 'widget_display_callback' ), 10, 2 );
42
+ add_filter( 'sidebars_widgets', array( $this, 'sidebars_widgets' ) );
43
 
44
  // Strings translation ( must be applied before WordPress applies its default formatting filters )
45
  foreach ( array( 'widget_text', 'widget_title', 'option_blogname', 'option_blogdescription', 'option_date_format', 'option_time_format' ) as $filter ) {
175
  * @return string modified JOIN clause
176
  */
177
  public function posts_join( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) {
178
+ return $this->model->is_translated_post_type( $post->post_type ) ? $sql . $this->model->post->join_clause( 'p' ) : $sql;
 
179
  }
180
 
181
  /**
191
  * @return string modified WHERE clause
192
  */
193
  public function posts_where( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) {
194
+ return $this->model->is_translated_post_type( $post->post_type ) ? $sql . $this->model->post->where_clause( $this->curlang ) : $sql;
 
 
 
 
 
 
 
 
195
  }
196
 
197
  /**
208
  return ! empty( $instance['pll_lang'] ) && $instance['pll_lang'] != $this->curlang->slug ? false : $instance;
209
  }
210
 
211
+ /**
212
+ * Remove widgets from sidebars if they are not visible in the current language
213
+ * Needed to allow is_active_sidebar() to return false if all widgets are not for the current language. See #54
214
+ *
215
+ * @since 2.1
216
+ *
217
+ * @param array $sidebars_widgets An associative array of sidebars and their widgets
218
+ * @return array
219
+ */
220
+ public function sidebars_widgets( $sidebars_widgets ) {
221
+ global $wp_registered_widgets;
222
+
223
+ foreach ( $sidebars_widgets as $sidebar => $widgets ) {
224
+ if ( 'wp_inactive_widgets' == $sidebar || empty( $widgets ) ) {
225
+ continue;
226
+ }
227
+
228
+ foreach ( $widgets as $key => $widget ) {
229
+ // Nothing can be done if the widget is created using pre WP2.8 API :(
230
+ // There is no object, so we can't access it to get the widget options
231
+ 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' ) ) {
232
+ continue;
233
+ }
234
+
235
+ $widget_settings = $wp_registered_widgets[ $widget ]['callback'][0]->get_settings();
236
+ $number = $wp_registered_widgets[ $widget ]['params'][0]['number'];
237
+
238
+ // Remove the widget if not visible in the current language
239
+ if ( ! empty( $widget_settings[ $number ]['pll_lang'] ) && $widget_settings[ $number ]['pll_lang'] !== $this->curlang->slug ) {
240
+ unset( $sidebars_widgets[ $sidebar ][ $key ] );
241
+ }
242
+ }
243
+ }
244
+
245
+ return $sidebars_widgets;
246
+ }
247
+
248
  /**
249
  * Translates biography
250
  *
frontend/frontend-static-pages.php CHANGED
@@ -175,7 +175,7 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
175
  }
176
 
177
  // Redirect the language page to the homepage when using a static front page
178
- elseif ( ( $this->options['redirect_lang'] || $this->options['hide_default'] ) && ( count( $query->query ) == 1 || ( ( is_paged() || ! empty( $query->query['page'] ) ) && count( $query->query ) == 2 ) ) && is_tax( 'language' ) ) {
179
  $lang = $this->model->get_language( get_query_var( 'lang' ) );
180
  $query->set( 'page_id', $lang->page_on_front );
181
  $query->is_singular = $query->is_page = true;
175
  }
176
 
177
  // Redirect the language page to the homepage when using a static front page
178
+ elseif ( ( $this->options['redirect_lang'] || $this->options['hide_default'] ) && ( count( $query->query ) == 1 || ( ( is_preview() || is_paged() || ! empty( $query->query['page'] ) ) && count( $query->query ) == 2 ) || ( ( is_preview() && ( is_paged() || ! empty( $query->query['page'] ) ) ) && count( $query->query ) == 3 ) ) && is_tax( 'language' ) ) {
179
  $lang = $this->model->get_language( get_query_var( 'lang' ) );
180
  $query->set( 'page_id', $lang->page_on_front );
181
  $query->is_singular = $query->is_page = true;
frontend/frontend.php CHANGED
@@ -104,6 +104,7 @@ class PLL_Frontend extends PLL_Base {
104
  */
105
  public function parse_query( $query ) {
106
  $qv = $query->query_vars;
 
107
 
108
  // to avoid returning an empty result if the query includes a translated taxonomy in a different language
109
  $has_tax = isset( $query->tax_query->queries ) && $this->model->have_translated_taxonomy( $query->tax_query->queries );
@@ -113,8 +114,6 @@ class PLL_Frontend extends PLL_Base {
113
  // do not filter if lang is set to an empty value
114
  // do not filter single page and translated taxonomies to avoid conflicts
115
  if ( ! empty( $this->curlang ) && ! isset( $qv['lang'] ) && ! $has_tax && empty( $qv['page_id'] ) && empty( $qv['pagename'] ) ) {
116
- $taxonomies = $this->get_queried_taxonomies( $query );
117
-
118
  if ( $taxonomies && ( empty( $qv['post_type'] ) || 'any' === $qv['post_type'] ) ) {
119
  foreach ( $taxonomies as $taxonomy ) {
120
  $tax_object = get_taxonomy( $taxonomy );
@@ -129,9 +128,9 @@ class PLL_Frontend extends PLL_Base {
129
  }
130
 
131
  // modifies query vars when the language is queried
132
- if ( ! empty( $qv['lang'] ) ) {
133
  // do we query a custom taxonomy?
134
- $taxonomies = array_diff( $this->get_queried_taxonomies( $query ) , array( 'language', 'category', 'post_tag' ) );
135
 
136
  // remove pages query when the language is set unless we do a search
137
  // take care not to break the single page, attachment and taxonomies queries!
104
  */
105
  public function parse_query( $query ) {
106
  $qv = $query->query_vars;
107
+ $taxonomies = $this->get_queried_taxonomies( $query );
108
 
109
  // to avoid returning an empty result if the query includes a translated taxonomy in a different language
110
  $has_tax = isset( $query->tax_query->queries ) && $this->model->have_translated_taxonomy( $query->tax_query->queries );
114
  // do not filter if lang is set to an empty value
115
  // do not filter single page and translated taxonomies to avoid conflicts
116
  if ( ! empty( $this->curlang ) && ! isset( $qv['lang'] ) && ! $has_tax && empty( $qv['page_id'] ) && empty( $qv['pagename'] ) ) {
 
 
117
  if ( $taxonomies && ( empty( $qv['post_type'] ) || 'any' === $qv['post_type'] ) ) {
118
  foreach ( $taxonomies as $taxonomy ) {
119
  $tax_object = get_taxonomy( $taxonomy );
128
  }
129
 
130
  // modifies query vars when the language is queried
131
+ if ( ! empty( $qv['lang'] ) || ( ! empty( $taxonomies ) && array( 'language') == array_values( $taxonomies ) ) ) {
132
  // do we query a custom taxonomy?
133
+ $taxonomies = array_diff( $taxonomies , array( 'language', 'category', 'post_tag' ) );
134
 
135
  // remove pages query when the language is set unless we do a search
136
  // take care not to break the single page, attachment and taxonomies queries!
include/api.php CHANGED
@@ -122,22 +122,35 @@ function pll_register_string( $name, $string, $context = 'polylang', $multiline
122
  * @return string the string translation in the current language
123
  */
124
  function pll__( $string ) {
125
- static $cache; // Cache object to avoid translating the same string several times
126
-
127
  if ( ! did_action( 'pll_language_defined' ) || ! is_scalar( $string ) ) { // No need for translation
128
  return $string;
129
  }
130
 
131
- if ( empty( $cache ) ) {
132
- $cache = new PLL_Cache();
133
- }
134
 
135
- if ( false === $str = $cache->get( $string ) ) {
136
- $str = __( $string, 'pll_string' );
137
- $cache->set( $string, $str );
138
- }
 
 
 
 
 
 
 
139
 
140
- return $str;
 
 
 
 
 
 
 
 
 
141
  }
142
 
143
  /**
@@ -151,6 +164,28 @@ function pll_e( $string ) {
151
  echo pll__( $string );
152
  }
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  /**
155
  * Translates a string ( previously registered with pll_register_string )
156
  *
122
  * @return string the string translation in the current language
123
  */
124
  function pll__( $string ) {
 
 
125
  if ( ! did_action( 'pll_language_defined' ) || ! is_scalar( $string ) ) { // No need for translation
126
  return $string;
127
  }
128
 
129
+ return __( $string, 'pll_string' );
130
+ }
 
131
 
132
+ /**
133
+ * Translates a string ( previously registered with pll_register_string ) and escapes it for safe use in HTML output.
134
+ *
135
+ * @since 2.1
136
+ *
137
+ * @param string $string the string to translate
138
+ * @return string translation in the current language
139
+ */
140
+ function pll_esc_html__( $string ) {
141
+ return esc_html( pll__( $string ) );
142
+ }
143
 
144
+ /**
145
+ * Translates a string ( previously registered with pll_register_string ) and escapes it for safe use in HTML attributes.
146
+ *
147
+ * @since 2.1
148
+ *
149
+ * @param $string
150
+ * @return string
151
+ */
152
+ function pll_esc_attr__( $string ) {
153
+ return esc_attr( pll__( $string ) );
154
  }
155
 
156
  /**
164
  echo pll__( $string );
165
  }
166
 
167
+ /**
168
+ * Echoes a translated string ( previously registered with pll_register_string ) and escapes it for safe use in HTML output.
169
+ *
170
+ * @since 2.1
171
+ *
172
+ * @param string $string the string to translate
173
+ */
174
+ function pll_esc_html_e( $string ) {
175
+ echo pll_esc_html__( $string );
176
+ }
177
+
178
+ /**
179
+ * Echoes a translated a string ( previously registered with pll_register_string ) and escapes it for safe use in HTML attributes.
180
+ *
181
+ * @since 2.1
182
+ *
183
+ * @param $string
184
+ */
185
+ function pll_esc_attr_e( $string ) {
186
+ echo pll_esc_attr__( $string );
187
+ }
188
+
189
  /**
190
  * Translates a string ( previously registered with pll_register_string )
191
  *
include/base.php CHANGED
@@ -44,21 +44,6 @@ abstract class PLL_Base {
44
  unregister_widget( 'WP_Widget_Calendar' );
45
  register_widget( 'PLL_Widget_Calendar' );
46
  }
47
-
48
- // Backward compatibility with WP < 4.4
49
- // Overwrites the recent posts and recent comments widget to use a language dependant cache key
50
- // Useful only if using a cache plugin
51
- if ( version_compare( $GLOBALS['wp_version'], '4.4', '<' ) && defined( 'WP_CACHE' ) && WP_CACHE ) {
52
- if ( ! defined( 'PLL_WIDGET_RECENT_POSTS' ) || PLL_WIDGET_RECENT_POSTS ) {
53
- unregister_widget( 'WP_Widget_Recent_Posts' );
54
- register_widget( 'PLL_Widget_Recent_Posts' );
55
- }
56
-
57
- if ( ! defined( 'PLL_WIDGET_RECENT_COMMENTS' ) || PLL_WIDGET_RECENT_COMMENTS ) {
58
- unregister_widget( 'WP_Widget_Recent_Comments' );
59
- register_widget( 'PLL_Widget_Recent_Comments' );
60
- }
61
- }
62
  }
63
 
64
  /**
44
  unregister_widget( 'WP_Widget_Calendar' );
45
  register_widget( 'PLL_Widget_Calendar' );
46
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  }
48
 
49
  /**
include/class-polylang.php CHANGED
@@ -97,16 +97,6 @@ class Polylang {
97
  * @since 1.6
98
  */
99
  static public function define_constants() {
100
- // our url. Don't use WP_PLUGIN_URL http://wordpress.org/support/topic/ssl-doesnt-work-properly
101
- if ( ! defined( 'POLYLANG_URL' ) ) {
102
- define( 'POLYLANG_URL', plugins_url( '', POLYLANG_FILE ) );
103
- }
104
-
105
- // default url to access user data such as custom flags
106
- if ( ! defined( 'PLL_LOCAL_URL' ) ) {
107
- define( 'PLL_LOCAL_URL', content_url( '/polylang' ) );
108
- }
109
-
110
  // cookie name. no cookie will be used if set to false
111
  if ( ! defined( 'PLL_COOKIE' ) ) {
112
  define( 'PLL_COOKIE', 'pll_language' );
@@ -127,7 +117,7 @@ class Polylang {
127
 
128
  // settings page whatever the tab
129
  if ( ! defined( 'PLL_SETTINGS' ) ) {
130
- define( 'PLL_SETTINGS', is_admin() && ( ( isset( $_GET['page'] ) && 'mlang' == $_GET['page'] ) || ! empty( $_REQUEST['pll_ajax_settings'] ) ) );
131
  }
132
  }
133
 
97
  * @since 1.6
98
  */
99
  static public function define_constants() {
 
 
 
 
 
 
 
 
 
 
100
  // cookie name. no cookie will be used if set to false
101
  if ( ! defined( 'PLL_COOKIE' ) ) {
102
  define( 'PLL_COOKIE', 'pll_language' );
117
 
118
  // settings page whatever the tab
119
  if ( ! defined( 'PLL_SETTINGS' ) ) {
120
+ define( 'PLL_SETTINGS', is_admin() && ( ( isset( $_GET['page'] ) && 0 === strpos( $_GET['page'], 'mlang' ) ) || ! empty( $_REQUEST['pll_ajax_settings'] ) ) );
121
  }
122
  }
123
 
include/filters.php CHANGED
@@ -157,6 +157,8 @@ class PLL_Filters {
157
  unset( $pages[ $key ] );
158
  }
159
  }
 
 
160
  }
161
 
162
  // Not done by WP but extremely useful for performance when manipulating taxonomies
157
  unset( $pages[ $key ] );
158
  }
159
  }
160
+
161
+ $pages = array_values( $pages ); // In case 3rd parties suppose the existence of $pages[0]
162
  }
163
 
164
  // Not done by WP but extremely useful for performance when manipulating taxonomies
include/language.php CHANGED
@@ -80,7 +80,6 @@ class PLL_Language {
80
  $this->description = &$this->locale; // backward compatibility with Polylang < 1.2
81
 
82
  $this->mo_id = PLL_MO::get_id( $this );
83
- $this->set_flag();
84
  }
85
  }
86
 
@@ -94,20 +93,21 @@ class PLL_Language {
94
 
95
  // Polylang builtin flags
96
  if ( ! empty( $this->flag_code ) && file_exists( POLYLANG_DIR . ( $file = '/flags/' . $this->flag_code . '.png' ) ) ) {
97
- $flags['flag']['url'] = esc_url_raw( POLYLANG_URL . $file );
98
 
99
  // if base64 encoded flags are preferred
100
  if ( ! defined( 'PLL_ENCODED_FLAGS' ) || PLL_ENCODED_FLAGS ) {
101
  $flags['flag']['src'] = 'data:image/png;base64,' . base64_encode( file_get_contents( POLYLANG_DIR . $file ) );
102
  } else {
103
- $flags['flag']['src'] = esc_url( POLYLANG_URL . $file );
104
  }
105
  }
106
 
107
  // custom flags ?
108
  if ( file_exists( PLL_LOCAL_DIR . ( $file = '/' . $this->locale . '.png' ) ) || file_exists( PLL_LOCAL_DIR . ( $file = '/' . $this->locale . '.jpg' ) ) ) {
109
- $flags['custom_flag']['url'] = esc_url_raw( PLL_LOCAL_URL . $file );
110
- $flags['custom_flag']['src'] = esc_url( PLL_LOCAL_URL . $file );
 
111
  }
112
 
113
  /**
80
  $this->description = &$this->locale; // backward compatibility with Polylang < 1.2
81
 
82
  $this->mo_id = PLL_MO::get_id( $this );
 
83
  }
84
  }
85
 
93
 
94
  // Polylang builtin flags
95
  if ( ! empty( $this->flag_code ) && file_exists( POLYLANG_DIR . ( $file = '/flags/' . $this->flag_code . '.png' ) ) ) {
96
+ $flags['flag']['url'] = esc_url_raw( plugins_url( $file, POLYLANG_FILE ) );
97
 
98
  // if base64 encoded flags are preferred
99
  if ( ! defined( 'PLL_ENCODED_FLAGS' ) || PLL_ENCODED_FLAGS ) {
100
  $flags['flag']['src'] = 'data:image/png;base64,' . base64_encode( file_get_contents( POLYLANG_DIR . $file ) );
101
  } else {
102
+ $flags['flag']['src'] = esc_url( plugins_url( $file, POLYLANG_FILE ) );
103
  }
104
  }
105
 
106
  // custom flags ?
107
  if ( file_exists( PLL_LOCAL_DIR . ( $file = '/' . $this->locale . '.png' ) ) || file_exists( PLL_LOCAL_DIR . ( $file = '/' . $this->locale . '.jpg' ) ) ) {
108
+ $url = content_url( '/polylang' . $file );
109
+ $flags['custom_flag']['url'] = esc_url_raw( $url );
110
+ $flags['custom_flag']['src'] = esc_url( $url );
111
  }
112
 
113
  /**
include/links-abstract-domain.php CHANGED
@@ -20,6 +20,7 @@ abstract class PLL_Links_Abstract_Domain extends PLL_Links_Permalinks {
20
  // Avoid cross domain requests ( mainly for custom fonts )
21
  add_filter( 'content_url', array( $this, 'site_url' ) );
22
  add_filter( 'plugins_url', array( $this, 'site_url' ) );
 
23
  add_filter( 'upload_dir', array( $this, 'upload_dir' ) );
24
  }
25
 
20
  // Avoid cross domain requests ( mainly for custom fonts )
21
  add_filter( 'content_url', array( $this, 'site_url' ) );
22
  add_filter( 'plugins_url', array( $this, 'site_url' ) );
23
+ add_filter( 'rest_url', array( $this, 'site_url' ) );
24
  add_filter( 'upload_dir', array( $this, 'upload_dir' ) );
25
  }
26
 
include/links-directory.php CHANGED
@@ -56,7 +56,9 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
56
  if ( ! empty( $lang ) ) {
57
  $base = $this->options['rewrite'] ? '' : 'language/';
58
  $slug = $this->options['default_lang'] == $lang->slug && $this->options['hide_default'] ? '' : $base . $lang->slug . '/';
59
- return str_replace( $this->home . '/' . $this->root, $this->home . '/' . $this->root . $slug, $url );
 
 
60
  }
61
  return $url;
62
  }
@@ -201,11 +203,7 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
201
  * @param string $filter current set of rules being modified
202
  * @param string|bool $archive custom post post type archive name or false if it is not a cpt archive
203
  */
204
- if ( ! apply_filters( 'pll_modify_rewrite_rule', true, array( $key => $rule ), $filter, false ) ) {
205
- continue;
206
- }
207
-
208
- if ( isset( $slug ) ) {
209
  $newrules[ $slug . str_replace( $wp_rewrite->root, '', $key ) ] = str_replace(
210
  array( '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]', '?' ),
211
  array( '[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '?lang=$matches[1]&' ),
@@ -213,34 +211,33 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
213
  ); // Should be enough!
214
  }
215
 
216
- if ( $this->options['hide_default'] ) {
217
- $newrules[ $key ] = $rules[ $key ];
218
- // Unset only if we hide the code for the default language as check_language_code_in_url will do its job in other cases
219
- unset( $rules[ $key ] );
220
- }
221
  }
222
 
223
  // Rewrite rules filtered by language
224
  elseif ( in_array( $filter, $this->always_rewrite ) || in_array( $filter, $this->model->get_filtered_taxonomies() ) || ( $cpts && preg_match( $cpts, $rule, $matches ) && ! strpos( $rule, 'name=' ) ) || ( 'rewrite_rules_array' != $filter && $this->options['force_lang'] ) ) {
225
 
226
  /** This filter is documented in include/links-directory.php */
227
- if ( ! apply_filters( 'pll_modify_rewrite_rule', true, array( $key => $rule ), $filter, empty( $matches[1] ) ? false : $matches[1] ) ) {
228
- continue;
229
- }
230
-
231
- if ( isset( $slug ) ) {
232
- $newrules[ $slug . str_replace( $wp_rewrite->root, '', $key ) ] = str_replace(
233
- array( '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]', '?' ),
234
- array( '[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '?lang=$matches[1]&' ),
235
- $rule
236
- ); // Should be enough!
237
- }
238
-
239
- if ( $this->options['hide_default'] ) {
240
- $newrules[ $key ] = str_replace( '?', '?lang=' . $this->options['default_lang'] . '&', $rule );
241
  }
 
242
 
243
- unset( $rules[ $key ] ); // Now useless
 
 
244
  }
245
  }
246
 
@@ -249,6 +246,6 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
249
  $newrules[ $slug . '?$' ] = $wp_rewrite->index.'?lang=$matches[1]';
250
  }
251
 
252
- return $newrules + $rules;
253
  }
254
  }
56
  if ( ! empty( $lang ) ) {
57
  $base = $this->options['rewrite'] ? '' : 'language/';
58
  $slug = $this->options['default_lang'] == $lang->slug && $this->options['hide_default'] ? '' : $base . $lang->slug . '/';
59
+ if ( false === strpos( $url, $this->home . '/' . $this->root . $slug ) ) {
60
+ return str_replace( $this->home . '/' . $this->root, $this->home . '/' . $this->root . $slug, $url );
61
+ }
62
  }
63
  return $url;
64
  }
203
  * @param string $filter current set of rules being modified
204
  * @param string|bool $archive custom post post type archive name or false if it is not a cpt archive
205
  */
206
+ if ( isset( $slug ) && apply_filters( 'pll_modify_rewrite_rule', true, array( $key => $rule ), $filter, false ) ) {
 
 
 
 
207
  $newrules[ $slug . str_replace( $wp_rewrite->root, '', $key ) ] = str_replace(
208
  array( '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]', '?' ),
209
  array( '[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '?lang=$matches[1]&' ),
211
  ); // Should be enough!
212
  }
213
 
214
+ $newrules[ $key ] = $rule;
 
 
 
 
215
  }
216
 
217
  // Rewrite rules filtered by language
218
  elseif ( in_array( $filter, $this->always_rewrite ) || in_array( $filter, $this->model->get_filtered_taxonomies() ) || ( $cpts && preg_match( $cpts, $rule, $matches ) && ! strpos( $rule, 'name=' ) ) || ( 'rewrite_rules_array' != $filter && $this->options['force_lang'] ) ) {
219
 
220
  /** This filter is documented in include/links-directory.php */
221
+ if ( apply_filters( 'pll_modify_rewrite_rule', true, array( $key => $rule ), $filter, empty( $matches[1] ) ? false : $matches[1] ) ) {
222
+ if ( isset( $slug ) ) {
223
+ $newrules[ $slug . str_replace( $wp_rewrite->root, '', $key ) ] = str_replace(
224
+ array( '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]', '?' ),
225
+ array( '[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '?lang=$matches[1]&' ),
226
+ $rule
227
+ ); // Should be enough!
228
+ }
229
+
230
+ if ( $this->options['hide_default'] ) {
231
+ $newrules[ $key ] = str_replace( '?', '?lang=' . $this->options['default_lang'] . '&', $rule );
232
+ }
233
+ } else {
234
+ $newrules[ $key ] = $rule;
235
  }
236
+ }
237
 
238
+ // Unmodified rules
239
+ else {
240
+ $newrules[ $key ] = $rule;
241
  }
242
  }
243
 
246
  $newrules[ $slug . '?$' ] = $wp_rewrite->index.'?lang=$matches[1]';
247
  }
248
 
249
+ return $newrules;
250
  }
251
  }
include/links-model.php CHANGED
@@ -82,7 +82,7 @@ abstract class PLL_Links_Model {
82
  }
83
 
84
  /**
85
- * Sets the home urls before it is persistently cached
86
  *
87
  * @since 1.8
88
  *
@@ -92,6 +92,7 @@ abstract class PLL_Links_Model {
92
  public function pll_languages_list( $languages ) {
93
  foreach ( $languages as $language ) {
94
  $this->set_home_url( $language );
 
95
  }
96
  return $languages;
97
  }
82
  }
83
 
84
  /**
85
+ * Sets the home urls and flags before the languages are persistently cached
86
  *
87
  * @since 1.8
88
  *
92
  public function pll_languages_list( $languages ) {
93
  foreach ( $languages as $language ) {
94
  $this->set_home_url( $language );
95
+ $language->set_flag();
96
  }
97
  return $languages;
98
  }
include/links-subdomain.php CHANGED
@@ -33,7 +33,7 @@ class PLL_Links_Subdomain extends PLL_Links_Abstract_Domain {
33
  * @return string modified url
34
  */
35
  public function add_language_to_link( $url, $lang ) {
36
- if ( ! empty( $lang ) ) {
37
  $url = $this->options['default_lang'] == $lang->slug && $this->options['hide_default'] ? $url : str_replace( $this->www, '://' . $lang->slug . '.', $url );
38
  }
39
  return $url;
33
  * @return string modified url
34
  */
35
  public function add_language_to_link( $url, $lang ) {
36
+ if ( ! empty( $lang ) && false === strpos( $url, '://' . $lang->slug . '.' ) ) {
37
  $url = $this->options['default_lang'] == $lang->slug && $this->options['hide_default'] ? $url : str_replace( $this->www, '://' . $lang->slug . '.', $url );
38
  }
39
  return $url;
include/links.php CHANGED
@@ -45,11 +45,11 @@ class PLL_Links {
45
  public function current_user_can_read( $post_id ) {
46
  $post = get_post( $post_id );
47
 
48
- if ( 'inherit' === $post->post_status ) {
49
  $post = get_post( $post->post_parent );
50
  }
51
 
52
- if ( in_array( $post->post_status, get_post_stati( array( 'public' => true ) ) ) ) {
53
  return true;
54
  }
55
 
45
  public function current_user_can_read( $post_id ) {
46
  $post = get_post( $post_id );
47
 
48
+ if ( 'inherit' === $post->post_status && $post->post_parent ) {
49
  $post = get_post( $post->post_parent );
50
  }
51
 
52
+ if ( 'inherit' === $post->post_status || in_array( $post->post_status, get_post_stati( array( 'public' => true ) ) ) ) {
53
  return true;
54
  }
55
 
include/mo.php CHANGED
@@ -1,14 +1,15 @@
1
  <?php
2
 
3
  /**
4
- * manages strings translations storage
5
  *
6
  * @since 1.2
 
7
  */
8
  class PLL_MO extends MO {
9
 
10
  /**
11
- * registers the polylang_mo custom post type, only at first object creation
12
  *
13
  * @since 1.2
14
  */
@@ -22,45 +23,45 @@ class PLL_MO extends MO {
22
  }
23
 
24
  /**
25
- * writes a PLL_MO object into a custom post
26
  *
27
  * @since 1.2
28
  *
29
- * @param object $lang the language in which we want to export strings
30
  */
31
  public function export_to_db( $lang ) {
32
- $this->add_entry( $this->make_entry( '', '' ) ); // empty string translation, just in case
33
 
34
- // would be convenient to store the whole object but it would take a huge space in DB
35
- // so let's keep only the strings in an array
36
  $strings = array();
37
  foreach ( $this->entries as $entry ) {
38
  $strings[] = array( $entry->singular, $this->translate( $entry->singular ) );
39
  }
40
 
41
- // we need to make sure that $post is empty when $lang->mo_id is empty: see https://wordpress.org/support/topic/problem-when-adding-a-language
42
- $post = empty( $lang->mo_id ) ? array() : get_post( $lang->mo_id, ARRAY_A ); // wp_insert_post wants an array
43
-
44
- $post['post_title'] = 'polylang_mo_' . $lang->term_id;
45
- // json_encode would take less space but is slower to decode
46
- // wp_insert_post expects slashed data
47
- $post['post_content'] = addslashes( serialize( $strings ) );
48
- $post['post_status'] = 'private'; // to avoid a conflict with WP Super Cache. See https://wordpress.org/support/topic/polylang_mo-and-404s-take-2
49
- $post['post_type'] = 'polylang_mo';
50
- wp_insert_post( $post );
 
51
  }
52
 
53
  /**
54
- * reads a PLL_MO object from a custom post
55
  *
56
  * @since 1.2
57
  *
58
- * @param object $lang the language in which we want to get strings
59
  */
60
  public function import_from_db( $lang ) {
61
  if ( ! empty( $lang->mo_id ) ) {
62
- $post = get_post( $lang->mo_id, OBJECT );
63
- $strings = unserialize( $post->post_content );
64
  if ( is_array( $strings ) ) {
65
  foreach ( $strings as $msg ) {
66
  $this->add_entry( $this->make_entry( $msg[0], $msg[1] ) );
@@ -70,7 +71,7 @@ class PLL_MO extends MO {
70
  }
71
 
72
  /**
73
- * returns the post id of the post storing the strings translations
74
  *
75
  * @since 1.4
76
  *
1
  <?php
2
 
3
  /**
4
+ * Manages strings translations storage
5
  *
6
  * @since 1.2
7
+ * @since 2.1 Stores the strings in a post meta instead of post content to avoid unserialize issues (See #63)
8
  */
9
  class PLL_MO extends MO {
10
 
11
  /**
12
+ * Registers the polylang_mo custom post type, only at first object creation
13
  *
14
  * @since 1.2
15
  */
23
  }
24
 
25
  /**
26
+ * Writes a PLL_MO object into a custom post meta
27
  *
28
  * @since 1.2
29
  *
30
+ * @param object $lang The language in which we want to export strings
31
  */
32
  public function export_to_db( $lang ) {
33
+ $this->add_entry( $this->make_entry( '', '' ) ); // Empty string translation, just in case
34
 
35
+ // Would be convenient to store the whole object but it would take a huge space in DB
36
+ // So let's keep only the strings in an array
37
  $strings = array();
38
  foreach ( $this->entries as $entry ) {
39
  $strings[] = array( $entry->singular, $this->translate( $entry->singular ) );
40
  }
41
 
42
+ if ( empty( $lang->mo_id ) ) {
43
+ $post = array(
44
+ 'post_title' => 'polylang_mo_' . $lang->term_id,
45
+ 'post_status' => 'private', // To avoid a conflict with WP Super Cache. See https://wordpress.org/support/topic/polylang_mo-and-404s-take-2
46
+ 'post_type' => 'polylang_mo',
47
+ );
48
+ $mo_id = wp_insert_post( $post );
49
+ update_post_meta( $mo_id, '_pll_strings_translations', $strings );
50
+ } else {
51
+ update_post_meta( $lang->mo_id, '_pll_strings_translations', $strings );
52
+ }
53
  }
54
 
55
  /**
56
+ * Reads a PLL_MO object from a custom post meta
57
  *
58
  * @since 1.2
59
  *
60
+ * @param object $lang The language in which we want to get strings
61
  */
62
  public function import_from_db( $lang ) {
63
  if ( ! empty( $lang->mo_id ) ) {
64
+ $strings = get_post_meta( $lang->mo_id, '_pll_strings_translations', true );
 
65
  if ( is_array( $strings ) ) {
66
  foreach ( $strings as $msg ) {
67
  $this->add_entry( $this->make_entry( $msg[0], $msg[1] ) );
71
  }
72
 
73
  /**
74
+ * Returns the post id of the post storing the strings translations
75
  *
76
  * @since 1.4
77
  *
include/model.php CHANGED
@@ -140,12 +140,6 @@ class PLL_Model {
140
  * @param string $taxonomy taxonomy name
141
  */
142
  public function clean_languages_cache( $term = 0, $taxonomy = null ) {
143
- // depending on WP version, the action is passed an object or a string
144
- // backward compatibility with WP < 4.2
145
- if ( ! empty( $taxonomy ) && is_object( $taxonomy ) ) {
146
- $taxonomy = $taxonomy->name;
147
- }
148
-
149
  if ( empty( $taxonomy ) || 'language' == $taxonomy ) {
150
  delete_transient( 'pll_languages_list' );
151
  $this->cache->clean();
140
  * @param string $taxonomy taxonomy name
141
  */
142
  public function clean_languages_cache( $term = 0, $taxonomy = null ) {
 
 
 
 
 
 
143
  if ( empty( $taxonomy ) || 'language' == $taxonomy ) {
144
  delete_transient( 'pll_languages_list' );
145
  $this->cache->clean();
include/translated-object.php CHANGED
@@ -50,7 +50,7 @@ abstract class PLL_Translated_Object {
50
  $taxonomies = array( $this->tax_language, $this->tax_translations );
51
 
52
  // query terms
53
- foreach ( wp_get_object_terms( $object_id, $taxonomies ) as $t ) {
54
  $terms[ $t->taxonomy ] = $t;
55
  if ( $t->taxonomy == $taxonomy ) {
56
  $term = $t;
50
  $taxonomies = array( $this->tax_language, $this->tax_translations );
51
 
52
  // query terms
53
+ foreach ( wp_get_object_terms( $object_id, $taxonomies, array( 'update_term_meta_cache' => false ) ) as $t ) {
54
  $terms[ $t->taxonomy ] = $t;
55
  if ( $t->taxonomy == $taxonomy ) {
56
  $term = $t;
include/translated-post.php CHANGED
@@ -79,7 +79,7 @@ class PLL_Translated_Post extends PLL_Translated_Object {
79
  */
80
  public function join_clause( $alias = '' ) {
81
  global $wpdb;
82
- if ( empty ( $alias ) ) {
83
  $alias = $wpdb->posts;
84
  }
85
  return " INNER JOIN $wpdb->term_relationships AS pll_tr ON pll_tr.object_id = $alias.ID";
79
  */
80
  public function join_clause( $alias = '' ) {
81
  global $wpdb;
82
+ if ( empty( $alias ) ) {
83
  $alias = $wpdb->posts;
84
  }
85
  return " INNER JOIN $wpdb->term_relationships AS pll_tr ON pll_tr.object_id = $alias.ID";
include/widget-languages.php CHANGED
@@ -44,7 +44,12 @@ class PLL_Widget_Languages extends WP_Widget {
44
  if ( $title ) {
45
  echo $args['before_title'] . $title . $args['after_title'];
46
  }
47
- echo $instance['dropdown'] ? $list : "<ul>\n" . $list . "</ul>\n";
 
 
 
 
 
48
  echo $args['after_widget'];
49
  }
50
  }
44
  if ( $title ) {
45
  echo $args['before_title'] . $title . $args['after_title'];
46
  }
47
+ if ( $instance['dropdown'] ) {
48
+ echo '<label class="screen-reader-text" for="' . esc_attr( 'lang_choice_' . $instance['dropdown'] ) . '">' . esc_html__( 'Choose a language', 'polylang' ). '</label>';
49
+ echo $list;
50
+ } else {
51
+ echo "<ul>\n" . $list . "</ul>\n";
52
+ }
53
  echo $args['after_widget'];
54
  }
55
  }
install/upgrade.php CHANGED
@@ -87,7 +87,7 @@ class PLL_Upgrade {
87
  * @since 1.2
88
  */
89
  public function _upgrade() {
90
- foreach ( array( '0.9', '1.0', '1.1', '1.2', '1.2.1', '1.2.3', '1.3', '1.4', '1.4.1', '1.4.4', '1.5', '1.6', '1.7.4', '1.8', '2.0.8' ) as $version ) {
91
  if ( version_compare( $this->options['version'], $version, '<' ) ) {
92
  call_user_func( array( $this, 'upgrade_' . str_replace( '.', '_', $version ) ) );
93
  }
@@ -563,4 +563,26 @@ class PLL_Upgrade {
563
  global $wpdb;
564
  $wpdb->update( $wpdb->usermeta, array( 'meta_key' => 'locale' ), array( 'meta_key' => 'user_lang' ) );
565
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566
  }
87
  * @since 1.2
88
  */
89
  public function _upgrade() {
90
+ foreach ( array( '0.9', '1.0', '1.1', '1.2', '1.2.1', '1.2.3', '1.3', '1.4', '1.4.1', '1.4.4', '1.5', '1.6', '1.7.4', '1.8', '2.0.8', '2.1' ) as $version ) {
91
  if ( version_compare( $this->options['version'], $version, '<' ) ) {
92
  call_user_func( array( $this, 'upgrade_' . str_replace( '.', '_', $version ) ) );
93
  }
563
  global $wpdb;
564
  $wpdb->update( $wpdb->usermeta, array( 'meta_key' => 'locale' ), array( 'meta_key' => 'user_lang' ) );
565
  }
566
+
567
+ /**
568
+ * Upgrades if the previous version is < 2.1
569
+ * Moves strings translations from polylang_mo post_content to post meta _pll_strings_translations
570
+ *
571
+ * @since 2.1
572
+ */
573
+ protected function upgrade_2_1() {
574
+ foreach ( get_terms( 'language', array( 'hide_empty' => 0 ) ) as $lang ) {
575
+ $mo_id = PLL_MO::get_id( $lang );
576
+ $meta = get_post_meta( $mo_id, '_pll_strings_translations', true );
577
+
578
+ if ( empty( $meta ) ) {
579
+ $post = get_post( $mo_id, OBJECT );
580
+ $strings = unserialize( $post->post_content );
581
+ if ( is_array( $strings ) ) {
582
+ update_post_meta( $mo_id, '_pll_strings_translations', $strings );
583
+ }
584
+ }
585
+ }
586
+ }
587
+
588
  }
js/admin.js CHANGED
@@ -118,7 +118,7 @@ jQuery( document ).ready(function( $ ) {
118
  tr.hide().prev().show(); // close only if there is no error
119
  case 'error':
120
  $( '.settings-error' ).remove(); // remove previous messages if any
121
- $( '.nav-tab-wrapper' ).after( this.data );
122
 
123
  // Make notices dismissible
124
  // copy paste of common.js from WP 4.2.2
118
  tr.hide().prev().show(); // close only if there is no error
119
  case 'error':
120
  $( '.settings-error' ).remove(); // remove previous messages if any
121
+ $( 'h1' ).after( this.data );
122
 
123
  // Make notices dismissible
124
  // copy paste of common.js from WP 4.2.2
js/admin.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(document).ready(function(e){function t(){var t=e(this).val();if(t){var n=e(this).iconselectmenu("widget").children(":last"),i=e('<img class="ui-icon" >').appendTo(n);i.attr("src",pll_flag_base_url+t+".png")}}var n;e("table.languages").on({focusin:function(){clearTimeout(n),focusedRowActions=e(this).find(".row-actions"),e(".row-actions").not(this).removeClass("visible"),focusedRowActions.addClass("visible")},focusout:function(){n=setTimeout(function(){focusedRowActions.removeClass("visible")},30)}},"tr"),e.widget("custom.iconselectmenu",e.ui.selectmenu,{_renderItem:function(t,n){var i=e("<li>",{text:n.label});return n.value&&e("<img>",{src:pll_flag_base_url+n.value+".png","class":"ui-icon"}).appendTo(i),i.appendTo(t)}}),e("#flag_list").iconselectmenu({create:t,select:t}),e("#lang_list").change(function(){var n=e(this).val().split(":"),i=e("select option:selected").text().split(" - ");e("#lang_slug").val(n[0]),e("#lang_locale").val(n[1]),e('input[name="rtl"]').val([n[2]]),e("#lang_name").val(i[0]),e('#flag_list option[value="'+n[3]+'"]').attr("selected","selected"),e("#flag_list").iconselectmenu("destroy").iconselectmenu({create:t,select:t})}),e(".translation input").keypress(function(t){13===t.keyCode&&(t.preventDefault(),e("#submit").click())}),e("#the-list").on("click",".configure>a",function(){return e(".pll-configure").hide().prev().show(),e(this).closest("tr").hide().next().show(),!1}),e("#the-list").on("click",".cancel",function(){e(this).closest("tr").hide().prev().show()}),e("#the-list").on("click",".save",function(){var t=e(this).closest("tr"),n=t.attr("id").split("-"),i={action:"pll_save_options",pll_ajax_settings:!0,module:n[n.length-1],_pll_nonce:e("#_pll_nonce").val()};i=t.find(":input").serialize()+"&"+e.param(i),e.post(ajaxurl,i,function(n){var i=wpAjax.parseAjaxResponse(n,"ajax-response");e.each(i.responses,function(){switch(this.what){case"license-update":e("#pll-license-"+this.data).replaceWith(this.supplemental.html);break;case"success":t.hide().prev().show();case"error":e(".settings-error").remove(),e(".nav-tab-wrapper").after(this.data),e(".notice.is-dismissible").each(function(){var t=e(this),n=e('<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>'),i=commonL10n.dismiss||"";n.find(".screen-reader-text").text(i),t.append(n),n.on("click.wp-dismiss-notice",function(n){n.preventDefault(),t.fadeTo(100,0,function(){e(this).slideUp(100,function(){e(this).remove()})})})})}})})}),e(".pll-configure").keypress(function(t){13===t.keyCode&&(t.preventDefault(),e(this).find(".save").click()),27===t.keyCode&&(t.preventDefault(),e(this).find(".cancel").click())}),e("input[name='force_lang']").change(function(){function t(e,t){t?e.show():e.hide()}var n=e(this).val();t(e("#pll-domains-table"),3==n),t(e("#pll-hide-default"),3>n),t(e("#pll-rewrite"),2>n)}),e(".pll-deactivate-license").on("click",function(){var t={action:"pll_deactivate_license",pll_ajax_settings:!0,id:e(this).attr("id"),_pll_nonce:e("#_pll_nonce").val()};e.post(ajaxurl,t,function(t){e("#pll-license-"+t.id).replaceWith(t.html)})})});
1
+ jQuery(document).ready(function(e){function t(){var t=e(this).val();if(t){var n=e(this).iconselectmenu("widget").children(":last"),i=e('<img class="ui-icon" >').appendTo(n);i.attr("src",pll_flag_base_url+t+".png")}}var n;e("table.languages").on({focusin:function(){clearTimeout(n),focusedRowActions=e(this).find(".row-actions"),e(".row-actions").not(this).removeClass("visible"),focusedRowActions.addClass("visible")},focusout:function(){n=setTimeout(function(){focusedRowActions.removeClass("visible")},30)}},"tr"),e.widget("custom.iconselectmenu",e.ui.selectmenu,{_renderItem:function(t,n){var i=e("<li>",{text:n.label});return n.value&&e("<img>",{src:pll_flag_base_url+n.value+".png","class":"ui-icon"}).appendTo(i),i.appendTo(t)}}),e("#flag_list").iconselectmenu({create:t,select:t}),e("#lang_list").change(function(){var n=e(this).val().split(":"),i=e("select option:selected").text().split(" - ");e("#lang_slug").val(n[0]),e("#lang_locale").val(n[1]),e('input[name="rtl"]').val([n[2]]),e("#lang_name").val(i[0]),e('#flag_list option[value="'+n[3]+'"]').attr("selected","selected"),e("#flag_list").iconselectmenu("destroy").iconselectmenu({create:t,select:t})}),e(".translation input").keypress(function(t){13===t.keyCode&&(t.preventDefault(),e("#submit").click())}),e("#the-list").on("click",".configure>a",function(){return e(".pll-configure").hide().prev().show(),e(this).closest("tr").hide().next().show(),!1}),e("#the-list").on("click",".cancel",function(){e(this).closest("tr").hide().prev().show()}),e("#the-list").on("click",".save",function(){var t=e(this).closest("tr"),n=t.attr("id").split("-"),i={action:"pll_save_options",pll_ajax_settings:!0,module:n[n.length-1],_pll_nonce:e("#_pll_nonce").val()};i=t.find(":input").serialize()+"&"+e.param(i),e.post(ajaxurl,i,function(n){var i=wpAjax.parseAjaxResponse(n,"ajax-response");e.each(i.responses,function(){switch(this.what){case"license-update":e("#pll-license-"+this.data).replaceWith(this.supplemental.html);break;case"success":t.hide().prev().show();case"error":e(".settings-error").remove(),e("h1").after(this.data),e(".notice.is-dismissible").each(function(){var t=e(this),n=e('<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>'),i=commonL10n.dismiss||"";n.find(".screen-reader-text").text(i),t.append(n),n.on("click.wp-dismiss-notice",function(n){n.preventDefault(),t.fadeTo(100,0,function(){e(this).slideUp(100,function(){e(this).remove()})})})})}})})}),e(".pll-configure").keypress(function(t){13===t.keyCode&&(t.preventDefault(),e(this).find(".save").click()),27===t.keyCode&&(t.preventDefault(),e(this).find(".cancel").click())}),e("input[name='force_lang']").change(function(){function t(e,t){t?e.show():e.hide()}var n=e(this).val();t(e("#pll-domains-table"),3==n),t(e("#pll-hide-default"),3>n),t(e("#pll-rewrite"),2>n)}),e(".pll-deactivate-license").on("click",function(){var t={action:"pll_deactivate_license",pll_ajax_settings:!0,id:e(this).attr("id"),_pll_nonce:e("#_pll_nonce").val()};e.post(ajaxurl,t,function(t){e("#pll-license-"+t.id).replaceWith(t.html)})})});
lingotek/lingotek.php CHANGED
@@ -24,7 +24,7 @@ class PLL_Lingotek {
24
  add_filter( 'pll_settings_tabs', array( $this, 'add_tab' ) );
25
  add_action( 'pll_settings_active_tab_lingotek', array( $this, 'display_tab' ) );
26
 
27
- if ( PLL_SETTINGS && isset( $_GET['tab'] ) && 'lingotek' == $_GET['tab'] ) {
28
  add_action( 'admin_print_styles', array( $this, 'print_css' ) );
29
  }
30
 
@@ -274,6 +274,8 @@ class PLL_Lingotek {
274
  * @return string
275
  */
276
  protected function get_activate_link() {
 
 
277
  if ( ! array_key_exists( self::LINGOTEK, get_plugins() ) ) {
278
  if ( current_user_can( 'install_plugins' ) ) {
279
  $plugin = dirname( self::LINGOTEK );
@@ -291,4 +293,4 @@ class PLL_Lingotek {
291
  }
292
  }
293
 
294
- add_action( 'admin_init', array( new PLL_Lingotek(), 'init' ) );
24
  add_filter( 'pll_settings_tabs', array( $this, 'add_tab' ) );
25
  add_action( 'pll_settings_active_tab_lingotek', array( $this, 'display_tab' ) );
26
 
27
+ if ( PLL_SETTINGS && isset( $_GET['page'] ) && 'mlang_lingotek' == $_GET['page'] ) {
28
  add_action( 'admin_print_styles', array( $this, 'print_css' ) );
29
  }
30
 
274
  * @return string
275
  */
276
  protected function get_activate_link() {
277
+ require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
278
+
279
  if ( ! array_key_exists( self::LINGOTEK, get_plugins() ) ) {
280
  if ( current_user_can( 'install_plugins' ) ) {
281
  $plugin = dirname( self::LINGOTEK );
293
  }
294
  }
295
 
296
+ add_action( 'wp_loaded', array( new PLL_Lingotek(), 'init' ) );
modules/plugins/plugins-compat.php CHANGED
@@ -32,15 +32,14 @@ class PLL_Plugins_Compat {
32
  add_filter( 'pll_home_url_black_list', array( $this, 'aq_home_url_black_list' ) );
33
 
34
  // Twenty Fourteen
35
- if ( 'twentyfourteen' == get_template() ) {
36
- add_filter( 'transient_featured_content_ids', array( $this, 'twenty_fourteen_featured_content_ids' ) );
37
- add_filter( 'option_featured-content', array( $this, 'twenty_fourteen_option_featured_content' ) );
38
- }
39
 
40
  // Duplicate post
41
  add_filter( 'option_duplicate_post_taxonomies_blacklist' , array( $this, 'duplicate_post_taxonomies_blacklist' ) );
42
 
43
  // Jetpack 3
 
44
  add_action( 'jetpack_widget_get_top_posts', array( $this, 'jetpack_widget_get_top_posts' ), 10, 3 );
45
  add_filter( 'grunion_contact_form_field_html', array( $this, 'grunion_contact_form_field_html_filter' ), 10, 3 );
46
  add_filter( 'jetpack_open_graph_tags', array( $this, 'jetpack_ogp' ) );
@@ -56,6 +55,9 @@ class PLL_Plugins_Compat {
56
 
57
  // Twenty Seventeen
58
  add_action( 'init', array( $this, 'twenty_seventeen_init' ) );
 
 
 
59
  }
60
 
61
  /**
@@ -212,6 +214,7 @@ class PLL_Plugins_Compat {
212
  if ( pll_is_translated_post_type( $t ) && ! empty( $options[ 'title-ptarchive-' . $t ] ) ) {
213
  pll_register_string( 'title-ptarchive-' . $t, $options[ 'title-ptarchive-' . $t ], 'wordpress-seo' );
214
  pll_register_string( 'metadesc-ptarchive-' . $t, $options[ 'metadesc-ptarchive-' . $t ], 'wordpress-seo' );
 
215
  }
216
  }
217
  foreach ( get_taxonomies( array( 'public' => true, '_builtin' => false ) ) as $t ) {
@@ -243,6 +246,7 @@ class PLL_Plugins_Compat {
243
  if ( pll_is_translated_post_type( $t ) && ! empty( $options[ 'title-ptarchive-' . $t ] ) ) {
244
  $options[ 'title-ptarchive-' . $t ] = pll__( $options[ 'title-ptarchive-' . $t ] );
245
  $options[ 'metadesc-ptarchive-' . $t ] = pll__( $options[ 'metadesc-ptarchive-' . $t ] );
 
246
  }
247
  }
248
  foreach ( get_taxonomies( array( 'public' => true, '_builtin' => false ) ) as $t ) {
@@ -455,7 +459,7 @@ class PLL_Plugins_Compat {
455
  * @return array modified featured posts ids ( include all languages )
456
  */
457
  public function twenty_fourteen_featured_content_ids( $featured_ids ) {
458
- if ( ! did_action( 'pll_init' ) || false !== $featured_ids ) {
459
  return $featured_ids;
460
  }
461
 
@@ -503,7 +507,7 @@ class PLL_Plugins_Compat {
503
  * @return array modified $settings
504
  */
505
  public function twenty_fourteen_option_featured_content( $settings ) {
506
- if ( PLL() instanceof PLL_Frontend && $settings['tag-id'] && $tr = pll_get_term( $settings['tag-id'] ) ) {
507
  $settings['tag-id'] = $tr;
508
  }
509
 
@@ -524,6 +528,24 @@ class PLL_Plugins_Compat {
524
  return $taxonomies;
525
  }
526
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
527
  /**
528
  * Jetpack
529
  * Adapted from the same function in jetpack-3.0.2/3rd-party/wpml.php
@@ -597,6 +619,20 @@ class PLL_Plugins_Compat {
597
  return $filters;
598
  }
599
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
  /**
601
  * WP Sweep
602
  * Add 'term_language' and 'term_translations' to excluded taxonomies otherwise terms loose their language and translation group
@@ -625,6 +661,21 @@ class PLL_Plugins_Compat {
625
  }
626
  }
627
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
628
  /**
629
  * Correspondance between WordPress locales and Facebook locales
630
  * @see https://translate.wordpress.org/
32
  add_filter( 'pll_home_url_black_list', array( $this, 'aq_home_url_black_list' ) );
33
 
34
  // Twenty Fourteen
35
+ add_filter( 'transient_featured_content_ids', array( $this, 'twenty_fourteen_featured_content_ids' ) );
36
+ add_filter( 'option_featured-content', array( $this, 'twenty_fourteen_option_featured_content' ) );
 
 
37
 
38
  // Duplicate post
39
  add_filter( 'option_duplicate_post_taxonomies_blacklist' , array( $this, 'duplicate_post_taxonomies_blacklist' ) );
40
 
41
  // Jetpack 3
42
+ add_action( 'init', array( $this, 'jetpack_init' ) );
43
  add_action( 'jetpack_widget_get_top_posts', array( $this, 'jetpack_widget_get_top_posts' ), 10, 3 );
44
  add_filter( 'grunion_contact_form_field_html', array( $this, 'grunion_contact_form_field_html_filter' ), 10, 3 );
45
  add_filter( 'jetpack_open_graph_tags', array( $this, 'jetpack_ogp' ) );
55
 
56
  // Twenty Seventeen
57
  add_action( 'init', array( $this, 'twenty_seventeen_init' ) );
58
+
59
+ // No category base (works for Yoast SEO too)
60
+ add_filter( 'get_terms_args', array( $this, 'no_category_base_get_terms_args' ), 5 ); // Before adding cache domain
61
  }
62
 
63
  /**
214
  if ( pll_is_translated_post_type( $t ) && ! empty( $options[ 'title-ptarchive-' . $t ] ) ) {
215
  pll_register_string( 'title-ptarchive-' . $t, $options[ 'title-ptarchive-' . $t ], 'wordpress-seo' );
216
  pll_register_string( 'metadesc-ptarchive-' . $t, $options[ 'metadesc-ptarchive-' . $t ], 'wordpress-seo' );
217
+ pll_register_string( 'bctitle-ptarchive-' . $t, $options[ 'bctitle-ptarchive-' . $t ], 'wordpress-seo' );
218
  }
219
  }
220
  foreach ( get_taxonomies( array( 'public' => true, '_builtin' => false ) ) as $t ) {
246
  if ( pll_is_translated_post_type( $t ) && ! empty( $options[ 'title-ptarchive-' . $t ] ) ) {
247
  $options[ 'title-ptarchive-' . $t ] = pll__( $options[ 'title-ptarchive-' . $t ] );
248
  $options[ 'metadesc-ptarchive-' . $t ] = pll__( $options[ 'metadesc-ptarchive-' . $t ] );
249
+ $options[ 'bctitle-ptarchive-' . $t ] = pll__( $options[ 'bctitle-ptarchive-' . $t ] );
250
  }
251
  }
252
  foreach ( get_taxonomies( array( 'public' => true, '_builtin' => false ) ) as $t ) {
459
  * @return array modified featured posts ids ( include all languages )
460
  */
461
  public function twenty_fourteen_featured_content_ids( $featured_ids ) {
462
+ if ( 'twentyfourteen' != get_template() || ! did_action( 'pll_init' ) || false !== $featured_ids ) {
463
  return $featured_ids;
464
  }
465
 
507
  * @return array modified $settings
508
  */
509
  public function twenty_fourteen_option_featured_content( $settings ) {
510
+ if ( 'twentyfourteen' == get_template() && PLL() instanceof PLL_Frontend && $settings['tag-id'] && $tr = pll_get_term( $settings['tag-id'] ) ) {
511
  $settings['tag-id'] = $tr;
512
  }
513
 
528
  return $taxonomies;
529
  }
530
 
531
+ /**
532
+ * Jetpack
533
+ * Add filters
534
+ *
535
+ * @since 2.1
536
+ */
537
+ public function jetpack_init() {
538
+ if ( ! defined( 'JETPACK__VERSION' ) ) {
539
+ return;
540
+ }
541
+
542
+ // Infinite scroll ajax url must be on the right domain
543
+ if ( did_action( 'pll_init' ) && PLL()->options['force_lang'] > 1 ) {
544
+ add_filter( 'infinite_scroll_ajax_url', array( PLL()->links_model, 'site_url' ) );
545
+ add_filter( 'infinite_scroll_js_settings', array( $this, 'jetpack_infinite_scroll_js_settings' ) );
546
+ }
547
+ }
548
+
549
  /**
550
  * Jetpack
551
  * Adapted from the same function in jetpack-3.0.2/3rd-party/wpml.php
619
  return $filters;
620
  }
621
 
622
+ /**
623
+ * Jetpack
624
+ * Fixes the settings history host for infinite scroll when using subdomains or multiple domains
625
+ *
626
+ * @since 2.1
627
+ *
628
+ * @param array $settings
629
+ * @return array
630
+ */
631
+ public function jetpack_infinite_scroll_js_settings( $settings ) {
632
+ $settings['history']['host'] = parse_url( pll_home_url(), PHP_URL_HOST ); // Jetpack uses get_option( 'home' )
633
+ return $settings;
634
+ }
635
+
636
  /**
637
  * WP Sweep
638
  * Add 'term_language' and 'term_translations' to excluded taxonomies otherwise terms loose their language and translation group
661
  }
662
  }
663
 
664
+ /**
665
+ * Make sure No category base plugins (including Yoast SEO) get all categories when flushing rules
666
+ *
667
+ * @since 2.1
668
+ *
669
+ * @param array $args
670
+ * @return array
671
+ */
672
+ public function no_category_base_get_terms_args( $args ) {
673
+ if ( doing_filter( 'category_rewrite_rules' ) ) {
674
+ $args['lang'] = '';
675
+ }
676
+ return $args;
677
+ }
678
+
679
  /**
680
  * Correspondance between WordPress locales and Facebook locales
681
  * @see https://translate.wordpress.org/
modules/sync/admin-sync.php CHANGED
@@ -73,6 +73,12 @@ class PLL_Admin_Sync {
73
  $post->$property = $from_post->$property;
74
  }
75
 
 
 
 
 
 
 
76
  if ( is_sticky( $from_post_id ) ) {
77
  stick_post( $post->ID );
78
  }
@@ -83,11 +89,15 @@ class PLL_Admin_Sync {
83
  * Get the list of taxonomies to copy or to synchronize
84
  *
85
  * @since 1.7
 
86
  *
87
- * @param bool $sync true if it is synchronization, false if it is a copy
 
 
 
88
  * @return array list of taxonomy names
89
  */
90
- public function get_taxonomies_to_copy( $sync ) {
91
  $taxonomies = ! $sync || in_array( 'taxonomies', $this->options['sync'] ) ? $this->model->get_translated_taxonomies() : array();
92
  if ( ! $sync || in_array( 'post_format', $this->options['sync'] ) ) {
93
  $taxonomies[] = 'post_format';
@@ -97,11 +107,15 @@ class PLL_Admin_Sync {
97
  * Filter the taxonomies to copy or synchronize
98
  *
99
  * @since 1.7
 
100
  *
101
- * @param array $taxonomies list of taxonomy names
102
- * @param bool $sync true if it is synchronization, false if it is a copy
 
 
 
103
  */
104
- return array_unique( apply_filters( 'pll_copy_taxonomies', $taxonomies, $sync ) );
105
  }
106
 
107
  /**
@@ -116,7 +130,7 @@ class PLL_Admin_Sync {
116
  */
117
  public function copy_taxonomies( $from, $to, $lang, $sync = false ) {
118
  // Get taxonomies to sync for this post type
119
- $taxonomies = array_intersect( get_post_taxonomies( $from ), $this->get_taxonomies_to_copy( $sync ) );
120
 
121
  // Update the term cache to reduce the number of queries in the loop
122
  update_object_term_cache( $sync ? array( $from, $to ) : $from, get_post_type( $from ) );
@@ -232,17 +246,30 @@ class PLL_Admin_Sync {
232
  * @param array $translations post translations
233
  */
234
  public function pll_save_post( $post_id, $post, $translations ) {
235
- global $wpdb, $post_type;
236
 
237
  // Prepare properties to synchronize
238
- foreach ( array( 'comment_status', 'ping_status', 'menu_order', 'post_date' ) as $property ) {
239
  if ( in_array( $property, $this->options['sync'] ) ) {
240
  $postarr[ $property ] = $post->$property;
241
  }
242
  }
243
 
244
  if ( in_array( 'post_date', $this->options['sync'] ) ) {
245
- $postarr['post_date_gmt'] = $post->post_date_gmt;
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  }
247
 
248
  // Synchronize terms and metas in translations
@@ -257,16 +284,22 @@ class PLL_Admin_Sync {
257
 
258
  // Sticky posts
259
  if ( in_array( 'sticky_posts', $this->options['sync'] ) ) {
260
- isset( $_REQUEST['sticky'] ) ? stick_post( $tr_id ) : unstick_post( $tr_id );
261
  }
262
 
263
  // Add comment status, ping status, menu order... to synchronization
264
  $tr_arr = empty( $postarr ) ? array() : $postarr;
265
 
 
 
 
 
 
 
266
  // Add post parent to synchronization
267
  // Make sure not to impact media translations when creating them at the same time as post
268
  // Do not udpate the translation parent if the user set a parent with no translation
269
- if ( in_array( 'post_parent', $this->options['sync'] ) && $post_type === $post->post_type ) {
270
  $post_parent = ( $parent_id = wp_get_post_parent_id( $post_id ) ) ? $this->model->post->get_translation( $parent_id, $lang ) : 0;
271
  if ( ! ( $parent_id && ! $post_parent ) ) {
272
  $tr_arr['post_parent'] = $post_parent;
73
  $post->$property = $from_post->$property;
74
  }
75
 
76
+ // Copy the date only if the synchronization is activated
77
+ if ( in_array( 'post_date', $this->options['sync'] ) ) {
78
+ $post->post_date = $from_post->post_date;
79
+ $post->post_date_gmt = $from_post->post_date_gmt;
80
+ }
81
+
82
  if ( is_sticky( $from_post_id ) ) {
83
  stick_post( $post->ID );
84
  }
89
  * Get the list of taxonomies to copy or to synchronize
90
  *
91
  * @since 1.7
92
+ * @since 2.1 The `$from`, `$to`, `$lang` parameters were added.
93
  *
94
+ * @param bool $sync true if it is synchronization, false if it is a copy
95
+ * @param int $from id of the post from which we copy informations, optional, defaults to null
96
+ * @param int $to id of the post to which we paste informations, optional, defaults to null
97
+ * @param string $lang language slug, optional, defaults to null
98
  * @return array list of taxonomy names
99
  */
100
+ public function get_taxonomies_to_copy( $sync, $from = null, $to = null, $lang = null ) {
101
  $taxonomies = ! $sync || in_array( 'taxonomies', $this->options['sync'] ) ? $this->model->get_translated_taxonomies() : array();
102
  if ( ! $sync || in_array( 'post_format', $this->options['sync'] ) ) {
103
  $taxonomies[] = 'post_format';
107
  * Filter the taxonomies to copy or synchronize
108
  *
109
  * @since 1.7
110
+ * @since 2.1 The `$from`, `$to`, `$lang` parameters were added.
111
  *
112
+ * @param array $taxonomies list of taxonomy names
113
+ * @param bool $sync true if it is synchronization, false if it is a copy
114
+ * @param int $from id of the post from which we copy informations
115
+ * @param int $to id of the post to which we paste informations
116
+ * @param string $lang language slug
117
  */
118
+ return array_unique( apply_filters( 'pll_copy_taxonomies', $taxonomies, $sync, $from, $to, $lang ) );
119
  }
120
 
121
  /**
130
  */
131
  public function copy_taxonomies( $from, $to, $lang, $sync = false ) {
132
  // Get taxonomies to sync for this post type
133
+ $taxonomies = array_intersect( get_post_taxonomies( $from ), $this->get_taxonomies_to_copy( $sync, $from, $to, $lang ) );
134
 
135
  // Update the term cache to reduce the number of queries in the loop
136
  update_object_term_cache( $sync ? array( $from, $to ) : $from, get_post_type( $from ) );
246
  * @param array $translations post translations
247
  */
248
  public function pll_save_post( $post_id, $post, $translations ) {
249
+ global $wpdb;
250
 
251
  // Prepare properties to synchronize
252
+ foreach ( array( 'comment_status', 'ping_status', 'menu_order' ) as $property ) {
253
  if ( in_array( $property, $this->options['sync'] ) ) {
254
  $postarr[ $property ] = $post->$property;
255
  }
256
  }
257
 
258
  if ( in_array( 'post_date', $this->options['sync'] ) ) {
259
+ // For new drafts, save the date now otherwise it is overriden by WP. Thanks to JoryHogeveen. See #32.
260
+ if ( 'post-new.php' === $GLOBALS['pagenow'] && isset( $_GET['from_post'], $_GET['new_lang'] ) ) {
261
+ $original = get_post( (int) $_GET['from_post'] );
262
+ $wpdb->update(
263
+ $wpdb->posts, array(
264
+ 'post_date' => $original->post_date,
265
+ 'post_date_gmt' => $original->post_date_gmt,
266
+ ),
267
+ array( 'ID' => $post_id )
268
+ );
269
+ } else {
270
+ $postarr['post_date'] = $post->post_date;
271
+ $postarr['post_date_gmt'] = $post->post_date_gmt;
272
+ }
273
  }
274
 
275
  // Synchronize terms and metas in translations
284
 
285
  // Sticky posts
286
  if ( in_array( 'sticky_posts', $this->options['sync'] ) ) {
287
+ isset( $_REQUEST['sticky'] ) && 'sticky' === $_REQUEST['sticky'] ? stick_post( $tr_id ) : unstick_post( $tr_id );
288
  }
289
 
290
  // Add comment status, ping status, menu order... to synchronization
291
  $tr_arr = empty( $postarr ) ? array() : $postarr;
292
 
293
+ if ( isset( $GLOBALS['post_type'] ) ) {
294
+ $post_type = $GLOBALS['post_type'];
295
+ } elseif ( isset( $_REQUEST['post_type'] ) ){
296
+ $post_type = $_REQUEST['post_type']; // 2nd case for quick edit
297
+ }
298
+
299
  // Add post parent to synchronization
300
  // Make sure not to impact media translations when creating them at the same time as post
301
  // Do not udpate the translation parent if the user set a parent with no translation
302
+ if ( in_array( 'post_parent', $this->options['sync'] ) && isset( $post_type ) && $post_type === $post->post_type ) {
303
  $post_parent = ( $parent_id = wp_get_post_parent_id( $post_id ) ) ? $this->model->post->get_translation( $parent_id, $lang ) : 0;
304
  if ( ! ( $parent_id && ! $post_parent ) ) {
305
  $tr_arr['post_parent'] = $post_parent;
modules/wpml/wpml-api.php CHANGED
@@ -52,7 +52,7 @@ class PLL_WPML_API {
52
  // Finding the Translation State of Content
53
 
54
  // wpml_element_translation_type
55
- add_filter( 'wpml_element_has_translations', array( $this, 'wpml_element_has_translations' ), 10, 2 );
56
  // wpml_master_post_from_duplicate => not applicable
57
  // wpml_post_duplicates => not applicable
58
 
52
  // Finding the Translation State of Content
53
 
54
  // wpml_element_translation_type
55
+ add_filter( 'wpml_element_has_translations', array( $this, 'wpml_element_has_translations' ), 10, 3 );
56
  // wpml_master_post_from_duplicate => not applicable
57
  // wpml_post_duplicates => not applicable
58
 
modules/wpml/wpml-legacy-api.php CHANGED
@@ -181,11 +181,12 @@ if ( ! function_exists( 'wpml_get_language_information' ) ) {
181
  }
182
 
183
  // FIXME WPML may return a WP_Error object
184
- return false === $lang = PLL()->model->post->get_language( $post_id ) ? array() : array(
185
- 'locale' => $lang->locale,
186
- 'text_direction' => $lang->is_rtl,
187
- 'display_name' => $lang->name, // Seems to be the post language name displayed in the current language, not a feature in Polylang
188
- 'native_name' => $lang->name,
 
189
  'different_language' => $lang->slug != pll_current_language(),
190
  );
191
  }
181
  }
182
 
183
  // FIXME WPML may return a WP_Error object
184
+ return false === ( $lang = PLL()->model->post->get_language( $post_id ) ) ? array() : array(
185
+ 'language_code' => $lang->slug,
186
+ 'locale' => $lang->locale,
187
+ 'text_direction' => (bool) $lang->is_rtl,
188
+ 'display_name' => $lang->name, // Seems to be the post language name displayed in the current language, not a feature in Polylang
189
+ 'native_name' => $lang->name,
190
  'different_language' => $lang->slug != pll_current_language(),
191
  );
192
  }
polylang.php CHANGED
@@ -3,7 +3,7 @@
3
  /*
4
  Plugin Name: Polylang
5
  Plugin URI: https://polylang.pro
6
- Version: 2.0.12
7
  Author: Frédéric Demarle
8
  Author uri: https://polylang.pro
9
  Description: Adds multilingual capability to WordPress
@@ -12,7 +12,7 @@ Domain Path: /languages
12
  */
13
 
14
  /*
15
- * Copyright 2011-2016 Frédéric Demarle
16
  *
17
  * This program is free software; you can redistribute it and/or modify
18
  * it under the terms of the GNU General Public License as published by
@@ -35,8 +35,8 @@ if ( ! defined( 'ABSPATH' ) ) {
35
  exit; // don't access directly
36
  };
37
 
38
- define( 'POLYLANG_VERSION', '2.0.12' );
39
- define( 'PLL_MIN_WP_VERSION', '4.0' );
40
 
41
  define( 'POLYLANG_FILE', __FILE__ ); // this file
42
  define( 'POLYLANG_BASENAME', plugin_basename( POLYLANG_FILE ) ); // plugin name as known by WP
3
  /*
4
  Plugin Name: Polylang
5
  Plugin URI: https://polylang.pro
6
+ Version: 2.1
7
  Author: Frédéric Demarle
8
  Author uri: https://polylang.pro
9
  Description: Adds multilingual capability to WordPress
12
  */
13
 
14
  /*
15
+ * Copyright 2011-2017 Frédéric Demarle
16
  *
17
  * This program is free software; you can redistribute it and/or modify
18
  * it under the terms of the GNU General Public License as published by
35
  exit; // don't access directly
36
  };
37
 
38
+ define( 'POLYLANG_VERSION', '2.1' );
39
+ define( 'PLL_MIN_WP_VERSION', '4.4' );
40
 
41
  define( 'POLYLANG_FILE', __FILE__ ); // this file
42
  define( 'POLYLANG_BASENAME', plugin_basename( POLYLANG_FILE ) ); // plugin name as known by WP
readme.txt CHANGED
@@ -2,9 +2,9 @@
2
  Contributors: Chouby
3
  Donate link: https://polylang.pro
4
  Tags: multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
- Requires at least: 4.0
6
  Tested up to: 4.7
7
- Stable tag: 2.0.12
8
  License: GPLv2 or later
9
 
10
  Making WordPress multilingual
@@ -21,7 +21,6 @@ Polylang allows you to create a bilingual or multilingual WordPress site. You wr
21
  * The language is either set by the content or by the language code in url, or you can use one different subdomain or domain per language
22
  * Categories, post tags as well as some other metas are automatically copied when adding a new post or page translation
23
  * A customizable language switcher is provided as a widget or in the nav menu
24
- * The admin interface is of course multilingual too and each user can set the WordPress admin language in its profile
25
 
26
  > The author does not provide support on the wordpress.org forum. Support and extra features are available to [Polylang Pro](https://polylang.pro) users.
27
 
@@ -32,7 +31,7 @@ If you wish to use a professional or automatic translation service, you can inst
32
  = Credits =
33
 
34
  Thanks a lot to all translators who [help translating Polylang](https://translate.wordpress.org/projects/wp-plugins/polylang).
35
- Thanks a lot to [Alex Lopez](http://www.alexlopez.rocks/) for the design of the banner and the logo.
36
  Most of the flags included with Polylang are coming from [famfamfam](http://famfamfam.com/) and are public domain.
37
  Wherever third party code has been used, credit has been given in the code’s comments.
38
 
@@ -62,7 +61,7 @@ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-
62
 
63
  = Is Polylang compatible with WooCommerce? =
64
 
65
- * You need a separate addon to make Polylang and WooCommerce work together. [A Premium addon](https://polylang.pro/downloads/polylang-for-woocommerce/), currently in beta stage, is available.
66
 
67
  = Do you need translation services? =
68
 
@@ -77,6 +76,36 @@ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-
77
 
78
  == Changelog ==
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  = 2.0.12 (2016-12-19) =
81
 
82
  * Fix plugin not loaded first (introduced in 2.0.11)
2
  Contributors: Chouby
3
  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.7
7
+ Stable tag: 2.1
8
  License: GPLv2 or later
9
 
10
  Making WordPress multilingual
21
  * The language is either set by the content or by the language code in url, or you can use one different subdomain or domain per language
22
  * Categories, post tags as well as some other metas are automatically copied when adding a new post or page translation
23
  * A customizable language switcher is provided as a widget or in the nav menu
 
24
 
25
  > The author does not provide support on the wordpress.org forum. Support and extra features are available to [Polylang Pro](https://polylang.pro) users.
26
 
31
  = Credits =
32
 
33
  Thanks a lot to all translators who [help translating Polylang](https://translate.wordpress.org/projects/wp-plugins/polylang).
34
+ Thanks a lot to [Alex Lopez](http://www.alexlopez.rocks/) for the design of the logo.
35
  Most of the flags included with Polylang are coming from [famfamfam](http://famfamfam.com/) and are public domain.
36
  Wherever third party code has been used, credit has been given in the code’s comments.
37
 
61
 
62
  = Is Polylang compatible with WooCommerce? =
63
 
64
+ * You need a separate addon to make Polylang and WooCommerce work together. [A Premium addon](https://polylang.pro/downloads/polylang-for-woocommerce/) is available.
65
 
66
  = Do you need translation services? =
67
 
76
 
77
  == Changelog ==
78
 
79
+ = 2.1 (2017-01-25) =
80
+
81
+ * Minimum WordPress version is now 4.4
82
+ * Pro: Add support for synchronized posts (same post in multiple languages)
83
+ * Pro: Add support for custom post type UI and the Divi Builder
84
+ * Improve support of Yoast SEO (no category base and post type archive breadcrumb title)
85
+ * Move Languages menu at top level instead of submenu of the WordPress settings
86
+ * Copy the original post date when creating a translation and when the date is synchronized (Props Jory Hogeveen) #32
87
+ * Remove hreflang attributes on paged pages and paged posts
88
+ * Add label to widget language dropdown for better accessibility (Props Lawrence Francell) #53 #56
89
+ * Remove constants POLYLANG_URL and PLL_LOCAL_URL
90
+ * wp_get_sidebars_widgets() and is_active_sidebar() are now filtered according to widgets languages #54
91
+ * Add functions pll_esc_html__(), pll_esc_html_e(), pll_esc_attr__() and pll_esc_attr_e() to the API (Props jegbagus) #83
92
+ * Pro: Fix conflict between WooCommerce shop on front and translated shop base slug
93
+ * Pro: Fix $wp_rewrite search base and author_base not translated #68
94
+ * Pro: Fix page preview does not log in the user when using sudomains
95
+ * Fix: avoid setting the language cookie on 404 pages
96
+ * Fix: rewrite rules order modified for custom post types archives
97
+ * Fix: conflict with WP All Import causing our filters to fail in "Add Media" modal when editing a post
98
+ * Fix: auto add pages not working for nav menus assigned to several locations
99
+ * Fix: Jetpack infinite scroll for multiple domains #58 #74
100
+ * Fix: serialize error in Strings translations when balanceTags option is active #63
101
+ * Fix: static front page preview when redirected from the languages page #49
102
+ * Fix: Auto add pages not working for nav menus assigned to several locations
103
+ * Fix: Conflict with Woocommerce Show Single Variation
104
+ * Fix: Parent page not synchronized in Quick edit (introduced in 2.0.8)
105
+ * Fix: WPML API wpml_element_has_translations and wpml_post_language_details
106
+ * Fix: unattached media translations not in language switcher
107
+ * Fix: Conflict with WP Residence advanced search
108
+
109
  = 2.0.12 (2016-12-19) =
110
 
111
  * Fix plugin not loaded first (introduced in 2.0.11)
settings/settings.php CHANGED
@@ -29,7 +29,9 @@ class PLL_Settings extends PLL_Admin_Base {
29
  public function __construct( &$links_model ) {
30
  parent::__construct( $links_model );
31
 
32
- $this->active_tab = ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'lang';
 
 
33
 
34
  PLL_Admin_Strings::init();
35
 
@@ -37,7 +39,8 @@ class PLL_Settings extends PLL_Admin_Base {
37
  add_action( 'admin_init', array( $this, 'register_settings_modules' ) );
38
 
39
  // adds screen options and the about box in the languages admin panel
40
- add_action( 'load-settings_page_mlang', array( $this, 'load_page' ) );
 
41
 
42
  // saves per-page value in screen option
43
  add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 10, 3 );
@@ -82,15 +85,6 @@ class PLL_Settings extends PLL_Admin_Base {
82
  }
83
  }
84
 
85
- /**
86
- * Adds the link to the languages panel in the WordPress admin menu
87
- *
88
- * @since 0.1
89
- */
90
- public function add_menus() {
91
- add_submenu_page( 'options-general.php', $title = __( 'Languages', 'polylang' ), $title, 'manage_options', 'mlang', array( $this, 'languages_page' ) );
92
- }
93
-
94
  /**
95
  * Loads the about metabox
96
  *
@@ -106,36 +100,36 @@ class PLL_Settings extends PLL_Admin_Base {
106
  * @since 0.9.5
107
  */
108
  public function load_page() {
109
- // test of $this->active_tab avoids displaying the automatically generated screen options on other tabs
110
- switch ( $this->active_tab ) {
111
- case 'lang':
112
- if ( ! defined( 'PLL_DISPLAY_ABOUT' ) || PLL_DISPLAY_ABOUT ) {
113
- add_meta_box(
114
- 'pll-about-box',
115
- __( 'About Polylang', 'polylang' ),
116
- array( $this, 'metabox_about' ),
117
- 'settings_page_mlang',
118
- 'normal'
119
- );
120
- }
121
 
122
- add_screen_option( 'per_page', array(
123
- 'label' => __( 'Languages', 'polylang' ),
124
- 'default' => 10,
125
- 'option' => 'pll_lang_per_page',
126
- ) );
127
 
128
- add_action( 'admin_notices', array( $this, 'notice_objects_with_no_lang' ) );
129
- break;
130
 
131
- case 'strings':
132
- add_screen_option( 'per_page', array(
133
- 'label' => __( 'Strings translations', 'polylang' ),
134
- 'default' => 10,
135
- 'option' => 'pll_strings_per_page',
136
- ) );
137
- break;
138
- }
 
 
 
139
  }
140
 
141
  /**
@@ -250,34 +244,15 @@ class PLL_Settings extends PLL_Admin_Base {
250
  * @since 0.1
251
  */
252
  public function languages_page() {
253
- // prepare the list of tabs
254
- $tabs = array( 'lang' => __( 'Languages','polylang' ) );
255
-
256
- // only if at least one language has been created
257
- if ( $listlanguages = $this->model->get_languages_list() ) {
258
- $tabs['strings'] = __( 'Strings translations', 'polylang' );
259
- }
260
-
261
- $tabs['settings'] = __( 'Settings', 'polylang' );
262
-
263
- /**
264
- * Filter the list of tabs in Polylang settings
265
- *
266
- * @since 1.5.1
267
- *
268
- * @param array $tabs list of tab names
269
- */
270
- $tabs = apply_filters( 'pll_settings_tabs', $tabs );
271
-
272
  switch ( $this->active_tab ) {
273
  case 'lang':
274
  // prepare the list table of languages
275
  $list_table = new PLL_Table_Languages();
276
- $list_table->prepare_items( $listlanguages );
277
  break;
278
 
279
  case 'strings':
280
- $string_table = new PLL_Table_String( $listlanguages );
281
  $string_table->prepare_items();
282
  break;
283
  }
@@ -291,7 +266,7 @@ class PLL_Settings extends PLL_Admin_Base {
291
  }
292
 
293
  // displays the page
294
- include( PLL_SETTINGS_INC.'/view-languages.php' );
295
  }
296
 
297
  /**
@@ -302,10 +277,10 @@ class PLL_Settings extends PLL_Admin_Base {
302
 
303
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
304
 
305
- wp_enqueue_script( 'pll_admin', POLYLANG_URL .'/js/admin'.$suffix.'.js', array( 'jquery', 'wp-ajax-response', 'postbox', 'jquery-ui-selectmenu' ), POLYLANG_VERSION );
306
- wp_localize_script( 'pll_admin', 'pll_flag_base_url', POLYLANG_URL . '/flags/' );
307
 
308
- wp_enqueue_style( 'pll_selectmenu', POLYLANG_URL .'/css/selectmenu'.$suffix.'.css', array(), POLYLANG_VERSION );
309
  }
310
 
311
  /**
29
  public function __construct( &$links_model ) {
30
  parent::__construct( $links_model );
31
 
32
+ if ( isset( $_GET['page'] ) ) {
33
+ $this->active_tab = 'mlang' === $_GET['page'] ? 'lang' : substr( $_GET['page'], 6 );
34
+ }
35
 
36
  PLL_Admin_Strings::init();
37
 
39
  add_action( 'admin_init', array( $this, 'register_settings_modules' ) );
40
 
41
  // adds screen options and the about box in the languages admin panel
42
+ add_action( 'load-toplevel_page_mlang', array( $this, 'load_page' ) );
43
+ add_action( 'load-languages_page_mlang_strings', array( $this, 'load_page_strings' ) );
44
 
45
  // saves per-page value in screen option
46
  add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 10, 3 );
85
  }
86
  }
87
 
 
 
 
 
 
 
 
 
 
88
  /**
89
  * Loads the about metabox
90
  *
100
  * @since 0.9.5
101
  */
102
  public function load_page() {
103
+ if ( ! defined( 'PLL_DISPLAY_ABOUT' ) || PLL_DISPLAY_ABOUT ) {
104
+ add_meta_box(
105
+ 'pll-about-box',
106
+ __( 'About Polylang', 'polylang' ),
107
+ array( $this, 'metabox_about' ),
108
+ 'settings_page_mlang', // FIXME not shown in screen options
109
+ 'normal'
110
+ );
111
+ }
 
 
 
112
 
113
+ add_screen_option( 'per_page', array(
114
+ 'label' => __( 'Languages', 'polylang' ),
115
+ 'default' => 10,
116
+ 'option' => 'pll_lang_per_page',
117
+ ) );
118
 
119
+ add_action( 'admin_notices', array( $this, 'notice_objects_with_no_lang' ) );
120
+ }
121
 
122
+ /**
123
+ * Adds screen options in the strings translations admin panel
124
+ *
125
+ * @since 2.1
126
+ */
127
+ public function load_page_strings() {
128
+ add_screen_option( 'per_page', array(
129
+ 'label' => __( 'Strings translations', 'polylang' ),
130
+ 'default' => 10,
131
+ 'option' => 'pll_strings_per_page',
132
+ ) );
133
  }
134
 
135
  /**
244
  * @since 0.1
245
  */
246
  public function languages_page() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  switch ( $this->active_tab ) {
248
  case 'lang':
249
  // prepare the list table of languages
250
  $list_table = new PLL_Table_Languages();
251
+ $list_table->prepare_items( $this->model->get_languages_list() );
252
  break;
253
 
254
  case 'strings':
255
+ $string_table = new PLL_Table_String( $this->model->get_languages_list() );
256
  $string_table->prepare_items();
257
  break;
258
  }
266
  }
267
 
268
  // displays the page
269
+ include( PLL_SETTINGS_INC . '/view-languages.php' );
270
  }
271
 
272
  /**
277
 
278
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
279
 
280
+ wp_enqueue_script( 'pll_admin', plugins_url( '/js/admin' . $suffix . '.js', POLYLANG_FILE ), array( 'jquery', 'wp-ajax-response', 'postbox', 'jquery-ui-selectmenu' ), POLYLANG_VERSION );
281
+ wp_localize_script( 'pll_admin', 'pll_flag_base_url', plugins_url( '/flags/', POLYLANG_FILE ) );
282
 
283
+ wp_enqueue_style( 'pll_selectmenu', plugins_url( '/css/selectmenu' . $suffix . '.css', POLYLANG_FILE ), array(), POLYLANG_VERSION );
284
  }
285
 
286
  /**
settings/table-languages.php CHANGED
@@ -83,7 +83,7 @@ class PLL_Table_Languages extends WP_List_Table {
83
  return sprintf(
84
  '<a title="%s" href="%s">%s</a>',
85
  esc_attr__( 'Edit this language', 'polylang' ),
86
- esc_url( admin_url( 'options-general.php?page=mlang&amp;pll_action=edit&amp;lang=' . $item->term_id ) ),
87
  esc_html( $item->name )
88
  );
89
  }
@@ -168,6 +168,17 @@ class PLL_Table_Languages extends WP_List_Table {
168
  );
169
  }
170
 
 
 
 
 
 
 
 
 
 
 
 
171
  /**
172
  * Generates and display row actions links for the list table.
173
  *
@@ -187,7 +198,7 @@ class PLL_Table_Languages extends WP_List_Table {
187
  'edit' => sprintf(
188
  '<a title="%s" href="%s">%s</a>',
189
  esc_attr__( 'Edit this language', 'polylang' ),
190
- esc_url( admin_url( 'options-general.php?page=mlang&amp;pll_action=edit&amp;lang=' . $item->term_id ) ),
191
  esc_html__( 'Edit','polylang' )
192
  ),
193
  'delete' => sprintf(
83
  return sprintf(
84
  '<a title="%s" href="%s">%s</a>',
85
  esc_attr__( 'Edit this language', 'polylang' ),
86
+ esc_url( admin_url( 'admin.php?page=mlang&amp;pll_action=edit&amp;lang=' . $item->term_id ) ),
87
  esc_html( $item->name )
88
  );
89
  }
168
  );
169
  }
170
 
171
+ /**
172
+ * Gets the name of the default primary column.
173
+ *
174
+ * @since 2.1
175
+ *
176
+ * @return string Name of the default primary column, in this case, 'name'.
177
+ */
178
+ protected function get_default_primary_column_name() {
179
+ return 'name';
180
+ }
181
+
182
  /**
183
  * Generates and display row actions links for the list table.
184
  *
198
  'edit' => sprintf(
199
  '<a title="%s" href="%s">%s</a>',
200
  esc_attr__( 'Edit this language', 'polylang' ),
201
+ esc_url( admin_url( 'admin.php?page=mlang&amp;pll_action=edit&amp;lang=' . $item->term_id ) ),
202
  esc_html__( 'Edit','polylang' )
203
  ),
204
  'delete' => sprintf(
settings/table-settings.php CHANGED
@@ -117,15 +117,6 @@ class PLL_Table_Settings extends WP_List_Table {
117
  }
118
  }
119
 
120
- /**
121
- * Added for backward compatibility with WP < 4.2
122
- *
123
- * @since 1.8.2
124
- *
125
- * @param object $item
126
- */
127
- protected function column_cb( $item ) {}
128
-
129
  /**
130
  * Displays the item information in a column ( default case )
131
  *
@@ -179,4 +170,13 @@ class PLL_Table_Settings extends WP_List_Table {
179
  $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns(), $this->get_primary_column_name() );
180
  $this->items = $items;
181
  }
 
 
 
 
 
 
 
 
 
182
  }
117
  }
118
  }
119
 
 
 
 
 
 
 
 
 
 
120
  /**
121
  * Displays the item information in a column ( default case )
122
  *
170
  $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns(), $this->get_primary_column_name() );
171
  $this->items = $items;
172
  }
173
+
174
+ /**
175
+ * Avoids displaying an empty tablenav
176
+ *
177
+ * @since 2.1
178
+ *
179
+ * @param string $which 'top' or 'bottom'
180
+ */
181
+ protected function display_tablenav( $which ) {}
182
  }
settings/table-string.php CHANGED
@@ -135,6 +135,17 @@ class PLL_Table_String extends WP_List_Table {
135
  );
136
  }
137
 
 
 
 
 
 
 
 
 
 
 
 
138
  /**
139
  * Sort items
140
  *
135
  );
136
  }
137
 
138
+ /**
139
+ * Gets the name of the default primary column.
140
+ *
141
+ * @since 2.1
142
+ *
143
+ * @return string Name of the default primary column, in this case, 'string'.
144
+ */
145
+ protected function get_default_primary_column_name() {
146
+ return 'string';
147
+ }
148
+
149
  /**
150
  * Sort items
151
  *
settings/view-languages.php CHANGED
@@ -1,32 +1,23 @@
1
  <?php
2
 
3
  /**
4
- * displays the Languages admin panel
5
  */
6
 
7
  if ( ! defined( 'ABSPATH' ) ) {
8
- exit; // don't access directly
9
  };
 
 
10
  ?>
11
  <div class="wrap">
12
- <h1><?php esc_html_e( 'Languages', 'polylang' ); ?></h1>
13
- <h2 class="nav-tab-wrapper"><?php
14
- // display tabs
15
- foreach ( $tabs as $key => $name ) {
16
- printf(
17
- '<a href="options-general.php?page=mlang&amp;tab=%1$s" id="nav-tab-%1$s" class="nav-tab %2$s">%3$s</a>',
18
- esc_attr( $key ),
19
- $key == $this->active_tab ? 'nav-tab-active' : '',
20
- esc_html( $name )
21
- );
22
- }?>
23
- </h2><?php
24
 
25
  switch ( $this->active_tab ) {
26
  case 'lang': // Languages tab
27
- case 'strings': // string translations tab
28
- case 'settings': // settings tab
29
- include( PLL_SETTINGS_INC.'/view-tab-' . $this->active_tab . '.php' );
30
  break;
31
 
32
  default:
1
  <?php
2
 
3
  /**
4
+ * Displays the Languages admin panel
5
  */
6
 
7
  if ( ! defined( 'ABSPATH' ) ) {
8
+ exit; // Don't access directly
9
  };
10
+
11
+ require(ABSPATH . 'wp-admin/options-head.php'); // Displays the errors messages as when we were a child of options-general.php
12
  ?>
13
  <div class="wrap">
14
+ <h1><?php echo esc_html( $GLOBALS['title'] ); ?></h1><?php
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  switch ( $this->active_tab ) {
17
  case 'lang': // Languages tab
18
+ case 'strings': // String translations tab
19
+ case 'settings': // Settings tab
20
+ include( PLL_SETTINGS_INC . '/view-tab-' . $this->active_tab . '.php' );
21
  break;
22
 
23
  default:
settings/view-tab-lang.php CHANGED
@@ -28,7 +28,7 @@ if ( ! defined( 'ABSPATH' ) ) {
28
 
29
  // displays the add ( or edit ) language form
30
  // adds noheader=true in the action url to allow using wp_redirect when processing the form ?>
31
- <form id="add-lang" method="post" action="options-general.php?page=mlang&amp;noheader=true" class="validate"><?php
32
  wp_nonce_field( 'add-lang', '_wpnonce_add-lang' );
33
 
34
  if ( ! empty( $edit_lang ) ) {?>
28
 
29
  // displays the add ( or edit ) language form
30
  // adds noheader=true in the action url to allow using wp_redirect when processing the form ?>
31
+ <form id="add-lang" method="post" action="admin.php?page=mlang&amp;noheader=true" class="validate"><?php
32
  wp_nonce_field( 'add-lang', '_wpnonce_add-lang' );
33
 
34
  if ( ! empty( $edit_lang ) ) {?>