Polylang - Version 2.0

Version Description

(2016-08-02) =

  • Pro: Improve integration with ACF Pro
  • Pro: Add support for single sign on across multiple domains or subdomains
  • Pro: Add support for browser language detection when using multiple domains
  • Pro: Add support for translation of the static portion of the post permalink structure
  • Pro: Fix deactivated languages appearing in Yoast SEO sitemaps
  • Pro: Fix impossibility to visit a deactivated language when using subdomains or multiple domains (#10)
  • Pro: Fix when sharing slug on the page for posts, only one of them is accessible (#33)
  • Add the possibility to use the language switcher as dropdown in menu
  • Add support for custom logo introduced in WP 4.5 (#6)
  • The backend current language ( PLL()->curlang ) is now equal to the language of current post or term being edited (#19)
  • The sample permalink is now updated when changing the language in the Languages metabox
  • Revamp the wpml-config.xml reader to use simplexml instead of our custom xml parser
  • Improve support for the WPML API (including Hook API introduced in WPML 3.2)
  • Add support for translation of meta titles and descriptions of custom post types and custom taxonomies in Yoast SEO
  • Replace uncached functions by WPCOM VIP functions when available
  • Improve compatibility with WP 4.6
  • Fix parent category wrongly assigned to post when synchronizing children categories (#21)
  • Fix custom fonts not loaded when using multiple domains or subdomains
  • Fix remove_accents() not working for German and Danish (#24)
  • Fix incorrect static front pages urls on backend
  • Fix impossible to directly enter the page number in strings translation table (introduced in 1.9.3)
  • Fix conflict with WP Sweep (needs WP Sweep 1.0.8+)
  • Fix potential performance issue by querying only taxonomies to show in quick edit to filter the category checklist
  • Fix conflict (database error) with ReOrder-posts-within-categories plugin
  • Fix languages per page option not saved
Download this release

Release Info

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

Code changes from version 1.9.3 to 2.0

Files changed (93) hide show
  1. admin/admin-base.php +99 -62
  2. admin/admin-filters-columns.php +19 -19
  3. admin/admin-filters-media.php +5 -5
  4. admin/admin-filters-post.php +55 -20
  5. admin/admin-filters-term.php +36 -112
  6. admin/admin-filters.php +79 -10
  7. admin/admin-links.php +4 -4
  8. admin/admin-model.php +5 -1
  9. admin/admin-nav-menu.php +13 -17
  10. admin/admin-static-pages.php +3 -3
  11. admin/admin.php +5 -4
  12. admin/view-translations-media.php +1 -1
  13. admin/view-translations-post.php +3 -2
  14. admin/view-translations-term.php +4 -3
  15. css/admin.css +4 -0
  16. css/admin.min.css +1 -1
  17. frontend/choose-lang-content.php +3 -3
  18. frontend/choose-lang-url.php +2 -2
  19. frontend/choose-lang.php +2 -2
  20. frontend/frontend-auto-translate.php +7 -7
  21. frontend/frontend-filters-links.php +10 -9
  22. frontend/frontend-filters-search.php +6 -6
  23. frontend/frontend-filters.php +17 -17
  24. frontend/frontend-links.php +2 -2
  25. frontend/frontend-nav-menu.php +20 -8
  26. frontend/frontend-static-pages.php +43 -62
  27. frontend/frontend.php +23 -31
  28. include/api.php +7 -8
  29. include/base.php +3 -3
  30. include/cache.php +1 -1
  31. include/class-polylang.php +14 -5
  32. include/filters-links.php +8 -23
  33. include/filters.php +61 -32
  34. include/functions-wpcom-vip.php +29 -0
  35. include/license.php +1 -1
  36. include/links-abstract-domain.php +38 -0
  37. include/links-default.php +8 -2
  38. include/links-directory.php +13 -8
  39. include/links-domain.php +8 -18
  40. include/links-model.php +3 -3
  41. include/links-permalinks.php +1 -1
  42. include/links-subdomain.php +6 -3
  43. include/mo.php +10 -1
  44. include/model.php +41 -23
  45. include/nav-menu.php +1 -1
  46. include/olt-manager.php +10 -10
  47. include/pointer.php +2 -2
  48. include/static-pages.php +21 -2
  49. include/switcher.php +28 -22
  50. include/translated-post.php +8 -5
  51. include/translated-term.php +18 -4
  52. include/walker-dropdown.php +3 -2
  53. include/walker-list.php +1 -1
  54. include/widget-languages.php +1 -1
  55. install/install-base.php +3 -3
  56. install/install.php +1 -1
  57. install/plugin-updater.php +2 -2
  58. install/upgrade.php +5 -5
  59. js/admin.js +2 -2
  60. js/admin.min.js +1 -1
  61. js/nav-menu.js +2 -2
  62. js/nav-menu.min.js +1 -1
  63. js/post.js +6 -0
  64. js/post.min.js +1 -1
  65. js/term.js +1 -0
  66. js/term.min.js +1 -1
  67. lingotek/lingotek.php +22 -22
  68. modules/plugins/plugins-compat.php +152 -41
  69. modules/share-slug/settings-share-slug.php +2 -2
  70. modules/sync/admin-sync.php +14 -10
  71. modules/sync/settings-sync.php +1 -1
  72. modules/wpml/wpml-api.php +185 -0
  73. modules/wpml/wpml-compat.php +46 -350
  74. modules/wpml/wpml-config.php +92 -255
  75. modules/wpml/wpml-legacy-api.php +327 -0
  76. polylang.php +2 -2
  77. readme.txt +32 -4
  78. settings/flags.php +2 -2
  79. settings/languages.php +2 -2
  80. settings/settings-browser.php +19 -6
  81. settings/settings-cpt.php +4 -4
  82. settings/settings-licenses.php +11 -11
  83. settings/settings-module.php +11 -11
  84. settings/settings-tools.php +1 -1
  85. settings/settings-url.php +29 -28
  86. settings/settings.php +29 -21
  87. settings/table-languages.php +34 -33
  88. settings/table-settings.php +25 -22
  89. settings/table-string.php +30 -26
  90. settings/view-about.php +3 -3
  91. settings/view-languages.php +1 -1
  92. settings/view-tab-lang.php +18 -18
  93. settings/view-tab-strings.php +2 -2
admin/admin-base.php CHANGED
@@ -1,16 +1,16 @@
1
  <?php
2
 
3
  /**
4
- * base class for both admin
5
  *
6
  * @since 1.8
7
  */
8
  class PLL_Admin_Base extends PLL_Base {
9
- public $curlang, $pref_lang;
10
 
11
  /**
12
- * loads the polylang text domain
13
- * setups actions needed on all admin pages
14
  *
15
  * @since 1.8
16
  *
@@ -19,15 +19,15 @@ class PLL_Admin_Base extends PLL_Base {
19
  public function __construct( &$links_model ) {
20
  parent::__construct( $links_model );
21
 
22
- // plugin i18n, only needed for backend
23
  load_plugin_textdomain( 'polylang', false, basename( POLYLANG_DIR ).'/languages' );
24
 
25
- // adds the link to the languages panel in the WordPress admin menu
26
- add_action( 'admin_menu', array( &$this, 'add_menus' ) );
27
 
28
- // setup js scripts and css styles
29
- add_action( 'admin_enqueue_scripts', array( &$this, 'admin_enqueue_scripts' ) );
30
- add_action( 'admin_print_footer_scripts', array( &$this, 'admin_print_footer_scripts' ) );
31
 
32
  // Lingotek
33
  if ( ! defined( 'PLL_LINGOTEK_AD' ) || PLL_LINGOTEK_AD ) {
@@ -36,8 +36,8 @@ class PLL_Admin_Base extends PLL_Base {
36
  }
37
 
38
  /**
39
- * setups filters and action needed on all admin pages and on plugins page
40
- * loads the settings pages or the filters base on the request
41
  *
42
  * @since 1.2
43
  *
@@ -52,17 +52,17 @@ class PLL_Admin_Base extends PLL_Base {
52
  $this->static_pages = new PLL_Admin_Static_Pages( $this ); // FIXME needed here ?
53
  $this->filters_links = new PLL_Filters_Links( $this ); // FIXME needed here ?
54
 
55
- // filter admin language for users
56
- // we must not call user info before WordPress defines user roles in wp-settings.php
57
- add_filter( 'setup_theme', array( &$this, 'init_user' ) );
58
- add_filter( 'request', array( &$this, 'request' ) );
59
 
60
- // adds the languages in admin bar
61
- add_action( 'admin_bar_menu', array( &$this, 'admin_bar_menu' ), 100 ); // 100 determines the position
62
  }
63
 
64
  /**
65
- * adds the link to the languages panel in the WordPress admin menu
66
  *
67
  * @since 0.1
68
  */
@@ -71,7 +71,7 @@ class PLL_Admin_Base extends PLL_Base {
71
  }
72
 
73
  /**
74
- * setup js scripts & css styles ( only on the relevant pages )
75
  *
76
  * @since 0.6
77
  */
@@ -83,7 +83,7 @@ class PLL_Admin_Base extends PLL_Base {
83
 
84
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
85
 
86
- // for each script:
87
  // 0 => the pages on which to load the script
88
  // 1 => the scripts it needs to work
89
  // 2 => 1 if loaded even if languages have not been defined yet, 0 otherwise
@@ -98,18 +98,18 @@ 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
  /**
109
- * sets pll_ajax_backend on all backend ajax request
110
- * the final goal is to detect if an ajax request is made on admin or frontend
111
  *
112
- * takes care to various situations:
113
  * when the ajax request has no options.data thanks to ScreenfeedFr
114
  * see: https://wordpress.org/support/topic/ajaxprefilter-may-not-work-as-expected
115
  * when options.data is a json string
@@ -127,38 +127,32 @@ class PLL_Admin_Base extends PLL_Base {
127
  $params = array_merge( $params, array( 'pll_post_id' => (int) $post_ID ) );
128
  }
129
 
130
- $str = $arr = '';
131
- foreach ( $params as $k => $v ) {
132
- $str .= ( empty( $str ) ? '' : '&' ) . $k . '=' . $v;
133
- $arr .= ( empty( $arr ) ? '' : ', ') . $k . ': ' . $v;
134
- }
135
  ?>
136
  <script type="text/javascript">
137
  if (typeof jQuery != 'undefined') {
138
  (function($){
139
  $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
140
- if (options.url.indexOf(ajaxurl) != -1) {
141
- if ( typeof options.data === 'undefined' ) {
142
- options.data = options.type.toLowerCase() === "get" ? '<?php echo $str;?>' : {<?php echo $arr;?>};
143
- }
144
- else {
145
- if (typeof options.data === "string") {
146
- if ( '' === options.data && "get" === options.type.toLowerCase() ) {
147
- options.url = options.url+'<?php echo '&' . $str;?>';
148
- }
149
- else {
150
  try {
151
  o = $.parseJSON(options.data);
152
- o = $.extend(o, {<?php echo $arr;?>});
153
  options.data = JSON.stringify(o);
154
  }
155
  catch(e) {
156
- options.data = '<?php echo $str . '&';?>'+options.data;
157
  }
158
  }
159
- }
160
- else {
161
- options.data = $.extend(options.data, {<?php echo $arr;?>});
162
  }
163
  }
164
  }
@@ -166,28 +160,62 @@ class PLL_Admin_Base extends PLL_Base {
166
  })(jQuery)
167
  }
168
  </script><?php
 
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  }
171
 
172
  /**
173
- * defines the backend language and the admin language filter based on user preferences
174
  *
175
  * @since 1.2.3
176
  */
177
  public function init_user() {
178
- // backend locale
179
- add_filter( 'locale', array( &$this, 'get_locale' ) );
180
 
181
- // language for admin language filter: may be empty
182
  // $_GET['lang'] is numeric when editing a language, not when selecting a new language in the filter
183
  if ( ! defined( 'DOING_AJAX' ) && ! empty( $_GET['lang'] ) && ! is_numeric( $_GET['lang'] ) && current_user_can( 'edit_user', $user_id = get_current_user_id() ) ) {
184
  update_user_meta( $user_id, 'pll_filter_content', ( $lang = $this->model->get_language( $_GET['lang'] ) ) ? $lang->slug : '' );
185
  }
186
 
187
- $this->curlang = $this->model->get_language( get_user_meta( get_current_user_id(), 'pll_filter_content', true ) );
188
 
189
- // set preferred language for use when saving posts and terms: must not be empty
190
- $this->pref_lang = empty( $this->curlang ) ? $this->model->get_language( $this->options['default_lang'] ) : $this->curlang;
191
 
192
  /**
193
  * Filter the preferred language on amin side
@@ -199,8 +227,10 @@ class PLL_Admin_Base extends PLL_Base {
199
  */
200
  $this->pref_lang = apply_filters( 'pll_admin_preferred_language', $this->pref_lang );
201
 
202
- // inform that the admin language has been set
203
- // only if the admin language is one of the Polylang defined language
 
 
204
  if ( $curlang = $this->model->get_language( get_locale() ) ) {
205
  $GLOBALS['text_direction'] = $curlang->is_rtl ? 'rtl' : 'ltr'; // force text direction according to language setting
206
  /** This action is documented in frontend/choose-lang.php */
@@ -213,8 +243,8 @@ class PLL_Admin_Base extends PLL_Base {
213
  }
214
 
215
  /**
216
- * avoids parsing a tax query when all languages are requested
217
- * fixes https://wordpress.org/support/topic/notice-undefined-offset-0-in-wp-includesqueryphp-on-line-3877 introduced in WP 4.1
218
  * @see the suggestion of @boonebgorges, https://core.trac.wordpress.org/ticket/31246
219
  *
220
  * @since 1.6.5
@@ -231,7 +261,7 @@ class PLL_Admin_Base extends PLL_Base {
231
  }
232
 
233
  /**
234
- * get the locale based on user preference
235
  *
236
  * @since 0.4
237
  *
@@ -243,7 +273,7 @@ class PLL_Admin_Base extends PLL_Base {
243
  }
244
 
245
  /**
246
- * adds the languages list in admin bar for the admin languages filter
247
  *
248
  * @since 0.9
249
  *
@@ -258,16 +288,22 @@ class PLL_Admin_Base extends PLL_Base {
258
  'flag' => '<span class="ab-icon"></span>',
259
  );
260
 
261
- $selected = empty( $this->curlang ) ? $all_item : $this->curlang;
 
 
 
 
 
 
262
 
263
  $wp_admin_bar->add_menu( array(
264
  'id' => 'languages',
265
- 'title' => $selected->flag . '<span class="ab-label">'. esc_html( $selected->name ) . '</span>',
266
  'meta' => array( 'title' => __( 'Filters content by language', 'polylang' ) ),
267
  ) );
268
 
269
  foreach ( array_merge( array( $all_item ), $this->model->get_languages_list() ) as $lang ) {
270
- if ( $selected->slug == $lang->slug ) {
271
  continue;
272
  }
273
 
@@ -276,6 +312,7 @@ class PLL_Admin_Base extends PLL_Base {
276
  'id' => $lang->slug,
277
  'title' => $lang->flag . esc_html( $lang->name ),
278
  'href' => esc_url( add_query_arg( 'lang', $lang->slug, remove_query_arg( 'paged', $url ) ) ),
 
279
  ) );
280
  }
281
  }
1
  <?php
2
 
3
  /**
4
+ * Base class for both admin
5
  *
6
  * @since 1.8
7
  */
8
  class PLL_Admin_Base extends PLL_Base {
9
+ public $filter_lang, $curlang, $pref_lang;
10
 
11
  /**
12
+ * Loads the polylang text domain
13
+ * Setups actions needed on all admin pages
14
  *
15
  * @since 1.8
16
  *
19
  public function __construct( &$links_model ) {
20
  parent::__construct( $links_model );
21
 
22
+ // Plugin i18n, only needed for backend
23
  load_plugin_textdomain( 'polylang', false, basename( POLYLANG_DIR ).'/languages' );
24
 
25
+ // Adds the link to the languages panel in the WordPress admin menu
26
+ add_action( 'admin_menu', array( $this, 'add_menus' ) );
27
 
28
+ // Setup js scripts and css styles
29
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
30
+ add_action( 'admin_print_footer_scripts', array( $this, 'admin_print_footer_scripts' ) );
31
 
32
  // Lingotek
33
  if ( ! defined( 'PLL_LINGOTEK_AD' ) || PLL_LINGOTEK_AD ) {
36
  }
37
 
38
  /**
39
+ * Setups filters and action needed on all admin pages and on plugins page
40
+ * Loads the settings pages or the filters base on the request
41
  *
42
  * @since 1.2
43
  *
52
  $this->static_pages = new PLL_Admin_Static_Pages( $this ); // FIXME needed here ?
53
  $this->filters_links = new PLL_Filters_Links( $this ); // FIXME needed here ?
54
 
55
+ // Filter admin language for users
56
+ // We must not call user info before WordPress defines user roles in wp-settings.php
57
+ add_filter( 'setup_theme', array( $this, 'init_user' ) );
58
+ add_filter( 'request', array( $this, 'request' ) );
59
 
60
+ // Adds the languages in admin bar
61
+ add_action( 'admin_bar_menu', array( $this, 'admin_bar_menu' ), 100 ); // 100 determines the position
62
  }
63
 
64
  /**
65
+ * Adds the link to the languages panel in the WordPress admin menu
66
  *
67
  * @since 0.1
68
  */
71
  }
72
 
73
  /**
74
+ * Setup js scripts & css styles ( only on the relevant pages )
75
  *
76
  * @since 0.6
77
  */
83
 
84
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
85
 
86
+ // For each script:
87
  // 0 => the pages on which to load the script
88
  // 1 => the scripts it needs to work
89
  // 2 => 1 if loaded even if languages have not been defined yet, 0 otherwise
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
  /**
109
+ * Sets pll_ajax_backend on all backend ajax request
110
+ * The final goal is to detect if an ajax request is made on admin or frontend
111
  *
112
+ * Takes care to various situations:
113
  * when the ajax request has no options.data thanks to ScreenfeedFr
114
  * see: https://wordpress.org/support/topic/ajaxprefilter-may-not-work-as-expected
115
  * when options.data is a json string
127
  $params = array_merge( $params, array( 'pll_post_id' => (int) $post_ID ) );
128
  }
129
 
130
+ $str = http_build_query( $params );
131
+ $arr = json_encode( $params );
 
 
 
132
  ?>
133
  <script type="text/javascript">
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 {
141
+ if ( 'string' === typeof options.data ) {
142
+ if ( '' === options.data && 'get' === options.type.toLowerCase() ) {
143
+ options.url = options.url+'&<?php echo $str;?>';
144
+ } else {
 
 
145
  try {
146
  o = $.parseJSON(options.data);
147
+ o = $.extend(o, <?php echo $arr;?>);
148
  options.data = JSON.stringify(o);
149
  }
150
  catch(e) {
151
+ options.data = '<?php echo $str;?>&'+options.data;
152
  }
153
  }
154
+ } else {
155
+ options.data = $.extend(options.data, <?php echo $arr;?>);
 
156
  }
157
  }
158
  }
160
  })(jQuery)
161
  }
162
  </script><?php
163
+ }
164
 
165
+ /**
166
+ * Sets the admin current language, used to filter the content
167
+ *
168
+ * @since 2.0
169
+ */
170
+ public function set_current_language() {
171
+ $this->curlang = $this->filter_lang;
172
+
173
+ // Edit Post
174
+ if ( isset( $_REQUEST['pll_post_id'] ) ) {
175
+ $this->curlang = $this->model->post->get_language( (int) $_REQUEST['pll_post_id'] );
176
+ } elseif ( 'post.php' === $GLOBALS['pagenow'] && isset( $_GET['post'] ) && is_numeric( $_GET['post'] ) ) {
177
+ $this->curlang = $this->model->post->get_language( (int) $_GET['post'] );
178
+ } elseif ( 'post-new.php' === $GLOBALS['pagenow'] ) {
179
+ $this->curlang = empty( $_GET['new_lang'] ) ? $this->pref_lang : $this->model->get_language( $_GET['new_lang'] );
180
+ }
181
+
182
+ // Edit Term
183
+ // FIXME 'edit-tags.php' for backward compatibility with WP < 4.5
184
+ elseif ( in_array( $GLOBALS['pagenow'], array( 'edit-tags.php', 'term.php' ) ) && isset( $_GET['tag_ID'] ) ) {
185
+ $this->curlang = $this->model->term->get_language( (int) $_GET['tag_ID'] );
186
+ } elseif ( 'edit-tags.php' === $GLOBALS['pagenow'] ) {
187
+ if ( ! empty( $_GET['new_lang'] ) ) {
188
+ $this->curlang = $this->model->get_language( $_GET['new_lang'] );
189
+ } elseif ( empty( $this->curlang ) ) {
190
+ $this->curlang = $this->pref_lang;
191
+ }
192
+ }
193
+
194
+ // Ajax
195
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX && ! empty( $_REQUEST['lang'] ) ) {
196
+ $this->curlang = $this->model->get_language( $_REQUEST['lang'] );
197
+ }
198
  }
199
 
200
  /**
201
+ * Defines the backend language and the admin language filter based on user preferences
202
  *
203
  * @since 1.2.3
204
  */
205
  public function init_user() {
206
+ // Backend locale
207
+ add_filter( 'locale', array( $this, 'get_locale' ) );
208
 
209
+ // Language for admin language filter: may be empty
210
  // $_GET['lang'] is numeric when editing a language, not when selecting a new language in the filter
211
  if ( ! defined( 'DOING_AJAX' ) && ! empty( $_GET['lang'] ) && ! is_numeric( $_GET['lang'] ) && current_user_can( 'edit_user', $user_id = get_current_user_id() ) ) {
212
  update_user_meta( $user_id, 'pll_filter_content', ( $lang = $this->model->get_language( $_GET['lang'] ) ) ? $lang->slug : '' );
213
  }
214
 
215
+ $this->filter_lang = $this->model->get_language( get_user_meta( get_current_user_id(), 'pll_filter_content', true ) );
216
 
217
+ // Set preferred language for use when saving posts and terms: must not be empty
218
+ $this->pref_lang = empty( $this->filter_lang ) ? $this->model->get_language( $this->options['default_lang'] ) : $this->filter_lang;
219
 
220
  /**
221
  * Filter the preferred language on amin side
227
  */
228
  $this->pref_lang = apply_filters( 'pll_admin_preferred_language', $this->pref_lang );
229
 
230
+ $this->set_current_language();
231
+
232
+ // Inform that the admin language has been set
233
+ // Only if the admin language is one of the Polylang defined language
234
  if ( $curlang = $this->model->get_language( get_locale() ) ) {
235
  $GLOBALS['text_direction'] = $curlang->is_rtl ? 'rtl' : 'ltr'; // force text direction according to language setting
236
  /** This action is documented in frontend/choose-lang.php */
243
  }
244
 
245
  /**
246
+ * Avoids parsing a tax query when all languages are requested
247
+ * Fixes https://wordpress.org/support/topic/notice-undefined-offset-0-in-wp-includesqueryphp-on-line-3877 introduced in WP 4.1
248
  * @see the suggestion of @boonebgorges, https://core.trac.wordpress.org/ticket/31246
249
  *
250
  * @since 1.6.5
261
  }
262
 
263
  /**
264
+ * Get the locale based on user preference
265
  *
266
  * @since 0.4
267
  *
273
  }
274
 
275
  /**
276
+ * Adds the languages list in admin bar for the admin languages filter
277
  *
278
  * @since 0.9
279
  *
288
  'flag' => '<span class="ab-icon"></span>',
289
  );
290
 
291
+ $selected = empty( $this->filter_lang ) ? $all_item : $this->filter_lang;
292
+
293
+ $title = sprintf(
294
+ '<span class="ab-label"%s>%s</span>',
295
+ 'all' === $selected->slug ? '' : sprintf( ' lang="%s"', esc_attr( $selected->get_locale( 'display' ) ) ),
296
+ esc_html( $selected->name )
297
+ );
298
 
299
  $wp_admin_bar->add_menu( array(
300
  'id' => 'languages',
301
+ 'title' => $selected->flag . $title,
302
  'meta' => array( 'title' => __( 'Filters content by language', 'polylang' ) ),
303
  ) );
304
 
305
  foreach ( array_merge( array( $all_item ), $this->model->get_languages_list() ) as $lang ) {
306
+ if ( $selected->slug === $lang->slug ) {
307
  continue;
308
  }
309
 
312
  'id' => $lang->slug,
313
  'title' => $lang->flag . esc_html( $lang->name ),
314
  'href' => esc_url( add_query_arg( 'lang', $lang->slug, remove_query_arg( 'paged', $url ) ) ),
315
+ 'meta' => 'all' === $lang->slug ? array() : array( 'lang' => esc_attr( $lang->get_locale( 'display' ) ) ),
316
  ) );
317
  }
318
  }
admin/admin-filters-columns.php CHANGED
@@ -7,7 +7,7 @@
7
  * @since 1.2
8
  */
9
  class PLL_Admin_Filters_Columns {
10
- public $links, $model, $curlang;
11
 
12
  /**
13
  * constructor: setups filters and actions
@@ -19,29 +19,29 @@ class PLL_Admin_Filters_Columns {
19
  public function __construct( &$polylang ) {
20
  $this->links = &$polylang->links;
21
  $this->model = &$polylang->model;
22
- $this->curlang = &$polylang->curlang;
23
 
24
  // add the language and translations columns in 'All Posts', 'All Pages' and 'Media library' panels
25
  foreach ( $this->model->get_translated_post_types() as $type ) {
26
  // use the latest filter late as some plugins purely overwrite what's done by others :(
27
  // specific case for media
28
- add_filter( 'manage_'. ( 'attachment' == $type ? 'upload' : 'edit-'. $type ) .'_columns', array( &$this, 'add_post_column' ), 100 );
29
- add_action( 'manage_'. ( 'attachment' == $type ? 'media' : $type .'_posts' ) .'_custom_column', array( &$this, 'post_column' ), 10, 2 );
30
  }
31
 
32
  // quick edit and bulk edit
33
- add_filter( 'quick_edit_custom_box', array( &$this, 'quick_edit_custom_box' ), 10, 2 );
34
- add_filter( 'bulk_edit_custom_box', array( &$this, 'quick_edit_custom_box' ), 10, 2 );
35
 
36
  // adds the language column in the 'Categories' and 'Post Tags' tables
37
  foreach ( $this->model->get_translated_taxonomies() as $tax ) {
38
- add_filter( 'manage_edit-'.$tax.'_columns', array( &$this, 'add_term_column' ) );
39
- add_filter( 'manage_'.$tax.'_custom_column', array( &$this, 'term_column' ), 10, 3 );
40
  }
41
 
42
  // ajax responses to update list table rows
43
- add_action( 'wp_ajax_pll_update_post_rows', array( &$this, 'ajax_update_post_rows' ) );
44
- add_action( 'wp_ajax_pll_update_term_rows', array( &$this, 'ajax_update_term_rows' ) );
45
  }
46
 
47
  /**
@@ -61,7 +61,7 @@ class PLL_Admin_Filters_Columns {
61
 
62
  foreach ( $this->model->get_languages_list() as $language ) {
63
  // don't add the column for the filtered language
64
- if ( empty( $this->curlang ) || $language->slug != $this->curlang->slug ) {
65
  $columns[ 'language_'.$language->slug ] = $language->flag ? $language->flag . '<span class="screen-reader-text">' . esc_html( $language->name ) . '</span>' : esc_html( $language->slug );
66
  }
67
  }
@@ -78,7 +78,7 @@ class PLL_Admin_Filters_Columns {
78
  */
79
  protected function get_first_language_column() {
80
  foreach ( $this->model->get_languages_list() as $language ) {
81
- if ( empty( $this->curlang ) || $language->slug != $this->curlang->slug ) {
82
  $columns[] = 'language_' . $language->slug;
83
  }
84
  }
@@ -131,11 +131,11 @@ class PLL_Admin_Filters_Columns {
131
  if ( $link = get_edit_post_link( $id ) ) {
132
  if ( $id === $post_id ) {
133
  $class = 'pll_icon_tick';
134
- /* translators: %s is a native language name */
135
  $s = sprintf( __( 'Edit this item in %s', 'polylang' ), $language->name );
136
  } else {
137
  $class = esc_attr( 'pll_icon_edit translation_' . $id );
138
- /* translators: %s is a native language name */
139
  $s = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
140
  }
141
  printf(
@@ -145,7 +145,7 @@ class PLL_Admin_Filters_Columns {
145
  } elseif ( $id === $post_id ) {
146
  printf(
147
  '<span class="pll_icon_tick"><span class="screen-reader-text">%s</span></span>',
148
- /* translators: %s is a native language name */
149
  esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) )
150
  );
151
  }
@@ -184,7 +184,7 @@ class PLL_Admin_Filters_Columns {
184
  </label>
185
  </div>
186
  </fieldset>',
187
- __( 'Language', 'polylang' ),
188
  $dropdown->walk( $elements, array( 'name' => 'inline_lang_choice', 'id' => '' ) )
189
  );
190
  }
@@ -242,11 +242,11 @@ class PLL_Admin_Filters_Columns {
242
  if ( $link = get_edit_term_link( $id, $taxonomy, $post_type ) ) {
243
  if ( $id === $term_id ) {
244
  $class = 'pll_icon_tick';
245
- /* translators: %s is a native language name */
246
  $s = sprintf( __( 'Edit this item in %s', 'polylang' ), $language->name );
247
  } else {
248
  $class = esc_attr( 'pll_icon_edit translation_' . $id );
249
- /* translators: %s is a native language name */
250
  $s = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
251
  }
252
  $out .= sprintf(
@@ -256,7 +256,7 @@ class PLL_Admin_Filters_Columns {
256
  } elseif ( $id === $term_id ) {
257
  $out .= printf(
258
  '<span class="pll_icon_tick"><span class="screen-reader-text">%s</span></span>',
259
- /* translators: %s is a native language name */
260
  esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) )
261
  );
262
  }
7
  * @since 1.2
8
  */
9
  class PLL_Admin_Filters_Columns {
10
+ public $links, $model, $filter_lang;
11
 
12
  /**
13
  * constructor: setups filters and actions
19
  public function __construct( &$polylang ) {
20
  $this->links = &$polylang->links;
21
  $this->model = &$polylang->model;
22
+ $this->filter_lang = &$polylang->filter_lang;
23
 
24
  // add the language and translations columns in 'All Posts', 'All Pages' and 'Media library' panels
25
  foreach ( $this->model->get_translated_post_types() as $type ) {
26
  // use the latest filter late as some plugins purely overwrite what's done by others :(
27
  // specific case for media
28
+ add_filter( 'manage_'. ( 'attachment' == $type ? 'upload' : 'edit-'. $type ) .'_columns', array( $this, 'add_post_column' ), 100 );
29
+ add_action( 'manage_'. ( 'attachment' == $type ? 'media' : $type .'_posts' ) .'_custom_column', array( $this, 'post_column' ), 10, 2 );
30
  }
31
 
32
  // quick edit and bulk edit
33
+ add_filter( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
34
+ add_filter( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
35
 
36
  // adds the language column in the 'Categories' and 'Post Tags' tables
37
  foreach ( $this->model->get_translated_taxonomies() as $tax ) {
38
+ add_filter( 'manage_edit-'.$tax.'_columns', array( $this, 'add_term_column' ) );
39
+ add_filter( 'manage_'.$tax.'_custom_column', array( $this, 'term_column' ), 10, 3 );
40
  }
41
 
42
  // ajax responses to update list table rows
43
+ add_action( 'wp_ajax_pll_update_post_rows', array( $this, 'ajax_update_post_rows' ) );
44
+ add_action( 'wp_ajax_pll_update_term_rows', array( $this, 'ajax_update_term_rows' ) );
45
  }
46
 
47
  /**
61
 
62
  foreach ( $this->model->get_languages_list() as $language ) {
63
  // don't add the column for the filtered language
64
+ if ( empty( $this->filter_lang ) || $language->slug != $this->filter_lang->slug ) {
65
  $columns[ 'language_'.$language->slug ] = $language->flag ? $language->flag . '<span class="screen-reader-text">' . esc_html( $language->name ) . '</span>' : esc_html( $language->slug );
66
  }
67
  }
78
  */
79
  protected function get_first_language_column() {
80
  foreach ( $this->model->get_languages_list() as $language ) {
81
+ if ( empty( $this->filter_lang ) || $language->slug != $this->filter_lang->slug ) {
82
  $columns[] = 'language_' . $language->slug;
83
  }
84
  }
131
  if ( $link = get_edit_post_link( $id ) ) {
132
  if ( $id === $post_id ) {
133
  $class = 'pll_icon_tick';
134
+ /* translators: accessibility text, %s is a native language name */
135
  $s = sprintf( __( 'Edit this item in %s', 'polylang' ), $language->name );
136
  } else {
137
  $class = esc_attr( 'pll_icon_edit translation_' . $id );
138
+ /* translators: accessibility text, %s is a native language name */
139
  $s = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
140
  }
141
  printf(
145
  } elseif ( $id === $post_id ) {
146
  printf(
147
  '<span class="pll_icon_tick"><span class="screen-reader-text">%s</span></span>',
148
+ /* translators: accessibility text, %s is a native language name */
149
  esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) )
150
  );
151
  }
184
  </label>
185
  </div>
186
  </fieldset>',
187
+ esc_html__( 'Language', 'polylang' ),
188
  $dropdown->walk( $elements, array( 'name' => 'inline_lang_choice', 'id' => '' ) )
189
  );
190
  }
242
  if ( $link = get_edit_term_link( $id, $taxonomy, $post_type ) ) {
243
  if ( $id === $term_id ) {
244
  $class = 'pll_icon_tick';
245
+ /* translators: accessibility text, %s is a native language name */
246
  $s = sprintf( __( 'Edit this item in %s', 'polylang' ), $language->name );
247
  } else {
248
  $class = esc_attr( 'pll_icon_edit translation_' . $id );
249
+ /* translators: accessibility text, %s is a native language name */
250
  $s = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
251
  }
252
  $out .= sprintf(
256
  } elseif ( $id === $term_id ) {
257
  $out .= printf(
258
  '<span class="pll_icon_tick"><span class="screen-reader-text">%s</span></span>',
259
+ /* translators: accessibility text, %s is a native language name */
260
  esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) )
261
  );
262
  }
admin/admin-filters-media.php CHANGED
@@ -18,16 +18,16 @@ class PLL_Admin_Filters_Media extends PLL_Admin_Filters_Post_Base {
18
  parent::__construct( $polylang );
19
 
20
  // Adds the language field and translations tables in the 'Edit Media' panel
21
- add_filter( 'attachment_fields_to_edit', array( &$this, 'attachment_fields_to_edit' ), 10, 2 );
22
 
23
  // Adds actions related to languages when creating, saving or deleting media
24
- add_action( 'add_attachment', array( &$this, 'set_default_language' ) );
25
- add_filter( 'attachment_fields_to_save', array( &$this, 'save_media' ), 10, 2 );
26
- add_filter( 'wp_delete_file', array( &$this, 'wp_delete_file' ) );
27
 
28
  // Creates a media translation
29
  if ( isset( $_GET['action'], $_GET['new_lang'], $_GET['from_media'] ) && 'translate_media' === $_GET['action'] ) {
30
- add_action( 'admin_init', array( &$this, 'translate_media' ) );
31
  }
32
  }
33
 
18
  parent::__construct( $polylang );
19
 
20
  // Adds the language field and translations tables in the 'Edit Media' panel
21
+ add_filter( 'attachment_fields_to_edit', array( $this, 'attachment_fields_to_edit' ), 10, 2 );
22
 
23
  // Adds actions related to languages when creating, saving or deleting media
24
+ add_action( 'add_attachment', array( $this, 'set_default_language' ) );
25
+ add_filter( 'attachment_fields_to_save', array( $this, 'save_media' ), 10, 2 );
26
+ add_filter( 'wp_delete_file', array( $this, 'wp_delete_file' ) );
27
 
28
  // Creates a media translation
29
  if ( isset( $_GET['action'], $_GET['new_lang'], $_GET['from_media'] ) && 'translate_media' === $_GET['action'] ) {
30
+ add_action( 'admin_init', array( $this, 'translate_media' ) );
31
  }
32
  }
33
 
admin/admin-filters-post.php CHANGED
@@ -20,28 +20,28 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
20
  $this->options = &$polylang->options;
21
  $this->curlang = &$polylang->curlang;
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' ) );
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
  /**
@@ -59,7 +59,7 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
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 && $this->model->is_translated_taxonomy( $taxonomy->name ) ) {
63
  $hierarchical_taxonomies[] = $taxonomy->name;
64
  }
65
  }
@@ -113,30 +113,62 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
113
  return;
114
  }
115
 
116
- if ( isset( $qvars['post_type'] ) && ! isset( $qvars['lang'] ) ) {
117
- // filters the list of media ( or wp-links ) by language when uploading from post
118
- if ( isset( $_REQUEST['pll_post_id'] ) && $lang = $this->model->post->get_language( (int) $_REQUEST['pll_post_id'] ) ) {
119
- $query->set( 'lang', $lang->slug );
120
- } elseif ( ! empty( $this->curlang ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  $qvars['lang'] = $this->curlang->slug;
122
  }
123
  }
124
 
 
125
  if ( isset( $qvars['lang'] ) && 'all' === $qvars['lang'] ) {
126
  unset( $qvars['lang'] );
127
  }
128
  }
129
 
130
  /**
131
- * adds the Language box in the 'Edit Post' and 'Edit Page' panels ( as well as in custom post types panels )
 
132
  *
133
  * @since 0.1
134
  *
135
  * @param string $post_type
136
  */
137
- public function add_meta_boxes( $post_type ) {
138
  if ( $this->model->is_translated_post_type( $post_type ) ) {
139
- add_meta_box( 'ml_box', __( 'Languages','polylang' ), array( &$this, 'post_language' ), $post_type, 'side', 'high' );
 
 
 
 
 
140
  }
141
  }
142
 
@@ -163,7 +195,7 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
163
  <p><strong>%1$s</strong></p>
164
  <label class="screen-reader-text" for="%2$s">%1$s</label>
165
  <div id="select-%3$s-language">%4$s</div>',
166
- __( 'Language', 'polylang' ),
167
  $id = ( 'attachment' === $post_type ) ? sprintf( 'attachments[%d][language]', $post_ID ) : 'post_lang_choice',
168
  'attachment' === $post_type ? 'media' : 'post',
169
  $dropdown->walk( $this->model->get_languages_list(), array(
@@ -270,6 +302,9 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
270
  // flag
271
  $x->Add( array( 'what' => 'flag', 'data' => empty( $lang->flag ) ? esc_html( $lang->slug ) : $lang->flag ) );
272
 
 
 
 
273
  $x->send();
274
  }
275
 
20
  $this->options = &$polylang->options;
21
  $this->curlang = &$polylang->curlang;
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
  /**
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;
64
  }
65
  }
113
  return;
114
  }
115
 
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 ) {
126
+ if ( ! empty( $qvars[ $k ] ) ) {
127
+ return;
128
+ }
129
+ }
130
+
131
+ $taxonomies = array_intersect( $this->model->get_translated_taxonomies(), get_taxonomies( array( '_builtin' => false ) ) );
132
+
133
+ foreach ( $taxonomies as $tax ) {
134
+ $tax = get_taxonomy( $tax );
135
+ if ( ! empty( $qv[ $tax->query_var ] ) ) {
136
+ return;
137
+ }
138
+ }
139
+
140
+ if ( ! empty( $qvars['tax_query'] ) && is_array( $qvars['tax_query'] ) && $this->model->have_translated_taxonomy( $qvars['tax_query'] ) ) {
141
+ return;
142
+ }
143
+
144
+ // Filter queries according to the current language
145
+ if ( isset( $qvars['post_type'] ) && ! empty( $this->curlang ) ) {
146
  $qvars['lang'] = $this->curlang->slug;
147
  }
148
  }
149
 
150
+
151
  if ( isset( $qvars['lang'] ) && 'all' === $qvars['lang'] ) {
152
  unset( $qvars['lang'] );
153
  }
154
  }
155
 
156
  /**
157
+ * Adds the Language box in the 'Edit Post' and 'Edit Page' panels ( as well as in custom post types panels )
158
+ * Removes the editor for translations of the pages for posts
159
  *
160
  * @since 0.1
161
  *
162
  * @param string $post_type
163
  */
164
+ public function add_meta_boxes( $post_type, $post ) {
165
  if ( $this->model->is_translated_post_type( $post_type ) ) {
166
+ add_meta_box( 'ml_box', __( 'Languages','polylang' ), array( $this, 'post_language' ), $post_type, 'side', 'high' );
167
+ }
168
+
169
+ if ( ( $page_for_posts = get_option( 'page_for_posts' ) ) && ( $translations = $this->model->post->get_translations( $page_for_posts ) ) && in_array( $post->ID, $translations ) && empty( $post->post_content ) ) {
170
+ add_action( 'edit_form_after_title', '_wp_posts_page_notice' );
171
+ remove_post_type_support( $post_type, 'editor' );
172
  }
173
  }
174
 
195
  <p><strong>%1$s</strong></p>
196
  <label class="screen-reader-text" for="%2$s">%1$s</label>
197
  <div id="select-%3$s-language">%4$s</div>',
198
+ esc_html__( 'Language', 'polylang' ),
199
  $id = ( 'attachment' === $post_type ) ? sprintf( 'attachments[%d][language]', $post_ID ) : 'post_lang_choice',
200
  'attachment' === $post_type ? 'media' : 'post',
201
  $dropdown->walk( $this->model->get_languages_list(), array(
302
  // flag
303
  $x->Add( array( 'what' => 'flag', 'data' => empty( $lang->flag ) ? esc_html( $lang->slug ) : $lang->flag ) );
304
 
305
+ // Sample permalink
306
+ $x->Add( array( 'what' => 'permalink', 'data' => get_sample_permalink_html( $post_ID ) ) );
307
+
308
  $x->send();
309
  }
310
 
admin/admin-filters-term.php CHANGED
@@ -6,7 +6,7 @@
6
  * @since 1.2
7
  */
8
  class PLL_Admin_Filters_Term {
9
- public $links, $model, $options, $curlang, $pref_lang;
10
  protected $pre_term_name; // Used to store the term name before creating a slug if needed
11
  protected $post_id; // Used to store the current post_id when bulk editing posts
12
 
@@ -20,43 +20,44 @@ class PLL_Admin_Filters_Term {
20
  $this->model = &$polylang->model;
21
  $this->options = &$polylang->options;
22
  $this->curlang = &$polylang->curlang;
 
23
  $this->pref_lang = &$polylang->pref_lang;
24
 
25
  foreach ( $this->model->get_translated_taxonomies() as $tax ) {
26
  // Adds the language field in the 'Categories' and 'Post Tags' panels
27
- add_action( $tax.'_add_form_fields', array( &$this, 'add_term_form' ) );
28
 
29
  // Adds the language field and translations tables in the 'Edit Category' and 'Edit Tag' panels
30
- add_action( $tax.'_edit_form_fields', array( &$this, 'edit_term_form' ) );
31
 
32
  // Adds action related to languages when deleting categories and post tags
33
- add_action( 'delete_'.$tax, array( &$this, 'delete_term' ) );
34
  }
35
 
36
  // Adds actions related to languages when creating or saving categories and post tags
37
- add_filter( 'wp_dropdown_cats', array( &$this, 'wp_dropdown_cats' ) );
38
- add_action( 'create_term', array( &$this, 'save_term' ), 999, 3 );
39
- add_action( 'edit_term', array( &$this, 'save_term' ), 999, 3 ); // late as it may conflict with other plugins, see http://wordpress.org/support/topic/polylang-and-wordpress-seo-by-yoast
40
- add_action( 'pre_post_update', array( &$this, 'pre_post_update' ) );
41
- add_filter( 'pre_term_name', array( &$this, 'pre_term_name' ) );
42
- add_filter( 'pre_term_slug', array( &$this, 'pre_term_slug' ), 10, 2 );
43
 
44
  // Ajax response for edit term form
45
- add_action( 'wp_ajax_term_lang_choice', array( &$this, 'term_lang_choice' ) );
46
- add_action( 'wp_ajax_pll_terms_not_translated', array( &$this, 'ajax_terms_not_translated' ) );
47
 
48
  // Adds cache domain when querying terms
49
- add_filter( 'get_terms_args', array( &$this, 'get_terms_args' ), 10, 2 );
50
 
51
  // Filters categories and post tags by language
52
- add_filter( 'terms_clauses', array( &$this, 'terms_clauses' ), 10, 3 );
53
 
54
  // Allows to get the default categories in all languages
55
- add_filter( 'option_default_category', array( &$this, 'option_default_category' ) );
56
- add_action( 'update_option_default_category', array( &$this, 'update_option_default_category' ), 10, 2 );
57
 
58
  // Updates the translations term ids when splitting a shared term
59
- add_action( 'split_shared_term', array( &$this, 'split_shared_term' ), 10, 4 ); // WP 4.2
60
  }
61
 
62
  /**
@@ -83,18 +84,18 @@ class PLL_Admin_Filters_Term {
83
  <div id="select-add-term-language">%s</div>
84
  <p>%s</p>
85
  </div>',
86
- __( 'Language', 'polylang' ),
87
  $dropdown->walk( $this->model->get_languages_list(), array(
88
  'name' => 'term_lang_choice',
89
  'value' => 'term_id',
90
  'selected' => $lang ? $lang->term_id : '',
91
  'flag' => true,
92
  ) ),
93
- __( 'Sets the language', 'polylang' )
94
  );
95
 
96
  if ( ! empty( $_GET['from_tag'] ) ) {
97
- printf( '<input type="hidden" name="from_tag" value="%d" />', $_GET['from_tag'] );
98
  }
99
 
100
  // Adds translation fields
@@ -137,7 +138,7 @@ class PLL_Admin_Filters_Term {
137
  <p class="description">%s</p>
138
  </td>
139
  </tr>',
140
- __( 'Language', 'polylang' ),
141
  $dropdown->walk( $this->model->get_languages_list(), array(
142
  'name' => 'term_lang_choice',
143
  'value' => 'term_id',
@@ -145,7 +146,7 @@ class PLL_Admin_Filters_Term {
145
  'disabled' => $disabled,
146
  'flag' => true,
147
  ) ),
148
- __( 'Sets the language', 'polylang' )
149
  );
150
 
151
  echo '<tr id="term-translations" class="form-field">';
@@ -494,7 +495,8 @@ class PLL_Admin_Filters_Term {
494
  }
495
 
496
  if ( $tag_cloud = wp_tag_cloud( $args ) ) {
497
- $x->Add( array( 'what' => 'tag_cloud', 'data' => '<h3>'.$tax->labels->popular_items.'</h3>'.$tag_cloud ) );
 
498
  }
499
  }
500
  }
@@ -527,8 +529,7 @@ class PLL_Admin_Filters_Term {
527
  $return = array();
528
 
529
  // It is more efficient to use one common query for all languages as soon as there are more than 2
530
- // pll_get_terms_not_translated arg to identify this query in terms_clauses filter
531
- foreach ( get_terms( $taxonomy, 'hide_empty=0&pll_get_terms_not_translated=1&name__like=' . $s ) as $term ) {
532
  $lang = $this->model->term->get_language( $term->term_id );
533
 
534
  if ( $lang && $lang->slug == $translation_language->slug && ! $this->model->term->get_translation( $term->term_id, $term_language ) ) {
@@ -554,7 +555,6 @@ class PLL_Admin_Filters_Term {
554
  wp_die( json_encode( $return ) );
555
  }
556
 
557
-
558
  /**
559
  * Get the language(s) to filter get_terms
560
  *
@@ -571,97 +571,16 @@ class PLL_Admin_Filters_Term {
571
  }
572
 
573
  // If get_terms is queried with a 'lang' parameter
574
- if ( ! empty( $args['lang'] ) ) {
575
  return $args['lang'];
576
  }
577
 
578
- // Do not filter 'get_terms_not_translated'
579
- if ( ! empty( $args['pll_get_terms_not_translated'] ) ) {
580
- return false;
581
- }
582
-
583
- if ( function_exists( 'get_current_screen' ) ) {
584
- $screen = get_current_screen(); // since WP 3.1, may not be available the first time(s) get_terms is called
585
- }
586
-
587
- // Don't filter nav menus on nav menus screen
588
- if ( isset( $screen ) && 'nav-menus' == $screen->base && in_array( 'nav_menu', $taxonomies ) ) {
589
- return false;
590
- }
591
-
592
- // Does nothing in Languages and dasboard admin panels
593
- if ( isset( $screen ) && in_array( $screen->base, array( 'toplevel_page_mlang', 'dashboard' ) ) ) {
594
- return false;
595
- }
596
-
597
- // Ajax actions
598
- if ( isset( $_POST['action'] ) ) {
599
- // Admin language filter for:
600
- // Ajax paginate_links in taxonomies metabox in nav menus panel
601
- // And taxonomies menus items in customizer menus ( since WP 4.3 )
602
- if ( ! empty( $this->curlang ) && in_array( $_POST['action'], array( 'menu-get-metabox', 'load-available-menu-items-customizer' ) ) ) {
603
- return $this->curlang;
604
- }
605
-
606
- // The only ajax response I want to deal with is when changing the language in post metabox
607
- if ( ! in_array( $_POST['action'], array( 'post_lang_choice', 'term_lang_choice', 'get-tagcloud' ) ) ) {
608
- return false;
609
- }
610
-
611
- // I only want to filter the parent dropdown list when editing a term in a hierarchical taxonomy
612
- if ( 'term_lang_choice' == $_POST['action'] && ! ( isset( $args['class'] ) || isset( $args['unit'] ) ) ) {
613
- return false;
614
- }
615
- }
616
-
617
- // Ajax response for changing the language in the post metabox ( or in the edit-tags panels )
618
- if ( isset( $_POST['lang'] ) ) {
619
- $lang = $this->model->get_language( $_POST['lang'] );
620
  }
621
 
622
- // The post ( or term ) is created with the 'add new' ( translation ) link
623
- // Test of $args['page'] to avoid filtering the terms list table in edit-tags panel
624
- elseif ( ! empty( $_GET['new_lang'] ) && empty( $args['page'] ) ) {
625
- $lang = $this->model->get_language( $_GET['new_lang'] );
626
- }
627
-
628
- // FIXME can we simplify how we deal with the admin language filter?
629
- // The language filter selection has just changed
630
- // Test $screen->base to avoid interference between the language filter and the post language selection and the category parent dropdown list
631
- elseif ( ! empty( $_GET['lang'] ) && ! ( isset( $screen ) && in_array( $screen->base, array( 'post', 'edit-tags', 'term' ) ) ) ) {
632
- if ( 'all' != $_GET['lang'] ) {
633
- $lang = $this->model->get_language( $_GET['lang'] );
634
- }
635
- elseif ( in_array( $screen->base, array( 'edit-tags', 'term' ) ) && isset( $args['class'] ) ) {
636
- $lang = $this->pref_lang; // Parent dropdown
637
- }
638
- }
639
-
640
- // Again the language filter
641
- elseif ( ! empty( $this->curlang ) && ( isset( $screen ) && 'post' != $screen->base && ! ( in_array( $screen->base, array( 'edit-tags', 'term' ) ) && isset( $args['class'] ) ) ) ) { // don't apply to post edit and the category parent dropdown list
642
- $lang = $this->curlang;
643
- }
644
-
645
- elseif ( isset( $_GET['post'] ) && is_numeric( $_GET['post'] ) ) { // is numeric avoids array of posts in *post* bulk edit
646
- $lang = $this->model->post->get_language( $_GET['post'] );
647
- }
648
-
649
- // For the parent dropdown list in edit term
650
- elseif ( isset( $_GET['tag_ID'] ) ) {
651
- $lang = $this->model->term->get_language( (int) $_GET['tag_ID'] );
652
- }
653
-
654
- // When a new category is created in the edit post panel
655
- elseif ( isset( $_POST['term_lang_choice'] ) ) {
656
- $lang = $this->model->get_language( $_POST['term_lang_choice'] );
657
- }
658
-
659
- // For a new post ( or the parent dropdown list of a new term )
660
- elseif ( isset( $screen ) && ( 'post' == $screen->base || ( in_array( $screen->base, array( 'edit-tags', 'term' ) ) && isset( $args['class'] ) ) ) ) {
661
- $lang = $this->pref_lang;
662
- }
663
-
664
- return empty( $lang ) ? false : $lang;
665
  }
666
 
667
  /**
@@ -675,6 +594,11 @@ class PLL_Admin_Filters_Term {
675
  * @return array modified arguments
676
  */
677
  public function get_terms_args( $args, $taxonomies ) {
 
 
 
 
 
678
  if ( $lang = $this->get_queried_language( $taxonomies, $args ) ) {
679
  $key = '_' . ( is_array( $lang ) ? implode( ',', $lang ) : $this->model->get_language( $lang )->slug );
680
  $args['cache_domain'] = empty( $args['cache_domain'] ) ? 'pll' . $key : $args['cache_domain'] . $key;
6
  * @since 1.2
7
  */
8
  class PLL_Admin_Filters_Term {
9
+ public $links, $model, $options, $curlang, $filter_lang, $pref_lang;
10
  protected $pre_term_name; // Used to store the term name before creating a slug if needed
11
  protected $post_id; // Used to store the current post_id when bulk editing posts
12
 
20
  $this->model = &$polylang->model;
21
  $this->options = &$polylang->options;
22
  $this->curlang = &$polylang->curlang;
23
+ $this->filter_lang = &$polylang->filter_lang;
24
  $this->pref_lang = &$polylang->pref_lang;
25
 
26
  foreach ( $this->model->get_translated_taxonomies() as $tax ) {
27
  // Adds the language field in the 'Categories' and 'Post Tags' panels
28
+ add_action( $tax.'_add_form_fields', array( $this, 'add_term_form' ) );
29
 
30
  // Adds the language field and translations tables in the 'Edit Category' and 'Edit Tag' panels
31
+ add_action( $tax.'_edit_form_fields', array( $this, 'edit_term_form' ) );
32
 
33
  // Adds action related to languages when deleting categories and post tags
34
+ add_action( 'delete_'.$tax, array( $this, 'delete_term' ) );
35
  }
36
 
37
  // Adds actions related to languages when creating or saving categories and post tags
38
+ add_filter( 'wp_dropdown_cats', array( $this, 'wp_dropdown_cats' ) );
39
+ add_action( 'create_term', array( $this, 'save_term' ), 999, 3 );
40
+ add_action( 'edit_term', array( $this, 'save_term' ), 999, 3 ); // late as it may conflict with other plugins, see http://wordpress.org/support/topic/polylang-and-wordpress-seo-by-yoast
41
+ add_action( 'pre_post_update', array( $this, 'pre_post_update' ) );
42
+ add_filter( 'pre_term_name', array( $this, 'pre_term_name' ) );
43
+ add_filter( 'pre_term_slug', array( $this, 'pre_term_slug' ), 10, 2 );
44
 
45
  // Ajax response for edit term form
46
+ add_action( 'wp_ajax_term_lang_choice', array( $this, 'term_lang_choice' ) );
47
+ add_action( 'wp_ajax_pll_terms_not_translated', array( $this, 'ajax_terms_not_translated' ) );
48
 
49
  // Adds cache domain when querying terms
50
+ add_filter( 'get_terms_args', array( $this, 'get_terms_args' ), 10, 2 );
51
 
52
  // Filters categories and post tags by language
53
+ add_filter( 'terms_clauses', array( $this, 'terms_clauses' ), 10, 3 );
54
 
55
  // Allows to get the default categories in all languages
56
+ add_filter( 'option_default_category', array( $this, 'option_default_category' ) );
57
+ add_action( 'update_option_default_category', array( $this, 'update_option_default_category' ), 10, 2 );
58
 
59
  // Updates the translations term ids when splitting a shared term
60
+ add_action( 'split_shared_term', array( $this, 'split_shared_term' ), 10, 4 ); // WP 4.2
61
  }
62
 
63
  /**
84
  <div id="select-add-term-language">%s</div>
85
  <p>%s</p>
86
  </div>',
87
+ esc_html__( 'Language', 'polylang' ),
88
  $dropdown->walk( $this->model->get_languages_list(), array(
89
  'name' => 'term_lang_choice',
90
  'value' => 'term_id',
91
  'selected' => $lang ? $lang->term_id : '',
92
  'flag' => true,
93
  ) ),
94
+ esc_html__( 'Sets the language', 'polylang' )
95
  );
96
 
97
  if ( ! empty( $_GET['from_tag'] ) ) {
98
+ printf( '<input type="hidden" name="from_tag" value="%d" />', (int) $_GET['from_tag'] );
99
  }
100
 
101
  // Adds translation fields
138
  <p class="description">%s</p>
139
  </td>
140
  </tr>',
141
+ esc_html__( 'Language', 'polylang' ),
142
  $dropdown->walk( $this->model->get_languages_list(), array(
143
  'name' => 'term_lang_choice',
144
  'value' => 'term_id',
146
  'disabled' => $disabled,
147
  'flag' => true,
148
  ) ),
149
+ esc_html__( 'Sets the language', 'polylang' )
150
  );
151
 
152
  echo '<tr id="term-translations" class="form-field">';
495
  }
496
 
497
  if ( $tag_cloud = wp_tag_cloud( $args ) ) {
498
+ $html = sprintf( '<div class="tagcloud"><h2>%1$s</h2>%2$s</div>', esc_html( $tax->labels->popular_items ), $tag_cloud );
499
+ $x->Add( array( 'what' => 'tag_cloud', 'data' => $html ) );
500
  }
501
  }
502
  }
529
  $return = array();
530
 
531
  // It is more efficient to use one common query for all languages as soon as there are more than 2
532
+ foreach ( get_terms( $taxonomy, 'hide_empty=0&lang=0&name__like=' . $s ) as $term ) {
 
533
  $lang = $this->model->term->get_language( $term->term_id );
534
 
535
  if ( $lang && $lang->slug == $translation_language->slug && ! $this->model->term->get_translation( $term->term_id, $term_language ) ) {
555
  wp_die( json_encode( $return ) );
556
  }
557
 
 
558
  /**
559
  * Get the language(s) to filter get_terms
560
  *
571
  }
572
 
573
  // If get_terms is queried with a 'lang' parameter
574
+ if ( isset( $args['lang'] ) ) {
575
  return $args['lang'];
576
  }
577
 
578
+ // On tags page, everything should be filtered according to the admin language filter except the parent dropdown
579
+ if ( 'edit-tags.php' === $GLOBALS['pagenow'] && empty( $args['class'] ) ) {
580
+ return $this->filter_lang;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
  }
582
 
583
+ return $this->curlang;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
584
  }
585
 
586
  /**
594
  * @return array modified arguments
595
  */
596
  public function get_terms_args( $args, $taxonomies ) {
597
+ // don't break _get_term_hierarchy()
598
+ if ( 'all' === $args['get'] && 'id' === $args['orderby'] && 'id=>parent' === $args['fields'] ) {
599
+ $args['lang'] = '';
600
+ }
601
+
602
  if ( $lang = $this->get_queried_language( $taxonomies, $args ) ) {
603
  $key = '_' . ( is_array( $lang ) ? implode( ',', $lang ) : $this->model->get_language( $lang )->slug );
604
  $args['cache_domain'] = empty( $args['cache_domain'] ) ? 'pll' . $key : $args['cache_domain'] . $key;
admin/admin-filters.php CHANGED
@@ -18,21 +18,28 @@ class PLL_Admin_Filters extends PLL_Filters {
18
  parent::__construct( $polylang );
19
 
20
  // widgets languages filter
21
- add_action( 'in_widget_form', array( &$this, 'in_widget_form' ), 10, 3 );
22
- add_filter( 'widget_update_callback', array( &$this, 'widget_update_callback' ), 10, 4 );
23
 
24
  // language management for users
25
- add_action( 'personal_options_update', array( &$this, 'personal_options_update' ) );
26
- add_action( 'edit_user_profile_update', array( &$this, 'personal_options_update' ) );
27
- add_action( 'personal_options', array( &$this, 'personal_options' ) );
28
 
29
  // ugrades languages files after a core upgrade ( timing is important )
30
  // backward compatibility WP < 4.0 *AND* Polylang < 1.6
31
- add_action( '_core_updated_successfully', array( &$this, 'upgrade_languages' ), 1 ); // since WP 3.3
32
 
33
  // upgrades plugins and themes translations files
34
- add_filter( 'themes_update_check_locales', array( &$this, 'update_check_locales' ) );
35
- add_filter( 'plugins_update_check_locales', array( &$this, 'update_check_locales' ) );
 
 
 
 
 
 
 
36
  }
37
 
38
  /**
@@ -46,7 +53,7 @@ class PLL_Admin_Filters extends PLL_Filters {
46
  $dropdown = new PLL_Walker_Dropdown();
47
  printf( '<p><label for="%1$s">%2$s %3$s</label></p>',
48
  esc_attr( $widget->id.'_lang_choice' ),
49
- __( 'The widget is displayed for:', 'polylang' ),
50
  $dropdown->walk(
51
  array_merge(
52
  array( (object) array( 'slug' => 0, 'name' => __( 'All languages', 'polylang' ) ) ),
@@ -121,7 +128,7 @@ class PLL_Admin_Filters extends PLL_Filters {
121
  <th><label for="user_lang">%s</label></th>
122
  <td>%s</td>
123
  </tr>',
124
- __( 'Admin language', 'polylang' ),
125
  $dropdown->walk(
126
  array_merge(
127
  array( (object) array( 'locale' => 0, 'name' => __( 'WordPress default', 'polylang' ) ) ),
@@ -179,4 +186,66 @@ class PLL_Admin_Filters extends PLL_Filters {
179
  function update_check_locales( $locales ) {
180
  return $this->model->get_languages_list( array( 'fields' => 'locale' ) );
181
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  }
18
  parent::__construct( $polylang );
19
 
20
  // widgets languages filter
21
+ add_action( 'in_widget_form', array( $this, 'in_widget_form' ), 10, 3 );
22
+ add_filter( 'widget_update_callback', array( $this, 'widget_update_callback' ), 10, 4 );
23
 
24
  // language management for users
25
+ add_action( 'personal_options_update', array( $this, 'personal_options_update' ) );
26
+ add_action( 'edit_user_profile_update', array( $this, 'personal_options_update' ) );
27
+ add_action( 'personal_options', array( $this, 'personal_options' ) );
28
 
29
  // ugrades languages files after a core upgrade ( timing is important )
30
  // backward compatibility WP < 4.0 *AND* Polylang < 1.6
31
+ add_action( '_core_updated_successfully', array( $this, 'upgrade_languages' ), 1 ); // since WP 3.3
32
 
33
  // upgrades plugins and themes translations files
34
+ add_filter( 'themes_update_check_locales', array( $this, 'update_check_locales' ) );
35
+ add_filter( 'plugins_update_check_locales', array( $this, 'update_check_locales' ) );
36
+
37
+ // We need specific filters for German and Danish
38
+ $specific_locales = array( 'da_DK', 'de_DE', 'de_DE_formal', 'de_CH', 'de_CH_informal' );
39
+ if ( array_intersect( $this->model->get_languages_list( array( 'fields' => 'locale' ) ), $specific_locales ) ) {
40
+ add_filter( 'sanitize_title', array( $this, 'sanitize_title' ), 10, 3 );
41
+ add_filter( 'sanitize_user', array( $this, 'sanitize_user' ), 10, 3 );
42
+ }
43
  }
44
 
45
  /**
53
  $dropdown = new PLL_Walker_Dropdown();
54
  printf( '<p><label for="%1$s">%2$s %3$s</label></p>',
55
  esc_attr( $widget->id.'_lang_choice' ),
56
+ esc_html__( 'The widget is displayed for:', 'polylang' ),
57
  $dropdown->walk(
58
  array_merge(
59
  array( (object) array( 'slug' => 0, 'name' => __( 'All languages', 'polylang' ) ) ),
128
  <th><label for="user_lang">%s</label></th>
129
  <td>%s</td>
130
  </tr>',
131
+ esc_html__( 'Admin language', 'polylang' ),
132
  $dropdown->walk(
133
  array_merge(
134
  array( (object) array( 'locale' => 0, 'name' => __( 'WordPress default', 'polylang' ) ) ),
186
  function update_check_locales( $locales ) {
187
  return $this->model->get_languages_list( array( 'fields' => 'locale' ) );
188
  }
189
+
190
+ /**
191
+ * Filters the locale according to the current language instead of the language
192
+ * of the admin interface
193
+ *
194
+ * @since 2.0
195
+ *
196
+ * @param string $locale
197
+ * @return string
198
+ */
199
+ public function get_locale( $locale ) {
200
+ return $this->curlang->locale;
201
+ }
202
+
203
+ /**
204
+ * Maybe fix the result of sanitize_title() in case the languages include German or Danish
205
+ * Without this filter, if language of the title being sanitized is different from the language
206
+ * used for the admin interface and if one this language is German or Danish, some specific
207
+ * characters such as ä, ö, ü, ß are incorrectly sanitized.
208
+ *
209
+ * @since 2.0
210
+ *
211
+ * @param string $title Sanitized title.
212
+ * @param string $raw_title The title prior to sanitization.
213
+ * @param string $context The context for which the title is being sanitized.
214
+ * @return string
215
+ */
216
+ public function sanitize_title( $title, $raw_title, $context ) {
217
+ static $once = false;
218
+
219
+ if ( ! $once && 'save' == $context && ! empty( $this->curlang ) && ! empty( $title ) ) {
220
+ $once = true;
221
+ add_filter( 'locale', array( $this, 'get_locale' ), 20 ); // After the filter for the admin interface
222
+ $title = sanitize_title( $raw_title, '', $context );
223
+ remove_filter( 'locale', array( $this, 'get_locale' ), 20 );
224
+ $once = false;
225
+ }
226
+ return $title;
227
+ }
228
+
229
+ /**
230
+ * Maybe fix the result of sanitize_user() in case the languages include German or Danish
231
+ *
232
+ * @since 2.0
233
+ *
234
+ * @param string $username Sanitized username.
235
+ * @param string $raw_username The username prior to sanitization.
236
+ * @param bool $strict Whether to limit the sanitization to specific characters. Default false.
237
+ * @return string
238
+ */
239
+ public function sanitize_user( $username, $raw_username, $strict ) {
240
+ static $once = false;
241
+
242
+ if ( ! $once && ! empty( $this->curlang ) ) {
243
+ $once = true;
244
+ add_filter( 'locale', array( $this, 'get_locale' ), 20 ); // After the filter for the admin interface
245
+ $title = sanitize_title( $raw_username, '', $strict );
246
+ remove_filter( 'locale', array( $this, 'get_locale' ), 20 );
247
+ $once = false;
248
+ }
249
+ return $username;
250
+ }
251
  }
admin/admin-links.php CHANGED
@@ -68,7 +68,7 @@ class PLL_Admin_Links extends PLL_Links {
68
  return $link ? sprintf(
69
  '<a href="%1$s" class="pll_icon_add"><span class="screen-reader-text">%2$s</span></a>',
70
  esc_url( $link ),
71
- /* translators: %s is a native language name */
72
  esc_html( sprintf( __( 'Add a translation in %s', 'polylang' ), $language->name ) )
73
  ) : '';
74
  }
@@ -87,7 +87,7 @@ class PLL_Admin_Links extends PLL_Links {
87
  return $link ? sprintf(
88
  '<a href="%1$s" class="pll_icon_edit"><span class="screen-reader-text">%2$s</span></a>',
89
  esc_url( $link ),
90
- /* translators: %s is a native language name */
91
  esc_html( sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name ) )
92
  ) : '';
93
  }
@@ -148,7 +148,7 @@ class PLL_Admin_Links extends PLL_Links {
148
  return $link ? sprintf(
149
  '<a href="%1$s" class="pll_icon_add"><span class="screen-reader-text">%2$s</span></a>',
150
  esc_url( $link ),
151
- /* translators: %s is a native language name */
152
  esc_html( sprintf( __( 'Add a translation in %s', 'polylang' ), $language->name ) )
153
  ) : '';
154
  }
@@ -169,7 +169,7 @@ class PLL_Admin_Links extends PLL_Links {
169
  return $link ? sprintf(
170
  '<a href="%1$s" class="pll_icon_edit"><span class="screen-reader-text">%2$s</span></a>',
171
  esc_url( $link ),
172
- /* translators: %s is a native language name */
173
  esc_html( sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name ) )
174
  ) : '';
175
  }
68
  return $link ? sprintf(
69
  '<a href="%1$s" class="pll_icon_add"><span class="screen-reader-text">%2$s</span></a>',
70
  esc_url( $link ),
71
+ /* translators: accessibility text, %s is a native language name */
72
  esc_html( sprintf( __( 'Add a translation in %s', 'polylang' ), $language->name ) )
73
  ) : '';
74
  }
87
  return $link ? sprintf(
88
  '<a href="%1$s" class="pll_icon_edit"><span class="screen-reader-text">%2$s</span></a>',
89
  esc_url( $link ),
90
+ /* translators: accessibility text, %s is a native language name */
91
  esc_html( sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name ) )
92
  ) : '';
93
  }
148
  return $link ? sprintf(
149
  '<a href="%1$s" class="pll_icon_add"><span class="screen-reader-text">%2$s</span></a>',
150
  esc_url( $link ),
151
+ /* translators: accessibility text, %s is a native language name */
152
  esc_html( sprintf( __( 'Add a translation in %s', 'polylang' ), $language->name ) )
153
  ) : '';
154
  }
169
  return $link ? sprintf(
170
  '<a href="%1$s" class="pll_icon_edit"><span class="screen-reader-text">%2$s</span></a>',
171
  esc_url( $link ),
172
+ /* translators: accessibility text, %s is a native language name */
173
  esc_html( sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name ) )
174
  ) : '';
175
  }
admin/admin-model.php CHANGED
@@ -137,7 +137,7 @@ class PLL_Admin_Model extends PLL_Model {
137
  }
138
 
139
  // Delete the string translations
140
- $post = get_page_by_title( 'polylang_mo_' . $lang->term_id, OBJECT, 'polylang_mo' );
141
  if ( ! empty( $post ) ) {
142
  wp_delete_post( $post->ID );
143
  }
@@ -483,6 +483,10 @@ class PLL_Admin_Model extends PLL_Model {
483
  WHERE term_id IN ( " . implode( ',', $ut['in'] ) . " )
484
  " );
485
  }
 
 
 
 
486
  }
487
 
488
  /**
137
  }
138
 
139
  // Delete the string translations
140
+ $post = wpcom_vip_get_page_by_title( 'polylang_mo_' . $lang->term_id, OBJECT, 'polylang_mo' );
141
  if ( ! empty( $post ) ) {
142
  wp_delete_post( $post->ID );
143
  }
483
  WHERE term_id IN ( " . implode( ',', $ut['in'] ) . " )
484
  " );
485
  }
486
+
487
+ foreach ( $terms as $term ) {
488
+ clean_term_cache( $term->term_id, $term->taxonomy );
489
+ }
490
  }
491
 
492
  /**
admin/admin-nav-menu.php CHANGED
@@ -24,12 +24,12 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
24
  add_filter( 'theme_mod_nav_menu_locations', array( $this, 'theme_mod_nav_menu_locations' ), 20 );
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
 
@@ -40,20 +40,20 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
40
  * @since 1.1
41
  */
42
  public function admin_init() {
43
- add_action( 'admin_enqueue_scripts', array( &$this, 'admin_enqueue_scripts' ) );
44
- add_action( 'wp_update_nav_menu_item', array( &$this, 'wp_update_nav_menu_item' ), 10, 2 );
45
- add_filter( 'wp_get_nav_menu_items', array( &$this, 'translate_switcher_title' ) );
46
 
47
  // translation of menus based on chosen locations
48
  add_filter( 'pre_update_option_theme_mods_' . $this->theme, array( $this, 'pre_update_option_theme_mods' ) );
49
- add_action( 'delete_nav_menu', array( &$this, 'delete_nav_menu' ) );
50
 
51
  // filter _wp_auto_add_pages_to_menu by language
52
- add_action( 'transition_post_status', array( &$this, 'auto_add_pages_to_menu' ), 5, 3 ); // before _wp_auto_add_pages_to_menu
53
 
54
  // FIXME is it possible to choose the order ( after theme locations in WP3.5 and older ) ?
55
  // FIXME not displayed if Polylang is activated before the first time the user goes to nav menus http://core.trac.wordpress.org/ticket/16828
56
- add_meta_box( 'pll_lang_switch_box', __( 'Language switcher', 'polylang' ), array( &$this, 'lang_switch' ), 'nav-menus', 'side', 'high' );
57
 
58
  $this->create_nav_menu_locations();
59
  }
@@ -74,10 +74,10 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
74
  <ul id ="lang-switch-checklist" class="categorychecklist form-no-clear">
75
  <li>
76
  <label class="menu-item-title">
77
- <input type="checkbox" class="menu-item-checkbox" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-object-id]" value="-1"> <?php _e( 'Language switcher', 'polylang' ); ?>
78
  </label>
79
  <input type="hidden" class="menu-item-type" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-type]" value="custom">
80
- <input type="hidden" class="menu-item-title" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-title]" value="<?php _e( 'Language switcher', 'polylang' ); ?>">
81
  <input type="hidden" class="menu-item-url" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-url]" value="#pll_switcher">
82
  </li>
83
  </ul>
@@ -105,11 +105,7 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
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
- // the strings for the options
109
- foreach ( array_reverse( PLL_Switcher::get_switcher_options( 'menu', 'string' ) ) as $str ) {
110
- $data['strings'][] = $str;
111
- }
112
-
113
  $data['title'] = __( 'Language switcher', 'polylang' ); // the title
114
 
115
  // get all language switcher menu items
@@ -149,7 +145,7 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
149
  if ( current_user_can( 'edit_theme_options' ) ) {
150
  check_admin_referer( 'update-nav_menu', 'update-nav-menu-nonce' );
151
 
152
- $options = array( 'hide_if_no_translation' => 0, 'hide_current' => 0,'force_home' => 0 ,'show_flags' => 0 ,'show_names' => 1 ); // default values
153
  // our jQuery form has not been displayed
154
  if ( empty( $_POST['menu-item-pll-detect'][ $menu_item_db_id ] ) ) {
155
  if ( ! get_post_meta( $menu_item_db_id, '_pll_menu_item', true ) ) { // our options were never saved
@@ -328,7 +324,7 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
328
 
329
  if ( ! empty( $menus ) ) {
330
  $this->auto_add_menus = implode( ',', $menus );
331
- add_filter( 'option_nav_menu_options', array( &$this, 'nav_menu_options' ) );
332
  }
333
  }
334
  }
24
  add_filter( 'theme_mod_nav_menu_locations', array( $this, 'theme_mod_nav_menu_locations' ), 20 );
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
 
40
  * @since 1.1
41
  */
42
  public function admin_init() {
43
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
44
+ add_action( 'wp_update_nav_menu_item', array( $this, 'wp_update_nav_menu_item' ), 10, 2 );
45
+ add_filter( 'wp_get_nav_menu_items', array( $this, 'translate_switcher_title' ) );
46
 
47
  // translation of menus based on chosen locations
48
  add_filter( 'pre_update_option_theme_mods_' . $this->theme, array( $this, 'pre_update_option_theme_mods' ) );
49
+ add_action( 'delete_nav_menu', array( $this, 'delete_nav_menu' ) );
50
 
51
  // filter _wp_auto_add_pages_to_menu by language
52
+ add_action( 'transition_post_status', array( $this, 'auto_add_pages_to_menu' ), 5, 3 ); // before _wp_auto_add_pages_to_menu
53
 
54
  // FIXME is it possible to choose the order ( after theme locations in WP3.5 and older ) ?
55
  // FIXME not displayed if Polylang is activated before the first time the user goes to nav menus http://core.trac.wordpress.org/ticket/16828
56
+ add_meta_box( 'pll_lang_switch_box', __( 'Language switcher', 'polylang' ), array( $this, 'lang_switch' ), 'nav-menus', 'side', 'high' );
57
 
58
  $this->create_nav_menu_locations();
59
  }
74
  <ul id ="lang-switch-checklist" class="categorychecklist form-no-clear">
75
  <li>
76
  <label class="menu-item-title">
77
+ <input type="checkbox" class="menu-item-checkbox" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-object-id]" value="-1"> <?php esc_html_e( 'Language switcher', 'polylang' ); ?>
78
  </label>
79
  <input type="hidden" class="menu-item-type" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-type]" value="custom">
80
+ <input type="hidden" class="menu-item-title" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-title]" value="<?php esc_html_e( 'Language switcher', 'polylang' ); ?>">
81
  <input type="hidden" class="menu-item-url" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-url]" value="#pll_switcher">
82
  </li>
83
  </ul>
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
110
 
111
  // get all language switcher menu items
145
  if ( current_user_can( 'edit_theme_options' ) ) {
146
  check_admin_referer( 'update-nav_menu', 'update-nav-menu-nonce' );
147
 
148
+ $options = array( 'hide_if_no_translation' => 0, 'hide_current' => 0,'force_home' => 0 ,'show_flags' => 0 ,'show_names' => 1, 'dropdown' => 0 ); // default values
149
  // our jQuery form has not been displayed
150
  if ( empty( $_POST['menu-item-pll-detect'][ $menu_item_db_id ] ) ) {
151
  if ( ! get_post_meta( $menu_item_db_id, '_pll_menu_item', true ) ) { // our options were never saved
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
  }
admin/admin-static-pages.php CHANGED
@@ -18,13 +18,13 @@ class PLL_Admin_Static_Pages extends PLL_Static_Pages {
18
  parent::__construct( $polylang );
19
 
20
  // add post state for translations of the front page and posts page
21
- add_filter( 'display_post_states', array( &$this, 'display_post_states' ), 10, 2 );
22
 
23
  // refresh language cache when a static front page has been translated
24
- add_action( 'pll_save_post', array( &$this, 'pll_save_post' ), 10, 3 );
25
 
26
  // checks if chosen page on front is translated
27
- add_filter( 'pre_update_option_page_on_front', array( &$this, 'update_page_on_front' ), 10, 2 );
28
 
29
  // Prevents WP resetting the option
30
  add_filter( 'pre_update_option_show_on_front', array( $this, 'update_show_on_front' ), 10, 2 );
18
  parent::__construct( $polylang );
19
 
20
  // add post state for translations of the front page and posts page
21
+ add_filter( 'display_post_states', array( $this, 'display_post_states' ), 10, 2 );
22
 
23
  // refresh language cache when a static front page has been translated
24
+ add_action( 'pll_save_post', array( $this, 'pll_save_post' ), 10, 3 );
25
 
26
  // checks if chosen page on front is translated
27
+ add_filter( 'pre_update_option_page_on_front', array( $this, 'update_page_on_front' ), 10, 2 );
28
 
29
  // Prevents WP resetting the option
30
  add_filter( 'pre_update_option_show_on_front', array( $this, 'update_show_on_front' ), 10, 2 );
admin/admin.php CHANGED
@@ -11,7 +11,8 @@
11
  * links => inherited, reference to PLL_Admin_Links object
12
  * static_pages => inherited, reference to PLL_Admin_Static_Pages object
13
  * filters_links => inherited, reference to PLL_Filters_Links object
14
- * curlang => inherited, optional, current language used to filter admin content
 
15
  * pref_lang => inherited, preferred language used as default when saving posts or terms
16
  * filters => reference to PLL_Filters object
17
  * filters_columns => reference to PLL_Admin_Filters_Columns object
@@ -37,8 +38,8 @@ class PLL_Admin extends PLL_Admin_Base {
37
  parent::__construct( $links_model );
38
 
39
  // adds a 'settings' link in the plugins table
40
- add_filter( 'plugin_action_links_' . POLYLANG_BASENAME, array( &$this, 'plugin_action_links' ) );
41
- add_action( 'in_plugin_update_message-' . POLYLANG_BASENAME, array( &$this, 'plugin_update_message' ), 10, 2 );
42
  }
43
 
44
  /**
@@ -55,7 +56,7 @@ class PLL_Admin extends PLL_Admin_Base {
55
  // setup filters for admin pages
56
  // priority 5 to make sure filters are there before customize_register is fired
57
  if ( $this->model->get_languages_list() ) {
58
- add_action( 'wp_loaded', array( &$this, 'add_filters' ), 5 );
59
  }
60
  }
61
 
11
  * links => inherited, reference to PLL_Admin_Links object
12
  * static_pages => inherited, reference to PLL_Admin_Static_Pages object
13
  * filters_links => inherited, reference to PLL_Filters_Links object
14
+ * curlang => inherited, optional, current language used to filter the content (language of the post or term being edited, equal to filter_lang otherwise)
15
+ * filter_lang => inherited, optional, current status of the admin languages filter (in the admin bar)
16
  * pref_lang => inherited, preferred language used as default when saving posts or terms
17
  * filters => reference to PLL_Filters object
18
  * filters_columns => reference to PLL_Admin_Filters_Columns object
38
  parent::__construct( $links_model );
39
 
40
  // adds a 'settings' link in the plugins table
41
+ add_filter( 'plugin_action_links_' . POLYLANG_BASENAME, array( $this, 'plugin_action_links' ) );
42
+ add_action( 'in_plugin_update_message-' . POLYLANG_BASENAME, array( $this, 'plugin_update_message' ), 10, 2 );
43
  }
44
 
45
  /**
56
  // setup filters for admin pages
57
  // priority 5 to make sure filters are there before customize_register is fired
58
  if ( $this->model->get_languages_list() ) {
59
+ add_action( 'wp_loaded', array( $this, 'add_filters' ), 5 );
60
  }
61
  }
62
 
admin/view-translations-media.php CHANGED
@@ -9,7 +9,7 @@ if ( ! defined( 'ABSPATH' ) ) {
9
  exit; // don't access directly
10
  };
11
  ?>
12
- <p><strong><?php _e( 'Translations', 'polylang' );?></strong></p>
13
  <table><?php
14
  foreach ( $this->model->get_languages_list() as $language ) {
15
  if ( $language->term_id == $lang->term_id ) {
9
  exit; // don't access directly
10
  };
11
  ?>
12
+ <p><strong><?php esc_html_e( 'Translations', 'polylang' );?></strong></p>
13
  <table><?php
14
  foreach ( $this->model->get_languages_list() as $language ) {
15
  if ( $language->term_id == $lang->term_id ) {
admin/view-translations-post.php CHANGED
@@ -8,7 +8,7 @@ if ( ! defined( 'ABSPATH' ) ) {
8
  exit; // don't access directly
9
  };
10
  ?>
11
- <p><strong><?php _e( 'Translations', 'polylang' ); ?></strong></p>
12
  <table><?php
13
  foreach ( $this->model->get_languages_list() as $language ) {
14
  if ( $language->term_id == $lang->term_id ) {
@@ -41,7 +41,8 @@ if ( ! defined( 'ABSPATH' ) ) {
41
  <input type="hidden" name="post_tr_lang[%1$s]" id="htr_lang_%1$s" value="%3$s" />
42
  <input type="text" class="tr_lang" id="tr_lang_%1$s" value="%4$s"%5$s />',
43
  esc_attr( $language->slug ),
44
- __( 'Translation', 'polylang' ),
 
45
  empty( $value ) ? 0 : esc_attr( $selected->ID ),
46
  empty( $value ) ? '' : esc_attr( $selected->post_title ),
47
  empty( $link ) ? ' disabled="disabled"' : ''
8
  exit; // don't access directly
9
  };
10
  ?>
11
+ <p><strong><?php esc_html_e( 'Translations', 'polylang' ); ?></strong></p>
12
  <table><?php
13
  foreach ( $this->model->get_languages_list() as $language ) {
14
  if ( $language->term_id == $lang->term_id ) {
41
  <input type="hidden" name="post_tr_lang[%1$s]" id="htr_lang_%1$s" value="%3$s" />
42
  <input type="text" class="tr_lang" id="tr_lang_%1$s" value="%4$s"%5$s />',
43
  esc_attr( $language->slug ),
44
+ /* translators: accessibility text */
45
+ esc_html__( 'Translation', 'polylang' ),
46
  empty( $value ) ? 0 : esc_attr( $selected->ID ),
47
  empty( $value ) ? '' : esc_attr( $selected->post_title ),
48
  empty( $link ) ? ' disabled="disabled"' : ''
admin/view-translations-term.php CHANGED
@@ -10,12 +10,12 @@ if ( ! defined( 'ABSPATH' ) ) {
10
 
11
  if ( isset( $term_id ) ) {
12
  // edit term form ?>
13
- <th scope="row"><?php _e( 'Translations', 'polylang' ); ?></th>
14
  <td><?php
15
  }
16
  else {
17
  // add term form ?>
18
- <p><?php _e( 'Translations', 'polylang' ); ?></p><?php
19
  }?>
20
  <table class="widefat term-translations" id="<?php echo isset( $term_id ) ? 'edit' : 'add'; ?>-term-translations"><?php
21
  foreach ( $this->model->get_languages_list() as $language ) {
@@ -60,7 +60,8 @@ else {
60
  <input type="hidden" class="htr_lang" name="term_tr_lang[%1$s]" id="htr_lang_%1$s" value="%3$s" />
61
  <input type="text" class="tr_lang" id="tr_lang_%1$s" value="%4$s"%5$s />',
62
  esc_attr( $language->slug ),
63
- __( 'Translation', 'polylang' ),
 
64
  empty( $translation ) ? 0 : esc_attr( $translation->term_id ),
65
  empty( $translation ) ? '' : esc_attr( $translation->name ),
66
  empty( $disabled ) ? '' : ' disabled="disabled"'
10
 
11
  if ( isset( $term_id ) ) {
12
  // edit term form ?>
13
+ <th scope="row"><?php esc_html_e( 'Translations', 'polylang' ); ?></th>
14
  <td><?php
15
  }
16
  else {
17
  // add term form ?>
18
+ <p><?php esc_html_e( 'Translations', 'polylang' ); ?></p><?php
19
  }?>
20
  <table class="widefat term-translations" id="<?php echo isset( $term_id ) ? 'edit' : 'add'; ?>-term-translations"><?php
21
  foreach ( $this->model->get_languages_list() as $language ) {
60
  <input type="hidden" class="htr_lang" name="term_tr_lang[%1$s]" id="htr_lang_%1$s" value="%3$s" />
61
  <input type="text" class="tr_lang" id="tr_lang_%1$s" value="%4$s"%5$s />',
62
  esc_attr( $language->slug ),
63
+ /* translators: accessibility text */
64
+ esc_html__( 'Translation', 'polylang' ),
65
  empty( $translation ) ? 0 : esc_attr( $translation->term_id ),
66
  empty( $translation ) ? '' : esc_attr( $translation->name ),
67
  empty( $disabled ) ? '' : ' disabled="disabled"'
css/admin.css CHANGED
@@ -169,6 +169,10 @@ td[class*='column-language_'] {
169
  width: 20px;
170
  }
171
 
 
 
 
 
172
  #post-translations td {
173
  padding: 2px;
174
  }
169
  width: 20px;
170
  }
171
 
172
+ #post-translations .tr_lang {
173
+ width: 100%;
174
+ }
175
+
176
  #post-translations td {
177
  padding: 2px;
178
  }
css/admin.min.css CHANGED
@@ -1 +1 @@
1
- #pll-licenses-table td,.translation label{vertical-align:top}#add-lang select{width:95%}.column-locale,.languages .column-slug{width:15%}.column-default_lang{width:5%}.column-count,.column-flag,.column-term_group{width:10%}.icon-default-lang:before{font-family:dashicons;content:"\f155"}.form-field input[type=radio]{width:auto;margin-right:2px}#pll-about-box p,#pll-recommended p{text-align:justify}#pll-about-box input{margin:0;padding:0;float:right}#string-translation .search-box{margin:16px 0 8px}.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 .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-edit-column,#post-translations .pll-language-column{width:20px}#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%}#add-term-translations .pll-language-column,#term-translations .pll-edit-column{width:20px}#edit-term-translations .pll-language-column{padding:15px 10px;font-weight:400}.pll_icon_tick:before{content:"\f147"}.pll_icon_add:before{content:"\f132"}.pll_icon_edit:before{content:"\f464"}[class^=pll_icon_]{font:20px/1 dashicons;vertical-align:middle}#wpadminbar #wp-admin-bar-languages .ab-item img{margin:0 8px 0 2px}#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon{float:none;top:4px}#wpadminbar #wp-admin-bar-languages .ab-icon:before{content:"\f326";top:1px}@media screen and (max-width:782px){#wpbody-content .pll-settings .pll-configure>td{padding:20px}#wpbody-content .pll-settings #cb{padding:20px 9px}.pll-inline-block{width:auto}.pll-settings-url-col{display:block;width:100%}#wpbody-content .pll-settings #pll-licenses-table td{display:block}.pll-configure .pll-deactivate-license{margin:10px 0 5px}.stringstranslations .column-context,.stringstranslations .column-name{display:none}.translation label{display:block;width:95%;padding-left:0}.translation input{width:95%}#edit-term-translations .pll-language-name,#select-add-term-language .pll-select-flag,#select-edit-term-language .pll-select-flag{display:none}#edit-term-translations{width:100%}#add-term-translations .pll-language-column{line-height:38px}#edit-term-translations td{padding:8px 10px}#edit-term-translations .pll-edit-column,#edit-term-translations .pll-language-column{width:20px}.term-translations .pll-edit-column,.term-translations .pll-language-column,.term-translations .pll-translation-column{display:table-cell}.term-translations .hidden{display:none}#wpadminbar #wp-admin-bar-languages{display:block}#wpadminbar #wp-admin-bar-languages>.ab-item{width:50px;text-align:center}#wpadminbar #wp-admin-bar-languages>.ab-item .ab-icon:before{font:32px/1 dashicons;top:-1px}#wpadminbar #wp-admin-bar-languages>.ab-item img{margin:19px 0}#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon{margin-right:6px;font-size:20px!important;line-height:20px!important}}
1
+ #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}}
frontend/choose-lang-content.php CHANGED
@@ -18,10 +18,10 @@ class PLL_Choose_Lang_Content extends PLL_Choose_lang {
18
 
19
  if ( ! did_action( 'pll_language_defined' ) ) {
20
  // set the languages from content
21
- add_action( 'wp', array( &$this, 'wp' ), 5 ); // priority 5 for post types and taxonomies registered in wp hook with default priority
22
 
23
  // if no language found, choose the preferred one
24
- add_filter( 'pll_get_current_language', array( &$this, 'pll_get_current_language' ) );
25
  }
26
  }
27
 
@@ -34,7 +34,7 @@ class PLL_Choose_Lang_Content extends PLL_Choose_lang {
34
  */
35
  protected function set_language( $curlang ) {
36
  parent::set_language( $curlang );
37
- remove_action( 'wp', array( &$this, 'wp' ), 5 ); // won't attempt to set the language a 2nd time
38
  }
39
 
40
  /**
18
 
19
  if ( ! did_action( 'pll_language_defined' ) ) {
20
  // set the languages from content
21
+ add_action( 'wp', array( $this, 'wp' ), 5 ); // priority 5 for post types and taxonomies registered in wp hook with default priority
22
 
23
  // if no language found, choose the preferred one
24
+ add_filter( 'pll_get_current_language', array( $this, 'pll_get_current_language' ) );
25
  }
26
  }
27
 
34
  */
35
  protected function set_language( $curlang ) {
36
  parent::set_language( $curlang );
37
+ remove_action( 'wp', array( $this, 'wp' ), 5 ); // won't attempt to set the language a 2nd time
38
  }
39
 
40
  /**
frontend/choose-lang-url.php CHANGED
@@ -22,7 +22,7 @@ class PLL_Choose_Lang_Url extends PLL_Choose_lang {
22
  $this->set_language_from_url();
23
  }
24
 
25
- add_action( 'request', array( &$this, 'request' ) );
26
  }
27
 
28
  /**
@@ -40,7 +40,7 @@ class PLL_Choose_Lang_Url extends PLL_Choose_lang {
40
  // home is resquested
41
  if ( $requested_host == $host && $requested_uri == $home_path && empty( $_SERVER['QUERY_STRING'] ) ) {
42
  $this->home_language();
43
- add_action( 'setup_theme', array( &$this, 'home_requested' ) );
44
  }
45
 
46
  // take care to post & page preview http://wordpress.org/support/topic/static-frontpage-url-parameter-url-language-information
22
  $this->set_language_from_url();
23
  }
24
 
25
+ add_action( 'request', array( $this, 'request' ) );
26
  }
27
 
28
  /**
40
  // home is resquested
41
  if ( $requested_host == $host && $requested_uri == $home_path && empty( $_SERVER['QUERY_STRING'] ) ) {
42
  $this->home_language();
43
+ add_action( 'setup_theme', array( $this, 'home_requested' ) );
44
  }
45
 
46
  // take care to post & page preview http://wordpress.org/support/topic/static-frontpage-url-parameter-url-language-information
frontend/choose-lang.php CHANGED
@@ -36,8 +36,8 @@ abstract class PLL_Choose_Lang {
36
  $this->set_language( empty( $_REQUEST['lang'] ) ? $this->get_preferred_language() : $this->model->get_language( $_REQUEST['lang'] ) );
37
  }
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
  /**
36
  $this->set_language( empty( $_REQUEST['lang'] ) ? $this->get_preferred_language() : $this->model->get_language( $_REQUEST['lang'] ) );
37
  }
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
  /**
frontend/frontend-auto-translate.php CHANGED
@@ -20,8 +20,8 @@ class PLL_Frontend_Auto_Translate {
20
  $this->model = &$polylang->model;
21
  $this->curlang = &$polylang->curlang;
22
 
23
- add_action( 'pre_get_posts', array( &$this, 'pre_get_posts' ) ); // after main Polylang filter
24
- add_filter( 'get_terms_args', array( &$this, 'get_terms_args' ), 10, 2 );
25
  }
26
 
27
  /**
@@ -80,7 +80,7 @@ class PLL_Frontend_Auto_Translate {
80
  $arr = array();
81
  if ( ! empty( $qv['category_name'] ) ) {
82
  foreach ( explode( ',', $qv['category_name'] ) as $slug ) {
83
- $arr[] = ( ( $cat = get_category_by_slug( $slug ) ) && ( $tr_id = $this->get_term( $cat->term_id ) ) && ! is_wp_error( $tr = get_category( $tr_id ) ) ) ? $tr->slug : $slug;
84
  }
85
 
86
  $qv['category_name'] = implode( ',', $arr );
@@ -102,7 +102,7 @@ class PLL_Frontend_Auto_Translate {
102
  if ( ! empty( $qv['tag'] ) ) {
103
  $sep = strpos( $qv['tag'], ',' ) !== false ? ',' : '+'; // two possible separators for tag slugs
104
  foreach ( explode( $sep, $qv['tag'] ) as $slug ) {
105
- $arr[] = ( ( $tag = get_term_by( 'slug', $slug, 'post_tag' ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_tag( $tr_id ) ) ) ? $tr->slug : $slug;
106
  }
107
 
108
  $qv['tag'] = implode( $sep, $arr );
@@ -118,7 +118,7 @@ class PLL_Frontend_Auto_Translate {
118
  $arr = array();
119
  if ( ! empty( $qv[ $key ] ) ) {
120
  foreach ( $qv[ $key ] as $slug ) {
121
- $arr[] = ( ( $tag = get_term_by( 'slug', $slug, 'post_tag' ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_tag( $tr_id ) ) ) ? $tr->slug : $slug;
122
  }
123
 
124
  $qv[ $key ] = $arr;
@@ -133,7 +133,7 @@ class PLL_Frontend_Auto_Translate {
133
  if ( ! empty( $qv[ $tax->query_var ] ) ) {
134
  $sep = strpos( $qv[ $tax->query_var ], ',' ) !== false ? ',' : '+'; // two possible separators
135
  foreach ( explode( $sep, $qv[ $tax->query_var ] ) as $slug ) {
136
- $arr[] = ( ( $tag = get_term_by( 'slug', $slug, $taxonomy ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_term( $tr_id, $taxonomy ) ) ) ? $tr->slug : $slug;
137
  }
138
 
139
  $qv[ $tax->query_var ] = implode( $sep, $arr );
@@ -219,7 +219,7 @@ class PLL_Frontend_Auto_Translate {
219
  $arr = array();
220
  $field = isset( $q['field'] ) && in_array( $q['field'], array( 'slug', 'name' ) ) ? $q['field'] : 'term_id';
221
  foreach ( (array) $q['terms'] as $t ) {
222
- $arr[] = ( ( $tag = get_term_by( $field, $t, $q['taxonomy'] ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_term( $tr_id, $q['taxonomy'] ) ) ) ? $tr->$field : $t;
223
  }
224
 
225
  $tax_queries[ $key ]['terms'] = $arr;
20
  $this->model = &$polylang->model;
21
  $this->curlang = &$polylang->curlang;
22
 
23
+ add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) ); // after main Polylang filter
24
+ add_filter( 'get_terms_args', array( $this, 'get_terms_args' ), 10, 2 );
25
  }
26
 
27
  /**
80
  $arr = array();
81
  if ( ! empty( $qv['category_name'] ) ) {
82
  foreach ( explode( ',', $qv['category_name'] ) as $slug ) {
83
+ $arr[] = ( ( $cat = wpcom_vip_get_category_by_slug( $slug ) ) && ( $tr_id = $this->get_term( $cat->term_id ) ) && ! is_wp_error( $tr = get_category( $tr_id ) ) ) ? $tr->slug : $slug;
84
  }
85
 
86
  $qv['category_name'] = implode( ',', $arr );
102
  if ( ! empty( $qv['tag'] ) ) {
103
  $sep = strpos( $qv['tag'], ',' ) !== false ? ',' : '+'; // two possible separators for tag slugs
104
  foreach ( explode( $sep, $qv['tag'] ) as $slug ) {
105
+ $arr[] = ( ( $tag = wpcom_vip_get_term_by( 'slug', $slug, 'post_tag' ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_tag( $tr_id ) ) ) ? $tr->slug : $slug;
106
  }
107
 
108
  $qv['tag'] = implode( $sep, $arr );
118
  $arr = array();
119
  if ( ! empty( $qv[ $key ] ) ) {
120
  foreach ( $qv[ $key ] as $slug ) {
121
+ $arr[] = ( ( $tag = wpcom_vip_get_term_by( 'slug', $slug, 'post_tag' ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_tag( $tr_id ) ) ) ? $tr->slug : $slug;
122
  }
123
 
124
  $qv[ $key ] = $arr;
133
  if ( ! empty( $qv[ $tax->query_var ] ) ) {
134
  $sep = strpos( $qv[ $tax->query_var ], ',' ) !== false ? ',' : '+'; // two possible separators
135
  foreach ( explode( $sep, $qv[ $tax->query_var ] ) as $slug ) {
136
+ $arr[] = ( ( $tag = wpcom_vip_get_term_by( 'slug', $slug, $taxonomy ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_term( $tr_id, $taxonomy ) ) ) ? $tr->slug : $slug;
137
  }
138
 
139
  $qv[ $tax->query_var ] = implode( $sep, $arr );
219
  $arr = array();
220
  $field = isset( $q['field'] ) && in_array( $q['field'], array( 'slug', 'name' ) ) ? $q['field'] : 'term_id';
221
  foreach ( (array) $q['terms'] as $t ) {
222
+ $arr[] = ( ( $tag = wpcom_vip_get_term_by( $field, $t, $q['taxonomy'] ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_term( $tr_id, $q['taxonomy'] ) ) ) ? $tr->$field : $t;
223
  }
224
 
225
  $tax_queries[ $key ]['terms'] = $arr;
frontend/frontend-filters-links.php CHANGED
@@ -26,31 +26,31 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
26
 
27
  // rewrites author and date links to filter them by language
28
  foreach ( array( 'feed_link', 'author_link', 'search_link', 'year_link', 'month_link', 'day_link' ) as $filter ) {
29
- add_filter( $filter, array( &$this, 'archive_link' ), 20 );
30
  }
31
 
32
  // rewrites post types archives links to filter them by language
33
- add_filter( 'post_type_archive_link', array( &$this, 'post_type_archive_link' ), 20, 2 );
34
 
35
  // meta in the html head section
36
- add_action( 'wp_head', array( &$this, 'wp_head' ) );
37
 
38
  // modifies the home url
39
  if ( ! defined( 'PLL_FILTER_HOME_URL' ) || PLL_FILTER_HOME_URL ) {
40
- add_filter( 'home_url', array( &$this, 'home_url' ), 10, 2 );
41
  }
42
 
43
  if ( $this->options['force_lang'] > 1 ) {
44
  // rewrites next and previous post links when not automatically done by WordPress
45
- add_filter( 'get_pagenum_link', array( &$this, 'archive_link' ), 20 );
46
 
47
  // rewrites ajax url
48
- add_filter( 'admin_url', array( &$this, 'admin_url' ), 10, 2 );
49
  }
50
 
51
  // redirects to canonical url before WordPress redirect_canonical
52
  // but after Nextgen Gallery which hacks $_SERVER['REQUEST_URI'] !!! and restores it in 'template_redirect' with priority 1
53
- add_action( 'template_redirect', array( &$this, 'check_canonical_url' ), 4 );
54
  }
55
 
56
  /**
@@ -76,7 +76,7 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
76
  * @return string modified link
77
  */
78
  public function post_type_archive_link( $link, $post_type ) {
79
- return $this->model->is_translated_post_type( $post_type ) ? $this->links_model->add_language_to_link( $link, $this->curlang ) : $link;
80
  }
81
 
82
  /**
@@ -258,6 +258,7 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
258
  array( 'file' => $theme_root ),
259
  array( 'function' => 'wp_nav_menu' ),
260
  array( 'function' => 'login_footer' ),
 
261
  ) );
262
  }
263
 
@@ -366,7 +367,7 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
366
  $language = $this->model->post->get_language( (int) $obj->ID );
367
  }
368
 
369
- elseif ( is_404() && $id = get_query_var('p') ) {
370
  // special case for page shortlinks when using subdomains or multiple domains
371
  // needed because redirect_canonical doesn't accept to change the domain name
372
  $language = $this->model->post->get_language( (int) $id );
26
 
27
  // rewrites author and date links to filter them by language
28
  foreach ( array( 'feed_link', 'author_link', 'search_link', 'year_link', 'month_link', 'day_link' ) as $filter ) {
29
+ add_filter( $filter, array( $this, 'archive_link' ), 20 );
30
  }
31
 
32
  // rewrites post types archives links to filter them by language
33
+ add_filter( 'post_type_archive_link', array( $this, 'post_type_archive_link' ), 20, 2 );
34
 
35
  // meta in the html head section
36
+ add_action( 'wp_head', array( $this, 'wp_head' ) );
37
 
38
  // modifies the home url
39
  if ( ! defined( 'PLL_FILTER_HOME_URL' ) || PLL_FILTER_HOME_URL ) {
40
+ add_filter( 'home_url', array( $this, 'home_url' ), 10, 2 );
41
  }
42
 
43
  if ( $this->options['force_lang'] > 1 ) {
44
  // rewrites next and previous post links when not automatically done by WordPress
45
+ add_filter( 'get_pagenum_link', array( $this, 'archive_link' ), 20 );
46
 
47
  // rewrites ajax url
48
+ add_filter( 'admin_url', array( $this, 'admin_url' ), 10, 2 );
49
  }
50
 
51
  // redirects to canonical url before WordPress redirect_canonical
52
  // but after Nextgen Gallery which hacks $_SERVER['REQUEST_URI'] !!! and restores it in 'template_redirect' with priority 1
53
+ add_action( 'template_redirect', array( $this, 'check_canonical_url' ), 4 );
54
  }
55
 
56
  /**
76
  * @return string modified link
77
  */
78
  public function post_type_archive_link( $link, $post_type ) {
79
+ return $this->model->is_translated_post_type( $post_type ) && 'post' !== $post_type ? $this->links_model->add_language_to_link( $link, $this->curlang ) : $link;
80
  }
81
 
82
  /**
258
  array( 'file' => $theme_root ),
259
  array( 'function' => 'wp_nav_menu' ),
260
  array( 'function' => 'login_footer' ),
261
+ array( 'function' => 'get_custom_logo' ),
262
  ) );
263
  }
264
 
367
  $language = $this->model->post->get_language( (int) $obj->ID );
368
  }
369
 
370
+ elseif ( is_404() && ! empty( $wp_query->query['page_id'] ) && $id = get_query_var( 'page_id' ) ) {
371
  // special case for page shortlinks when using subdomains or multiple domains
372
  // needed because redirect_canonical doesn't accept to change the domain name
373
  $language = $this->model->post->get_language( (int) $id );
frontend/frontend-filters-search.php CHANGED
@@ -21,15 +21,15 @@ class PLL_Frontend_Filters_Search {
21
 
22
  // adds the language information in the search form
23
  // low priority in case the search form is created using the same filter as described in http://codex.wordpress.org/Function_Reference/get_search_form
24
- add_filter( 'get_search_form', array( &$this, 'get_search_form' ), 99 );
25
 
26
  // adds the language information in admin bar search form
27
- add_action( 'add_admin_bar_menus', array( &$this, 'add_admin_bar_menus' ) );
28
 
29
  // adds javascript at the end of the document
30
  // was used for WP < 3.6. kept just in case
31
  if ( defined( 'PLL_SEARCH_FORM_JS' ) && PLL_SEARCH_FORM_JS ) {
32
- add_action( 'wp_footer', array( &$this, 'wp_print_footer_scripts' ) );
33
  }
34
  }
35
 
@@ -66,7 +66,7 @@ class PLL_Frontend_Filters_Search {
66
  */
67
  function add_admin_bar_menus() {
68
  remove_action( 'admin_bar_menu', 'wp_admin_bar_search_menu', 4 );
69
- add_action( 'admin_bar_menu', array( &$this, 'admin_bar_search_menu' ), 4 );
70
  }
71
 
72
  /**
@@ -80,8 +80,8 @@ class PLL_Frontend_Filters_Search {
80
  public function admin_bar_search_menu( $wp_admin_bar ) {
81
  $form = '<form action="' . esc_url( home_url( '/' ) ) . '" method="get" id="adminbarsearch">';
82
  $form .= '<input class="adminbar-input" name="s" id="adminbar-search" type="text" value="" maxlength="150" />';
83
- $form .= '<label for="adminbar-search" class="screen-reader-text">' . __( 'Search' ) . '</label>';
84
- $form .= '<input type="submit" class="adminbar-button" value="' . __( 'Search' ) . '"/>';
85
  $form .= '</form>';
86
 
87
  $wp_admin_bar->add_menu( array(
21
 
22
  // adds the language information in the search form
23
  // low priority in case the search form is created using the same filter as described in http://codex.wordpress.org/Function_Reference/get_search_form
24
+ add_filter( 'get_search_form', array( $this, 'get_search_form' ), 99 );
25
 
26
  // adds the language information in admin bar search form
27
+ add_action( 'add_admin_bar_menus', array( $this, 'add_admin_bar_menus' ) );
28
 
29
  // adds javascript at the end of the document
30
  // was used for WP < 3.6. kept just in case
31
  if ( defined( 'PLL_SEARCH_FORM_JS' ) && PLL_SEARCH_FORM_JS ) {
32
+ add_action( 'wp_footer', array( $this, 'wp_print_footer_scripts' ) );
33
  }
34
  }
35
 
66
  */
67
  function add_admin_bar_menus() {
68
  remove_action( 'admin_bar_menu', 'wp_admin_bar_search_menu', 4 );
69
+ add_action( 'admin_bar_menu', array( $this, 'admin_bar_search_menu' ), 4 );
70
  }
71
 
72
  /**
80
  public function admin_bar_search_menu( $wp_admin_bar ) {
81
  $form = '<form action="' . esc_url( home_url( '/' ) ) . '" method="get" id="adminbarsearch">';
82
  $form .= '<input class="adminbar-input" name="s" id="adminbar-search" type="text" value="" maxlength="150" />';
83
+ $form .= '<label for="adminbar-search" class="screen-reader-text">' . esc_html__( 'Search' ) . '</label>';
84
+ $form .= '<input type="submit" class="adminbar-button" value="' . esc_attr__( 'Search' ) . '"/>';
85
  $form .= '</form>';
86
 
87
  $wp_admin_bar->add_menu( array(
frontend/frontend-filters.php CHANGED
@@ -18,27 +18,27 @@ class PLL_Frontend_Filters extends PLL_Filters{
18
  parent::__construct( $polylang );
19
 
20
  // Filters the WordPress locale
21
- add_filter( 'locale', array( &$this, 'get_locale' ) );
22
 
23
  // Filter sticky posts by current language
24
- add_filter( 'option_sticky_posts', array( &$this, 'option_sticky_posts' ) );
25
 
26
  // Adds cache domain when querying terms
27
- add_filter( 'get_terms_args', array( &$this, 'get_terms_args' ) );
28
 
29
  // Filters categories and post tags by language
30
- add_filter( 'terms_clauses', array( &$this, 'terms_clauses' ), 10, 3 );
31
 
32
  // Rewrites archives, next and previous post links to filter them by language
33
- add_filter( 'getarchives_join', array( &$this, 'getarchives_join' ), 10, 2 );
34
- add_filter( 'getarchives_where', array( &$this, 'getarchives_where' ), 10, 2 );
35
- add_filter( 'get_previous_post_join', array( &$this, 'posts_join' ), 10, 5 );
36
- add_filter( 'get_next_post_join', array( &$this, 'posts_join' ), 10, 5 );
37
- add_filter( 'get_previous_post_where', array( &$this, 'posts_where' ), 10, 5 );
38
- add_filter( 'get_next_post_where', array( &$this, 'posts_where' ), 10, 5 );
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 ) {
@@ -46,15 +46,15 @@ class PLL_Frontend_Filters extends PLL_Filters{
46
  }
47
 
48
  // Translates biography
49
- add_filter( 'get_user_metadata', array( &$this, 'get_user_metadata' ), 10, 4 );
50
 
51
  // Set posts and terms language when created from frontend ( ex with P2 theme )
52
- add_action( 'save_post', array( &$this, 'save_post' ), 200, 2 );
53
- add_action( 'create_term', array( &$this, 'save_term' ), 10, 3 );
54
- add_action( 'edit_term', array( &$this, 'save_term' ), 10, 3 );
55
 
56
  if ( $this->options['media_support'] ) {
57
- add_action( 'add_attachment', array( &$this, 'set_default_language' ) );
58
  }
59
 
60
  // Support theme customizer
@@ -174,7 +174,7 @@ class PLL_Frontend_Filters extends PLL_Filters{
174
  */
175
  public function posts_join( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) {
176
  // FIXME empty( $post ) for backward compatibility with WP < 4.4
177
- return empty( $post ) || $this->model->is_translated_post_type( $post->post_type ) ? $sql . $this->model->post->join_clause() : $sql;
178
  }
179
 
180
  /**
18
  parent::__construct( $polylang );
19
 
20
  // Filters the WordPress locale
21
+ add_filter( 'locale', array( $this, 'get_locale' ) );
22
 
23
  // Filter sticky posts by current language
24
+ add_filter( 'option_sticky_posts', array( $this, 'option_sticky_posts' ) );
25
 
26
  // Adds cache domain when querying terms
27
+ add_filter( 'get_terms_args', array( $this, 'get_terms_args' ) );
28
 
29
  // Filters categories and post tags by language
30
+ add_filter( 'terms_clauses', array( $this, 'terms_clauses' ), 10, 3 );
31
 
32
  // Rewrites archives, next and previous post links to filter them by language
33
+ add_filter( 'getarchives_join', array( $this, 'getarchives_join' ), 10, 2 );
34
+ add_filter( 'getarchives_where', array( $this, 'getarchives_where' ), 10, 2 );
35
+ add_filter( 'get_previous_post_join', array( $this, 'posts_join' ), 10, 5 );
36
+ add_filter( 'get_next_post_join', array( $this, 'posts_join' ), 10, 5 );
37
+ add_filter( 'get_previous_post_where', array( $this, 'posts_where' ), 10, 5 );
38
+ add_filter( 'get_next_post_where', array( $this, 'posts_where' ), 10, 5 );
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 ) {
46
  }
47
 
48
  // Translates biography
49
+ add_filter( 'get_user_metadata', array( $this, 'get_user_metadata' ), 10, 4 );
50
 
51
  // Set posts and terms language when created from frontend ( ex with P2 theme )
52
+ add_action( 'save_post', array( $this, 'save_post' ), 200, 2 );
53
+ add_action( 'create_term', array( $this, 'save_term' ), 10, 3 );
54
+ add_action( 'edit_term', array( $this, 'save_term' ), 10, 3 );
55
 
56
  if ( $this->options['media_support'] ) {
57
+ add_action( 'add_attachment', array( $this, 'set_default_language' ) );
58
  }
59
 
60
  // Support theme customizer
174
  */
175
  public function posts_join( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) {
176
  // FIXME empty( $post ) for backward compatibility with WP < 4.4
177
+ return empty( $post ) || $this->model->is_translated_post_type( $post->post_type ) ? $sql . $this->model->post->join_clause( 'p' ) : $sql;
178
  }
179
 
180
  /**
frontend/frontend-links.php CHANGED
@@ -96,14 +96,14 @@ class PLL_Frontend_Links extends PLL_Links {
96
  $lang = $this->model->term->get_language( $term->term_id );
97
 
98
  if ( ! $lang || $language->slug == $lang->slug ) {
99
- $url = get_term_link( $term, $term->taxonomy ); // self link
100
  }
101
 
102
  elseif ( $tr_id = $this->model->term->get_translation( $term->term_id, $language ) ) {
103
  $tr_term = get_term( $tr_id, $term->taxonomy );
104
  // check if translated term ( or children ) have posts
105
  if ( $tr_term && ( $tr_term->count || ( is_taxonomy_hierarchical( $term->taxonomy ) && array_sum( wp_list_pluck( get_terms( $term->taxonomy, array( 'child_of' => $tr_term->term_id, 'lang' => $language->slug ) ), 'count' ) ) ) ) ) {
106
- $url = get_term_link( $tr_term, $term->taxonomy );
107
  }
108
  }
109
  }
96
  $lang = $this->model->term->get_language( $term->term_id );
97
 
98
  if ( ! $lang || $language->slug == $lang->slug ) {
99
+ $url = wpcom_vip_get_term_link( $term, $term->taxonomy ); // self link
100
  }
101
 
102
  elseif ( $tr_id = $this->model->term->get_translation( $term->term_id, $language ) ) {
103
  $tr_term = get_term( $tr_id, $term->taxonomy );
104
  // check if translated term ( or children ) have posts
105
  if ( $tr_term && ( $tr_term->count || ( is_taxonomy_hierarchical( $term->taxonomy ) && array_sum( wp_list_pluck( get_terms( $term->taxonomy, array( 'child_of' => $tr_term->term_id, 'lang' => $language->slug ) ), 'count' ) ) ) ) ) {
106
+ $url = wpcom_vip_get_term_link( $tr_term, $term->taxonomy );
107
  }
108
  }
109
  }
frontend/frontend-nav-menu.php CHANGED
@@ -19,18 +19,18 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
19
  $this->curlang = &$polylang->curlang;
20
 
21
  // Split the language switcher menu item in several language menu items
22
- add_filter( 'wp_get_nav_menu_items', array( &$this, 'wp_get_nav_menu_items' ), 20 ); // after the customizer menus
23
- add_filter( 'wp_nav_menu_objects', array( &$this, 'wp_nav_menu_objects' ) );
24
- add_filter( 'nav_menu_link_attributes', array( &$this, 'nav_menu_link_attributes' ), 10, 3 );
25
 
26
  // Filters menus by language
27
  add_filter( 'theme_mod_nav_menu_locations', array( $this, 'nav_menu_locations' ), 20 );
28
- add_filter( 'wp_nav_menu_args', array( &$this, 'wp_nav_menu_args' ) );
29
 
30
  // The customizer
31
  if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) {
32
- add_filter( 'wp_nav_menu_args', array( &$this, 'filter_args_before_customizer' ) );
33
- add_filter( 'wp_nav_menu_args', array( &$this, 'filter_args_after_customizer' ), 2000 );
34
  }
35
  }
36
 
@@ -75,6 +75,15 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
75
  $args = array_merge( array( 'raw' => 1 ), $options );
76
  $the_languages = $switcher->the_languages( PLL()->links, $args );
77
 
 
 
 
 
 
 
 
 
 
78
  foreach ( $the_languages as $lang ) {
79
  $lang_item = clone $item;
80
  $lang_item->ID = $lang_item->ID . '-' . $lang['slug']; // A unique ID
@@ -83,6 +92,10 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
83
  $lang_item->lang = $lang['locale']; // Save this for use in nav_menu_link_attributes
84
  $lang_item->classes = $lang['classes'];
85
  $lang_item->menu_order += $offset + $i++;
 
 
 
 
86
  $new_items[] = $lang_item;
87
  }
88
  $offset += $i - 1;
@@ -91,7 +104,6 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
91
  $new_items[] = $item;
92
  }
93
  }
94
-
95
  return $new_items;
96
  }
97
 
@@ -156,7 +168,7 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
156
  */
157
  public function nav_menu_link_attributes( $atts, $item, $args ) {
158
  if ( isset( $item->lang ) ) {
159
- $atts['hreflang'] = esc_attr( $item->lang );
160
  }
161
  return $atts;
162
  }
19
  $this->curlang = &$polylang->curlang;
20
 
21
  // Split the language switcher menu item in several language menu items
22
+ add_filter( 'wp_get_nav_menu_items', array( $this, 'wp_get_nav_menu_items' ), 20 ); // after the customizer menus
23
+ add_filter( 'wp_nav_menu_objects', array( $this, 'wp_nav_menu_objects' ) );
24
+ add_filter( 'nav_menu_link_attributes', array( $this, 'nav_menu_link_attributes' ), 10, 3 );
25
 
26
  // Filters menus by language
27
  add_filter( 'theme_mod_nav_menu_locations', array( $this, 'nav_menu_locations' ), 20 );
28
+ add_filter( 'wp_nav_menu_args', array( $this, 'wp_nav_menu_args' ) );
29
 
30
  // The customizer
31
  if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) {
32
+ add_filter( 'wp_nav_menu_args', array( $this, 'filter_args_before_customizer' ) );
33
+ add_filter( 'wp_nav_menu_args', array( $this, 'filter_args_after_customizer' ), 2000 );
34
  }
35
  }
36
 
75
  $args = array_merge( array( 'raw' => 1 ), $options );
76
  $the_languages = $switcher->the_languages( PLL()->links, $args );
77
 
78
+ // parent item for dropdown
79
+ if ( ! empty( $options['dropdown'] ) ) {
80
+ $item->title = $options['show_flags'] && $options['show_names'] ? $this->curlang->flag . '&nbsp;' . esc_html( $this->curlang->name ) : ( $options['show_flags'] ? $this->curlang->flag : esc_html( $this->curlang->name ) );
81
+ $item->url = '';
82
+ $item->classes = array( 'pll-parent-menu-item' );
83
+ $new_items[] = $item;
84
+ $offset++;
85
+ }
86
+
87
  foreach ( $the_languages as $lang ) {
88
  $lang_item = clone $item;
89
  $lang_item->ID = $lang_item->ID . '-' . $lang['slug']; // A unique ID
92
  $lang_item->lang = $lang['locale']; // Save this for use in nav_menu_link_attributes
93
  $lang_item->classes = $lang['classes'];
94
  $lang_item->menu_order += $offset + $i++;
95
+ if ( ! empty( $options['dropdown'] ) ) {
96
+ $lang_item->menu_item_parent = $item->db_id;
97
+ $lang_item->db_id = 0; // to avoid recursion
98
+ }
99
  $new_items[] = $lang_item;
100
  }
101
  $offset += $i - 1;
104
  $new_items[] = $item;
105
  }
106
  }
 
107
  return $new_items;
108
  }
109
 
168
  */
169
  public function nav_menu_link_attributes( $atts, $item, $args ) {
170
  if ( isset( $item->lang ) ) {
171
+ $atts['lang'] = $atts['hreflang'] = esc_attr( $item->lang );
172
  }
173
  return $atts;
174
  }
frontend/frontend-static-pages.php CHANGED
@@ -1,14 +1,14 @@
1
  <?php
2
 
3
  /**
4
- * manages the static front page and the page for posts on frontend
5
  *
6
  * @since 1.8
7
  */
8
  class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
9
 
10
  /**
11
- * constructor: setups filters and actions
12
  *
13
  * @since 1.8
14
  *
@@ -21,36 +21,33 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
21
  $this->links = &$polylang->links;
22
  $this->curlang = &$polylang->curlang;
23
 
24
- add_action( 'pll_language_defined', array( &$this, 'pll_language_defined' ) );
25
- add_action( 'pll_home_requested', array( &$this, 'pll_home_requested' ) );
26
 
27
- // modifies the page link in case the front page is not in the default language
28
- add_filter( 'page_link', array( &$this, 'page_link' ), 20, 2 );
29
 
30
- // manages the redirection of the homepage
31
- add_filter( 'redirect_canonical', array( &$this, 'redirect_canonical' ), 10, 2 );
32
 
33
- add_filter( 'pll_pre_translation_url', array( &$this, 'pll_pre_translation_url' ), 10, 3 );
34
- add_filter( 'pll_check_canonical_url', array( &$this, 'pll_check_canonical_url' ) );
35
-
36
- add_filter( 'pll_set_language_from_query', array( &$this, 'page_on_front_query' ), 10, 2 );
37
- add_filter( 'pll_set_language_from_query', array( &$this, 'page_for_posts_query' ), 10, 2 );
38
  }
39
 
40
  /**
41
- * init the filters
42
  *
43
  * @since 1.8
44
  */
45
  public function pll_language_defined() {
46
- // translates our page on front and page for posts properties
47
  $this->init();
48
 
49
- // translates page for posts and page on front
50
- add_filter( 'option_page_on_front', array( &$this, 'translate_page_on_front' ) );
51
- add_filter( 'option_page_for_posts', array( &$this, 'translate_page_for_posts' ) );
52
 
53
- // support theme customizer
54
  if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) {
55
  add_filter( 'pre_option_page_on_front', 'pll_get_post', 20 );
56
  add_filter( 'pre_option_page_for_post', 'pll_get_post', 20 );
@@ -58,7 +55,7 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
58
  }
59
 
60
  /**
61
- * translates the page_id query var when the site root page is requested
62
  *
63
  * @since 1.8
64
  */
@@ -67,7 +64,7 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
67
  }
68
 
69
  /**
70
- * translates page on front
71
  *
72
  * @since 1.8
73
  *
@@ -80,7 +77,7 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
80
  }
81
 
82
  /**
83
- * translates page for posts
84
  *
85
  * @since 1.8
86
  *
@@ -88,28 +85,12 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
88
  * @return int
89
  */
90
  public function translate_page_for_posts( $v ) {
91
- // returns the current page if there is no translation to avoid ugly notices
92
  return isset( $this->curlang->page_for_posts ) ? $this->curlang->page_for_posts : $v;
93
  }
94
 
95
  /**
96
- * modifies the page link in case the front page is not in the default language
97
- *
98
- * @since 0.7.2
99
- *
100
- * @param string $link
101
- * @param int $id
102
- * @return string modified link
103
- */
104
- public function page_link( $link, $id ) {
105
- if ( ( $lang = $this->model->post->get_language( $id ) ) && $id == $lang->page_on_front ) {
106
- return $lang->home_url;
107
- }
108
- return $link;
109
- }
110
-
111
- /**
112
- * manages canonical redirection of the homepage when using page on front
113
  *
114
  * @since 0.1
115
  *
@@ -122,7 +103,7 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
122
  if ( is_page() && ! is_feed() && isset( $wp_query->queried_object ) && $wp_query->queried_object->ID == $this->curlang->page_on_front ) {
123
  $url = is_paged() ? $this->links_model->add_paged_to_link( $this->links->get_home_url(), $wp_query->query_vars['page'] ) : $this->links->get_home_url();
124
 
125
- // don't forget additional query vars
126
  $query = parse_url( $redirect_url, PHP_URL_QUERY );
127
  if ( ! empty( $query ) ) {
128
  parse_str( $query, $query_vars );
@@ -137,23 +118,23 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
137
  }
138
 
139
  /**
140
- * translates the url of the page on front and page for posts
141
  *
142
  * @since 1.8
143
  *
144
- * @param string $url not used
145
- * @param object $language
146
- * @param int $queried_object_id
147
  * @return string
148
  */
149
  public function pll_pre_translation_url( $url, $language, $queried_object_id ) {
150
  if ( ! empty( $queried_object_id ) ) {
151
- // page for posts
152
  if ( $GLOBALS['wp_query']->is_posts_page && ( $id = $this->model->post->get( $queried_object_id, $language ) ) ) {
153
  $url = get_permalink( $id );
154
  }
155
 
156
- // page on front
157
  elseif ( is_front_page() && $language->page_on_front && ( $language->page_on_front == $this->model->post->get( $queried_object_id, $language ) ) ) {
158
  $url = $language->home_url;
159
  }
@@ -163,7 +144,7 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
163
  }
164
 
165
  /**
166
- * prevents canonical redirection if we are on a static front page
167
  *
168
  * @since 1.8
169
  *
@@ -171,16 +152,16 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
171
  * @return bool|string
172
  */
173
  public function pll_check_canonical_url( $redirect_url ) {
174
- return $this->options['redirect_lang'] && isset( $this->curlang->page_on_front ) && is_page( $this->curlang->page_on_front ) ? false : $redirect_url;
175
  }
176
 
177
  /**
178
- * setups query vars when requesting a static front page
179
  *
180
  * @since 1.8
181
  *
182
  * @param bool|object $lang
183
- * @param object $query
184
  * @return bool|object
185
  */
186
  public function page_on_front_query( $lang, $query ) {
@@ -188,21 +169,21 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
188
  return $lang;
189
  }
190
 
191
- // the home page is requested
192
  if ( did_action( 'home_requested' ) ) {
193
  $query->set( 'page_id', $lang->page_on_front );
194
  }
195
 
196
- // redirect the language page to the homepage when using a static front page
197
  elseif ( ( $this->options['redirect_lang'] || $this->options['hide_default'] ) && ( count( $query->query ) == 1 || ( is_paged() && count( $query->query ) == 2 ) ) && is_tax( 'language' ) ) {
198
  $lang = $this->model->get_language( get_query_var( 'lang' ) );
199
  $query->set( 'page_id', $lang->page_on_front );
200
  $query->is_singular = $query->is_page = true;
201
  $query->is_archive = $query->is_tax = false;
202
- unset( $query->query_vars['lang'], $query->queried_object ); // reset queried object
203
  }
204
 
205
- // set the language when requesting a static front page
206
  else {
207
  $page_id = $this->get_page_id( $query );
208
  $languages = $this->model->get_languages_list();
@@ -213,7 +194,7 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
213
  }
214
  }
215
 
216
- // correct <!--nextpage--> for page_on_front
217
  if ( ! empty( $lang ) ) {
218
  $query->set( 'page', $query->query_vars['paged'] );
219
  unset( $query->query_vars['paged'] );
@@ -223,12 +204,12 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
223
  }
224
 
225
  /**
226
- * setups query vars when requesting a posts page
227
  *
228
  * @since 1.8
229
  *
230
  * @param bool|object $lang
231
- * @param object $query
232
  * @return bool|object
233
  */
234
  public function page_for_posts_query( $lang, $query ) {
@@ -236,8 +217,8 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
236
  $page_id = $this->get_page_id( $query );
237
 
238
  if ( ! empty( $page_id ) && in_array( $page_id, $pages = $this->model->get_languages_list( array( 'fields' => 'page_for_posts' ) ) ) ) {
239
- // fill the cache with all pages for posts to avoid one query per page later
240
- // the posts_per_page limit is a trick to avoid splitting the query
241
  get_posts( array( 'posts_per_page' => 999, 'post_type' => 'page', 'post__in' => $pages, 'lang' => '' ) );
242
 
243
  $lang = $this->model->post->get_language( $page_id );
@@ -249,7 +230,7 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
249
  }
250
 
251
  /**
252
- * get queried page_id ( if exists )
253
  * If permalinks are used, WordPress does set and use $query->queried_object_id and sets $query->query_vars['page_id'] to 0
254
  * and does set and use $query->query_vars['page_id'] if permalinks are not used :(
255
  *
@@ -267,6 +248,6 @@ class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
267
  return $query->query_vars['page_id'];
268
  }
269
 
270
- return 0; // no page queried
271
  }
272
  }
1
  <?php
2
 
3
  /**
4
+ * Manages the static front page and the page for posts on frontend
5
  *
6
  * @since 1.8
7
  */
8
  class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
9
 
10
  /**
11
+ * Constructor: setups filters and actions
12
  *
13
  * @since 1.8
14
  *
21
  $this->links = &$polylang->links;
22
  $this->curlang = &$polylang->curlang;
23
 
24
+ add_action( 'pll_language_defined', array( $this, 'pll_language_defined' ) );
25
+ add_action( 'pll_home_requested', array( $this, 'pll_home_requested' ) );
26
 
27
+ // Manages the redirection of the homepage
28
+ add_filter( 'redirect_canonical', array( $this, 'redirect_canonical' ), 10, 2 );
29
 
30
+ add_filter( 'pll_pre_translation_url', array( $this, 'pll_pre_translation_url' ), 10, 3 );
31
+ add_filter( 'pll_check_canonical_url', array( $this, 'pll_check_canonical_url' ) );
32
 
33
+ add_filter( 'pll_set_language_from_query', array( $this, 'page_on_front_query' ), 10, 2 );
34
+ add_filter( 'pll_set_language_from_query', array( $this, 'page_for_posts_query' ), 10, 2 );
 
 
 
35
  }
36
 
37
  /**
38
+ * Init the filters
39
  *
40
  * @since 1.8
41
  */
42
  public function pll_language_defined() {
43
+ // Translates our page on front and page for posts properties
44
  $this->init();
45
 
46
+ // Translates page for posts and page on front
47
+ add_filter( 'option_page_on_front', array( $this, 'translate_page_on_front' ) );
48
+ add_filter( 'option_page_for_posts', array( $this, 'translate_page_for_posts' ) );
49
 
50
+ // Support theme customizer
51
  if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) {
52
  add_filter( 'pre_option_page_on_front', 'pll_get_post', 20 );
53
  add_filter( 'pre_option_page_for_post', 'pll_get_post', 20 );
55
  }
56
 
57
  /**
58
+ * Translates the page_id query var when the site root page is requested
59
  *
60
  * @since 1.8
61
  */
64
  }
65
 
66
  /**
67
+ * Translates page on front
68
  *
69
  * @since 1.8
70
  *
77
  }
78
 
79
  /**
80
+ * Translates page for posts
81
  *
82
  * @since 1.8
83
  *
85
  * @return int
86
  */
87
  public function translate_page_for_posts( $v ) {
88
+ // Returns the current page if there is no translation to avoid ugly notices
89
  return isset( $this->curlang->page_for_posts ) ? $this->curlang->page_for_posts : $v;
90
  }
91
 
92
  /**
93
+ * Manages canonical redirection of the homepage when using page on front
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  *
95
  * @since 0.1
96
  *
103
  if ( is_page() && ! is_feed() && isset( $wp_query->queried_object ) && $wp_query->queried_object->ID == $this->curlang->page_on_front ) {
104
  $url = is_paged() ? $this->links_model->add_paged_to_link( $this->links->get_home_url(), $wp_query->query_vars['page'] ) : $this->links->get_home_url();
105
 
106
+ // Don't forget additional query vars
107
  $query = parse_url( $redirect_url, PHP_URL_QUERY );
108
  if ( ! empty( $query ) ) {
109
  parse_str( $query, $query_vars );
118
  }
119
 
120
  /**
121
+ * Translates the url of the page on front and page for posts
122
  *
123
  * @since 1.8
124
  *
125
+ * @param string $url not used
126
+ * @param object $language language in which we want the translation
127
+ * @param int $queried_object_id id of the queried object
128
  * @return string
129
  */
130
  public function pll_pre_translation_url( $url, $language, $queried_object_id ) {
131
  if ( ! empty( $queried_object_id ) ) {
132
+ // Page for posts
133
  if ( $GLOBALS['wp_query']->is_posts_page && ( $id = $this->model->post->get( $queried_object_id, $language ) ) ) {
134
  $url = get_permalink( $id );
135
  }
136
 
137
+ // Page on front
138
  elseif ( is_front_page() && $language->page_on_front && ( $language->page_on_front == $this->model->post->get( $queried_object_id, $language ) ) ) {
139
  $url = $language->home_url;
140
  }
144
  }
145
 
146
  /**
147
+ * Prevents canonical redirection if we are on a static front page
148
  *
149
  * @since 1.8
150
  *
152
  * @return bool|string
153
  */
154
  public function pll_check_canonical_url( $redirect_url ) {
155
+ return $this->options['redirect_lang'] && ! empty( $this->curlang->page_on_front ) && is_page( $this->curlang->page_on_front ) ? false : $redirect_url;
156
  }
157
 
158
  /**
159
+ * Setups query vars when requesting a static front page
160
  *
161
  * @since 1.8
162
  *
163
  * @param bool|object $lang
164
+ * @param object $query
165
  * @return bool|object
166
  */
167
  public function page_on_front_query( $lang, $query ) {
169
  return $lang;
170
  }
171
 
172
+ // The home page is requested
173
  if ( did_action( 'home_requested' ) ) {
174
  $query->set( 'page_id', $lang->page_on_front );
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() && 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;
182
  $query->is_archive = $query->is_tax = false;
183
+ unset( $query->query_vars['lang'], $query->queried_object ); // Reset queried object
184
  }
185
 
186
+ // Set the language when requesting a static front page
187
  else {
188
  $page_id = $this->get_page_id( $query );
189
  $languages = $this->model->get_languages_list();
194
  }
195
  }
196
 
197
+ // Fix <!--nextpage--> for page_on_front
198
  if ( ! empty( $lang ) ) {
199
  $query->set( 'page', $query->query_vars['paged'] );
200
  unset( $query->query_vars['paged'] );
204
  }
205
 
206
  /**
207
+ * Setups query vars when requesting a posts page
208
  *
209
  * @since 1.8
210
  *
211
  * @param bool|object $lang
212
+ * @param object $query
213
  * @return bool|object
214
  */
215
  public function page_for_posts_query( $lang, $query ) {
217
  $page_id = $this->get_page_id( $query );
218
 
219
  if ( ! empty( $page_id ) && in_array( $page_id, $pages = $this->model->get_languages_list( array( 'fields' => 'page_for_posts' ) ) ) ) {
220
+ // Fill the cache with all pages for posts to avoid one query per page later
221
+ // The posts_per_page limit is a trick to avoid splitting the query
222
  get_posts( array( 'posts_per_page' => 999, 'post_type' => 'page', 'post__in' => $pages, 'lang' => '' ) );
223
 
224
  $lang = $this->model->post->get_language( $page_id );
230
  }
231
 
232
  /**
233
+ * Get queried page_id ( if exists )
234
  * If permalinks are used, WordPress does set and use $query->queried_object_id and sets $query->query_vars['page_id'] to 0
235
  * and does set and use $query->query_vars['page_id'] if permalinks are not used :(
236
  *
248
  return $query->query_vars['page_id'];
249
  }
250
 
251
+ return 0; // No page queried
252
  }
253
  }
frontend/frontend.php CHANGED
@@ -34,17 +34,19 @@ class PLL_Frontend extends PLL_Base {
34
  public function __construct( &$links_model ) {
35
  parent::__construct( $links_model );
36
 
37
- add_action( 'pll_language_defined', array( &$this, 'pll_language_defined' ), 1 );
 
 
38
 
39
  // avoids the language being the queried object when querying multiple taxonomies
40
- add_action( 'parse_tax_query', array( &$this, 'parse_tax_query' ), 1 );
41
 
42
  // filters posts by language
43
- add_action( 'parse_query', array( &$this, 'parse_query' ), 6 );
44
 
45
  // not before 'check_canonical_url'
46
  if ( ! defined( 'PLL_AUTO_TRANSLATE' ) || PLL_AUTO_TRANSLATE ) {
47
- add_action( 'template_redirect', array( &$this, 'auto_translate' ), 7 );
48
  }
49
  }
50
 
@@ -68,7 +70,22 @@ class PLL_Frontend extends PLL_Base {
68
  }
69
 
70
  /**
71
- * setups filters and nav menus once the language has been defined
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  *
73
  * @since 1.2
74
  */
@@ -106,7 +123,7 @@ class PLL_Frontend extends PLL_Base {
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->have_translated_taxonomy( $query->tax_query->queries );
110
 
111
  // allow filtering recent posts and secondary queries by the current language
112
  // take care not to break queries for non visible post types such as nav_menu_items
@@ -183,31 +200,6 @@ class PLL_Frontend extends PLL_Base {
183
  }
184
  }
185
 
186
- /**
187
- * check if translated taxonomy is queried
188
- * compatible with nested queries introduced in WP 4.1
189
- * @see https://wordpress.org/support/topic/tax_query-bug
190
- *
191
- * @since 1.7
192
- *
193
- * @param array $tax_queries
194
- * @return bool
195
- */
196
- protected function have_translated_taxonomy( $tax_queries ) {
197
- foreach ( $tax_queries as $tax_query ) {
198
- if ( isset( $tax_query['taxonomy'] ) && $this->model->is_translated_taxonomy( $tax_query['taxonomy'] ) && ! ( isset( $tax_query['operator'] ) && 'NOT IN' === $tax_query['operator'] ) ) {
199
- return true;
200
- }
201
-
202
- // nested queries
203
- elseif ( is_array( $tax_query ) && $this->have_translated_taxonomy( $tax_query ) ) {
204
- return true;
205
- }
206
- }
207
-
208
- return false;
209
- }
210
-
211
  /**
212
  * get queried taxonomies
213
  *
34
  public function __construct( &$links_model ) {
35
  parent::__construct( $links_model );
36
 
37
+ add_filter( 'pll_after_languages_cache', array( $this, 'pll_after_languages_cache' ) );
38
+
39
+ add_action( 'pll_language_defined', array( $this, 'pll_language_defined' ), 1 );
40
 
41
  // avoids the language being the queried object when querying multiple taxonomies
42
+ add_action( 'parse_tax_query', array( $this, 'parse_tax_query' ), 1 );
43
 
44
  // filters posts by language
45
+ add_action( 'parse_query', array( $this, 'parse_query' ), 6 );
46
 
47
  // not before 'check_canonical_url'
48
  if ( ! defined( 'PLL_AUTO_TRANSLATE' ) || PLL_AUTO_TRANSLATE ) {
49
+ add_action( 'template_redirect', array( $this, 'auto_translate' ), 7 );
50
  }
51
  }
52
 
70
  }
71
 
72
  /**
73
+ * Set custom flags
74
+ *
75
+ * @since 2.0
76
+ *
77
+ * @param array $languages array of PLL_Language objects
78
+ * @return array
79
+ */
80
+ public function pll_after_languages_cache( $languages ) {
81
+ foreach ( $languages as $language ) {
82
+ $language->set_custom_flag();
83
+ }
84
+ return $languages;
85
+ }
86
+
87
+ /**
88
+ * Setups filters and nav menus once the language has been defined
89
  *
90
  * @since 1.2
91
  */
123
  $qv = $query->query_vars;
124
 
125
  // to avoid returning an empty result if the query includes a translated taxonomy in a different language
126
+ $has_tax = isset( $query->tax_query->queries ) && $this->model->have_translated_taxonomy( $query->tax_query->queries );
127
 
128
  // allow filtering recent posts and secondary queries by the current language
129
  // take care not to break queries for non visible post types such as nav_menu_items
200
  }
201
  }
202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  /**
204
  * get queried taxonomies
205
  *
include/api.php CHANGED
@@ -23,12 +23,11 @@
23
  * @return null|string|array null if displaying, array if raw is requested, string otherwise
24
  */
25
  function pll_the_languages( $args = '' ) {
26
- if ( PLL_ADMIN ) {
27
- return '';
 
28
  }
29
-
30
- $switcher = new PLL_Switcher;
31
- return $switcher->the_languages( PLL()->links, $args );
32
  }
33
 
34
  /**
@@ -63,7 +62,7 @@ function pll_default_language( $field = 'slug' ) {
63
  *
64
  * @param int $post_id post id
65
  * @param string $slug optional language code, defaults to current language
66
- * @return int|null post id of the translation if exists, null otherwise
67
  */
68
  function pll_get_post( $post_id, $slug = '' ) {
69
  return ( $slug = $slug ? $slug : pll_current_language() ) ? PLL()->model->post->get( $post_id, $slug ) : null;
@@ -76,7 +75,7 @@ function pll_get_post( $post_id, $slug = '' ) {
76
  *
77
  * @param int $term_id term id
78
  * @param string $slug optional language code, defaults to current language
79
- * @return int|null term id of the translation if exists, null otherwise
80
  */
81
  function pll_get_term( $term_id, $slug = '' ) {
82
  return ( $slug = $slug ? $slug : pll_current_language() ) ? PLL()->model->term->get( $term_id, $slug ) : null;
@@ -109,7 +108,7 @@ function pll_home_url( $lang = '' ) {
109
  * @param bool $multiline optional wether the string table should display a multiline textarea or a single line input, defaults to single line
110
  */
111
  function pll_register_string( $name, $string, $context = 'polylang', $multiline = false ) {
112
- if ( PLL_ADMIN ) {
113
  PLL_Admin_Strings::register_string( $name, $string, $context, $multiline );
114
  }
115
  }
23
  * @return null|string|array null if displaying, array if raw is requested, string otherwise
24
  */
25
  function pll_the_languages( $args = '' ) {
26
+ if ( PLL() instanceof PLL_Frontend ) {
27
+ $switcher = new PLL_Switcher;
28
+ return $switcher->the_languages( PLL()->links, $args );
29
  }
30
+ return '';
 
 
31
  }
32
 
33
  /**
62
  *
63
  * @param int $post_id post id
64
  * @param string $slug optional language code, defaults to current language
65
+ * @return int|false|null post id of the translation if exists, false otherwise, null if the current language is not defined yet
66
  */
67
  function pll_get_post( $post_id, $slug = '' ) {
68
  return ( $slug = $slug ? $slug : pll_current_language() ) ? PLL()->model->post->get( $post_id, $slug ) : null;
75
  *
76
  * @param int $term_id term id
77
  * @param string $slug optional language code, defaults to current language
78
+ * @return int|false|null term id of the translation if exists, false otherwise, null if the current language is not defined yet
79
  */
80
  function pll_get_term( $term_id, $slug = '' ) {
81
  return ( $slug = $slug ? $slug : pll_current_language() ) ? PLL()->model->term->get( $term_id, $slug ) : null;
108
  * @param bool $multiline optional wether the string table should display a multiline textarea or a single line input, defaults to single line
109
  */
110
  function pll_register_string( $name, $string, $context = 'polylang', $multiline = false ) {
111
+ if ( PLL() instanceof PLL_Admin_Base ) {
112
  PLL_Admin_Strings::register_string( $name, $string, $context, $multiline );
113
  }
114
  }
include/base.php CHANGED
@@ -20,13 +20,13 @@ abstract class PLL_Base {
20
  $this->model = &$links_model->model;
21
  $this->options = &$this->model->options;
22
 
23
- add_action( 'widgets_init', array( &$this, 'widgets_init' ) );
24
 
25
  // user defined strings translations
26
- add_action( 'pll_language_defined', array( &$this, 'load_strings_translations' ), 5 );
27
 
28
  // switch_to_blog
29
- add_action( 'switch_blog', array( &$this, 'switch_blog' ), 10, 2 );
30
  }
31
 
32
  /**
20
  $this->model = &$links_model->model;
21
  $this->options = &$this->model->options;
22
 
23
+ add_action( 'widgets_init', array( $this, 'widgets_init' ) );
24
 
25
  // user defined strings translations
26
+ add_action( 'pll_language_defined', array( $this, 'load_strings_translations' ), 5 );
27
 
28
  // switch_to_blog
29
+ add_action( 'switch_blog', array( $this, 'switch_blog' ), 10, 2 );
30
  }
31
 
32
  /**
include/cache.php CHANGED
@@ -16,7 +16,7 @@ class PLL_Cache {
16
  */
17
  public function __construct() {
18
  $this->blog_id = get_current_blog_id();
19
- add_action( 'switch_blog', array( &$this, 'switch_blog' ) );
20
  }
21
 
22
  /**
16
  */
17
  public function __construct() {
18
  $this->blog_id = get_current_blog_id();
19
+ add_action( 'switch_blog', array( $this, 'switch_blog' ) );
20
  }
21
 
22
  /**
include/class-polylang.php CHANGED
@@ -27,8 +27,8 @@ class Polylang {
27
  * @since 0.1
28
  */
29
  public function __construct() {
30
- // FIXME maybe not available on every installations but widely used by WP plugins
31
- spl_autoload_register( array( &$this, 'autoload' ) ); // autoload classes
32
 
33
  $install = new PLL_Install( POLYLANG_BASENAME );
34
 
@@ -39,7 +39,7 @@ class Polylang {
39
 
40
  // plugin initialization
41
  // take no action before all plugins are loaded
42
- add_action( 'plugins_loaded', array( &$this, 'init' ), 1 );
43
 
44
  // override load text domain waiting for the language to be defined
45
  // here for plugins which load text domain as soon as loaded :(
@@ -68,8 +68,8 @@ class Polylang {
68
  }
69
 
70
  $class = str_replace( '_', '-', strtolower( substr( $class, 4 ) ) );
71
- $to_remove = array( 'post-', 'term-', 'settings-', 'admin-', 'frontend-', '-config', '-compat', '-model', 'advanced-' );
72
- $dir = str_replace( $to_remove, array(), $class );
73
 
74
  $dirs = array(
75
  PLL_FRONT_INC,
@@ -187,6 +187,15 @@ class Polylang {
187
  }
188
 
189
  if ( ! empty( $polylang ) ) {
 
 
 
 
 
 
 
 
 
190
  require_once( PLL_INC.'/api.php' ); // loads the API
191
 
192
  if ( ! defined( 'PLL_WPML_COMPAT' ) || PLL_WPML_COMPAT ) {
27
  * @since 0.1
28
  */
29
  public function __construct() {
30
+ require_once( PLL_INC . '/functions-wpcom-vip.php' ); // VIP functions
31
+ spl_autoload_register( array( $this, 'autoload' ) ); // autoload classes
32
 
33
  $install = new PLL_Install( POLYLANG_BASENAME );
34
 
39
 
40
  // plugin initialization
41
  // take no action before all plugins are loaded
42
+ add_action( 'plugins_loaded', array( $this, 'init' ), 1 );
43
 
44
  // override load text domain waiting for the language to be defined
45
  // here for plugins which load text domain as soon as loaded :(
68
  }
69
 
70
  $class = str_replace( '_', '-', strtolower( substr( $class, 4 ) ) );
71
+ $to_find = array( 'media', 'share', 'slug', 'slugs', 'sync', 'translate', 'wpml', 'xdata' );
72
+ $dir = implode( '-', array_intersect( explode( '-', $class ), $to_find ) );
73
 
74
  $dirs = array(
75
  PLL_FRONT_INC,
187
  }
188
 
189
  if ( ! empty( $polylang ) ) {
190
+ /**
191
+ * Fires after the $polylang object is created and before the API is loaded
192
+ *
193
+ * @since 2.0
194
+ *
195
+ * @param object $polylang
196
+ */
197
+ do_action_ref_array( 'pll_pre_init', array( &$polylang ) );
198
+
199
  require_once( PLL_INC.'/api.php' ); // loads the API
200
 
201
  if ( ! defined( 'PLL_WPML_COMPAT' ) || PLL_WPML_COMPAT ) {
include/filters-links.php CHANGED
@@ -23,37 +23,22 @@ class PLL_Filters_Links {
23
 
24
  // low priority on links filters to come after any other modifications
25
  if ( $this->options['force_lang'] ) {
26
- add_filter( 'post_link', array( &$this, 'post_link' ), 20, 2 );
27
- add_filter( '_get_page_link', array( &$this, '_get_page_link' ), 20, 2 );
28
  }
29
 
30
- add_filter( 'post_type_link', array( &$this, 'post_type_link' ), 20, 2 );
31
- add_filter( 'term_link', array( &$this, 'term_link' ), 20, 3 );
32
 
33
  if ( $this->options['force_lang'] > 0 ) {
34
- add_filter( 'attachment_link', array( &$this, 'attachment_link' ), 20, 2 );
35
  }
36
 
37
  if ( 3 === $this->options['force_lang'] ) {
38
- add_filter( 'preview_post_link', array( &$this, 'preview_post_link' ), 20 );
39
  }
40
  }
41
 
42
- /**
43
- * modifies post & page links
44
- *
45
- * @since 0.7
46
- *
47
- * @param string $link post link
48
- * @param object $post post object
49
- * @return string modified post link
50
- */
51
- public function post_link( $link, $post ) {
52
- // /!\ WP does not use pretty permalinks for preview
53
- return false !== strpos( $link, 'preview=true' ) && false !== strpos( $link, 'p=' ) ? $link : $this->links_model->add_language_to_link( $link, $this->model->post->get_language( $post->ID ) );
54
- }
55
-
56
-
57
  /**
58
  * modifies page links
59
  *
@@ -97,11 +82,11 @@ class PLL_Filters_Links {
97
  $link = $this->options['force_lang'] ? $this->links_model->add_language_to_link( $link, $lang ) : $link;
98
 
99
  /**
100
- * Filter a custom post type link
101
  *
102
  * @since 1.6
103
  *
104
- * @param string $link the term link
105
  * @param object $lang the current language
106
  * @param object $post the post object
107
  */
23
 
24
  // low priority on links filters to come after any other modifications
25
  if ( $this->options['force_lang'] ) {
26
+ add_filter( 'post_link', array( $this, 'post_type_link' ), 20, 2 );
27
+ add_filter( '_get_page_link', array( $this, '_get_page_link' ), 20, 2 );
28
  }
29
 
30
+ add_filter( 'post_type_link', array( $this, 'post_type_link' ), 20, 2 );
31
+ add_filter( 'term_link', array( $this, 'term_link' ), 20, 3 );
32
 
33
  if ( $this->options['force_lang'] > 0 ) {
34
+ add_filter( 'attachment_link', array( $this, 'attachment_link' ), 20, 2 );
35
  }
36
 
37
  if ( 3 === $this->options['force_lang'] ) {
38
+ add_filter( 'preview_post_link', array( $this, 'preview_post_link' ), 20 );
39
  }
40
  }
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  /**
43
  * modifies page links
44
  *
82
  $link = $this->options['force_lang'] ? $this->links_model->add_language_to_link( $link, $lang ) : $link;
83
 
84
  /**
85
+ * Filter a post or custom post type link
86
  *
87
  * @since 1.6
88
  *
89
+ * @param string $link the post link
90
  * @param object $lang the current language
91
  * @param object $post the post object
92
  */
include/filters.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  /**
4
- * setup filters common to admin and frontend
5
  *
6
  * @since 1.4
7
  */
@@ -9,7 +9,7 @@ class PLL_Filters {
9
  public $links_model, $model, $options, $curlang;
10
 
11
  /**
12
- * constructor: setups filters
13
  *
14
  * @since 1.4
15
  *
@@ -21,45 +21,74 @@ class PLL_Filters {
21
  $this->options = &$polylang->options;
22
  $this->curlang = &$polylang->curlang;
23
 
24
- // filters the comments according to the current language
25
- add_filter( 'comments_clauses', array( &$this, 'comments_clauses' ), 10, 2 );
 
26
 
27
- // filters the get_pages function according to the current language
28
- add_filter( 'get_pages', array( &$this, 'get_pages' ), 10, 2 );
29
 
30
- // converts the locale to a valid W3C locale
31
- add_filter( 'language_attributes', array( &$this, 'language_attributes' ) );
32
  }
33
 
34
  /**
35
- * filters the comments according to the current language
36
- * used by the recent comments widget and admin language filter
37
  *
38
- * @since 0.2
39
  *
40
- * @param array $clauses sql clauses
41
- * @param object $query WP_Comment_Query object
42
- * @return array modified $clauses
43
  */
44
- public function comments_clauses( $clauses, $query ) {
45
- global $wpdb;
46
-
47
- // don't filter comments if comment ids or post ids are specified
48
  $plucked = wp_array_slice_assoc( $query->query_vars, array( 'comment__in', 'parent', 'post_id', 'post__in', 'post_parent' ) );
49
  $fields = array_filter( $plucked );
50
  if ( ! empty( $fields ) ) {
51
- return $clauses;
52
  }
53
 
54
- // don't filter comments if a non translated post type is specified
55
  if ( ! empty( $query->query_vars['post_type'] ) && ! $this->model->is_translated_post_type( $query->query_vars['post_type'] ) ) {
56
- return $clauses;
57
  }
58
 
59
- $lang = empty( $query->query_vars['lang'] ) ? $this->curlang : $this->model->get_language( $query->query_vars['lang'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
  if ( ! empty( $lang ) ) {
62
- // if this clause is not already added by WP
63
  if ( ! strpos( $clauses['join'], '.ID' ) ) {
64
  $clauses['join'] .= " JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
65
  }
@@ -71,12 +100,12 @@ class PLL_Filters {
71
  }
72
 
73
  /**
74
- * filters get_pages per language
75
  *
76
  * @since 1.4
77
  *
78
  * @param array $pages an array of pages already queried
79
- * @param array $args get_pages arguments
80
  * @return array modified list of pages
81
  */
82
  public function get_pages( $pages, $args ) {
@@ -92,19 +121,19 @@ class PLL_Filters {
92
 
93
  static $once = false;
94
 
95
- // obliged to redo the get_pages query if we want to get the right number
96
  if ( ! empty( $args['number'] ) && ! $once ) {
97
  $once = true; // avoid infinite loop
98
 
99
  $r = array(
100
- 'lang' => 0, // so this query is not filtered
101
  'numberposts' => -1,
102
  'nopaging' => true,
103
  'post_type' => $args['post_type'],
104
  'fields' => 'ids',
105
  'tax_query' => array( array(
106
  'taxonomy' => 'language',
107
- 'field' => 'term_taxonomy_id', // since WP 3.5
108
  'terms' => $language->term_taxonomy_id,
109
  'operator' => 'NOT IN',
110
  ) ),
@@ -116,7 +145,7 @@ class PLL_Filters {
116
 
117
  $ids = wp_list_pluck( $pages, 'ID' );
118
 
119
- // filters the queried list of pages by language
120
  if ( ! $once ) {
121
  $ids = array_intersect( $ids, $this->model->post->get_objects_in_language( $language ) );
122
 
@@ -127,15 +156,15 @@ class PLL_Filters {
127
  }
128
  }
129
 
130
- // not done by WP but extremely useful for performance when manipulating taxonomies
131
  update_object_term_cache( $ids, $args['post_type'] );
132
 
133
- $once = false; // in case get_pages is called another time
134
  return $pages;
135
  }
136
 
137
  /**
138
- * converts WordPress locale to valid W3 locale in html language attributes
139
  *
140
  * @since 1.8
141
  *
1
  <?php
2
 
3
  /**
4
+ * Setup filters common to admin and frontend
5
  *
6
  * @since 1.4
7
  */
9
  public $links_model, $model, $options, $curlang;
10
 
11
  /**
12
+ * Constructor: setups filters
13
  *
14
  * @since 1.4
15
  *
21
  $this->options = &$polylang->options;
22
  $this->curlang = &$polylang->curlang;
23
 
24
+ // Filters the comments according to the current language
25
+ add_action( 'parse_comment_query', array( $this, 'parse_comment_query' ) );
26
+ add_filter( 'comments_clauses', array( $this, 'comments_clauses' ), 10, 2 );
27
 
28
+ // Filters the get_pages function according to the current language
29
+ add_filter( 'get_pages', array( $this, 'get_pages' ), 10, 2 );
30
 
31
+ // Converts the locale to a valid W3C locale
32
+ add_filter( 'language_attributes', array( $this, 'language_attributes' ) );
33
  }
34
 
35
  /**
36
+ * Get the language to filter a comments query
 
37
  *
38
+ * @since 2.0
39
  *
40
+ * @param object $query
41
+ * @return object|bool the language(s) to use in the filter, false otherwise
 
42
  */
43
+ protected function get_comments_queried_language( $query ) {
44
+ // Don't filter comments if comment ids or post ids are specified
 
 
45
  $plucked = wp_array_slice_assoc( $query->query_vars, array( 'comment__in', 'parent', 'post_id', 'post__in', 'post_parent' ) );
46
  $fields = array_filter( $plucked );
47
  if ( ! empty( $fields ) ) {
48
+ return false;
49
  }
50
 
51
+ // Don't filter comments if a non translated post type is specified
52
  if ( ! empty( $query->query_vars['post_type'] ) && ! $this->model->is_translated_post_type( $query->query_vars['post_type'] ) ) {
53
+ return false;
54
  }
55
 
56
+ return empty( $query->query_vars['lang'] ) ? $this->curlang : $this->model->get_language( $query->query_vars['lang'] );
57
+ }
58
+
59
+ /**
60
+ * Adds language dependent cache domain when querying comments
61
+ * Useful as the 'lang' parameter is not included in cache key by WordPress
62
+ * Needed since WP 4.6 as comments have been added to persistent cache. See #36906, #37419
63
+ *
64
+ * @since 2.0
65
+ *
66
+ * @param object $query
67
+ */
68
+ public function parse_comment_query( $query ) {
69
+ if ( $lang = $this->get_comments_queried_language( $query ) ) {
70
+ $key = '_' . ( is_array( $lang ) ? implode( ',', $lang ) : $this->model->get_language( $lang )->slug );
71
+ $query->query_vars['cache_domain'] = empty( $query->query_vars['cache_domain'] ) ? 'pll' . $key : $query->query_vars['cache_domain'] . $key;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Filters the comments according to the current language
77
+ * Used by the recent comments widget and admin language filter
78
+ *
79
+ * @since 0.2
80
+ *
81
+ * @param array $clauses sql clauses
82
+ * @param object $query WP_Comment_Query object
83
+ * @return array modified $clauses
84
+ */
85
+ public function comments_clauses( $clauses, $query ) {
86
+ global $wpdb;
87
+
88
+ $lang = $this->get_comments_queried_language( $query );
89
 
90
  if ( ! empty( $lang ) ) {
91
+ // If this clause is not already added by WP
92
  if ( ! strpos( $clauses['join'], '.ID' ) ) {
93
  $clauses['join'] .= " JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
94
  }
100
  }
101
 
102
  /**
103
+ * Filters get_pages per language
104
  *
105
  * @since 1.4
106
  *
107
  * @param array $pages an array of pages already queried
108
+ * @param array $args get_pages arguments
109
  * @return array modified list of pages
110
  */
111
  public function get_pages( $pages, $args ) {
121
 
122
  static $once = false;
123
 
124
+ // Obliged to redo the get_pages query if we want to get the right number
125
  if ( ! empty( $args['number'] ) && ! $once ) {
126
  $once = true; // avoid infinite loop
127
 
128
  $r = array(
129
+ 'lang' => 0, // So this query is not filtered
130
  'numberposts' => -1,
131
  'nopaging' => true,
132
  'post_type' => $args['post_type'],
133
  'fields' => 'ids',
134
  'tax_query' => array( array(
135
  'taxonomy' => 'language',
136
+ 'field' => 'term_taxonomy_id', // Since WP 3.5
137
  'terms' => $language->term_taxonomy_id,
138
  'operator' => 'NOT IN',
139
  ) ),
145
 
146
  $ids = wp_list_pluck( $pages, 'ID' );
147
 
148
+ // Filters the queried list of pages by language
149
  if ( ! $once ) {
150
  $ids = array_intersect( $ids, $this->model->post->get_objects_in_language( $language ) );
151
 
156
  }
157
  }
158
 
159
+ // Not done by WP but extremely useful for performance when manipulating taxonomies
160
  update_object_term_cache( $ids, $args['post_type'] );
161
 
162
+ $once = false; // In case get_pages is called another time
163
  return $pages;
164
  }
165
 
166
  /**
167
+ * Converts WordPress locale to valid W3 locale in html language attributes
168
  *
169
  * @since 1.8
170
  *
include/functions-wpcom-vip.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Define wordpress.com VIP equivalent of uncached functions
5
+ */
6
+
7
+ if ( ! function_exists( 'wpcom_vip_get_page_by_title' ) ) {
8
+ function wpcom_vip_get_page_by_title( $page_title, $output = OBJECT, $post_type = 'page' ) {
9
+ return get_page_by_title( $page_title, $output, $post_type );
10
+ }
11
+ }
12
+
13
+ if ( ! function_exists( 'wpcom_vip_get_category_by_slug' ) ) {
14
+ function wpcom_vip_get_category_by_slug( $slug ) {
15
+ return get_category_by_slug( $slug );
16
+ }
17
+ }
18
+
19
+ if ( ! function_exists( 'wpcom_vip_get_term_by' ) ) {
20
+ function wpcom_vip_get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) {
21
+ return get_term_by( $field, $value, $taxonomy, $output, $filter );
22
+ }
23
+ }
24
+
25
+ if ( ! function_exists( 'wpcom_vip_get_term_link' ) ) {
26
+ function wpcom_vip_get_term_link( $term, $taxonomy = '' ) {
27
+ return get_term_link( $term, $taxonomy );
28
+ }
29
+ }
include/license.php CHANGED
@@ -46,7 +46,7 @@ class PLL_License {
46
  wp_schedule_event( time(), 'weekly', 'polylang_check_licenses' );
47
  }
48
 
49
- add_action( 'polylang_check_licenses', array( &$this, 'check_license' ) );
50
  }
51
 
52
  /**
46
  wp_schedule_event( time(), 'weekly', 'polylang_check_licenses' );
47
  }
48
 
49
+ add_action( 'polylang_check_licenses', array( $this, 'check_license' ) );
50
  }
51
 
52
  /**
include/links-abstract-domain.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Links model for use when using one domain or subdomain per language
5
+ *
6
+ * @since 2.0
7
+ */
8
+ abstract class PLL_Links_Abstract_Domain extends PLL_Links_Permalinks {
9
+
10
+ /**
11
+ * Constructor
12
+ *
13
+ * @since 2.0
14
+ *
15
+ * @param object $model PLL_Model instance
16
+ */
17
+ public function __construct( &$model ) {
18
+ parent::__construct( $model );
19
+
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
+ }
24
+
25
+ /**
26
+ * Returns the current site url
27
+ *
28
+ * @since 1.8
29
+ *
30
+ * @param string $url
31
+ * @return string
32
+ */
33
+ public function site_url( $url ) {
34
+ $lang = $this->get_language_from_url();
35
+ $lang = $this->model->get_language( $lang );
36
+ return $this->add_language_to_link( $url, $lang );
37
+ }
38
+ }
include/links-default.php CHANGED
@@ -68,12 +68,18 @@ class PLL_Links_Default extends PLL_Links_Model {
68
  * links_model interface
69
  *
70
  * @since 1.2
 
71
  *
 
72
  * @return string language slug
73
  */
74
- public function get_language_from_url() {
 
 
 
 
75
  $pattern = '#lang=('.implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ).')#';
76
- return preg_match( $pattern, trailingslashit( $_SERVER['REQUEST_URI'] ), $matches ) ? $matches[1] : ''; // $matches[1] is the slug of the requested language
77
  }
78
 
79
  /**
68
  * links_model interface
69
  *
70
  * @since 1.2
71
+ * @since 2.0 add $url argument
72
  *
73
+ * @param string $url optional, defaults to current url
74
  * @return string language slug
75
  */
76
+ public function get_language_from_url( $url = '' ) {
77
+ if ( empty( $url ) ) {
78
+ $url = $_SERVER['REQUEST_URI'];
79
+ }
80
+
81
  $pattern = '#lang=('.implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ).')#';
82
+ return preg_match( $pattern, trailingslashit( $url ), $matches ) ? $matches[1] : ''; // $matches[1] is the slug of the requested language
83
  }
84
 
85
  /**
include/links-directory.php CHANGED
@@ -22,7 +22,7 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
22
  if ( did_action( 'pll_init' ) ) {
23
  $this->init();
24
  } else {
25
- add_action( 'pll_init', array( &$this, 'init' ) );
26
  }
27
  }
28
 
@@ -35,11 +35,11 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
35
  if ( did_action( 'setup_theme' ) ) {
36
  $this->add_permastruct();
37
  } else {
38
- add_action( 'setup_theme', array( &$this, 'add_permastruct' ), 2 );
39
  }
40
 
41
  // Make sure to prepare rewrite rules when flushing
42
- add_action( 'pre_option_rewrite_rules', array( &$this, 'prepare_rewrite_rules' ) );
43
  }
44
 
45
  /**
@@ -90,14 +90,19 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
90
  * links_model interface
91
  *
92
  * @since 1.2
 
93
  *
 
94
  * @return string language slug
95
  */
96
- public function get_language_from_url() {
97
- $requested_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
 
 
 
98
  $pattern = str_replace( '/', '\/', $this->home . '/' . $this->root . ( $this->options['rewrite'] ? '' : 'language/' ) );
99
  $pattern = '#' . $pattern . '('. implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) . ')(\/|$)#';
100
- return preg_match( $pattern, trailingslashit( $requested_url ), $matches ) ? $matches[1] : ''; // $matches[1] is the slug of the requested language
101
  }
102
 
103
  /**
@@ -141,10 +146,10 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
141
  add_filter( 'language_rewrite_rules', '__return_empty_array' );
142
 
143
  foreach ( $this->get_rewrite_rules_filters() as $type ) {
144
- add_filter( $type . '_rewrite_rules', array( &$this, 'rewrite_rules' ) );
145
  }
146
 
147
- add_filter( 'rewrite_rules_array', array( &$this, 'rewrite_rules' ) ); // needed for post type archives
148
  }
149
  return $pre;
150
  }
22
  if ( did_action( 'pll_init' ) ) {
23
  $this->init();
24
  } else {
25
+ add_action( 'pll_init', array( $this, 'init' ) );
26
  }
27
  }
28
 
35
  if ( did_action( 'setup_theme' ) ) {
36
  $this->add_permastruct();
37
  } else {
38
+ add_action( 'setup_theme', array( $this, 'add_permastruct' ), 2 );
39
  }
40
 
41
  // Make sure to prepare rewrite rules when flushing
42
+ add_action( 'pre_option_rewrite_rules', array( $this, 'prepare_rewrite_rules' ) );
43
  }
44
 
45
  /**
90
  * links_model interface
91
  *
92
  * @since 1.2
93
+ * @since 2.0 add $url argument
94
  *
95
+ * @param string $url optional, defaults to current url
96
  * @return string language slug
97
  */
98
+ public function get_language_from_url( $url = '' ) {
99
+ if ( empty( $url ) ) {
100
+ $url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
101
+ }
102
+
103
  $pattern = str_replace( '/', '\/', $this->home . '/' . $this->root . ( $this->options['rewrite'] ? '' : 'language/' ) );
104
  $pattern = '#' . $pattern . '('. implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) . ')(\/|$)#';
105
+ return preg_match( $pattern, trailingslashit( $url ), $matches ) ? $matches[1] : ''; // $matches[1] is the slug of the requested language
106
  }
107
 
108
  /**
146
  add_filter( 'language_rewrite_rules', '__return_empty_array' );
147
 
148
  foreach ( $this->get_rewrite_rules_filters() as $type ) {
149
+ add_filter( $type . '_rewrite_rules', array( $this, 'rewrite_rules' ) );
150
  }
151
 
152
+ add_filter( 'rewrite_rules_array', array( $this, 'rewrite_rules' ) ); // needed for post type archives
153
  }
154
  return $pre;
155
  }
include/links-domain.php CHANGED
@@ -7,7 +7,7 @@
7
  *
8
  * @since 1.2
9
  */
10
- class PLL_Links_Domain extends PLL_Links_Permalinks {
11
 
12
  /**
13
  * Constructor
@@ -19,7 +19,8 @@ class PLL_Links_Domain extends PLL_Links_Permalinks {
19
  public function __construct( &$model ) {
20
  parent::__construct( $model );
21
 
22
- add_filter( 'site_url', array( &$this, 'site_url' ) );
 
23
  }
24
 
25
 
@@ -61,11 +62,14 @@ class PLL_Links_Domain extends PLL_Links_Permalinks {
61
  * links_model interface
62
  *
63
  * @since 1.2
 
64
  *
 
65
  * @return string language slug
66
  */
67
- public function get_language_from_url() {
68
- return ( $lang = array_search( ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . parse_url( $this->home, PHP_URL_PATH ), $this->options['domains'] ) ) ? $lang : '';
 
69
  }
70
 
71
  /**
@@ -95,18 +99,4 @@ class PLL_Links_Domain extends PLL_Links_Permalinks {
95
  }
96
  return $hosts;
97
  }
98
-
99
- /**
100
- * Returns the correct site url ( mainly to get the correct login form )
101
- *
102
- * @since 1.8
103
- *
104
- * @param string $url
105
- * @return string
106
- */
107
- public function site_url( $url ) {
108
- $lang = $this->get_language_from_url();
109
- $lang = $this->model->get_language( $lang );
110
- return $this->add_language_to_link( $url, $lang );
111
- }
112
  }
7
  *
8
  * @since 1.2
9
  */
10
+ class PLL_Links_Domain extends PLL_Links_Abstract_Domain {
11
 
12
  /**
13
  * Constructor
19
  public function __construct( &$model ) {
20
  parent::__construct( $model );
21
 
22
+ // Filrer the site url ( mainly to get the correct login form )
23
+ add_filter( 'site_url', array( $this, 'site_url' ) );
24
  }
25
 
26
 
62
  * links_model interface
63
  *
64
  * @since 1.2
65
+ * @since 2.0 add $url argument
66
  *
67
+ * @param string $url optional, defaults to current url
68
  * @return string language slug
69
  */
70
+ public function get_language_from_url( $url = '' ) {
71
+ $host = empty( $url ) ? $_SERVER['HTTP_HOST'] : parse_url( $url, PHP_URL_HOST );
72
+ return ( $lang = array_search( ( is_ssl() ? 'https://' : 'http://' ) . $host . parse_url( $this->home, PHP_URL_PATH ), $this->options['domains'] ) ) ? $lang : '';
73
  }
74
 
75
  /**
99
  }
100
  return $hosts;
101
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  }
include/links-model.php CHANGED
@@ -23,11 +23,11 @@ abstract class PLL_Links_Model {
23
 
24
  $this->home = home_url();
25
 
26
- add_filter( 'pll_languages_list', array( &$this, 'pll_languages_list' ), 4 ); // after PLL_Static_Pages
27
- add_filter( 'pll_after_languages_cache', array( &$this, 'pll_after_languages_cache' ) );
28
 
29
  // adds our domains or subdomains to allowed hosts for safe redirection
30
- add_filter( 'allowed_redirect_hosts', array( &$this, 'allowed_redirect_hosts' ) );
31
  }
32
 
33
  /**
23
 
24
  $this->home = home_url();
25
 
26
+ add_filter( 'pll_languages_list', array( $this, 'pll_languages_list' ), 4 ); // after PLL_Static_Pages
27
+ add_filter( 'pll_after_languages_cache', array( $this, 'pll_after_languages_cache' ) );
28
 
29
  // adds our domains or subdomains to allowed hosts for safe redirection
30
+ add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
31
  }
32
 
33
  /**
include/links-permalinks.php CHANGED
@@ -91,7 +91,7 @@ abstract class PLL_Links_Permalinks extends PLL_Links_Model {
91
  $types = array_values( array_merge( $this->model->get_translated_post_types(), $this->model->get_translated_taxonomies(), $this->model->get_filtered_taxonomies() ) );
92
  $types = array_merge( $this->always_rewrite, $types );
93
 
94
- /*
95
  * Filter the list of rewrite rules filters to be used by Polylang
96
  *
97
  * @since 0.8.1
91
  $types = array_values( array_merge( $this->model->get_translated_post_types(), $this->model->get_translated_taxonomies(), $this->model->get_filtered_taxonomies() ) );
92
  $types = array_merge( $this->always_rewrite, $types );
93
 
94
+ /**
95
  * Filter the list of rewrite rules filters to be used by Polylang
96
  *
97
  * @since 0.8.1
include/links-subdomain.php CHANGED
@@ -7,7 +7,7 @@
7
  *
8
  * @since 1.2
9
  */
10
- class PLL_Links_Subdomain extends PLL_Links_Permalinks {
11
  protected $www;
12
 
13
  /**
@@ -67,12 +67,15 @@ class PLL_Links_Subdomain extends PLL_Links_Permalinks {
67
  * links_model interface
68
  *
69
  * @since 1.2
 
70
  *
 
71
  * @return string language slug
72
  */
73
- public function get_language_from_url() {
 
74
  $pattern = '#('.implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ).')\.#';
75
- return preg_match( $pattern, trailingslashit( $_SERVER['HTTP_HOST'] ), $matches ) ? $matches[1] : ''; // $matches[1] is the slug of the requested language
76
  }
77
 
78
  /**
7
  *
8
  * @since 1.2
9
  */
10
+ class PLL_Links_Subdomain extends PLL_Links_Abstract_Domain {
11
  protected $www;
12
 
13
  /**
67
  * links_model interface
68
  *
69
  * @since 1.2
70
+ * @since 2.0 add $url argument
71
  *
72
+ * @param string $url optional, defaults to current url
73
  * @return string language slug
74
  */
75
+ public function get_language_from_url( $url = '' ) {
76
+ $host = empty( $url ) ? $_SERVER['HTTP_HOST'] : parse_url( $url, PHP_URL_HOST );
77
  $pattern = '#('.implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ).')\.#';
78
+ return preg_match( $pattern, trailingslashit( $host ), $matches ) ? $matches[1] : ''; // $matches[1] is the slug of the requested language
79
  }
80
 
81
  /**
include/mo.php CHANGED
@@ -77,6 +77,15 @@ class PLL_MO extends MO {
77
  */
78
  public static function get_id( $lang ) {
79
  global $wpdb;
80
- return $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type= %s", 'polylang_mo_' . $lang->term_id, 'polylang_mo' ) );
 
 
 
 
 
 
 
 
 
81
  }
82
  }
77
  */
78
  public static function get_id( $lang ) {
79
  global $wpdb;
80
+
81
+ $ids = wp_cache_get( 'polylang_mo_ids' );
82
+
83
+ if ( empty( $ids ) ) {
84
+ $ids = $wpdb->get_results( "SELECT post_title, ID FROM $wpdb->posts WHERE post_type='polylang_mo'", OBJECT_K );
85
+ wp_cache_add( 'polylang_mo_ids', $ids );
86
+ }
87
+
88
+ // The mo id for a language can be transiently empty
89
+ return isset( $ids[ 'polylang_mo_' . $lang->term_id ] ) ? $ids[ 'polylang_mo_' . $lang->term_id ]->ID : null;
90
  }
91
  }
include/model.php CHANGED
@@ -27,10 +27,10 @@ class PLL_Model {
27
  $this->term = new PLL_Translated_Term( $this ); // translated term sub model
28
 
29
  // we need to clean languages cache when editing a language and when modifying the permalink structure
30
- add_action( 'edited_term_taxonomy', array( &$this, 'clean_languages_cache' ), 10, 2 );
31
- add_action( 'update_option_permalink_structure', array( &$this, 'clean_languages_cache' ) );
32
- add_action( 'update_option_siteurl', array( &$this, 'clean_languages_cache' ) );
33
- add_action( 'update_option_home', array( &$this, 'clean_languages_cache' ) );
34
 
35
  // just in case someone would like to display the language description ;- )
36
  add_filter( 'language_description', '__return_empty_string' );
@@ -100,13 +100,6 @@ class PLL_Model {
100
  }
101
  }
102
 
103
- // custom flags
104
- if ( ! PLL_ADMIN ) {
105
- foreach ( $languages as $language ) {
106
- $language->set_custom_flag();
107
- }
108
- }
109
-
110
  /**
111
  * Filter the list of languages *after* it is stored in the persistent cache
112
  * /!\ this filter is fired *before* the $polylang object is available
@@ -188,7 +181,7 @@ class PLL_Model {
188
  * @return array modifed list of clauses
189
  */
190
  public function terms_clauses( $clauses, $lang ) {
191
- if ( ! empty( $lang ) ) {
192
  $clauses['join'] .= $this->term->join_clause();
193
  $clauses['where'] .= $this->term->where_clause( $lang );
194
  }
@@ -210,7 +203,7 @@ class PLL_Model {
210
  $post_types = array( 'post' => 'post', 'page' => 'page' );
211
 
212
  if ( ! empty( $this->options['media_support'] ) ) {
213
- $post_types['attachement'] = 'attachment';
214
  }
215
 
216
  if ( ! empty( $this->options['post_types'] ) && is_array( $this->options['post_types'] ) ) {
@@ -300,6 +293,31 @@ class PLL_Model {
300
  return ( is_array( $tax ) && array_intersect( $tax, $taxonomies ) || in_array( $tax, $taxonomies ) );
301
  }
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  /**
304
  * Return taxonomies that need to be filtered ( post_format like )
305
  *
@@ -462,34 +480,34 @@ class PLL_Model {
462
  $counts = wp_cache_get( $cache_key, 'pll_count_posts' );
463
 
464
  if ( false === $counts ) {
465
- $select = "SELECT pll_tr.term_taxonomy_id, COUNT( * ) AS num_posts FROM {$wpdb->posts} AS p";
466
  $join = $this->post->join_clause();
467
  $where = " WHERE post_status = 'publish'";
468
- $where .= $wpdb->prepare( " AND p.post_type IN ( '%s' )", join( "', '", $q['post_type'] ) );
469
  $where .= $this->post->where_clause( $this->get_languages_list() );
470
  $groupby = ' GROUP BY pll_tr.term_taxonomy_id';
471
 
472
  if ( ! empty( $q['m'] ) ) {
473
  $q['m'] = '' . preg_replace( '|[^0-9]|', '', $q['m'] );
474
- $where .= $wpdb->prepare( ' AND YEAR( p.post_date ) = %d', substr( $q['m'], 0, 4 ) );
475
  if ( strlen( $q['m'] ) > 5 ) {
476
- $where .= $wpdb->prepare( ' AND MONTH( p.post_date ) = %d', substr( $q['m'], 4, 2 ) );
477
  }
478
  if ( strlen( $q['m'] ) > 7 ) {
479
- $where .= $wpdb->prepare( ' AND DAYOFMONTH( p.post_date ) = %d', substr( $q['m'], 6, 2 ) );
480
  }
481
  }
482
 
483
  if ( ! empty( $q['year'] ) ) {
484
- $where .= $wpdb->prepare( ' AND YEAR( p.post_date ) = %d', $q['year'] );
485
  }
486
 
487
  if ( ! empty( $q['monthnum'] ) ) {
488
- $where .= $wpdb->prepare( ' AND MONTH( p.post_date ) = %d', $q['monthnum'] );
489
  }
490
 
491
  if ( ! empty( $q['day'] ) ) {
492
- $where .= $wpdb->prepare( ' AND DAYOFMONTH( p.post_date ) = %d', $q['day'] );
493
  }
494
 
495
  if ( ! empty( $q['author_name'] ) ) {
@@ -500,14 +518,14 @@ class PLL_Model {
500
  }
501
 
502
  if ( ! empty( $q['author'] ) ) {
503
- $where .= $wpdb->prepare( ' AND p.post_author = %d', $q['author'] );
504
  }
505
 
506
  // filtered taxonomies ( post_format )
507
  foreach ( $this->get_filtered_taxonomies_query_vars() as $tax_qv ) {
508
 
509
  if ( ! empty( $q[ $tax_qv ] ) ) {
510
- $join .= " INNER JOIN {$wpdb->term_relationships} AS tr ON tr.object_id = p.ID";
511
  $join .= " INNER JOIN {$wpdb->term_taxonomy} AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id";
512
  $join .= " INNER JOIN {$wpdb->terms} AS t ON t.term_id = tt.term_id";
513
  $where .= $wpdb->prepare( ' AND t.slug = %s', $q[ $tax_qv ] );
27
  $this->term = new PLL_Translated_Term( $this ); // translated term sub model
28
 
29
  // we need to clean languages cache when editing a language and when modifying the permalink structure
30
+ add_action( 'edited_term_taxonomy', array( $this, 'clean_languages_cache' ), 10, 2 );
31
+ add_action( 'update_option_permalink_structure', array( $this, 'clean_languages_cache' ) );
32
+ add_action( 'update_option_siteurl', array( $this, 'clean_languages_cache' ) );
33
+ add_action( 'update_option_home', array( $this, 'clean_languages_cache' ) );
34
 
35
  // just in case someone would like to display the language description ;- )
36
  add_filter( 'language_description', '__return_empty_string' );
100
  }
101
  }
102
 
 
 
 
 
 
 
 
103
  /**
104
  * Filter the list of languages *after* it is stored in the persistent cache
105
  * /!\ this filter is fired *before* the $polylang object is available
181
  * @return array modifed list of clauses
182
  */
183
  public function terms_clauses( $clauses, $lang ) {
184
+ if ( ! empty( $lang ) && false === strpos( $clauses['join'], 'pll_tr' ) ) {
185
  $clauses['join'] .= $this->term->join_clause();
186
  $clauses['where'] .= $this->term->where_clause( $lang );
187
  }
203
  $post_types = array( 'post' => 'post', 'page' => 'page' );
204
 
205
  if ( ! empty( $this->options['media_support'] ) ) {
206
+ $post_types['attachment'] = 'attachment';
207
  }
208
 
209
  if ( ! empty( $this->options['post_types'] ) && is_array( $this->options['post_types'] ) ) {
293
  return ( is_array( $tax ) && array_intersect( $tax, $taxonomies ) || in_array( $tax, $taxonomies ) );
294
  }
295
 
296
+ /**
297
+ * Check if translated taxonomy is queried
298
+ * Compatible with nested queries introduced in WP 4.1
299
+ * @see https://wordpress.org/support/topic/tax_query-bug
300
+ *
301
+ * @since 1.7
302
+ *
303
+ * @param array $tax_queries
304
+ * @return bool
305
+ */
306
+ public function have_translated_taxonomy( $tax_queries ) {
307
+ foreach ( $tax_queries as $tax_query ) {
308
+ if ( isset( $tax_query['taxonomy'] ) && $this->is_translated_taxonomy( $tax_query['taxonomy'] ) && ! ( isset( $tax_query['operator'] ) && 'NOT IN' === $tax_query['operator'] ) ) {
309
+ return true;
310
+ }
311
+
312
+ // Nested queries
313
+ elseif ( is_array( $tax_query ) && $this->have_translated_taxonomy( $tax_query ) ) {
314
+ return true;
315
+ }
316
+ }
317
+
318
+ return false;
319
+ }
320
+
321
  /**
322
  * Return taxonomies that need to be filtered ( post_format like )
323
  *
480
  $counts = wp_cache_get( $cache_key, 'pll_count_posts' );
481
 
482
  if ( false === $counts ) {
483
+ $select = "SELECT pll_tr.term_taxonomy_id, COUNT( * ) AS num_posts FROM {$wpdb->posts}";
484
  $join = $this->post->join_clause();
485
  $where = " WHERE post_status = 'publish'";
486
+ $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_type IN ( '%s' )", join( "', '", $q['post_type'] ) );
487
  $where .= $this->post->where_clause( $this->get_languages_list() );
488
  $groupby = ' GROUP BY pll_tr.term_taxonomy_id';
489
 
490
  if ( ! empty( $q['m'] ) ) {
491
  $q['m'] = '' . preg_replace( '|[^0-9]|', '', $q['m'] );
492
+ $where .= $wpdb->prepare( " AND YEAR( {$wpdb->posts}.post_date ) = %d", substr( $q['m'], 0, 4 ) );
493
  if ( strlen( $q['m'] ) > 5 ) {
494
+ $where .= $wpdb->prepare( " AND MONTH( {$wpdb->posts}.post_date ) = %d", substr( $q['m'], 4, 2 ) );
495
  }
496
  if ( strlen( $q['m'] ) > 7 ) {
497
+ $where .= $wpdb->prepare( " AND DAYOFMONTH( {$wpdb->posts}.post_date ) = %d", substr( $q['m'], 6, 2 ) );
498
  }
499
  }
500
 
501
  if ( ! empty( $q['year'] ) ) {
502
+ $where .= $wpdb->prepare( " AND YEAR( {$wpdb->posts}.post_date ) = %d", $q['year'] );
503
  }
504
 
505
  if ( ! empty( $q['monthnum'] ) ) {
506
+ $where .= $wpdb->prepare( " AND MONTH( {$wpdb->posts}.post_date ) = %d", $q['monthnum'] );
507
  }
508
 
509
  if ( ! empty( $q['day'] ) ) {
510
+ $where .= $wpdb->prepare( " AND DAYOFMONTH( {$wpdb->posts}.post_date ) = %d", $q['day'] );
511
  }
512
 
513
  if ( ! empty( $q['author_name'] ) ) {
518
  }
519
 
520
  if ( ! empty( $q['author'] ) ) {
521
+ $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_author = %d", $q['author'] );
522
  }
523
 
524
  // filtered taxonomies ( post_format )
525
  foreach ( $this->get_filtered_taxonomies_query_vars() as $tax_qv ) {
526
 
527
  if ( ! empty( $q[ $tax_qv ] ) ) {
528
+ $join .= " INNER JOIN {$wpdb->term_relationships} AS tr ON tr.object_id = {$wpdb->posts}.ID";
529
  $join .= " INNER JOIN {$wpdb->term_taxonomy} AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id";
530
  $join .= " INNER JOIN {$wpdb->terms} AS t ON t.term_id = tt.term_id";
531
  $where .= $wpdb->prepare( ' AND t.slug = %s', $q[ $tax_qv ] );
include/nav-menu.php CHANGED
@@ -21,7 +21,7 @@ class PLL_Nav_Menu {
21
  $this->options = &$polylang->options;
22
 
23
  // integration with WP customizer
24
- add_action( 'customize_register', array( &$this, 'create_nav_menu_locations' ), 5 );
25
  }
26
 
27
  /**
21
  $this->options = &$polylang->options;
22
 
23
  // integration with WP customizer
24
+ add_action( 'customize_register', array( $this, 'create_nav_menu_locations' ), 5 );
25
  }
26
 
27
  /**
include/olt-manager.php CHANGED
@@ -25,17 +25,17 @@ class PLL_OLT_Manager {
25
  $this->default_locale = get_locale();
26
 
27
  // filters for text domain management
28
- add_filter( 'override_load_textdomain', array( &$this, 'mofile' ), 10, 3 );
29
- add_filter( 'gettext', array( &$this, 'gettext' ), 10, 3 );
30
- add_filter( 'gettext_with_context', array( &$this, 'gettext_with_context' ), 10, 4 );
31
 
32
  // loads text domains
33
- add_action( 'pll_language_defined', array( &$this, 'load_textdomains' ), 2 ); // after PLL_Frontend::pll_language_defined
34
- add_action( 'pll_no_language_defined', array( &$this, 'load_textdomains' ) );
35
 
36
  // allows Polylang to be the first plugin loaded ;- )
37
- add_filter( 'pre_update_option_active_plugins', array( &$this, 'make_polylang_first' ) );
38
- add_filter( 'pre_update_option_active_sitewide_plugins', array( &$this, 'make_polylang_first' ) );
39
  }
40
 
41
  /**
@@ -60,9 +60,9 @@ class PLL_OLT_Manager {
60
  */
61
  public function load_textdomains() {
62
  // our override_load_textdomain filter has done its job. let's remove it before calling load_textdomain
63
- remove_filter( 'override_load_textdomain', array( &$this, 'mofile' ), 10, 3 );
64
- remove_filter( 'gettext', array( &$this, 'gettext' ), 10, 3 );
65
- remove_filter( 'gettext_with_context', array( &$this, 'gettext_with_context' ), 10, 4 );
66
  $new_locale = get_locale();
67
 
68
  // don't try to save time for en_US as some users have theme written in another language
25
  $this->default_locale = get_locale();
26
 
27
  // filters for text domain management
28
+ add_filter( 'override_load_textdomain', array( $this, 'mofile' ), 10, 3 );
29
+ add_filter( 'gettext', array( $this, 'gettext' ), 10, 3 );
30
+ add_filter( 'gettext_with_context', array( $this, 'gettext_with_context' ), 10, 4 );
31
 
32
  // loads text domains
33
+ add_action( 'pll_language_defined', array( $this, 'load_textdomains' ), 2 ); // after PLL_Frontend::pll_language_defined
34
+ add_action( 'pll_no_language_defined', array( $this, 'load_textdomains' ) );
35
 
36
  // allows Polylang to be the first plugin loaded ;- )
37
+ add_filter( 'pre_update_option_active_plugins', array( $this, 'make_polylang_first' ) );
38
+ add_filter( 'pre_update_option_active_sitewide_plugins', array( $this, 'make_polylang_first' ) );
39
  }
40
 
41
  /**
60
  */
61
  public function load_textdomains() {
62
  // our override_load_textdomain filter has done its job. let's remove it before calling load_textdomain
63
+ remove_filter( 'override_load_textdomain', array( $this, 'mofile' ), 10, 3 );
64
+ remove_filter( 'gettext', array( $this, 'gettext' ), 10, 3 );
65
+ remove_filter( 'gettext_with_context', array( $this, 'gettext_with_context' ), 10, 4 );
66
  $new_locale = get_locale();
67
 
68
  // don't try to save time for en_US as some users have theme written in another language
include/pointer.php CHANGED
@@ -33,7 +33,7 @@ class PLL_Pointer {
33
  */
34
  public function __construct( $args ) {
35
  $this->args = $args;
36
- add_action( 'admin_enqueue_scripts', array( &$this, 'enqueue_scripts' ) );
37
  }
38
 
39
  /**
@@ -48,7 +48,7 @@ class PLL_Pointer {
48
  }
49
 
50
  // Add pointer javascript
51
- add_action( 'admin_print_footer_scripts', array( &$this, 'print_js' ) );
52
 
53
  wp_enqueue_style( 'wp-pointer' );
54
  wp_enqueue_script( 'wp-pointer' );
33
  */
34
  public function __construct( $args ) {
35
  $this->args = $args;
36
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
37
  }
38
 
39
  /**
48
  }
49
 
50
  // Add pointer javascript
51
+ add_action( 'admin_print_footer_scripts', array( $this, 'print_js' ) );
52
 
53
  wp_enqueue_style( 'wp-pointer' );
54
  wp_enqueue_script( 'wp-pointer' );
include/static-pages.php CHANGED
@@ -22,9 +22,12 @@ abstract class PLL_Static_Pages {
22
 
23
  $this->init();
24
 
 
 
 
25
  // clean the languages cache when editing page of front, page for posts
26
- add_action( 'update_option_page_on_front', array( &$this->model, 'clean_languages_cache' ) );
27
- add_action( 'update_option_page_for_posts', array( &$this->model, 'clean_languages_cache' ) );
28
 
29
  // refresh rewrite rules when the page on front is modified
30
  add_action( 'update_option_page_on_front', 'flush_rewrite_rules' );
@@ -47,6 +50,22 @@ abstract class PLL_Static_Pages {
47
  }
48
  }
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  /**
51
  * adds page_on_front and page_for_posts properties to the language objects
52
  *
22
 
23
  $this->init();
24
 
25
+ // Modifies the page link in case the front page is not in the default language
26
+ add_filter( 'page_link', array( $this, 'page_link' ), 20, 2 );
27
+
28
  // clean the languages cache when editing page of front, page for posts
29
+ add_action( 'update_option_page_on_front', array( $this->model, 'clean_languages_cache' ) );
30
+ add_action( 'update_option_page_for_posts', array( $this->model, 'clean_languages_cache' ) );
31
 
32
  // refresh rewrite rules when the page on front is modified
33
  add_action( 'update_option_page_on_front', 'flush_rewrite_rules' );
50
  }
51
  }
52
 
53
+ /**
54
+ * Modifies the page link in case the front page is not in the default language
55
+ *
56
+ * @since 0.7.2
57
+ *
58
+ * @param string $link link to the page
59
+ * @param int $id post id of the page
60
+ * @return string modified link
61
+ */
62
+ public function page_link( $link, $id ) {
63
+ if ( ( $lang = $this->model->post->get_language( $id ) ) && $id == $lang->page_on_front ) {
64
+ return $lang->home_url;
65
+ }
66
+ return $link;
67
+ }
68
+
69
  /**
70
  * adds page_on_front and page_for_posts properties to the language objects
71
  *
include/switcher.php CHANGED
@@ -1,14 +1,14 @@
1
  <?php
2
 
3
  /**
4
- * a class to display a language switcher on frontend
5
  *
6
  * @since 1.2
7
  */
8
  class PLL_Switcher {
9
 
10
  /**
11
- * returns options available for the language switcher - menu or widget
12
  * either strings to display the options or default values
13
  *
14
  * @since 0.7
@@ -19,6 +19,7 @@ class PLL_Switcher {
19
  */
20
  static public function get_switcher_options( $type = 'widget', $key = 'string' ) {
21
  $options = array(
 
22
  'show_names' => array( 'string' => __( 'Displays language names', 'polylang' ), 'default' => 1 ),
23
  'show_flags' => array( 'string' => __( 'Displays flags', 'polylang' ), 'default' => 0 ),
24
  'force_home' => array( 'string' => __( 'Forces link to front page', 'polylang' ), 'default' => 0 ),
@@ -26,17 +27,13 @@ class PLL_Switcher {
26
  'hide_if_no_translation' => array( 'string' => __( 'Hides languages with no translation', 'polylang' ), 'default' => 0 ),
27
  );
28
 
29
- if ( 'menu' != $type ) {
30
- $options = array( 'dropdown' => array( 'string' => __( 'Displays as dropdown', 'polylang' ), 'default' => 0 ) ) + $options;
31
- }
32
-
33
  return wp_list_pluck( $options, $key );
34
  }
35
 
36
  /**
37
- * get the language elements for use in a walker
38
  *
39
- * list of parameters accepted in $args:
40
  * @see PLL_Switcher::the_languages
41
  *
42
  * @since 1.2
@@ -46,16 +43,25 @@ class PLL_Switcher {
46
  * @return array
47
  */
48
  protected function get_elements( $links, $args ) {
 
 
 
49
  foreach ( $links->model->get_languages_list( array( 'hide_empty' => $args['hide_if_empty'] ) ) as $language ) {
50
  $id = (int) $language->term_id;
 
51
  $slug = $language->slug;
52
  $locale = $language->get_locale( 'display' );
53
  $classes = array( 'lang-item', 'lang-item-' . $id, 'lang-item-' . esc_attr( $slug ) );
54
- $url = null; // avoids potential notice
 
 
 
 
 
55
 
56
  if ( $current_lang = $links->curlang->slug == $slug ) {
57
- if ( $args['hide_current'] && ! $args['dropdown'] ) {
58
- continue; // hide current language except for dropdown
59
  }
60
  else {
61
  $classes[] = 'current-lang';
@@ -72,7 +78,7 @@ class PLL_Switcher {
72
  $classes[] = 'no-translation';
73
  }
74
 
75
- /*
76
  * Filter the link in the language switcher
77
  *
78
  * @since 0.7
@@ -83,27 +89,27 @@ class PLL_Switcher {
83
  */
84
  $url = apply_filters( 'pll_the_language_link', $url, $slug, $language->locale );
85
 
86
- // hide if no translation exists
87
  if ( empty( $url ) && $args['hide_if_no_translation'] ) {
88
  continue;
89
  }
90
 
91
- $url = empty( $url ) || $args['force_home'] ? $links->get_home_url( $language ) : $url ; // if the page is not translated, link to the home page
92
 
93
  $name = $args['show_names'] || ! $args['show_flags'] || $args['raw'] ? ( 'slug' == $args['display_names_as'] ? $slug : $language->name ) : '';
94
  $flag = $args['raw'] && ! $args['show_flags'] ? $language->flag_url : ( $args['show_flags'] ? $language->flag : '' );
95
 
96
- $out[ $slug ] = compact( 'id', 'slug', 'locale', 'name', 'url', 'flag', 'current_lang', 'no_translation', 'classes' );
97
  }
98
 
99
  return empty( $out ) ? array() : $out;
100
  }
101
 
102
  /**
103
- * displays a language switcher
104
  * or returns the raw elements to build a custom language switcher
105
  *
106
- * list of parameters accepted in $args:
107
  *
108
  * dropdown => the list is displayed as dropdown if set, defaults to 0
109
  * echo => echoes the list if set to 1, defaults to 1
@@ -140,7 +146,7 @@ class PLL_Switcher {
140
  );
141
  $args = wp_parse_args( $args, $defaults );
142
 
143
- /*
144
  * Filter the arguments of the 'pll_the_languages' template tag
145
  *
146
  * @since 1.5
@@ -149,7 +155,7 @@ class PLL_Switcher {
149
  */
150
  $args = apply_filters( 'pll_the_languages_args', $args );
151
 
152
- // prevents showing empty options in dropdown
153
  if ( $args['dropdown'] ) {
154
  $args['show_names'] = 1;
155
  }
@@ -169,7 +175,7 @@ class PLL_Switcher {
169
  $walker = new PLL_Walker_List();
170
  }
171
 
172
- /*
173
  * Filter the whole html markup returned by the 'pll_the_languages' template tag
174
  *
175
  * @since 0.8
@@ -179,13 +185,13 @@ class PLL_Switcher {
179
  */
180
  $out = apply_filters( 'pll_the_languages', $walker->walk( $elements, $args ), $args );
181
 
182
- // javascript to switch the language when using a dropdown list
183
  if ( $args['dropdown'] ) {
184
  foreach ( $links->model->get_languages_list() as $language ) {
185
  $urls[ $language->slug ] = $args['force_home'] || ( $url = $links->get_translation_url( $language ) ) === null ? $links->get_home_url( $language ) : $url;
186
  }
187
 
188
- // accept only few valid characters for the urls_x variable name ( as the widget id includes '-' which is invalid )
189
  $out .= sprintf( '
190
  <script type="text/javascript">
191
  //<![CDATA[
1
  <?php
2
 
3
  /**
4
+ * A class to display a language switcher on frontend
5
  *
6
  * @since 1.2
7
  */
8
  class PLL_Switcher {
9
 
10
  /**
11
+ * Returns options available for the language switcher - menu or widget
12
  * either strings to display the options or default values
13
  *
14
  * @since 0.7
19
  */
20
  static public function get_switcher_options( $type = 'widget', $key = 'string' ) {
21
  $options = array(
22
+ 'dropdown' => array( 'string' => __( 'Displays as dropdown', 'polylang' ), 'default' => 0 ),
23
  'show_names' => array( 'string' => __( 'Displays language names', 'polylang' ), 'default' => 1 ),
24
  'show_flags' => array( 'string' => __( 'Displays flags', 'polylang' ), 'default' => 0 ),
25
  'force_home' => array( 'string' => __( 'Forces link to front page', 'polylang' ), 'default' => 0 ),
27
  'hide_if_no_translation' => array( 'string' => __( 'Hides languages with no translation', 'polylang' ), 'default' => 0 ),
28
  );
29
 
 
 
 
 
30
  return wp_list_pluck( $options, $key );
31
  }
32
 
33
  /**
34
+ * Get the language elements for use in a walker
35
  *
36
+ * List of parameters accepted in $args:
37
  * @see PLL_Switcher::the_languages
38
  *
39
  * @since 1.2
43
  * @return array
44
  */
45
  protected function get_elements( $links, $args ) {
46
+
47
+ $first = true;
48
+
49
  foreach ( $links->model->get_languages_list( array( 'hide_empty' => $args['hide_if_empty'] ) ) as $language ) {
50
  $id = (int) $language->term_id;
51
+ $order = (int) $language->term_group;
52
  $slug = $language->slug;
53
  $locale = $language->get_locale( 'display' );
54
  $classes = array( 'lang-item', 'lang-item-' . $id, 'lang-item-' . esc_attr( $slug ) );
55
+ $url = null; // Avoids potential notice
56
+
57
+ if ( $first ) {
58
+ $classes[] = 'lang-item-first';
59
+ $first = false;
60
+ }
61
 
62
  if ( $current_lang = $links->curlang->slug == $slug ) {
63
+ if ( $args['hide_current'] && ! ( $args['dropdown'] && ! $args['raw'] ) ) {
64
+ continue; // Hide current language except for dropdown
65
  }
66
  else {
67
  $classes[] = 'current-lang';
78
  $classes[] = 'no-translation';
79
  }
80
 
81
+ /**
82
  * Filter the link in the language switcher
83
  *
84
  * @since 0.7
89
  */
90
  $url = apply_filters( 'pll_the_language_link', $url, $slug, $language->locale );
91
 
92
+ // Hide if no translation exists
93
  if ( empty( $url ) && $args['hide_if_no_translation'] ) {
94
  continue;
95
  }
96
 
97
+ $url = empty( $url ) || $args['force_home'] ? $links->get_home_url( $language ) : $url ; // If the page is not translated, link to the home page
98
 
99
  $name = $args['show_names'] || ! $args['show_flags'] || $args['raw'] ? ( 'slug' == $args['display_names_as'] ? $slug : $language->name ) : '';
100
  $flag = $args['raw'] && ! $args['show_flags'] ? $language->flag_url : ( $args['show_flags'] ? $language->flag : '' );
101
 
102
+ $out[ $slug ] = compact( 'id', 'order', 'slug', 'locale', 'name', 'url', 'flag', 'current_lang', 'no_translation', 'classes' );
103
  }
104
 
105
  return empty( $out ) ? array() : $out;
106
  }
107
 
108
  /**
109
+ * Displays a language switcher
110
  * or returns the raw elements to build a custom language switcher
111
  *
112
+ * List of parameters accepted in $args:
113
  *
114
  * dropdown => the list is displayed as dropdown if set, defaults to 0
115
  * echo => echoes the list if set to 1, defaults to 1
146
  );
147
  $args = wp_parse_args( $args, $defaults );
148
 
149
+ /**
150
  * Filter the arguments of the 'pll_the_languages' template tag
151
  *
152
  * @since 1.5
155
  */
156
  $args = apply_filters( 'pll_the_languages_args', $args );
157
 
158
+ // Prevents showing empty options in dropdown
159
  if ( $args['dropdown'] ) {
160
  $args['show_names'] = 1;
161
  }
175
  $walker = new PLL_Walker_List();
176
  }
177
 
178
+ /**
179
  * Filter the whole html markup returned by the 'pll_the_languages' template tag
180
  *
181
  * @since 0.8
185
  */
186
  $out = apply_filters( 'pll_the_languages', $walker->walk( $elements, $args ), $args );
187
 
188
+ // Javascript to switch the language when using a dropdown list
189
  if ( $args['dropdown'] ) {
190
  foreach ( $links->model->get_languages_list() as $language ) {
191
  $urls[ $language->slug ] = $args['force_home'] || ( $url = $links->get_translation_url( $language ) ) === null ? $links->get_home_url( $language ) : $url;
192
  }
193
 
194
+ // Accept only few valid characters for the urls_x variable name ( as the widget id includes '-' which is invalid )
195
  $out .= sprintf( '
196
  <script type="text/javascript">
197
  //<![CDATA[
include/translated-post.php CHANGED
@@ -24,13 +24,13 @@ class PLL_Translated_Post extends PLL_Translated_Object {
24
  parent::__construct( $model );
25
 
26
  // registers completely the language taxonomy
27
- add_action( 'setup_theme', array( &$this, 'register_taxonomy' ), 1 );
28
 
29
  // setups post types to translate
30
- add_action( 'registered_post_type', array( &$this, 'registered_post_type' ) );
31
 
32
  // forces updating posts cache
33
- add_action( 'pre_get_posts', array( &$this, 'pre_get_posts' ) );
34
  }
35
 
36
  /**
@@ -77,9 +77,12 @@ class PLL_Translated_Post extends PLL_Translated_Object {
77
  *
78
  * @return string join clause
79
  */
80
- public function join_clause() {
81
  global $wpdb;
82
- return " INNER JOIN $wpdb->term_relationships AS pll_tr ON pll_tr.object_id = ID";
 
 
 
83
  }
84
 
85
  /**
24
  parent::__construct( $model );
25
 
26
  // registers completely the language taxonomy
27
+ add_action( 'setup_theme', array( $this, 'register_taxonomy' ), 1 );
28
 
29
  // setups post types to translate
30
+ add_action( 'registered_post_type', array( $this, 'registered_post_type' ) );
31
 
32
  // forces updating posts cache
33
+ add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
34
  }
35
 
36
  /**
77
  *
78
  * @return string join clause
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";
86
  }
87
 
88
  /**
include/translated-term.php CHANGED
@@ -23,8 +23,10 @@ class PLL_Translated_Term extends PLL_Translated_Object {
23
  parent::__construct( $model );
24
 
25
  // filters to prime terms cache
26
- add_filter( 'get_terms', array( &$this, '_prime_terms_cache' ), 10, 2 );
27
- add_filter( 'wp_get_object_terms', array( &$this, 'wp_get_object_terms' ), 10, 3 );
 
 
28
  }
29
 
30
  /**
@@ -75,7 +77,7 @@ class PLL_Translated_Term extends PLL_Translated_Object {
75
 
76
  // get_term_by still not cached in WP 3.5.1 but internally, the function is always called by term_id
77
  elseif ( is_string( $value ) && $taxonomy ) {
78
- $term_id = get_term_by( 'slug', $value , $taxonomy )->term_id;
79
  }
80
 
81
  // get the language and make sure it is a PLL_Language object
@@ -136,7 +138,7 @@ class PLL_Translated_Term extends PLL_Translated_Object {
136
  * @return array unmodified $terms
137
  */
138
  public function _prime_terms_cache( $terms, $taxonomies ) {
139
- if ( $this->model->is_translated_taxonomy( $taxonomies ) ) {
140
  foreach ( $terms as $term ) {
141
  $term_ids[] = is_object( $term ) ? $term->term_id : (int) $term;
142
  }
@@ -165,4 +167,16 @@ class PLL_Translated_Term extends PLL_Translated_Object {
165
  }
166
  return $terms;
167
  }
 
 
 
 
 
 
 
 
 
 
 
 
168
  }
23
  parent::__construct( $model );
24
 
25
  // filters to prime terms cache
26
+ add_filter( 'get_terms', array( $this, '_prime_terms_cache' ), 10, 2 );
27
+ add_filter( 'wp_get_object_terms', array( $this, 'wp_get_object_terms' ), 10, 3 );
28
+
29
+ add_action( 'clean_term_cache', array( $this, 'clean_term_cache' ) );
30
  }
31
 
32
  /**
77
 
78
  // get_term_by still not cached in WP 3.5.1 but internally, the function is always called by term_id
79
  elseif ( is_string( $value ) && $taxonomy ) {
80
+ $term_id = wpcom_vip_get_term_by( 'slug', $value , $taxonomy )->term_id;
81
  }
82
 
83
  // get the language and make sure it is a PLL_Language object
138
  * @return array unmodified $terms
139
  */
140
  public function _prime_terms_cache( $terms, $taxonomies ) {
141
+ if ( is_array( $terms ) && $this->model->is_translated_taxonomy( $taxonomies ) ) {
142
  foreach ( $terms as $term ) {
143
  $term_ids[] = is_object( $term ) ? $term->term_id : (int) $term;
144
  }
167
  }
168
  return $terms;
169
  }
170
+
171
+ /**
172
+ * When the term cache is cleaned, clean the object term cache too
173
+ *
174
+ * @since 2.0
175
+ *
176
+ * @param array $ids An array of term IDs.
177
+ * @param string $taxonomy Taxonomy slug.
178
+ */
179
+ function clean_term_cache( $ids ) {
180
+ clean_object_term_cache( $ids, 'term' );
181
+ }
182
  }
include/walker-dropdown.php CHANGED
@@ -18,8 +18,9 @@ class PLL_Walker_Dropdown extends Walker {
18
  function start_el( &$output, $element, $depth = 0, $args = array(), $current_object_id = 0 ) {
19
  $value = $args['value'];
20
  $output .= sprintf(
21
- "\t".'<option value="%s"%s>%s</option>'."\n",
22
  esc_attr( $element->$value ),
 
23
  isset( $args['selected'] ) && $args['selected'] === $element->$value ? ' selected="selected"' : '',
24
  esc_html( $element->name )
25
  );
@@ -71,7 +72,7 @@ class PLL_Walker_Dropdown extends Walker {
71
  }
72
 
73
  $output .= sprintf(
74
- '<select name="%1$s" %2$s%3$s%4$s>' . "\n" . '%5$s' . "\n" . '</select>'."\n",
75
  $name = esc_attr( $args['name'] ),
76
  isset( $args['id'] ) && ! $args['id'] ? '' : ' id="' . ( empty( $args['id'] ) ? $name : esc_attr( $args['id'] ) ) . '"',
77
  empty( $args['class'] ) ? '' : ' class="' . esc_attr( $args['class'] ) . '"',
18
  function start_el( &$output, $element, $depth = 0, $args = array(), $current_object_id = 0 ) {
19
  $value = $args['value'];
20
  $output .= sprintf(
21
+ "\t".'<option value="%1$s"%2$s%3$s>%4$s</option>'."\n",
22
  esc_attr( $element->$value ),
23
+ empty( $element->locale ) ? '' : sprintf( ' lang="%s"', esc_attr( $element->locale ) ),
24
  isset( $args['selected'] ) && $args['selected'] === $element->$value ? ' selected="selected"' : '',
25
  esc_html( $element->name )
26
  );
72
  }
73
 
74
  $output .= sprintf(
75
+ '<select name="%1$s"%2$s%3$s%4$s>' . "\n" . '%5$s' . "\n" . '</select>'."\n",
76
  $name = esc_attr( $args['name'] ),
77
  isset( $args['id'] ) && ! $args['id'] ? '' : ' id="' . ( empty( $args['id'] ) ? $name : esc_attr( $args['id'] ) ) . '"',
78
  empty( $args['class'] ) ? '' : ' class="' . esc_attr( $args['class'] ) . '"',
include/walker-list.php CHANGED
@@ -17,7 +17,7 @@ class PLL_Walker_List extends Walker {
17
  */
18
  function start_el( &$output, $element, $depth = 0, $args = array(), $current_object_id = 0 ) {
19
  $output .= sprintf(
20
- "\t".'<li class="%s"><a hreflang="%s" href="%s">%s%s</a></li>'."\n",
21
  esc_attr( implode( ' ', $element->classes ) ),
22
  esc_attr( $element->locale ),
23
  esc_url( $element->url ),
17
  */
18
  function start_el( &$output, $element, $depth = 0, $args = array(), $current_object_id = 0 ) {
19
  $output .= sprintf(
20
+ "\t".'<li class="%1$s"><a lang="%2$s" hreflang="%2$s" href="%3$s">%4$s%5$s</a></li>'."\n",
21
  esc_attr( implode( ' ', $element->classes ) ),
22
  esc_attr( $element->locale ),
23
  esc_url( $element->url ),
include/widget-languages.php CHANGED
@@ -82,7 +82,7 @@ class PLL_Widget_Languages extends WP_Widget {
82
  printf(
83
  '<p><label for="%1$s">%2$s</label><input class="widefat" id="%1$s" name="%3$s" type="text" value="%4$s" /></p>',
84
  $this->get_field_id( 'title' ),
85
- __( 'Title:', 'polylang' ),
86
  $this->get_field_name( 'title' ),
87
  esc_attr( $instance['title'] )
88
  );
82
  printf(
83
  '<p><label for="%1$s">%2$s</label><input class="widefat" id="%1$s" name="%3$s" type="text" value="%4$s" /></p>',
84
  $this->get_field_id( 'title' ),
85
+ esc_html__( 'Title:', 'polylang' ),
86
  $this->get_field_name( 'title' ),
87
  esc_attr( $instance['title'] )
88
  );
install/install-base.php CHANGED
@@ -17,11 +17,11 @@ class PLL_Install_Base {
17
  $this->plugin_basename = $plugin_basename;
18
 
19
  // manages plugin activation and deactivation
20
- register_activation_hook( $plugin_basename, array( &$this, 'activate' ) );
21
- register_deactivation_hook( $plugin_basename, array( &$this, 'deactivate' ) );
22
 
23
  // blog creation on multisite
24
- add_action( 'wpmu_new_blog', array( &$this, 'wpmu_new_blog' ), 5 ); // before WP attempts to send mails which can break on some PHP versions
25
  }
26
 
27
  /**
17
  $this->plugin_basename = $plugin_basename;
18
 
19
  // manages plugin activation and deactivation
20
+ register_activation_hook( $plugin_basename, array( $this, 'activate' ) );
21
+ register_deactivation_hook( $plugin_basename, array( $this, 'deactivate' ) );
22
 
23
  // blog creation on multisite
24
+ add_action( 'wpmu_new_blog', array( $this, 'wpmu_new_blog' ), 5 ); // before WP attempts to send mails which can break on some PHP versions
25
  }
26
 
27
  /**
install/install.php CHANGED
@@ -22,7 +22,7 @@ class PLL_Install extends PLL_Install_Base {
22
  if ( version_compare( $wp_version, PLL_MIN_WP_VERSION , '<' ) ) {
23
  die( sprintf( '<p style = "font-family: sans-serif; font-size: 12px; color: #333; margin: -5px">%s</p>',
24
  /* translators: %s are WordPress version numbers */
25
- sprintf( __( 'You are using WordPress %s. Polylang requires at least WordPress %s.', 'polylang' ),
26
  esc_html( $wp_version ),
27
  PLL_MIN_WP_VERSION
28
  )
22
  if ( version_compare( $wp_version, PLL_MIN_WP_VERSION , '<' ) ) {
23
  die( sprintf( '<p style = "font-family: sans-serif; font-size: 12px; color: #333; margin: -5px">%s</p>',
24
  /* translators: %s are WordPress version numbers */
25
+ sprintf( esc_html__( 'You are using WordPress %s. Polylang requires at least WordPress %s.', 'polylang' ),
26
  esc_html( $wp_version ),
27
  PLL_MIN_WP_VERSION
28
  )
install/plugin-updater.php CHANGED
@@ -187,7 +187,7 @@ class PLL_Plugin_Updater {
187
  if ( empty( $version_info->download_link ) ) {
188
  printf(
189
  /* translators: %1$s plugin name, %3$s plugin version, %2$s and %4$s are html tags */
190
- __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s.', 'polylang' ),
191
  esc_html( $version_info->name ),
192
  '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
193
  esc_html( $version_info->new_version ),
@@ -196,7 +196,7 @@ class PLL_Plugin_Updater {
196
  } else {
197
  printf(
198
  /* translators: %1$s plugin name, %3$s plugin version, %2$s, %4$s, %5$s and %6$s are html tags */
199
- __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s or %5$supdate now%6$s.', 'polylang' ),
200
  esc_html( $version_info->name ),
201
  '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
202
  esc_html( $version_info->new_version ),
187
  if ( empty( $version_info->download_link ) ) {
188
  printf(
189
  /* translators: %1$s plugin name, %3$s plugin version, %2$s and %4$s are html tags */
190
+ esc_html__( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s.', 'polylang' ),
191
  esc_html( $version_info->name ),
192
  '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
193
  esc_html( $version_info->new_version ),
196
  } else {
197
  printf(
198
  /* translators: %1$s plugin name, %3$s plugin version, %2$s, %4$s, %5$s and %6$s are html tags */
199
+ esc_html__( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s or %5$supdate now%6$s.', 'polylang' ),
200
  esc_html( $version_info->name ),
201
  '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
202
  esc_html( $version_info->new_version ),
install/upgrade.php CHANGED
@@ -39,11 +39,11 @@ class PLL_Upgrade {
39
  */
40
  public function upgrade() {
41
  if ( ! $this->can_upgrade() ) {
42
- add_action( 'all_admin_notices', array( &$this, 'admin_notices' ) );
43
  return false;
44
  }
45
 
46
- add_action( 'admin_init', array( &$this, '_upgrade' ) );
47
  return true;
48
  }
49
 
@@ -71,10 +71,10 @@ class PLL_Upgrade {
71
  load_plugin_textdomain( 'polylang', false, basename( POLYLANG_DIR ) . '/languages' );
72
  printf(
73
  '<div class="error"><p>%s</p><p>%s</p></div>',
74
- __( 'Polylang has been deactivated because you upgraded from a too old version.', 'polylang' ),
75
  sprintf(
76
  /* translators: %s are Polylang version numbers */
77
- __( 'Please upgrade first to %s before ugrading to %s.', 'polylang' ),
78
  '<strong>0.9.8</strong>',
79
  POLYLANG_VERSION
80
  )
@@ -89,7 +89,7 @@ class PLL_Upgrade {
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' ) as $version ) {
91
  if ( version_compare( $this->options['version'], $version, '<' ) ) {
92
- call_user_func( array( &$this, 'upgrade_' . str_replace( '.', '_', $version ) ) );
93
  }
94
  }
95
 
39
  */
40
  public function upgrade() {
41
  if ( ! $this->can_upgrade() ) {
42
+ add_action( 'all_admin_notices', array( $this, 'admin_notices' ) );
43
  return false;
44
  }
45
 
46
+ add_action( 'admin_init', array( $this, '_upgrade' ) );
47
  return true;
48
  }
49
 
71
  load_plugin_textdomain( 'polylang', false, basename( POLYLANG_DIR ) . '/languages' );
72
  printf(
73
  '<div class="error"><p>%s</p><p>%s</p></div>',
74
+ esc_html__( 'Polylang has been deactivated because you upgraded from a too old version.', 'polylang' ),
75
  sprintf(
76
  /* translators: %s are Polylang version numbers */
77
+ esc_html__( 'Please upgrade first to %s before ugrading to %s.', 'polylang' ),
78
  '<strong>0.9.8</strong>',
79
  POLYLANG_VERSION
80
  )
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' ) as $version ) {
91
  if ( version_compare( $this->options['version'], $version, '<' ) ) {
92
+ call_user_func( array( $this, 'upgrade_' . str_replace( '.', '_', $version ) ) );
93
  }
94
  }
95
 
js/admin.js CHANGED
@@ -56,8 +56,8 @@ jQuery( document ).ready(function( $ ) {
56
  // languages form
57
  // fills the fields based on the language dropdown list choice
58
  $( '#lang_list' ).change(function() {
59
- value = $( this ).val().split( '-' );
60
- selected = $( "select option:selected" ).text().split( ' - ' );
61
  $( '#lang_slug' ).val( value[0] );
62
  $( '#lang_locale' ).val( value[1] );
63
  $( 'input[name="rtl"]' ).val( [value[2]] );
56
  // languages form
57
  // fills the fields based on the language dropdown list choice
58
  $( '#lang_list' ).change(function() {
59
+ var value = $( this ).val().split( ':' );
60
+ var selected = $( "select option:selected" ).text().split( ' - ' );
61
  $( '#lang_slug' ).val( value[0] );
62
  $( '#lang_locale' ).val( value[1] );
63
  $( 'input[name="rtl"]' ).val( [value[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(){value=e(this).val().split("-"),selected=e("select option:selected").text().split(" - "),e("#lang_slug").val(value[0]),e("#lang_locale").val(value[1]),e('input[name="rtl"]').val([value[2]]),e("#lang_name").val(selected[0]),e('#flag_list option[value="'+value[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(".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)})})});
js/nav-menu.js CHANGED
@@ -30,13 +30,13 @@ jQuery( document ).ready(function( $ ) {
30
  });
31
  $( this ).append( h );
32
 
33
- ids = Array( 'hide_if_no_translation', 'hide_current','force_home','show_flags','show_names' ); // reverse order
34
 
35
  // add the fields
36
  for ( var i = 0; i < ids.length; i++ ) {
37
  p = $( '<p>' ).attr( 'class', 'description' );
38
  $( this ).prepend( p );
39
- label = $( '<label>' ).attr( 'for', 'edit-menu-item-' + ids[ i ] + '-' + item ).text( ' ' + pll_data.strings[ i ] );
40
  p.append( label );
41
  cb = $( '<input>' ).attr({
42
  type: 'checkbox',
30
  });
31
  $( this ).append( h );
32
 
33
+ ids = Array( 'hide_if_no_translation', 'hide_current', 'force_home', 'show_flags', 'show_names', 'dropdown' ); // reverse order
34
 
35
  // add the fields
36
  for ( var i = 0; i < ids.length; i++ ) {
37
  p = $( '<p>' ).attr( 'class', 'description' );
38
  $( this ).prepend( p );
39
+ label = $( '<label>' ).attr( 'for', 'edit-menu-item-' + ids[ i ] + '-' + item ).text( ' ' + pll_data.strings[ ids[ i ] ] );
40
  p.append( label );
41
  cb = $( '<input>' ).attr({
42
  type: 'checkbox',
js/nav-menu.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(document).ready(function(e){e("#update-nav-menu").bind("click",function(t){t.target&&t.target.className&&-1!=t.target.className.indexOf("item-edit")&&(e("input[value='#pll_switcher'][type=text]").parent().parent().parent().each(function(){var t=e(this).attr("id").substring(19);e(this).children("p:not( .field-move )").remove(),h=e("<input>").attr({type:"hidden",id:"edit-menu-item-title-"+t,name:"menu-item-title["+t+"]",value:pll_data.title}),e(this).append(h),h=e("<input>").attr({type:"hidden",id:"edit-menu-item-url-"+t,name:"menu-item-url["+t+"]",value:"#pll_switcher"}),e(this).append(h),h=e("<input>").attr({type:"hidden",id:"edit-menu-item-pll-detect-"+t,name:"menu-item-pll-detect["+t+"]",value:1}),e(this).append(h),ids=Array("hide_if_no_translation","hide_current","force_home","show_flags","show_names");for(var i=0;i<ids.length;i++)p=e("<p>").attr("class","description"),e(this).prepend(p),label=e("<label>").attr("for","edit-menu-item-"+ids[i]+"-"+t).text(" "+pll_data.strings[i]),p.append(label),cb=e("<input>").attr({type:"checkbox",id:"edit-menu-item-"+ids[i]+"-"+t,name:"menu-item-"+ids[i]+"["+t+"]",value:1}),("undefined"!=typeof pll_data.val[t]&&1==pll_data.val[t][ids[i]]||"undefined"==typeof pll_data.val[t]&&"show_names"==ids[i])&&cb.prop("checked",!0),label.prepend(cb)}),e(".menu-item-data-object-id").each(function(){var t=e(this).val(),i=["names-","flags-"];e.each(i,function(a,n){e("#edit-menu-item-show_"+n+t).change(function(){"checked"!=e(this).attr("checked")&&e("#edit-menu-item-show_"+i[1-a]+t).prop("checked",!0)})})}))})});
1
+ jQuery(document).ready(function(a){a("#update-nav-menu").bind("click",function(b){if(b.target&&b.target.className&&-1!=b.target.className.indexOf("item-edit")){a("input[value='#pll_switcher'][type=text]").parent().parent().parent().each(function(){var d=a(this).attr("id").substring(19);a(this).children("p:not( .field-move )").remove();h=a("<input>").attr({type:"hidden",id:"edit-menu-item-title-"+d,name:"menu-item-title["+d+"]",value:pll_data.title});a(this).append(h);h=a("<input>").attr({type:"hidden",id:"edit-menu-item-url-"+d,name:"menu-item-url["+d+"]",value:"#pll_switcher"});a(this).append(h);h=a("<input>").attr({type:"hidden",id:"edit-menu-item-pll-detect-"+d,name:"menu-item-pll-detect["+d+"]",value:1});a(this).append(h);ids=Array("hide_if_no_translation","hide_current","force_home","show_flags","show_names","dropdown");for(var c=0;c<ids.length;c++){p=a("<p>").attr("class","description");a(this).prepend(p);label=a("<label>").attr("for","edit-menu-item-"+ids[c]+"-"+d).text(" "+pll_data.strings[ids[c]]);p.append(label);cb=a("<input>").attr({type:"checkbox",id:"edit-menu-item-"+ids[c]+"-"+d,name:"menu-item-"+ids[c]+"["+d+"]",value:1});if((typeof(pll_data.val[d])!="undefined"&&pll_data.val[d][ids[c]]==1)||(typeof(pll_data.val[d])=="undefined"&&ids[c]=="show_names")){cb.prop("checked",true)}label.prepend(cb)}});a(".menu-item-data-object-id").each(function(){var d=a(this).val();var c=["names-","flags-"];a.each(c,function(f,e){a("#edit-menu-item-show_"+e+d).change(function(){if("checked"!=a(this).attr("checked")){a("#edit-menu-item-show_"+c[1-f]+d).prop("checked",true)}})})})}})});
js/post.js CHANGED
@@ -191,6 +191,12 @@ jQuery( document ).ready(function( $ ) {
191
  case 'flag': // flag in front of the select dropdown
192
  $( '.pll-select-flag' ).html( this.data );
193
  break;
 
 
 
 
 
 
194
  }
195
  });
196
 
191
  case 'flag': // flag in front of the select dropdown
192
  $( '.pll-select-flag' ).html( this.data );
193
  break;
194
+ case 'permalink': // Sample permalink
195
+ var div = $( '#edit-slug-box' );
196
+ if ( '-1' != this.data && div.children().length ) {
197
+ div.html( this.data );
198
+ }
199
+ break;
200
  }
201
  });
202
 
js/post.min.js CHANGED
@@ -1 +1 @@
1
- !function(t){t.ajaxPrefilter(function(a){-1!==a.url.indexOf("action=ajax-tag-search")&&((lang=t(".post_lang_choice").val())||(lang=t(':input[name="inline_lang_choice"]').val()))&&(a.data="lang="+lang+"&"+a.data)})}(jQuery),function(t){tagBox.get=function(a){var n=a.substr(a.indexOf("-")+1),e={action:"get-tagcloud",lang:t(".post_lang_choice").val(),tax:n};t.post(ajaxurl,e,function(e,i){0!=e&&"success"==i||(e=wpAjax.broken),e=t('<p id="tagcloud-'+n+'" class="the-tagcloud">'+e+"</p>"),t("a",e).click(function(){return tagBox.flushTags(t(this).closest(".inside").children(".tagsdiv"),this),!1}),(v=t(".the-tagcloud").css("display"))?(t(".the-tagcloud").replaceWith(e),t(".the-tagcloud").css("display",v)):t("#"+a).after(e)})}}(jQuery),function(t){t(document).bind("DOMNodeInserted",function(a){function n(a){"undefined"!=typeof pll_term_languages&&t.each(pll_term_languages,function(n,e){t.each(e,function(e,i){t.each(i,function(i){id="#"+e+"-"+pll_term_languages[n][e][i],a==n?t(id).show():t(id).hide()})})})}function e(a){"undefined"!=typeof pll_page_languages&&t.each(pll_page_languages,function(n,e){t.each(e,function(e){v=t('#post_parent option[value="'+pll_page_languages[n][e]+'"]'),a==n?v.show():v.hide()})})}var i=t(a.target);if("inline-edit"==i.attr("id")){var l=i.prev().attr("id").replace("post-","");if(l>0){var s=i.find(':input[name="inline_lang_choice"]'),o=t("#lang_"+l).html();s.val(o),n(o),e(o),s.change(function(){n(t(this).val()),e(t(this).val())})}}})}(jQuery),function(t){t(document).ajaxSuccess(function(a,n,e){function i(a){var n=new Array;t(".translation_"+a).each(function(){n.push(t(this).parent().parent().attr("id").substring(5))});var e={action:"pll_update_post_rows",post_id:a,translations:n.join(","),post_type:t("input[name='post_type']").val(),screen:t("input[name='screen']").val(),_pll_nonce:t("input[name='_inline_edit']").val()};t.post(ajaxurl,e,function(a){if(a){var n=wpAjax.parseAjaxResponse(a,"ajax-response");t.each(n.responses,function(){"row"==this.what&&t("#post-"+this.supplemental.post_id).replaceWith(this.data)})}})}var l=wpAjax.unserialize(e.data);"undefined"!=typeof l.action&&"inline-save"==l.action&&i(l.post_ID)})}(jQuery),jQuery(document).ready(function(t){function a(){t(".tr_lang").each(function(){var a=t(this).attr("id").substring(8),n=t(this).parent().siblings(".pll-edit-column");t(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_posts_not_translated&post_language="+t(".post_lang_choice").val()+"&translation_language="+a+"&post_type="+t("#post_type").val()+"&_pll_nonce="+t("#_pll_nonce").val(),select:function(e,i){t("#htr_lang_"+a).val(i.item.id),n.html(i.item.link)}}),t(this).blur(function(){t(this).val()||(t("#htr_lang_"+a).val(0),n.html(n.siblings(".hidden").children().clone()))})})}var n=new Array;t(".categorydiv").each(function(){var a,e,i=t(this).attr("id");a=i.split("-"),a.shift(),e=a.join("-"),n.push(e),t("#"+e+"-add-submit").before(t("<input />").attr("type","hidden").attr("id",e+"-lang").attr("name","term_lang_choice").attr("value",t(".post_lang_choice").val()))}),t(".post_lang_choice").change(function(){var e={action:"post_lang_choice",lang:t(this).val(),post_type:t("#post_type").val(),taxonomies:n,post_id:t("#post_ID").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,e,function(n){var e=wpAjax.parseAjaxResponse(n,"ajax-response");t.each(e.responses,function(){switch(this.what){case"translations":t(".translations").html(this.data),a();break;case"taxonomy":var n=this.data;t("#"+n+"checklist").html(this.supplemental.all),t("#"+n+"checklist-pop").html(this.supplemental.populars),t("#new"+n+"_parent").replaceWith(this.supplemental.dropdown),t("#"+n+"-lang").val(t(".post_lang_choice").val());break;case"pages":t("#parent_id").html(this.data);break;case"flag":t(".pll-select-flag").html(this.data)}}),t(".tagcloud-link").each(function(){var a=t(this).attr("id");tagBox.get(a)})})}),a()});
1
+ !function(a){a.ajaxPrefilter(function(t,n,e){-1!==t.url.indexOf("action=ajax-tag-search")&&((lang=a(".post_lang_choice").val())||(lang=a(':input[name="inline_lang_choice"]').val()))&&(t.data="lang="+lang+"&"+t.data)})}(jQuery),function(a){tagBox.get=function(t){var n=t.substr(t.indexOf("-")+1),e={action:"get-tagcloud",lang:a(".post_lang_choice").val(),tax:n};a.post(ajaxurl,e,function(e,i){(0==e||"success"!=i)&&(e=wpAjax.broken),e=a('<p id="tagcloud-'+n+'" class="the-tagcloud">'+e+"</p>"),a("a",e).click(function(){return tagBox.flushTags(a(this).closest(".inside").children(".tagsdiv"),this),!1}),(v=a(".the-tagcloud").css("display"))?(a(".the-tagcloud").replaceWith(e),a(".the-tagcloud").css("display",v)):a("#"+t).after(e)})}}(jQuery),function(a){a(document).bind("DOMNodeInserted",function(t){function n(t){"undefined"!=typeof pll_term_languages&&a.each(pll_term_languages,function(n,e){a.each(e,function(e,i){a.each(i,function(i){id="#"+e+"-"+pll_term_languages[n][e][i],t==n?a(id).show():a(id).hide()})})})}function e(t){"undefined"!=typeof pll_page_languages&&a.each(pll_page_languages,function(n,e){a.each(e,function(e){v=a('#post_parent option[value="'+pll_page_languages[n][e]+'"]'),t==n?v.show():v.hide()})})}var i=a(t.target);if("inline-edit"==i.attr("id")){var l=i.prev().attr("id").replace("post-","");if(l>0){var s=i.find(':input[name="inline_lang_choice"]'),o=a("#lang_"+l).html();s.val(o),n(o),e(o),s.change(function(){n(a(this).val()),e(a(this).val())})}}})}(jQuery),function(a){a(document).ajaxSuccess(function(t,n,e){function i(t){var n=new Array;a(".translation_"+t).each(function(){n.push(a(this).parent().parent().attr("id").substring(5))});var e={action:"pll_update_post_rows",post_id:t,translations:n.join(","),post_type:a("input[name='post_type']").val(),screen:a("input[name='screen']").val(),_pll_nonce:a("input[name='_inline_edit']").val()};a.post(ajaxurl,e,function(t){if(t){var n=wpAjax.parseAjaxResponse(t,"ajax-response");a.each(n.responses,function(){"row"==this.what&&a("#post-"+this.supplemental.post_id).replaceWith(this.data)})}})}var l=wpAjax.unserialize(e.data);"undefined"!=typeof l.action&&"inline-save"==l.action&&i(l.post_ID)})}(jQuery),jQuery(document).ready(function(a){function t(){a(".tr_lang").each(function(){var t=a(this).attr("id").substring(8),n=a(this).parent().siblings(".pll-edit-column");a(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_posts_not_translated&post_language="+a(".post_lang_choice").val()+"&translation_language="+t+"&post_type="+a("#post_type").val()+"&_pll_nonce="+a("#_pll_nonce").val(),select:function(e,i){a("#htr_lang_"+t).val(i.item.id),n.html(i.item.link)}}),a(this).blur(function(){a(this).val()||(a("#htr_lang_"+t).val(0),n.html(n.siblings(".hidden").children().clone()))})})}var n=new Array;a(".categorydiv").each(function(){var t,e,i=a(this).attr("id");t=i.split("-"),t.shift(),e=t.join("-"),n.push(e),a("#"+e+"-add-submit").before(a("<input />").attr("type","hidden").attr("id",e+"-lang").attr("name","term_lang_choice").attr("value",a(".post_lang_choice").val()))}),a(".post_lang_choice").change(function(){var e={action:"post_lang_choice",lang:a(this).val(),post_type:a("#post_type").val(),taxonomies:n,post_id:a("#post_ID").val(),_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,e,function(n){var e=wpAjax.parseAjaxResponse(n,"ajax-response");a.each(e.responses,function(){switch(this.what){case"translations":a(".translations").html(this.data),t();break;case"taxonomy":var n=this.data;a("#"+n+"checklist").html(this.supplemental.all),a("#"+n+"checklist-pop").html(this.supplemental.populars),a("#new"+n+"_parent").replaceWith(this.supplemental.dropdown),a("#"+n+"-lang").val(a(".post_lang_choice").val());break;case"pages":a("#parent_id").html(this.data);break;case"flag":a(".pll-select-flag").html(this.data);break;case"permalink":var e=a("#edit-slug-box");"-1"!=this.data&&e.children().length&&e.html(this.data)}}),a(".tagcloud-link").each(function(){var t=a(this).attr("id");tagBox.get(t)})})}),t()});
js/term.js CHANGED
@@ -40,6 +40,7 @@
40
  term_id: term_id,
41
  translations: translations.join( ',' ),
42
  taxonomy: $( "input[name='taxonomy']" ).val(),
 
43
  screen: $( "input[name='screen']" ).val(),
44
  _pll_nonce: $( '#_pll_nonce' ).val()
45
  }
40
  term_id: term_id,
41
  translations: translations.join( ',' ),
42
  taxonomy: $( "input[name='taxonomy']" ).val(),
43
+ post_type: $( "input[name='post_type']" ).val(),
44
  screen: $( "input[name='screen']" ).val(),
45
  _pll_nonce: $( '#_pll_nonce' ).val()
46
  }
js/term.min.js CHANGED
@@ -1 +1 @@
1
- !function(a){a(document).bind("DOMNodeInserted",function(t){var n=a(t.target);if("inline-edit"==n.attr("id")){var e=n.prev().attr("id").replace("tag-","");if(e>0){var i=n.find(':input[name="inline_lang_choice"]'),l=a("#lang_"+e).html();i.val(l);var s=a("#default_cat_"+e).html();e==s&&i.prop("disabled",!0)}}})}(jQuery),function(a){a(document).ajaxSuccess(function(t,n,e){function i(t){var n=new Array;a(".translation_"+t).each(function(){n.push(a(this).parent().parent().attr("id").substring(4))});var e={action:"pll_update_term_rows",term_id:t,translations:n.join(","),taxonomy:a("input[name='taxonomy']").val(),screen:a("input[name='screen']").val(),_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,e,function(t){if(t){var n=wpAjax.parseAjaxResponse(t,"ajax-response");a.each(n.responses,function(){"row"==this.what&&a("#tag-"+this.supplemental.term_id).replaceWith(this.data)})}})}var l=wpAjax.unserialize(e.data);if("undefined"!=typeof l.action)switch(l.action){case"add-tag":res=wpAjax.parseAjaxResponse(n.responseXML,"ajax-response"),a.each(res.responses,function(){"term"==this.what&&i(this.supplemental.term_id)}),a(".htr_lang").val(0);break;case"delete-tag":i(l.tag_ID);break;case"inline-save-tax":i(l.tax_ID)}})}(jQuery),jQuery(document).ready(function(a){function t(){a(".tr_lang").each(function(){var t=a(this).attr("id").substring(8),n=a(this).parent().siblings(".pll-edit-column");a(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_terms_not_translated&term_language="+a("#term_lang_choice").val()+"&term_id="+a("input[name='tag_ID']").val()+"&taxonomy="+a("input[name='taxonomy']").val()+"&translation_language="+t+"&post_type="+typenow+"&_pll_nonce="+a("#_pll_nonce").val(),select:function(e,i){a("#htr_lang_"+t).val(i.item.id),n.html(i.item.link)}}),a(this).blur(function(){a(this).val()||(a("#htr_lang_"+t).val(0),n.html(n.siblings(".hidden").children().clone()))})})}t(),a("#term_lang_choice").change(function(){var n={action:"term_lang_choice",lang:a(this).val(),from_tag:a("input[name='from_tag']").val(),term_id:a("input[name='tag_ID']").val(),taxonomy:a("input[name='taxonomy']").val(),post_type:typenow,_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,n,function(n){var e=wpAjax.parseAjaxResponse(n,"ajax-response");a.each(e.responses,function(){switch(this.what){case"translations":a("#term-translations").html(this.data),t();break;case"parent":a("#parent").replaceWith(this.data);break;case"tag_cloud":a(".tagcloud").replaceWith(this.data);break;case"flag":a(".pll-select-flag").html(this.data)}})})})});
1
+ !function(a){a(document).bind("DOMNodeInserted",function(t){var e=a(t.target);if("inline-edit"==e.attr("id")){var n=e.prev().attr("id").replace("tag-","");if(n>0){var i=e.find(':input[name="inline_lang_choice"]'),l=a("#lang_"+n).html();i.val(l);var s=a("#default_cat_"+n).html();n==s&&i.prop("disabled",!0)}}})}(jQuery),function(a){a(document).ajaxSuccess(function(t,e,n){function i(t){var e=new Array;a(".translation_"+t).each(function(){e.push(a(this).parent().parent().attr("id").substring(4))});var n={action:"pll_update_term_rows",term_id:t,translations:e.join(","),taxonomy:a("input[name='taxonomy']").val(),post_type:a("input[name='post_type']").val(),screen:a("input[name='screen']").val(),_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,n,function(t){if(t){var e=wpAjax.parseAjaxResponse(t,"ajax-response");a.each(e.responses,function(){"row"==this.what&&a("#tag-"+this.supplemental.term_id).replaceWith(this.data)})}})}var l=wpAjax.unserialize(n.data);if("undefined"!=typeof l.action)switch(l.action){case"add-tag":res=wpAjax.parseAjaxResponse(e.responseXML,"ajax-response"),a.each(res.responses,function(){"term"==this.what&&i(this.supplemental.term_id)}),a(".htr_lang").val(0);break;case"delete-tag":i(l.tag_ID);break;case"inline-save-tax":i(l.tax_ID)}})}(jQuery),jQuery(document).ready(function(a){function t(){a(".tr_lang").each(function(){var t=a(this).attr("id").substring(8),e=a(this).parent().siblings(".pll-edit-column");a(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_terms_not_translated&term_language="+a("#term_lang_choice").val()+"&term_id="+a("input[name='tag_ID']").val()+"&taxonomy="+a("input[name='taxonomy']").val()+"&translation_language="+t+"&post_type="+typenow+"&_pll_nonce="+a("#_pll_nonce").val(),select:function(n,i){a("#htr_lang_"+t).val(i.item.id),e.html(i.item.link)}}),a(this).blur(function(){a(this).val()||(a("#htr_lang_"+t).val(0),e.html(e.siblings(".hidden").children().clone()))})})}t(),a("#term_lang_choice").change(function(){var e={action:"term_lang_choice",lang:a(this).val(),from_tag:a("input[name='from_tag']").val(),term_id:a("input[name='tag_ID']").val(),taxonomy:a("input[name='taxonomy']").val(),post_type:typenow,_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,e,function(e){var n=wpAjax.parseAjaxResponse(e,"ajax-response");a.each(n.responses,function(){switch(this.what){case"translations":a("#term-translations").html(this.data),t();break;case"parent":a("#parent").replaceWith(this.data);break;case"tag_cloud":a(".tagcloud").replaceWith(this.data);break;case"flag":a(".pll-select-flag").html(this.data)}})})})});
lingotek/lingotek.php CHANGED
@@ -1,18 +1,18 @@
1
  <?php
2
 
3
  if ( ! defined( 'ABSPATH' ) ) {
4
- exit; // don't access directly
5
  };
6
 
7
- /*
8
- * class to manage Lingotek ads
9
  *
10
  * @since 1.7.7
11
  */
12
  class PLL_Lingotek {
13
  const LINGOTEK = 'lingotek-translation/lingotek.php';
14
 
15
- /*
16
  * Init
17
  *
18
  * @since 1.7.7
@@ -20,15 +20,15 @@ class PLL_Lingotek {
20
  public function init() {
21
  $options = get_option( 'polylang' );
22
 
23
- // the Lingotek tab
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
 
31
- // the pointer
32
  $content = __( 'You’ve just upgraded to the latest version of Polylang! Would you like to automatically translate your website for free?', 'polylang' );
33
 
34
  $buttons = array(
@@ -66,8 +66,8 @@ class PLL_Lingotek {
66
  new PLL_Pointer( $args );
67
  }
68
 
69
- /*
70
- * adds the Lingotek tab in Polylang settings
71
  *
72
  * @since 1.7.7
73
  *
@@ -79,8 +79,8 @@ class PLL_Lingotek {
79
  return $tabs;
80
  }
81
 
82
- /*
83
- * displays the content in the Lingotek tab
84
  *
85
  * @since 1.7.7
86
  */
@@ -161,8 +161,8 @@ class PLL_Lingotek {
161
 
162
  }
163
 
164
- /*
165
- * styles the content of the Lingotek tab
166
  *
167
  * @since 1.7.7
168
  */
@@ -224,16 +224,16 @@ class PLL_Lingotek {
224
  </style><?php
225
  }
226
 
227
- /*
228
- * outputs the content of each box
229
  *
230
  * @since 1.7.7
231
  *
232
  * @param string $title
233
  * @param string $desc
234
- * @param array $list
235
- * @param array $links
236
- * @param $img string
237
  */
238
  protected function box( $title, $desc, $list, $links, $img ) {?>
239
  <div class="ltk-feature">
@@ -265,8 +265,8 @@ class PLL_Lingotek {
265
  </div><?php
266
  }
267
 
268
- /*
269
- * get a link to install / activate Lingotek
270
  * depending on user rights and if plugin is already installed
271
  *
272
  * @since 1.7.7
1
  <?php
2
 
3
  if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Don't access directly
5
  };
6
 
7
+ /**
8
+ * Class to manage Lingotek ads
9
  *
10
  * @since 1.7.7
11
  */
12
  class PLL_Lingotek {
13
  const LINGOTEK = 'lingotek-translation/lingotek.php';
14
 
15
+ /**
16
  * Init
17
  *
18
  * @since 1.7.7
20
  public function init() {
21
  $options = get_option( 'polylang' );
22
 
23
+ // The Lingotek tab
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
 
31
+ // The pointer
32
  $content = __( 'You’ve just upgraded to the latest version of Polylang! Would you like to automatically translate your website for free?', 'polylang' );
33
 
34
  $buttons = array(
66
  new PLL_Pointer( $args );
67
  }
68
 
69
+ /**
70
+ * Adds the Lingotek tab in Polylang settings
71
  *
72
  * @since 1.7.7
73
  *
79
  return $tabs;
80
  }
81
 
82
+ /**
83
+ * Displays the content in the Lingotek tab
84
  *
85
  * @since 1.7.7
86
  */
161
 
162
  }
163
 
164
+ /**
165
+ * Styles the content of the Lingotek tab
166
  *
167
  * @since 1.7.7
168
  */
224
  </style><?php
225
  }
226
 
227
+ /**
228
+ * Outputs the content of each box
229
  *
230
  * @since 1.7.7
231
  *
232
  * @param string $title
233
  * @param string $desc
234
+ * @param array $list
235
+ * @param array $links
236
+ * @param string $img
237
  */
238
  protected function box( $title, $desc, $list, $links, $img ) {?>
239
  <div class="ltk-feature">
265
  </div><?php
266
  }
267
 
268
+ /**
269
+ * Get a link to install / activate Lingotek
270
  * depending on user rights and if plugin is already installed
271
  *
272
  * @since 1.7.7
modules/plugins/plugins-compat.php CHANGED
@@ -16,40 +16,43 @@ class PLL_Plugins_Compat {
16
  */
17
  protected function __construct() {
18
  // WordPress Importer
19
- add_action( 'init', array( &$this, 'maybe_wordpress_importer' ) );
20
- add_filter( 'wp_import_terms', array( &$this, 'wp_import_terms' ) );
21
 
22
  // YARPP
23
- add_action( 'init', array( &$this, 'yarpp_init' ) ); // after Polylang has registered its taxonomy in setup_theme
24
 
25
  // Yoast SEO
26
- add_action( 'pll_language_defined', array( &$this, 'wpseo_init' ) );
27
 
28
  // Custom field template
29
- add_action( 'add_meta_boxes', array( &$this, 'cft_copy' ), 10, 2 );
30
 
31
  // Aqua Resizer
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' ) );
47
- add_filter( 'jetpack_relatedposts_filter_filters', array( &$this, 'jetpack_relatedposts_filter_filters' ), 10, 2 );
48
 
49
  // Jetpack infinite scroll
50
  if ( ! defined( 'PLL_AJAX_ON_FRONT' ) && isset( $_GET['infinity'], $_POST['action'] ) && 'infinite_scroll' == $_POST['action'] ) {
51
  define( 'PLL_AJAX_ON_FRONT', true );
52
  }
 
 
 
53
  }
54
 
55
  /**
@@ -76,7 +79,7 @@ class PLL_Plugins_Compat {
76
  function maybe_wordpress_importer() {
77
  if ( defined( 'WP_LOAD_IMPORTERS' ) && class_exists( 'WP_Import' ) ) {
78
  remove_action( 'admin_init', 'wordpress_importer_init' );
79
- add_action( 'admin_init', array( &$this, 'wordpress_importer_init' ) );
80
  }
81
  }
82
 
@@ -136,10 +139,17 @@ class PLL_Plugins_Compat {
136
  * @since 1.6.4
137
  */
138
  public function wpseo_init() {
139
- if ( ! defined( 'WPSEO_VERSION' ) || PLL_ADMIN ) {
 
 
 
 
 
140
  return;
141
  }
142
 
 
 
143
  // Reloads options once the language has been defined to enable translations
144
  // Useful only when the language is set from content
145
  if ( did_action( 'wp_loaded' ) ) {
@@ -155,30 +165,91 @@ class PLL_Plugins_Compat {
155
  }
156
  }
157
 
158
- // One sitemap per language when using multiple domains or subdomains
 
159
  // because WPSEO does not accept several domains or subdomains in one sitemap
160
- if ( PLL()->options['force_lang'] > 1 ) {
161
- add_filter( 'wpseo_enable_xml_sitemap_transient_caching', '__return_false' ); // disable cache! otherwise WPSEO keeps only one domain (thanks to Junaid Bhura)
162
- add_filter( 'home_url', array( &$this, 'wpseo_home_url' ) , 10, 2 ); // fix home_url
163
- add_filter( 'wpseo_posts_join', array( &$this, 'wpseo_posts_join' ), 10, 2 );
164
- add_filter( 'wpseo_posts_where', array( &$this, 'wpseo_posts_where' ), 10, 2 );
165
- add_filter( 'wpseo_typecount_join', array( &$this, 'wpseo_posts_join' ), 10, 2 );
166
- add_filter( 'wpseo_typecount_where', array( &$this, 'wpseo_posts_where' ), 10, 2 );
167
- }
168
 
169
- // One sitemap for all languages when the language is set from the content or directory name
170
- else {
171
- add_filter( 'get_terms_args', array( &$this, 'wpseo_remove_terms_filter' ) );
 
 
 
172
 
173
  // Add the homepages for all languages to the sitemap when the front page displays posts
174
  if ( ! get_option( 'page_on_front' ) ) {
175
- add_filter( 'wpseo_sitemap_post_content', array( &$this, 'add_language_home_urls' ) );
176
  }
177
  }
178
 
179
- add_filter( 'pll_home_url_white_list', array( &$this, 'wpseo_home_url_white_list' ) );
180
- add_action( 'wpseo_opengraph', array( &$this, 'wpseo_ogp' ), 2 );
181
- add_filter( 'wpseo_canonical', array( &$this, 'wpseo_canonical' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  }
183
 
184
  /**
@@ -201,10 +272,26 @@ class PLL_Plugins_Compat {
201
  return $url;
202
  }
203
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  /**
205
  * Yoast SEO
206
  * Modifies the sql request for posts sitemaps
207
- * Only when using multiple domains or subdomains
208
  *
209
  * @since 1.6.4
210
  *
@@ -213,13 +300,13 @@ class PLL_Plugins_Compat {
213
  * @return string
214
  */
215
  public function wpseo_posts_join( $sql, $post_type ) {
216
- return pll_is_translated_post_type( $post_type ) ? $sql. PLL()->model->post->join_clause() : $sql;
217
  }
218
 
219
  /**
220
  * Yoast SEO
221
  * Modifies the sql request for posts sitemaps
222
- * Only when using multiple domains or subdomains
223
  *
224
  * @since 1.6.4
225
  *
@@ -228,12 +315,21 @@ class PLL_Plugins_Compat {
228
  * @return string
229
  */
230
  public function wpseo_posts_where( $sql, $post_type ) {
231
- return pll_is_translated_post_type( $post_type ) ? $sql . PLL()->model->post->where_clause( PLL()->curlang ) : $sql;
 
 
 
 
 
 
 
 
 
232
  }
233
 
234
  /**
235
  * Yoast SEO
236
- * Removes the language filter for the taxonomy sitemaps
237
  * Only when the language is set from the content or directory name
238
  *
239
  * @since 1.0.3
@@ -243,14 +339,14 @@ class PLL_Plugins_Compat {
243
  */
244
  public function wpseo_remove_terms_filter( $args ) {
245
  if ( isset( $GLOBALS['wp_query']->query['sitemap'] ) ) {
246
- $args['lang'] = 0;
247
  }
248
  return $args;
249
  }
250
 
251
  /**
252
  * Yoast SEO
253
- * Adds the home urls for all languages to the sitemap
254
  *
255
  * @since 1.9
256
  *
@@ -260,7 +356,9 @@ class PLL_Plugins_Compat {
260
  public function add_language_home_urls( $str ) {
261
  global $wpseo_sitemaps;
262
 
263
- foreach ( pll_languages_list() as $lang ) {
 
 
264
  if ( empty( PLL()->options['hide_default'] ) || pll_default_language() !== $lang ) {
265
  $str .= $wpseo_sitemaps->sitemap_url( array(
266
  'loc' => pll_home_url( $lang ),
@@ -360,7 +458,7 @@ class PLL_Plugins_Compat {
360
 
361
  $settings = Featured_Content::get_setting();
362
 
363
- if ( ! $term = get_term_by( 'name', $settings['tag-name'], 'post_tag' ) ) {
364
  return $featured_ids;
365
  }
366
 
@@ -402,7 +500,7 @@ class PLL_Plugins_Compat {
402
  * @return array modified $settings
403
  */
404
  public function twenty_fourteen_option_featured_content( $settings ) {
405
- if ( ! PLL_ADMIN && $settings['tag-id'] && $tr = pll_get_term( $settings['tag-id'] ) ) {
406
  $settings['tag-id'] = $tr;
407
  }
408
 
@@ -496,6 +594,19 @@ class PLL_Plugins_Compat {
496
  return $filters;
497
  }
498
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
  /**
500
  * Correspondance between WordPress locales and Facebook locales
501
  * @see https://translate.wordpress.org/
16
  */
17
  protected function __construct() {
18
  // WordPress Importer
19
+ add_action( 'init', array( $this, 'maybe_wordpress_importer' ) );
20
+ add_filter( 'wp_import_terms', array( $this, 'wp_import_terms' ) );
21
 
22
  // YARPP
23
+ add_action( 'init', array( $this, 'yarpp_init' ) ); // after Polylang has registered its taxonomy in setup_theme
24
 
25
  // Yoast SEO
26
+ add_action( 'pll_language_defined', array( $this, 'wpseo_init' ) );
27
 
28
  // Custom field template
29
+ add_action( 'add_meta_boxes', array( $this, 'cft_copy' ), 10, 2 );
30
 
31
  // Aqua Resizer
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' ) );
47
+ add_filter( 'jetpack_relatedposts_filter_filters', array( $this, 'jetpack_relatedposts_filter_filters' ), 10, 2 );
48
 
49
  // Jetpack infinite scroll
50
  if ( ! defined( 'PLL_AJAX_ON_FRONT' ) && isset( $_GET['infinity'], $_POST['action'] ) && 'infinite_scroll' == $_POST['action'] ) {
51
  define( 'PLL_AJAX_ON_FRONT', true );
52
  }
53
+
54
+ // WP Sweep
55
+ add_filter( 'wp_sweep_excluded_taxonomies', array( $this, 'wp_sweep_excluded_taxonomies' ) );
56
  }
57
 
58
  /**
79
  function maybe_wordpress_importer() {
80
  if ( defined( 'WP_LOAD_IMPORTERS' ) && class_exists( 'WP_Import' ) ) {
81
  remove_action( 'admin_init', 'wordpress_importer_init' );
82
+ add_action( 'admin_init', array( $this, 'wordpress_importer_init' ) );
83
  }
84
  }
85
 
139
  * @since 1.6.4
140
  */
141
  public function wpseo_init() {
142
+ if ( ! defined( 'WPSEO_VERSION' ) ) {
143
+ return;
144
+ }
145
+
146
+ if ( ! PLL() instanceof PLL_Frontend ) {
147
+ add_action( 'admin_init', array( $this, 'wpseo_register_strings' ) );
148
  return;
149
  }
150
 
151
+ add_filter( 'option_wpseo_titles', array( $this, 'wpseo_translate_titles' ) );
152
+
153
  // Reloads options once the language has been defined to enable translations
154
  // Useful only when the language is set from content
155
  if ( did_action( 'wp_loaded' ) ) {
165
  }
166
  }
167
 
168
+ // Filters sitemap queries to remove inactive language or to get
169
+ // one sitemap per language when using multiple domains or subdomains
170
  // because WPSEO does not accept several domains or subdomains in one sitemap
171
+ add_filter( 'wpseo_posts_join', array( $this, 'wpseo_posts_join' ), 10, 2 );
172
+ add_filter( 'wpseo_posts_where', array( $this, 'wpseo_posts_where' ), 10, 2 );
173
+ add_filter( 'wpseo_typecount_join', array( $this, 'wpseo_posts_join' ), 10, 2 );
174
+ add_filter( 'wpseo_typecount_where', array( $this, 'wpseo_posts_where' ), 10, 2 );
 
 
 
 
175
 
176
+ if ( PLL()->options['force_lang'] > 1 ) {
177
+ add_filter( 'wpseo_enable_xml_sitemap_transient_caching', '__return_false' ); // Disable cache! otherwise WPSEO keeps only one domain (thanks to Junaid Bhura)
178
+ add_filter( 'home_url', array( $this, 'wpseo_home_url' ) , 10, 2 ); // Fix home_url
179
+ } else {
180
+ // Get all terms in all languages when the language is set from the content or directory name
181
+ add_filter( 'get_terms_args', array( $this, 'wpseo_remove_terms_filter' ) );
182
 
183
  // Add the homepages for all languages to the sitemap when the front page displays posts
184
  if ( ! get_option( 'page_on_front' ) ) {
185
+ add_filter( 'wpseo_sitemap_post_content', array( $this, 'add_language_home_urls' ) );
186
  }
187
  }
188
 
189
+ add_filter( 'pll_home_url_white_list', array( $this, 'wpseo_home_url_white_list' ) );
190
+ add_action( 'wpseo_opengraph', array( $this, 'wpseo_ogp' ), 2 );
191
+ add_filter( 'wpseo_canonical', array( $this, 'wpseo_canonical' ) );
192
+ }
193
+
194
+ /**
195
+ * Yoast SEO
196
+ * Registers strings for custom post types and custom taxonomies titles and meta descriptions
197
+ *
198
+ * @since 2.0
199
+ */
200
+ function wpseo_register_strings() {
201
+ $options = get_option( 'wpseo_titles' );
202
+ foreach ( get_post_types( array( 'public' => true, '_builtin' => false ) ) as $t ) {
203
+ if ( pll_is_translated_post_type( $t ) && ! empty( $options[ 'title-' . $t ] ) ) {
204
+ pll_register_string( 'title-' . $t, $options[ 'title-' . $t ], 'wordpress-seo' );
205
+ pll_register_string( 'metadesc-' . $t, $options[ 'metadesc-' . $t ], 'wordpress-seo' );
206
+ }
207
+ }
208
+ foreach ( get_post_types( array( 'has_archive' => true, '_builtin' => false ) ) as $t ) {
209
+ if ( pll_is_translated_post_type( $t ) && ! empty( $options[ 'title-ptarchive-' . $t ] ) ) {
210
+ pll_register_string( 'title-ptarchive-' . $t, $options[ 'title-ptarchive-' . $t ], 'wordpress-seo' );
211
+ pll_register_string( 'metadesc-ptarchive-' . $t, $options[ 'metadesc-ptarchive-' . $t ], 'wordpress-seo' );
212
+ }
213
+ }
214
+ foreach ( get_taxonomies( array( 'public' => true, '_builtin' => false ) ) as $t ) {
215
+ if ( pll_is_translated_taxonomy( $t ) && ! empty( $options[ 'title-tax-' . $t ] ) ) {
216
+ pll_register_string( 'title-tax-' . $t, $options[ 'title-tax-' . $t ], 'wordpress-seo' );
217
+ pll_register_string( 'metadesc-tax-' . $t, $options[ 'metadesc-tax-' . $t ], 'wordpress-seo' );
218
+ }
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Yoast SEO
224
+ * Translates strings for custom post types and custom taxonomies titles and meta descriptions
225
+ *
226
+ * @since 2.0
227
+ *
228
+ * @param array $options
229
+ * @return array
230
+ */
231
+ function wpseo_translate_titles( $options ) {
232
+ if ( PLL() instanceof PLL_Frontend ) {
233
+ foreach ( get_post_types( array( 'public' => true, '_builtin' => false ) ) as $t ) {
234
+ if ( pll_is_translated_post_type( $t ) && ! empty( $options[ 'title-' . $t ] ) ) {
235
+ $options[ 'title-' . $t ] = pll__( $options[ 'title-' . $t ] );
236
+ $options[ 'metadesc-' . $t ] = pll__( $options[ 'metadesc-' . $t ] );
237
+ }
238
+ }
239
+ foreach ( get_post_types( array( 'has_archive' => true, '_builtin' => false ) ) as $t ) {
240
+ if ( pll_is_translated_post_type( $t ) && ! empty( $options[ 'title-ptarchive-' . $t ] ) ) {
241
+ $options[ 'title-ptarchive-' . $t ] = pll__( $options[ 'title-ptarchive-' . $t ] );
242
+ $options[ 'metadesc-ptarchive-' . $t ] = pll__( $options[ 'metadesc-ptarchive-' . $t ] );
243
+ }
244
+ }
245
+ foreach ( get_taxonomies( array( 'public' => true, '_builtin' => false ) ) as $t ) {
246
+ if ( pll_is_translated_taxonomy( $t ) && ! empty( $options[ 'title-tax-' . $t ] ) ) {
247
+ $options[ 'title-tax-' . $t ] = pll__( $options[ 'title-tax-' . $t ] );
248
+ $options[ 'metadesc-tax-' . $t ] = pll__( $options[ 'metadesc-tax-' . $t ] );
249
+ }
250
+ }
251
+ }
252
+ return $options;
253
  }
254
 
255
  /**
272
  return $url;
273
  }
274
 
275
+ /**
276
+ * Yoast SEO
277
+ * Get active languages for the sitemaps
278
+ *
279
+ * @since 2.0
280
+ *
281
+ * @return array list of active language slugs, empty if all languages are active
282
+ */
283
+ protected function wpseo_get_active_languages() {
284
+ $languages = PLL()->model->get_languages_list();
285
+ if ( wp_list_filter( $languages, array( 'active' => false ) ) ) {
286
+ return wp_list_pluck( wp_list_filter( $languages, array( 'active' => false ), 'NOT' ), 'slug' );
287
+ }
288
+ return array();
289
+ }
290
+
291
  /**
292
  * Yoast SEO
293
  * Modifies the sql request for posts sitemaps
294
+ * Only when using multiple domains or subdomains or if some languages are not active
295
  *
296
  * @since 1.6.4
297
  *
300
  * @return string
301
  */
302
  public function wpseo_posts_join( $sql, $post_type ) {
303
+ return pll_is_translated_post_type( $post_type ) && ( PLL()->options['force_lang'] > 1 || $this->wpseo_get_active_languages() ) ? $sql. PLL()->model->post->join_clause() : $sql;
304
  }
305
 
306
  /**
307
  * Yoast SEO
308
  * Modifies the sql request for posts sitemaps
309
+ * Only when using multiple domains or subdomains or if some languages are not active
310
  *
311
  * @since 1.6.4
312
  *
315
  * @return string
316
  */
317
  public function wpseo_posts_where( $sql, $post_type ) {
318
+ if ( pll_is_translated_post_type( $post_type ) ) {
319
+ if ( PLL()->options['force_lang'] > 1 ) {
320
+ return $sql . PLL()->model->post->where_clause( PLL()->curlang );
321
+ }
322
+
323
+ if ( $languages = $this->wpseo_get_active_languages() ) {
324
+ return $sql . PLL()->model->post->where_clause( $languages );
325
+ }
326
+ }
327
+ return $sql;
328
  }
329
 
330
  /**
331
  * Yoast SEO
332
+ * Removes the language filter (and remove inactive languages) for the taxonomy sitemaps
333
  * Only when the language is set from the content or directory name
334
  *
335
  * @since 1.0.3
339
  */
340
  public function wpseo_remove_terms_filter( $args ) {
341
  if ( isset( $GLOBALS['wp_query']->query['sitemap'] ) ) {
342
+ $args['lang'] = implode( ',', $this->wpseo_get_active_languages() );
343
  }
344
  return $args;
345
  }
346
 
347
  /**
348
  * Yoast SEO
349
+ * Adds the home urls for all (active) languages to the sitemap
350
  *
351
  * @since 1.9
352
  *
356
  public function add_language_home_urls( $str ) {
357
  global $wpseo_sitemaps;
358
 
359
+ $languages = wp_list_pluck( wp_list_filter( PLL()->model->get_languages_list() , array( 'active' => false ), 'NOT' ), 'slug' );
360
+
361
+ foreach ( $languages as $lang ) {
362
  if ( empty( PLL()->options['hide_default'] ) || pll_default_language() !== $lang ) {
363
  $str .= $wpseo_sitemaps->sitemap_url( array(
364
  'loc' => pll_home_url( $lang ),
458
 
459
  $settings = Featured_Content::get_setting();
460
 
461
+ if ( ! $term = wpcom_vip_get_term_by( 'name', $settings['tag-name'], 'post_tag' ) ) {
462
  return $featured_ids;
463
  }
464
 
500
  * @return array modified $settings
501
  */
502
  public function twenty_fourteen_option_featured_content( $settings ) {
503
+ if ( PLL() instanceof PLL_Frontend && $settings['tag-id'] && $tr = pll_get_term( $settings['tag-id'] ) ) {
504
  $settings['tag-id'] = $tr;
505
  }
506
 
594
  return $filters;
595
  }
596
 
597
+ /**
598
+ * WP Sweep
599
+ * Add 'term_language' and 'term_translations' to excluded taxonomies otherwise terms loose their language and translation group
600
+ *
601
+ * @since 2.0
602
+ *
603
+ * @param array $excluded_taxonomies list of taxonomies excluded from sweeping
604
+ * @return array
605
+ */
606
+ public function wp_sweep_excluded_taxonomies( $excluded_taxonomies ) {
607
+ return array_merge( $excluded_taxonomies, array( 'term_language', 'term_translations' ) );
608
+ }
609
+
610
  /**
611
  * Correspondance between WordPress locales and Facebook locales
612
  * @see https://translate.wordpress.org/
modules/share-slug/settings-share-slug.php CHANGED
@@ -22,7 +22,7 @@ class PLL_Settings_Share_Slug extends PLL_Settings_Module {
22
  ) );
23
 
24
  if ( class_exists( 'PLL_Share_Post_Slug', true ) && get_option( 'permalink_structure' ) ) {
25
- add_action( 'admin_print_footer_scripts', array( &$this, 'print_js' ) );
26
  }
27
  }
28
 
@@ -58,7 +58,7 @@ class PLL_Settings_Share_Slug extends PLL_Settings_Module {
58
  wp_enqueue_script( 'jquery' );
59
 
60
  $activated = sprintf( '<span class="activated">%s</span>', $this->action_links['activated'] );
61
- $deactivated = sprintf( '<span class="deactivated">%s</span>', $this->action_links['deactivated'] );
62
 
63
  ?>
64
  <script type='text/javascript'>
22
  ) );
23
 
24
  if ( class_exists( 'PLL_Share_Post_Slug', true ) && get_option( 'permalink_structure' ) ) {
25
+ add_action( 'admin_print_footer_scripts', array( $this, 'print_js' ) );
26
  }
27
  }
28
 
58
  wp_enqueue_script( 'jquery' );
59
 
60
  $activated = sprintf( '<span class="activated">%s</span>', $this->action_links['activated'] );
61
+ $deactivated = sprintf( '<span class="deactivated">%s</span>', $this->action_links['deactivated'] );
62
 
63
  ?>
64
  <script type='text/javascript'>
modules/sync/admin-sync.php CHANGED
@@ -18,16 +18,16 @@ class PLL_Admin_Sync {
18
  $this->model = &$polylang->model;
19
  $this->options = &$polylang->options;
20
 
21
- add_filter( 'wp_insert_post_parent', array( &$this, 'wp_insert_post_parent' ) );
22
- add_action( 'add_meta_boxes', array( &$this, 'add_meta_boxes' ), 5, 2 ); // before Types which populates custom fields in same hook with priority 10
23
 
24
- add_action( 'pll_save_post', array( &$this, 'pll_save_post' ), 10, 3 );
25
- add_action( 'pll_save_term', array( &$this, 'pll_save_term' ), 10, 3 );
26
 
27
  if ( $this->options['media_support'] ) {
28
- add_action( 'pll_translate_media', array( &$this, 'copy_taxonomies' ), 10, 3 );
29
- add_action( 'pll_translate_media', array( &$this, 'copy_post_metas' ), 10, 3 );
30
- add_action( 'edit_attachment', array( &$this, 'edit_attachment' ) );
31
  }
32
  }
33
 
@@ -115,6 +115,9 @@ class PLL_Admin_Sync {
115
  // get taxonomies to sync for this post type
116
  $taxonomies = array_intersect( get_post_taxonomies( $from ), $this->get_taxonomies_to_copy( $sync ) );
117
 
 
 
 
118
  // copy or synchronize terms
119
  // FIXME quite a lot of query in foreach
120
  foreach ( $taxonomies as $tax ) {
@@ -298,9 +301,10 @@ class PLL_Admin_Sync {
298
  'post_status' => 'any',
299
  'fields' => 'ids',
300
  'tax_query' => array( array(
301
- 'taxonomy' => $taxonomy,
302
- 'field' => 'id',
303
- 'terms' => array_merge( array( $term_id ), array_values( $translations ) ),
 
304
  ) ),
305
  ) );
306
 
18
  $this->model = &$polylang->model;
19
  $this->options = &$polylang->options;
20
 
21
+ add_filter( 'wp_insert_post_parent', array( $this, 'wp_insert_post_parent' ) );
22
+ add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 5, 2 ); // before Types which populates custom fields in same hook with priority 10
23
 
24
+ add_action( 'pll_save_post', array( $this, 'pll_save_post' ), 10, 3 );
25
+ add_action( 'pll_save_term', array( $this, 'pll_save_term' ), 10, 3 );
26
 
27
  if ( $this->options['media_support'] ) {
28
+ add_action( 'pll_translate_media', array( $this, 'copy_taxonomies' ), 10, 3 );
29
+ add_action( 'pll_translate_media', array( $this, 'copy_post_metas' ), 10, 3 );
30
+ add_action( 'edit_attachment', array( $this, 'edit_attachment' ) );
31
  }
32
  }
33
 
115
  // get taxonomies to sync for this post type
116
  $taxonomies = array_intersect( get_post_taxonomies( $from ), $this->get_taxonomies_to_copy( $sync ) );
117
 
118
+ // update the term cache to reduce the number of queries in the loop
119
+ update_object_term_cache( $sync ? array( $from, $to ) : $from, get_post_type( $from ) );
120
+
121
  // copy or synchronize terms
122
  // FIXME quite a lot of query in foreach
123
  foreach ( $taxonomies as $tax ) {
301
  'post_status' => 'any',
302
  'fields' => 'ids',
303
  'tax_query' => array( array(
304
+ 'taxonomy' => $taxonomy,
305
+ 'field' => 'id',
306
+ 'terms' => array_merge( array( $term_id ), array_values( $translations ) ),
307
+ 'include_children' => false,
308
  ) ),
309
  ) );
310
 
modules/sync/settings-sync.php CHANGED
@@ -43,7 +43,7 @@ class PLL_Settings_Sync extends PLL_Settings_Module {
43
  printf(
44
  '<li><label><input name="sync[%s]" type="checkbox" value="1" %s /> %s</label></li>',
45
  esc_attr( $key ),
46
- in_array( $key, $this->options['sync'] ) ? 'checked="checked"' :'',
47
  esc_html( $str )
48
  );
49
  } ?>
43
  printf(
44
  '<li><label><input name="sync[%s]" type="checkbox" value="1" %s /> %s</label></li>',
45
  esc_attr( $key ),
46
+ in_array( $key, $this->options['sync'] ) ? 'checked="checked"' : '',
47
  esc_html( $str )
48
  );
49
  } ?>
modules/wpml/wpml-api.php ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * A class to handle the WPML API based on hooks, introduced since WPML 3.2
5
+ * It partly relies on the legacy API
6
+ * @see https://wpml.org/documentation/support/wpml-coding-api/wpml-hooks-reference/
7
+ *
8
+ * @since 2.0
9
+ */
10
+ class PLL_WPML_API {
11
+
12
+ /**
13
+ * Constructor
14
+ *
15
+ * @since 2.0
16
+ */
17
+ public function __construct() {
18
+ // Site Wide Language informations
19
+
20
+ add_filter( 'wpml_active_languages', array( $this, 'wpml_active_languages' ), 10, 2 );
21
+ // wpml_display_language_names => not implemented
22
+ // wpml_translated_language_name => not applicable
23
+ add_filter( 'wpml_current_language', 'pll_current_language' );
24
+ add_filter( 'wpml_default_language', 'pll_default_language' );
25
+ // wpml_add_language_selector => not implemented
26
+ // wpml_footer_language_selector => not applicable
27
+ add_action( 'wpml_add_language_form_field', array( $this, 'wpml_add_language_form_field' ) );
28
+ add_filter( 'wpml_language_is_active', array( $this, 'wpml_language_is_active' ), 10, 2 );
29
+ add_filter( 'wpml_is_rtl' , array( $this, 'wpml_is_rtl' ) );
30
+ // wpml_language_form_input_field => not implemented
31
+ // wpml_language_has_switched => not implemented
32
+
33
+ // Retrieving Language Information for Content
34
+
35
+ add_filter( 'wpml_post_language_details', 'wpml_get_language_information', 10, 2 );
36
+ // wpml_switch_language => not implemented
37
+ add_filter( 'wpml_element_language_code', array( $this, 'wpml_element_language_code' ), 10, 3 );
38
+ // wpml_element_language_details => not applicable
39
+
40
+ // Retrieving Localized Content
41
+
42
+ add_filter( 'wpml_home_url', 'pll_home_url' );
43
+ add_filter( 'wpml_element_link', 'icl_link_to_element' , 10, 7 );
44
+ add_filter( 'wpml_object_id', 'icl_object_id', 10, 4 );
45
+ add_filter( 'wpml_translate_single_string', array( $this, 'wpml_translate_single_string' ), 10, 4 );
46
+ // wpml_translate_string => not applicable
47
+ // wpml_unfiltered_admin_string => not implemented
48
+ add_filter( 'wpml_permalink', array( PLL()->links_model, 'switch_language_in_link' ), 10, 2 );
49
+ // wpml_elements_without_translations => not implemented
50
+ // wpml_get_translated_slug => not implemented
51
+
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
+
59
+ // Inserting Content
60
+
61
+ // wpml_admin_make_post_duplicates => not applicable
62
+ // wpml_make_post_duplicates => not applicable
63
+ add_action( 'wpml_register_single_string', 'icl_register_string', 10, 3 );
64
+ // wpml_register_string => not applicable
65
+ // wpml_register_string_packages => not applicable
66
+ // wpml_delete_package_action => not applicable
67
+ // wpml_show_package_language_ui => not applicable
68
+ // wpml_set_element_language_details => not implemented
69
+
70
+ // Miscellaneous
71
+
72
+ // wpml_element_type => not applicable
73
+ // wpml_setting => not applicable
74
+ // wpml_sub_setting => not applicable
75
+ // wpml_editor_cf_to_display => not applicable
76
+ // wpml_tm_save_translation_cf => not implemented
77
+ // wpml_tm_xliff_export_translated_cf => not applicable
78
+ // wpml_tm_xliff_export_original_cf => not applicable
79
+ // wpml_duplicate_generic_string => not applicable
80
+ // wpml_translatable_user_meta_fields => not implemented
81
+ // wpml_cross_domain_language_data => not applicable
82
+ // wpml_get_cross_domain_language_data => not applicable
83
+ // wpml_loaded => not applicable
84
+ // wpml_st_loaded => not applicable
85
+ // wpml_tm_loaded => not applicable
86
+ }
87
+
88
+ /**
89
+ * Get a list of the languages enabled for a site
90
+ *
91
+ * @since 2.0
92
+ *
93
+ * @param mixed $null not used
94
+ * @param array| string $args see arguments of icl_get_languages()
95
+ * @return array array of arrays per language
96
+ */
97
+ public function wpml_active_languages( $null, $args = '' ) {
98
+ return icl_get_languages( $args );
99
+ }
100
+
101
+ /**
102
+ * Returns an HTML hidden input field with name=”lang” and as value the current language
103
+ *
104
+ * @since 2.0
105
+ */
106
+ public function wpml_add_language_form_field() {
107
+ printf( '<input type="hidden" name="lang" value="%s" />', esc_attr( pll_current_language() ) );
108
+ }
109
+
110
+ /**
111
+ * Find out if a specific language is enabled for the site
112
+ *
113
+ * @since 2.0
114
+ *
115
+ * @param mixed $null not used
116
+ * @param string $slug language code
117
+ * @return bool
118
+ */
119
+ public function wpml_language_is_active( $null, $slug ) {
120
+ $language = PLL()->model->get_language( $slug );
121
+ return empty( $language->active ) || true === $language->active;
122
+ }
123
+
124
+ /**
125
+ * Find out whether the current language text direction is RTL or not
126
+ *
127
+ * @since 2.0
128
+ *
129
+ * @param mixed $null not used
130
+ * @return bool
131
+ */
132
+ public function wpml_is_rtl( $null ) {
133
+ return pll_current_language( 'is_rtl' );
134
+ }
135
+
136
+ /**
137
+ * Get the language code for a translatable element
138
+ *
139
+ * @since 2.0
140
+ *
141
+ * @param mixed $language_code
142
+ * @param array $args an array with two keys element_id => post_id or term_taxonomy_id, element_type => post type or taxonomy
143
+ * @return string
144
+ */
145
+ public function wpml_element_language_code( $language_code, $args ) {
146
+ $type = $args['element_type'];
147
+ $id = $args['element_id'];
148
+ $pll_type = ( 'post' == $type || pll_is_translated_post_type( $type ) ) ? 'post' : ( 'term' == $type || pll_is_translated_taxonomy( $type ) ? 'term' : false );
149
+ if ( 'term' === $pll_type && $term = wpcom_vip_get_term_by( 'term_taxonomy_id', $id ) ) {
150
+ $id = $term->term_id;
151
+ }
152
+ return $pll_type ? call_user_func( "pll_get_$pll_type_language", $id ) : $language_code;
153
+ }
154
+
155
+ /**
156
+ * Translates a string
157
+ *
158
+ * @since 2.0
159
+ *
160
+ * @param string $string the string's original value
161
+ * @param string $context the string's registered context
162
+ * @param string $name the string's registered name
163
+ * @param null|string $lang optional, return the translation in this language, defaults to current language
164
+ * @return string the translated string
165
+ */
166
+ public function wpml_translate_single_string( $string, $context, $name, $lang = null ) {
167
+ $has_translation = null; // passed by reference
168
+ return icl_translate( $context, $name, $original_value, false, $has_translation, $lang );
169
+ }
170
+
171
+ /**
172
+ * Find out whether a post type or a taxonomy term is translated
173
+ *
174
+ * @since 2.0
175
+ *
176
+ * @param mixed $null
177
+ * @param int $id post_id or term_id
178
+ * @param string $type post type or taxonomy
179
+ * @return bool
180
+ */
181
+ public function wpml_element_has_translations( $null, $id, $type ) {
182
+ $pll_type = ( 'post' == $type || pll_is_translated_post_type( $type ) ) ? 'post' : ( 'term' == $type || pll_is_translated_taxonomy( $type ) ? 'term' : false );
183
+ return ( $pll_type && $translations = call_user_func( "pll_get_$pll_type_translations", $id ) ) ? count( $translations ) > 1 : false;
184
+ }
185
+ }
modules/wpml/wpml-compat.php CHANGED
@@ -1,366 +1,35 @@
1
  <?php
2
 
3
  /**
4
- * Compatibility with WPML API. See http://wpml.org/documentation/support/wpml-coding-api/
5
- */
6
-
7
- if ( ! defined( 'ABSPATH' ) ) {
8
- exit; // don't access directly
9
- };
10
-
11
- /**
12
- * defines two WPML constants once the language has been defined
13
- * the compatibility with WPML is not perfect on admin side as the constants are defined
14
- * in 'setup_theme' by Polylang ( based on user info ) and 'plugins_loaded' by WPML ( based on cookie )
15
- *
16
- * @since 0.9.5
17
- */
18
- function pll_define_wpml_constants() {
19
- if ( ! empty( PLL()->curlang ) ) {
20
- if ( ! defined( 'ICL_LANGUAGE_CODE' ) ) {
21
- define( 'ICL_LANGUAGE_CODE', PLL()->curlang->slug );
22
- }
23
-
24
- if ( ! defined( 'ICL_LANGUAGE_NAME' ) ) {
25
- define( 'ICL_LANGUAGE_NAME', PLL()->curlang->name );
26
- }
27
- }
28
-
29
- elseif ( PLL_ADMIN ) {
30
- if ( ! defined( 'ICL_LANGUAGE_CODE' ) ) {
31
- define( 'ICL_LANGUAGE_CODE', 'all' );
32
- }
33
-
34
- if ( ! defined( 'ICL_LANGUAGE_NAME' ) ) {
35
- define( 'ICL_LANGUAGE_NAME', '' );
36
- }
37
- }
38
- }
39
-
40
- add_action( 'pll_language_defined', 'pll_define_wpml_constants' );
41
-
42
- /**
43
- * link to the home page in the active language
44
- *
45
- * @since 0.9.4
46
- *
47
- * @return string
48
- */
49
- if ( ! function_exists( 'icl_get_home_url' ) ) {
50
- function icl_get_home_url() {
51
- return pll_home_url();
52
- }
53
- }
54
-
55
- /**
56
- * used for building custom language selectors
57
- * available only on frontend
58
- *
59
- * list of paramaters accepted in $args
60
- *
61
- * skip_missing => wether to skip missing translation or not, 0 or 1, defaults to 0
62
- * orderby => 'id', 'code', 'name', defaults to 'id'
63
- * order => 'ASC' or 'DESC', defaults to 'ASC'
64
- * link_empty_to => link to use when the translation is missing {$lang} is replaced by the language code
65
- *
66
- * list of parameters returned per language:
67
- *
68
- * id => the language id
69
- * active => wether this is the active language or no, 0 or 1
70
- * native_name => the language name
71
- * missing => wether the translation is missing or not, 0 or 1
72
- * translated_name => empty, does not exist in Polylang
73
- * language_code => the language code ( slug )
74
- * country_flag_url => the url of the flag
75
- * url => the url of the translation
76
- *
77
- * @since 1.0
78
- *
79
- * @param string|array $args optional
80
- * @return array array of arrays per language
81
- */
82
- if ( ! function_exists( 'icl_get_languages' ) ) {
83
- function icl_get_languages( $args = '' ) {
84
- $args = wp_parse_args( $args, array( 'skip_missing' => 0, 'orderby' => 'id', 'order' => 'ASC' ) );
85
- $orderby = ( isset( $args['orderby'] ) && 'code' == $args['orderby'] ) ? 'slug' : ( isset( $args['orderby'] ) && 'name' == $args['orderby'] ? 'name' : 'id' );
86
- $order = ( ! empty( $args['order'] ) && 'desc' == $args['order'] ) ? 'DESC' : 'ASC';
87
-
88
- $arr = array();
89
-
90
- foreach ( PLL()->model->get_languages_list( array( 'hide_empty' => true, 'orderby' => $orderby, 'order' => $order ) ) as $lang ) {
91
- // we can find a translation only on frontend
92
- if ( method_exists( PLL()->links, 'get_translation_url' ) ) {
93
- $url = PLL()->links->get_translation_url( $lang );
94
- }
95
-
96
- // it seems that WPML does not bother of skip_missing parameter on admin side and before the $wp_query object has been filled
97
- if ( empty( $url ) && ! empty( $args['skip_missing'] ) && ! is_admin() && did_action( 'parse_query' ) ) {
98
- continue;
99
- }
100
-
101
- $arr[ $lang->slug ] = array(
102
- 'id' => $lang->term_id,
103
- 'active' => isset( PLL()->curlang->slug ) && PLL()->curlang->slug == $lang->slug ? 1 : 0,
104
- 'native_name' => $lang->name,
105
- 'missing' => empty( $url ) ? 1 : 0,
106
- 'translated_name' => '', // does not exist in Polylang
107
- 'language_code' => $lang->slug,
108
- 'country_flag_url' => $lang->flag_url,
109
- 'url' => ! empty( $url ) ? $url :
110
- ( empty( $args['link_empty_to'] ) ? PLL()->links->get_home_url( $lang ) :
111
- str_replace( '{$lang}', $lang->slug, $args['link_empty_to'] ) ),
112
- );
113
- }
114
- return $arr;
115
- }
116
- }
117
-
118
- /**
119
- * used for creating language dependent links in themes
120
- *
121
- * @since 1.0
122
- *
123
- * @param int $id object id
124
- * @param string $type optional, post type or taxonomy name of the object, defaults to 'post'
125
- * @param string $text optional the link text. If not specified will produce the name of the element in the current language
126
- * @param array $args optional an array of arguments to add to the link, defaults to empty
127
- * @param string $anchor optional the anchor to add to teh link, defaults to empty
128
- * @return string a language dependent link
129
- */
130
- if ( ! function_exists( 'icl_link_to_element' ) ) {
131
- function icl_link_to_element( $id, $type = 'post', $text = '', $args = array(), $anchor = '' ) {
132
- if ( 'tag' == $type ) {
133
- $type = 'post_tag';
134
- }
135
-
136
- $pll_type = ( 'post' == $type || pll_is_translated_post_type( $type ) ) ? 'post' : ( 'term' == $type || pll_is_translated_taxonomy( $type ) ? 'term' : false );
137
- if ( $pll_type && ( $lang = pll_current_language() ) && ( $tr_id = PLL()->model->$pll_type->get_translation( $id, $lang ) ) && PLL()->links->current_user_can_read( $tr_id ) ) {
138
- $id = $tr_id;
139
- }
140
-
141
- if ( post_type_exists( $type ) ) {
142
- $link = get_permalink( $id );
143
- if ( empty( $text ) ) {
144
- $text = get_the_title( $id );
145
- }
146
- }
147
- elseif ( taxonomy_exists( $type ) ) {
148
- $link = get_term_link( $id, $type );
149
- if ( empty( $text ) && ( $term = get_term( $id, $type ) ) && ! empty( $term ) && ! is_wp_error( $term ) ) {
150
- $text = $term->name;
151
- }
152
- }
153
-
154
- if ( empty( $link ) || is_wp_error( $link ) ) {
155
- return '';
156
- }
157
-
158
- if ( ! empty( $args ) ) {
159
- $link .= ( false === strpos( $link, '?' ) ? '?' : '&' ) . http_build_query( $args );
160
- }
161
-
162
- if ( ! empty( $anchor ) ) {
163
- $link .= '#' . $anchor;
164
- }
165
-
166
- return sprintf( '<a href="%s">%s</a>', esc_url( $link ), esc_html( $text ) );
167
- }
168
- }
169
-
170
- /**
171
- * used for calculating the IDs of objects ( usually categories ) in the current language
172
- *
173
- * @since 0.9.5
174
- *
175
- * @param int $id object id
176
- * @param string $type, post type or taxonomy name of the object, defaults to 'post'
177
- * @param bool $return_original_if_missing optional, true if Polylang should return the original id if the translation is missing, defaults to false
178
- * @param string $lang optional language code, defaults to current language
179
- * @return int|null the object id of the translation, null if the translation is missing and $return_original_if_missing set to false
180
- */
181
- if ( ! function_exists( 'icl_object_id' ) ) {
182
- function icl_object_id( $id, $type, $return_original_if_missing = false, $lang = false ) {
183
- $pll_type = ( 'post' === $type || pll_is_translated_post_type( $type ) ) ? 'post' : ( 'term' === $type || pll_is_translated_taxonomy( $type ) ? 'term' : false );
184
- return $pll_type && ( $lang = $lang ? $lang : pll_current_language() ) && ( $tr_id = PLL()->model->$pll_type->get_translation( $id, $lang ) ) ? $tr_id :
185
- ( $return_original_if_missing ? $id : null );
186
- }
187
- }
188
-
189
- /**
190
- * undocumented function used by the theme Maya
191
- * returns the post language
192
- * @see original WPML code at https://wpml.org/forums/topic/canonical-urls-for-wpml-duplicated-posts/#post-52198
193
- *
194
- * @since 1.8
195
- *
196
- * @param int $post_id
197
- * @return array
198
- */
199
- if ( ! function_exists( 'wpml_get_language_information' ) ) {
200
- function wpml_get_language_information( $post_id = null ) {
201
- if ( empty( $post_id ) ) {
202
- $post_id = get_the_ID();
203
- }
204
-
205
- // FIXME WPML may return a WP_Error object
206
- return false === $lang = PLL()->model->post->get_language( $post_id ) ? array() : array(
207
- 'locale' => $lang->locale,
208
- 'text_direction' => $lang->is_rtl,
209
- 'display_name' => $lang->name, // seems to be the post language name displayed in the current language, not a feature in Polylang
210
- 'native_name' => $lang->name,
211
- 'different_language' => $lang->slug != pll_current_language(),
212
- );
213
- }
214
- }
215
-
216
- /**
217
- * registers a string for translation in the "strings translation" panel
218
- *
219
- * @since 0.9.3
220
- *
221
- * @param string $context the group in which the string is registered, defaults to 'polylang'
222
- * @param string $name a unique name for the string
223
- * @param string $string the string to register
224
- */
225
- if ( ! function_exists( 'icl_register_string' ) ) {
226
- function icl_register_string( $context, $name, $string ) {
227
- PLL_WPML_Compat::instance()->register_string( $context, $name, $string );
228
- }
229
- }
230
-
231
- /**
232
- * removes a string from the "strings translation" panel
233
- *
234
- * @since 1.0.2
235
- *
236
- * @param string $context the group in which the string is registered, defaults to 'polylang'
237
- * @param string $name a unique name for the string
238
- */
239
- if ( ! function_exists( 'icl_unregister_string' ) ) {
240
- function icl_unregister_string( $context, $name ) {
241
- PLL_WPML_Compat::instance()->unregister_string( $context, $name );
242
- }
243
- }
244
-
245
- /**
246
- * gets the translated value of a string ( previously registered with icl_register_string or pll_register_string )
247
- *
248
- * @since 0.9.3
249
- *
250
- * @param string $context the group in which the string is registered
251
- * @param string $name a unique name for the string
252
- * @param string $string the string to translate, optional for strings registered with icl_register_string
253
- * @return string the translated string in the current language
254
- */
255
- if ( ! function_exists( 'icl_t' ) ) {
256
- function icl_t( $context, $name, $string = false ) {
257
- if ( empty( $string ) ) {
258
- $string = PLL_WPML_Compat::instance()->get_string_by_context_and_name( $context, $name );
259
- }
260
- return pll__( $string );
261
- }
262
- }
263
-
264
- /**
265
- * undocumented function used by NextGen Gallery
266
- * seems to be used to both register and translate a string
267
- * used in PLL_Plugins_Compat for Jetpack with only 3 arguments
268
- *
269
- * @since 1.0.2
270
- *
271
- * @param string $context the group in which the string is registered, defaults to 'polylang'
272
- * @param string $name a unique name for the string
273
- * @param string $string the string to register
274
- * @param bool $bool optional, not used by Polylang
275
- * @return string the translated string in the current language
276
- */
277
- if ( ! function_exists( 'icl_translate' ) ) {
278
- function icl_translate( $context, $name, $string, $bool = false ) {
279
- PLL_WPML_Compat::instance()->register_string( $context, $name, $string );
280
- return pll__( $string );
281
- }
282
- }
283
-
284
- /**
285
- * undocumented function used by Types
286
- * FIXME: tested only with Types
287
- * probably incomplete as Types locks the custom fields for a new post, but not when edited
288
- * this is probably linked to the fact that WPML has always an original post in the default language and not Polylang :)
289
- *
290
- * @since 1.1.2
291
- *
292
- * @return array
293
- */
294
- if ( ! function_exists( 'wpml_get_copied_fields_for_post_edit' ) ) {
295
- function wpml_get_copied_fields_for_post_edit() {
296
- if ( empty( $_GET['from_post'] ) ) {
297
- return array();
298
- }
299
-
300
- // don't know what WPML does but Polylang does copy all public meta keys by default
301
- foreach ( $keys = array_unique( array_keys( get_post_custom( (int) $_GET['from_post'] ) ) ) as $k => $meta_key ) {
302
- if ( is_protected_meta( $meta_key ) ) {
303
- unset( $keys[ $k ] );
304
- }
305
- }
306
-
307
- // apply our filter and fill the expected output ( see /types/embedded/includes/fields-post.php )
308
- /** This filter is documented in modules/sync/admin-sync.php */
309
- $arr['fields'] = array_unique( apply_filters( 'pll_copy_post_metas', empty( $keys ) ? array() : $keys, false ) );
310
- $arr['original_post_id'] = (int) $_GET['from_post'];
311
- return $arr;
312
- }
313
- }
314
-
315
- /**
316
- * undocumented function used by Warp 6 by Yootheme
317
- *
318
- * @since 1.0.5
319
- *
320
- * @return string default language code
321
- */
322
- if ( ! function_exists( 'icl_get_default_language' ) ) {
323
- function icl_get_default_language() {
324
- return pll_default_language();
325
- }
326
- }
327
-
328
- /**
329
- * undocumented function reported to be used by Table Rate Shipping for WooCommerce
330
- * @see https://wordpress.org/support/topic/add-wpml-compatibility-function
331
- *
332
- * @since 1.8.2
333
- *
334
- * @return string default language code
335
- */
336
- if ( ! function_exists( 'wpml_get_default_language' ) ) {
337
- function wpml_get_default_language() {
338
- return pll_default_language();
339
- }
340
- }
341
-
342
- /**
343
- * registers strings in a persistent way as done by WPML
344
  *
345
  * @since 1.0.2
346
  */
347
  class PLL_WPML_Compat {
348
- static protected $instance; // for singleton
349
- static protected $strings; // used for cache
 
350
 
351
  /**
352
- * constructor
353
  *
354
  * @since 1.0.2
355
  */
356
  protected function __construct() {
 
 
 
 
357
  self::$strings = get_option( 'polylang_wpml_strings', array() );
358
 
359
- add_action( 'pll_get_strings', array( &$this, 'get_strings' ) );
 
360
  }
361
 
362
  /**
363
- * access to the single instance of the class
364
  *
365
  * @since 1.7
366
  *
@@ -374,7 +43,34 @@ class PLL_WPML_Compat {
374
  }
375
 
376
  /**
377
- * unlike pll_register_string, icl_register_string stores the string in database
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  * so we need to do the same as some plugins or themes may expect this
379
  * we use a serialized option to do this
380
  *
@@ -385,7 +81,7 @@ class PLL_WPML_Compat {
385
  * @param string $string the string to register
386
  */
387
  public function register_string( $context, $name, $string ) {
388
- // registers the string if it does not exist yet
389
  $to_register = array( 'context' => $context, 'name' => $name, 'string' => $string, 'multiline' => false, 'icl' => true );
390
  if ( ! in_array( $to_register, self::$strings ) && $to_register['string'] ) {
391
  self::$strings[] = $to_register;
@@ -394,7 +90,7 @@ class PLL_WPML_Compat {
394
  }
395
 
396
  /**
397
- * removes a string from the registered strings list
398
  *
399
  * @since 1.0.2
400
  *
@@ -411,7 +107,7 @@ class PLL_WPML_Compat {
411
  }
412
 
413
  /**
414
- * adds strings registered by icl_register_string to those registered by pll_register_string
415
  *
416
  * @since 1.0.2
417
  *
@@ -425,7 +121,7 @@ class PLL_WPML_Compat {
425
  /**
426
  * Get a registered string by its context and name
427
  *
428
- * @since 1.9.2
429
  *
430
  * @param string $context the group in which the string is registered
431
  * @param string $name a unique name for the string
1
  <?php
2
 
3
  /**
4
+ * WPML Compatibility class
5
+ * Defines some WPML constants
6
+ * Registers strings in a persistent way as done by WPML
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  *
8
  * @since 1.0.2
9
  */
10
  class PLL_WPML_Compat {
11
+ static protected $instance; // For singleton
12
+ static protected $strings; // Used for cache
13
+ public $api;
14
 
15
  /**
16
+ * Constructor
17
  *
18
  * @since 1.0.2
19
  */
20
  protected function __construct() {
21
+ // Load the WPML API
22
+ require_once( PLL_MODULES_INC . '/wpml/wpml-legacy-api.php' );
23
+ $this->api = new PLL_WPML_API();
24
+
25
  self::$strings = get_option( 'polylang_wpml_strings', array() );
26
 
27
+ add_action( 'pll_language_defined', array( $this, 'define_constants' ) );
28
+ add_action( 'pll_get_strings', array( $this, 'get_strings' ) );
29
  }
30
 
31
  /**
32
+ * Access to the single instance of the class
33
  *
34
  * @since 1.7
35
  *
43
  }
44
 
45
  /**
46
+ * Defines two WPML constants once the language has been defined
47
+ * The compatibility with WPML is not perfect on admin side as the constants are defined
48
+ * in 'setup_theme' by Polylang ( based on user info ) and 'plugins_loaded' by WPML ( based on cookie )
49
+ *
50
+ * @since 0.9.5
51
+ */
52
+ public function define_constants() {
53
+ if ( ! empty( PLL()->curlang ) ) {
54
+ if ( ! defined( 'ICL_LANGUAGE_CODE' ) ) {
55
+ define( 'ICL_LANGUAGE_CODE', PLL()->curlang->slug );
56
+ }
57
+
58
+ if ( ! defined( 'ICL_LANGUAGE_NAME' ) ) {
59
+ define( 'ICL_LANGUAGE_NAME', PLL()->curlang->name );
60
+ }
61
+ } elseif ( ! PLL() instanceof PLL_Frontend ) {
62
+ if ( ! defined( 'ICL_LANGUAGE_CODE' ) ) {
63
+ define( 'ICL_LANGUAGE_CODE', 'all' );
64
+ }
65
+
66
+ if ( ! defined( 'ICL_LANGUAGE_NAME' ) ) {
67
+ define( 'ICL_LANGUAGE_NAME', '' );
68
+ }
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Unlike pll_register_string, icl_register_string stores the string in database
74
  * so we need to do the same as some plugins or themes may expect this
75
  * we use a serialized option to do this
76
  *
81
  * @param string $string the string to register
82
  */
83
  public function register_string( $context, $name, $string ) {
84
+ // Registers the string if it does not exist yet
85
  $to_register = array( 'context' => $context, 'name' => $name, 'string' => $string, 'multiline' => false, 'icl' => true );
86
  if ( ! in_array( $to_register, self::$strings ) && $to_register['string'] ) {
87
  self::$strings[] = $to_register;
90
  }
91
 
92
  /**
93
+ * Removes a string from the registered strings list
94
  *
95
  * @since 1.0.2
96
  *
107
  }
108
 
109
  /**
110
+ * Adds strings registered by icl_register_string to those registered by pll_register_string
111
  *
112
  * @since 1.0.2
113
  *
121
  /**
122
  * Get a registered string by its context and name
123
  *
124
+ * @since 2.0
125
  *
126
  * @param string $context the group in which the string is registered
127
  * @param string $name a unique name for the string
modules/wpml/wpml-config.php CHANGED
@@ -1,21 +1,18 @@
1
  <?php
2
 
3
  /**
4
- * reads and interprets the file wpml-config.xml
5
- * see http://wpml.org/documentation/support/language-configuration-files/
6
- * the language switcher configuration is not interpreted
7
- * the xml parser has been adapted from http://php.net/manual/en/function.xml-parse-into-struct.php#84261
8
- * many thanks to wickedfather at hotmail dot com
9
  *
10
  * @since 1.0
11
  */
12
  class PLL_WPML_Config {
13
- static protected $instance; // for singleton
14
- protected $values, $index, $strings;
15
- public $tags;
16
 
17
  /**
18
- * constructor
19
  *
20
  * @since 1.0
21
  */
@@ -24,7 +21,7 @@ class PLL_WPML_Config {
24
  }
25
 
26
  /**
27
- * access to the single instance of the class
28
  *
29
  * @since 1.7
30
  *
@@ -38,246 +35,74 @@ class PLL_WPML_Config {
38
  }
39
 
40
  /**
41
- * parses the wpml-config.xml file
42
- *
43
- * @since 1.0
44
- *
45
- * @param string wpml-config.xml file content
46
- * @param string $context identifies where the file was found
47
- */
48
- protected function xml_parse( $xml, $context ) {
49
- $parser = xml_parser_create();
50
- xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, 0 );
51
- xml_parser_set_option( $parser, XML_OPTION_SKIP_WHITE, 1 );
52
- xml_parse_into_struct( $parser, $xml, $this->values );
53
- xml_parser_free( $parser );
54
-
55
- $this->index = 0;
56
- $arr = $this->xml_parse_recursive();
57
- $arr = $arr['wpml-config'];
58
-
59
- $keys = array(
60
- array( 'custom-fields', 'custom-field' ),
61
- array( 'custom-types','custom-type' ),
62
- array( 'taxonomies','taxonomy' ),
63
- array( 'admin-texts','key' ),
64
- );
65
-
66
- foreach ( $keys as $k ) {
67
- if ( isset( $arr[ $k[0] ] ) ) {
68
- if ( ! isset( $arr[ $k[0] ][ $k[1] ][0] ) ) {
69
- $elem = $arr[ $k[0] ][ $k[1] ];
70
- unset( $arr[ $k[0] ][ $k[1] ] );
71
- $arr[ $k[0] ][ $k[1] ][0] = $elem;
72
- }
73
-
74
- $this->tags[ $k[0] ][ $context ] = $arr[ $k[0] ];
75
- }
76
- }
77
- }
78
-
79
- /**
80
- * recursively parses the wpml-config.xml file
81
- *
82
- * @since 1.0
83
- *
84
- * @return array
85
- */
86
- protected function xml_parse_recursive() {
87
- $found = array();
88
- $tagCount = array();
89
-
90
- while ( isset( $this->values[ $this->index ] ) ) {
91
- $tag = $this->values[ $this->index ]['tag'];
92
- $type = $this->values[ $this->index ]['type'];
93
- if ( isset( $this->values[ $this->index ]['attributes'] ) ) {
94
- $attributes = $this->values[ $this->index ]['attributes'];
95
- }
96
- if ( isset( $this->values[ $this->index ]['value'] ) ) {
97
- $value = $this->values[ $this->index ]['value'];
98
- }
99
-
100
- $this->index++;
101
-
102
- if ( 'close' == $type ) {
103
- return $found;
104
- }
105
-
106
- if ( isset( $tagCount[ $tag ] ) ) {
107
- if ( 1 == $tagCount[ $tag ] ) {
108
- $found[ $tag ] = array( $found[ $tag ] );
109
- }
110
-
111
- $tagRef = &$found[ $tag ][ $tagCount[ $tag ] ];
112
- $tagCount[ $tag ]++;
113
- }
114
- else {
115
- $tagCount[ $tag ] = 1;
116
- $tagRef = &$found[ $tag ];
117
- }
118
-
119
- if ( 'open' == $type ) {
120
- $tagRef = $this->xml_parse_recursive();
121
- if ( isset( $attributes ) ) {
122
- $tagRef['attributes'] = $attributes;
123
- }
124
- }
125
-
126
- if ( 'complete' == $type ) {
127
- if ( isset( $attributes ) ) {
128
- $tagRef['attributes'] = $attributes;
129
- $tagRef = &$tagRef['value'];
130
- }
131
- if ( isset( $value ) ) {
132
- $tagRef = $value;
133
- }
134
- }
135
- }
136
-
137
- return $found;
138
- }
139
-
140
- /**
141
- * finds the wpml-config.xml files to parse and setup filters
142
  *
143
  * @since 1.0
144
  */
145
  public function init() {
146
- $this->tags = array();
147
 
148
- // theme
149
- if ( file_exists( $file = ( $template = get_template_directory() ) .'/wpml-config.xml' ) ) {
150
- $this->xml_parse( file_get_contents( $file ), get_template() ); // FIXME fopen + fread + fclose quicker ?
151
  }
152
 
153
- // child theme
154
  if ( ( $stylesheet = get_stylesheet_directory() ) !== $template && file_exists( $file = $stylesheet . '/wpml-config.xml' ) ) {
155
- $this->xml_parse( file_get_contents( $file ), get_stylesheet() );
156
  }
157
 
158
- // plugins
159
- // don't forget sitewide active plugins thanks to Reactorshop http://wordpress.org/support/topic/polylang-and-yoast-seo-plugin/page/2?replies=38#post-4801829
160
  $plugins = ( is_multisite() && $sitewide_plugins = get_site_option( 'active_sitewide_plugins' ) ) && is_array( $sitewide_plugins ) ? array_keys( $sitewide_plugins ) : array();
161
  $plugins = array_merge( $plugins, get_option( 'active_plugins' ) );
162
 
163
  foreach ( $plugins as $plugin ) {
164
- if ( file_exists( $file = WP_PLUGIN_DIR.'/'.dirname( $plugin ).'/wpml-config.xml' ) ) {
165
- $this->xml_parse( file_get_contents( $file ), dirname( $plugin ) );
166
  }
167
  }
168
 
169
- // custom
170
- if ( file_exists( $file = PLL_LOCAL_DIR.'/wpml-config.xml' ) ) {
171
- $this->xml_parse( file_get_contents( $file ), 'Polylang' );
172
- }
173
-
174
- if ( isset( $this->tags['custom-fields'] ) ) {
175
- add_filter( 'pll_copy_post_metas', array( &$this, 'copy_post_metas' ), 10, 2 );
176
- }
177
-
178
- if ( isset( $this->tags['custom-types'] ) ) {
179
- add_filter( 'pll_get_post_types', array( &$this, 'translate_types' ), 10, 2 );
180
- }
181
-
182
- if ( isset( $this->tags['taxonomies'] ) ) {
183
- add_filter( 'pll_get_taxonomies', array( &$this, 'translate_taxonomies' ), 10, 2 );
184
  }
185
 
186
- if ( ! isset( $this->tags['admin-texts'] ) ) {
187
- return;
188
- }
 
189
 
190
- // get a cleaner array for easy manipulation
191
- foreach ( $this->tags['admin-texts'] as $context => $arr ) {
192
- foreach ( $arr as $keys ) {
193
- $this->strings[ $context ] = $this->admin_texts_recursive( $keys );
194
- }
195
- }
196
-
197
- foreach ( $this->strings as $context => $options ) {
198
- foreach ( $options as $option_name => $value ) {
199
- if ( PLL_ADMIN ) { // backend
200
- $option = get_option( $option_name );
201
- if ( is_string( $option ) && 1 == $value ) {
202
- pll_register_string( $option_name, $option, $context );
203
- }
204
- elseif ( is_array( $option ) && is_array( $value ) ) {
205
- $this->register_string_recursive( $context, $value, $option ); // for a serialized option
206
  }
207
  }
208
- else {
209
- add_filter( 'option_'.$option_name, array( &$this, 'translate_strings' ) );
210
- }
211
- }
212
- }
213
- }
214
-
215
- /**
216
- * arranges strings in a cleaner way
217
- *
218
- * @since 1.0
219
- *
220
- * @param array $keys
221
- * @return array
222
- */
223
- protected function admin_texts_recursive( $keys ) {
224
- if ( ! isset( $keys[0] ) ) {
225
- $elem = $keys;
226
- unset( $keys );
227
- $keys[0] = $elem;
228
- }
229
- foreach ( $keys as $key ) {
230
- $strings[ $key['attributes']['name'] ] = isset( $key['key'] ) ? $this->admin_texts_recursive( $key['key'] ) : 1;
231
- }
232
-
233
- return $strings;
234
- }
235
-
236
- /**
237
- * recursively registers strings for a serialized option
238
- *
239
- * @since 1.0
240
- *
241
- * @param string $context the group in which the strings will be registered
242
- * @param array $strings
243
- * @param array $options
244
- */
245
- protected function register_string_recursive( $context, $strings, $options ) {
246
- foreach ( $options as $name => $value ) {
247
- if ( isset( $strings[ $name ] ) ) {
248
- // allow numeric values to be translated
249
- // https://wordpress.org/support/topic/wpml-configxml-strings-skipped-when-numbers-ids
250
- if ( ( is_numeric( $value ) || is_string( $value ) ) && 1 == $strings[ $name ] ) {
251
- pll_register_string( $name, $value, $context );
252
- }
253
- elseif ( is_array( $value ) && is_array( $strings[ $name ] ) ) {
254
- $this->register_string_recursive( $context, $strings[ $name ], $value );
255
- }
256
  }
257
  }
258
  }
259
 
260
  /**
261
- * adds custom fields to the list of metas to copy when creating a new translation
262
  *
263
  * @since 1.0
264
  *
265
  * @param array $metas the list of custom fields to copy or synchronize
266
- * @param bool $sync true for sync, false for copy
267
  * @return array the list of custom fields to copy or synchronize
268
  */
269
  public function copy_post_metas( $metas, $sync ) {
270
- foreach ( $this->tags['custom-fields'] as $context ) {
271
- foreach ( $context['custom-field'] as $cf ) {
272
- // copy => copy and synchronize
273
- // translate => copy but don't synchronize
274
- // ignore => don't copy
275
- // see http://wordpress.org/support/topic/custom-field-values-override-other-translation-values?replies=8#post-4655563
276
- if ( 'copy' == $cf['attributes']['action'] || ( ! $sync && 'translate' == $cf['attributes']['action'] ) ) {
277
- $metas[] = $cf['value'];
278
- }
279
- else {
280
- $metas = array_diff( $metas, array( $cf['value'] ) );
281
  }
282
  }
283
  }
@@ -285,22 +110,21 @@ class PLL_WPML_Config {
285
  }
286
 
287
  /**
288
- * language and translation management for custom post types
289
  *
290
  * @since 1.0
291
  *
292
  * @param array $types list of post type names for which Polylang manages language and translations
293
- * @param bool $hide true when displaying the list in Polylang settings
294
  * @return array list of post type names for which Polylang manages language and translations
295
  */
296
  public function translate_types( $types, $hide ) {
297
- foreach ( $this->tags['custom-types'] as $context ) {
298
- foreach ( $context['custom-type'] as $pt ) {
299
- if ( 1 == $pt['attributes']['translate'] && ! $hide ) {
300
- $types[ $pt['value'] ] = $pt['value'];
301
- }
302
- else {
303
- unset( $types[ $pt['value'] ] ); // the author decided what to do with the post type so don't allow the user to change this
304
  }
305
  }
306
  }
@@ -308,31 +132,29 @@ class PLL_WPML_Config {
308
  }
309
 
310
  /**
311
- * language and translation management for custom taxonomies
312
  *
313
  * @since 1.0
314
  *
315
  * @param array $taxonomies list of taxonomy names for which Polylang manages language and translations
316
- * @param bool $hide true when displaying the list in Polylang settings
317
  * @return array list of taxonomy names for which Polylang manages language and translations
318
  */
319
  public function translate_taxonomies( $taxonomies, $hide ) {
320
- foreach ( $this->tags['taxonomies'] as $context ) {
321
- foreach ( $context['taxonomy'] as $tax ) {
322
- if ( 1 == $tax['attributes']['translate'] && ! $hide ) {
323
- $taxonomies[ $tax['value'] ] = $tax['value'];
324
- }
325
- else {
326
- unset( $taxonomies[ $tax['value'] ] ); // the author decided what to do with the taxonomy so don't allow the user to change this
327
  }
328
  }
329
  }
330
-
331
  return $taxonomies;
332
  }
333
 
334
  /**
335
- * translates the strings for an option
336
  *
337
  * @since 1.0
338
  *
@@ -340,39 +162,54 @@ class PLL_WPML_Config {
340
  * @return array|string translated string(s)
341
  */
342
  public function translate_strings( $value ) {
343
- if ( is_array( $value ) ) {
344
- $option = substr( current_filter(), 7 );
345
- foreach ( $this->strings as $context => $options ) {
346
- if ( array_key_exists( $option, $options ) ) {
347
- return $this->translate_strings_recursive( $options[ $option ], $value ); // for a serialized option
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  }
349
  }
 
 
350
  }
351
- return pll__( $value );
352
  }
353
 
354
  /**
355
- * recursively translates strings for a serialized option
356
  *
357
  * @since 1.0
358
  *
359
- * @param array $strings
360
  * @param array|string $values either a string to translate or a list of strings to translate
 
361
  * @return array|string translated string(s)
362
  */
363
- protected function translate_strings_recursive( $strings, $values ) {
364
- foreach ( $values as $name => $value ) {
365
- if ( isset( $strings[ $name ] ) ) {
366
- // allow numeric values to be translated
367
- // https://wordpress.org/support/topic/wpml-configxml-strings-skipped-when-numbers-ids
368
- if ( ( is_numeric( $value ) || is_string( $value ) ) && 1 == $strings[ $name ] ) {
369
- $values[ $name ] = pll__( $value );
370
- }
371
- elseif ( is_array( $value ) && is_array( $strings[ $name ] ) ) {
372
- $values[ $name ] = $this->translate_strings_recursive( $strings[ $name ], $value );
373
  }
374
  }
 
 
375
  }
376
  return $values;
377
  }
378
- } // class PLL_WPML_Config
1
  <?php
2
 
3
  /**
4
+ * Reads and interprets the file wpml-config.xml
5
+ * See http://wpml.org/documentation/support/language-configuration-files/
6
+ * The language switcher configuration is not interpreted
 
 
7
  *
8
  * @since 1.0
9
  */
10
  class PLL_WPML_Config {
11
+ static protected $instance; // For singleton
12
+ protected $xmls, $options;
 
13
 
14
  /**
15
+ * Constructor
16
  *
17
  * @since 1.0
18
  */
21
  }
22
 
23
  /**
24
+ * Access to the single instance of the class
25
  *
26
  * @since 1.7
27
  *
35
  }
36
 
37
  /**
38
+ * Finds the wpml-config.xml files to parse and setup filters
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  *
40
  * @since 1.0
41
  */
42
  public function init() {
43
+ $this->xmls = array();
44
 
45
+ // Theme
46
+ if ( file_exists( $file = ( $template = get_template_directory() ) . '/wpml-config.xml' ) ) {
47
+ $this->xmls[ get_template() ] = simplexml_load_file( $file );
48
  }
49
 
50
+ // Child theme
51
  if ( ( $stylesheet = get_stylesheet_directory() ) !== $template && file_exists( $file = $stylesheet . '/wpml-config.xml' ) ) {
52
+ $this->xmls[ get_stylesheet() ] = simplexml_load_file( $file );
53
  }
54
 
55
+ // Plugins
56
+ // Don't forget sitewide active plugins thanks to Reactorshop http://wordpress.org/support/topic/polylang-and-yoast-seo-plugin/page/2?replies=38#post-4801829
57
  $plugins = ( is_multisite() && $sitewide_plugins = get_site_option( 'active_sitewide_plugins' ) ) && is_array( $sitewide_plugins ) ? array_keys( $sitewide_plugins ) : array();
58
  $plugins = array_merge( $plugins, get_option( 'active_plugins' ) );
59
 
60
  foreach ( $plugins as $plugin ) {
61
+ if ( file_exists( $file = WP_PLUGIN_DIR . '/' . dirname( $plugin ) . '/wpml-config.xml' ) ) {
62
+ $this->xmls[ dirname( $plugin ) ] = simplexml_load_file( $file );
63
  }
64
  }
65
 
66
+ // Custom
67
+ if ( file_exists( $file = PLL_LOCAL_DIR . '/wpml-config.xml' ) ) {
68
+ $this->xmls['Polylang'] = simplexml_load_file( $file );
 
 
 
 
 
 
 
 
 
 
 
 
69
  }
70
 
71
+ if ( ! empty( $this->xmls ) ) {
72
+ add_filter( 'pll_copy_post_metas', array( $this, 'copy_post_metas' ), 10, 2 );
73
+ add_filter( 'pll_get_post_types', array( $this, 'translate_types' ), 10, 2 );
74
+ add_filter( 'pll_get_taxonomies', array( $this, 'translate_taxonomies' ), 10, 2 );
75
 
76
+ foreach ( $this->xmls as $context => $xml ) {
77
+ foreach ( $xml->xpath( 'admin-texts/key' ) as $key ) {
78
+ $name = (string) $key->attributes()['name'];
79
+ if ( PLL() instanceof PLL_Frontend ) {
80
+ $this->options[ $name ] = $key;
81
+ add_filter( 'option_' . $name, array( $this, 'translate_strings' ) );
82
+ } else {
83
+ $this->register_string_recursive( $context, get_option( $name ), $key );
 
 
 
 
 
 
 
 
84
  }
85
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  }
87
  }
88
  }
89
 
90
  /**
91
+ * Adds custom fields to the list of metas to copy when creating a new translation
92
  *
93
  * @since 1.0
94
  *
95
  * @param array $metas the list of custom fields to copy or synchronize
96
+ * @param bool $sync true for sync, false for copy
97
  * @return array the list of custom fields to copy or synchronize
98
  */
99
  public function copy_post_metas( $metas, $sync ) {
100
+ foreach ( $this->xmls as $xml ) {
101
+ foreach ( $xml->xpath( 'custom-fields/custom-field' ) as $cf ) {
102
+ if ( 'copy' == $cf->attributes()['action'] || ( ! $sync && 'translate' == $cf->attributes()['action'] ) ) {
103
+ $metas[] = (string) $cf;
104
+ } else {
105
+ $metas = array_diff( $metas, array( (string) $cf ) );
 
 
 
 
 
106
  }
107
  }
108
  }
110
  }
111
 
112
  /**
113
+ * Language and translation management for custom post types
114
  *
115
  * @since 1.0
116
  *
117
  * @param array $types list of post type names for which Polylang manages language and translations
118
+ * @param bool $hide true when displaying the list in Polylang settings
119
  * @return array list of post type names for which Polylang manages language and translations
120
  */
121
  public function translate_types( $types, $hide ) {
122
+ foreach ( $this->xmls as $xml ) {
123
+ foreach ( $xml->xpath( 'custom-types/custom-type' ) as $pt ) {
124
+ if ( 1 == $pt->attributes()['translate'] && ! $hide ) {
125
+ $types[ (string) $pt ] = (string) $pt;
126
+ } else {
127
+ unset( $types[ (string) $pt ] ); // The theme/plugin author decided what to do with the post type so don't allow the user to change this
 
128
  }
129
  }
130
  }
132
  }
133
 
134
  /**
135
+ * Language and translation management for custom taxonomies
136
  *
137
  * @since 1.0
138
  *
139
  * @param array $taxonomies list of taxonomy names for which Polylang manages language and translations
140
+ * @param bool $hide true when displaying the list in Polylang settings
141
  * @return array list of taxonomy names for which Polylang manages language and translations
142
  */
143
  public function translate_taxonomies( $taxonomies, $hide ) {
144
+ foreach ( $this->xmls as $xml ) {
145
+ foreach ( $xml->xpath( 'taxonomies/taxonomy' ) as $tax ) {
146
+ if ( 1 == $tax->attributes()['translate'] && ! $hide ) {
147
+ $taxonomies[ (string) $tax ] = (string) $tax;
148
+ } else {
149
+ unset( $taxonomies[ (string) $tax ] ); // the theme/plugin author decided what to do with the taxonomy so don't allow the user to change this
 
150
  }
151
  }
152
  }
 
153
  return $taxonomies;
154
  }
155
 
156
  /**
157
+ * Translates the strings for an option
158
  *
159
  * @since 1.0
160
  *
162
  * @return array|string translated string(s)
163
  */
164
  public function translate_strings( $value ) {
165
+ $option = substr( current_filter(), 7 );
166
+ return $this->translate_strings_recursive( $value, $this->options[ $option ] );
167
+ }
168
+
169
+ /**
170
+ * Recursively registers strings for a serialized option
171
+ *
172
+ * @since 1.0
173
+ *
174
+ * @param string $context the group in which the strings will be registered
175
+ * @param array $options
176
+ * @param object $key XML node
177
+ */
178
+ protected function register_string_recursive( $context, $options, $key ) {
179
+ $children = $key->children();
180
+ if ( count( $children ) ) {
181
+ foreach ( $children as $child ) {
182
+ $name = (string) $child->attributes()['name'];
183
+ if ( isset( $options[ $name ] ) ) {
184
+ $this->register_string_recursive( $context, $options[ $name ], $child );
185
  }
186
  }
187
+ } else {
188
+ pll_register_string( (string) $key->attributes()['name'], $options, $context );
189
  }
 
190
  }
191
 
192
  /**
193
+ * Recursively translates strings for a serialized option
194
  *
195
  * @since 1.0
196
  *
 
197
  * @param array|string $values either a string to translate or a list of strings to translate
198
+ * @param object $key XML node
199
  * @return array|string translated string(s)
200
  */
201
+ protected function translate_strings_recursive( $values, $key ) {
202
+ $children = $key->children();
203
+ if ( count( $children ) ) {
204
+ foreach ( $children as $child ) {
205
+ $name = (string) $child->attributes()['name'];
206
+ if ( isset( $values[ $name ] ) ) {
207
+ $values[ $name ] = $this->translate_strings_recursive( $values[ $name ], $child );
 
 
 
208
  }
209
  }
210
+ } else {
211
+ $values = pll__( $values );
212
  }
213
  return $values;
214
  }
215
+ }
modules/wpml/wpml-legacy-api.php ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Compatibility with WPML legacy API
5
+ * Deprecated since WPML 3.2 and no more documented
6
+ * But a lot of 3rd party plugins / themes are still using these functions
7
+ */
8
+
9
+ /**
10
+ * Link to the home page in the active language
11
+ *
12
+ * @since 0.9.4
13
+ *
14
+ * @return string
15
+ */
16
+ if ( ! function_exists( 'icl_get_home_url' ) ) {
17
+ function icl_get_home_url() {
18
+ return pll_home_url();
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Used for building custom language selectors
24
+ * available only on frontend
25
+ *
26
+ * list of paramaters accepted in $args
27
+ *
28
+ * skip_missing => wether to skip missing translation or not, 0 or 1, defaults to 0
29
+ * orderby => 'id', 'code', 'name', defaults to 'id'
30
+ * order => 'ASC' or 'DESC', defaults to 'ASC'
31
+ * link_empty_to => link to use when the translation is missing {$lang} is replaced by the language code
32
+ *
33
+ * list of parameters returned per language:
34
+ *
35
+ * id => the language id
36
+ * active => wether this is the active language or no, 0 or 1
37
+ * native_name => the language name
38
+ * missing => wether the translation is missing or not, 0 or 1
39
+ * translated_name => empty, does not exist in Polylang
40
+ * language_code => the language code ( slug )
41
+ * country_flag_url => the url of the flag
42
+ * url => the url of the translation
43
+ *
44
+ * @since 1.0
45
+ *
46
+ * @param string|array $args optional
47
+ * @return array array of arrays per language
48
+ */
49
+ if ( ! function_exists( 'icl_get_languages' ) ) {
50
+ function icl_get_languages( $args = '' ) {
51
+ $args = wp_parse_args( $args, array( 'skip_missing' => 0, 'orderby' => 'id', 'order' => 'ASC' ) );
52
+ $orderby = ( isset( $args['orderby'] ) && 'code' == $args['orderby'] ) ? 'slug' : ( isset( $args['orderby'] ) && 'name' == $args['orderby'] ? 'name' : 'id' );
53
+ $order = ( ! empty( $args['order'] ) && 'desc' == $args['order'] ) ? 'DESC' : 'ASC';
54
+
55
+ $arr = array();
56
+
57
+ foreach ( PLL()->model->get_languages_list( array( 'hide_empty' => true, 'orderby' => $orderby, 'order' => $order ) ) as $lang ) {
58
+ // We can find a translation only on frontend
59
+ if ( method_exists( PLL()->links, 'get_translation_url' ) ) {
60
+ $url = PLL()->links->get_translation_url( $lang );
61
+ }
62
+
63
+ // It seems that WPML does not bother of skip_missing parameter on admin side and before the $wp_query object has been filled
64
+ if ( empty( $url ) && ! empty( $args['skip_missing'] ) && ! is_admin() && did_action( 'parse_query' ) ) {
65
+ continue;
66
+ }
67
+
68
+ $arr[ $lang->slug ] = array(
69
+ 'id' => $lang->term_id,
70
+ 'active' => isset( PLL()->curlang->slug ) && PLL()->curlang->slug == $lang->slug ? 1 : 0,
71
+ 'native_name' => $lang->name,
72
+ 'missing' => empty( $url ) ? 1 : 0,
73
+ 'translated_name' => '', // Does not exist in Polylang
74
+ 'language_code' => $lang->slug,
75
+ 'country_flag_url' => $lang->flag_url,
76
+ 'url' => ! empty( $url ) ? $url :
77
+ ( empty( $args['link_empty_to'] ) ? PLL()->links->get_home_url( $lang ) :
78
+ str_replace( '{$lang}', $lang->slug, $args['link_empty_to'] ) ),
79
+ );
80
+ }
81
+ return $arr;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Used for creating language dependent links in themes
87
+ *
88
+ * @since 1.0
89
+ * @since 2.0 add support for arguments 6 and 7
90
+ *
91
+ * @param int $id object id
92
+ * @param string $type optional, post type or taxonomy name of the object, defaults to 'post'
93
+ * @param string $text optional, the link text. If not specified will produce the name of the element in the current language
94
+ * @param array $args optional, an array of arguments to add to the link, defaults to empty
95
+ * @param string $anchor optional, the anchor to add to the link, defaults to empty
96
+ * @param bool $echo optional, whether to echo the link, defaults to true
97
+ * @param bool $return_original_if_missing optional, whether to return a value if the translation is missing
98
+ * @return string a language dependent link
99
+ */
100
+ if ( ! function_exists( 'icl_link_to_element' ) ) {
101
+ function icl_link_to_element( $id, $type = 'post', $text = '', $args = array(), $anchor = '', $echo = true, $return_original_if_missing = true ) {
102
+ if ( 'tag' == $type ) {
103
+ $type = 'post_tag';
104
+ }
105
+
106
+ $pll_type = ( 'post' == $type || pll_is_translated_post_type( $type ) ) ? 'post' : ( 'term' == $type || pll_is_translated_taxonomy( $type ) ? 'term' : false );
107
+ if ( $pll_type && ( $lang = pll_current_language() ) && ( $tr_id = PLL()->model->$pll_type->get_translation( $id, $lang ) ) && PLL()->links->current_user_can_read( $tr_id ) ) {
108
+ $id = $tr_id;
109
+ } elseif ( ! $return_original_if_missing ) {
110
+ return '';
111
+ }
112
+
113
+ if ( post_type_exists( $type ) ) {
114
+ $link = get_permalink( $id );
115
+ if ( empty( $text ) ) {
116
+ $text = get_the_title( $id );
117
+ }
118
+ } elseif ( taxonomy_exists( $type ) ) {
119
+ $link = wpcom_vip_get_term_link( $id, $type );
120
+ if ( empty( $text ) && ( $term = get_term( $id, $type ) ) && ! empty( $term ) && ! is_wp_error( $term ) ) {
121
+ $text = $term->name;
122
+ }
123
+ }
124
+
125
+ if ( empty( $link ) || is_wp_error( $link ) ) {
126
+ return '';
127
+ }
128
+
129
+ if ( ! empty( $args ) ) {
130
+ $link .= ( false === strpos( $link, '?' ) ? '?' : '&' ) . http_build_query( $args );
131
+ }
132
+
133
+ if ( ! empty( $anchor ) ) {
134
+ $link .= '#' . $anchor;
135
+ }
136
+
137
+ $link = sprintf( '<a href="%s">%s</a>', esc_url( $link ), esc_html( $text ) );
138
+
139
+ if ( $echo ) {
140
+ echo $link;
141
+ }
142
+
143
+ return $link;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Used for calculating the IDs of objects (usually categories) in the current language
149
+ *
150
+ * @since 0.9.5
151
+ *
152
+ * @param int $id object id
153
+ * @param string $type post type or taxonomy name of the object, defaults to 'post'
154
+ * @param bool $return_original_if_missing optional, true if Polylang should return the original id if the translation is missing, defaults to false
155
+ * @param string $lang optional, language code, defaults to current language
156
+ * @return int|null the object id of the translation, null if the translation is missing and $return_original_if_missing set to false
157
+ */
158
+ if ( ! function_exists( 'icl_object_id' ) ) {
159
+ function icl_object_id( $id, $type, $return_original_if_missing = false, $lang = false ) {
160
+ $pll_type = ( 'post' === $type || pll_is_translated_post_type( $type ) ) ? 'post' : ( 'term' === $type || pll_is_translated_taxonomy( $type ) ? 'term' : false );
161
+ return $pll_type && ( $lang = $lang ? $lang : pll_current_language() ) && ( $tr_id = PLL()->model->$pll_type->get_translation( $id, $lang ) ) ? $tr_id :
162
+ ( $return_original_if_missing ? $id : null );
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Undocumented function used by the theme Maya
168
+ * returns the post language
169
+ * @see original WPML code at https://wpml.org/forums/topic/canonical-urls-for-wpml-duplicated-posts/#post-52198
170
+ *
171
+ * @since 1.8
172
+ *
173
+ * @param null $empty optional, not used
174
+ * @param int $post_id optional, post id, defaults to current post
175
+ * @return array
176
+ */
177
+ if ( ! function_exists( 'wpml_get_language_information' ) ) {
178
+ function wpml_get_language_information( $empty = null, $post_id = null ) {
179
+ if ( empty( $post_id ) ) {
180
+ $post_id = get_the_ID();
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
+ }
192
+ }
193
+
194
+ /**
195
+ * Registers a string for translation in the "strings translation" panel
196
+ *
197
+ * @since 0.9.3
198
+ *
199
+ * @param string $context the group in which the string is registered, defaults to 'polylang'
200
+ * @param string $name a unique name for the string
201
+ * @param string $string the string to register
202
+ * @param bool $allow_empty_value not used
203
+ * @param string $source_lang not used by Polylang
204
+ */
205
+ if ( ! function_exists( 'icl_register_string' ) ) {
206
+ function icl_register_string( $context, $name, $string, $allow_empty_value = false, $source_lang = '' ) {
207
+ PLL_WPML_Compat::instance()->register_string( $context, $name, $string );
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Removes a string from the "strings translation" panel
213
+ *
214
+ * @since 1.0.2
215
+ *
216
+ * @param string $context the group in which the string is registered, defaults to 'polylang'
217
+ * @param string $name a unique name for the string
218
+ */
219
+ if ( ! function_exists( 'icl_unregister_string' ) ) {
220
+ function icl_unregister_string( $context, $name ) {
221
+ PLL_WPML_Compat::instance()->unregister_string( $context, $name );
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Gets the translated value of a string ( previously registered with icl_register_string or pll_register_string )
227
+ *
228
+ * @since 0.9.3
229
+ * @since 1.9.2 argument 3 is optional
230
+ * @since 2.0 add support for arguments 4 to 6
231
+ *
232
+ * @param string $context the group in which the string is registered
233
+ * @param string $name a unique name for the string
234
+ * @param string $string the string to translate, optional for strings registered with icl_register_string
235
+ * @param bool|null $has_translation optional, not supported in Polylang
236
+ * @param bool $bool optional, not used
237
+ * @param string|null $lang optional, return the translation in this language, defaults to current language
238
+ * @return string the translated string
239
+ */
240
+ if ( ! function_exists( 'icl_t' ) ) {
241
+ function icl_t( $context, $name, $string = false, &$has_translation = null, $bool = false, $lang = null ) {
242
+ return icl_translate( $context, $name, $string, false, $has_translation, $lang );
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Undocumented function used by NextGen Gallery
248
+ * used in PLL_Plugins_Compat for Jetpack with only 3 arguments
249
+ *
250
+ * @since 1.0.2
251
+ * @since 2.0 add support for arguments 5 and 6, strings are no more automatically registered
252
+ *
253
+ * @param string $context the group in which the string is registered
254
+ * @param string $name a unique name for the string
255
+ * @param string $string the string to translate, optional for strings registered with icl_register_string
256
+ * @param bool $bool optional, not used
257
+ * @param bool|null $has_translation optional, not supported in Polylang
258
+ * @param string|null $lang optional, return the translation in this language, defaults to current language
259
+ * @return string the translated string
260
+ */
261
+ if ( ! function_exists( 'icl_translate' ) ) {
262
+ function icl_translate( $context, $name, $string = false, $bool = false, &$has_translation = null, $lang = null ) {
263
+ // FIXME WPML can automatically registers the string based on an option
264
+ if ( empty( $string ) ) {
265
+ $string = PLL_WPML_Compat::instance()->get_string_by_context_and_name( $context, $name );
266
+ }
267
+ return empty( $lang ) ? pll__( $string ) : pll_translate_string( $string, $lang );
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Undocumented function used by Types
273
+ * FIXME: tested only with Types
274
+ * probably incomplete as Types locks the custom fields for a new post, but not when edited
275
+ * This is probably linked to the fact that WPML has always an original post in the default language and not Polylang :)
276
+ *
277
+ * @since 1.1.2
278
+ *
279
+ * @return array
280
+ */
281
+ if ( ! function_exists( 'wpml_get_copied_fields_for_post_edit' ) ) {
282
+ function wpml_get_copied_fields_for_post_edit() {
283
+ if ( empty( $_GET['from_post'] ) ) {
284
+ return array();
285
+ }
286
+
287
+ // Don't know what WPML does but Polylang does copy all public meta keys by default
288
+ foreach ( $keys = array_unique( array_keys( get_post_custom( (int) $_GET['from_post'] ) ) ) as $k => $meta_key ) {
289
+ if ( is_protected_meta( $meta_key ) ) {
290
+ unset( $keys[ $k ] );
291
+ }
292
+ }
293
+
294
+ // Apply our filter and fill the expected output ( see /types/embedded/includes/fields-post.php )
295
+ /** This filter is documented in modules/sync/admin-sync.php */
296
+ $arr['fields'] = array_unique( apply_filters( 'pll_copy_post_metas', empty( $keys ) ? array() : $keys, false ) );
297
+ $arr['original_post_id'] = (int) $_GET['from_post'];
298
+ return $arr;
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Undocumented function used by Warp 6 by Yootheme
304
+ *
305
+ * @since 1.0.5
306
+ *
307
+ * @return string default language code
308
+ */
309
+ if ( ! function_exists( 'icl_get_default_language' ) ) {
310
+ function icl_get_default_language() {
311
+ return pll_default_language();
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Undocumented function reported to be used by Table Rate Shipping for WooCommerce
317
+ * @see https://wordpress.org/support/topic/add-wpml-compatibility-function
318
+ *
319
+ * @since 1.8.2
320
+ *
321
+ * @return string default language code
322
+ */
323
+ if ( ! function_exists( 'wpml_get_default_language' ) ) {
324
+ function wpml_get_default_language() {
325
+ return pll_default_language();
326
+ }
327
+ }
polylang.php CHANGED
@@ -3,7 +3,7 @@
3
  /*
4
  Plugin Name: Polylang
5
  Plugin URI: https://polylang.pro
6
- Version: 1.9.3
7
  Author: Frédéric Demarle
8
  Author uri: https://polylang.pro
9
  Description: Adds multilingual capability to WordPress
@@ -35,7 +35,7 @@ if ( ! defined( 'ABSPATH' ) ) {
35
  exit; // don't access directly
36
  };
37
 
38
- define( 'POLYLANG_VERSION', '1.9.3' );
39
  define( 'PLL_MIN_WP_VERSION', '4.0' );
40
 
41
  define( 'POLYLANG_FILE', __FILE__ ); // this file
3
  /*
4
  Plugin Name: Polylang
5
  Plugin URI: https://polylang.pro
6
+ Version: 2.0
7
  Author: Frédéric Demarle
8
  Author uri: https://polylang.pro
9
  Description: Adds multilingual capability to WordPress
35
  exit; // don't access directly
36
  };
37
 
38
+ define( 'POLYLANG_VERSION', '2.0' );
39
  define( 'PLL_MIN_WP_VERSION', '4.0' );
40
 
41
  define( 'POLYLANG_FILE', __FILE__ ); // this file
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: Chouby
3
  Donate link: https://polylang.pro
4
  Tags: multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
  Requires at least: 4.0
6
- Tested up to: 4.5
7
- Stable tag: 1.9.3
8
  License: GPLv2 or later
9
 
10
  Making WordPress multilingual
@@ -62,7 +62,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. Our Premium addon is currently in beta stage and is available for tests to Polyang Pro users who request it.
66
 
67
  = Do you need translation services? =
68
 
@@ -77,6 +77,34 @@ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-
77
 
78
  == Changelog ==
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  = 1.9.3 (2016-06-28) =
81
 
82
  * Pro: Allow to add slashes in url slugs translations
@@ -116,4 +144,4 @@ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-
116
  * Fix get_adjacent_post() and wp_get_archives() for untranslated post types ( needs WP 4.4+ )
117
  * Fix language homepage urls not present in Yoast SEO sitemap (when the homepages display posts)
118
 
119
- See changelog.txt for older changelog
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.6
7
+ Stable tag: 2.0
8
  License: GPLv2 or later
9
 
10
  Making WordPress multilingual
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
 
78
  == Changelog ==
79
 
80
+ = 2.0 (2016-08-02) =
81
+
82
+ * Pro: Improve integration with ACF Pro
83
+ * Pro: Add support for single sign on across multiple domains or subdomains
84
+ * Pro: Add support for browser language detection when using multiple domains
85
+ * Pro: Add support for translation of the static portion of the post permalink structure
86
+ * Pro: Fix deactivated languages appearing in Yoast SEO sitemaps
87
+ * Pro: Fix impossibility to visit a deactivated language when using subdomains or multiple domains (#10)
88
+ * Pro: Fix when sharing slug on the page for posts, only one of them is accessible (#33)
89
+ * Add the possibility to use the language switcher as dropdown in menu
90
+ * Add support for custom logo introduced in WP 4.5 (#6)
91
+ * The backend current language ( PLL()->curlang ) is now equal to the language of current post or term being edited (#19)
92
+ * The sample permalink is now updated when changing the language in the Languages metabox
93
+ * Revamp the wpml-config.xml reader to use simplexml instead of our custom xml parser
94
+ * Improve support for the WPML API (including Hook API introduced in WPML 3.2)
95
+ * Add support for translation of meta titles and descriptions of custom post types and custom taxonomies in Yoast SEO
96
+ * Replace uncached functions by WPCOM VIP functions when available
97
+ * Improve compatibility with WP 4.6
98
+ * Fix parent category wrongly assigned to post when synchronizing children categories (#21)
99
+ * Fix custom fonts not loaded when using multiple domains or subdomains
100
+ * Fix remove_accents() not working for German and Danish (#24)
101
+ * Fix incorrect static front pages urls on backend
102
+ * Fix impossible to directly enter the page number in strings translation table (introduced in 1.9.3)
103
+ * Fix conflict with WP Sweep (needs WP Sweep 1.0.8+)
104
+ * Fix potential performance issue by querying only taxonomies to show in quick edit to filter the category checklist
105
+ * Fix conflict (database error) with ReOrder-posts-within-categories plugin
106
+ * Fix languages per page option not saved
107
+
108
  = 1.9.3 (2016-06-28) =
109
 
110
  * Pro: Allow to add slashes in url slugs translations
144
  * Fix get_adjacent_post() and wp_get_archives() for untranslated post types ( needs WP 4.4+ )
145
  * Fix language homepage urls not present in Yoast SEO sitemap (when the homepages display posts)
146
 
147
+ See [changelog.txt](https://plugins.svn.wordpress.org/polylang/trunk/changelog.txt) for older changelog
settings/flags.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  if ( ! defined( 'ABSPATH' ) ) {
4
- exit; // don't access directly
5
  };
6
 
7
  /**
@@ -262,7 +262,7 @@ $flags = array(
262
  'zw' => __( 'Zimbabwe', 'polylang' ),
263
  );
264
 
265
- /*
266
  * Filter the list of predefined flags
267
  *
268
  * @since 1.8
1
  <?php
2
 
3
  if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Don't access directly
5
  };
6
 
7
  /**
262
  'zw' => __( 'Zimbabwe', 'polylang' ),
263
  );
264
 
265
+ /**
266
  * Filter the list of predefined flags
267
  *
268
  * @since 1.8
settings/languages.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  if ( ! defined( 'ABSPATH' ) ) {
4
- exit; // don't access directly
5
  };
6
 
7
  /**
@@ -119,7 +119,7 @@ $languages = array(
119
  'zh_TW' => array( 'zh', 'zh_TW', '中文 (台灣)', 'ltr', 'tw' ),
120
  );
121
 
122
- /*
123
  * Filter the list of predefined languages
124
  *
125
  * @since 1.7.10
1
  <?php
2
 
3
  if ( ! defined( 'ABSPATH' ) ) {
4
+ exit; // Don't access directly
5
  };
6
 
7
  /**
119
  'zh_TW' => array( 'zh', 'zh_TW', '中文 (台灣)', 'ltr', 'tw' ),
120
  );
121
 
122
+ /**
123
  * Filter the list of predefined languages
124
  *
125
  * @since 1.7.10
settings/settings-browser.php CHANGED
@@ -8,7 +8,7 @@
8
  class PLL_Settings_Browser extends PLL_Settings_Module {
9
 
10
  /**
11
- * constructor
12
  *
13
  * @since 1.8
14
  *
@@ -19,25 +19,38 @@ class PLL_Settings_Browser extends PLL_Settings_Module {
19
  'module' => 'browser',
20
  'title' => __( 'Detect browser language', 'polylang' ),
21
  'description' => __( 'When the front page is visited, set the language according to the browser preference', 'polylang' ),
22
- 'active_option' => 3 > $polylang->options['force_lang'] ? 'browser' : false,
23
  ) );
24
 
25
- add_action( 'admin_print_footer_scripts', array( &$this, 'print_js' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  }
27
 
28
  /**
29
- * tells if the module is active
30
  *
31
  * @since 1.8
32
  *
33
  * @return bool
34
  */
35
  public function is_active() {
36
- return 3 > $this->options['force_lang'] ? parent::is_active() : false;
37
  }
38
 
39
  /**
40
- * displays the javascript to handle dynamically the change in url modifications
41
  * as the preferred browser language is not used when the language is set from different domains
42
  *
43
  * @since 1.8
8
  class PLL_Settings_Browser extends PLL_Settings_Module {
9
 
10
  /**
11
+ * Constructor
12
  *
13
  * @since 1.8
14
  *
19
  'module' => 'browser',
20
  'title' => __( 'Detect browser language', 'polylang' ),
21
  'description' => __( 'When the front page is visited, set the language according to the browser preference', 'polylang' ),
22
+ 'active_option' => $this->is_available() ? 'browser' : false,
23
  ) );
24
 
25
+ if ( ! class_exists( 'PLL_Xdata_Domain', true ) ) {
26
+ add_action( 'admin_print_footer_scripts', array( $this, 'print_js' ) );
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Tells if the option is available
32
+ *
33
+ * @since 2.0
34
+ *
35
+ * @return bool
36
+ */
37
+ protected function is_available() {
38
+ return ( 3 > $this->options['force_lang'] ) || class_exists( 'PLL_Xdata_Domain', true );
39
  }
40
 
41
  /**
42
+ * Tells if the module is active
43
  *
44
  * @since 1.8
45
  *
46
  * @return bool
47
  */
48
  public function is_active() {
49
+ return $this->is_available() ? parent::is_active() : false;
50
  }
51
 
52
  /**
53
+ * Displays the javascript to handle dynamically the change in url modifications
54
  * as the preferred browser language is not used when the language is set from different domains
55
  *
56
  * @since 1.8
settings/settings-cpt.php CHANGED
@@ -51,7 +51,7 @@ class PLL_Settings_CPT extends PLL_Settings_Module {
51
  */
52
  protected function form() {
53
  if ( ! empty( $this->post_types ) ) {?>
54
- <h4><?php _e( 'Custom post types', 'polylang' ) ?></h4>
55
  <ul class="pll-inline-block-list"><?php
56
  foreach ( $this->post_types as $post_type ) {
57
  $pt = get_post_type_object( $post_type );
@@ -63,11 +63,11 @@ class PLL_Settings_CPT extends PLL_Settings_Module {
63
  );
64
  }?>
65
  </ul>
66
- <p class="description"><?php _e( 'Activate languages and translations for custom post types.', 'polylang' );?></p><?php
67
  }
68
 
69
  if ( ! empty( $this->taxonomies ) ) {?>
70
- <h4><?php _e( 'Custom taxonomies', 'polylang' ) ?></h4>
71
  <ul class="pll-inline-block-list"><?php
72
  foreach ( $this->taxonomies as $taxonomy ) {
73
  $tax = get_taxonomy( $taxonomy );
@@ -79,7 +79,7 @@ class PLL_Settings_CPT extends PLL_Settings_Module {
79
  );
80
  }?>
81
  </ul>
82
- <p class="description"><?php _e( 'Activate languages and translations for custom taxonomies.', 'polylang' );?></p><?php
83
  }
84
  }
85
 
51
  */
52
  protected function form() {
53
  if ( ! empty( $this->post_types ) ) {?>
54
+ <h4><?php esc_html_e( 'Custom post types', 'polylang' ) ?></h4>
55
  <ul class="pll-inline-block-list"><?php
56
  foreach ( $this->post_types as $post_type ) {
57
  $pt = get_post_type_object( $post_type );
63
  );
64
  }?>
65
  </ul>
66
+ <p class="description"><?php esc_html_e( 'Activate languages and translations for custom post types.', 'polylang' );?></p><?php
67
  }
68
 
69
  if ( ! empty( $this->taxonomies ) ) {?>
70
+ <h4><?php esc_html_e( 'Custom taxonomies', 'polylang' ) ?></h4>
71
  <ul class="pll-inline-block-list"><?php
72
  foreach ( $this->taxonomies as $taxonomy ) {
73
  $tax = get_taxonomy( $taxonomy );
79
  );
80
  }?>
81
  </ul>
82
+ <p class="description"><?php esc_html_e( 'Activate languages and translations for custom taxonomies.', 'polylang' );?></p><?php
83
  }
84
  }
85
 
settings/settings-licenses.php CHANGED
@@ -9,7 +9,7 @@ class PLL_Settings_Licenses extends PLL_Settings_Module {
9
  protected $items;
10
 
11
  /**
12
- * constructor
13
  *
14
  * @since 1.9
15
  *
@@ -26,11 +26,11 @@ class PLL_Settings_Licenses extends PLL_Settings_Module {
26
 
27
  $this->items = apply_filters( 'pll_settings_licenses', array() );
28
 
29
- add_action( 'wp_ajax_pll_deactivate_license', array( &$this, 'deactivate_license' ) );
30
  }
31
 
32
  /**
33
- * tells if the module is active
34
  *
35
  * @since 1.9
36
  *
@@ -41,7 +41,7 @@ class PLL_Settings_Licenses extends PLL_Settings_Module {
41
  }
42
 
43
  /**
44
- * displays the settings form
45
  *
46
  * @since 1.9
47
  */
@@ -56,7 +56,7 @@ class PLL_Settings_Licenses extends PLL_Settings_Module {
56
  }
57
 
58
  /**
59
- * get the html for a row (one per license key) for display
60
  *
61
  * @since 1.9
62
  *
@@ -80,7 +80,7 @@ class PLL_Settings_Licenses extends PLL_Settings_Module {
80
  $now = current_time( 'timestamp' );
81
  $expiration = strtotime( $license->expires, $now );
82
 
83
- // special case: the license expired after the last check
84
  if ( $license->success && $expiration < $now ) {
85
  $license->success = false;
86
  $license->error = 'expired';
@@ -167,9 +167,9 @@ class PLL_Settings_Licenses extends PLL_Settings_Module {
167
  return sprintf( '<tr id="pll-license-%s" class="%s">%s</tr>', $item->id, $class, $out );
168
  }
169
 
170
- /*
171
- * ajax method to save the license keys and activate the licenses at the same time
172
- * overrides parent's method
173
  *
174
  * @since 1.9
175
  */
@@ -186,7 +186,7 @@ class PLL_Settings_Licenses extends PLL_Settings_Module {
186
  $x->Add( array( 'what' => 'license-update', 'data' => $item->id, 'supplemental' => array( 'html' => $this->get_row( $updated_item ) ) ) );
187
  }
188
 
189
- // updated message
190
  add_settings_error( 'general', 'settings_updated', __( 'Settings saved.' ), 'updated' );
191
  ob_start();
192
  settings_errors();
@@ -196,7 +196,7 @@ class PLL_Settings_Licenses extends PLL_Settings_Module {
196
  }
197
 
198
  /**
199
- * ajax method to deactivate a license
200
  *
201
  * @since 1.9
202
  */
9
  protected $items;
10
 
11
  /**
12
+ * Constructor
13
  *
14
  * @since 1.9
15
  *
26
 
27
  $this->items = apply_filters( 'pll_settings_licenses', array() );
28
 
29
+ add_action( 'wp_ajax_pll_deactivate_license', array( $this, 'deactivate_license' ) );
30
  }
31
 
32
  /**
33
+ * Tells if the module is active
34
  *
35
  * @since 1.9
36
  *
41
  }
42
 
43
  /**
44
+ * Displays the settings form
45
  *
46
  * @since 1.9
47
  */
56
  }
57
 
58
  /**
59
+ * Get the html for a row (one per license key) for display
60
  *
61
  * @since 1.9
62
  *
80
  $now = current_time( 'timestamp' );
81
  $expiration = strtotime( $license->expires, $now );
82
 
83
+ // Special case: the license expired after the last check
84
  if ( $license->success && $expiration < $now ) {
85
  $license->success = false;
86
  $license->error = 'expired';
167
  return sprintf( '<tr id="pll-license-%s" class="%s">%s</tr>', $item->id, $class, $out );
168
  }
169
 
170
+ /**
171
+ * Ajax method to save the license keys and activate the licenses at the same time
172
+ * Overrides parent's method
173
  *
174
  * @since 1.9
175
  */
186
  $x->Add( array( 'what' => 'license-update', 'data' => $item->id, 'supplemental' => array( 'html' => $this->get_row( $updated_item ) ) ) );
187
  }
188
 
189
+ // Updated message
190
  add_settings_error( 'general', 'settings_updated', __( 'Settings saved.' ), 'updated' );
191
  ob_start();
192
  settings_errors();
196
  }
197
 
198
  /**
199
+ * Ajax method to deactivate a license
200
  *
201
  * @since 1.9
202
  */
settings/settings-module.php CHANGED
@@ -38,37 +38,37 @@ class PLL_Settings_Module {
38
  $this->action_links = array(
39
  'configure' => sprintf(
40
  '<a title="%s" href="%s">%s</a>',
41
- __( 'Configure this module', 'polylang' ),
42
  '#',
43
- __( 'Settings', 'polylang' )
44
  ),
45
 
46
  'deactivate' => sprintf(
47
  '<a title="%s" href="%s">%s</a>',
48
- __( 'Deactivate this module', 'polylang' ),
49
  wp_nonce_url( '?page=mlang&amp;tab=modules&amp;pll_action=deactivate&amp;noheader=true&amp;module=' . $this->module, 'pll_deactivate' ),
50
- __( 'Deactivate', 'polylang' )
51
  ),
52
 
53
  'activate' => sprintf(
54
  '<a title="%s" href="%s">%s</a>',
55
- __( 'Activate this module', 'polylang' ),
56
  wp_nonce_url( '?page=mlang&amp;tab=modules&amp;pll_action=activate&amp;noheader=true&amp;module=' . $this->module, 'pll_activate' ),
57
- __( 'Activate', 'polylang' )
58
  ),
59
 
60
- 'activated' => __( 'Activated', 'polylang' ),
61
 
62
- 'deactivated' => __( 'Deactivated', 'polylang' ),
63
  );
64
 
65
  $this->buttons = array(
66
- 'cancel' => sprintf( '<button type="button" class="button button-secondary cancel">%s</button>', __( 'Cancel' ) ),
67
- 'save' => sprintf( '<button type="button" class="button button-primary save">%s</button>', __( 'Save Changes' ) ),
68
  );
69
 
70
  // ajax action to save options
71
- add_action( 'wp_ajax_pll_save_options', array( &$this, 'save_options' ) );
72
  }
73
 
74
  /**
38
  $this->action_links = array(
39
  'configure' => sprintf(
40
  '<a title="%s" href="%s">%s</a>',
41
+ esc_attr__( 'Configure this module', 'polylang' ),
42
  '#',
43
+ esc_html__( 'Settings', 'polylang' )
44
  ),
45
 
46
  'deactivate' => sprintf(
47
  '<a title="%s" href="%s">%s</a>',
48
+ esc_attr__( 'Deactivate this module', 'polylang' ),
49
  wp_nonce_url( '?page=mlang&amp;tab=modules&amp;pll_action=deactivate&amp;noheader=true&amp;module=' . $this->module, 'pll_deactivate' ),
50
+ esc_html__( 'Deactivate', 'polylang' )
51
  ),
52
 
53
  'activate' => sprintf(
54
  '<a title="%s" href="%s">%s</a>',
55
+ esc_attr__( 'Activate this module', 'polylang' ),
56
  wp_nonce_url( '?page=mlang&amp;tab=modules&amp;pll_action=activate&amp;noheader=true&amp;module=' . $this->module, 'pll_activate' ),
57
+ esc_html__( 'Activate', 'polylang' )
58
  ),
59
 
60
+ 'activated' => esc_html__( 'Activated', 'polylang' ),
61
 
62
+ 'deactivated' => esc_html__( 'Deactivated', 'polylang' ),
63
  );
64
 
65
  $this->buttons = array(
66
+ 'cancel' => sprintf( '<button type="button" class="button button-secondary cancel">%s</button>', esc_html__( 'Cancel' ) ),
67
+ 'save' => sprintf( '<button type="button" class="button button-primary save">%s</button>', esc_html__( 'Save Changes' ) ),
68
  );
69
 
70
  // ajax action to save options
71
+ add_action( 'wp_ajax_pll_save_options', array( $this, 'save_options' ) );
72
  }
73
 
74
  /**
settings/settings-tools.php CHANGED
@@ -31,7 +31,7 @@ class PLL_Settings_Tools extends PLL_Settings_Module {
31
  printf(
32
  '<label for="uninstall"><input id="uninstall" name="uninstall" type="checkbox" value="1" %s /> %s</label>',
33
  empty( $this->options['uninstall'] ) ? '' : 'checked="checked"',
34
- __( 'Remove all Polylang data when using the "Delete" link on the plugins screen.', 'polylang' )
35
  );
36
  }
37
 
31
  printf(
32
  '<label for="uninstall"><input id="uninstall" name="uninstall" type="checkbox" value="1" %s /> %s</label>',
33
  empty( $this->options['uninstall'] ) ? '' : 'checked="checked"',
34
+ esc_html__( 'Remove all Polylang data when using the "Delete" link on the plugins screen.', 'polylang' )
35
  );
36
  }
37
 
settings/settings-url.php CHANGED
@@ -8,7 +8,7 @@
8
  class PLL_Settings_Url extends PLL_Settings_Module {
9
 
10
  /**
11
- * constructor
12
  *
13
  * @since 1.8
14
  *
@@ -27,7 +27,7 @@ class PLL_Settings_Url extends PLL_Settings_Module {
27
  }
28
 
29
  /**
30
- * displays the fieldset to choose how the language is set
31
  *
32
  * @since 1.8
33
  */
@@ -36,33 +36,33 @@ class PLL_Settings_Url extends PLL_Settings_Module {
36
  printf(
37
  '<input name="force_lang" type="radio" value="0" %s /> %s',
38
  $this->options['force_lang'] ? '' : 'checked="checked"',
39
- __( 'The language is set from content', 'polylang' )
40
  );?>
41
  </label>
42
- <p class="description"><?php _e( 'Posts, pages, categories and tags urls are not modified.', 'polylang' );?></p>
43
  <label><?php
44
  printf(
45
  '<input name="force_lang" type="radio" value="1" %s/> %s',
46
  1 == $this->options['force_lang'] ? 'checked="checked"' : '',
47
- $this->links_model->using_permalinks ? __( 'The language is set from the directory name in pretty permalinks', 'polylang' ) : __( 'The language is set from the code in the URL', 'polylang' )
48
  );?>
49
  </label>
50
- <p class="description"><?php echo __( 'Example:', 'polylang' ) . ' <code>' . esc_html( home_url( $this->links_model->using_permalinks ? 'en/my-post/' : '?lang=en&p=1' ) ) . '</code>';?></p>
51
  <label><?php
52
  printf(
53
  '<input name="force_lang" type="radio" value="2" %s %s/> %s',
54
  $this->links_model->using_permalinks ? '' : 'disabled="disabled"',
55
  2 == $this->options['force_lang'] ? 'checked="checked"' : '',
56
- __( 'The language is set from the subdomain name in pretty permalinks', 'polylang' )
57
  );?>
58
  </label>
59
- <p class="description"><?php echo __( 'Example:', 'polylang' ) . ' <code>' . esc_html( str_replace( array( '://', 'www.' ), array( '://en.', '' ), home_url( 'my-post/' ) ) ) . '</code>';?></p>
60
  <label><?php
61
  printf(
62
  '<input name="force_lang" type="radio" value="3" %s %s/> %s',
63
  $this->links_model->using_permalinks ? '' : 'disabled="disabled"',
64
  3 == $this->options['force_lang'] ? 'checked="checked"' : '',
65
- __( 'The language is set from different domains', 'polylang' )
66
  );?>
67
  </label>
68
  <table id="pll-domains-table" class="form-table" <?php echo 3 == $this->options['force_lang'] ? '' : 'style="display: none;"'; ?>><?php
@@ -78,8 +78,8 @@ class PLL_Settings_Url extends PLL_Settings_Module {
78
  </table><?php
79
  }
80
 
81
- /*
82
- * displays the fieldset to choose to hide the default language information in url
83
  *
84
  * @since 1.8
85
  */
@@ -88,13 +88,13 @@ class PLL_Settings_Url extends PLL_Settings_Module {
88
  printf(
89
  '<input name="hide_default" type="checkbox" value="1" %s /> %s',
90
  $this->options['hide_default'] ? 'checked="checked"' :'',
91
- __( 'Hide URL language information for default language', 'polylang' )
92
  );?>
93
  </label><?php
94
  }
95
 
96
  /**
97
- * displays the fieldset to choose to hide /language/ in url
98
  *
99
  * @since 1.8
100
  */
@@ -104,23 +104,23 @@ class PLL_Settings_Url extends PLL_Settings_Module {
104
  '<input name="rewrite" type="radio" value="1" %s %s/> %s',
105
  $this->links_model->using_permalinks ? '' : 'disabled="disabled"',
106
  $this->options['rewrite'] ? 'checked="checked"' : '',
107
- __( 'Remove /language/ in pretty permalinks', 'polylang' )
108
  );?>
109
  </label>
110
- <p class="description"><?php echo __( 'Example:', 'polylang' ) . ' <code>' . esc_html( home_url( 'en/' ) ) . '</code>';?></p>
111
  <label><?php
112
  printf(
113
  '<input name="rewrite" type="radio" value="0" %s %s/> %s',
114
  $this->links_model->using_permalinks ? '' : 'disabled="disabled"',
115
  $this->options['rewrite'] ? '' : 'checked="checked"',
116
- __( 'Keep /language/ in pretty permalinks', 'polylang' )
117
  );?>
118
  </label>
119
- <p class="description"><?php echo __( 'Example:', 'polylang' ) . ' <code>' . esc_html( home_url( 'language/en/' ) ) . '</code>';?></p><?php
120
  }
121
 
122
  /**
123
- * displays the fieldset to choose to redirect the home page to language page
124
  *
125
  * @since 1.8
126
  */
@@ -129,16 +129,16 @@ class PLL_Settings_Url extends PLL_Settings_Module {
129
  printf(
130
  '<input name="redirect_lang" type="checkbox" value="1" %s/> %s',
131
  $this->options['redirect_lang'] ? 'checked="checked"' :'',
132
- __( 'The front page url contains the language code instead of the page name or page id', 'polylang' )
133
  );?>
134
  </label>
135
  <p class="description"><?php
136
- // that's nice to display the right home urls but don't forget that the page on front may have no language yet
137
  $lang = $this->model->post->get_language( $this->page_on_front );
138
  $lang = $lang ? $lang : $this->model->get_language( $this->options['default_lang'] );
139
  printf(
140
  /* translators: %s are urls */
141
- __( 'Example: %s instead of %s', 'polylang' ),
142
  '<code>' . esc_html( $this->links_model->home_url( $lang ) ) . '</code>',
143
  '<code>' . esc_html( _get_page_link( $this->page_on_front ) ) . '</code>'
144
  ); ?>
@@ -146,7 +146,7 @@ class PLL_Settings_Url extends PLL_Settings_Module {
146
  }
147
 
148
  /**
149
- * displays the settings
150
  *
151
  * @since 1.8
152
  */
@@ -177,7 +177,7 @@ class PLL_Settings_Url extends PLL_Settings_Module {
177
  }
178
 
179
  /**
180
- * sanitizes the settings before saving
181
  *
182
  * @since 1.8
183
  *
@@ -211,16 +211,16 @@ class PLL_Settings_Url extends PLL_Settings_Module {
211
  $newoptions['browser'] = $newoptions['hide_default'] = 0;
212
  }
213
 
214
- // check if domains exist
215
  if ( $newoptions['force_lang'] > 1 ) {
216
  $this->check_domains( $newoptions );
217
  }
218
 
219
- return $newoptions; // take care to return only validated options
220
  }
221
 
222
  /**
223
- * check if subdomains or domains are accessible
224
  *
225
  * @since 1.8
226
  *
@@ -231,8 +231,9 @@ class PLL_Settings_Url extends PLL_Settings_Module {
231
  $model = new PLL_Model( $options );
232
  $links_model = $model->get_links_model();
233
  foreach ( $this->model->get_languages_list() as $lang ) {
234
- $url = $links_model->home_url( $lang );
235
- $response = wp_remote_get( add_query_arg( 'deactivate-polylang', 1, $url ) );
 
236
  $response_code = wp_remote_retrieve_response_code( $response );
237
 
238
  if ( 200 != $response_code ) {
8
  class PLL_Settings_Url extends PLL_Settings_Module {
9
 
10
  /**
11
+ * Constructor
12
  *
13
  * @since 1.8
14
  *
27
  }
28
 
29
  /**
30
+ * Displays the fieldset to choose how the language is set
31
  *
32
  * @since 1.8
33
  */
36
  printf(
37
  '<input name="force_lang" type="radio" value="0" %s /> %s',
38
  $this->options['force_lang'] ? '' : 'checked="checked"',
39
+ esc_html__( 'The language is set from content', 'polylang' )
40
  );?>
41
  </label>
42
+ <p class="description"><?php esc_html_e( 'Posts, pages, categories and tags urls are not modified.', 'polylang' );?></p>
43
  <label><?php
44
  printf(
45
  '<input name="force_lang" type="radio" value="1" %s/> %s',
46
  1 == $this->options['force_lang'] ? 'checked="checked"' : '',
47
+ $this->links_model->using_permalinks ? esc_html__( 'The language is set from the directory name in pretty permalinks', 'polylang' ) : esc_html__( 'The language is set from the code in the URL', 'polylang' )
48
  );?>
49
  </label>
50
+ <p class="description"><?php echo esc_html__( 'Example:', 'polylang' ) . ' <code>' . esc_html( home_url( $this->links_model->using_permalinks ? 'en/my-post/' : '?lang=en&p=1' ) ) . '</code>';?></p>
51
  <label><?php
52
  printf(
53
  '<input name="force_lang" type="radio" value="2" %s %s/> %s',
54
  $this->links_model->using_permalinks ? '' : 'disabled="disabled"',
55
  2 == $this->options['force_lang'] ? 'checked="checked"' : '',
56
+ esc_html__( 'The language is set from the subdomain name in pretty permalinks', 'polylang' )
57
  );?>
58
  </label>
59
+ <p class="description"><?php echo esc_html__( 'Example:', 'polylang' ) . ' <code>' . esc_html( str_replace( array( '://', 'www.' ), array( '://en.', '' ), home_url( 'my-post/' ) ) ) . '</code>';?></p>
60
  <label><?php
61
  printf(
62
  '<input name="force_lang" type="radio" value="3" %s %s/> %s',
63
  $this->links_model->using_permalinks ? '' : 'disabled="disabled"',
64
  3 == $this->options['force_lang'] ? 'checked="checked"' : '',
65
+ esc_html__( 'The language is set from different domains', 'polylang' )
66
  );?>
67
  </label>
68
  <table id="pll-domains-table" class="form-table" <?php echo 3 == $this->options['force_lang'] ? '' : 'style="display: none;"'; ?>><?php
78
  </table><?php
79
  }
80
 
81
+ /**
82
+ * Displays the fieldset to choose to hide the default language information in url
83
  *
84
  * @since 1.8
85
  */
88
  printf(
89
  '<input name="hide_default" type="checkbox" value="1" %s /> %s',
90
  $this->options['hide_default'] ? 'checked="checked"' :'',
91
+ esc_html__( 'Hide URL language information for default language', 'polylang' )
92
  );?>
93
  </label><?php
94
  }
95
 
96
  /**
97
+ * Displays the fieldset to choose to hide /language/ in url
98
  *
99
  * @since 1.8
100
  */
104
  '<input name="rewrite" type="radio" value="1" %s %s/> %s',
105
  $this->links_model->using_permalinks ? '' : 'disabled="disabled"',
106
  $this->options['rewrite'] ? 'checked="checked"' : '',
107
+ esc_html__( 'Remove /language/ in pretty permalinks', 'polylang' )
108
  );?>
109
  </label>
110
+ <p class="description"><?php echo esc_html__( 'Example:', 'polylang' ) . ' <code>' . esc_html( home_url( 'en/' ) ) . '</code>';?></p>
111
  <label><?php
112
  printf(
113
  '<input name="rewrite" type="radio" value="0" %s %s/> %s',
114
  $this->links_model->using_permalinks ? '' : 'disabled="disabled"',
115
  $this->options['rewrite'] ? '' : 'checked="checked"',
116
+ esc_html__( 'Keep /language/ in pretty permalinks', 'polylang' )
117
  );?>
118
  </label>
119
+ <p class="description"><?php echo esc_html__( 'Example:', 'polylang' ) . ' <code>' . esc_html( home_url( 'language/en/' ) ) . '</code>';?></p><?php
120
  }
121
 
122
  /**
123
+ * Displays the fieldset to choose to redirect the home page to language page
124
  *
125
  * @since 1.8
126
  */
129
  printf(
130
  '<input name="redirect_lang" type="checkbox" value="1" %s/> %s',
131
  $this->options['redirect_lang'] ? 'checked="checked"' :'',
132
+ esc_html__( 'The front page url contains the language code instead of the page name or page id', 'polylang' )
133
  );?>
134
  </label>
135
  <p class="description"><?php
136
+ // That's nice to display the right home urls but don't forget that the page on front may have no language yet
137
  $lang = $this->model->post->get_language( $this->page_on_front );
138
  $lang = $lang ? $lang : $this->model->get_language( $this->options['default_lang'] );
139
  printf(
140
  /* translators: %s are urls */
141
+ esc_html__( 'Example: %s instead of %s', 'polylang' ),
142
  '<code>' . esc_html( $this->links_model->home_url( $lang ) ) . '</code>',
143
  '<code>' . esc_html( _get_page_link( $this->page_on_front ) ) . '</code>'
144
  ); ?>
146
  }
147
 
148
  /**
149
+ * Displays the settings
150
  *
151
  * @since 1.8
152
  */
177
  }
178
 
179
  /**
180
+ * Sanitizes the settings before saving
181
  *
182
  * @since 1.8
183
  *
211
  $newoptions['browser'] = $newoptions['hide_default'] = 0;
212
  }
213
 
214
+ // Check if domains exist
215
  if ( $newoptions['force_lang'] > 1 ) {
216
  $this->check_domains( $newoptions );
217
  }
218
 
219
+ return $newoptions; // Take care to return only validated options
220
  }
221
 
222
  /**
223
+ * Check if subdomains or domains are accessible
224
  *
225
  * @since 1.8
226
  *
231
  $model = new PLL_Model( $options );
232
  $links_model = $model->get_links_model();
233
  foreach ( $this->model->get_languages_list() as $lang ) {
234
+ $url = add_query_arg( 'deactivate-polylang', 1, $links_model->home_url( $lang ) );
235
+ // Don't redefine vip_safe_wp_remote_get() as it has not the same signature as wp_remote_get()
236
+ $response = function_exists( 'vip_safe_wp_remote_get' ) ? vip_safe_wp_remote_get( esc_url_raw( $url ) ) : wp_remote_get( esc_url_raw( $url ) );
237
  $response_code = wp_remote_retrieve_response_code( $response );
238
 
239
  if ( 200 != $response_code ) {
settings/settings.php CHANGED
@@ -34,13 +34,13 @@ class PLL_Settings extends PLL_Admin_Base {
34
  PLL_Admin_Strings::init();
35
 
36
  // FIXME put this as late as possible
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 );
44
  }
45
 
46
  /**
@@ -49,6 +49,24 @@ class PLL_Settings extends PLL_Admin_Base {
49
  * @since 1.8
50
  */
51
  public function register_settings_modules() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  /**
53
  * Filter the list of setting modules
54
  *
@@ -56,18 +74,7 @@ class PLL_Settings extends PLL_Admin_Base {
56
  *
57
  * @param array $modules the list of module classes
58
  */
59
- $modules = apply_filters( 'pll_settings_modules', array(
60
- 'PLL_Settings_Url',
61
- 'PLL_Settings_Browser',
62
- 'PLL_Settings_Media',
63
- 'PLL_Settings_CPT',
64
- 'PLL_Settings_Sync',
65
- 'PLL_Settings_WPML',
66
- 'PLL_Settings_Share_Slug',
67
- 'PLL_Settings_Translate_Slugs',
68
- 'PLL_Settings_Tools',
69
- 'PLL_Settings_Licenses',
70
- ) );
71
 
72
  foreach ( $modules as $key => $class ) {
73
  $key = is_numeric( $key ) ? strtolower( str_replace( 'PLL_Settings_', '', $class ) ) : $key;
@@ -106,7 +113,7 @@ class PLL_Settings extends PLL_Admin_Base {
106
  add_meta_box(
107
  'pll-about-box',
108
  __( 'About Polylang', 'polylang' ),
109
- array( &$this, 'metabox_about' ),
110
  'settings_page_mlang',
111
  'normal'
112
  );
@@ -118,7 +125,7 @@ class PLL_Settings extends PLL_Admin_Base {
118
  'option' => 'pll_lang_per_page',
119
  ) );
120
 
121
- add_action( 'admin_notices', array( &$this, 'notice_objects_with_no_lang' ) );
122
  break;
123
 
124
  case 'strings':
@@ -143,7 +150,7 @@ class PLL_Settings extends PLL_Admin_Base {
143
  * @return string New value if this is our option, otherwise nothing
144
  */
145
  public function set_screen_option( $status, $option, $value ) {
146
- return 'pll_strings_per_page' === $option ? $value : $status;
147
  }
148
 
149
  /**
@@ -249,9 +256,10 @@ class PLL_Settings extends PLL_Admin_Base {
249
  // only if at least one language has been created
250
  if ( $listlanguages = $this->model->get_languages_list() ) {
251
  $tabs['strings'] = __( 'Strings translations', 'polylang' );
252
- $tabs['settings'] = __( 'Settings', 'polylang' );
253
  }
254
 
 
 
255
  /**
256
  * Filter the list of tabs in Polylang settings
257
  *
@@ -309,9 +317,9 @@ class PLL_Settings extends PLL_Admin_Base {
309
  if ( ! empty( $this->options['default_lang'] ) && $this->model->get_objects_with_no_lang() ) {
310
  printf(
311
  '<div class="error"><p>%s <a href="%s">%s</a></p></div>',
312
- __( 'There are posts, pages, categories or tags without language.', 'polylang' ),
313
  wp_nonce_url( '?page=mlang&amp;pll_action=content-default-lang&amp;noheader=true', 'content-default-lang' ),
314
- __( 'You can set them all to the default language.', 'polylang' )
315
  );
316
  }
317
  }
34
  PLL_Admin_Strings::init();
35
 
36
  // FIXME put this as late as possible
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 );
44
  }
45
 
46
  /**
49
  * @since 1.8
50
  */
51
  public function register_settings_modules() {
52
+ $modules = array(
53
+ 'PLL_Settings_Tools',
54
+ 'PLL_Settings_Licenses',
55
+ );
56
+
57
+ if ( $this->model->get_languages_list() ) {
58
+ $modules = array_merge( array(
59
+ 'PLL_Settings_Url',
60
+ 'PLL_Settings_Browser',
61
+ 'PLL_Settings_Media',
62
+ 'PLL_Settings_CPT',
63
+ 'PLL_Settings_Sync',
64
+ 'PLL_Settings_WPML',
65
+ 'PLL_Settings_Share_Slug',
66
+ 'PLL_Settings_Translate_Slugs',
67
+ ), $modules );
68
+ }
69
+
70
  /**
71
  * Filter the list of setting modules
72
  *
74
  *
75
  * @param array $modules the list of module classes
76
  */
77
+ $modules = apply_filters( 'pll_settings_modules', $modules );
 
 
 
 
 
 
 
 
 
 
 
78
 
79
  foreach ( $modules as $key => $class ) {
80
  $key = is_numeric( $key ) ? strtolower( str_replace( 'PLL_Settings_', '', $class ) ) : $key;
113
  add_meta_box(
114
  'pll-about-box',
115
  __( 'About Polylang', 'polylang' ),
116
+ array( $this, 'metabox_about' ),
117
  'settings_page_mlang',
118
  'normal'
119
  );
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':
150
  * @return string New value if this is our option, otherwise nothing
151
  */
152
  public function set_screen_option( $status, $option, $value ) {
153
+ return 'pll_lang_per_page' === $option || 'pll_strings_per_page' === $option ? $value : $status;
154
  }
155
 
156
  /**
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
  *
317
  if ( ! empty( $this->options['default_lang'] ) && $this->model->get_objects_with_no_lang() ) {
318
  printf(
319
  '<div class="error"><p>%s <a href="%s">%s</a></p></div>',
320
+ esc_html__( 'There are posts, pages, categories or tags without language.', 'polylang' ),
321
  wp_nonce_url( '?page=mlang&amp;pll_action=content-default-lang&amp;noheader=true', 'content-default-lang' ),
322
+ esc_html__( 'You can set them all to the default language.', 'polylang' )
323
  );
324
  }
325
  }
settings/table-languages.php CHANGED
@@ -5,7 +5,7 @@ if ( ! class_exists( 'WP_List_Table' ) ) {
5
  }
6
 
7
  /**
8
- * a class to create the languages table in Polylang settings
9
  * Thanks to Matt Van Andel ( http://www.mattvanandel.com ) for its plugin "Custom List Table Example" !
10
  *
11
  * @since 0.1
@@ -13,13 +13,13 @@ if ( ! class_exists( 'WP_List_Table' ) ) {
13
  class PLL_Table_Languages extends WP_List_Table {
14
 
15
  /**
16
- * constructor
17
  *
18
  * @since 0.1
19
  */
20
  function __construct() {
21
  parent::__construct( array(
22
- 'plural' => 'Languages', // do not translate ( used for css class )
23
  'ajax' => false,
24
  ) );
25
  }
@@ -32,7 +32,7 @@ class PLL_Table_Languages extends WP_List_Table {
32
  * @param object $item The current item
33
  */
34
  public function single_row( $item ) {
35
- /*
36
  * Filter the list of classes assigned a row in the languages list table
37
  *
38
  * @since 1.8
@@ -47,7 +47,7 @@ class PLL_Table_Languages extends WP_List_Table {
47
  }
48
 
49
  /**
50
- * displays the item information in a column ( default case )
51
  *
52
  * @since 0.1
53
  *
@@ -71,8 +71,8 @@ class PLL_Table_Languages extends WP_List_Table {
71
  }
72
 
73
  /**
74
- * displays the item information in the column 'name'
75
- * displays the edit and delete action links
76
  *
77
  * @since 0.1
78
  *
@@ -82,15 +82,15 @@ class PLL_Table_Languages extends WP_List_Table {
82
  function column_name( $item ) {
83
  return sprintf(
84
  '<a title="%s" href="%s">%s</a>',
85
- __( '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
  }
90
 
91
  /**
92
- * displays the item information in the default language
93
- * displays the 'make default' action link
94
  *
95
  * @since 1.8
96
  *
@@ -105,13 +105,13 @@ class PLL_Table_Languages extends WP_List_Table {
105
  <div class="row-actions"><span class="default-lang">
106
  <a class="icon-default-lang" title="%1$s" href="%2$s"><span class="screen-reader-text">%3$s</span></a>
107
  </span></div>',
108
- __( 'Select as default language', 'polylang' ),
109
  wp_nonce_url( '?page=mlang&amp;pll_action=default-lang&amp;noheader=true&amp;lang=' . $item->term_id, 'default-lang' ),
110
- /* translators: %s is a native language name */
111
  esc_html( sprintf( __( 'Choose %s as default language', 'polylang' ), $item->name ) )
112
  );
113
 
114
- /*
115
  * Filter the default language row action in the languages list table
116
  *
117
  * @since 1.8
@@ -123,7 +123,8 @@ class PLL_Table_Languages extends WP_List_Table {
123
  } else {
124
  $s = sprintf(
125
  '<span class="icon-default-lang"><span class="screen-reader-text">%1$s</span></span>',
126
- __( 'Default language', 'polylang' )
 
127
  );
128
  $actions = array();
129
  }
@@ -132,7 +133,7 @@ class PLL_Table_Languages extends WP_List_Table {
132
  }
133
 
134
  /**
135
- * gets the list of columns
136
  *
137
  * @since 0.1
138
  *
@@ -140,18 +141,18 @@ class PLL_Table_Languages extends WP_List_Table {
140
  */
141
  function get_columns() {
142
  return array(
143
- 'name' => __( 'Full name', 'polylang' ),
144
- 'locale' => __( 'Locale', 'polylang' ),
145
- 'slug' => __( 'Code', 'polylang' ),
146
- 'default_lang' => sprintf( '<span title="%1$s" class="icon-default-lang"><span class="screen-reader-text">%1$s</span></span>', __( 'Default language', 'polylang' ) ),
147
- 'term_group' => __( 'Order', 'polylang' ),
148
- 'flag' => __( 'Flag', 'polylang' ),
149
- 'count' => __( 'Posts', 'polylang' ),
150
  );
151
  }
152
 
153
  /**
154
- * gets the list of sortable columns
155
  *
156
  * @since 0.1
157
  *
@@ -185,20 +186,20 @@ class PLL_Table_Languages extends WP_List_Table {
185
  $actions = array(
186
  'edit' => sprintf(
187
  '<a title="%s" href="%s">%s</a>',
188
- __( 'Edit this language', 'polylang' ),
189
  esc_url( admin_url( 'options-general.php?page=mlang&amp;pll_action=edit&amp;lang=' . $item->term_id ) ),
190
- __( 'Edit','polylang' )
191
  ),
192
  'delete' => sprintf(
193
  '<a title="%s" href="%s" onclick = "return confirm( \'%s\' );">%s</a>',
194
- __( 'Delete this language and all its associated data', 'polylang' ),
195
  wp_nonce_url( '?page=mlang&amp;pll_action=delete&amp;noheader=true&amp;lang=' . $item->term_id, 'delete-lang' ),
196
- __( 'You are about to permanently delete this language. Are you sure?', 'polylang' ),
197
- __( 'Delete','polylang' )
198
  ),
199
  );
200
 
201
- /*
202
  * Filter the list of row actions in the languages list table
203
  *
204
  * @since 1.8
@@ -222,18 +223,18 @@ class PLL_Table_Languages extends WP_List_Table {
222
  */
223
  protected function usort_reorder( $a, $b ) {
224
  $orderby = ! empty( $_GET['orderby'] ) ? $_GET['orderby'] : 'name';
225
- // determine sort order
226
  if ( is_numeric( $a->$orderby ) ) {
227
  $result = $a->$orderby > $b->$orderby ? 1 : -1;
228
  } else {
229
  $result = strcmp( $a->$orderby, $b->$orderby );
230
  }
231
- // send final sort direction to usort
232
  return ( empty( $_GET['order'] ) || 'asc' == $_GET['order'] ) ? $result : -$result;
233
  }
234
 
235
  /**
236
- * prepares the list of items for displaying
237
  *
238
  * @since 0.1
239
  *
@@ -243,7 +244,7 @@ class PLL_Table_Languages extends WP_List_Table {
243
  $per_page = $this->get_items_per_page( 'pll_lang_per_page' );
244
  $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
245
 
246
- usort( $data, array( &$this, 'usort_reorder' ) );
247
 
248
  $total_items = count( $data );
249
  $this->items = array_slice( $data, ( $this->get_pagenum() - 1 ) * $per_page, $per_page );
5
  }
6
 
7
  /**
8
+ * A class to create the languages table in Polylang settings
9
  * Thanks to Matt Van Andel ( http://www.mattvanandel.com ) for its plugin "Custom List Table Example" !
10
  *
11
  * @since 0.1
13
  class PLL_Table_Languages extends WP_List_Table {
14
 
15
  /**
16
+ * Constructor
17
  *
18
  * @since 0.1
19
  */
20
  function __construct() {
21
  parent::__construct( array(
22
+ 'plural' => 'Languages', // Do not translate ( used for css class )
23
  'ajax' => false,
24
  ) );
25
  }
32
  * @param object $item The current item
33
  */
34
  public function single_row( $item ) {
35
+ /**
36
  * Filter the list of classes assigned a row in the languages list table
37
  *
38
  * @since 1.8
47
  }
48
 
49
  /**
50
+ * Displays the item information in a column ( default case )
51
  *
52
  * @since 0.1
53
  *
71
  }
72
 
73
  /**
74
+ * Displays the item information in the column 'name'
75
+ * Displays the edit and delete action links
76
  *
77
  * @since 0.1
78
  *
82
  function column_name( $item ) {
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
  }
90
 
91
  /**
92
+ * Displays the item information in the default language
93
+ * Displays the 'make default' action link
94
  *
95
  * @since 1.8
96
  *
105
  <div class="row-actions"><span class="default-lang">
106
  <a class="icon-default-lang" title="%1$s" href="%2$s"><span class="screen-reader-text">%3$s</span></a>
107
  </span></div>',
108
+ esc_attr__( 'Select as default language', 'polylang' ),
109
  wp_nonce_url( '?page=mlang&amp;pll_action=default-lang&amp;noheader=true&amp;lang=' . $item->term_id, 'default-lang' ),
110
+ /* translators: accessibility text, %s is a native language name */
111
  esc_html( sprintf( __( 'Choose %s as default language', 'polylang' ), $item->name ) )
112
  );
113
 
114
+ /**
115
  * Filter the default language row action in the languages list table
116
  *
117
  * @since 1.8
123
  } else {
124
  $s = sprintf(
125
  '<span class="icon-default-lang"><span class="screen-reader-text">%1$s</span></span>',
126
+ /* translators: accessibility text */
127
+ esc_html__( 'Default language', 'polylang' )
128
  );
129
  $actions = array();
130
  }
133
  }
134
 
135
  /**
136
+ * Gets the list of columns
137
  *
138
  * @since 0.1
139
  *
141
  */
142
  function get_columns() {
143
  return array(
144
+ 'name' => esc_html__( 'Full name', 'polylang' ),
145
+ 'locale' => esc_html__( 'Locale', 'polylang' ),
146
+ 'slug' => esc_html__( 'Code', 'polylang' ),
147
+ 'default_lang' => sprintf( '<span title="%1$s" class="icon-default-lang"><span class="screen-reader-text">%2$s</span></span>', esc_attr__( 'Default language', 'polylang' ), esc_html__( 'Default language', 'polylang' ) ),
148
+ 'term_group' => esc_html__( 'Order', 'polylang' ),
149
+ 'flag' => esc_html__( 'Flag', 'polylang' ),
150
+ 'count' => esc_html__( 'Posts', 'polylang' ),
151
  );
152
  }
153
 
154
  /**
155
+ * Gets the list of sortable columns
156
  *
157
  * @since 0.1
158
  *
186
  $actions = array(
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(
194
  '<a title="%s" href="%s" onclick = "return confirm( \'%s\' );">%s</a>',
195
+ esc_attr__( 'Delete this language and all its associated data', 'polylang' ),
196
  wp_nonce_url( '?page=mlang&amp;pll_action=delete&amp;noheader=true&amp;lang=' . $item->term_id, 'delete-lang' ),
197
+ esc_js( __( 'You are about to permanently delete this language. Are you sure?', 'polylang' ) ),
198
+ esc_html__( 'Delete','polylang' )
199
  ),
200
  );
201
 
202
+ /**
203
  * Filter the list of row actions in the languages list table
204
  *
205
  * @since 1.8
223
  */
224
  protected function usort_reorder( $a, $b ) {
225
  $orderby = ! empty( $_GET['orderby'] ) ? $_GET['orderby'] : 'name';
226
+ // Determine sort order
227
  if ( is_numeric( $a->$orderby ) ) {
228
  $result = $a->$orderby > $b->$orderby ? 1 : -1;
229
  } else {
230
  $result = strcmp( $a->$orderby, $b->$orderby );
231
  }
232
+ // Send final sort direction to usort
233
  return ( empty( $_GET['order'] ) || 'asc' == $_GET['order'] ) ? $result : -$result;
234
  }
235
 
236
  /**
237
+ * Prepares the list of items for displaying
238
  *
239
  * @since 0.1
240
  *
244
  $per_page = $this->get_items_per_page( 'pll_lang_per_page' );
245
  $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
246
 
247
+ usort( $data, array( $this, 'usort_reorder' ) );
248
 
249
  $total_items = count( $data );
250
  $this->items = array_slice( $data, ( $this->get_pagenum() - 1 ) * $per_page, $per_page );
settings/table-settings.php CHANGED
@@ -5,65 +5,68 @@ if ( ! class_exists( 'WP_List_Table' ) ) {
5
  }
6
 
7
  /**
8
- * a class to create a table to list all settings modules
9
  *
10
  * @since 1.8
11
  */
12
  class PLL_Table_Settings extends WP_List_Table {
13
 
14
  /**
15
- * constructor
16
  *
17
  * @since 1.8
18
  */
19
  function __construct() {
20
  parent::__construct( array(
21
- 'plural' => 'Settings', // do not translate ( used for css class )
22
  'ajax' => false,
23
  ) );
24
  }
25
 
26
  /**
27
- * get the table classes for styling
28
  *
29
- * @øince 1.8
30
  */
31
  protected function get_table_classes() {
32
  return array( 'wp-list-table', 'widefat', 'plugins', 'pll-settings' ); // get the style of the plugins list table + one specific class
33
  }
34
 
35
  /**
36
- * displays a single row
37
  *
38
- * @øince 1.8
39
  *
40
  * @param object $item PLL_Settings_Module object
41
  */
42
  public function single_row( $item ) {
43
- // classes to reuse css from the plugins list table
44
  $classes = $item->is_active() ? 'active' : 'inactive';
45
  if ( $message = $item->get_upgrade_message() ) {
46
  $classes .= ' update';
47
  }
48
 
49
- // display the columns
50
  printf( '<tr id="pll-module-%s" class="%s">', esc_attr( $item->module ), esc_attr( $classes ) );
51
  $this->single_row_columns( $item );
52
  echo '</tr>';
53
 
54
- // display an upgrade message if there is any
55
  if ( $message = $item->get_upgrade_message() ) {
56
  printf( '
57
  <tr class="plugin-update-tr">
58
- <td colspan="3" class="plugin-update colspanchange">
59
- <div class="update-message">%s</div>
60
- </td>
61
  </tr>',
62
- $message
 
 
 
 
 
63
  );
64
  }
65
 
66
- // the settings if there are
67
  // "inactive" class to reuse css from the plugins list table
68
  if ( $form = $item->get_form() ) {
69
  printf( '
@@ -115,7 +118,7 @@ class PLL_Table_Settings extends WP_List_Table {
115
  }
116
 
117
  /**
118
- * added for backward compatibility with WP < 4.2
119
  *
120
  * @since 1.8.2
121
  *
@@ -124,7 +127,7 @@ class PLL_Table_Settings extends WP_List_Table {
124
  protected function column_cb( $item ) {}
125
 
126
  /**
127
- * displays the item information in a column ( default case )
128
  *
129
  * @since 1.8
130
  *
@@ -140,7 +143,7 @@ class PLL_Table_Settings extends WP_List_Table {
140
  }
141
 
142
  /**
143
- * gets the list of columns
144
  *
145
  * @since 1.8
146
  *
@@ -148,9 +151,9 @@ class PLL_Table_Settings extends WP_List_Table {
148
  */
149
  public function get_columns() {
150
  return array(
151
- 'cb' => '', // for the 4px border inherited from plugins when the module is activated
152
- 'plugin-title' => __( 'Module', 'polylang' ), // plugin-title for styling
153
- 'description' => __( 'Description', 'polylang' ),
154
  );
155
  }
156
 
@@ -166,7 +169,7 @@ class PLL_Table_Settings extends WP_List_Table {
166
  }
167
 
168
  /**
169
- * prepares the list of items for displaying
170
  *
171
  * @since 1.8
172
  *
5
  }
6
 
7
  /**
8
+ * A class to create a table to list all settings modules
9
  *
10
  * @since 1.8
11
  */
12
  class PLL_Table_Settings extends WP_List_Table {
13
 
14
  /**
15
+ * Constructor
16
  *
17
  * @since 1.8
18
  */
19
  function __construct() {
20
  parent::__construct( array(
21
+ 'plural' => 'Settings', // Do not translate ( used for css class )
22
  'ajax' => false,
23
  ) );
24
  }
25
 
26
  /**
27
+ * Get the table classes for styling
28
  *
29
+ * @since 1.8
30
  */
31
  protected function get_table_classes() {
32
  return array( 'wp-list-table', 'widefat', 'plugins', 'pll-settings' ); // get the style of the plugins list table + one specific class
33
  }
34
 
35
  /**
36
+ * Displays a single row
37
  *
38
+ * @since 1.8
39
  *
40
  * @param object $item PLL_Settings_Module object
41
  */
42
  public function single_row( $item ) {
43
+ // Classes to reuse css from the plugins list table
44
  $classes = $item->is_active() ? 'active' : 'inactive';
45
  if ( $message = $item->get_upgrade_message() ) {
46
  $classes .= ' update';
47
  }
48
 
49
+ // Display the columns
50
  printf( '<tr id="pll-module-%s" class="%s">', esc_attr( $item->module ), esc_attr( $classes ) );
51
  $this->single_row_columns( $item );
52
  echo '</tr>';
53
 
54
+ // Display an upgrade message if there is any, reusing css from the plugins updates
55
  if ( $message = $item->get_upgrade_message() ) {
56
  printf( '
57
  <tr class="plugin-update-tr">
58
+ <td colspan="3" class="plugin-update colspanchange">%s</td>
 
 
59
  </tr>',
60
+ sprintf(
61
+ version_compare( $GLOBALS['wp_version'], '4.6', '<' ) ?
62
+ '<div class="update-message">%s</div>' : // backward compatibility with WP < 4.6
63
+ '<div class="update-message notice inline notice-warning notice-alt"><p>%s</p></div>',
64
+ $message
65
+ )
66
  );
67
  }
68
 
69
+ // The settings if there are
70
  // "inactive" class to reuse css from the plugins list table
71
  if ( $form = $item->get_form() ) {
72
  printf( '
118
  }
119
 
120
  /**
121
+ * Added for backward compatibility with WP < 4.2
122
  *
123
  * @since 1.8.2
124
  *
127
  protected function column_cb( $item ) {}
128
 
129
  /**
130
+ * Displays the item information in a column ( default case )
131
  *
132
  * @since 1.8
133
  *
143
  }
144
 
145
  /**
146
+ * Gets the list of columns
147
  *
148
  * @since 1.8
149
  *
151
  */
152
  public function get_columns() {
153
  return array(
154
+ 'cb' => '', // For the 4px border inherited from plugins when the module is activated
155
+ 'plugin-title' => esc_html__( 'Module', 'polylang' ), // plugin-title for styling
156
+ 'description' => esc_html__( 'Description', 'polylang' ),
157
  );
158
  }
159
 
169
  }
170
 
171
  /**
172
+ * Prepares the list of items for displaying
173
  *
174
  * @since 1.8
175
  *
settings/table-string.php CHANGED
@@ -22,7 +22,7 @@ class PLL_Table_String extends WP_List_Table {
22
  */
23
  function __construct( $languages ) {
24
  parent::__construct( array(
25
- 'plural' => 'Strings translations', // do not translate ( used for css class )
26
  'ajax' => false,
27
  ) );
28
 
@@ -31,7 +31,7 @@ class PLL_Table_String extends WP_List_Table {
31
  $this->groups = array_unique( wp_list_pluck( $this->strings, 'context' ) );
32
  $this->selected_group = empty( $_GET['group'] ) || ! in_array( $_GET['group'], $this->groups ) ? -1 : $_GET['group'];
33
 
34
- add_action( 'mlang_action_string-translation', array( &$this, 'save_translations' ) );
35
  }
36
 
37
  /**
@@ -59,9 +59,9 @@ class PLL_Table_String extends WP_List_Table {
59
  return sprintf(
60
  '<label class="screen-reader-text" for="cb-select-%1$s">%2$s</label><input id="cb-select-%1$s" type="checkbox" name="strings[]" value="%1$s" %3$s />',
61
  esc_attr( $item['row'] ),
62
- /* translators: %s is a string potentially in any language */
63
  sprintf( __( 'Select %s' ), format_to_edit( $item['string'] ) ),
64
- empty( $item['icl'] ) ? 'disabled' : '' // only strings registered with WPML API can be removed
65
  );
66
  }
67
 
@@ -74,7 +74,7 @@ class PLL_Table_String extends WP_List_Table {
74
  * @return string
75
  */
76
  function column_string( $item ) {
77
- return format_to_edit( $item['string'] ); // don't interpret special chars for the string column
78
  }
79
 
80
  /**
@@ -97,7 +97,7 @@ class PLL_Table_String extends WP_List_Table {
97
  esc_attr( $key ),
98
  esc_attr( $item['row'] ),
99
  esc_html( $languages[ $key ] ),
100
- format_to_edit( $translation ) ); // don't interpret special chars
101
  }
102
 
103
  return $out;
@@ -112,11 +112,11 @@ class PLL_Table_String extends WP_List_Table {
112
  */
113
  function get_columns() {
114
  return array(
115
- 'cb' => '<input type="checkbox" />', // checkbox
116
- 'string' => __( 'String', 'polylang' ),
117
- 'name' => __( 'Name', 'polylang' ),
118
- 'context' => __( 'Group', 'polylang' ),
119
- 'translations' => __( 'Translations', 'polylang' ),
120
  );
121
  }
122
 
@@ -157,7 +157,7 @@ class PLL_Table_String extends WP_List_Table {
157
  function prepare_items() {
158
  $data = $this->strings;
159
 
160
- // filter for search string
161
  $s = empty( $_GET['s'] ) ? '' : wp_unslash( $_GET['s'] );
162
  foreach ( $data as $key => $row ) {
163
  if ( ( -1 !== $this->selected_group && $row['context'] !== $this->selected_group ) || ( ! empty( $s ) && stripos( $row['name'], $s ) === false && stripos( $row['string'], $s ) === false ) ) {
@@ -165,9 +165,9 @@ class PLL_Table_String extends WP_List_Table {
165
  }
166
  }
167
 
168
- // load translations
169
  foreach ( $this->languages as $language ) {
170
- // filters by language if requested
171
  if ( ( $lg = get_user_meta( get_current_user_id(), 'pll_filter_content', true ) ) && $language->slug !== $lg ) {
172
  continue;
173
  }
@@ -176,15 +176,15 @@ class PLL_Table_String extends WP_List_Table {
176
  $mo->import_from_db( $language );
177
  foreach ( $data as $key => $row ) {
178
  $data[ $key ]['translations'][ $language->slug ] = $mo->translate( $row['string'] );
179
- $data[ $key ]['row'] = $key; // store the row number for convenience
180
  }
181
  }
182
 
183
  $per_page = $this->get_items_per_page( 'pll_strings_per_page' );
184
  $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
185
 
186
- if ( ! empty( $_GET['orderby'] ) ) { // no sort by default
187
- usort( $data, array( &$this, 'usort_reorder' ) );
188
  }
189
 
190
  $total_items = count( $data );
@@ -233,12 +233,16 @@ class PLL_Table_String extends WP_List_Table {
233
  }
234
 
235
  echo '<div class="alignleft actions">';
236
- printf( '<label class="screen-reader-text" for="select-group" >%s</label>', __( 'Filter by group', 'polylang' ) );
 
 
 
 
237
  echo '<select id="select-group" name="group">' . "\n";
238
  printf(
239
  '<option value="-1"%s>%s</option>' . "\n",
240
  -1 === $this->group_selected ? ' selected="selected"' : '',
241
- __( 'View all groups', 'polylang' )
242
  );
243
 
244
  foreach ( $this->groups as $group ) {
@@ -266,7 +270,7 @@ class PLL_Table_String extends WP_List_Table {
266
 
267
  if ( ! empty( $_POST['submit'] ) ) {
268
  foreach ( $this->languages as $language ) {
269
- if ( empty( $_POST['translation'][ $language->slug ] ) ) { // in case the language filter is active ( thanks to John P. Bloch )
270
  continue;
271
  }
272
 
@@ -274,7 +278,7 @@ class PLL_Table_String extends WP_List_Table {
274
  $mo->import_from_db( $language );
275
 
276
  foreach ( $_POST['translation'][ $language->slug ] as $key => $translation ) {
277
- /*
278
  * Filter the string translation before it is saved in DB
279
  * Allows to sanitize strings registered with pll_register_string
280
  *
@@ -288,7 +292,7 @@ class PLL_Table_String extends WP_List_Table {
288
  $mo->add_entry( $mo->make_entry( $this->strings[ $key ]['string'], $translation ) );
289
  }
290
 
291
- // clean database ( removes all strings which were registered some day but are no more )
292
  if ( ! empty( $_POST['clean'] ) ) {
293
  $new_mo = new PLL_MO();
294
 
@@ -310,7 +314,7 @@ class PLL_Table_String extends WP_List_Table {
310
  do_action( 'pll_save_strings_translations' );
311
  }
312
 
313
- // unregisters strings registered through WPML API
314
  if ( $this->current_action() === 'delete' && ! empty( $_POST['strings'] ) && function_exists( 'icl_unregister_string' ) ) {
315
  foreach ( $_POST['strings'] as $key ) {
316
  icl_unregister_string( $this->strings[ $key ]['context'], $this->strings[ $key ]['name'] );
@@ -318,12 +322,12 @@ class PLL_Table_String extends WP_List_Table {
318
  }
319
 
320
  // To refresh the page ( possible thanks to the $_GET['noheader']=true )
321
- $args = array_intersect_key( $_REQUEST, array_flip( array( 's', 'group' ) ) );
322
- if ( ! empty( $_GET['paged'] ) ) {
323
  $args['paged'] = (int) $_GET['paged']; // Don't rely on $_REQUEST['paged'] or $_POST['paged']. See #14
324
  }
325
  if ( ! empty( $args['s'] ) ) {
326
- $args['s'] = urlencode( $args['s'] ); // searched string needs to be encoded as it comes from $_POST
327
  }
328
  PLL_Settings::redirect( $args );
329
  }
22
  */
23
  function __construct( $languages ) {
24
  parent::__construct( array(
25
+ 'plural' => 'Strings translations', // Do not translate ( used for css class )
26
  'ajax' => false,
27
  ) );
28
 
31
  $this->groups = array_unique( wp_list_pluck( $this->strings, 'context' ) );
32
  $this->selected_group = empty( $_GET['group'] ) || ! in_array( $_GET['group'], $this->groups ) ? -1 : $_GET['group'];
33
 
34
+ add_action( 'mlang_action_string-translation', array( $this, 'save_translations' ) );
35
  }
36
 
37
  /**
59
  return sprintf(
60
  '<label class="screen-reader-text" for="cb-select-%1$s">%2$s</label><input id="cb-select-%1$s" type="checkbox" name="strings[]" value="%1$s" %3$s />',
61
  esc_attr( $item['row'] ),
62
+ /* translators: accessibility text, %s is a string potentially in any language */
63
  sprintf( __( 'Select %s' ), format_to_edit( $item['string'] ) ),
64
+ empty( $item['icl'] ) ? 'disabled' : '' // Only strings registered with WPML API can be removed
65
  );
66
  }
67
 
74
  * @return string
75
  */
76
  function column_string( $item ) {
77
+ return format_to_edit( $item['string'] ); // Don't interpret special chars for the string column
78
  }
79
 
80
  /**
97
  esc_attr( $key ),
98
  esc_attr( $item['row'] ),
99
  esc_html( $languages[ $key ] ),
100
+ format_to_edit( $translation ) ); // Don't interpret special chars
101
  }
102
 
103
  return $out;
112
  */
113
  function get_columns() {
114
  return array(
115
+ 'cb' => '<input type="checkbox" />', // Checkbox
116
+ 'string' => esc_html__( 'String', 'polylang' ),
117
+ 'name' => esc_html__( 'Name', 'polylang' ),
118
+ 'context' => esc_html__( 'Group', 'polylang' ),
119
+ 'translations' => esc_html__( 'Translations', 'polylang' ),
120
  );
121
  }
122
 
157
  function prepare_items() {
158
  $data = $this->strings;
159
 
160
+ // Filter for search string
161
  $s = empty( $_GET['s'] ) ? '' : wp_unslash( $_GET['s'] );
162
  foreach ( $data as $key => $row ) {
163
  if ( ( -1 !== $this->selected_group && $row['context'] !== $this->selected_group ) || ( ! empty( $s ) && stripos( $row['name'], $s ) === false && stripos( $row['string'], $s ) === false ) ) {
165
  }
166
  }
167
 
168
+ // Load translations
169
  foreach ( $this->languages as $language ) {
170
+ // Filters by language if requested
171
  if ( ( $lg = get_user_meta( get_current_user_id(), 'pll_filter_content', true ) ) && $language->slug !== $lg ) {
172
  continue;
173
  }
176
  $mo->import_from_db( $language );
177
  foreach ( $data as $key => $row ) {
178
  $data[ $key ]['translations'][ $language->slug ] = $mo->translate( $row['string'] );
179
+ $data[ $key ]['row'] = $key; // Store the row number for convenience
180
  }
181
  }
182
 
183
  $per_page = $this->get_items_per_page( 'pll_strings_per_page' );
184
  $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
185
 
186
+ if ( ! empty( $_GET['orderby'] ) ) { // No sort by default
187
+ usort( $data, array( $this, 'usort_reorder' ) );
188
  }
189
 
190
  $total_items = count( $data );
233
  }
234
 
235
  echo '<div class="alignleft actions">';
236
+ printf(
237
+ '<label class="screen-reader-text" for="select-group" >%s</label>',
238
+ /* translators: accessibility text */
239
+ esc_html__( 'Filter by group', 'polylang' )
240
+ );
241
  echo '<select id="select-group" name="group">' . "\n";
242
  printf(
243
  '<option value="-1"%s>%s</option>' . "\n",
244
  -1 === $this->group_selected ? ' selected="selected"' : '',
245
+ esc_html__( 'View all groups', 'polylang' )
246
  );
247
 
248
  foreach ( $this->groups as $group ) {
270
 
271
  if ( ! empty( $_POST['submit'] ) ) {
272
  foreach ( $this->languages as $language ) {
273
+ if ( empty( $_POST['translation'][ $language->slug ] ) ) { // In case the language filter is active ( thanks to John P. Bloch )
274
  continue;
275
  }
276
 
278
  $mo->import_from_db( $language );
279
 
280
  foreach ( $_POST['translation'][ $language->slug ] as $key => $translation ) {
281
+ /**
282
  * Filter the string translation before it is saved in DB
283
  * Allows to sanitize strings registered with pll_register_string
284
  *
292
  $mo->add_entry( $mo->make_entry( $this->strings[ $key ]['string'], $translation ) );
293
  }
294
 
295
+ // Clean database ( removes all strings which were registered some day but are no more )
296
  if ( ! empty( $_POST['clean'] ) ) {
297
  $new_mo = new PLL_MO();
298
 
314
  do_action( 'pll_save_strings_translations' );
315
  }
316
 
317
+ // Unregisters strings registered through WPML API
318
  if ( $this->current_action() === 'delete' && ! empty( $_POST['strings'] ) && function_exists( 'icl_unregister_string' ) ) {
319
  foreach ( $_POST['strings'] as $key ) {
320
  icl_unregister_string( $this->strings[ $key ]['context'], $this->strings[ $key ]['name'] );
322
  }
323
 
324
  // To refresh the page ( possible thanks to the $_GET['noheader']=true )
325
+ $args = array_intersect_key( $_REQUEST, array_flip( array( 's', 'paged', 'group' ) ) );
326
+ if ( ! empty( $_GET['paged'] ) && ! empty( $_POST['submit'] ) ) {
327
  $args['paged'] = (int) $_GET['paged']; // Don't rely on $_REQUEST['paged'] or $_POST['paged']. See #14
328
  }
329
  if ( ! empty( $args['s'] ) ) {
330
+ $args['s'] = urlencode( $args['s'] ); // Searched string needs to be encoded as it comes from $_POST
331
  }
332
  PLL_Settings::redirect( $args );
333
  }
settings/view-about.php CHANGED
@@ -11,7 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
11
  <p><?php
12
  printf(
13
  /* translators: %s are html tags */
14
- __( 'Polylang is provided with an extensive %sdocumentation%s (in English only). It includes information on how to set up your multilingual site and use it on a daily basis, a FAQ, as well as a documentation for developers to adapt their plugins and themes.', 'polylang' ),
15
  '<a href="https://polylang.pro/doc/">',
16
  '</a>'
17
  );
@@ -19,7 +19,7 @@ if ( ! defined( 'POLYLANG_PRO' ) ) {
19
  echo ' ';
20
  printf(
21
  /* translators: %s are html tags */
22
- __( 'Support and extra features are available to %sPolylang Pro%s users.' ),
23
  '<a href="https://polylang.pro">',
24
  '</a>'
25
  );
@@ -28,7 +28,7 @@ if ( ! defined( 'POLYLANG_PRO' ) ) {
28
  <p><?php
29
  printf(
30
  /* translators: %s are html tags */
31
- __( 'Polylang is released under the same license as WordPress, the %sGPL%s.', 'polylang' ),
32
  '<a href="http://wordpress.org/about/gpl/">',
33
  '</a>'
34
  );
11
  <p><?php
12
  printf(
13
  /* translators: %s are html tags */
14
+ esc_html__( 'Polylang is provided with an extensive %sdocumentation%s (in English only). It includes information on how to set up your multilingual site and use it on a daily basis, a FAQ, as well as a documentation for developers to adapt their plugins and themes.', 'polylang' ),
15
  '<a href="https://polylang.pro/doc/">',
16
  '</a>'
17
  );
19
  echo ' ';
20
  printf(
21
  /* translators: %s are html tags */
22
+ esc_html__( 'Support and extra features are available to %sPolylang Pro%s users.' ),
23
  '<a href="https://polylang.pro">',
24
  '</a>'
25
  );
28
  <p><?php
29
  printf(
30
  /* translators: %s are html tags */
31
+ esc_html__( 'Polylang is released under the same license as WordPress, the %sGPL%s.', 'polylang' ),
32
  '<a href="http://wordpress.org/about/gpl/">',
33
  '</a>'
34
  );
settings/view-languages.php CHANGED
@@ -9,7 +9,7 @@ if ( ! defined( 'ABSPATH' ) ) {
9
  };
10
  ?>
11
  <div class="wrap">
12
- <h1><?php _e( 'Languages', 'polylang' ); ?></h1>
13
  <h2 class="nav-tab-wrapper"><?php
14
  // display tabs
15
  foreach ( $tabs as $key => $name ) {
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 ) {
settings/view-tab-lang.php CHANGED
@@ -24,7 +24,7 @@ if ( ! defined( 'ABSPATH' ) ) {
24
  <div class="col-wrap">
25
 
26
  <div class="form-wrap">
27
- <h3><?php echo ! empty( $edit_lang ) ? __( 'Edit language', 'polylang' ) : __( 'Add new language', 'polylang' ); ?></h3><?php
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 ?>
@@ -39,13 +39,13 @@ if ( ! defined( 'ABSPATH' ) ) {
39
  }?>
40
 
41
  <div class="form-field">
42
- <label for="lang_list"><?php _e( 'Choose a language', 'polylang' );?></label>
43
  <select name="lang_list" id="lang_list">
44
  <option value=""></option><?php
45
  include( PLL_SETTINGS_INC.'/languages.php' );
46
  foreach ( $languages as $lg ) {
47
  printf(
48
- '<option value="%1$s-%2$s-%3$s-%4$s">%5$s - %2$s</option>'."\n",
49
  esc_attr( $lg[0] ),
50
  esc_attr( $lg[1] ),
51
  'rtl' == $lg[3] ? '1' : '0',
@@ -54,53 +54,53 @@ if ( ! defined( 'ABSPATH' ) ) {
54
  );
55
  } ?>
56
  </select>
57
- <p><?php _e( 'You can choose a language in the list or directly edit it below.', 'polylang' );?></p>
58
  </div>
59
 
60
  <div class="form-field form-required">
61
- <label for="lang_name"><?php _e( 'Full name', 'polylang' );?></label><?php
62
  printf(
63
  '<input name="name" id="lang_name" type="text" value="%s" size="40" aria-required="true" />',
64
  ! empty( $edit_lang ) ? esc_attr( $edit_lang->name ) : ''
65
  );?>
66
- <p><?php _e( 'The name is how it is displayed on your site (for example: English).', 'polylang' );?></p>
67
  </div>
68
 
69
  <div class="form-field form-required">
70
- <label for="lang_locale"><?php _e( 'Locale', 'polylang' );?></label><?php
71
  printf(
72
  '<input name="locale" id="lang_locale" type="text" value="%s" size="40" aria-required="true" />',
73
  ! empty( $edit_lang ) ? esc_attr( $edit_lang->locale ) : ''
74
  );?>
75
- <p><?php _e( 'WordPress Locale for the language (for example: en_US). You will need to install the .mo file for this language.', 'polylang' );?></p>
76
  </div>
77
 
78
  <div class="form-field">
79
- <label for="lang_slug"><?php _e( 'Language code', 'polylang' );?></label><?php
80
  printf(
81
  '<input name="slug" id="lang_slug" type="text" value="%s" size="40"/>',
82
  ! empty( $edit_lang ) ? esc_attr( $edit_lang->slug ) : ''
83
  );?>
84
- <p><?php _e( 'Language code - preferably 2-letters ISO 639-1 (for example: en)', 'polylang' );?></p>
85
  </div>
86
 
87
  <div class="form-field"><fieldset>
88
- <legend><?php _e( 'Text direction', 'polylang' );?></legend><?php
89
  printf(
90
  '<label><input name="rtl" type="radio" value="0" %s /> %s</label>',
91
  ! empty( $edit_lang ) && $edit_lang->is_rtl ? '' : 'checked="checked"',
92
- __( 'left to right', 'polylang' )
93
  );
94
  printf(
95
  '<label><input name="rtl" type="radio" value="1" %s /> %s</label>',
96
  ! empty( $edit_lang ) && $edit_lang->is_rtl ? 'checked="checked"' : '',
97
- __( 'right to left', 'polylang' )
98
  );?>
99
- <p><?php _e( 'Choose the text direction for the language', 'polylang' );?></p>
100
  </fieldset></div>
101
 
102
  <div class="form-field">
103
- <label for="flag_list"><?php _e( 'Flag', 'polylang' );?></label>
104
  <select name="flag" id="flag_list">
105
  <option value=""></option><?php
106
  include( PLL_SETTINGS_INC.'/flags.php' );
@@ -113,16 +113,16 @@ if ( ! defined( 'ABSPATH' ) ) {
113
  );
114
  } ?>
115
  </select>
116
- <p><?php _e( 'Choose a flag for the language.', 'polylang' );?></p>
117
  </div>
118
 
119
  <div class="form-field">
120
- <label for="lang_order"><?php _e( 'Order', 'polylang' );?></label><?php
121
  printf(
122
  '<input name="term_group" id="lang_order" type="text" value="%d" />',
123
  ! empty( $edit_lang ) ? esc_attr( $edit_lang->term_group ) : ''
124
  );?>
125
- <p><?php _e( 'Position of the language in the language switcher', 'polylang' );?></p>
126
  </div><?php
127
 
128
  if ( ! empty( $edit_lang ) ) {
24
  <div class="col-wrap">
25
 
26
  <div class="form-wrap">
27
+ <h3><?php echo ! empty( $edit_lang ) ? esc_html__( 'Edit language', 'polylang' ) : esc_html__( 'Add new language', 'polylang' ); ?></h3><?php
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 ?>
39
  }?>
40
 
41
  <div class="form-field">
42
+ <label for="lang_list"><?php esc_html_e( 'Choose a language', 'polylang' );?></label>
43
  <select name="lang_list" id="lang_list">
44
  <option value=""></option><?php
45
  include( PLL_SETTINGS_INC.'/languages.php' );
46
  foreach ( $languages as $lg ) {
47
  printf(
48
+ '<option value="%1$s:%2$s:%3$s:%4$s">%5$s - %2$s</option>'."\n",
49
  esc_attr( $lg[0] ),
50
  esc_attr( $lg[1] ),
51
  'rtl' == $lg[3] ? '1' : '0',
54
  );
55
  } ?>
56
  </select>
57
+ <p><?php esc_html_e( 'You can choose a language in the list or directly edit it below.', 'polylang' );?></p>
58
  </div>
59
 
60
  <div class="form-field form-required">
61
+ <label for="lang_name"><?php esc_html_e( 'Full name', 'polylang' );?></label><?php
62
  printf(
63
  '<input name="name" id="lang_name" type="text" value="%s" size="40" aria-required="true" />',
64
  ! empty( $edit_lang ) ? esc_attr( $edit_lang->name ) : ''
65
  );?>
66
+ <p><?php esc_html_e( 'The name is how it is displayed on your site (for example: English).', 'polylang' );?></p>
67
  </div>
68
 
69
  <div class="form-field form-required">
70
+ <label for="lang_locale"><?php esc_html_e( 'Locale', 'polylang' );?></label><?php
71
  printf(
72
  '<input name="locale" id="lang_locale" type="text" value="%s" size="40" aria-required="true" />',
73
  ! empty( $edit_lang ) ? esc_attr( $edit_lang->locale ) : ''
74
  );?>
75
+ <p><?php esc_html_e( 'WordPress Locale for the language (for example: en_US). You will need to install the .mo file for this language.', 'polylang' );?></p>
76
  </div>
77
 
78
  <div class="form-field">
79
+ <label for="lang_slug"><?php esc_html_e( 'Language code', 'polylang' );?></label><?php
80
  printf(
81
  '<input name="slug" id="lang_slug" type="text" value="%s" size="40"/>',
82
  ! empty( $edit_lang ) ? esc_attr( $edit_lang->slug ) : ''
83
  );?>
84
+ <p><?php esc_html_e( 'Language code - preferably 2-letters ISO 639-1 (for example: en)', 'polylang' );?></p>
85
  </div>
86
 
87
  <div class="form-field"><fieldset>
88
+ <legend><?php esc_html_e( 'Text direction', 'polylang' );?></legend><?php
89
  printf(
90
  '<label><input name="rtl" type="radio" value="0" %s /> %s</label>',
91
  ! empty( $edit_lang ) && $edit_lang->is_rtl ? '' : 'checked="checked"',
92
+ esc_html__( 'left to right', 'polylang' )
93
  );
94
  printf(
95
  '<label><input name="rtl" type="radio" value="1" %s /> %s</label>',
96
  ! empty( $edit_lang ) && $edit_lang->is_rtl ? 'checked="checked"' : '',
97
+ esc_html__( 'right to left', 'polylang' )
98
  );?>
99
+ <p><?php esc_html_e( 'Choose the text direction for the language', 'polylang' );?></p>
100
  </fieldset></div>
101
 
102
  <div class="form-field">
103
+ <label for="flag_list"><?php esc_html_e( 'Flag', 'polylang' );?></label>
104
  <select name="flag" id="flag_list">
105
  <option value=""></option><?php
106
  include( PLL_SETTINGS_INC.'/flags.php' );
113
  );
114
  } ?>
115
  </select>
116
+ <p><?php esc_html_e( 'Choose a flag for the language.', 'polylang' );?></p>
117
  </div>
118
 
119
  <div class="form-field">
120
+ <label for="lang_order"><?php esc_html_e( 'Order', 'polylang' );?></label><?php
121
  printf(
122
  '<input name="term_group" id="lang_order" type="text" value="%d" />',
123
  ! empty( $edit_lang ) ? esc_attr( $edit_lang->term_group ) : ''
124
  );?>
125
+ <p><?php esc_html_e( 'Position of the language in the language switcher', 'polylang' );?></p>
126
  </div><?php
127
 
128
  if ( ! empty( $edit_lang ) ) {
settings/view-tab-strings.php CHANGED
@@ -14,8 +14,8 @@ if ( ! defined( 'ABSPATH' ) ) {
14
  $string_table->search_box( __( 'Search translations', 'polylang' ), 'translations' );
15
  wp_nonce_field( 'string-translation', '_wpnonce_string-translation' );
16
  $string_table->display();
17
- printf( '<br /><label><input name="clean" type="checkbox" value="1" /> %s</label>', __( 'Clean strings translation database', 'polylang' ) ); ?>
18
- <p><?php _e( 'Use this to remove unused strings from database, for example after a plugin has been uninstalled.', 'polylang' );?></p><?php
19
  submit_button(); // since WP 3.1 ?>
20
  </form>
21
  </div>
14
  $string_table->search_box( __( 'Search translations', 'polylang' ), 'translations' );
15
  wp_nonce_field( 'string-translation', '_wpnonce_string-translation' );
16
  $string_table->display();
17
+ printf( '<br /><label><input name="clean" type="checkbox" value="1" /> %s</label>', esc_html__( 'Clean strings translation database', 'polylang' ) ); ?>
18
+ <p><?php esc_html_e( 'Use this to remove unused strings from database, for example after a plugin has been uninstalled.', 'polylang' );?></p><?php
19
  submit_button(); // since WP 3.1 ?>
20
  </form>
21
  </div>