Polylang - Version 2.7

Version Description

(2020-04-06) =

  • Minimum WordPress version is now 4.9
  • Pro: Strings translations can now be exported and imported (in PO format)
  • Pro: Allow to decide individually which ACF fields to copy or synchronize
  • Pro: Add action pll_inactive_language_requested
  • Pro; Fix fatal error in The Events Calendar compatibility when no language is defined yet
  • Pro: Fix bulk translate when a post has no language
  • Pro: Fix reusable block saved without language
  • Pro: Fix post requested by slug not filtered in REST API, when the slug is shared
  • Add a setup wizard
  • Add Swahili, Upper Sorbian, Sindhi and Spanish from Uruguay to the list of predefined languages
  • Add flags in the predefined list of languages
  • Allow to hide the metaboxes from the screen options
  • The deletion of the plugin's data at uninstall is now controlled by a PHP constant instead of an option #456
  • Add parent in ajax response when selecting a term in autocomplete field #328
  • Add Vary: Accept-Language http header in home page redirect. Props @chesio #452
  • Improve performance to register/unregister WPML strings
  • Add support for the action wpml_switch_language
  • Add post_status to the list of accepted args of pll_count_posts()
  • Apply the filter pll_preferred_language in wp-login.php
  • Use filtered wrappers to create meta when creating media translations #231
  • Allow to translate the Twenty Seventeen header video Youtube url #460
  • Notices are now dismissed per site instead of per user #478
  • Fix terms not visible in the quick edit when only one language is defined and teh admin language filter is active
  • Fix post state not displayed for translations of the privacy policy page #395
  • Fix wildcards not correctly interpreted in wpml-config.xml
  • Fix product categories with special characters duplicated when importing WooCommerce products #474
Download this release

Release Info

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

Code changes from version 2.6.10 to 2.7

Files changed (90) hide show
  1. admin/admin-base.php +24 -2
  2. admin/admin-classic-editor.php +7 -7
  3. admin/admin-filters-columns.php +28 -15
  4. admin/admin-filters-post-base.php +2 -0
  5. admin/admin-filters-post.php +5 -2
  6. admin/admin-filters-term.php +36 -25
  7. admin/admin-filters.php +22 -0
  8. admin/admin-model.php +23 -8
  9. admin/admin-nav-menu.php +7 -7
  10. admin/admin-notices.php +81 -54
  11. admin/admin-static-pages.php +2 -2
  12. admin/admin-strings.php +0 -1
  13. admin/admin.php +16 -5
  14. admin/view-translations-media.php +2 -2
  15. css/admin.css +14 -2
  16. css/admin.min.css +1 -1
  17. css/selectmenu.css +29 -1
  18. css/selectmenu.min.css +1 -1
  19. frontend/choose-lang.php +18 -6
  20. frontend/frontend-auto-translate.php +4 -2
  21. frontend/frontend-filters-links.php +5 -1
  22. frontend/frontend-links.php +2 -3
  23. frontend/frontend-nav-menu.php +3 -3
  24. frontend/frontend.php +5 -3
  25. include/api.php +3 -3
  26. include/base.php +2 -2
  27. include/class-polylang.php +18 -4
  28. include/crud-posts.php +25 -21
  29. include/filters.php +3 -3
  30. include/functions.php +1 -48
  31. include/language.php +23 -10
  32. include/license.php +119 -1
  33. include/links-directory.php +9 -7
  34. include/links-domain.php +4 -9
  35. include/links-permalinks.php +1 -1
  36. include/links-subdomain.php +3 -1
  37. include/model.php +7 -5
  38. include/nav-menu.php +2 -0
  39. include/rest-request.php +5 -3
  40. include/switcher.php +4 -2
  41. include/translated-object.php +3 -2
  42. include/translated-term.php +4 -2
  43. include/walker-dropdown.php +2 -2
  44. include/widget-languages.php +1 -1
  45. install/install.php +2 -2
  46. install/upgrade.php +40 -6
  47. js/admin.js +260 -188
  48. js/admin.min.js +1 -1
  49. js/block-editor.js +194 -145
  50. js/block-editor.min.js +1 -1
  51. js/classic-editor.js +199 -149
  52. js/classic-editor.min.js +1 -1
  53. js/nav-menu.js +88 -61
  54. js/nav-menu.min.js +1 -1
  55. js/post.js +191 -128
  56. js/post.min.js +1 -1
  57. js/term.js +181 -145
  58. js/term.min.js +1 -1
  59. js/user.js +25 -18
  60. js/user.min.js +1 -1
  61. js/widgets.js +76 -66
  62. js/widgets.min.js +1 -1
  63. lingotek/lingotek.php +2 -4
  64. modules/plugins/featured-content.php +1 -1
  65. modules/plugins/plugins-compat.php +12 -4
  66. modules/plugins/wp-import.php +4 -0
  67. modules/sync/admin-sync.php +2 -2
  68. modules/sync/settings-sync.php +1 -1
  69. modules/sync/sync-metas.php +0 -2
  70. modules/sync/sync.php +0 -1
  71. modules/wpml/wpml-api.php +33 -3
  72. modules/wpml/wpml-compat.php +13 -17
  73. modules/wpml/wpml-config.php +43 -55
  74. modules/wpml/wpml-legacy-api.php +3 -3
  75. polylang.php +3 -3
  76. readme.txt +35 -5
  77. settings/languages.php +27 -0
  78. settings/settings-cpt.php +2 -0
  79. settings/settings-licenses.php +1 -103
  80. settings/settings-module.php +2 -0
  81. settings/settings-tools.php +0 -52
  82. settings/settings-url.php +2 -0
  83. settings/settings.php +29 -5
  84. settings/table-languages.php +0 -1
  85. settings/table-settings.php +5 -8
  86. settings/table-string.php +7 -4
  87. settings/view-languages.php +1 -1
  88. settings/view-tab-lang.php +9 -21
  89. settings/view-tab-strings.php +7 -0
  90. uninstall.php +12 -8
admin/admin-base.php CHANGED
@@ -48,18 +48,27 @@ class PLL_Admin_Base extends PLL_Base {
48
  public function init() {
49
  parent::init();
50
 
 
 
 
 
 
 
 
 
 
 
51
  if ( ! $this->model->get_languages_list() ) {
52
  return;
53
  }
54
 
55
- $this->notices = new PLL_Admin_Notices( $this );
56
  $this->links = new PLL_Admin_Links( $this ); // FIXME needed here ?
57
  $this->static_pages = new PLL_Admin_Static_Pages( $this ); // FIXME needed here ?
58
  $this->filters_links = new PLL_Filters_Links( $this ); // FIXME needed here ?
59
 
60
  // Filter admin language for users
61
  // We must not call user info before WordPress defines user roles in wp-settings.php
62
- add_filter( 'setup_theme', array( $this, 'init_user' ) );
63
  add_filter( 'request', array( $this, 'request' ) );
64
 
65
  // Adds the languages in admin bar
@@ -78,6 +87,8 @@ class PLL_Admin_Base extends PLL_Base {
78
  * @since 0.1
79
  */
80
  public function add_menus() {
 
 
81
  // Prepare the list of tabs
82
  $tabs = array( 'lang' => __( 'Languages', 'polylang' ) );
83
 
@@ -97,11 +108,14 @@ class PLL_Admin_Base extends PLL_Base {
97
  */
98
  $tabs = apply_filters( 'pll_settings_tabs', $tabs );
99
 
 
 
100
  foreach ( $tabs as $tab => $title ) {
101
  $page = 'lang' === $tab ? 'mlang' : "mlang_$tab";
102
  if ( empty( $parent ) ) {
103
  $parent = $page;
104
  add_menu_page( $title, __( 'Languages', 'polylang' ), 'manage_options', $page, null, 'dashicons-translation' );
 
105
  }
106
 
107
  add_submenu_page( $parent, $title, $title, 'manage_options', $page, array( $this, 'languages_page' ) );
@@ -421,4 +435,12 @@ class PLL_Admin_Base extends PLL_Base {
421
  );
422
  }
423
  }
 
 
 
 
 
 
 
 
424
  }
48
  public function init() {
49
  parent::init();
50
 
51
+ $this->notices = new PLL_Admin_Notices( $this );
52
+
53
+ if ( Polylang::is_wizard() && class_exists( 'PLL_Wizard_Pro' ) ) {
54
+ // Instantiate PLL_Wizard_Pro class after all PLL_Admin object is all initialized.
55
+ // After PLL_Admin::maybe_load_sync_post which is hooked on admin_init with priority 20.
56
+ add_action( 'admin_init', array( $this, 'instantiate_wizard_pro' ), 30 );
57
+ }
58
+
59
+ $this->wizard = new PLL_Wizard( $this );
60
+
61
  if ( ! $this->model->get_languages_list() ) {
62
  return;
63
  }
64
 
 
65
  $this->links = new PLL_Admin_Links( $this ); // FIXME needed here ?
66
  $this->static_pages = new PLL_Admin_Static_Pages( $this ); // FIXME needed here ?
67
  $this->filters_links = new PLL_Filters_Links( $this ); // FIXME needed here ?
68
 
69
  // Filter admin language for users
70
  // We must not call user info before WordPress defines user roles in wp-settings.php
71
+ add_action( 'setup_theme', array( $this, 'init_user' ) );
72
  add_filter( 'request', array( $this, 'request' ) );
73
 
74
  // Adds the languages in admin bar
87
  * @since 0.1
88
  */
89
  public function add_menus() {
90
+ global $admin_page_hooks;
91
+
92
  // Prepare the list of tabs
93
  $tabs = array( 'lang' => __( 'Languages', 'polylang' ) );
94
 
108
  */
109
  $tabs = apply_filters( 'pll_settings_tabs', $tabs );
110
 
111
+ $parent = '';
112
+
113
  foreach ( $tabs as $tab => $title ) {
114
  $page = 'lang' === $tab ? 'mlang' : "mlang_$tab";
115
  if ( empty( $parent ) ) {
116
  $parent = $page;
117
  add_menu_page( $title, __( 'Languages', 'polylang' ), 'manage_options', $page, null, 'dashicons-translation' );
118
+ $admin_page_hooks[ $page ] = 'languages'; // Hack to avoid the localization of the hook name. See: https://core.trac.wordpress.org/ticket/18857
119
  }
120
 
121
  add_submenu_page( $parent, $title, $title, 'manage_options', $page, array( $this, 'languages_page' ) );
435
  );
436
  }
437
  }
438
+ /**
439
+ * Instantiate PLL_Wizard_Pro class.
440
+ *
441
+ * @since 2.7
442
+ */
443
+ public function instantiate_wizard_pro() {
444
+ $this->wizard_pro = new PLL_Wizard_Pro( $this );
445
+ }
446
  }
admin/admin-classic-editor.php CHANGED
@@ -66,10 +66,10 @@ class PLL_Admin_Classic_Editor {
66
  */
67
  public function post_language() {
68
  global $post_ID;
69
- $post_id = $post_ID;
70
  $post_type = get_post_type( $post_ID );
71
 
72
- $from_post_id = isset( $_GET['from_post'] ) ? (int) $_GET['from_post'] : 0; // phpcs:ignore WordPress.Security.NonceVerification
 
73
 
74
  $lang = ( $lg = $this->model->post->get_language( $post_ID ) ) ? $lg :
75
  ( isset( $_GET['new_lang'] ) ? $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification
@@ -130,9 +130,8 @@ class PLL_Admin_Classic_Editor {
130
  }
131
 
132
  global $post_ID; // Obliged to use the global variable for wp_popular_terms_checklist
133
- $post_id = $post_ID = (int) $_POST['post_id'];
134
- $lang = $this->model->get_language( sanitize_key( $_POST['lang'] ) );
135
-
136
  $post_type = sanitize_key( $_POST['post_type'] );
137
 
138
  if ( ! post_type_exists( $post_type ) ) {
@@ -159,8 +158,9 @@ class PLL_Admin_Classic_Editor {
159
  ob_end_clean();
160
 
161
  // Categories
162
- if ( isset( $_POST['taxonomies'] ) ) {
163
- // Not set for pages
 
164
  foreach ( array_map( 'sanitize_key', $_POST['taxonomies'] ) as $taxname ) {
165
  $taxonomy = get_taxonomy( $taxname );
166
 
66
  */
67
  public function post_language() {
68
  global $post_ID;
 
69
  $post_type = get_post_type( $post_ID );
70
 
71
+ // phpcs:ignore WordPress.Security.NonceVerification, WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable
72
+ $from_post_id = isset( $_GET['from_post'] ) ? (int) $_GET['from_post'] : 0;
73
 
74
  $lang = ( $lg = $this->model->post->get_language( $post_ID ) ) ? $lg :
75
  ( isset( $_GET['new_lang'] ) ? $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification
130
  }
131
 
132
  global $post_ID; // Obliged to use the global variable for wp_popular_terms_checklist
133
+ $post_ID = (int) $_POST['post_id'];
134
+ $lang = $this->model->get_language( sanitize_key( $_POST['lang'] ) );
 
135
  $post_type = sanitize_key( $_POST['post_type'] );
136
 
137
  if ( ! post_type_exists( $post_type ) ) {
158
  ob_end_clean();
159
 
160
  // Categories
161
+ if ( isset( $_POST['taxonomies'] ) ) { // Not set for pages
162
+ $supplemental = array();
163
+
164
  foreach ( array_map( 'sanitize_key', $_POST['taxonomies'] ) as $taxname ) {
165
  $taxonomy = get_taxonomy( $taxname );
166
 
admin/admin-filters-columns.php CHANGED
@@ -21,25 +21,28 @@ class PLL_Admin_Filters_Columns {
21
  $this->model = &$polylang->model;
22
  $this->filter_lang = &$polylang->filter_lang;
23
 
24
- // Add the language and translations columns in 'All Posts', 'All Pages' and 'Media library' panels
 
 
 
25
  foreach ( $this->model->get_translated_post_types() as $type ) {
26
  // Use the latest filter late as some plugins purely overwrite what's done by others :(
27
- // Specific case for media
28
  add_filter( 'manage_' . ( 'attachment' == $type ? 'upload' : 'edit-' . $type ) . '_columns', array( $this, 'add_post_column' ), 100 );
29
  add_action( 'manage_' . ( 'attachment' == $type ? 'media' : $type . '_posts' ) . '_custom_column', array( $this, 'post_column' ), 10, 2 );
30
  }
31
 
32
- // Quick edit and bulk edit
33
  add_filter( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
34
  add_filter( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
35
 
36
- // Adds the language column in the 'Categories' and 'Post Tags' tables
37
  foreach ( $this->model->get_translated_taxonomies() as $tax ) {
38
  add_filter( 'manage_edit-' . $tax . '_columns', array( $this, 'add_term_column' ) );
39
  add_filter( 'manage_' . $tax . '_custom_column', array( $this, 'term_column' ), 10, 3 );
40
  }
41
 
42
- // Ajax responses to update list table rows
43
  add_action( 'wp_ajax_pll_update_post_rows', array( $this, 'ajax_update_post_rows' ) );
44
  add_action( 'wp_ajax_pll_update_term_rows', array( $this, 'ajax_update_term_rows' ) );
45
  }
@@ -60,10 +63,7 @@ class PLL_Admin_Filters_Columns {
60
  }
61
 
62
  foreach ( $this->model->get_languages_list() as $language ) {
63
- // Don't add the column for the filtered language
64
- if ( empty( $this->filter_lang ) || $language->slug != $this->filter_lang->slug ) {
65
- $columns[ 'language_' . $language->slug ] = $language->flag ? $language->flag . '<span class="screen-reader-text">' . esc_html( $language->name ) . '</span>' : esc_html( $language->slug );
66
- }
67
  }
68
 
69
  return isset( $end ) ? array_merge( $columns, $end ) : $columns;
@@ -77,15 +77,30 @@ class PLL_Admin_Filters_Columns {
77
  * @return string first language column name
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
  }
85
 
86
  return empty( $columns ) ? '' : reset( $columns );
87
  }
88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  /**
90
  * Adds the language and translations columns ( before the comments column ) in the posts, pages and media library tables
91
  *
@@ -122,8 +137,6 @@ class PLL_Admin_Filters_Columns {
122
  printf( '<div class="hidden" id="lang_%d">%s</div>', intval( $post_id ), esc_html( $lang->slug ) );
123
  }
124
 
125
- $post_type_object = get_post_type_object( get_post_type( $post_id ) );
126
-
127
  // Link to edit post ( or a translation )
128
  if ( $id = $this->model->post->get( $post_id, $language ) ) {
129
  // get_edit_post_link returns nothing if the user cannot edit the post
@@ -273,7 +286,7 @@ class PLL_Admin_Filters_Columns {
273
  esc_html( $s )
274
  );
275
  } elseif ( $id === $term_id ) {
276
- $out .= printf(
277
  '<span class="pll_icon_tick"><span class="screen-reader-text">%s</span></span>',
278
  /* translators: accessibility text, %s is a native language name */
279
  esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) )
21
  $this->model = &$polylang->model;
22
  $this->filter_lang = &$polylang->filter_lang;
23
 
24
+ // Hide the column of the filtered language.
25
+ add_filter( 'hidden_columns', array( $this, 'hidden_columns' ) ); // Since WP 4.4.
26
+
27
+ // Add the language and translations columns in 'All Posts', 'All Pages' and 'Media library' panels.
28
  foreach ( $this->model->get_translated_post_types() as $type ) {
29
  // Use the latest filter late as some plugins purely overwrite what's done by others :(
30
+ // Specific case for media.
31
  add_filter( 'manage_' . ( 'attachment' == $type ? 'upload' : 'edit-' . $type ) . '_columns', array( $this, 'add_post_column' ), 100 );
32
  add_action( 'manage_' . ( 'attachment' == $type ? 'media' : $type . '_posts' ) . '_custom_column', array( $this, 'post_column' ), 10, 2 );
33
  }
34
 
35
+ // Quick edit and bulk edit.
36
  add_filter( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
37
  add_filter( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
38
 
39
+ // Adds the language column in the 'Categories' and 'Post Tags' tables.
40
  foreach ( $this->model->get_translated_taxonomies() as $tax ) {
41
  add_filter( 'manage_edit-' . $tax . '_columns', array( $this, 'add_term_column' ) );
42
  add_filter( 'manage_' . $tax . '_custom_column', array( $this, 'term_column' ), 10, 3 );
43
  }
44
 
45
+ // Ajax responses to update list table rows.
46
  add_action( 'wp_ajax_pll_update_post_rows', array( $this, 'ajax_update_post_rows' ) );
47
  add_action( 'wp_ajax_pll_update_term_rows', array( $this, 'ajax_update_term_rows' ) );
48
  }
63
  }
64
 
65
  foreach ( $this->model->get_languages_list() as $language ) {
66
+ $columns[ 'language_' . $language->slug ] = $language->flag ? $language->flag . '<span class="screen-reader-text">' . esc_html( $language->name ) . '</span>' : esc_html( $language->slug );
 
 
 
67
  }
68
 
69
  return isset( $end ) ? array_merge( $columns, $end ) : $columns;
77
  * @return string first language column name
78
  */
79
  protected function get_first_language_column() {
80
+ $columns = array();
81
+
82
  foreach ( $this->model->get_languages_list() as $language ) {
83
+ $columns[] = 'language_' . $language->slug;
 
 
84
  }
85
 
86
  return empty( $columns ) ? '' : reset( $columns );
87
  }
88
 
89
+ /**
90
+ * Hide the column for the filtered language
91
+ *
92
+ * @since 2.7
93
+ *
94
+ * @param array $hidden Array of hidden columns
95
+ * @return array
96
+ */
97
+ public function hidden_columns( $hidden ) {
98
+ if ( ! empty( $this->filter_lang ) ) {
99
+ $hidden[] = 'language_' . $this->filter_lang->slug;
100
+ }
101
+ return $hidden;
102
+ }
103
+
104
  /**
105
  * Adds the language and translations columns ( before the comments column ) in the posts, pages and media library tables
106
  *
137
  printf( '<div class="hidden" id="lang_%d">%s</div>', intval( $post_id ), esc_html( $lang->slug ) );
138
  }
139
 
 
 
140
  // Link to edit post ( or a translation )
141
  if ( $id = $this->model->post->get( $post_id, $language ) ) {
142
  // get_edit_post_link returns nothing if the user cannot edit the post
286
  esc_html( $s )
287
  );
288
  } elseif ( $id === $term_id ) {
289
+ $out .= sprintf(
290
  '<span class="pll_icon_tick"><span class="screen-reader-text">%s</span></span>',
291
  /* translators: accessibility text, %s is a native language name */
292
  esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) )
admin/admin-filters-post-base.php CHANGED
@@ -34,6 +34,8 @@ abstract class PLL_Admin_Filters_Post_Base {
34
  // Security check as 'wp_insert_post' can be called from outside WP admin
35
  check_admin_referer( 'pll_language', '_pll_nonce' );
36
 
 
 
37
  // Save translations after checking the translated post is in the right language
38
  foreach ( $arr as $lang => $tr_id ) {
39
  $translations[ $lang ] = ( $tr_id && $this->model->post->get_language( (int) $tr_id )->slug == $lang ) ? (int) $tr_id : 0;
34
  // Security check as 'wp_insert_post' can be called from outside WP admin
35
  check_admin_referer( 'pll_language', '_pll_nonce' );
36
 
37
+ $translations = array();
38
+
39
  // Save translations after checking the translated post is in the right language
40
  foreach ( $arr as $lang => $tr_id ) {
41
  $translations[ $lang ] = ( $tr_id && $this->model->post->get_language( (int) $tr_id )->slug == $lang ) ? (int) $tr_id : 0;
admin/admin-filters-post.php CHANGED
@@ -47,6 +47,7 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
47
  // Hierarchical taxonomies
48
  if ( 'edit' == $screen->base && $taxonomies = get_object_taxonomies( $screen->post_type, 'object' ) ) {
49
  // Get translated hierarchical taxonomies
 
50
  foreach ( $taxonomies as $taxonomy ) {
51
  if ( $taxonomy->hierarchical && $taxonomy->show_in_quick_edit && $this->model->is_translated_taxonomy( $taxonomy->name ) ) {
52
  $hierarchical_taxonomies[] = $taxonomy->name;
@@ -54,7 +55,8 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
54
  }
55
 
56
  if ( ! empty( $hierarchical_taxonomies ) ) {
57
- $terms = get_terms( $hierarchical_taxonomies, array( 'get' => 'all' ) );
 
58
 
59
  foreach ( $terms as $term ) {
60
  if ( $lang = $this->model->term->get_language( $term->term_id ) ) {
@@ -71,7 +73,8 @@ class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
71
 
72
  // Hierarchical post types
73
  if ( 'edit' == $screen->base && is_post_type_hierarchical( $screen->post_type ) ) {
74
- $pages = get_pages();
 
75
 
76
  foreach ( $pages as $page ) {
77
  if ( $lang = $this->model->post->get_language( $page->ID ) ) {
47
  // Hierarchical taxonomies
48
  if ( 'edit' == $screen->base && $taxonomies = get_object_taxonomies( $screen->post_type, 'object' ) ) {
49
  // Get translated hierarchical taxonomies
50
+ $hierarchical_taxonomies = array();
51
  foreach ( $taxonomies as $taxonomy ) {
52
  if ( $taxonomy->hierarchical && $taxonomy->show_in_quick_edit && $this->model->is_translated_taxonomy( $taxonomy->name ) ) {
53
  $hierarchical_taxonomies[] = $taxonomy->name;
55
  }
56
 
57
  if ( ! empty( $hierarchical_taxonomies ) ) {
58
+ $terms = get_terms( $hierarchical_taxonomies, array( 'get' => 'all' ) );
59
+ $term_languages = array();
60
 
61
  foreach ( $terms as $term ) {
62
  if ( $lang = $this->model->term->get_language( $term->term_id ) ) {
73
 
74
  // Hierarchical post types
75
  if ( 'edit' == $screen->base && is_post_type_hierarchical( $screen->post_type ) ) {
76
+ $pages = get_pages();
77
+ $page_languages = array();
78
 
79
  foreach ( $pages as $page ) {
80
  if ( $lang = $this->model->post->get_language( $page->ID ) ) {
admin/admin-filters-term.php CHANGED
@@ -133,8 +133,8 @@ class PLL_Admin_Filters_Term {
133
  return;
134
  }
135
 
136
- $term_id = $tag->term_id;
137
- $taxonomy = $tag->taxonomy;
138
 
139
  $lang = $this->model->term->get_language( $term_id );
140
  $lang = empty( $lang ) ? $this->pref_lang : $lang;
@@ -271,8 +271,10 @@ class PLL_Admin_Filters_Term {
271
 
272
  // If we have several terms with the same name, they are translations of each other
273
  if ( count( $terms ) > 1 ) {
 
 
274
  foreach ( $terms as $term ) {
275
- $translations[ $this->model->term->get_language( $term->term_id )->slug ] = $term->term_id;
276
  }
277
 
278
  $this->model->term->save_translations( $term_id, $translations );
@@ -368,7 +370,7 @@ class PLL_Admin_Filters_Term {
368
  $this->save_language( $term_id, $taxonomy );
369
 
370
  if ( isset( $_POST['term_tr_lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
371
- $translations = $this->save_translations( $term_id );
372
  }
373
  }
374
  }
@@ -433,9 +435,9 @@ class PLL_Admin_Filters_Term {
433
  wp_die( 0 );
434
  }
435
 
436
- $lang = $this->model->get_language( sanitize_key( $_POST['lang'] ) );
437
- $term_id = isset( $_POST['term_id'] ) ? (int) $_POST['term_id'] : null;
438
- $taxonomy = sanitize_key( $_POST['taxonomy'] );
439
  $post_type = sanitize_key( $_POST['post_type'] );
440
 
441
  if ( ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
@@ -502,7 +504,7 @@ class PLL_Admin_Filters_Term {
502
 
503
  $s = wp_unslash( $_GET['term'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
504
  $post_type = sanitize_key( $_GET['post_type'] );
505
- $taxonomy = sanitize_key( $_GET['taxonomy'] );
506
 
507
  if ( ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
508
  wp_die( 0 );
@@ -511,32 +513,40 @@ class PLL_Admin_Filters_Term {
511
  $term_language = $this->model->get_language( sanitize_key( $_GET['term_language'] ) );
512
  $translation_language = $this->model->get_language( sanitize_key( $_GET['translation_language'] ) );
513
 
 
514
  $return = array();
515
 
516
- // It is more efficient to use one common query for all languages as soon as there are more than 2
 
 
 
 
 
 
517
  foreach ( get_terms( $taxonomy, 'hide_empty=0&lang=0&name__like=' . $s ) as $term ) {
518
  $lang = $this->model->term->get_language( $term->term_id );
519
 
520
  if ( $lang && $lang->slug == $translation_language->slug && ! $this->model->term->get_translation( $term->term_id, $term_language ) ) {
521
- $return[] = array(
522
- 'id' => $term->term_id,
523
- 'value' => $term->name,
524
- 'link' => $this->links->edit_term_translation_link( $term->term_id, $taxonomy, $post_type ),
525
- );
526
  }
527
  }
528
 
529
- // Add current translation in list
530
- // Not in add term as term_id is not set
531
- if ( isset( $_GET['term_id'] ) && 'undefined' !== $_GET['term_id'] && $term_id = $this->model->term->get_translation( (int) $_GET['term_id'], $translation_language ) ) {
532
- $term = get_term( $term_id, $taxonomy );
533
- array_unshift(
534
- $return,
535
- array(
536
- 'id' => $term_id,
537
- 'value' => $term->name,
538
- 'link' => $this->links->edit_term_translation_link( $term->term_id, $taxonomy, $post_type ),
539
- )
 
 
 
 
 
540
  );
541
  }
542
 
@@ -607,6 +617,7 @@ class PLL_Admin_Filters_Term {
607
 
608
  $avoid_recursion = true;
609
  $lang = $this->model->term->get_language( $term_id );
 
610
 
611
  foreach ( $this->model->term->get_translations( $term_id ) as $key => $tr_id ) {
612
  if ( $lang->slug == $key ) {
133
  return;
134
  }
135
 
136
+ $term_id = $tag->term_id;
137
+ $taxonomy = $tag->taxonomy; // phpcs:ignore WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable
138
 
139
  $lang = $this->model->term->get_language( $term_id );
140
  $lang = empty( $lang ) ? $this->pref_lang : $lang;
271
 
272
  // If we have several terms with the same name, they are translations of each other
273
  if ( count( $terms ) > 1 ) {
274
+ $translations = array();
275
+
276
  foreach ( $terms as $term ) {
277
+ $translations[ $this->model->term->get_language( $term->term_id )->slug ] = $term->term_id;
278
  }
279
 
280
  $this->model->term->save_translations( $term_id, $translations );
370
  $this->save_language( $term_id, $taxonomy );
371
 
372
  if ( isset( $_POST['term_tr_lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
373
+ $this->save_translations( $term_id );
374
  }
375
  }
376
  }
435
  wp_die( 0 );
436
  }
437
 
438
+ $lang = $this->model->get_language( sanitize_key( $_POST['lang'] ) );
439
+ $term_id = isset( $_POST['term_id'] ) ? (int) $_POST['term_id'] : null; // phpcs:ignore WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable
440
+ $taxonomy = sanitize_key( $_POST['taxonomy'] ); // phpcs:ignore WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable
441
  $post_type = sanitize_key( $_POST['post_type'] );
442
 
443
  if ( ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
504
 
505
  $s = wp_unslash( $_GET['term'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
506
  $post_type = sanitize_key( $_GET['post_type'] );
507
+ $taxonomy = sanitize_key( $_GET['taxonomy'] );
508
 
509
  if ( ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
510
  wp_die( 0 );
513
  $term_language = $this->model->get_language( sanitize_key( $_GET['term_language'] ) );
514
  $translation_language = $this->model->get_language( sanitize_key( $_GET['translation_language'] ) );
515
 
516
+ $terms = array();
517
  $return = array();
518
 
519
+ // Add current translation in list.
520
+ // Not in add term as term_id is not set.
521
+ if ( isset( $_GET['term_id'] ) && 'undefined' !== $_GET['term_id'] && $term_id = $this->model->term->get_translation( (int) $_GET['term_id'], $translation_language ) ) {
522
+ $terms = array( get_term( $term_id, $taxonomy ) );
523
+ }
524
+
525
+ // It is more efficient to use one common query for all languages as soon as there are more than 2.
526
  foreach ( get_terms( $taxonomy, 'hide_empty=0&lang=0&name__like=' . $s ) as $term ) {
527
  $lang = $this->model->term->get_language( $term->term_id );
528
 
529
  if ( $lang && $lang->slug == $translation_language->slug && ! $this->model->term->get_translation( $term->term_id, $term_language ) ) {
530
+ $terms[] = $term;
 
 
 
 
531
  }
532
  }
533
 
534
+ // Format the ajax response.
535
+ foreach ( $terms as $term ) {
536
+ $return[] = array(
537
+ 'id' => $term->term_id,
538
+ 'value' => rtrim( // Trim the seperator added at the end by WP.
539
+ get_term_parents_list(
540
+ $term->term_id,
541
+ $term->taxonomy,
542
+ array(
543
+ 'separator' => ' > ',
544
+ 'link' => false,
545
+ )
546
+ ),
547
+ ' >'
548
+ ),
549
+ 'link' => $this->links->edit_term_translation_link( $term->term_id, $term->taxonomy, $post_type ),
550
  );
551
  }
552
 
617
 
618
  $avoid_recursion = true;
619
  $lang = $this->model->term->get_language( $term_id );
620
+ $translations = array();
621
 
622
  foreach ( $this->model->term->get_translations( $term_id ) as $key => $tr_id ) {
623
  if ( $lang->slug == $key ) {
admin/admin-filters.php CHANGED
@@ -42,6 +42,9 @@ class PLL_Admin_Filters extends PLL_Filters {
42
  }
43
 
44
  add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
 
 
 
45
  }
46
 
47
  /**
@@ -265,4 +268,23 @@ class PLL_Admin_Filters extends PLL_Filters {
265
  }
266
  return $classes;
267
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  }
42
  }
43
 
44
  add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
45
+
46
+ // Add post state for translations of the privacy policy page
47
+ add_filter( 'display_post_states', array( $this, 'display_post_states' ), 10, 2 );
48
  }
49
 
50
  /**
268
  }
269
  return $classes;
270
  }
271
+
272
+ /**
273
+ * Add post state for translations of the privacy policy page
274
+ *
275
+ * @since 2.7
276
+ *
277
+ * @param array $post_states An array of post display states.
278
+ * @param object $post The current post object.
279
+ * @return array
280
+ */
281
+ public function display_post_states( $post_states, $post ) {
282
+ $page_for_privacy_policy = get_option( 'wp_page_for_privacy_policy' );
283
+
284
+ if ( $page_for_privacy_policy && in_array( $post->ID, $this->model->post->get_translations( $page_for_privacy_policy ) ) ) {
285
+ $post_states['page_for_privacy_policy'] = __( 'Privacy Policy Page', 'polylang' );
286
+ }
287
+
288
+ return $post_states;
289
+ }
290
  }
admin/admin-model.php CHANGED
@@ -125,7 +125,7 @@ class PLL_Admin_Model extends PLL_Model {
125
  // Delete menus locations
126
  if ( ! empty( $this->options['nav_menus'] ) ) {
127
  foreach ( $this->options['nav_menus'] as $theme => $locations ) {
128
- foreach ( $locations as $location => $languages ) {
129
  unset( $this->options['nav_menus'][ $theme ][ $location ][ $lang->slug ] );
130
  }
131
  }
@@ -211,7 +211,7 @@ class PLL_Admin_Model extends PLL_Model {
211
  // Update menus locations
212
  if ( ! empty( $this->options['nav_menus'] ) ) {
213
  foreach ( $this->options['nav_menus'] as $theme => $locations ) {
214
- foreach ( $locations as $location => $languages ) {
215
  if ( ! empty( $this->options['nav_menus'][ $theme ][ $location ][ $old_slug ] ) ) {
216
  $this->options['nav_menus'][ $theme ][ $location ][ $slug ] = $this->options['nav_menus'][ $theme ][ $location ][ $old_slug ];
217
  unset( $this->options['nav_menus'][ $theme ][ $location ][ $old_slug ] );
@@ -318,9 +318,10 @@ class PLL_Admin_Model extends PLL_Model {
318
  public function set_language_in_mass( $type, $ids, $lang ) {
319
  global $wpdb;
320
 
321
- $ids = array_map( 'intval', $ids );
322
- $lang = $this->get_language( $lang );
323
- $tt_id = 'term' === $type ? $lang->tl_term_taxonomy_id : $lang->term_taxonomy_id;
 
324
 
325
  foreach ( $ids as $id ) {
326
  $values[] = $wpdb->prepare( '( %d, %d )', $id, $tt_id );
@@ -334,6 +335,7 @@ class PLL_Admin_Model extends PLL_Model {
334
 
335
  if ( 'term' === $type ) {
336
  clean_term_cache( $ids, 'term_language' );
 
337
 
338
  foreach ( $ids as $id ) {
339
  $translations[] = array( $lang->slug => $id );
@@ -358,7 +360,11 @@ class PLL_Admin_Model extends PLL_Model {
358
  public function set_translation_in_mass( $type, $translations ) {
359
  global $wpdb;
360
 
361
- $taxonomy = $type . '_translations';
 
 
 
 
362
 
363
  foreach ( $translations as $t ) {
364
  $term = uniqid( 'pll_' ); // the term name
@@ -376,7 +382,9 @@ class PLL_Admin_Model extends PLL_Model {
376
 
377
  // Get all terms with their term_id
378
  // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
379
- $terms = $wpdb->get_results( "SELECT term_id, slug FROM {$wpdb->terms} WHERE slug IN ( " . implode( ',', $slugs ) . ' )' );
 
 
380
 
381
  // Prepare terms taxonomy relationship
382
  foreach ( $terms as $term ) {
@@ -392,6 +400,7 @@ class PLL_Admin_Model extends PLL_Model {
392
 
393
  // Get all terms with term_taxonomy_id
394
  $terms = get_terms( $taxonomy, array( 'hide_empty' => false ) );
 
395
 
396
  // Prepare objects relationships
397
  foreach ( $terms as $term ) {
@@ -492,7 +501,11 @@ class PLL_Admin_Model extends PLL_Model {
492
  public function update_translations( $old_slug, $new_slug = '' ) {
493
  global $wpdb;
494
 
495
- $terms = get_terms( array( 'post_translations', 'term_translations' ) );
 
 
 
 
496
 
497
  foreach ( $terms as $term ) {
498
  $term_ids[ $term->taxonomy ][] = $term->term_id;
@@ -563,6 +576,8 @@ class PLL_Admin_Model extends PLL_Model {
563
  // The nav menus stored in theme locations should be in the default language
564
  $theme = get_stylesheet();
565
  if ( ! empty( $this->options['nav_menus'][ $theme ] ) ) {
 
 
566
  foreach ( $this->options['nav_menus'][ $theme ] as $key => $loc ) {
567
  $menus[ $key ] = empty( $loc[ $slug ] ) ? 0 : $loc[ $slug ];
568
  }
125
  // Delete menus locations
126
  if ( ! empty( $this->options['nav_menus'] ) ) {
127
  foreach ( $this->options['nav_menus'] as $theme => $locations ) {
128
+ foreach ( array_keys( $locations ) as $location ) {
129
  unset( $this->options['nav_menus'][ $theme ][ $location ][ $lang->slug ] );
130
  }
131
  }
211
  // Update menus locations
212
  if ( ! empty( $this->options['nav_menus'] ) ) {
213
  foreach ( $this->options['nav_menus'] as $theme => $locations ) {
214
+ foreach ( array_keys( $locations ) as $location ) {
215
  if ( ! empty( $this->options['nav_menus'][ $theme ][ $location ][ $old_slug ] ) ) {
216
  $this->options['nav_menus'][ $theme ][ $location ][ $slug ] = $this->options['nav_menus'][ $theme ][ $location ][ $old_slug ];
217
  unset( $this->options['nav_menus'][ $theme ][ $location ][ $old_slug ] );
318
  public function set_language_in_mass( $type, $ids, $lang ) {
319
  global $wpdb;
320
 
321
+ $ids = array_map( 'intval', $ids );
322
+ $lang = $this->get_language( $lang );
323
+ $tt_id = 'term' === $type ? $lang->tl_term_taxonomy_id : $lang->term_taxonomy_id;
324
+ $values = array();
325
 
326
  foreach ( $ids as $id ) {
327
  $values[] = $wpdb->prepare( '( %d, %d )', $id, $tt_id );
335
 
336
  if ( 'term' === $type ) {
337
  clean_term_cache( $ids, 'term_language' );
338
+ $translations = array();
339
 
340
  foreach ( $ids as $id ) {
341
  $translations[] = array( $lang->slug => $id );
360
  public function set_translation_in_mass( $type, $translations ) {
361
  global $wpdb;
362
 
363
+ $taxonomy = $type . '_translations';
364
+ $terms = array();
365
+ $slugs = array();
366
+ $description = array();
367
+ $count = array();
368
 
369
  foreach ( $translations as $t ) {
370
  $term = uniqid( 'pll_' ); // the term name
382
 
383
  // Get all terms with their term_id
384
  // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
385
+ $terms = $wpdb->get_results( "SELECT term_id, slug FROM {$wpdb->terms} WHERE slug IN ( " . implode( ',', $slugs ) . ' )' );
386
+ $term_ids = array();
387
+ $tts = array();
388
 
389
  // Prepare terms taxonomy relationship
390
  foreach ( $terms as $term ) {
400
 
401
  // Get all terms with term_taxonomy_id
402
  $terms = get_terms( $taxonomy, array( 'hide_empty' => false ) );
403
+ $trs = array();
404
 
405
  // Prepare objects relationships
406
  foreach ( $terms as $term ) {
501
  public function update_translations( $old_slug, $new_slug = '' ) {
502
  global $wpdb;
503
 
504
+ $terms = get_terms( array( 'post_translations', 'term_translations' ) );
505
+ $term_ids = array();
506
+ $dr = array();
507
+ $dt = array();
508
+ $ut = array();
509
 
510
  foreach ( $terms as $term ) {
511
  $term_ids[ $term->taxonomy ][] = $term->term_id;
576
  // The nav menus stored in theme locations should be in the default language
577
  $theme = get_stylesheet();
578
  if ( ! empty( $this->options['nav_menus'][ $theme ] ) ) {
579
+ $menus = array();
580
+
581
  foreach ( $this->options['nav_menus'][ $theme ] as $key => $loc ) {
582
  $menus[ $key ] = empty( $loc[ $slug ] ) ? 0 : $loc[ $slug ];
583
  }
admin/admin-nav-menu.php CHANGED
@@ -94,8 +94,11 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
94
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
95
  wp_enqueue_script( 'pll_nav_menu', plugins_url( '/js/nav-menu' . $suffix . '.js', POLYLANG_FILE ), array( 'jquery' ), POLYLANG_VERSION );
96
 
97
- $data['strings'] = PLL_Switcher::get_switcher_options( 'menu', 'string' ); // The strings for the options
98
- $data['title'] = __( 'Languages', 'polylang' ); // The title
 
 
 
99
 
100
  // Get all language switcher menu items
101
  $items = get_posts(
@@ -109,7 +112,6 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
109
  );
110
 
111
  // The options values for the language switcher
112
- $data['val'] = array();
113
  foreach ( $items as $item ) {
114
  $data['val'][ $item ] = get_post_meta( $item, '_pll_menu_item', true );
115
  }
@@ -143,7 +145,7 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
143
  }
144
  }
145
  else {
146
- foreach ( $options as $opt => $v ) {
147
  $options[ $opt ] = empty( $_POST[ 'menu-item-' . $opt ][ $menu_item_db_id ] ) ? 0 : 1;
148
  }
149
  update_post_meta( $menu_item_db_id, '_pll_menu_item', $options ); // Allow us to easily identify our nav menu item
@@ -160,8 +162,6 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
160
  * @return array
161
  */
162
  public function update_nav_menu_locations( $locations ) {
163
- $default = $this->options['default_lang'];
164
-
165
  // Extract language and menu from locations
166
  foreach ( $locations as $loc => $menu ) {
167
  $infos = $this->explode_location( $loc );
@@ -232,7 +232,7 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
232
  }
233
 
234
  if ( is_array( $menus ) ) {
235
- foreach ( $menus as $loc => $menu ) {
236
  foreach ( $this->model->get_languages_list() as $lang ) {
237
  if ( ! empty( $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ] ) && term_exists( $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ], 'nav_menu' ) ) {
238
  $menus[ $this->combine_location( $loc, $lang ) ] = $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ];
94
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
95
  wp_enqueue_script( 'pll_nav_menu', plugins_url( '/js/nav-menu' . $suffix . '.js', POLYLANG_FILE ), array( 'jquery' ), POLYLANG_VERSION );
96
 
97
+ $data = array(
98
+ 'strings' => PLL_Switcher::get_switcher_options( 'menu', 'string' ), // The strings for the options
99
+ 'title' => __( 'Languages', 'polylang' ), // The title
100
+ 'val' => array(),
101
+ );
102
 
103
  // Get all language switcher menu items
104
  $items = get_posts(
112
  );
113
 
114
  // The options values for the language switcher
 
115
  foreach ( $items as $item ) {
116
  $data['val'][ $item ] = get_post_meta( $item, '_pll_menu_item', true );
117
  }
145
  }
146
  }
147
  else {
148
+ foreach ( array_keys( $options ) as $opt ) {
149
  $options[ $opt ] = empty( $_POST[ 'menu-item-' . $opt ][ $menu_item_db_id ] ) ? 0 : 1;
150
  }
151
  update_post_meta( $menu_item_db_id, '_pll_menu_item', $options ); // Allow us to easily identify our nav menu item
162
  * @return array
163
  */
164
  public function update_nav_menu_locations( $locations ) {
 
 
165
  // Extract language and menu from locations
166
  foreach ( $locations as $loc => $menu ) {
167
  $infos = $this->explode_location( $loc );
232
  }
233
 
234
  if ( is_array( $menus ) ) {
235
+ foreach ( array_keys( $menus ) as $loc ) {
236
  foreach ( $this->model->get_languages_list() as $lang ) {
237
  if ( ! empty( $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ] ) && term_exists( $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ], 'nav_menu' ) ) {
238
  $menus[ $this->combine_location( $loc, $lang ) ] = $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ];
admin/admin-notices.php CHANGED
@@ -6,6 +6,7 @@
6
  * and only on dashboard, plugins and Polylang admin pages
7
  *
8
  * @since 2.3.9
 
9
  */
10
  class PLL_Admin_Notices {
11
  private static $notices = array();
@@ -57,8 +58,22 @@ class PLL_Admin_Notices {
57
  * @return bool
58
  */
59
  public static function is_dismissed( $notice ) {
60
- $dismissed = get_user_meta( get_current_user_id(), 'pll_dismissed_notices', true );
61
- return is_array( $dismissed ) && in_array( $notice, $dismissed );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  }
63
 
64
  /**
@@ -66,21 +81,34 @@ class PLL_Admin_Notices {
66
  *
67
  * @since 2.3.9
68
  *
 
69
  * @return bool
70
  */
71
- protected function can_display_notice() {
72
  $screen = get_current_screen();
73
  $screen_id = sanitize_title( __( 'Languages', 'polylang' ) );
74
 
75
- return in_array(
76
- $screen->id,
77
- array(
78
- 'dashboard',
79
- 'plugins',
80
- 'toplevel_page_mlang',
81
- $screen_id . '_page_mlang_strings',
82
- $screen_id . '_page_mlang_settings',
83
- )
 
 
 
 
 
 
 
 
 
 
 
 
84
  );
85
  }
86
 
@@ -92,13 +120,11 @@ class PLL_Admin_Notices {
92
  * @param string $notice
93
  */
94
  public static function dismiss( $notice ) {
95
- if ( ! $dismissed = get_user_meta( get_current_user_id(), 'pll_dismissed_notices', true ) ) {
96
- $dismissed = array();
97
- }
98
 
99
  if ( ! in_array( $notice, $dismissed ) ) {
100
  $dismissed[] = $notice;
101
- update_user_meta( get_current_user_id(), 'pll_dismissed_notices', array_unique( $dismissed ) );
102
  }
103
  }
104
 
@@ -123,14 +149,19 @@ class PLL_Admin_Notices {
123
  * @since 2.3.9
124
  */
125
  public function display_notices() {
126
- if ( current_user_can( 'manage_options' ) && $this->can_display_notice() ) {
127
  // Core notices
128
- $this->pllwc_notice();
129
- $this->review_notice();
 
 
 
 
 
130
 
131
  // Custom notices
132
  foreach ( $this->get_notices() as $notice => $html ) {
133
- if ( ! $this->is_dismissed( $notice ) ) {
134
  ?>
135
  <div class="pll-notice notice notice-info">
136
  <?php
@@ -166,23 +197,21 @@ class PLL_Admin_Notices {
166
  * @since 2.3.9
167
  */
168
  private function pllwc_notice() {
169
- if ( defined( 'WOOCOMMERCE_VERSION' ) && ! defined( 'PLLWC_VERSION' ) && ! $this->is_dismissed( 'pllwc' ) ) {
170
- ?>
171
- <div class="pll-notice notice notice-warning">
172
- <?php $this->dismiss_button( 'pllwc' ); ?>
173
- <p>
174
- <?php
175
- printf(
176
- /* translators: %1$s is link start tag, %2$s is link end tag. */
177
- esc_html__( 'We have noticed that you are using Polylang with WooCommerce. To ensure compatibility, we recommend you use %1$sPolylang for WooCommerce%2$s.', 'polylang' ),
178
- '<a href="https://polylang.pro/downloads/polylang-for-woocommerce/">',
179
- '</a>'
180
- );
181
- ?>
182
- </p>
183
- </div>
184
- <?php
185
- }
186
  }
187
 
188
  /**
@@ -191,22 +220,20 @@ class PLL_Admin_Notices {
191
  * @since 2.3.9
192
  */
193
  private function review_notice() {
194
- if ( ! defined( 'POLYLANG_PRO' ) && ! $this->is_dismissed( 'review' ) && ! empty( $this->options['first_activation'] ) && time() > $this->options['first_activation'] + 15 * DAY_IN_SECONDS ) {
195
- ?>
196
- <div class="pll-notice notice notice-info">
197
- <?php $this->dismiss_button( 'review' ); ?>
198
- <p>
199
- <?php
200
- printf(
201
- /* translators: %1$s is link start tag, %2$s is link end tag. */
202
- esc_html__( 'We have noticed that you have been using Polylang for some time. We hope you love it, and we would really appreciate it if you would %1$sgive us a 5 stars rating%2$s.', 'polylang' ),
203
- '<a href="https://wordpress.org/support/plugin/polylang/reviews/?rate=5#new-post">',
204
- '</a>'
205
- );
206
- ?>
207
- </p>
208
- </div>
209
- <?php
210
- }
211
  }
212
  }
6
  * and only on dashboard, plugins and Polylang admin pages
7
  *
8
  * @since 2.3.9
9
+ * @since 2.7 Dismissed notices are stored in an option instead of a user meta
10
  */
11
  class PLL_Admin_Notices {
12
  private static $notices = array();
58
  * @return bool
59
  */
60
  public static function is_dismissed( $notice ) {
61
+ $dismissed = get_option( 'pll_dismissed_notices', array() );
62
+
63
+ // Handle legacy user meta
64
+ $dismissed_meta = get_user_meta( get_current_user_id(), 'pll_dismissed_notices', true );
65
+ if ( is_array( $dismissed_meta ) ) {
66
+ if ( array_diff( $dismissed_meta, $dismissed ) ) {
67
+ $dismissed = array_merge( $dismissed, $dismissed_meta );
68
+ update_option( 'pll_dismissed_notices', $dismissed );
69
+ }
70
+ if ( ! is_multisite() ) {
71
+ // Don't delete on multisite to avoid the notices to appear in other sites.
72
+ delete_user_meta( get_current_user_id(), 'pll_dismissed_notices' );
73
+ }
74
+ }
75
+
76
+ return in_array( $notice, $dismissed );
77
  }
78
 
79
  /**
81
  *
82
  * @since 2.3.9
83
  *
84
+ * @param string $notice The notice name.
85
  * @return bool
86
  */
87
+ protected function can_display_notice( $notice ) {
88
  $screen = get_current_screen();
89
  $screen_id = sanitize_title( __( 'Languages', 'polylang' ) );
90
 
91
+ /**
92
+ * Filter admin notices which can be displayed
93
+ *
94
+ * @since 2.7.0
95
+ *
96
+ * @param bool $display Whether the notice should be displayed or not.
97
+ * @param string $notice The notice name.
98
+ */
99
+ return apply_filters(
100
+ 'pll_can_display_notice',
101
+ in_array(
102
+ $screen->id,
103
+ array(
104
+ 'dashboard',
105
+ 'plugins',
106
+ 'toplevel_page_mlang',
107
+ $screen_id . '_page_mlang_strings',
108
+ $screen_id . '_page_mlang_settings',
109
+ )
110
+ ),
111
+ $notice
112
  );
113
  }
114
 
120
  * @param string $notice
121
  */
122
  public static function dismiss( $notice ) {
123
+ $dismissed = get_option( 'pll_dismissed_notices', array() );
 
 
124
 
125
  if ( ! in_array( $notice, $dismissed ) ) {
126
  $dismissed[] = $notice;
127
+ update_option( 'pll_dismissed_notices', array_unique( $dismissed ) );
128
  }
129
  }
130
 
149
  * @since 2.3.9
150
  */
151
  public function display_notices() {
152
+ if ( current_user_can( 'manage_options' ) ) {
153
  // Core notices
154
+ if ( defined( 'WOOCOMMERCE_VERSION' ) && ! defined( 'PLLWC_VERSION' ) && $this->can_display_notice( 'pllwc' ) && ! $this->is_dismissed( 'pllwc' ) ) {
155
+ $this->pllwc_notice();
156
+ }
157
+
158
+ if ( ! defined( 'POLYLANG_PRO' ) && $this->can_display_notice( 'review' ) && ! $this->is_dismissed( 'review' ) && ! empty( $this->options['first_activation'] ) && time() > $this->options['first_activation'] + 15 * DAY_IN_SECONDS ) {
159
+ $this->review_notice();
160
+ }
161
 
162
  // Custom notices
163
  foreach ( $this->get_notices() as $notice => $html ) {
164
+ if ( $this->can_display_notice( $notice ) && ! $this->is_dismissed( $notice ) ) {
165
  ?>
166
  <div class="pll-notice notice notice-info">
167
  <?php
197
  * @since 2.3.9
198
  */
199
  private function pllwc_notice() {
200
+ ?>
201
+ <div class="pll-notice notice notice-warning">
202
+ <?php $this->dismiss_button( 'pllwc' ); ?>
203
+ <p>
204
+ <?php
205
+ printf(
206
+ /* translators: %1$s is link start tag, %2$s is link end tag. */
207
+ esc_html__( 'We have noticed that you are using Polylang with WooCommerce. To ensure compatibility, we recommend you use %1$sPolylang for WooCommerce%2$s.', 'polylang' ),
208
+ '<a href="https://polylang.pro/downloads/polylang-for-woocommerce/">',
209
+ '</a>'
210
+ );
211
+ ?>
212
+ </p>
213
+ </div>
214
+ <?php
 
 
215
  }
216
 
217
  /**
220
  * @since 2.3.9
221
  */
222
  private function review_notice() {
223
+ ?>
224
+ <div class="pll-notice notice notice-info">
225
+ <?php $this->dismiss_button( 'review' ); ?>
226
+ <p>
227
+ <?php
228
+ printf(
229
+ /* translators: %1$s is link start tag, %2$s is link end tag. */
230
+ esc_html__( 'We have noticed that you have been using Polylang for some time. We hope you love it, and we would really appreciate it if you would %1$sgive us a 5 stars rating%2$s.', 'polylang' ),
231
+ '<a href="https://wordpress.org/support/plugin/polylang/reviews/?rate=5#new-post">',
232
+ '</a>'
233
+ );
234
+ ?>
235
+ </p>
236
+ </div>
237
+ <?php
 
 
238
  }
239
  }
admin/admin-static-pages.php CHANGED
@@ -86,8 +86,8 @@ class PLL_Admin_Static_Pages extends PLL_Static_Pages {
86
  *
87
  * @since 1.8
88
  *
89
- * @param array $post_states
90
- * @param object $post
91
  * @return array
92
  */
93
  public function display_post_states( $post_states, $post ) {
86
  *
87
  * @since 1.8
88
  *
89
+ * @param array $post_states An array of post display states.
90
+ * @param object $post The current post object.
91
  * @return array
92
  */
93
  public function display_post_states( $post_states, $post ) {
admin/admin-strings.php CHANGED
@@ -118,7 +118,6 @@ class PLL_Admin_Strings {
118
  * @return string
119
  */
120
  public static function sanitize_string_translation( $translation, $name ) {
121
- $translation = wp_unslash( trim( $translation ) );
122
 
123
  if ( false !== ( $option = array_search( $name, self::$default_strings['options'], true ) ) ) {
124
  $translation = sanitize_option( $option, $translation );
118
  * @return string
119
  */
120
  public static function sanitize_string_translation( $translation, $name ) {
 
121
 
122
  if ( false !== ( $option = array_search( $name, self::$default_strings['options'], true ) ) ) {
123
  $translation = sanitize_option( $option, $translation );
admin/admin.php CHANGED
@@ -25,8 +25,12 @@
25
  * block_editor => reference to PLL_Admin_Block_Editor object
26
  * classic_editor => reference to PLL_Admin_Classic_Editor object
27
  * filters_media => optional, reference to PLL_Admin_Filters_Media object
 
 
28
  *
29
  * @since 1.2
 
 
30
  */
31
  class PLL_Admin extends PLL_Admin_Base {
32
  public $filters, $filters_columns, $filters_post, $filters_term, $nav_menu, $sync, $filters_media;
@@ -61,11 +65,6 @@ class PLL_Admin extends PLL_Admin_Base {
61
  if ( $this->model->get_languages_list() ) {
62
  add_action( 'wp_loaded', array( $this, 'add_filters' ), 5 );
63
  add_action( 'admin_init', array( $this, 'maybe_load_sync_post' ), 20 ); // After fusion Builder.
64
-
65
- // Bulk Translate
66
- if ( class_exists( 'PLL_Bulk_Translate' ) ) {
67
- add_action( 'current_screen', array( $this->bulk_translate = new PLL_Bulk_Translate( $this ), 'init' ) );
68
- }
69
  }
70
  }
71
 
@@ -100,6 +99,7 @@ class PLL_Admin extends PLL_Admin_Base {
100
  * Setup filters for admin pages
101
  *
102
  * @since 1.2
 
103
  */
104
  public function add_filters() {
105
  // All these are separated just for convenience and maintainability
@@ -127,6 +127,13 @@ class PLL_Admin extends PLL_Admin_Base {
127
  $this->posts = new PLL_CRUD_Posts( $this );
128
  $this->terms = new PLL_CRUD_Terms( $this );
129
 
 
 
 
 
 
 
 
130
  // Advanced media
131
  if ( $this->options['media_support'] && class_exists( 'PLL_Admin_Advanced_Media' ) ) {
132
  $this->advanced_media = new PLL_Admin_Advanced_Media( $this );
@@ -146,6 +153,10 @@ class PLL_Admin extends PLL_Admin_Base {
146
  $this->duplicate_rest = new PLL_Duplicate_REST();
147
  }
148
 
 
 
 
 
149
  // Block editor metabox
150
  if ( pll_use_block_editor_plugin() ) {
151
  $this->block_editor_plugin = new PLL_Block_Editor_Plugin( $this );
25
  * block_editor => reference to PLL_Admin_Block_Editor object
26
  * classic_editor => reference to PLL_Admin_Classic_Editor object
27
  * filters_media => optional, reference to PLL_Admin_Filters_Media object
28
+ * bulk_translate => reference, a PLL_Bulk_Translate subclass instance
29
+ * wizard => reference, a PLL_Wizard object
30
  *
31
  * @since 1.2
32
+ * @since 2.7 Added a reference to a PLL_Bulk_Translate instance.
33
+ * @since 2.7 Added a reference to a PLL_Wizard object.
34
  */
35
  class PLL_Admin extends PLL_Admin_Base {
36
  public $filters, $filters_columns, $filters_post, $filters_term, $nav_menu, $sync, $filters_media;
65
  if ( $this->model->get_languages_list() ) {
66
  add_action( 'wp_loaded', array( $this, 'add_filters' ), 5 );
67
  add_action( 'admin_init', array( $this, 'maybe_load_sync_post' ), 20 ); // After fusion Builder.
 
 
 
 
 
68
  }
69
  }
70
 
99
  * Setup filters for admin pages
100
  *
101
  * @since 1.2
102
+ * @since 2.7 instantiate a PLL_Bulk_Translate instance.
103
  */
104
  public function add_filters() {
105
  // All these are separated just for convenience and maintainability
127
  $this->posts = new PLL_CRUD_Posts( $this );
128
  $this->terms = new PLL_CRUD_Terms( $this );
129
 
130
+ // Bulk Translate
131
+ // Needs to be loaded before other modules.
132
+ if ( class_exists( 'PLL_Bulk_Translate' ) ) {
133
+ $this->bulk_translate = new PLL_Bulk_Translate( $this->model );
134
+ add_action( 'current_screen', array( $this->bulk_translate, 'init' ) );
135
+ }
136
+
137
  // Advanced media
138
  if ( $this->options['media_support'] && class_exists( 'PLL_Admin_Advanced_Media' ) ) {
139
  $this->advanced_media = new PLL_Admin_Advanced_Media( $this );
153
  $this->duplicate_rest = new PLL_Duplicate_REST();
154
  }
155
 
156
+ if ( class_exists( 'PLL_Sync_Post_Model' ) ) {
157
+ $this->sync_post_model = new PLL_Sync_Post_Model( $this );
158
+ }
159
+
160
  // Block editor metabox
161
  if ( pll_use_block_editor_plugin() ) {
162
  $this->block_editor_plugin = new PLL_Block_Editor_Plugin( $this );
admin/view-translations-media.php CHANGED
@@ -21,7 +21,7 @@ if ( ! defined( 'ABSPATH' ) ) {
21
  <td class = "pll-media-language-column"><span class = "pll-translation-flag"><?php echo $language->flag; // phpcs:ignore WordPress.Security.EscapeOutput ?></span><?php echo esc_html( $language->name ); ?></td>
22
  <td class = "pll-media-edit-column">
23
  <?php
24
- if ( ( $translation_id = $this->model->post->get_translation( $post_id, $language ) ) && $translation_id !== $post_id ) {
25
  // The translation exists
26
  printf(
27
  '<input type="hidden" name="media_tr_lang[%s]" value="%d" />',
@@ -31,7 +31,7 @@ if ( ! defined( 'ABSPATH' ) ) {
31
  echo $this->links->edit_post_translation_link( $translation_id ); // phpcs:ignore WordPress.Security.EscapeOutput
32
  } else {
33
  // No translation
34
- echo $this->links->new_post_translation_link( $post_id, $language ); // phpcs:ignore WordPress.Security.EscapeOutput
35
  }
36
  ?>
37
  </td>
21
  <td class = "pll-media-language-column"><span class = "pll-translation-flag"><?php echo $language->flag; // phpcs:ignore WordPress.Security.EscapeOutput ?></span><?php echo esc_html( $language->name ); ?></td>
22
  <td class = "pll-media-edit-column">
23
  <?php
24
+ if ( ( $translation_id = $this->model->post->get_translation( $post_ID, $language ) ) && $translation_id !== $post_ID ) {
25
  // The translation exists
26
  printf(
27
  '<input type="hidden" name="media_tr_lang[%s]" value="%d" />',
31
  echo $this->links->edit_post_translation_link( $translation_id ); // phpcs:ignore WordPress.Security.EscapeOutput
32
  } else {
33
  // No translation
34
+ echo $this->links->new_post_translation_link( $post_ID, $language ); // phpcs:ignore WordPress.Security.EscapeOutput
35
  }
36
  ?>
37
  </td>
css/admin.css CHANGED
@@ -21,6 +21,14 @@
21
  font-family: 'dashicons';
22
  content: "\f155";
23
  }
 
 
 
 
 
 
 
 
24
 
25
  .form-field input[type="radio"] {
26
  width: auto;
@@ -324,6 +332,10 @@ td[class*='column-language_'] {
324
  text-decoration: none;
325
  }
326
 
 
 
 
 
327
  /* Bulk translate */
328
  .bulk-translate-save .button {
329
  margin-right: 20px;
@@ -340,8 +352,8 @@ td[class*='column-language_'] {
340
 
341
  #pll-translate .title {
342
  display: block;
343
- margin: 0.2em 0;
344
- line-height: 2.5;
345
  }
346
 
347
  @media screen and ( max-width: 782px ) {
21
  font-family: 'dashicons';
22
  content: "\f155";
23
  }
24
+ .pll-icon:before{
25
+ display: inline-block;
26
+ text-align: left;
27
+ width: 15px;
28
+ }
29
+ .pll-circle:before{
30
+ content: "\25cf";
31
+ }
32
 
33
  .form-field input[type="radio"] {
34
  width: auto;
332
  text-decoration: none;
333
  }
334
 
335
+ .pll-notice .button {
336
+ margin-right: 10px;
337
+ }
338
+
339
  /* Bulk translate */
340
  .bulk-translate-save .button {
341
  margin-right: 20px;
352
 
353
  #pll-translate .title {
354
  display: block;
355
+ margin: 0.2em 0;
356
+ line-height: 2.5;
357
  }
358
 
359
  @media screen and ( max-width: 782px ) {
css/admin.min.css CHANGED
@@ -1 +1 @@
1
- #pll-licenses-table td,.translation label{vertical-align:top}#post-translations a,.pll-notice a.notice-dismiss{text-decoration:none}#add-lang select{width:95%}.column-locale,.languages .column-slug{width:15%}.column-default_lang{width:5%}.column-count,.column-flag,.column-term_group{width:10%}.icon-default-lang:before{font-family:dashicons;content:"\f155"}.form-field input[type=radio]{width:auto;margin-right:2px}#pll-about-box p,#pll-recommended p{text-align:justify}#pll-about-box input{margin:0;padding:0;float:right}.stringstranslations .column-context,.stringstranslations .column-name{width:10%}.stringstranslations .column-string{width:33%}.translation label{display:inline-block;width:23%}.translation input,.translation textarea{width:72%}.pll-settings{margin-top:20px}.pll-settings .plugin-title{width:25%}#wpbody-content .pll-settings .pll-configure tr{display:table-row}#wpbody-content .pll-settings .pll-configure td{display:table-cell}#wpbody-content .pll-settings .pll-configure>td{padding:20px 20px 20px 40px}.pll-configure legend{font-size:14px;font-weight:600;margin-bottom:.5em}.pll-configure td .description{margin-top:2px;margin-bottom:.5em}.pll-configure p.submit{margin-top:20px}.pll-configure .button{margin-right:20px}.pll-configure fieldset{margin-bottom:1.5em}.pll-inline-block-list{margin:0}.pll-inline-block-list li{display:inline-block;margin:0;width:250px}#pll-domains-table td{padding:2px 2px 2px 1.5em;-webkit-box-shadow:none;box-shadow:none;border:none}.pll-settings-url-col{display:inline-block;width:49%;vertical-align:top}#pll-licenses-table label{font-size:1em;font-weight:600}.pll-configure .pll-deactivate-license{margin:0 0 0 20px}td[class*=column-language_],th[class*=column-language_]{width:1.5em}.pll-dir-rtl input[type=text],.pll-dir-rtl textarea{direction:rtl}.pll-dir-ltr input[type=text],.pll-dir-ltr textarea{direction:ltr}.pll-dir-ltr .tr_lang,.pll-dir-rtl .tr_lang{direction:inherit}#post-translations p{float:left}#post-translations table{table-layout:fixed;width:100%;clear:both}#post-translations .pll-column-icon,#post-translations .pll-language-column{width:20px}#post-translations .tr_lang{width:100%}#post-translations td{padding:2px}#post-translations .spinner,#term-translations .spinner{float:none;margin:0;background-position:center;width:auto}.pll-column-icon{text-align:center}#select-post-language .pll-select-flag{padding:4px;margin-right:32px}#select-media-language .pll-select-flag{padding:4px;margin-right:10px}.pll-media-edit-column{float:right}.pll-button{padding:0;height:20px;background:0 0;border:none;font-size:20px;cursor:pointer}.pll-button:not(.wp-ui-text-highlight){color:#DDD}#pll-duplicate{float:right;margin:13px 7px}.pll-translation-flag{margin-right:14px}#select-add-term-language .pll-select-flag{padding:11px;margin-right:13px}#select-edit-term-language .pll-select-flag{padding:11px;margin-right:4px}#term-translations p{font-weight:400;font-style:normal;padding:2px;color:#23282d}#add-term-translations,#edit-term-translations{width:95%}#term-translations .pll-language-column{line-height:28px;width:20%}#add-term-translations .pll-language-column,#term-translations .pll-edit-column{width:20px}#edit-term-translations .pll-language-column{padding:15px 10px;font-weight:400}.pll_icon_tick:before{content:"\f147"}.pll_icon_add:before{content:"\f132"}.pll_icon_edit:before{content:"\f464"}[class^=pll_icon_]{font:20px/1 dashicons;vertical-align:middle}#wpadminbar #wp-admin-bar-languages .ab-item img{margin:0 8px 0 2px}#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon{float:none;top:4px}#wpadminbar #wp-admin-bar-languages .ab-icon:before{content:"\f326";top:1px}.pll-notice.notice{padding-right:38px;position:relative}.bulk-translate-save .button{margin-right:20px}#wpbody-content #pll-translate fieldset{display:inline-block;width:300px}#pll-translate .pll-translation-flag{margin-right:.3em}#pll-translate .title{display:block;margin:.2em 0;line-height:2.5}@media screen and (max-width:782px){#wpbody-content .pll-settings .pll-configure>td{padding:20px}#wpbody-content .pll-settings #cb{padding:20px 9px}.pll-inline-block{width:auto}.pll-settings-url-col{display:block;width:100%}#wpbody-content .pll-settings #pll-licenses-table td{display:block}.pll-configure .pll-deactivate-license{margin:10px 0 5px}.stringstranslations .column-context,.stringstranslations .column-name{display:none}.translation label{display:block;width:95%;padding-left:0}.translation input{width:95%}#edit-term-translations .pll-language-name,#select-add-term-language .pll-select-flag,#select-edit-term-language .pll-select-flag{display:none}#edit-term-translations{width:100%}#add-term-translations .pll-language-column{line-height:38px}#edit-term-translations td{padding:8px 10px}#edit-term-translations .pll-edit-column,#edit-term-translations .pll-language-column{width:20px}.term-translations .pll-edit-column,.term-translations .pll-language-column,.term-translations .pll-translation-column{display:table-cell}.term-translations .hidden{display:none}#wpadminbar #wp-admin-bar-languages{display:block}#wpadminbar #wp-admin-bar-languages>.ab-item{width:50px;text-align:center}#wpadminbar #wp-admin-bar-languages>.ab-item .ab-icon:before{font:32px/1 dashicons;top:-1px}#wpadminbar #wp-admin-bar-languages>.ab-item img{margin:19px 0}#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon{margin-right:6px;font-size:20px!important;line-height:20px!important}}
1
+ #add-lang select{width:95%}.column-locale,.languages .column-slug{width:15%}.column-default_lang{width:5%}.column-count,.column-flag,.column-term_group{width:10%}.icon-default-lang:before{font-family:dashicons;content:"\f155"}.pll-icon:before{display:inline-block;text-align:left;width:15px}.pll-circle:before{content:"\25cf"}.form-field input[type=radio]{width:auto;margin-right:2px}#pll-about-box p,#pll-recommended p{text-align:justify}#pll-about-box input{margin:0;padding:0;float:right}.stringstranslations .column-context,.stringstranslations .column-name{width:10%}.stringstranslations .column-string{width:33%}.translation label{display:inline-block;width:23%;vertical-align:top}.translation input,.translation textarea{width:72%}.pll-settings{margin-top:20px}.pll-settings .plugin-title{width:25%}#wpbody-content .pll-settings .pll-configure tr{display:table-row}#wpbody-content .pll-settings .pll-configure td{display:table-cell}#wpbody-content .pll-settings .pll-configure>td{padding:20px 20px 20px 40px}.pll-configure legend{font-size:14px;font-weight:600;margin-bottom:.5em}.pll-configure td .description{margin-top:2px;margin-bottom:.5em}.pll-configure p.submit{margin-top:20px}.pll-configure .button{margin-right:20px}.pll-configure fieldset{margin-bottom:1.5em}.pll-inline-block-list{margin:0}.pll-inline-block-list li{display:inline-block;margin:0;width:250px}#pll-domains-table td{padding:2px 2px 2px 1.5em;-webkit-box-shadow:none;box-shadow:none;border:none}.pll-settings-url-col{display:inline-block;width:49%;vertical-align:top}#pll-licenses-table td{vertical-align:top}#pll-licenses-table label{font-size:1em;font-weight:600}.pll-configure .pll-deactivate-license{margin:0 0 0 20px}td[class*=column-language_],th[class*=column-language_]{width:1.5em}.pll-dir-rtl input[type=text],.pll-dir-rtl textarea{direction:rtl}.pll-dir-ltr input[type=text],.pll-dir-ltr textarea{direction:ltr}.pll-dir-ltr .tr_lang,.pll-dir-rtl .tr_lang{direction:inherit}#post-translations p{float:left}#post-translations table{table-layout:fixed;width:100%;clear:both}#post-translations a{text-decoration:none}#post-translations .pll-column-icon,#post-translations .pll-language-column{width:20px}#post-translations .tr_lang{width:100%}#post-translations td{padding:2px}#post-translations .spinner,#term-translations .spinner{float:none;margin:0;background-position:center;width:auto}.pll-column-icon{text-align:center}#select-post-language .pll-select-flag{padding:4px;margin-right:32px}#select-media-language .pll-select-flag{padding:4px;margin-right:10px}.pll-media-edit-column{float:right}.pll-button{padding:0;height:20px;background:0 0;border:none;font-size:20px;cursor:pointer}.pll-button:not(.wp-ui-text-highlight){color:#ddd}#pll-duplicate{float:right;margin:13px 7px}.pll-translation-flag{margin-right:14px}#select-add-term-language .pll-select-flag{padding:11px;margin-right:13px}#select-edit-term-language .pll-select-flag{padding:11px;margin-right:4px}#term-translations p{font-weight:400;font-style:normal;padding:2px;color:#23282d}#add-term-translations,#edit-term-translations{width:95%}#term-translations .pll-language-column{line-height:28px;width:20%}#add-term-translations .pll-language-column,#term-translations .pll-edit-column{width:20px}#edit-term-translations .pll-language-column{padding:15px 10px;font-weight:400}.pll_icon_tick:before{content:"\f147"}.pll_icon_add:before{content:"\f132"}.pll_icon_edit:before{content:"\f464"}[class^=pll_icon_]{font:20px/1 dashicons;vertical-align:middle}#wpadminbar #wp-admin-bar-languages .ab-item img{margin:0 8px 0 2px}#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon{float:none;top:4px}#wpadminbar #wp-admin-bar-languages .ab-icon:before{content:"\f326";top:1px}.pll-notice.notice{padding-right:38px;position:relative}.pll-notice a.notice-dismiss{text-decoration:none}.pll-notice .button{margin-right:10px}.bulk-translate-save .button{margin-right:20px}#wpbody-content #pll-translate fieldset{display:inline-block;width:300px}#pll-translate .pll-translation-flag{margin-right:.3em}#pll-translate .title{display:block;margin:.2em 0;line-height:2.5}@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}}
css/selectmenu.css CHANGED
@@ -24,11 +24,17 @@
24
  position: relative;
25
  margin: 0;
26
  padding: 3px 1em 3px .4em;
 
27
  min-height: 0; /* support: IE7 */
28
  /* support: IE10, see #8844 */
29
  list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
30
  }
31
 
 
 
 
 
 
32
  /* icon support */
33
  .ui-menu-icons {
34
  position: relative;
@@ -37,6 +43,10 @@
37
  .ui-menu-icons .ui-menu-item {
38
  padding-left: 2em;
39
  }
 
 
 
 
40
 
41
  /* left-aligned */
42
  .ui-selectmenu-text .ui-icon,
@@ -44,10 +54,16 @@
44
  position: absolute;
45
  top: 0;
46
  bottom: 0;
47
- left: .2em;
48
  margin: auto 0;
49
  }
50
 
 
 
 
 
 
 
51
  /* right-aligned */
52
  .ui-menu .ui-menu-icon {
53
  left: auto;
@@ -97,8 +113,15 @@
97
  margin-top: -10px;
98
  position: absolute;
99
  top: 50%;
 
 
 
 
 
 
100
  }
101
 
 
102
  .ui-selectmenu-button span.ui-selectmenu-text {
103
  text-align: left;
104
  padding: 0.2em 2.1em 0.2em 2em;
@@ -109,6 +132,11 @@
109
  white-space: nowrap;
110
  }
111
 
 
 
 
 
 
112
  .ui-widget-content,
113
  .ui-state-default,
114
  .ui-widget-content .ui-state-default,
24
  position: relative;
25
  margin: 0;
26
  padding: 3px 1em 3px .4em;
27
+ cursor: pointer;
28
  min-height: 0; /* support: IE7 */
29
  /* support: IE10, see #8844 */
30
  list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
31
  }
32
 
33
+ .rtl .ui-menu .ui-menu-item {
34
+ text-align: right;
35
+ }
36
+
37
+
38
  /* icon support */
39
  .ui-menu-icons {
40
  position: relative;
43
  .ui-menu-icons .ui-menu-item {
44
  padding-left: 2em;
45
  }
46
+ .rtl .ui-menu-icons .ui-menu-item {
47
+ padding-left: 1em;
48
+ padding-right: 2em;
49
+ }
50
 
51
  /* left-aligned */
52
  .ui-selectmenu-text .ui-icon,
54
  position: absolute;
55
  top: 0;
56
  bottom: 0;
57
+ left: .3em;
58
  margin: auto 0;
59
  }
60
 
61
+ .rtl .ui-selectmenu-text .ui-icon,
62
+ .rtl .ui-menu .ui-icon {
63
+ right: .3em;
64
+ left: auto;
65
+ }
66
+
67
  /* right-aligned */
68
  .ui-menu .ui-menu-icon {
69
  left: auto;
113
  margin-top: -10px;
114
  position: absolute;
115
  top: 50%;
116
+ text-indent: 0; /* due to text-indent for jquery ui-dialog in wizard */
117
+ }
118
+
119
+ .rtl .ui-selectmenu-button span.ui-icon {
120
+ left: 0.5em;
121
+ right: auto;
122
  }
123
 
124
+
125
  .ui-selectmenu-button span.ui-selectmenu-text {
126
  text-align: left;
127
  padding: 0.2em 2.1em 0.2em 2em;
132
  white-space: nowrap;
133
  }
134
 
135
+ .rtl .ui-selectmenu-button span.ui-selectmenu-text {
136
+ text-align: right;
137
+ padding: 0.2em 2em 0.2em 2.1em;
138
+ }
139
+
140
  .ui-widget-content,
141
  .ui-state-default,
142
  .ui-widget-content .ui-state-default,
css/selectmenu.min.css CHANGED
@@ -1 +1 @@
1
- .ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;min-height:0;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item{padding-left:2em}.ui-menu .ui-icon,.ui-selectmenu-text .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:700;line-height:23px;padding:2px .4em;margin:.5em 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-button{display:inline-block;overflow:hidden;position:relative;text-decoration:none}.ui-selectmenu-button span.ui-icon{right:.5em;left:auto;margin-top:-10px;position:absolute;top:50%}.ui-selectmenu-button span.ui-selectmenu-text{text-align:left;padding:.2em 2.1em .2em 2em;display:block;line-height:23px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-state-default,.ui-widget-content,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{background:#fff;border:1px solid #ddd;box-shadow:0 1px 2px rgba(0,0,0,.07) inset;color:#32373c}.ui-widget-content .ui-state-focus,.ui-widget-content .ui-state-hover{background:#f5f5f5}.ui-selectmenu-button.ui-state-focus{border:1px solid #5b9dd9;box-shadow:0 0 2px rgba(30,140,190,.8)}.ui-icon-triangle-1-s:before{content:"\f140";font:20px/1 dashicons}.ui-widget-content{max-height:231px;box-shadow:0 2px 6px rgba(100,100,100,.3)}
1
+ .ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;cursor:pointer;min-height:0;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.rtl .ui-menu .ui-menu-item{text-align:right}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item{padding-left:2em}.rtl .ui-menu-icons .ui-menu-item{padding-left:1em;padding-right:2em}.ui-menu .ui-icon,.ui-selectmenu-text .ui-icon{position:absolute;top:0;bottom:0;left:.3em;margin:auto 0}.rtl .ui-menu .ui-icon,.rtl .ui-selectmenu-text .ui-icon{right:.3em;left:auto}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:700;line-height:23px;padding:2px .4em;margin:.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-button{display:inline-block;overflow:hidden;position:relative;text-decoration:none}.ui-selectmenu-button span.ui-icon{right:.5em;left:auto;margin-top:-10px;position:absolute;top:50%;text-indent:0}.rtl .ui-selectmenu-button span.ui-icon{left:.5em;right:auto}.ui-selectmenu-button span.ui-selectmenu-text{text-align:left;padding:.2em 2.1em .2em 2em;display:block;line-height:23px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rtl .ui-selectmenu-button span.ui-selectmenu-text{text-align:right;padding:.2em 2em .2em 2.1em}.ui-state-default,.ui-widget-content,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{background:#fff;border:1px solid #ddd;box-shadow:0 1px 2px rgba(0,0,0,.07) inset;color:#32373c}.ui-widget-content .ui-state-focus,.ui-widget-content .ui-state-hover{background:#f5f5f5}.ui-selectmenu-button.ui-state-focus{border:1px solid #5b9dd9;box-shadow:0 0 2px rgba(30,140,190,.8)}.ui-icon-triangle-1-s:before{content:"\f140";font:20px/1 dashicons}.ui-widget-content{max-height:231px;box-shadow:0 2px 6px rgba(100,100,100,.3)}
frontend/choose-lang.php CHANGED
@@ -191,16 +191,25 @@ abstract class PLL_Choose_Lang {
191
  }
192
 
193
  /**
194
- * Returns the language according to browser preference or the default language
 
 
 
195
  *
196
  * @since 0.1
197
  *
198
  * @return object browser preferred language or default language
199
  */
200
  public function get_preferred_language() {
201
- // check first if the user was already browsing this site
 
 
202
  if ( isset( $_COOKIE[ PLL_COOKIE ] ) ) {
203
- return $this->model->get_language( sanitize_key( $_COOKIE[ PLL_COOKIE ] ) );
 
 
 
 
204
  }
205
 
206
  /**
@@ -210,12 +219,14 @@ abstract class PLL_Choose_Lang {
210
  * Polylang fallbacks to the default language
211
  *
212
  * @since 1.0
 
213
  *
214
- * @param string $language preferred language code
 
215
  */
216
- $slug = apply_filters( 'pll_preferred_language', $this->options['browser'] ? $this->get_preferred_browser_language() : false );
217
 
218
- // return default if there is no preferences in the browser or preferences does not match our languages or it is requested not to use the browser preference
219
  return ( $lang = $this->model->get_language( $slug ) ) ? $lang : $this->model->get_language( $this->options['default_lang'] );
220
  }
221
 
@@ -274,6 +285,7 @@ abstract class PLL_Choose_Lang {
274
  */
275
  if ( $redirect = apply_filters( 'pll_redirect_home', $redirect ) ) {
276
  $this->maybe_setcookie();
 
277
  wp_safe_redirect( $redirect, 302, POLYLANG );
278
  exit;
279
  }
191
  }
192
 
193
  /**
194
+ * Returns the preferred language
195
+ * either from the cookie if it's a returning visit
196
+ * or according to browser preference
197
+ * or the default language
198
  *
199
  * @since 0.1
200
  *
201
  * @return object browser preferred language or default language
202
  */
203
  public function get_preferred_language() {
204
+ $language = false;
205
+ $cookie = false;
206
+
207
  if ( isset( $_COOKIE[ PLL_COOKIE ] ) ) {
208
+ // Check first if the user was already browsing this site.
209
+ $language = sanitize_key( $_COOKIE[ PLL_COOKIE ] );
210
+ $cookie = true;
211
+ } elseif ( $this->options['browser'] ) {
212
+ $language = $this->get_preferred_browser_language();
213
  }
214
 
215
  /**
219
  * Polylang fallbacks to the default language
220
  *
221
  * @since 1.0
222
+ * @since 2.7 Added $cookie parameter.
223
  *
224
+ * @param string|bool $language Preferred language code, false if none has been found.
225
+ * @param bool $cookie Whether the preferred language has been defined by the cookie.
226
  */
227
+ $slug = apply_filters( 'pll_preferred_language', $language, $cookie );
228
 
229
+ // Return default if there is no preferences in the browser or preferences does not match our languages or it is requested not to use the browser preference
230
  return ( $lang = $this->model->get_language( $slug ) ) ? $lang : $this->model->get_language( $this->options['default_lang'] );
231
  }
232
 
285
  */
286
  if ( $redirect = apply_filters( 'pll_redirect_home', $redirect ) ) {
287
  $this->maybe_setcookie();
288
+ header( 'Vary: Accept-Language' );
289
  wp_safe_redirect( $redirect, 302, POLYLANG );
290
  exit;
291
  }
frontend/frontend-auto-translate.php CHANGED
@@ -185,12 +185,12 @@ class PLL_Frontend_Auto_Translate {
185
 
186
  // Array of post ids
187
  // post_parent__in & post_parent__not_in since WP 3.6
188
- foreach ( array( 'post__in', 'post__not_in', 'post_parent__in', 'post_parent__not_in' ) as $key ) {
189
  $arr = array();
190
  if ( ! empty( $qv[ $key ] ) ) {
191
  // post__in used by the 2 functions below
192
  // Useless to filter them as output is already in the right language and would result in performance loss
193
- foreach ( debug_backtrace() as $trace ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions
194
  if ( in_array( $trace['function'], array( 'wp_nav_menu', 'gallery_shortcode' ) ) ) {
195
  return;
196
  }
@@ -216,6 +216,8 @@ class PLL_Frontend_Auto_Translate {
216
  */
217
  public function get_terms_args( $args, $taxonomies ) {
218
  if ( ! isset( $args['lang'] ) && ! empty( $args['include'] ) && ( empty( $taxonomies ) || $this->model->is_translated_taxonomy( $taxonomies ) ) ) {
 
 
219
  foreach ( wp_parse_id_list( $args['include'] ) as $id ) {
220
  $arr[] = ( $tr = $this->get_term( $id ) ) ? $tr : $id;
221
  }
185
 
186
  // Array of post ids
187
  // post_parent__in & post_parent__not_in since WP 3.6
188
+ foreach ( array( 'post__in', 'post__not_in', 'post_parent__in', 'post_parent__not_in' ) as $key ) { // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn
189
  $arr = array();
190
  if ( ! empty( $qv[ $key ] ) ) {
191
  // post__in used by the 2 functions below
192
  // Useless to filter them as output is already in the right language and would result in performance loss
193
+ foreach ( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ) as $trace ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions
194
  if ( in_array( $trace['function'], array( 'wp_nav_menu', 'gallery_shortcode' ) ) ) {
195
  return;
196
  }
216
  */
217
  public function get_terms_args( $args, $taxonomies ) {
218
  if ( ! isset( $args['lang'] ) && ! empty( $args['include'] ) && ( empty( $taxonomies ) || $this->model->is_translated_taxonomy( $taxonomies ) ) ) {
219
+ $arr = array();
220
+
221
  foreach ( wp_parse_id_list( $args['include'] ) as $id ) {
222
  $arr[] = ( $tr = $this->get_term( $id ) ) ? $tr : $id;
223
  }
frontend/frontend-filters-links.php CHANGED
@@ -175,6 +175,8 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
175
  return;
176
  }
177
 
 
 
178
  // Google recommends to include self link https://support.google.com/webmasters/answer/189077?hl=en
179
  foreach ( $this->model->get_languages_list() as $language ) {
180
  if ( $url = $this->links->get_translation_url( $language ) ) {
@@ -184,6 +186,8 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
184
 
185
  // Outputs the section only if there are translations ( $urls always contains self link )
186
  if ( ! empty( $urls ) && count( $urls ) > 1 ) {
 
 
187
 
188
  // Prepare the list of languages to remove the country code
189
  foreach ( array_keys( $urls ) as $locale ) {
@@ -285,7 +289,7 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
285
  );
286
  }
287
 
288
- $traces = version_compare( PHP_VERSION, '5.2.5', '>=' ) ? debug_backtrace( false ) : debug_backtrace(); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
289
  unset( $traces[0], $traces[1] ); // We don't need the last 2 calls: this function + call_user_func_array (or apply_filters on PHP7+)
290
 
291
  foreach ( $traces as $trace ) {
175
  return;
176
  }
177
 
178
+ $urls = array();
179
+
180
  // Google recommends to include self link https://support.google.com/webmasters/answer/189077?hl=en
181
  foreach ( $this->model->get_languages_list() as $language ) {
182
  if ( $url = $this->links->get_translation_url( $language ) ) {
186
 
187
  // Outputs the section only if there are translations ( $urls always contains self link )
188
  if ( ! empty( $urls ) && count( $urls ) > 1 ) {
189
+ $languages = array();
190
+ $hreflangs = array();
191
 
192
  // Prepare the list of languages to remove the country code
193
  foreach ( array_keys( $urls ) as $locale ) {
289
  );
290
  }
291
 
292
+ $traces = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
293
  unset( $traces[0], $traces[1] ); // We don't need the last 2 calls: this function + call_user_func_array (or apply_filters on PHP7+)
294
 
295
  foreach ( $traces as $trace ) {
frontend/frontend-links.php CHANGED
@@ -55,7 +55,6 @@ class PLL_Frontend_Links extends PLL_Links {
55
  */
56
  if ( ! $url = apply_filters( 'pll_pre_translation_url', '', $language, $queried_object_id ) ) {
57
  $qv = $wp_query->query_vars;
58
- $hide = $this->options['default_lang'] == $language->slug && $this->options['hide_default'];
59
 
60
  // Post and attachment
61
  if ( is_single() && ( $this->options['media_support'] || ! is_attachment() ) && ( $id = $this->model->post->get( $queried_object_id, $language ) ) && $this->model->post->current_user_can_read( $id ) ) {
@@ -96,7 +95,7 @@ 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 = 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 ) ) {
@@ -115,7 +114,7 @@ class PLL_Frontend_Links extends PLL_Links {
115
  * @param array $args Arguments used to evaluated the number of posts in the archive
116
  */
117
  if ( ! apply_filters( 'pll_hide_archive_translation_url', ! $count, $language->slug, array( 'taxonomy' => $term->taxonomy ) ) ) {
118
- $url = wpcom_vip_get_term_link( $tr_term, $term->taxonomy );
119
  }
120
  }
121
  }
55
  */
56
  if ( ! $url = apply_filters( 'pll_pre_translation_url', '', $language, $queried_object_id ) ) {
57
  $qv = $wp_query->query_vars;
 
58
 
59
  // Post and attachment
60
  if ( is_single() && ( $this->options['media_support'] || ! is_attachment() ) && ( $id = $this->model->post->get( $queried_object_id, $language ) ) && $this->model->post->current_user_can_read( $id ) ) {
95
  $lang = $this->model->term->get_language( $term->term_id );
96
 
97
  if ( ! $lang || $language->slug == $lang->slug ) {
98
+ $url = get_term_link( $term, $term->taxonomy ); // Self link
99
  }
100
 
101
  elseif ( $tr_id = $this->model->term->get_translation( $term->term_id, $language ) ) {
114
  * @param array $args Arguments used to evaluated the number of posts in the archive
115
  */
116
  if ( ! apply_filters( 'pll_hide_archive_translation_url', ! $count, $language->slug, array( 'taxonomy' => $term->taxonomy ) ) ) {
117
+ $url = get_term_link( $tr_term, $term->taxonomy );
118
  }
119
  }
120
  }
frontend/frontend-nav-menu.php CHANGED
@@ -92,7 +92,7 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
92
  $new_items = array();
93
  $offset = 0;
94
 
95
- foreach ( $items as $key => $item ) {
96
  if ( $options = get_post_meta( $item->ID, '_pll_menu_item', true ) ) {
97
  $i = 0;
98
 
@@ -219,7 +219,7 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
219
  // First get multilingual menu locations from DB
220
  $theme = get_option( 'stylesheet' );
221
 
222
- foreach ( $menus as $loc => $menu ) {
223
  $menus[ $loc ] = empty( $this->options['nav_menus'][ $theme ][ $loc ][ $this->curlang->slug ] ) ? 0 : $this->options['nav_menus'][ $theme ][ $loc ][ $this->curlang->slug ];
224
  }
225
 
@@ -275,7 +275,7 @@ class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
275
  if ( ! $menu && ! $args['theme_location'] ) {
276
  $menus = wp_get_nav_menus();
277
  foreach ( $menus as $menu_maybe ) {
278
- if ( $menu_items = wp_get_nav_menu_items( $menu_maybe->term_id, array( 'update_post_term_cache' => false ) ) ) {
279
  foreach ( $this->options['nav_menus'][ $theme ] as $menus ) {
280
  if ( in_array( $menu_maybe->term_id, $menus ) && ! empty( $menus[ $this->curlang->slug ] ) ) {
281
  $args['menu'] = $menus[ $this->curlang->slug ];
92
  $new_items = array();
93
  $offset = 0;
94
 
95
+ foreach ( $items as $item ) {
96
  if ( $options = get_post_meta( $item->ID, '_pll_menu_item', true ) ) {
97
  $i = 0;
98
 
219
  // First get multilingual menu locations from DB
220
  $theme = get_option( 'stylesheet' );
221
 
222
+ foreach ( array_keys( $menus ) as $loc ) {
223
  $menus[ $loc ] = empty( $this->options['nav_menus'][ $theme ][ $loc ][ $this->curlang->slug ] ) ? 0 : $this->options['nav_menus'][ $theme ][ $loc ][ $this->curlang->slug ];
224
  }
225
 
275
  if ( ! $menu && ! $args['theme_location'] ) {
276
  $menus = wp_get_nav_menus();
277
  foreach ( $menus as $menu_maybe ) {
278
+ if ( wp_get_nav_menu_items( $menu_maybe->term_id, array( 'update_post_term_cache' => false ) ) ) {
279
  foreach ( $this->options['nav_menus'][ $theme ] as $menus ) {
280
  if ( in_array( $menu_maybe->term_id, $menus ) && ! empty( $menus[ $this->curlang->slug ] ) ) {
281
  $args['menu'] = $menus[ $this->curlang->slug ];
frontend/frontend.php CHANGED
@@ -92,12 +92,14 @@ class PLL_Frontend extends PLL_Base {
92
 
93
  // Share term slugs
94
  if ( $this->options['force_lang'] && class_exists( 'PLL_Share_Term_Slug' ) ) {
95
- $this->share_term_slug = version_compare( $GLOBALS['wp_version'], '4.8', '<' ) ?
96
- new PLL_Frontend_Share_Term_Slug( $this ) :
97
- new PLL_Share_Term_Slug( $this );
98
  }
99
  }
100
 
 
 
 
 
101
  if ( class_exists( 'PLL_Sync_Post' ) ) {
102
  $this->sync_post = new PLL_Sync_Post( $this );
103
  }
92
 
93
  // Share term slugs
94
  if ( $this->options['force_lang'] && class_exists( 'PLL_Share_Term_Slug' ) ) {
95
+ $this->share_term_slug = new PLL_Share_Term_Slug( $this );
 
 
96
  }
97
  }
98
 
99
+ if ( class_exists( 'PLL_Sync_Post_Model' ) ) {
100
+ $this->sync_post_model = new PLL_Sync_Post_Model( $this );
101
+ }
102
+
103
  if ( class_exists( 'PLL_Sync_Post' ) ) {
104
  $this->sync_post = new PLL_Sync_Post( $this );
105
  }
include/api.php CHANGED
@@ -372,9 +372,9 @@ function pll_get_term_translations( $term_id ) {
372
  *
373
  * @since 1.5
374
  *
375
- * @param string $lang language code
376
- * @param array $args ( accepted keys: post_type, m, year, monthnum, day, author, author_name, post_format )
377
- * @return int posts count
378
  */
379
  function pll_count_posts( $lang, $args = array() ) {
380
  return PLL()->model->count_posts( PLL()->model->get_language( $lang ), $args );
372
  *
373
  * @since 1.5
374
  *
375
+ * @param string $lang Language code.
376
+ * @param array $args WP_Query arguments ( accepted keys: post_type, m, year, monthnum, day, author, author_name, post_format, post_status ).
377
+ * @return int Posts count.
378
  */
379
  function pll_count_posts( $lang, $args = array() ) {
380
  return PLL()->model->count_posts( PLL()->model->get_language( $lang ), $args );
include/base.php CHANGED
@@ -139,7 +139,7 @@ abstract class PLL_Base {
139
  foreach ( $this as $prop => &$obj ) {
140
  if ( is_object( $obj ) && method_exists( $obj, $func ) ) {
141
  if ( WP_DEBUG ) {
142
- $debug = debug_backtrace(); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
143
  $i = 1 + empty( $debug[1]['line'] ); // The file and line are in $debug[2] if the function was called using call_user_func
144
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
145
  sprintf(
@@ -155,7 +155,7 @@ abstract class PLL_Base {
155
  }
156
  }
157
 
158
- $debug = debug_backtrace(); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
159
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
160
  sprintf(
161
  'Call to undefined function PLL()->%1$s() in %2$s on line %3$s' . "\nError handler",
139
  foreach ( $this as $prop => &$obj ) {
140
  if ( is_object( $obj ) && method_exists( $obj, $func ) ) {
141
  if ( WP_DEBUG ) {
142
+ $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
143
  $i = 1 + empty( $debug[1]['line'] ); // The file and line are in $debug[2] if the function was called using call_user_func
144
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
145
  sprintf(
155
  }
156
  }
157
 
158
+ $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
159
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
160
  sprintf(
161
  'Call to undefined function PLL()->%1$s() in %2$s on line %3$s' . "\nError handler",
include/class-polylang.php CHANGED
@@ -30,6 +30,9 @@ class Polylang {
30
  require_once PLL_INC . '/functions.php'; // VIP functions
31
  spl_autoload_register( array( $this, 'autoload' ) ); // Autoload classes
32
 
 
 
 
33
  $install = new PLL_Install( POLYLANG_BASENAME );
34
 
35
  // Stopping here if we are going to deactivate the plugin ( avoids breaking rewrite rules )
@@ -98,7 +101,7 @@ class Polylang {
98
 
99
  foreach ( $dirs as $dir ) {
100
  if ( file_exists( $file = "$dir/$class.php" ) ) {
101
- require_once $file;
102
  return;
103
  }
104
  }
@@ -155,6 +158,17 @@ class Polylang {
155
  return 0 === strpos( $req_uri, rest_get_url_prefix() . '/' ) || ! empty( $rest_route );
156
  }
157
 
 
 
 
 
 
 
 
 
 
 
 
158
  /**
159
  * Defines constants
160
  * May be overridden by a plugin if set before plugins_loaded, 1
@@ -177,9 +191,9 @@ class Polylang {
177
  define( 'PLL_ADMIN', wp_doing_cron() || ( defined( 'WP_CLI' ) && WP_CLI ) || ( is_admin() && ! PLL_AJAX_ON_FRONT ) );
178
  }
179
 
180
- // Settings page whatever the tab
181
  if ( ! defined( 'PLL_SETTINGS' ) ) {
182
- define( 'PLL_SETTINGS', is_admin() && ( ( isset( $_GET['page'] ) && 0 === strpos( sanitize_key( $_GET['page'] ), 'mlang' ) ) || ! empty( $_REQUEST['pll_ajax_settings'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
183
  }
184
  }
185
 
@@ -219,7 +233,7 @@ class Polylang {
219
  *
220
  * @param string $class either PLL_Model or PLL_Admin_Model
221
  */
222
- $class = apply_filters( 'pll_model', PLL_SETTINGS ? 'PLL_Admin_Model' : 'PLL_Model' );
223
  $model = new $class( $options );
224
  $links_model = $model->get_links_model();
225
 
30
  require_once PLL_INC . '/functions.php'; // VIP functions
31
  spl_autoload_register( array( $this, 'autoload' ) ); // Autoload classes
32
 
33
+ // register an action when plugin is activating.
34
+ register_activation_hook( POLYLANG_BASENAME, array( 'PLL_Wizard', 'start_wizard' ) );
35
+
36
  $install = new PLL_Install( POLYLANG_BASENAME );
37
 
38
  // Stopping here if we are going to deactivate the plugin ( avoids breaking rewrite rules )
101
 
102
  foreach ( $dirs as $dir ) {
103
  if ( file_exists( $file = "$dir/$class.php" ) ) {
104
+ require_once $file; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
105
  return;
106
  }
107
  }
158
  return 0 === strpos( $req_uri, rest_get_url_prefix() . '/' ) || ! empty( $rest_route );
159
  }
160
 
161
+ /**
162
+ * Tells if we are in the wizard process.
163
+ *
164
+ * @since 2.7
165
+ *
166
+ * @return bool
167
+ */
168
+ public static function is_wizard() {
169
+ return isset( $_GET['page'] ) && ! empty( $_GET['page'] ) && 'mlang_wizard' === sanitize_key( $_GET['page'] ); // phpcs:ignore WordPress.Security.NonceVerification
170
+ }
171
+
172
  /**
173
  * Defines constants
174
  * May be overridden by a plugin if set before plugins_loaded, 1
191
  define( 'PLL_ADMIN', wp_doing_cron() || ( defined( 'WP_CLI' ) && WP_CLI ) || ( is_admin() && ! PLL_AJAX_ON_FRONT ) );
192
  }
193
 
194
+ // Settings page whatever the tab except for the wizard which needs to be an admin process.
195
  if ( ! defined( 'PLL_SETTINGS' ) ) {
196
+ define( 'PLL_SETTINGS', is_admin() && ( ( isset( $_GET['page'] ) && 0 === strpos( sanitize_key( $_GET['page'] ), 'mlang' ) && ! self::is_wizard() ) || ! empty( $_REQUEST['pll_ajax_settings'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
197
  }
198
  }
199
 
233
  *
234
  * @param string $class either PLL_Model or PLL_Admin_Model
235
  */
236
+ $class = apply_filters( 'pll_model', PLL_SETTINGS || self::is_wizard() ? 'PLL_Admin_Model' : 'PLL_Model' );
237
  $model = new $class( $options );
238
  $links_model = $model->get_links_model();
239
 
include/crud-posts.php CHANGED
@@ -227,7 +227,7 @@ class PLL_CRUD_Posts {
227
  if ( ! empty( $ids ) ) {
228
  // Regenerate intermediate sizes if it's an image ( since we could not prevent WP deleting them before ).
229
  require_once ABSPATH . 'wp-admin/includes/image.php'; // In case the file is deleted outside admin.
230
- wp_update_attachment_metadata( $ids[0], wp_generate_attachment_metadata( $ids[0], $file ) );
231
  return ''; // Prevent deleting the main file.
232
  }
233
 
@@ -239,9 +239,9 @@ class PLL_CRUD_Posts {
239
  *
240
  * @since 1.8
241
  *
242
- * @param int $post_id
243
- * @param string|object $lang
244
- * @return int id of the translated media
245
  */
246
  public function create_media_translation( $post_id, $lang ) {
247
  if ( empty( $post_id ) ) {
@@ -254,30 +254,34 @@ class PLL_CRUD_Posts {
254
  return $post;
255
  }
256
 
257
- $lang = $this->model->get_language( $lang ); // Make sure we get a valid language slug
258
 
259
- // Create a new attachment ( translate attachment parent if exists )
260
- add_filter( 'pll_enable_duplicate_media', '__return_false', 99 ); // Avoid a conflict with automatic duplicate at upload
261
  $post->ID = null; // Will force the creation
262
  $post->post_parent = ( $post->post_parent && $tr_parent = $this->model->post->get_translation( $post->post_parent, $lang->slug ) ) ? $tr_parent : 0;
263
- $post->tax_input = array( 'language' => array( $lang->slug ) ); // Assigns the language
264
  $tr_id = wp_insert_attachment( wp_slash( (array) $post ) );
265
- remove_filter( 'pll_enable_duplicate_media', '__return_false', 99 ); // Restore automatic duplicate at upload
266
 
267
- // Copy metadata, attached file and alternative text
268
- foreach ( array( '_wp_attachment_metadata', '_wp_attached_file', '_wp_attachment_image_alt' ) as $key ) {
269
- if ( $meta = get_post_meta( $post_id, $key, true ) ) {
270
- add_post_meta( $tr_id, $key, wp_slash( $meta ) );
271
- }
272
  }
273
 
274
- $this->model->post->set_language( $tr_id, $lang );
 
 
 
275
 
276
- $translations = $this->model->post->get_translations( $post_id );
277
- if ( ! $translations && $src_lang = $this->model->post->get_language( $post_id ) ) {
278
- $translations[ $src_lang->slug ] = $post_id;
279
  }
280
 
 
 
 
281
  $translations[ $lang->slug ] = $tr_id;
282
  $this->model->post->save_translations( $tr_id, $translations );
283
 
@@ -286,9 +290,9 @@ class PLL_CRUD_Posts {
286
  *
287
  * @since 1.6.4
288
  *
289
- * @param int $post_id post id of the source media
290
- * @param int $tr_id post id of the new media translation
291
- * @param string $slug language code of the new translation
292
  */
293
  do_action( 'pll_translate_media', $post_id, $tr_id, $lang->slug );
294
  return $tr_id;
227
  if ( ! empty( $ids ) ) {
228
  // Regenerate intermediate sizes if it's an image ( since we could not prevent WP deleting them before ).
229
  require_once ABSPATH . 'wp-admin/includes/image.php'; // In case the file is deleted outside admin.
230
+ wp_update_attachment_metadata( $ids[0], wp_slash( wp_generate_attachment_metadata( $ids[0], $file ) ) ); // Directly uses update_post_meta, so expects slashed.
231
  return ''; // Prevent deleting the main file.
232
  }
233
 
239
  *
240
  * @since 1.8
241
  *
242
+ * @param int $post_id Original attachment id.
243
+ * @param string|object $lang New translation language.
244
+ * @return int Attachment id of the translated media.
245
  */
246
  public function create_media_translation( $post_id, $lang ) {
247
  if ( empty( $post_id ) ) {
254
  return $post;
255
  }
256
 
257
+ $lang = $this->model->get_language( $lang ); // Make sure we get a valid language slug.
258
 
259
+ // Create a new attachment ( translate attachment parent if exists ).
260
+ add_filter( 'pll_enable_duplicate_media', '__return_false', 99 ); // Avoid a conflict with automatic duplicate at upload.
261
  $post->ID = null; // Will force the creation
262
  $post->post_parent = ( $post->post_parent && $tr_parent = $this->model->post->get_translation( $post->post_parent, $lang->slug ) ) ? $tr_parent : 0;
263
+ $post->tax_input = array( 'language' => array( $lang->slug ) ); // Assigns the language.
264
  $tr_id = wp_insert_attachment( wp_slash( (array) $post ) );
265
+ remove_filter( 'pll_enable_duplicate_media', '__return_false', 99 ); // Restore automatic duplicate at upload.
266
 
267
+ // Copy metadata.
268
+ if ( $data = wp_get_attachment_metadata( $post_id, true ) ) { // Unfiltered.
269
+ wp_update_attachment_metadata( $tr_id, wp_slash( $data ) ); // Directly uses update_post_meta, so expects slashed.
 
 
270
  }
271
 
272
+ // Copy attached file.
273
+ if ( $file = get_attached_file( $post_id, true ) ) { // Unfiltered.
274
+ update_attached_file( $tr_id, wp_slash( $file ) ); // Directly uses update_post_meta, so expects slashed.
275
+ }
276
 
277
+ // Copy alternative text. Direct use of the meta as there is no filtered wrapper to manipulate it.
278
+ if ( $text = get_post_meta( $post_id, '_wp_attachment_image_alt', true ) ) {
279
+ add_post_meta( $tr_id, '_wp_attachment_image_alt', wp_slash( $text ) );
280
  }
281
 
282
+ $this->model->post->set_language( $tr_id, $lang );
283
+
284
+ $translations = $this->model->post->get_translations( $post_id );
285
  $translations[ $lang->slug ] = $tr_id;
286
  $this->model->post->save_translations( $tr_id, $translations );
287
 
290
  *
291
  * @since 1.6.4
292
  *
293
+ * @param int $post_id Post id of the source media.
294
+ * @param int $tr_id Post id of the new media translation.
295
+ * @param string $slug Language code of the new translation.
296
  */
297
  do_action( 'pll_translate_media', $post_id, $tr_id, $lang->slug );
298
  return $tr_id;
include/filters.php CHANGED
@@ -335,9 +335,9 @@ class PLL_Filters {
335
  * @return array Personal data
336
  */
337
  public function user_data_exporter( $email_address ) {
338
- $email_address = trim( $email_address );
339
-
340
- $data_to_export = array();
341
 
342
  if ( $user = get_user_by( 'email', $email_address ) ) {
343
  foreach ( $this->model->get_languages_list() as $lang ) {
335
  * @return array Personal data
336
  */
337
  public function user_data_exporter( $email_address ) {
338
+ $email_address = trim( $email_address );
339
+ $data_to_export = array();
340
+ $user_data_to_export = array();
341
 
342
  if ( $user = get_user_by( 'email', $email_address ) ) {
343
  foreach ( $this->model->get_languages_list() as $lang ) {
include/functions.php CHANGED
@@ -18,54 +18,7 @@ if ( ! function_exists( 'wpcom_vip_get_page_by_title' ) ) {
18
  * @return WP_Post|array|null WP_Post (or array) on success, or null on failure.
19
  */
20
  function wpcom_vip_get_page_by_title( $page_title, $output = OBJECT, $post_type = 'page' ) {
21
- return get_page_by_title( $page_title, $output, $post_type );
22
- }
23
- }
24
-
25
- if ( ! function_exists( 'wpcom_vip_get_category_by_slug' ) ) {
26
- /**
27
- * Retrieve category object by category slug.
28
- *
29
- * @since 2.0
30
- *
31
- * @param string $slug The category slug.
32
- * @return object Category data object
33
- */
34
- function wpcom_vip_get_category_by_slug( $slug ) {
35
- return get_category_by_slug( $slug );
36
- }
37
- }
38
-
39
- if ( ! function_exists( 'wpcom_vip_get_term_by' ) ) {
40
- /**
41
- * Get all Term data from database by Term field and data.
42
- *
43
- * @since 2.0
44
- *
45
- * @param string $field Either 'slug', 'name', 'id' (term_id), or 'term_taxonomy_id'
46
- * @param string|int $value Search for this term value
47
- * @param string $taxonomy Taxonomy name. Optional, if `$field` is 'term_taxonomy_id'.
48
- * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N. Default OBJECT.
49
- * @param string $filter Optional, default is raw or no WordPress defined filter will applied.
50
- * @return WP_Term|array|false WP_Term instance (or array) on success. Will return false if `$taxonomy` does not exist or `$term` was not found.
51
- */
52
- function wpcom_vip_get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) {
53
- return get_term_by( $field, $value, $taxonomy, $output, $filter );
54
- }
55
- }
56
-
57
- if ( ! function_exists( 'wpcom_vip_get_term_link' ) ) {
58
- /**
59
- * Generate a permalink for a taxonomy term archive.
60
- *
61
- * @since 2.0
62
- *
63
- * @param object|int|string $term The term object, ID, or slug whose link will be retrieved.
64
- * @param string $taxonomy Optional. Taxonomy. Default empty.
65
- * @return string|WP_Error HTML link to taxonomy term archive on success, WP_Error if term does not exist.
66
- */
67
- function wpcom_vip_get_term_link( $term, $taxonomy = '' ) {
68
- return get_term_link( $term, $taxonomy );
69
  }
70
  }
71
 
18
  * @return WP_Post|array|null WP_Post (or array) on success, or null on failure.
19
  */
20
  function wpcom_vip_get_page_by_title( $page_title, $output = OBJECT, $post_type = 'page' ) {
21
+ return get_page_by_title( $page_title, $output, $post_type ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.get_page_by_title_get_page_by_title
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
23
  }
24
 
include/language.php CHANGED
@@ -102,7 +102,7 @@ class PLL_Language {
102
  * @return array Flag informations.
103
  */
104
  public static function get_flag_informations( $code ) {
105
- $flag['url'] = '';
106
 
107
  // Polylang builtin flags
108
  if ( ! empty( $code ) && file_exists( POLYLANG_DIR . ( $file = '/flags/' . $code . '.png' ) ) ) {
@@ -145,7 +145,7 @@ class PLL_Language {
145
  * @since 1.2
146
  */
147
  public function set_flag() {
148
- $flags['flag'] = self::get_flag_informations( $this->flag_code );
149
 
150
  // Custom flags ?
151
  $directories = array(
@@ -210,19 +210,32 @@ class PLL_Language {
210
  */
211
  $this->{$key} = apply_filters(
212
  'pll_get_flag',
213
- empty( $flag['src'] ) ? '' : sprintf(
214
- '<img src="%s" title="%s" alt="%s"%s%s />',
215
- $flag['src'],
216
- esc_attr( $title ),
217
- esc_attr( $this->name ),
218
- empty( $flag['width'] ) ? '' : sprintf( ' width="%s"', (int) $flag['width'] ),
219
- empty( $flag['height'] ) ? '' : sprintf( ' height="%s"', (int) $flag['height'] )
220
- ),
221
  $this->slug
222
  );
223
  }
224
  }
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  /**
227
  * Replace flag by custom flag
228
  * Takes care of url scheme
102
  * @return array Flag informations.
103
  */
104
  public static function get_flag_informations( $code ) {
105
+ $flag = array( 'url' => '' );
106
 
107
  // Polylang builtin flags
108
  if ( ! empty( $code ) && file_exists( POLYLANG_DIR . ( $file = '/flags/' . $code . '.png' ) ) ) {
145
  * @since 1.2
146
  */
147
  public function set_flag() {
148
+ $flags = array( 'flag' => self::get_flag_informations( $this->flag_code ) );
149
 
150
  // Custom flags ?
151
  $directories = array(
210
  */
211
  $this->{$key} = apply_filters(
212
  'pll_get_flag',
213
+ self::get_flag_html( $flag, $title, $this->name ),
 
 
 
 
 
 
 
214
  $this->slug
215
  );
216
  }
217
  }
218
 
219
+ /**
220
+ * Get HTML code for flag
221
+ *
222
+ * @since 2.7
223
+ *
224
+ * @param array $flag flag properties: src, width and height
225
+ * @param string $title optional title attribute
226
+ * @param string $alt optional alt attribute
227
+ */
228
+ public static function get_flag_html( $flag, $title = '', $alt = '' ) {
229
+ return empty( $flag['src'] ) ? '' : sprintf(
230
+ '<img src="%s"%s%s%s%s />',
231
+ $flag['src'],
232
+ empty( $title ) ? '' : sprintf( ' title="%s"', esc_attr( $title ) ),
233
+ empty( $alt ) ? '' : sprintf( ' alt="%s"', esc_attr( $alt ) ),
234
+ empty( $flag['width'] ) ? '' : sprintf( ' width="%s"', (int) $flag['width'] ),
235
+ empty( $flag['height'] ) ? '' : sprintf( ' height="%s"', (int) $flag['height'] )
236
+ );
237
+ }
238
+
239
  /**
240
  * Replace flag by custom flag
241
  * Takes care of url scheme
include/license.php CHANGED
@@ -153,7 +153,7 @@ class PLL_License {
153
  $response = wp_remote_post(
154
  $this->api_url,
155
  array(
156
- 'timeout' => 15,
157
  'sslverify' => false,
158
  'body' => $api_params,
159
  )
@@ -175,4 +175,122 @@ class PLL_License {
175
 
176
  update_option( 'polylang_licenses', $licenses ); // FIXME called multiple times when saving all licenses
177
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  }
153
  $response = wp_remote_post(
154
  $this->api_url,
155
  array(
156
+ 'timeout' => 3,
157
  'sslverify' => false,
158
  'body' => $api_params,
159
  )
175
 
176
  update_option( 'polylang_licenses', $licenses ); // FIXME called multiple times when saving all licenses
177
  }
178
+
179
+ /**
180
+ * Get the html form field in a table row (one per license key) for display
181
+ *
182
+ * @since 2.7
183
+ *
184
+ * @return string
185
+ */
186
+ public function get_form_field() {
187
+ if ( ! empty( $this->license_data ) ) {
188
+ $license = $this->license_data;
189
+ }
190
+
191
+ $class = 'license-null';
192
+
193
+ $out = sprintf(
194
+ '<td><label for="pll-licenses[%1$s]">%2$s</label></td>' .
195
+ '<td><input name="licenses[%1$s]" id="pll-licenses[%1$s]" type="text" value="%3$s" class="regular-text code" />',
196
+ esc_attr( $this->id ),
197
+ esc_attr( $this->name ),
198
+ esc_html( $this->license_key )
199
+ );
200
+
201
+ if ( ! empty( $license ) && is_object( $license ) ) {
202
+ $now = time();
203
+ $expiration = isset( $license->expires ) ? strtotime( $license->expires ) : false;
204
+
205
+ // Special case: the license expired after the last check
206
+ if ( $license->success && $expiration && $expiration < $now ) {
207
+ $license->success = false;
208
+ $license->error = 'expired';
209
+ }
210
+
211
+ if ( false === $license->success ) {
212
+ $class = 'notice-error notice-alt';
213
+
214
+ switch ( $license->error ) {
215
+ case 'expired':
216
+ $message = sprintf(
217
+ /* translators: %1$s is a date, %2$s is link start tag, %3$s is link end tag. */
218
+ esc_html__( 'Your license key expired on %1$s. Please %2$srenew your license key%3$s.', 'polylang' ),
219
+ esc_html( date_i18n( get_option( 'date_format' ), $expiration ) ),
220
+ sprintf( '<a href="%s" target="_blank">', esc_url( 'https://polylang.pro/checkout/?edd_license_key=' . $this->license_key ) ),
221
+ '</a>'
222
+ );
223
+ break;
224
+
225
+ case 'disabled':
226
+ case 'revoked':
227
+ $message = esc_html__( 'Your license key has been disabled.', 'polylang' );
228
+ break;
229
+
230
+ case 'missing':
231
+ $message = sprintf(
232
+ /* translators: %1$s is link start tag, %2$s is link end tag. */
233
+ esc_html__( 'Invalid license. Please %1$svisit your account page%2$s and verify it.', 'polylang' ),
234
+ sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account' ),
235
+ '</a>'
236
+ );
237
+ break;
238
+
239
+ case 'invalid':
240
+ case 'site_inactive':
241
+ $message = sprintf(
242
+ /* translators: %1$s is a product name, %2$s is link start tag, %3$s is link end tag. */
243
+ esc_html__( 'Your %1$s license key is not active for this URL. Please %2$svisit your account page%3$s to manage your license key URLs.', 'polylang' ),
244
+ esc_html( $this->name ),
245
+ sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account' ),
246
+ '</a>'
247
+ );
248
+ break;
249
+
250
+ case 'item_name_mismatch':
251
+ /* translators: %s is a product name */
252
+ $message = sprintf( esc_html__( 'This is not a %s license key.', 'polylang' ), esc_html( $this->name ) );
253
+ break;
254
+
255
+ case 'no_activations_left':
256
+ $message = sprintf(
257
+ /* translators: %1$s is link start tag, %2$s is link end tag */
258
+ esc_html__( 'Your license key has reached its activation limit. %1$sView possible upgrades%2$s now.', 'polylang' ),
259
+ sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account' ),
260
+ '</a>'
261
+ );
262
+ break;
263
+ }
264
+ } else {
265
+ $class = 'license-valid';
266
+
267
+ $out .= sprintf( '<button id="deactivate_%s" type="button" class="button button-secondary pll-deactivate-license">%s</button>', esc_attr( $this->id ), esc_html__( 'Deactivate', 'polylang' ) );
268
+
269
+ if ( 'lifetime' === $license->expires ) {
270
+ $message = esc_html__( 'The license key never expires.', 'polylang' );
271
+ } elseif ( $expiration > $now && $expiration - $now < ( DAY_IN_SECONDS * 30 ) ) {
272
+ $class = 'notice-warning notice-alt';
273
+ $message = sprintf(
274
+ /* translators: %1$s is a date, %2$s is link start tag, %3$s is link end tag. */
275
+ esc_html__( 'Your license key will expire soon! Precisely, it will expire on %1$s. %2$sRenew your license key today!%3$s.', 'polylang' ),
276
+ esc_html( date_i18n( get_option( 'date_format' ), $expiration ) ),
277
+ sprintf( '<a href="%s" target="_blank">', esc_url( 'https://polylang.pro/checkout/?edd_license_key=' . $this->license_key ) ),
278
+ '</a>'
279
+ );
280
+ } else {
281
+ $message = sprintf(
282
+ /* translators: %s is a date */
283
+ esc_html__( 'Your license key expires on %s.', 'polylang' ),
284
+ esc_html( date_i18n( get_option( 'date_format' ), $expiration ) )
285
+ );
286
+ }
287
+ }
288
+ }
289
+
290
+ if ( ! empty( $message ) ) {
291
+ $out .= '<p>' . $message . '</p>';
292
+ }
293
+
294
+ return sprintf( '<tr id="pll-license-%s" class="%s">%s</tr>', esc_attr( $this->id ), $class, $out );
295
+ }
296
  }
include/links-directory.php CHANGED
@@ -59,10 +59,10 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
59
  if ( ! empty( $lang ) ) {
60
  $base = $this->options['rewrite'] ? '' : 'language/';
61
  $slug = $this->options['default_lang'] == $lang->slug && $this->options['hide_default'] ? '' : $base . $lang->slug . '/';
62
- $root = ( false === strpos( $url, '://' ) ) ? $this->home_relative . $this->root : preg_replace( '/^https?:\/\//', '://', $this->home . '/' . $this->root );
63
 
64
  if ( false === strpos( $url, $new = $root . $slug ) ) {
65
- $pattern = str_replace( '/', '\/', $root );
66
  $pattern = '#' . $pattern . '#';
67
  return preg_replace( $pattern, $new, $url, 1 ); // Only once
68
  }
@@ -80,6 +80,8 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
80
  * @return string modified url
81
  */
82
  public function remove_language_from_link( $url ) {
 
 
83
  foreach ( $this->model->get_languages_list() as $language ) {
84
  if ( ! $this->options['hide_default'] || $this->options['default_lang'] != $language->slug ) {
85
  $languages[] = $language->slug;
@@ -87,10 +89,10 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
87
  }
88
 
89
  if ( ! empty( $languages ) ) {
90
- $root = ( false === strpos( $url, '://' ) ) ? $this->home_relative . $this->root : preg_replace( '/^https?:\/\//', '://', $this->home . '/' . $this->root );
91
 
92
- $pattern = str_replace( '/', '\/', $root );
93
- $pattern = '#' . $pattern . ( $this->options['rewrite'] ? '' : 'language\/' ) . '(' . implode( '|', $languages ) . ')(\/|$)#';
94
  $url = preg_replace( $pattern, $root, $url );
95
  }
96
  return $url;
@@ -115,8 +117,8 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
115
  $root = ( false === strpos( $url, '://' ) ) ? $this->home_relative . $this->root : $this->home . '/' . $this->root;
116
 
117
  $pattern = wp_parse_url( $root . ( $this->options['rewrite'] ? '' : 'language/' ), PHP_URL_PATH );
118
- $pattern = str_replace( '/', '\/', $pattern );
119
- $pattern = '#^' . $pattern . '(' . implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) . ')(\/|$)#';
120
  return preg_match( $pattern, trailingslashit( $path ), $matches ) ? $matches[1] : ''; // $matches[1] is the slug of the requested language
121
  }
122
 
59
  if ( ! empty( $lang ) ) {
60
  $base = $this->options['rewrite'] ? '' : 'language/';
61
  $slug = $this->options['default_lang'] == $lang->slug && $this->options['hide_default'] ? '' : $base . $lang->slug . '/';
62
+ $root = ( false === strpos( $url, '://' ) ) ? $this->home_relative . $this->root : preg_replace( '#^https?://#', '://', $this->home . '/' . $this->root );
63
 
64
  if ( false === strpos( $url, $new = $root . $slug ) ) {
65
+ $pattern = preg_quote( $root, '#' );
66
  $pattern = '#' . $pattern . '#';
67
  return preg_replace( $pattern, $new, $url, 1 ); // Only once
68
  }
80
  * @return string modified url
81
  */
82
  public function remove_language_from_link( $url ) {
83
+ $languages = array();
84
+
85
  foreach ( $this->model->get_languages_list() as $language ) {
86
  if ( ! $this->options['hide_default'] || $this->options['default_lang'] != $language->slug ) {
87
  $languages[] = $language->slug;
89
  }
90
 
91
  if ( ! empty( $languages ) ) {
92
+ $root = ( false === strpos( $url, '://' ) ) ? $this->home_relative . $this->root : preg_replace( '#^https?://#', '://', $this->home . '/' . $this->root );
93
 
94
+ $pattern = preg_quote( $root, '#' );
95
+ $pattern = '#' . $pattern . ( $this->options['rewrite'] ? '' : 'language/' ) . '(' . implode( '|', $languages ) . ')(/|$)#';
96
  $url = preg_replace( $pattern, $root, $url );
97
  }
98
  return $url;
117
  $root = ( false === strpos( $url, '://' ) ) ? $this->home_relative . $this->root : $this->home . '/' . $this->root;
118
 
119
  $pattern = wp_parse_url( $root . ( $this->options['rewrite'] ? '' : 'language/' ), PHP_URL_PATH );
120
+ $pattern = preg_quote( $pattern, '#' );
121
+ $pattern = '#^' . $pattern . '(' . implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) . ')(/|$)#';
122
  return preg_match( $pattern, trailingslashit( $path ), $matches ) ? $matches[1] : ''; // $matches[1] is the slug of the requested language
123
  }
124
 
include/links-domain.php CHANGED
@@ -38,7 +38,7 @@ class PLL_Links_Domain extends PLL_Links_Abstract_Domain {
38
  */
39
  public function add_language_to_link( $url, $lang ) {
40
  if ( ! empty( $lang ) && ! empty( $this->hosts[ $lang->slug ] ) ) {
41
- $url = preg_replace( '#:\/\/(' . wp_parse_url( $this->home, PHP_URL_HOST ) . ')($|\/.*)#', '://' . $this->hosts[ $lang->slug ] . '$2', $url );
42
  }
43
  return $url;
44
  }
@@ -54,7 +54,7 @@ class PLL_Links_Domain extends PLL_Links_Abstract_Domain {
54
  */
55
  public function remove_language_from_link( $url ) {
56
  if ( ! empty( $this->hosts ) ) {
57
- $url = preg_replace( '#:\/\/(' . implode( '|', $this->hosts ) . ')($|\/.*)#', '://' . wp_parse_url( $this->home, PHP_URL_HOST ) . '$2', $url );
58
  }
59
  return $url;
60
  }
@@ -83,13 +83,8 @@ class PLL_Links_Domain extends PLL_Links_Abstract_Domain {
83
  $hosts = array();
84
  foreach ( $this->options['domains'] as $lang => $domain ) {
85
  $host = wp_parse_url( $domain, PHP_URL_HOST );
86
- // idn_to_ascii is much faster than the WordPress method.
87
- if ( function_exists( 'idn_to_ascii' ) ) {
88
- // The use of the constant is mandatory in PHP 7.2 and PHP 7.3 to avoid a deprecated notice.
89
- $hosts[ $lang ] = defined( 'INTL_IDNA_VARIANT_UTS46' ) ? idn_to_ascii( $host, 0, INTL_IDNA_VARIANT_UTS46 ) : idn_to_ascii( $host );
90
- } else {
91
- $hosts[ $lang ] = Requests_IDNAEncoder::encode( $host );
92
- }
93
  }
94
 
95
  return $hosts;
38
  */
39
  public function add_language_to_link( $url, $lang ) {
40
  if ( ! empty( $lang ) && ! empty( $this->hosts[ $lang->slug ] ) ) {
41
+ $url = preg_replace( '#://(' . wp_parse_url( $this->home, PHP_URL_HOST ) . ')($|/.*)#', '://' . $this->hosts[ $lang->slug ] . '$2', $url );
42
  }
43
  return $url;
44
  }
54
  */
55
  public function remove_language_from_link( $url ) {
56
  if ( ! empty( $this->hosts ) ) {
57
+ $url = preg_replace( '#://(' . implode( '|', $this->hosts ) . ')($|/.*)#', '://' . wp_parse_url( $this->home, PHP_URL_HOST ) . '$2', $url );
58
  }
59
  return $url;
60
  }
83
  $hosts = array();
84
  foreach ( $this->options['domains'] as $lang => $domain ) {
85
  $host = wp_parse_url( $domain, PHP_URL_HOST );
86
+ // The use of the constant is mandatory in PHP 7.2 and PHP 7.3 to avoid a deprecated notice.
87
+ $hosts[ $lang ] = defined( 'INTL_IDNA_VARIANT_UTS46' ) ? idn_to_ascii( $host, 0, INTL_IDNA_VARIANT_UTS46 ) : idn_to_ascii( $host );
 
 
 
 
 
88
  }
89
 
90
  return $hosts;
include/links-permalinks.php CHANGED
@@ -44,7 +44,7 @@ abstract class PLL_Links_Permalinks extends PLL_Links_Model {
44
  * @param string $modified_url The link to the first page
45
  * @param string $original_url The link to the original paged page
46
  */
47
- return apply_filters( 'pll_remove_paged_from_link', preg_replace( '#\/page\/[0-9]+\/?#', $this->use_trailing_slashes ? '/' : '', $url ), $url );
48
  }
49
 
50
  /**
44
  * @param string $modified_url The link to the first page
45
  * @param string $original_url The link to the original paged page
46
  */
47
+ return apply_filters( 'pll_remove_paged_from_link', preg_replace( '#/page/[0-9]+/?#', $this->use_trailing_slashes ? '/' : '', $url ), $url );
48
  }
49
 
50
  /**
include/links-subdomain.php CHANGED
@@ -49,6 +49,8 @@ class PLL_Links_Subdomain extends PLL_Links_Abstract_Domain {
49
  * @return string modified url
50
  */
51
  public function remove_language_from_link( $url ) {
 
 
52
  foreach ( $this->model->get_languages_list() as $language ) {
53
  if ( ! $this->options['hide_default'] || $this->options['default_lang'] != $language->slug ) {
54
  $languages[] = $language->slug;
@@ -56,7 +58,7 @@ class PLL_Links_Subdomain extends PLL_Links_Abstract_Domain {
56
  }
57
 
58
  if ( ! empty( $languages ) ) {
59
- $url = preg_replace( '#:\/\/(' . implode( '|', $languages ) . ')\.#', $this->www, $url );
60
  }
61
 
62
  return $url;
49
  * @return string modified url
50
  */
51
  public function remove_language_from_link( $url ) {
52
+ $languages = array();
53
+
54
  foreach ( $this->model->get_languages_list() as $language ) {
55
  if ( ! $this->options['hide_default'] || $this->options['default_lang'] != $language->slug ) {
56
  $languages[] = $language->slug;
58
  }
59
 
60
  if ( ! empty( $languages ) ) {
61
+ $url = preg_replace( '#://(' . implode( '|', $languages ) . ')\.#', $this->www, $url );
62
  }
63
 
64
  return $url;
include/model.php CHANGED
@@ -182,6 +182,7 @@ class PLL_Model {
182
  $this->cache->set( 'language:' . $lang->tl_term_id, $lang );
183
  $this->cache->set( 'language:' . $lang->slug, $lang );
184
  $this->cache->set( 'language:' . $lang->locale, $lang );
 
185
  }
186
  $return = $this->cache->get( 'language:' . $value );
187
  }
@@ -427,6 +428,7 @@ class PLL_Model {
427
  global $wpdb;
428
 
429
  $term_name = trim( wp_unslash( $term_name ) );
 
430
 
431
  $select = "SELECT t.term_id FROM $wpdb->terms AS t";
432
  $join = " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
@@ -448,13 +450,13 @@ class PLL_Model {
448
  * @since 1.2
449
  *
450
  * @param object $lang PLL_Language instance.
451
- * @param array $q WP_Query arguments ( accepted: post_type, m, year, monthnum, day, author, author_name, post_format ).
452
  * @return int
453
  */
454
  public function count_posts( $lang, $q = array() ) {
455
  global $wpdb;
456
 
457
- $q = wp_parse_args( $q, array( 'post_type' => 'post' ) );
458
 
459
  if ( ! is_array( $q['post_type'] ) ) {
460
  $q['post_type'] = array( $q['post_type'] );
@@ -476,7 +478,7 @@ class PLL_Model {
476
  if ( false === $counts ) {
477
  $select = "SELECT pll_tr.term_taxonomy_id, COUNT( * ) AS num_posts FROM {$wpdb->posts}";
478
  $join = $this->post->join_clause();
479
- $where = " WHERE post_status = 'publish'";
480
  $where .= sprintf( " AND {$wpdb->posts}.post_type IN ( '%s' )", join( "', '", esc_sql( $q['post_type'] ) ) );
481
  $where .= $this->post->where_clause( $this->get_languages_list() );
482
  $groupby = ' GROUP BY pll_tr.term_taxonomy_id';
@@ -610,7 +612,7 @@ class PLL_Model {
610
 
611
  if ( ! empty( $o ) && is_object( $this->$o ) && method_exists( $this->$o, $f ) ) {
612
  if ( WP_DEBUG ) {
613
- $debug = debug_backtrace(); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
614
  $i = 1 + empty( $debug[1]['line'] ); // the file and line are in $debug[2] if the function was called using call_user_func
615
 
616
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
@@ -627,7 +629,7 @@ class PLL_Model {
627
  return call_user_func_array( array( $this->$o, $f ), $args );
628
  }
629
 
630
- $debug = debug_backtrace(); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
631
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
632
  sprintf(
633
  'Call to undefined function PLL()->model->%1$s() in %2$s on line %3$s' . "\nError handler",
182
  $this->cache->set( 'language:' . $lang->tl_term_id, $lang );
183
  $this->cache->set( 'language:' . $lang->slug, $lang );
184
  $this->cache->set( 'language:' . $lang->locale, $lang );
185
+ $this->cache->set( 'language:' . $lang->w3c, $lang );
186
  }
187
  $return = $this->cache->get( 'language:' . $value );
188
  }
428
  global $wpdb;
429
 
430
  $term_name = trim( wp_unslash( $term_name ) );
431
+ $term_name = _wp_specialchars( $term_name );
432
 
433
  $select = "SELECT t.term_id FROM $wpdb->terms AS t";
434
  $join = " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
450
  * @since 1.2
451
  *
452
  * @param object $lang PLL_Language instance.
453
+ * @param array $q WP_Query arguments ( accepted: post_type, m, year, monthnum, day, author, author_name, post_format, post_status ).
454
  * @return int
455
  */
456
  public function count_posts( $lang, $q = array() ) {
457
  global $wpdb;
458
 
459
+ $q = wp_parse_args( $q, array( 'post_type' => 'post', 'post_status' => 'publish' ) );
460
 
461
  if ( ! is_array( $q['post_type'] ) ) {
462
  $q['post_type'] = array( $q['post_type'] );
478
  if ( false === $counts ) {
479
  $select = "SELECT pll_tr.term_taxonomy_id, COUNT( * ) AS num_posts FROM {$wpdb->posts}";
480
  $join = $this->post->join_clause();
481
+ $where = sprintf( " WHERE post_status = '%s'", esc_sql( $q['post_status'] ) );
482
  $where .= sprintf( " AND {$wpdb->posts}.post_type IN ( '%s' )", join( "', '", esc_sql( $q['post_type'] ) ) );
483
  $where .= $this->post->where_clause( $this->get_languages_list() );
484
  $groupby = ' GROUP BY pll_tr.term_taxonomy_id';
612
 
613
  if ( ! empty( $o ) && is_object( $this->$o ) && method_exists( $this->$o, $f ) ) {
614
  if ( WP_DEBUG ) {
615
+ $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
616
  $i = 1 + empty( $debug[1]['line'] ); // the file and line are in $debug[2] if the function was called using call_user_func
617
 
618
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
629
  return call_user_func_array( array( $this->$o, $f ), $args );
630
  }
631
 
632
+ $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
633
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
634
  sprintf(
635
  'Call to undefined function PLL()->model->%1$s() in %2$s on line %3$s' . "\nError handler",
include/nav-menu.php CHANGED
@@ -57,6 +57,8 @@ class PLL_Nav_Menu {
57
  static $once;
58
  global $_wp_registered_nav_menus;
59
 
 
 
60
  if ( isset( $_wp_registered_nav_menus ) && ! $once ) {
61
  foreach ( $_wp_registered_nav_menus as $loc => $name ) {
62
  foreach ( $this->model->get_languages_list() as $lang ) {
57
  static $once;
58
  global $_wp_registered_nav_menus;
59
 
60
+ $arr = array();
61
+
62
  if ( isset( $_wp_registered_nav_menus ) && ! $once ) {
63
  foreach ( $_wp_registered_nav_menus as $loc => $name ) {
64
  foreach ( $this->model->get_languages_list() as $lang ) {
include/rest-request.php CHANGED
@@ -51,9 +51,7 @@ class PLL_REST_Request extends PLL_Base {
51
 
52
  // Share term slugs
53
  if ( get_option( 'permalink_structure' ) && $this->options['force_lang'] && class_exists( 'PLL_Share_Term_Slug' ) ) {
54
- $this->share_term_slug = version_compare( $GLOBALS['wp_version'], '4.8', '<' ) ?
55
- new PLL_Frontend_Share_Term_Slug( $this ) :
56
- new PLL_Share_Term_Slug( $this );
57
  }
58
 
59
  // Translate slugs, only for pretty permalinks
@@ -63,6 +61,10 @@ class PLL_REST_Request extends PLL_Base {
63
  $this->translate_slugs = new PLL_Translate_Slugs( $slugs_model, $curlang );
64
  }
65
 
 
 
 
 
66
  if ( class_exists( 'PLL_Sync_Post_REST' ) ) {
67
  $this->sync_post = new PLL_Sync_Post_REST( $this );
68
  }
51
 
52
  // Share term slugs
53
  if ( get_option( 'permalink_structure' ) && $this->options['force_lang'] && class_exists( 'PLL_Share_Term_Slug' ) ) {
54
+ $this->share_term_slug = new PLL_Share_Term_Slug( $this );
 
 
55
  }
56
 
57
  // Translate slugs, only for pretty permalinks
61
  $this->translate_slugs = new PLL_Translate_Slugs( $slugs_model, $curlang );
62
  }
63
 
64
+ if ( class_exists( 'PLL_Sync_Post_Model' ) ) {
65
+ $this->sync_post_model = new PLL_Sync_Post_Model( $this );
66
+ }
67
+
68
  if ( class_exists( 'PLL_Sync_Post_REST' ) ) {
69
  $this->sync_post = new PLL_Sync_Post_REST( $this );
70
  }
include/switcher.php CHANGED
@@ -42,8 +42,8 @@ class PLL_Switcher {
42
  * @return array
43
  */
44
  protected function get_elements( $links, $args ) {
45
-
46
  $first = true;
 
47
 
48
  foreach ( $links->model->get_languages_list( array( 'hide_empty' => $args['hide_if_empty'] ) ) as $language ) {
49
  $id = (int) $language->term_id;
@@ -100,7 +100,7 @@ class PLL_Switcher {
100
  $out[ $slug ] = compact( 'id', 'order', 'slug', 'locale', 'name', 'url', 'flag', 'current_lang', 'no_translation', 'classes' );
101
  }
102
 
103
- return empty( $out ) ? array() : $out;
104
  }
105
 
106
  /**
@@ -187,6 +187,8 @@ class PLL_Switcher {
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
  $url = $links->get_translation_url( $language );
192
  $urls[ $language->slug ] = $args['force_home'] || empty( $url ) ? $links->get_home_url( $language ) : $url;
42
  * @return array
43
  */
44
  protected function get_elements( $links, $args ) {
 
45
  $first = true;
46
+ $out = array();
47
 
48
  foreach ( $links->model->get_languages_list( array( 'hide_empty' => $args['hide_if_empty'] ) ) as $language ) {
49
  $id = (int) $language->term_id;
100
  $out[ $slug ] = compact( 'id', 'order', 'slug', 'locale', 'name', 'url', 'flag', 'current_lang', 'no_translation', 'classes' );
101
  }
102
 
103
+ return $out;
104
  }
105
 
106
  /**
187
 
188
  // Javascript to switch the language when using a dropdown list
189
  if ( $args['dropdown'] ) {
190
+ $urls = array();
191
+
192
  foreach ( $links->model->get_languages_list() as $language ) {
193
  $url = $links->get_translation_url( $language );
194
  $urls[ $language->slug ] = $args['force_home'] || empty( $url ) ? $links->get_home_url( $language ) : $url;
include/translated-object.php CHANGED
@@ -50,6 +50,7 @@ abstract class PLL_Translated_Object {
50
  $taxonomies = array( $this->tax_language, $this->tax_translations );
51
 
52
  // query terms
 
53
  foreach ( wp_get_object_terms( $object_id, $taxonomies, array( 'update_term_meta_cache' => false ) ) as $t ) {
54
  $terms[ $t->taxonomy ] = $t;
55
  if ( $t->taxonomy == $taxonomy ) {
@@ -239,7 +240,6 @@ abstract class PLL_Translated_Object {
239
  * @return string where clause
240
  */
241
  public function where_clause( $lang ) {
242
- global $wpdb;
243
  $tt_id = $this->tax_tt;
244
 
245
  // $lang is an object
@@ -250,7 +250,8 @@ abstract class PLL_Translated_Object {
250
 
251
  // $lang is a comma separated list of slugs ( or an array of slugs )
252
  // generally the case is the query is coming from outside with 'lang' parameter
253
- $slugs = is_array( $lang ) ? $lang : explode( ',', $lang );
 
254
  foreach ( $slugs as $slug ) {
255
  $languages[] = absint( $this->model->get_language( $slug )->$tt_id );
256
  }
50
  $taxonomies = array( $this->tax_language, $this->tax_translations );
51
 
52
  // query terms
53
+ $terms = array();
54
  foreach ( wp_get_object_terms( $object_id, $taxonomies, array( 'update_term_meta_cache' => false ) ) as $t ) {
55
  $terms[ $t->taxonomy ] = $t;
56
  if ( $t->taxonomy == $taxonomy ) {
240
  * @return string where clause
241
  */
242
  public function where_clause( $lang ) {
 
243
  $tt_id = $this->tax_tt;
244
 
245
  // $lang is an object
250
 
251
  // $lang is a comma separated list of slugs ( or an array of slugs )
252
  // generally the case is the query is coming from outside with 'lang' parameter
253
+ $slugs = is_array( $lang ) ? $lang : explode( ',', $lang );
254
+ $languages = array();
255
  foreach ( $slugs as $slug ) {
256
  $languages[] = absint( $this->model->get_language( $slug )->$tt_id );
257
  }
include/translated-term.php CHANGED
@@ -85,7 +85,7 @@ class PLL_Translated_Term extends PLL_Translated_Object {
85
 
86
  // get_term_by still not cached in WP 3.5.1 but internally, the function is always called by term_id
87
  elseif ( is_string( $value ) && $taxonomy ) {
88
- $term_id = wpcom_vip_get_term_by( 'slug', $value, $taxonomy )->term_id;
89
  }
90
 
91
  // Get the language and make sure it is a PLL_Language object
@@ -128,7 +128,7 @@ class PLL_Translated_Term extends PLL_Translated_Object {
128
 
129
  if ( ! doing_action( 'pre_delete_term' ) && $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( * ) FROM $wpdb->terms WHERE term_id = %d;", $id ) ) ) {
130
  // Always keep a group for terms to allow relationships remap when importing from a WXR file
131
- $translations[ $slug ] = $id;
132
  wp_insert_term( $group = uniqid( 'pll_' ), 'term_translations', array( 'description' => maybe_serialize( $translations ) ) );
133
  wp_set_object_terms( $id, $group, 'term_translations' );
134
  }
@@ -158,6 +158,8 @@ class PLL_Translated_Term extends PLL_Translated_Object {
158
  * @return array unmodified $terms
159
  */
160
  public function _prime_terms_cache( $terms, $taxonomies ) {
 
 
161
  if ( is_array( $terms ) && $this->model->is_translated_taxonomy( $taxonomies ) ) {
162
  foreach ( $terms as $term ) {
163
  $term_ids[] = is_object( $term ) ? $term->term_id : (int) $term;
85
 
86
  // get_term_by still not cached in WP 3.5.1 but internally, the function is always called by term_id
87
  elseif ( is_string( $value ) && $taxonomy ) {
88
+ $term_id = get_term_by( 'slug', $value, $taxonomy )->term_id;
89
  }
90
 
91
  // Get the language and make sure it is a PLL_Language object
128
 
129
  if ( ! doing_action( 'pre_delete_term' ) && $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( * ) FROM $wpdb->terms WHERE term_id = %d;", $id ) ) ) {
130
  // Always keep a group for terms to allow relationships remap when importing from a WXR file
131
+ $translations = array( $slug => $id );
132
  wp_insert_term( $group = uniqid( 'pll_' ), 'term_translations', array( 'description' => maybe_serialize( $translations ) ) );
133
  wp_set_object_terms( $id, $group, 'term_translations' );
134
  }
158
  * @return array unmodified $terms
159
  */
160
  public function _prime_terms_cache( $terms, $taxonomies ) {
161
+ $term_ids = array();
162
+
163
  if ( is_array( $terms ) && $this->model->is_translated_taxonomy( $taxonomies ) ) {
164
  foreach ( $terms as $term ) {
165
  $term_ids[] = is_object( $term ) ? $term->term_id : (int) $term;
include/walker-dropdown.php CHANGED
@@ -101,8 +101,8 @@ class PLL_Walker_Dropdown extends Walker {
101
 
102
  $output .= sprintf(
103
  '<select name="%1$s"%2$s%3$s%4$s>' . "\n" . '%5$s' . "\n" . '</select>' . "\n",
104
- $name = esc_attr( $args['name'] ),
105
- isset( $args['id'] ) && ! $args['id'] ? '' : ' id="' . ( empty( $args['id'] ) ? $name : esc_attr( $args['id'] ) ) . '"',
106
  empty( $args['class'] ) ? '' : ' class="' . esc_attr( $args['class'] ) . '"',
107
  disabled( empty( $args['disabled'] ), false, false ),
108
  parent::walk( $elements, $max_depth, $args )
101
 
102
  $output .= sprintf(
103
  '<select name="%1$s"%2$s%3$s%4$s>' . "\n" . '%5$s' . "\n" . '</select>' . "\n",
104
+ esc_attr( $args['name'] ),
105
+ isset( $args['id'] ) && ! $args['id'] ? '' : ' id="' . ( empty( $args['id'] ) ? esc_attr( $args['name'] ) : esc_attr( $args['id'] ) ) . '"',
106
  empty( $args['class'] ) ? '' : ' class="' . esc_attr( $args['class'] ) . '"',
107
  disabled( empty( $args['disabled'] ), false, false ),
108
  parent::walk( $elements, $max_depth, $args )
include/widget-languages.php CHANGED
@@ -64,7 +64,7 @@ class PLL_Widget_Languages extends WP_Widget {
64
  * @return array Settings to save or bool false to cancel saving
65
  */
66
  public function update( $new_instance, $old_instance ) {
67
- $instance['title'] = sanitize_text_field( $new_instance['title'] );
68
  foreach ( array_keys( PLL_Switcher::get_switcher_options( 'widget' ) ) as $key ) {
69
  $instance[ $key ] = ! empty( $new_instance[ $key ] ) ? 1 : 0;
70
  }
64
  * @return array Settings to save or bool false to cancel saving
65
  */
66
  public function update( $new_instance, $old_instance ) {
67
+ $instance = array( 'title' => sanitize_text_field( $new_instance['title'] ) );
68
  foreach ( array_keys( PLL_Switcher::get_switcher_options( 'widget' ) ) as $key ) {
69
  $instance[ $key ] = ! empty( $new_instance[ $key ] ) ? 1 : 0;
70
  }
install/install.php CHANGED
@@ -40,7 +40,7 @@ class PLL_Install extends PLL_Install_Base {
40
  '<div class="error"><p>%s</p></div>',
41
  sprintf(
42
  /* translators: 1: Plugin name 2: Current PHP version 3: Required PHP version */
43
- esc_html__( '%1$s has deactivated itself because you are using an old PHP version. You are using using PHP %2$s. %1$s requires PHP %3$s.', 'polylang' ),
44
  esc_html( POLYLANG ),
45
  PHP_VERSION,
46
  esc_html( PLL_MIN_PHP_VERSION )
@@ -62,7 +62,7 @@ class PLL_Install extends PLL_Install_Base {
62
  '<div class="error"><p>%s</p></div>',
63
  sprintf(
64
  /* translators: 1: Plugin name 2: Current WordPress version 3: Required WordPress version */
65
- esc_html__( '%1$s has deactivated itself because you are using an old WordPress version. You are using using WordPress %2$s. %1$s requires at least WordPress %3$s.', 'polylang' ),
66
  esc_html( POLYLANG ),
67
  esc_html( $wp_version ),
68
  esc_html( PLL_MIN_WP_VERSION )
40
  '<div class="error"><p>%s</p></div>',
41
  sprintf(
42
  /* translators: 1: Plugin name 2: Current PHP version 3: Required PHP version */
43
+ esc_html__( '%1$s has deactivated itself because you are using an old version of PHP. You are using using PHP %2$s. %1$s requires PHP %3$s.', 'polylang' ),
44
  esc_html( POLYLANG ),
45
  PHP_VERSION,
46
  esc_html( PLL_MIN_PHP_VERSION )
62
  '<div class="error"><p>%s</p></div>',
63
  sprintf(
64
  /* translators: 1: Plugin name 2: Current WordPress version 3: Required WordPress version */
65
+ esc_html__( '%1$s has deactivated itself because you are using an old version of WordPress. You are using using WordPress %2$s. %1$s requires at least WordPress %3$s.', 'polylang' ),
66
  esc_html( POLYLANG ),
67
  esc_html( $wp_version ),
68
  esc_html( PLL_MIN_WP_VERSION )
install/upgrade.php CHANGED
@@ -89,7 +89,7 @@ class PLL_Upgrade {
89
  * @since 1.2
90
  */
91
  public function _upgrade() {
92
- foreach ( array( '0.9', '1.0', '1.1', '1.2', '1.2.1', '1.2.3', '1.3', '1.4', '1.4.1', '1.4.4', '1.5', '1.6', '1.7.4', '1.8', '2.0.8', '2.1', '2.3' ) as $version ) {
93
  if ( version_compare( $this->options['version'], $version, '<' ) ) {
94
  call_user_func( array( $this, 'upgrade_' . str_replace( '.', '_', $version ) ) );
95
  }
@@ -143,7 +143,7 @@ class PLL_Upgrade {
143
  // Update strings register with icl_register_string
144
  $strings = get_option( 'polylang_wpml_strings' );
145
  if ( $strings ) {
146
- foreach ( $strings as $key => $string ) {
147
  $strings[ $key ]['icl'] = 1;
148
  }
149
  update_option( 'polylang_wpml_strings', $strings );
@@ -179,7 +179,8 @@ class PLL_Upgrade {
179
  // Upgrade old model based on metas to new model based on taxonomies
180
  global $wpdb;
181
  $wpdb->termmeta = $wpdb->prefix . 'termmeta'; // Registers the termmeta table in wpdb
182
- $languages = get_terms( 'language', array( 'hide_empty' => 0 ) ); // Don't use get_languages_list which can't work with the old model
 
183
 
184
  foreach ( $languages as $lang ) {
185
  // First update language with new storage for locale and text direction
@@ -208,8 +209,12 @@ class PLL_Upgrade {
208
 
209
  // Translations
210
  foreach ( array( 'post', 'term' ) as $type ) {
211
- $table = $type . 'meta';
212
- $terms = $slugs = $tts = $trs = array();
 
 
 
 
213
 
214
  // Get all translated objects
215
  // PHPCS:ignore WordPress.DB.PreparedSQL
@@ -307,6 +312,8 @@ class PLL_Upgrade {
307
  // Multilingal locations and switcher item were stored in a dedicated option
308
  if ( version_compare( $this->options['version'], '1.1', '<' ) ) {
309
  if ( $menu_lang = get_option( 'polylang_nav_menus' ) ) {
 
 
310
  foreach ( $menu_lang as $location => $arr ) {
311
  if ( ! in_array( $location, array_keys( get_registered_nav_menus() ) ) ) {
312
  continue;
@@ -355,7 +362,7 @@ class PLL_Upgrade {
355
  // Clean the WP option as it was a bad idea to pollute it
356
  if ( version_compare( $this->options['version'], '1.2', '<' ) ) {
357
  foreach ( $menus as $loc => $menu ) {
358
- if ( $pos = strpos( $loc, '#' ) ) {
359
  unset( $menus[ $loc ] );
360
  }
361
  }
@@ -518,6 +525,8 @@ class PLL_Upgrade {
518
  return;
519
  }
520
 
 
 
521
  foreach ( $translations as $translation ) {
522
  if ( in_array( $translation['language'], $languages ) ) {
523
  $translation['type'] = 'core';
@@ -608,4 +617,29 @@ class PLL_Upgrade {
608
  protected function upgrade_2_3() {
609
  delete_transient( 'pll_languages_list' );
610
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
611
  }
89
  * @since 1.2
90
  */
91
  public function _upgrade() {
92
+ foreach ( array( '0.9', '1.0', '1.1', '1.2', '1.2.1', '1.2.3', '1.3', '1.4', '1.4.1', '1.4.4', '1.5', '1.6', '1.7.4', '1.8', '2.0.8', '2.1', '2.3', '2.7' ) as $version ) {
93
  if ( version_compare( $this->options['version'], $version, '<' ) ) {
94
  call_user_func( array( $this, 'upgrade_' . str_replace( '.', '_', $version ) ) );
95
  }
143
  // Update strings register with icl_register_string
144
  $strings = get_option( 'polylang_wpml_strings' );
145
  if ( $strings ) {
146
+ foreach ( array_keys( $strings ) as $key ) {
147
  $strings[ $key ]['icl'] = 1;
148
  }
149
  update_option( 'polylang_wpml_strings', $strings );
179
  // Upgrade old model based on metas to new model based on taxonomies
180
  global $wpdb;
181
  $wpdb->termmeta = $wpdb->prefix . 'termmeta'; // Registers the termmeta table in wpdb
182
+ $languages = get_terms( 'language', array( 'hide_empty' => 0 ) ); // Don't use get_languages_list which can't work with the old model
183
+ $lang_tt_ids = array();
184
 
185
  foreach ( $languages as $lang ) {
186
  // First update language with new storage for locale and text direction
209
 
210
  // Translations
211
  foreach ( array( 'post', 'term' ) as $type ) {
212
+ $table = $type . 'meta';
213
+ $terms = array();
214
+ $slugs = array();
215
+ $tts = array();
216
+ $trs = array();
217
+ $description = array();
218
 
219
  // Get all translated objects
220
  // PHPCS:ignore WordPress.DB.PreparedSQL
312
  // Multilingal locations and switcher item were stored in a dedicated option
313
  if ( version_compare( $this->options['version'], '1.1', '<' ) ) {
314
  if ( $menu_lang = get_option( 'polylang_nav_menus' ) ) {
315
+ $locations = array();
316
+
317
  foreach ( $menu_lang as $location => $arr ) {
318
  if ( ! in_array( $location, array_keys( get_registered_nav_menus() ) ) ) {
319
  continue;
362
  // Clean the WP option as it was a bad idea to pollute it
363
  if ( version_compare( $this->options['version'], '1.2', '<' ) ) {
364
  foreach ( $menus as $loc => $menu ) {
365
+ if ( strpos( $loc, '#' ) ) {
366
  unset( $menus[ $loc ] );
367
  }
368
  }
525
  return;
526
  }
527
 
528
+ $translations_to_load = array();
529
+
530
  foreach ( $translations as $translation ) {
531
  if ( in_array( $translation['language'], $languages ) ) {
532
  $translation['type'] = 'core';
617
  protected function upgrade_2_3() {
618
  delete_transient( 'pll_languages_list' );
619
  }
620
+
621
+ /**
622
+ * Upgrades if the previous version is < 2.7
623
+ * Replace numeric keys by hashes in WPML registered strings
624
+ * Dismiss the wizard notice for existing sites
625
+ *
626
+ * @since 2.7
627
+ */
628
+ protected function upgrade_2_7() {
629
+ $strings = get_option( 'polylang_wpml_strings' );
630
+ if ( is_array( $strings ) ) {
631
+ $new_strings = array();
632
+
633
+ foreach ( $strings as $string ) {
634
+ $context = $string['context'];
635
+ $name = $string['name'];
636
+
637
+ $key = md5( "$context | $name" );
638
+ $new_strings[ $key ] = $string;
639
+ }
640
+ update_option( 'polylang_wpml_strings', $new_strings );
641
+ }
642
+
643
+ PLL_Admin_Notices::dismiss( 'wizard' );
644
+ }
645
  }
js/admin.js CHANGED
@@ -1,195 +1,267 @@
1
- jQuery( document ).ready(function( $ ) {
2
- var transitionTimeout;
3
-
4
- // languages list table
5
- // accessibility to row actions on focus
6
- // mainly copy paste of WP code from common.js
7
- $( 'table.languages' ).on({ // restricted to languages list table
8
- focusin: function() {
9
- clearTimeout( transitionTimeout );
10
- focusedRowActions = $( this ).find( '.row-actions' );
11
- // transitionTimeout is necessary for Firefox, but Chrome won't remove the CSS class without a little help.
12
- $( '.row-actions' ).not( this ).removeClass( 'visible' );
13
- focusedRowActions.addClass( 'visible' );
14
- },
15
- focusout: function() {
16
- // Tabbing between post title and .row-actions links needs a brief pause, otherwise
17
- // the .row-actions div gets hidden in transit in some browsers ( ahem, Firefox ).
18
- transitionTimeout = setTimeout(function() {
19
- focusedRowActions.removeClass( 'visible' );
20
- }, 30 );
21
- }
22
- }, 'tr' ); // acts on the whole tr instead of single td as we have actions links in several columns
23
-
24
- // extends selectmenu to add flags in menu items
25
- $.widget( "custom.iconselectmenu", $.ui.selectmenu, {
26
- _renderItem: function( ul, item ) {
27
- var li = $( "<li>", { text: item.label } );
28
- var el = $( item.element );
29
- var url = el.data( "url" );
30
-
31
- if ( url ) {
32
- var w = el.data( "width" );
33
- var h = el.data( "height" );
34
- $( "<img class='ui-icon' />" ).prop( "src", url ).prop( "width", w ).prop( "height", h ).appendTo( li );
35
- }
36
-
37
- return li.appendTo( ul );
38
- }
39
- });
40
-
41
- // allows to display the flag for the selected menu item
42
- function add_icon( event, ui ) {
43
- var sel = $( this ).find( ":selected" );
44
- var url = sel.data( "url" );
45
-
46
- if ( url ) {
47
- var txt = $( this ).iconselectmenu( "widget" ).children( ":last" );
48
- var w = sel.data( "width" );
49
- var h = sel.data( "height" );
50
- $( "<img class='ui-icon' />" ).prop( "src", url ).prop( "width", w ).prop( "height", h ).appendTo( txt );
51
- }
52
- }
53
 
54
- // overrides the flag dropdown list with our customized jquery ui selectmenu
55
- $( "#flag_list" ).iconselectmenu({
56
- create: add_icon,
57
- select: add_icon,
58
- });
59
-
60
- // languages form
61
- // fills the fields based on the language dropdown list choice
62
- $( '#lang_list' ).change(function() {
63
- var value = $( this ).val().split( ':' );
64
- var selected = $( "option:selected", this ).text().split( ' - ' );
65
- $( '#lang_slug' ).val( value[0] );
66
- $( '#lang_locale' ).val( value[1] );
67
- $( 'input[name="rtl"]' ).val( [value[2]] );
68
- $( '#lang_name' ).val( selected[0] );
69
- $( '#flag_list option[value="' + value[3] + '"]' ).attr( 'selected', 'selected' );
70
-
71
- // recreate the jquery ui selectmenu
72
- $( "#flag_list" ).iconselectmenu( 'destroy' ).iconselectmenu({
73
- create: add_icon,
74
- select: add_icon,
75
- })
76
- });
77
-
78
- // strings translations
79
- // save translations when pressing enter
80
- $( '.translation input' ).keypress(function( event ){
81
- if ( 13 === event.keyCode ) {
82
- event.preventDefault();
83
- $( '#submit' ).click();
84
- }
85
- });
86
-
87
- // settings page
88
- // click on configure link
89
- $( '#the-list' ).on( 'click', '.configure>a', function(){
90
- $( '.pll-configure' ).hide().prev().show();
91
- $( this ).closest( 'tr' ).hide().next().show();
92
- return false;
93
- });
94
-
95
- // cancel
96
- $( '#the-list' ).on( 'click', '.cancel', function(){
97
- $( this ).closest( 'tr' ).hide().prev().show();
98
- });
99
-
100
- // save settings
101
- $( '#the-list' ).on( 'click', '.save', function(){
102
- var tr = $( this ).closest( 'tr' );
103
- var parts = tr.attr( 'id' ).split( '-' );
104
-
105
- var data = {
106
- action: 'pll_save_options',
107
- pll_ajax_settings: true,
108
- module: parts[parts.length - 1],
109
- _pll_nonce: $( '#_pll_nonce' ).val()
110
- }
 
111
 
112
- data = tr.find( ':input' ).serialize() + '&' + $.param( data );
113
-
114
- $.post( ajaxurl, data, function( response ) {
115
- var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
116
- $.each( res.responses, function() {
117
- switch ( this.what ) {
118
- case 'license-update':
119
- $( '#pll-license-' + this.data ).replaceWith( this.supplemental.html );
120
- break;
121
- case 'success':
122
- tr.hide().prev().show(); // close only if there is no error
123
- case 'error':
124
- $( '.settings-error' ).remove(); // remove previous messages if any
125
- $( 'h1' ).after( this.data );
126
-
127
- // Make notices dismissible
128
- // copy paste of common.js from WP 4.2.2
129
- $( '.notice.is-dismissible' ).each(function() {
130
- var $this = $( this ),
131
- $button = $( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
132
- btnText = commonL10n.dismiss || '';
133
-
134
- // Ensure plain text
135
- $button.find( '.screen-reader-text' ).text( btnText );
136
-
137
- $this.append( $button );
138
-
139
- $button.on( 'click.wp-dismiss-notice', function( event ) {
140
- event.preventDefault();
141
- $this.fadeTo( 100 , 0, function() {
142
- $( this ).slideUp( 100, function() {
143
- $( this ).remove();
144
- });
145
- });
146
- });
147
- });
148
- break;
 
 
 
149
  }
150
- });
151
- });
152
- });
153
-
154
- // act when pressing enter or esc in configurations
155
- $( '.pll-configure' ).keypress(function( event ){
156
- if ( 13 === event.keyCode ) {
157
- event.preventDefault();
158
- $( this ).find( '.save' ).click();
159
- }
160
 
161
- if ( 27 === event.keyCode ) {
162
- event.preventDefault();
163
- $( this ).find( '.cancel' ).click();
164
- }
165
- });
 
 
 
 
 
 
166
 
167
- // settings URL modifications
168
- // manages visibility of fields
169
- $( "input[name='force_lang']" ).change(function() {
170
- function pll_toggle( a, test ) {
171
- test ? a.show() : a.hide();
172
- }
 
 
173
 
174
- var value = $( this ).val();
175
- pll_toggle( $( '#pll-domains-table' ), 3 == value );
176
- pll_toggle( $( "#pll-hide-default" ), 3 > value );
177
- pll_toggle( $( "#pll-rewrite" ), 2 > value );
178
- pll_toggle( $( "#pll-redirect-lang" ), 2 > value );
179
- });
180
-
181
- // settings license
182
- // deactivate button
183
- $( '.pll-deactivate-license' ).on( 'click', function() {
184
- var data = {
185
- action: 'pll_deactivate_license',
186
- pll_ajax_settings: true,
187
- id: $( this ).attr( 'id' ),
188
- _pll_nonce: $( '#_pll_nonce' ).val()
189
- }
190
- $.post( ajaxurl, data , function( response ){
191
- $( '#pll-license-' + response.id ).replaceWith( response.html );
192
- });
193
- });
194
 
195
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready(
2
+ function( $ ) {
3
+ var transitionTimeout;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
+ // languages list table
6
+ // accessibility to row actions on focus
7
+ // mainly copy paste of WP code from common.js
8
+ $( 'table.languages' ).on(
9
+ { // restricted to languages list table
10
+ focusin: function() {
11
+ clearTimeout( transitionTimeout );
12
+ focusedRowActions = $( this ).find( '.row-actions' );
13
+ // transitionTimeout is necessary for Firefox, but Chrome won't remove the CSS class without a little help.
14
+ $( '.row-actions' ).not( this ).removeClass( 'visible' );
15
+ focusedRowActions.addClass( 'visible' );
16
+ },
17
+ focusout: function() {
18
+ // Tabbing between post title and .row-actions links needs a brief pause, otherwise
19
+ // the .row-actions div gets hidden in transit in some browsers ( ahem, Firefox ).
20
+ transitionTimeout = setTimeout(
21
+ function() {
22
+ focusedRowActions.removeClass( 'visible' );
23
+ },
24
+ 30
25
+ );
26
+ }
27
+ },
28
+ 'tr'
29
+ ); // acts on the whole tr instead of single td as we have actions links in several columns
30
+
31
+ // Common functions for overriding language and flag dropdown list.
32
+ var selectmenuRenderItem = function( wrapper, item ) {
33
+ var li = $( '<li>' ).text( item.label ).prepend( $( item.element ).data( 'flag-html' ) );
34
+ li.children( 'img' ).addClass( 'ui-icon' );
35
+ return li.appendTo( wrapper );
36
+ };
37
+ var selectmenuRefreshButtonText = function( selectElement ) {
38
+ var buttonText = $( selectElement ).selectmenu( 'instance' ).buttonText;
39
+ buttonText.prepend( $( selectElement ).children( ':selected' ).data( 'flag-html' ) );
40
+ buttonText.children( 'img' ).addClass( 'ui-icon' );
41
+ };
42
+ // Overrides the flag dropdown list with our customized jquery ui selectmenu.
43
+
44
+ // Inject flag image when jQuery UI selectmenu is created or an item is selected.
45
+ $( '#flag_list' ).on(
46
+ 'selectmenucreate selectmenuselect',
47
+ function(){
48
+ selectmenuRefreshButtonText( this );
49
+ }
50
+ );
51
+ // Refresh jQuery UI selectmenu when the value is changed programmatically: select another language or edit an existing language.
52
+ // For putting the focus in the list on the selected item and injecting the right flag on the selected item.
53
+ $( '#flag_list' ).on(
54
+ 'selectmenuopen',
55
+ function(){
56
+ $( this ).selectmenu( 'refresh' ).trigger( 'selectmenuselect' );
57
+ }
58
+ );
59
+ // Create the jQuery UI selectmenu widget
60
+ $( '#flag_list' ).selectmenu( { width: '100%' } );
61
+ // Overrides each item in the jQuery UI selectmenu list by injecting flag image.
62
+ $( '#flag_list' ).selectmenu( 'instance' )._renderItem = selectmenuRenderItem;
63
 
64
+ // Language choice in predefined languages in Polylang Languages settings page and wizard.
65
+ // Overrides the predefined language dropdown list with our customized jquery ui selectmenu.
66
+
67
+ // Inject flag image when jQuery UI selectmenu is created or an item is selected.
68
+ $( '#lang_list' ).on(
69
+ 'selectmenucreate selectmenuselect',
70
+ function() {
71
+ selectmenuRefreshButtonText( this );
72
+ }
73
+ );
74
+ // Create the jQuery UI selectmenu widget
75
+ $( '#lang_list' ).selectmenu( { width: '100%' } );
76
+ // Overrides each element in the jQuery UI selectmenu list by injecting flag image.
77
+ $( '#lang_list' ).selectmenu( 'instance' )._renderItem = selectmenuRenderItem;
78
+
79
+ // Languages form
80
+ // Fills the fields based on the language dropdown list choice
81
+ $( '#add-lang #lang_list' ).on(
82
+ 'selectmenuchange',
83
+ function() {
84
+ var value = $( this ).val().split( ':' );
85
+ var selected = $( "option:selected", this ).text().split( ' - ' );
86
+ $( '#lang_slug' ).val( value[0] );
87
+ $( '#lang_locale' ).val( value[1] );
88
+ $( 'input[name="rtl"]' ).val( [value[2]] );
89
+ $( '#lang_name' ).val( selected[0] );
90
+ $( '#flag_list').val( value[3] );
91
+
92
+ // Refresh the jQuery UI selectmenu flags list.
93
+ $( '#flag_list' ).selectmenu( 'refresh' ).trigger( 'selectmenuselect' );
94
+ }
95
+ );
96
+
97
+ // strings translations
98
+ // save translations when pressing enter
99
+ $( '.translation input' ).keypress(
100
+ function( event ){
101
+ if ( 13 === event.keyCode ) {
102
+ event.preventDefault();
103
+ $( '#submit' ).click();
104
  }
105
+ }
106
+ );
 
 
 
 
 
 
 
 
107
 
108
+ // settings page
109
+ // click on configure link
110
+ $( '#the-list' ).on(
111
+ 'click',
112
+ '.configure>a',
113
+ function(){
114
+ $( '.pll-configure' ).hide().prev().show();
115
+ $( this ).closest( 'tr' ).hide().next().show();
116
+ return false;
117
+ }
118
+ );
119
 
120
+ // cancel
121
+ $( '#the-list' ).on(
122
+ 'click',
123
+ '.cancel',
124
+ function(){
125
+ $( this ).closest( 'tr' ).hide().prev().show();
126
+ }
127
+ );
128
 
129
+ // save settings
130
+ $( '#the-list' ).on(
131
+ 'click',
132
+ '.save',
133
+ function(){
134
+ var tr = $( this ).closest( 'tr' );
135
+ var parts = tr.attr( 'id' ).split( '-' );
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
+ var data = {
138
+ action: 'pll_save_options',
139
+ pll_ajax_settings: true,
140
+ module: parts[parts.length - 1],
141
+ _pll_nonce: $( '#_pll_nonce' ).val()
142
+ };
143
+
144
+ data = tr.find( ':input' ).serialize() + '&' + $.param( data );
145
+
146
+ $.post(
147
+ ajaxurl,
148
+ data,
149
+ function( response ) {
150
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
151
+ $.each(
152
+ res.responses,
153
+ function() {
154
+ switch ( this.what ) {
155
+ case 'license-update':
156
+ $( '#pll-license-' + this.data ).replaceWith( this.supplemental.html );
157
+ break;
158
+ case 'success':
159
+ tr.hide().prev().show(); // close only if there is no error
160
+ case 'error':
161
+ $( '.settings-error' ).remove(); // remove previous messages if any
162
+ $( 'h1' ).after( this.data );
163
+
164
+ // Make notices dismissible
165
+ // copy paste of common.js from WP 4.2.2
166
+ $( '.notice.is-dismissible' ).each(
167
+ function() {
168
+ var $this = $( this ),
169
+ $button = $( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
170
+ btnText = commonL10n.dismiss || '';
171
+
172
+ // Ensure plain text
173
+ $button.find( '.screen-reader-text' ).text( btnText );
174
+
175
+ // Whitelist because of how the button is built. See above
176
+ $this.append( $button ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
177
+
178
+ $button.on(
179
+ 'click.wp-dismiss-notice',
180
+ function( event ) {
181
+ event.preventDefault();
182
+ $this.fadeTo(
183
+ 100,
184
+ 0,
185
+ function() {
186
+ $( this ).slideUp(
187
+ 100,
188
+ function() {
189
+ $( this ).remove();
190
+ }
191
+ );
192
+ }
193
+ );
194
+ }
195
+ );
196
+ }
197
+ );
198
+ break;
199
+ }
200
+ }
201
+ );
202
+ }
203
+ );
204
+ }
205
+ );
206
+
207
+ // act when pressing enter or esc in configurations
208
+ $( '.pll-configure' ).keypress(
209
+ function( event ){
210
+ if ( 13 === event.keyCode ) {
211
+ event.preventDefault();
212
+ $( this ).find( '.save' ).click();
213
+ }
214
+
215
+ if ( 27 === event.keyCode ) {
216
+ event.preventDefault();
217
+ $( this ).find( '.cancel' ).click();
218
+ }
219
+ }
220
+ );
221
+
222
+ // settings URL modifications
223
+ // manages visibility of fields
224
+ $( "input[name='force_lang']" ).change(
225
+ function() {
226
+ function pll_toggle( a, test ) {
227
+ test ? a.show() : a.hide();
228
+ }
229
+
230
+ var value = $( this ).val();
231
+ pll_toggle( $( '#pll-domains-table' ), 3 == value );
232
+ pll_toggle( $( "#pll-hide-default" ), 3 > value );
233
+ pll_toggle( $( "#pll-rewrite" ), 2 > value );
234
+ pll_toggle( $( "#pll-redirect-lang" ), 2 > value );
235
+ }
236
+ );
237
+
238
+ // settings license
239
+ // deactivate button
240
+ $( '.pll-deactivate-license' ).on(
241
+ 'click',
242
+ function() {
243
+ var data = {
244
+ action: 'pll_deactivate_license',
245
+ pll_ajax_settings: true,
246
+ id: $( this ).attr( 'id' ),
247
+ _pll_nonce: $( '#_pll_nonce' ).val()
248
+ };
249
+ $.post(
250
+ ajaxurl,
251
+ data,
252
+ function( response ){
253
+ $( '#pll-license-' + response.id ).replaceWith( response.html );
254
+ }
255
+ );
256
+ }
257
+ );
258
+
259
+ // Manage closing the metabox.
260
+ // close postboxes that should be closed
261
+ $( '.if-js-closed' ).removeClass( 'if-js-closed' ).addClass( 'closed' );
262
+ // postboxes setup
263
+ if ( 'undefined' !== typeof postboxes ) {
264
+ postboxes.add_postbox_toggles( pagenow );
265
+ }
266
+ }
267
+ );
js/admin.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(document).ready(function(e){function t(){var t=e(this).find(":selected"),i=t.data("url");if(i){var n=e(this).iconselectmenu("widget").children(":last"),s=t.data("width"),l=t.data("height");e("<img class='ui-icon' />").prop("src",i).prop("width",s).prop("height",l).appendTo(n)}}var i;e("table.languages").on({focusin:function(){clearTimeout(i),focusedRowActions=e(this).find(".row-actions"),e(".row-actions").not(this).removeClass("visible"),focusedRowActions.addClass("visible")},focusout:function(){i=setTimeout(function(){focusedRowActions.removeClass("visible")},30)}},"tr"),e.widget("custom.iconselectmenu",e.ui.selectmenu,{_renderItem:function(t,i){var n=e("<li>",{text:i.label}),s=e(i.element),l=s.data("url");if(l){var a=s.data("width"),c=s.data("height");e("<img class='ui-icon' />").prop("src",l).prop("width",a).prop("height",c).appendTo(n)}return n.appendTo(t)}}),e("#flag_list").iconselectmenu({create:t,select:t}),e("#lang_list").change(function(){var i=e(this).val().split(":"),n=e("option:selected",this).text().split(" - ");e("#lang_slug").val(i[0]),e("#lang_locale").val(i[1]),e('input[name="rtl"]').val([i[2]]),e("#lang_name").val(n[0]),e('#flag_list option[value="'+i[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"),i=t.attr("id").split("-"),n={action:"pll_save_options",pll_ajax_settings:!0,module:i[i.length-1],_pll_nonce:e("#_pll_nonce").val()};n=t.find(":input").serialize()+"&"+e.param(n),e.post(ajaxurl,n,function(i){var n=wpAjax.parseAjaxResponse(i,"ajax-response");e.each(n.responses,function(){switch(this.what){case"license-update":e("#pll-license-"+this.data).replaceWith(this.supplemental.html);break;case"success":t.hide().prev().show();case"error":e(".settings-error").remove(),e("h1").after(this.data),e(".notice.is-dismissible").each(function(){var t=e(this),i=e('<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>'),n=commonL10n.dismiss||"";i.find(".screen-reader-text").text(n),t.append(i),i.on("click.wp-dismiss-notice",function(i){i.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 i=e(this).val();t(e("#pll-domains-table"),3==i),t(e("#pll-hide-default"),3>i),t(e("#pll-rewrite"),2>i),t(e("#pll-redirect-lang"),2>i)}),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){var t;e("table.languages").on({focusin:function(){clearTimeout(t),focusedRowActions=e(this).find(".row-actions"),e(".row-actions").not(this).removeClass("visible"),focusedRowActions.addClass("visible")},focusout:function(){t=setTimeout(function(){focusedRowActions.removeClass("visible")},30)}},"tr");var n=function(t,n){var s=e("<li>").text(n.label).prepend(e(n.element).data("flag-html"));return s.children("img").addClass("ui-icon"),s.appendTo(t)},s=function(t){var n=e(t).selectmenu("instance").buttonText;n.prepend(e(t).children(":selected").data("flag-html")),n.children("img").addClass("ui-icon")};e("#flag_list").on("selectmenucreate selectmenuselect",function(){s(this)}),e("#flag_list").on("selectmenuopen",function(){e(this).selectmenu("refresh").trigger("selectmenuselect")}),e("#flag_list").selectmenu({width:"100%"}),e("#flag_list").selectmenu("instance")._renderItem=n,e("#lang_list").on("selectmenucreate selectmenuselect",function(){s(this)}),e("#lang_list").selectmenu({width:"100%"}),e("#lang_list").selectmenu("instance")._renderItem=n,e("#add-lang #lang_list").on("selectmenuchange",function(){var t=e(this).val().split(":"),n=e("option:selected",this).text().split(" - ");e("#lang_slug").val(t[0]),e("#lang_locale").val(t[1]),e('input[name="rtl"]').val([t[2]]),e("#lang_name").val(n[0]),e("#flag_list").val(t[3]),e("#flag_list").selectmenu("refresh").trigger("selectmenuselect")}),e(".translation input").keypress(function(t){13===t.keyCode&&(t.preventDefault(),e("#submit").click())}),e("#the-list").on("click",".configure>a",function(){return e(".pll-configure").hide().prev().show(),e(this).closest("tr").hide().next().show(),!1}),e("#the-list").on("click",".cancel",function(){e(this).closest("tr").hide().prev().show()}),e("#the-list").on("click",".save",function(){var t=e(this).closest("tr"),n=t.attr("id").split("-"),s={action:"pll_save_options",pll_ajax_settings:!0,module:n[n.length-1],_pll_nonce:e("#_pll_nonce").val()};s=t.find(":input").serialize()+"&"+e.param(s),e.post(ajaxurl,s,function(n){var s=wpAjax.parseAjaxResponse(n,"ajax-response");e.each(s.responses,function(){switch(this.what){case"license-update":e("#pll-license-"+this.data).replaceWith(this.supplemental.html);break;case"success":t.hide().prev().show();case"error":e(".settings-error").remove(),e("h1").after(this.data),e(".notice.is-dismissible").each(function(){var t=e(this),n=e('<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>'),s=commonL10n.dismiss||"";n.find(".screen-reader-text").text(s),t.append(n),n.on("click.wp-dismiss-notice",function(n){n.preventDefault(),t.fadeTo(100,0,function(){e(this).slideUp(100,function(){e(this).remove()})})})})}})})}),e(".pll-configure").keypress(function(t){13===t.keyCode&&(t.preventDefault(),e(this).find(".save").click()),27===t.keyCode&&(t.preventDefault(),e(this).find(".cancel").click())}),e("input[name='force_lang']").change(function(){function t(e,t){t?e.show():e.hide()}var n=e(this).val();t(e("#pll-domains-table"),3==n),t(e("#pll-hide-default"),3>n),t(e("#pll-rewrite"),2>n),t(e("#pll-redirect-lang"),2>n)}),e(".pll-deactivate-license").on("click",function(){var t={action:"pll_deactivate_license",pll_ajax_settings:!0,id:e(this).attr("id"),_pll_nonce:e("#_pll_nonce").val()};e.post(ajaxurl,t,function(t){e("#pll-license-"+t.id).replaceWith(t.html)})}),e(".if-js-closed").removeClass("if-js-closed").addClass("closed"),"undefined"!=typeof postboxes&&postboxes.add_postbox_toggles(pagenow)});
js/block-editor.js CHANGED
@@ -3,19 +3,22 @@
3
  *
4
  * @since 2.5
5
  */
6
- wp.apiFetch.use( function( options, next ) {
7
- // If options.url is defined, this is not a REST request but a direct call to post.php for legacy metaboxes.
8
- if ( 'undefined' === typeof options.url ) {
9
- if ( 'undefined' === typeof options.data ) {
10
- // GET
11
- options.path += ( ( options.path.indexOf ( '?' ) >= 0 ) ? '&lang=' : '?lang=' ) + getCurrentLanguage();
12
- } else {
13
- // PUT, POST
14
- options.data.lang = getCurrentLanguage();
 
 
 
15
  }
 
16
  }
17
- return next( options );
18
- } );
19
 
20
  /**
21
  * Get the language from the HTML form
@@ -33,63 +36,77 @@ function getCurrentLanguage() {
33
  *
34
  * @since 2.5
35
  */
36
- jQuery( document ).ready(function( $ ) {
37
- // savePost after changing the post's language and reload page for refreshing post translated data
38
- $( '.post_lang_choice' ).change(function() {
39
- const select = wp.data.select;
40
- const dispatch = wp.data.dispatch;
41
- const subscribe = wp.data.subscribe;
42
-
43
- let unsubscribe = null;
44
-
45
- // Listen if the savePost is done
46
- const savePostIsDone = new Promise( function( resolve, reject ) {
47
- unsubscribe = subscribe( function() {
48
- const isSavePostSucceeded = select('core/editor').didPostSaveRequestSucceed();
49
- const isSavePostFailed = select('core/editor').didPostSaveRequestFail();
50
- if ( isSavePostSucceeded || isSavePostFailed ) {
51
- if ( isSavePostFailed ) {
52
- reject();
53
- } else {
54
- resolve();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
  }
57
- } );
58
- });
59
-
60
- // Specific case for empty posts
61
- if ( location.pathname.match( /post-new.php/gi ) ) {
62
- const title = select('core/editor').getEditedPostAttribute('title');
63
- const content = select('core/editor').getEditedPostAttribute('content');
64
- const excerpt = select('core/editor').getEditedPostAttribute('excerpt');
65
- if ( '' === title && '' === content && '' === excerpt ) {
66
- // Change the new_lang parameter with the new language value for reloading the page
67
- if ( -1 != location.search.indexOf( 'new_lang' ) ) {
68
- window.location.search = window.location.search.replace( /(?:new_lang=[^&]*)(&)?(.*)/, 'new_lang=' + this.value + '$1$2' );;
69
- } else {
70
- window.location.search = window.location.search + ( ( -1 != window.location.search.indexOf( '?' ) ) ? '&' : '?' ) + 'new_lang=' + this.value;
71
- }
72
- }
73
- }
74
 
75
- // For empty posts savePost does nothing
76
- dispatch( 'core/editor' ).savePost();
77
-
78
- savePostIsDone
79
- .then( function() {
80
- // If the post is well saved, we can reload the page
81
- unsubscribe();
82
- window.location.reload();
83
- }, function() {
84
- // If the post save failed
85
- unsubscribe();
86
- } )
87
- .catch( function() {
88
- // If an exception is thrown
89
- unsubscribe();
90
- } );
91
- } );
92
- } );
 
 
 
 
 
93
 
94
  /**
95
  * Handles internals of the metabox:
@@ -97,88 +114,120 @@ jQuery( document ).ready(function( $ ) {
97
  *
98
  * @since 1.5
99
  */
100
- jQuery( document ).ready(function( $ ) {
101
- // Ajax for changing the post's language in the languages metabox
102
- $( '.post_lang_choice' ).change(function() {
103
- var data = {
104
- action: 'post_lang_choice',
105
- lang: $( this ).val(),
106
- post_type: $( '#post_type' ).val(),
107
- post_id: $( '#post_ID' ).val(),
108
- _pll_nonce: $( '#_pll_nonce' ).val()
109
- }
110
-
111
- $.post( ajaxurl, data , function( response ) {
112
- var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
113
- $.each( res.responses, function() {
114
- switch ( this.what ) {
115
- case 'translations': // Translations fields
116
- $( '.translations' ).html( this.data );
117
- init_translations();
118
- break;
119
- case 'flag': // Flag in front of the select dropdown
120
- $( '.pll-select-flag' ).html( this.data );
121
- break;
122
- }
123
- });
124
- });
125
- });
126
-
127
- // Translations autocomplete input box
128
- function init_translations() {
129
- $( '.tr_lang' ).each(function(){
130
- var tr_lang = $( this ).attr( 'id' ).substring( 8 );
131
- var td = $( this ).parent().parent().siblings( '.pll-edit-column' );
132
-
133
- $( this ).autocomplete({
134
- minLength: 0,
135
-
136
- source: ajaxurl + '?action=pll_posts_not_translated' +
137
- '&post_language=' + $( '.post_lang_choice' ).val() +
138
- '&translation_language=' + tr_lang +
139
- '&post_type=' + $( '#post_type' ).val() +
140
- '&_pll_nonce=' + $( '#_pll_nonce' ).val(),
141
-
142
- select: function( event, ui ) {
143
- $( '#htr_lang_' + tr_lang ).val( ui.item.id );
144
- td.html( ui.item.link );
145
- },
146
- });
147
-
148
- // When the input box is emptied
149
- $( this ).blur(function() {
150
- if ( ! $( this ).val() ) {
151
- $( '#htr_lang_' + tr_lang ).val( 0 );
152
- td.html( td.siblings( '.hidden' ).children().clone() );
153
  }
154
- });
155
- });
156
- }
157
 
158
- init_translations();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
- // Handle the response to a click on a Languages metabox button
161
- $( '#ml_box' ).on( 'click', '.pll-button', function(){
162
- var value = $( this ).hasClass( 'wp-ui-text-highlight' );
163
- var id = $( this ).attr( 'id' );
164
- var post_id = $( '#htr_lang_' + id.replace( 'pll_sync_post[', '' ).replace( ']', '' ) ).val();
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
- if ( 'undefined' == typeof( post_id ) || 0 == post_id || value || confirm( confirm_text ) ) {
167
- var data = {
168
- action: 'toggle_' + id,
169
- value: value,
170
- post_type: $( '#post_type' ).val(),
171
- _pll_nonce: $( '#_pll_nonce' ).val()
 
 
 
 
 
 
 
 
 
 
172
  }
173
-
174
- $.post( ajaxurl, data , function( response ){
175
- var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
176
- $.each( res.responses, function() {
177
- id = id.replace( '[', '\\[' ).replace( ']', '\\]' );
178
- $( '#' + id ).toggleClass( 'wp-ui-text-highlight' ).attr( 'title', this.data ).children( 'span' ).html( this.data );
179
- $( 'input[name="' + id + '"]' ).val( ! data['value'] );
180
- });
181
- });
182
- }
183
- });
184
- });
3
  *
4
  * @since 2.5
5
  */
6
+
7
+ wp.apiFetch.use(
8
+ function( options, next ) {
9
+ // If options.url is defined, this is not a REST request but a direct call to post.php for legacy metaboxes.
10
+ if ( 'undefined' === typeof options.url ) {
11
+ if ( 'undefined' === typeof options.data ) {
12
+ // GET
13
+ options.path += ( ( options.path.indexOf( '?' ) >= 0 ) ? '&lang=' : '?lang=' ) + getCurrentLanguage();
14
+ } else {
15
+ // PUT, POST
16
+ options.data.lang = getCurrentLanguage();
17
+ }
18
  }
19
+ return next( options );
20
  }
21
+ );
 
22
 
23
  /**
24
  * Get the language from the HTML form
36
  *
37
  * @since 2.5
38
  */
39
+ jQuery( document ).ready(
40
+ function( $ ) {
41
+ // savePost after changing the post's language and reload page for refreshing post translated data
42
+ $( '.post_lang_choice' ).change(
43
+ function() {
44
+ const select = wp.data.select;
45
+ const dispatch = wp.data.dispatch;
46
+ const subscribe = wp.data.subscribe;
47
+
48
+ let unsubscribe = null;
49
+
50
+ // Listen if the savePost is done
51
+ const savePostIsDone = new Promise(
52
+ function( resolve, reject ) {
53
+ unsubscribe = subscribe(
54
+ function() {
55
+ const isSavePostSucceeded = select( 'core/editor' ).didPostSaveRequestSucceed();
56
+ const isSavePostFailed = select( 'core/editor' ).didPostSaveRequestFail();
57
+ if ( isSavePostSucceeded || isSavePostFailed ) {
58
+ if ( isSavePostFailed ) {
59
+ reject();
60
+ } else {
61
+ resolve();
62
+ }
63
+ }
64
+ }
65
+ );
66
+ }
67
+ );
68
+
69
+ // Specific case for empty posts
70
+ if ( location.pathname.match( /post-new.php/gi ) ) {
71
+ const title = select( 'core/editor' ).getEditedPostAttribute( 'title' );
72
+ const content = select( 'core/editor' ).getEditedPostAttribute( 'content' );
73
+ const excerpt = select( 'core/editor' ).getEditedPostAttribute( 'excerpt' );
74
+ if ( '' === title && '' === content && '' === excerpt ) {
75
+ // Change the new_lang parameter with the new language value for reloading the page
76
+ // WPCS location.search is never written in the page, just used to relaoad page ( See line 94 ) with the right value of new_lang
77
+ // new_lang input is controlled server side in PHP. The value come from the dropdown list of language returned and escaped server side
78
+ if ( -1 != location.search.indexOf( 'new_lang' ) ) {
79
+ // use regexp non capturing group to replace new_lang parameter no matter where it is and capture other parameters which can be behind it
80
+ window.location.search = window.location.search.replace( /(?:new_lang=[^&]*)(&)?(.*)/, 'new_lang=' + this.value + '$1$2' ); // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment
81
+ } else {
82
+ window.location.search = window.location.search + ( ( -1 != window.location.search.indexOf( '?' ) ) ? '&' : '?' ) + 'new_lang=' + this.value; // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment
83
+ }
84
  }
85
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
+ // For empty posts savePost does nothing
88
+ dispatch( 'core/editor' ).savePost();
89
+
90
+ savePostIsDone.then(
91
+ function() {
92
+ // If the post is well saved, we can reload the page
93
+ unsubscribe();
94
+ window.location.reload();
95
+ },
96
+ function() {
97
+ // If the post save failed
98
+ unsubscribe();
99
+ }
100
+ ).catch(
101
+ function() {
102
+ // If an exception is thrown
103
+ unsubscribe();
104
+ }
105
+ );
106
+ }
107
+ );
108
+ }
109
+ );
110
 
111
  /**
112
  * Handles internals of the metabox:
114
  *
115
  * @since 1.5
116
  */
117
+ jQuery( document ).ready(
118
+ function( $ ) {
119
+ // Ajax for changing the post's language in the languages metabox
120
+ $( '.post_lang_choice' ).change(
121
+ function() {
122
+ var data = {
123
+ action: 'post_lang_choice',
124
+ lang: $( this ).val(),
125
+ post_type: $( '#post_type' ).val(),
126
+ post_id: $( '#post_ID' ).val(),
127
+ _pll_nonce: $( '#_pll_nonce' ).val()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  }
 
 
 
129
 
130
+ $.post(
131
+ ajaxurl,
132
+ data,
133
+ function( response ) {
134
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
135
+ $.each(
136
+ res.responses,
137
+ function() {
138
+ switch ( this.what ) {
139
+ case 'translations': // Translations fields
140
+ // Data is built and come from server side and is well escaped when necessary
141
+ $( '.translations' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
142
+ init_translations();
143
+ break;
144
+ case 'flag': // Flag in front of the select dropdown
145
+ // Data is built and come from server side and is well escaped when necessary
146
+ $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
147
+ break;
148
+ }
149
+ }
150
+ );
151
+ }
152
+ );
153
+ }
154
+ );
155
+
156
+ // Translations autocomplete input box
157
+ function init_translations() {
158
+ $( '.tr_lang' ).each(
159
+ function(){
160
+ var tr_lang = $( this ).attr( 'id' ).substring( 8 );
161
+ var td = $( this ).parent().parent().siblings( '.pll-edit-column' );
162
+
163
+ $( this ).autocomplete(
164
+ {
165
+ minLength: 0,
166
+
167
+ source: ajaxurl + '?action=pll_posts_not_translated' +
168
+ '&post_language=' + $( '.post_lang_choice' ).val() +
169
+ '&translation_language=' + tr_lang +
170
+ '&post_type=' + $( '#post_type' ).val() +
171
+ '&_pll_nonce=' + $( '#_pll_nonce' ).val(),
172
+
173
+ select: function( event, ui ) {
174
+ $( '#htr_lang_' + tr_lang ).val( ui.item.id );
175
+ // ui.item.link is built and come from server side and is well escaped when necessary
176
+ td.html( ui.item.link ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
177
+ },
178
+ }
179
+ );
180
+
181
+ // When the input box is emptied
182
+ $( this ).blur(
183
+ function() {
184
+ if ( ! $( this ).val() ) {
185
+ $( '#htr_lang_' + tr_lang ).val( 0 );
186
+ // Value is retrieved from HTML already generated server side
187
+ td.html( td.siblings( '.hidden' ).children().clone() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
188
+ }
189
+ }
190
+ );
191
+ }
192
+ );
193
+ }
194
 
195
+ init_translations();
196
+
197
+ // Handle the response to a click on a Languages metabox button
198
+ $( '#ml_box' ).on(
199
+ 'click',
200
+ '.pll-button',
201
+ function(){
202
+ var value = $( this ).hasClass( 'wp-ui-text-highlight' );
203
+ var id = $( this ).attr( 'id' );
204
+ var post_id = $( '#htr_lang_' + id.replace( 'pll_sync_post[', '' ).replace( ']', '' ) ).val();
205
+
206
+ if ( 'undefined' == typeof( post_id ) || 0 == post_id || value || confirm( confirm_text ) ) {
207
+ var data = {
208
+ action: 'toggle_' + id,
209
+ value: value,
210
+ post_type: $( '#post_type' ).val(),
211
+ _pll_nonce: $( '#_pll_nonce' ).val()
212
+ }
213
 
214
+ $.post(
215
+ ajaxurl,
216
+ data,
217
+ function( response ){
218
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
219
+ $.each(
220
+ res.responses,
221
+ function() {
222
+ id = id.replace( '[', '\\[' ).replace( ']', '\\]' );
223
+ $( '#' + id ).toggleClass( 'wp-ui-text-highlight' ).attr( 'title', this.data ).children( 'span' ).text( this.data );
224
+ $( 'input[name="' + id + '"]' ).val( ! data['value'] );
225
+ }
226
+ );
227
+ }
228
+ );
229
+ }
230
  }
231
+ );
232
+ }
233
+ );
 
 
 
 
 
 
 
 
 
js/block-editor.min.js CHANGED
@@ -1 +1 @@
1
- wp.apiFetch.use(function(options,next){if('undefined'===typeof options.url){if('undefined'===typeof options.data){options.path+=((options.path.indexOf('?')>=0)?'&lang=':'?lang=')+getCurrentLanguage()}else{options.data.lang=getCurrentLanguage()}}return next(options)});function getCurrentLanguage(){return document.querySelector('[name=post_lang_choice]').value}jQuery(document).ready(function($){$('.post_lang_choice').change(function(){const select=wp.data.select;const dispatch=wp.data.dispatch;const subscribe=wp.data.subscribe;let unsubscribe=null;const savePostIsDone=new Promise(function(resolve,reject){unsubscribe=subscribe(function(){const isSavePostSucceeded=select('core/editor').didPostSaveRequestSucceed();const isSavePostFailed=select('core/editor').didPostSaveRequestFail();if(isSavePostSucceeded||isSavePostFailed){if(isSavePostFailed){reject()}else{resolve()}}})});if(location.pathname.match(/post-new.php/gi)){const title=select('core/editor').getEditedPostAttribute('title');const content=select('core/editor').getEditedPostAttribute('content');const excerpt=select('core/editor').getEditedPostAttribute('excerpt');if(''===title&&''===content&&''===excerpt){if(-1!=location.search.indexOf('new_lang')){window.location.search=window.location.search.replace(/(?:new_lang=[^&]*)(&)?(.*)/,'new_lang='+this.value+'$1$2');}else{window.location.search=window.location.search+((-1!=window.location.search.indexOf('?'))?'&':'?')+'new_lang='+this.value}}}dispatch('core/editor').savePost();savePostIsDone.then(function(){unsubscribe();window.location.reload()},function(){unsubscribe()}).catch(function(){unsubscribe()})})});jQuery(document).ready(function($){$('.post_lang_choice').change(function(){var data={action:'post_lang_choice',lang:$(this).val(),post_type:$('#post_type').val(),post_id:$('#post_ID').val(),_pll_nonce:$('#_pll_nonce').val()};$.post(ajaxurl,data,function(response){var res=wpAjax.parseAjaxResponse(response,'ajax-response');$.each(res.responses,function(){switch(this.what){case 'translations':$('.translations').html(this.data);init_translations();break;case 'flag':$('.pll-select-flag').html(this.data);break}})})});function init_translations(){$('.tr_lang').each(function(){var tr_lang=$(this).attr('id').substring(8);var td=$(this).parent().parent().siblings('.pll-edit-column');$(this).autocomplete({minLength:0,source:ajaxurl+'?action=pll_posts_not_translated&post_language='+$('.post_lang_choice').val()+'&translation_language='+tr_lang+'&post_type='+$('#post_type').val()+'&_pll_nonce='+$('#_pll_nonce').val(),select:function(event,ui){$('#htr_lang_'+tr_lang).val(ui.item.id);td.html(ui.item.link)}});$(this).blur(function(){if(!$(this).val()){$('#htr_lang_'+tr_lang).val(0);td.html(td.siblings('.hidden').children().clone())}})})}init_translations();$('#ml_box').on('click','.pll-button',function(){var value=$(this).hasClass('wp-ui-text-highlight');var id=$(this).attr('id');var post_id=$('#htr_lang_'+id.replace('pll_sync_post[','').replace(']','')).val();if('undefined'==typeof(post_id)||0==post_id||value||confirm(confirm_text)){var data={action:'toggle_'+id,value:value,post_type:$('#post_type').val(),_pll_nonce:$('#_pll_nonce').val()};$.post(ajaxurl,data,function(response){var res=wpAjax.parseAjaxResponse(response,'ajax-response');$.each(res.responses,function(){id=id.replace('[','\\[').replace(']','\\]');$('#'+id).toggleClass('wp-ui-text-highlight').attr('title',this.data).children('span').html(this.data);$('input[name="'+id+'"]').val(!data['value'])})})}})});
1
+ function getCurrentLanguage(){return document.querySelector("[name=post_lang_choice]").value}wp.apiFetch.use(function(t,e){return void 0===t.url&&(void 0===t.data?t.path+=(t.path.indexOf("?")>=0?"&lang=":"?lang=")+getCurrentLanguage():t.data.lang=getCurrentLanguage()),e(t)}),jQuery(document).ready(function(t){t(".post_lang_choice").change(function(){const t=wp.data.select,e=wp.data.dispatch,n=wp.data.subscribe;let a=null;const o=new Promise(function(e,o){a=n(function(){const n=t("core/editor").didPostSaveRequestSucceed(),a=t("core/editor").didPostSaveRequestFail();(n||a)&&(a?o():e())})});if(location.pathname.match(/post-new.php/gi)){const e=t("core/editor").getEditedPostAttribute("title"),n=t("core/editor").getEditedPostAttribute("content"),a=t("core/editor").getEditedPostAttribute("excerpt");""===e&&""===n&&""===a&&(-1!=location.search.indexOf("new_lang")?window.location.search=window.location.search.replace(/(?:new_lang=[^&]*)(&)?(.*)/,"new_lang="+this.value+"$1$2"):window.location.search=window.location.search+(-1!=window.location.search.indexOf("?")?"&":"?")+"new_lang="+this.value)}e("core/editor").savePost(),o.then(function(){a(),window.location.reload()},function(){a()}).catch(function(){a()})})}),jQuery(document).ready(function(t){function e(){t(".tr_lang").each(function(){var e=t(this).attr("id").substring(8),n=t(this).parent().parent().siblings(".pll-edit-column");t(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_posts_not_translated&post_language="+t(".post_lang_choice").val()+"&translation_language="+e+"&post_type="+t("#post_type").val()+"&_pll_nonce="+t("#_pll_nonce").val(),select:function(a,o){t("#htr_lang_"+e).val(o.item.id),n.html(o.item.link)}}),t(this).blur(function(){t(this).val()||(t("#htr_lang_"+e).val(0),n.html(n.siblings(".hidden").children().clone()))})})}t(".post_lang_choice").change(function(){var n={action:"post_lang_choice",lang:t(this).val(),post_type:t("#post_type").val(),post_id:t("#post_ID").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,n,function(n){var a=wpAjax.parseAjaxResponse(n,"ajax-response");t.each(a.responses,function(){switch(this.what){case"translations":t(".translations").html(this.data),e();break;case"flag":t(".pll-select-flag").html(this.data)}})})}),e(),t("#ml_box").on("click",".pll-button",function(){var e=t(this).hasClass("wp-ui-text-highlight"),n=t(this).attr("id"),a=t("#htr_lang_"+n.replace("pll_sync_post[","").replace("]","")).val();if(void 0===a||0==a||e||confirm(confirm_text)){var o={action:"toggle_"+n,value:e,post_type:t("#post_type").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,o,function(e){var a=wpAjax.parseAjaxResponse(e,"ajax-response");t.each(a.responses,function(){n=n.replace("[","\\[").replace("]","\\]"),t("#"+n).toggleClass("wp-ui-text-highlight").attr("title",this.data).children("span").text(this.data),t('input[name="'+n+'"]').val(!o.value)})})}})});
js/classic-editor.js CHANGED
@@ -1,10 +1,12 @@
1
  // tag suggest in metabox
2
  (function( $ ){
3
- $.ajaxPrefilter(function( options, originalOptions, jqXHR ) {
4
- if ( 'string' === typeof options.data && -1 !== options.url.indexOf( 'action=ajax-tag-search' ) && ( lang = $( '.post_lang_choice' ).val() ) ) {
5
- options.data = 'lang=' + lang + '&' + options.data;
 
 
6
  }
7
- });
8
  })( jQuery );
9
 
10
  // overrides tagBox.get
@@ -20,163 +22,211 @@
20
  tax: tax
21
  }
22
 
23
- $.post( ajaxurl, data, function( r, stat ) {
24
- if ( 0 == r || 'success' != stat ) {
25
- r = wpAjax.broken;
26
- }
27
-
28
- r = $( '<div id="tagcloud-' + tax + '" class="the-tagcloud">' + r + '</div>' );
29
- $( 'a', r ).click(function(){
30
- tagBox.flushTags( $( this ).closest( '.inside' ).children( '.tagsdiv' ), this );
31
- return false;
32
- });
33
 
34
- // add an if else condition to allow modifying the tags outputed when switching the language
35
- if ( v = $( '.the-tagcloud' ).css( 'display' ) ) {
36
- $( '.the-tagcloud' ).replaceWith( r );
37
- $( '.the-tagcloud' ).css( 'display', v );
38
- }
39
- else {
40
- $( '#' + id ).after( r );
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
- });
43
  }
44
  })( jQuery );
45
 
46
- jQuery( document ).ready(function( $ ) {
47
- // collect taxonomies - code partly copied from WordPress
48
- var taxonomies = new Array();
49
- $( '.categorydiv' ).each(function(){
50
- var this_id = $( this ).attr( 'id' ), taxonomyParts, taxonomy;
51
-
52
- taxonomyParts = this_id.split( '-' );
53
- taxonomyParts.shift();
54
- taxonomy = taxonomyParts.join( '-' );
55
- taxonomies.push( taxonomy ); // store the taxonomy for future use
56
-
57
- // add our hidden field in the new category form - for each hierarchical taxonomy
58
- // to set the language when creating a new category
59
- $( '#' + taxonomy + '-add-submit' ).before( $( '<input />' )
60
- .attr( 'type', 'hidden' )
61
- .attr( 'id', taxonomy + '-lang' )
62
- .attr( 'name', 'term_lang_choice' )
63
- .attr( 'value', $( '.post_lang_choice' ).val() )
 
 
 
 
64
  );
65
- });
66
 
67
- // ajax for changing the post's language in the languages metabox
68
- $( '.post_lang_choice' ).change(function() {
69
- var value = $( this ).val();
70
- var lang = $( this ).children( 'option[value="' + value + '"]' ).attr( 'lang' );
71
- var dir = $( '.pll-translation-column > span[lang="' + lang + '"]' ).attr( 'dir' );
 
 
 
 
 
 
 
 
 
 
72
 
73
- var data = {
74
- action: 'post_lang_choice',
75
- lang: value,
76
- post_type: $( '#post_type' ).val(),
77
- taxonomies: taxonomies,
78
- post_id: $( '#post_ID' ).val(),
79
- _pll_nonce: $( '#_pll_nonce' ).val()
80
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
- $.post( ajaxurl, data , function( response ) {
83
- var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
84
- $.each( res.responses, function() {
85
- switch ( this.what ) {
86
- case 'translations': // translations fields
87
- $( '.translations' ).html( this.data );
88
- init_translations();
89
- break;
90
- case 'taxonomy': // categories metabox for posts
91
- var tax = this.data;
92
- $( '#' + tax + 'checklist' ).html( this.supplemental.all );
93
- $( '#' + tax + 'checklist-pop' ).html( this.supplemental.populars );
94
- $( '#new' + tax + '_parent' ).replaceWith( this.supplemental.dropdown );
95
- $( '#' + tax + '-lang' ).val( $( '.post_lang_choice' ).val() ); // hidden field
96
- break;
97
- case 'pages': // parent dropdown list for pages
98
- $( '#parent_id' ).html( this.data );
99
- break;
100
- case 'flag': // flag in front of the select dropdown
101
- $( '.pll-select-flag' ).html( this.data );
102
- break;
103
- case 'permalink': // Sample permalink
104
- var div = $( '#edit-slug-box' );
105
- if ( '-1' != this.data && div.children().length ) {
106
- div.html( this.data );
 
 
 
 
 
 
107
  }
108
- break;
109
  }
110
- });
 
 
 
111
 
112
- // modifies the language in the tag cloud
113
- $( '.tagcloud-link' ).each(function() {
 
 
 
 
114
  var id = $( this ).attr( 'id' );
115
- tagBox.get( id );
116
- });
117
-
118
- // Modifies the text direction
119
- $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
120
- $( '#content_ifr' ).contents().find( 'html' ).attr( 'lang', lang ).attr( 'dir', dir );
121
- $( '#content_ifr' ).contents().find( 'body' ).attr( 'dir', dir );
122
- });
123
- });
124
-
125
- // translations autocomplete input box
126
- function init_translations() {
127
- $( '.tr_lang' ).each(function(){
128
- var tr_lang = $( this ).attr( 'id' ).substring( 8 );
129
- var td = $( this ).parent().parent().siblings( '.pll-edit-column' );
130
-
131
- $( this ).autocomplete({
132
- minLength: 0,
133
-
134
- source: ajaxurl + '?action=pll_posts_not_translated' +
135
- '&post_language=' + $( '.post_lang_choice' ).val() +
136
- '&translation_language=' + tr_lang +
137
- '&post_type=' + $( '#post_type' ).val() +
138
- '&_pll_nonce=' + $( '#_pll_nonce' ).val(),
139
-
140
- select: function( event, ui ) {
141
- $( '#htr_lang_' + tr_lang ).val( ui.item.id );
142
- td.html( ui.item.link );
143
- },
144
- });
145
-
146
- // when the input box is emptied
147
- $( this ).blur(function() {
148
- if ( ! $( this ).val() ) {
149
- $( '#htr_lang_' + tr_lang ).val( 0 );
150
- td.html( td.siblings( '.hidden' ).children().clone() );
151
  }
152
- });
153
- });
154
- }
155
-
156
- init_translations();
157
-
158
- // Handle the response to a click on a Languages metabox button
159
- $( '#ml_box' ).on( 'click', '.pll-button', function(){
160
- var value = $( this ).hasClass( 'wp-ui-text-highlight' );
161
- var id = $( this ).attr( 'id' );
162
- var post_id = $( '#htr_lang_' + id.replace( 'pll_sync_post[', '' ).replace( ']', '' ) ).val();
163
-
164
- if ( 'undefined' == typeof( post_id ) || 0 == post_id || value || confirm( confirm_text ) ) {
165
- var data = {
166
- action: 'toggle_' + id,
167
- value: value,
168
- post_type: $( '#post_type' ).val(),
169
- _pll_nonce: $( '#_pll_nonce' ).val()
170
  }
171
-
172
- $.post( ajaxurl, data , function( response ){
173
- var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
174
- $.each( res.responses, function() {
175
- id = id.replace( '[', '\\[' ).replace( ']', '\\]' );
176
- $( '#' + id ).toggleClass( 'wp-ui-text-highlight' ).attr( 'title', this.data ).children( 'span' ).html( this.data );
177
- $( 'input[name="' + id + '"]' ).val( ! data['value'] );
178
- });
179
- });
180
- }
181
- });
182
- });
1
  // tag suggest in metabox
2
  (function( $ ){
3
+ $.ajaxPrefilter(
4
+ function( options, originalOptions, jqXHR ) {
5
+ if ( 'string' === typeof options.data && -1 !== options.url.indexOf( 'action=ajax-tag-search' ) && ( lang = $( '.post_lang_choice' ).val() ) ) {
6
+ options.data = 'lang=' + lang + '&' + options.data;
7
+ }
8
  }
9
+ );
10
  })( jQuery );
11
 
12
  // overrides tagBox.get
22
  tax: tax
23
  }
24
 
25
+ $.post(
26
+ ajaxurl,
27
+ data,
28
+ function( r, stat ) {
29
+ if ( 0 == r || 'success' != stat ) {
30
+ r = wpAjax.broken;
31
+ }
 
 
 
32
 
33
+ // @see code from WordPress core https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/js/tags-box.js#L291
34
+ // @see wp_generate_tag_cloud function which generate the escaped HTML https://github.com/WordPress/WordPress/blob/a02b5cc2a8eecb8e076fbb7cf4de7bd2ec8a8eb1/wp-includes/category-template.php#L966-L975
35
+ r = $( '<div />' ).addClass( 'the-tagcloud' ).attr( 'id', 'tagcloud-' + tax ).html( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
36
+ $( 'a', r ).click(
37
+ function(){
38
+ tagBox.flushTags( $( this ).closest( '.inside' ).children( '.tagsdiv' ), this );
39
+ return false;
40
+ }
41
+ );
42
+
43
+ // add an if else condition to allow modifying the tags outputed when switching the language
44
+ if ( v = $( '.the-tagcloud' ).css( 'display' ) ) {
45
+ $( '.the-tagcloud' ).replaceWith( r );
46
+ $( '.the-tagcloud' ).css( 'display', v );
47
+ }
48
+ else {
49
+ $( '#' + id ).after( r );
50
+ }
51
  }
52
+ );
53
  }
54
  })( jQuery );
55
 
56
+ jQuery( document ).ready(
57
+ function( $ ) {
58
+ // collect taxonomies - code partly copied from WordPress
59
+ var taxonomies = new Array();
60
+ $( '.categorydiv' ).each(
61
+ function(){
62
+ var this_id = $( this ).attr( 'id' ), taxonomyParts, taxonomy;
63
+
64
+ taxonomyParts = this_id.split( '-' );
65
+ taxonomyParts.shift();
66
+ taxonomy = taxonomyParts.join( '-' );
67
+ taxonomies.push( taxonomy ); // store the taxonomy for future use
68
+
69
+ // add our hidden field in the new category form - for each hierarchical taxonomy
70
+ // to set the language when creating a new category
71
+ $( '#' + taxonomy + '-add-submit' ).before(
72
+ $( '<input />' ).attr( 'type', 'hidden' )
73
+ .attr( 'id', taxonomy + '-lang' )
74
+ .attr( 'name', 'term_lang_choice' )
75
+ .attr( 'value', $( '.post_lang_choice' ).val() )
76
+ );
77
+ }
78
  );
 
79
 
80
+ // ajax for changing the post's language in the languages metabox
81
+ $( '.post_lang_choice' ).change(
82
+ function() {
83
+ var value = $( this ).val();
84
+ var lang = $( this ).children( 'option[value="' + value + '"]' ).attr( 'lang' );
85
+ var dir = $( '.pll-translation-column > span[lang="' + lang + '"]' ).attr( 'dir' );
86
+
87
+ var data = {
88
+ action: 'post_lang_choice',
89
+ lang: value,
90
+ post_type: $( '#post_type' ).val(),
91
+ taxonomies: taxonomies,
92
+ post_id: $( '#post_ID' ).val(),
93
+ _pll_nonce: $( '#_pll_nonce' ).val()
94
+ }
95
 
96
+ $.post(
97
+ ajaxurl,
98
+ data,
99
+ function( response ) {
100
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
101
+ $.each(
102
+ res.responses,
103
+ function() {
104
+ switch ( this.what ) {
105
+ case 'translations': // translations fields
106
+ // Data is built and come from server side and is well escaped when necessary
107
+ $( '.translations' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
108
+ init_translations();
109
+ break;
110
+ case 'taxonomy': // categories metabox for posts
111
+ var tax = this.data;
112
+ // @see wp_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L175
113
+ // @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/class-walker-category-checklist.php#L89-L111
114
+ $( '#' + tax + 'checklist' ).html( this.supplemental.all ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
115
+ // @see wp_popular_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L236
116
+ $( '#' + tax + 'checklist-pop' ).html( this.supplemental.populars ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
117
+ $( '#new' + tax + '_parent' ).replaceWith( this.supplemental.dropdown );
118
+ $( '#' + tax + '-lang' ).val( $( '.post_lang_choice' ).val() ); // hidden field
119
+ break;
120
+ case 'pages': // parent dropdown list for pages
121
+ // @see wp_dropdown_pages https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/post-template.php#L1186-L1208
122
+ // @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/class-walker-page-dropdown.php#L88
123
+ $( '#parent_id' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
124
+ break;
125
+ case 'flag': // flag in front of the select dropdown
126
+ // Data is built and come from server side and is well escaped when necessary
127
+ $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
128
+ break;
129
+ case 'permalink': // Sample permalink
130
+ var div = $( '#edit-slug-box' );
131
+ if ( '-1' != this.data && div.children().length ) {
132
+ // @see get_sample_permalink_html https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/post.php#L1425-L1454
133
+ div.html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
134
+ }
135
+ break;
136
+ }
137
+ }
138
+ );
139
+
140
+ // modifies the language in the tag cloud
141
+ $( '.tagcloud-link' ).each(
142
+ function() {
143
+ var id = $( this ).attr( 'id' );
144
+ tagBox.get( id );
145
+ }
146
+ );
147
+
148
+ // Modifies the text direction
149
+ $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
150
+ $( '#content_ifr' ).contents().find( 'html' ).attr( 'lang', lang ).attr( 'dir', dir );
151
+ $( '#content_ifr' ).contents().find( 'body' ).attr( 'dir', dir );
152
+ }
153
+ );
154
+ }
155
+ );
156
 
157
+ // translations autocomplete input box
158
+ function init_translations() {
159
+ $( '.tr_lang' ).each(
160
+ function(){
161
+ var tr_lang = $( this ).attr( 'id' ).substring( 8 );
162
+ var td = $( this ).parent().parent().siblings( '.pll-edit-column' );
163
+
164
+ $( this ).autocomplete(
165
+ {
166
+ minLength: 0,
167
+ source: ajaxurl + '?action=pll_posts_not_translated' +
168
+ '&post_language=' + $( '.post_lang_choice' ).val() +
169
+ '&translation_language=' + tr_lang +
170
+ '&post_type=' + $( '#post_type' ).val() +
171
+ '&_pll_nonce=' + $( '#_pll_nonce' ).val(),
172
+ select: function( event, ui ) {
173
+ $( '#htr_lang_' + tr_lang ).val( ui.item.id );
174
+ // ui.item.link is built and come from server side and is well escaped when necessary
175
+ td.html( ui.item.link ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
176
+ },
177
+ }
178
+ );
179
+
180
+ // when the input box is emptied
181
+ $( this ).blur(
182
+ function() {
183
+ if ( ! $( this ).val() ) {
184
+ $( '#htr_lang_' + tr_lang ).val( 0 );
185
+ // Value is retrieved from HTML already generated server side
186
+ td.html( td.siblings( '.hidden' ).children().clone() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
187
+ }
188
  }
189
+ );
190
  }
191
+ );
192
+ }
193
+
194
+ init_translations();
195
 
196
+ // Handle the response to a click on a Languages metabox button
197
+ $( '#ml_box' ).on(
198
+ 'click',
199
+ '.pll-button',
200
+ function(){
201
+ var value = $( this ).hasClass( 'wp-ui-text-highlight' );
202
  var id = $( this ).attr( 'id' );
203
+ var post_id = $( '#htr_lang_' + id.replace( 'pll_sync_post[', '' ).replace( ']', '' ) ).val();
204
+
205
+ if ( 'undefined' == typeof( post_id ) || 0 == post_id || value || confirm( confirm_text ) ) {
206
+ var data = {
207
+ action: 'toggle_' + id,
208
+ value: value,
209
+ post_type: $( '#post_type' ).val(),
210
+ _pll_nonce: $( '#_pll_nonce' ).val()
211
+ }
212
+
213
+ $.post(
214
+ ajaxurl,
215
+ data,
216
+ function( response ){
217
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
218
+ $.each(
219
+ res.responses,
220
+ function() {
221
+ id = id.replace( '[', '\\[' ).replace( ']', '\\]' );
222
+ $( '#' + id ).toggleClass( 'wp-ui-text-highlight' ).attr( 'title', this.data ).children( 'span' ).text( this.data );
223
+ $( 'input[name="' + id + '"]' ).val( ! data['value'] );
224
+ }
225
+ );
226
+ }
227
+ );
 
 
 
 
 
 
 
 
 
 
 
228
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  }
230
+ );
231
+ }
232
+ );
 
 
 
 
 
 
 
 
 
js/classic-editor.min.js CHANGED
@@ -1 +1 @@
1
- !function(t){t.ajaxPrefilter(function(a){"string"==typeof a.data&&-1!==a.url.indexOf("action=ajax-tag-search")&&(lang=t(".post_lang_choice").val())&&(a.data="lang="+lang+"&"+a.data)})}(jQuery),function(t){tagBox.get=function(a){var l=a.substr(a.indexOf("-")+1),n={action:"get-tagcloud",lang:t(".post_lang_choice").val(),tax:l};t.post(ajaxurl,n,function(n,e){0!=n&&"success"==e||(n=wpAjax.broken),n=t('<div id="tagcloud-'+l+'" class="the-tagcloud">'+n+"</div>"),t("a",n).click(function(){return tagBox.flushTags(t(this).closest(".inside").children(".tagsdiv"),this),!1}),(v=t(".the-tagcloud").css("display"))?(t(".the-tagcloud").replaceWith(n),t(".the-tagcloud").css("display",v)):t("#"+a).after(n)})}}(jQuery),jQuery(document).ready(function(t){function a(){t(".tr_lang").each(function(){var a=t(this).attr("id").substring(8),l=t(this).parent().parent().siblings(".pll-edit-column");t(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_posts_not_translated&post_language="+t(".post_lang_choice").val()+"&translation_language="+a+"&post_type="+t("#post_type").val()+"&_pll_nonce="+t("#_pll_nonce").val(),select:function(n,e){t("#htr_lang_"+a).val(e.item.id),l.html(e.item.link)}}),t(this).blur(function(){t(this).val()||(t("#htr_lang_"+a).val(0),l.html(l.siblings(".hidden").children().clone()))})})}var l=new Array;t(".categorydiv").each(function(){var a,n,e=t(this).attr("id");a=e.split("-"),a.shift(),n=a.join("-"),l.push(n),t("#"+n+"-add-submit").before(t("<input />").attr("type","hidden").attr("id",n+"-lang").attr("name","term_lang_choice").attr("value",t(".post_lang_choice").val()))}),t(".post_lang_choice").change(function(){var n=t(this).val(),e=t(this).children('option[value="'+n+'"]').attr("lang"),i=t('.pll-translation-column > span[lang="'+e+'"]').attr("dir"),s={action:"post_lang_choice",lang:n,post_type:t("#post_type").val(),taxonomies:l,post_id:t("#post_ID").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,s,function(l){var n=wpAjax.parseAjaxResponse(l,"ajax-response");t.each(n.responses,function(){switch(this.what){case"translations":t(".translations").html(this.data),a();break;case"taxonomy":var l=this.data;t("#"+l+"checklist").html(this.supplemental.all),t("#"+l+"checklist-pop").html(this.supplemental.populars),t("#new"+l+"_parent").replaceWith(this.supplemental.dropdown),t("#"+l+"-lang").val(t(".post_lang_choice").val());break;case"pages":t("#parent_id").html(this.data);break;case"flag":t(".pll-select-flag").html(this.data);break;case"permalink":var n=t("#edit-slug-box");"-1"!=this.data&&n.children().length&&n.html(this.data)}}),t(".tagcloud-link").each(function(){var a=t(this).attr("id");tagBox.get(a)}),t("body").removeClass("pll-dir-rtl").removeClass("pll-dir-ltr").addClass("pll-dir-"+i),t("#content_ifr").contents().find("html").attr("lang",e).attr("dir",i),t("#content_ifr").contents().find("body").attr("dir",i)})}),a(),t("#ml_box").on("click",".pll-button",function(){var a=t(this).hasClass("wp-ui-text-highlight"),l=t(this).attr("id"),n=t("#htr_lang_"+l.replace("pll_sync_post[","").replace("]","")).val();if("undefined"==typeof n||0==n||a||confirm(confirm_text)){var e={action:"toggle_"+l,value:a,post_type:t("#post_type").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,e,function(a){var n=wpAjax.parseAjaxResponse(a,"ajax-response");t.each(n.responses,function(){l=l.replace("[","\\[").replace("]","\\]"),t("#"+l).toggleClass("wp-ui-text-highlight").attr("title",this.data).children("span").html(this.data),t('input[name="'+l+'"]').val(!e.value)})})}})});
1
+ !function(t){t.ajaxPrefilter(function(a,l,n){"string"==typeof a.data&&-1!==a.url.indexOf("action=ajax-tag-search")&&(lang=t(".post_lang_choice").val())&&(a.data="lang="+lang+"&"+a.data)})}(jQuery),function(t){tagBox.get=function(a){var l=a.substr(a.indexOf("-")+1),n={action:"get-tagcloud",lang:t(".post_lang_choice").val(),tax:l};t.post(ajaxurl,n,function(n,e){0!=n&&"success"==e||(n=wpAjax.broken),n=t("<div />").addClass("the-tagcloud").attr("id","tagcloud-"+l).html(n),t("a",n).click(function(){return tagBox.flushTags(t(this).closest(".inside").children(".tagsdiv"),this),!1}),(v=t(".the-tagcloud").css("display"))?(t(".the-tagcloud").replaceWith(n),t(".the-tagcloud").css("display",v)):t("#"+a).after(n)})}}(jQuery),jQuery(document).ready(function(t){var a=new Array;function l(){t(".tr_lang").each(function(){var a=t(this).attr("id").substring(8),l=t(this).parent().parent().siblings(".pll-edit-column");t(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_posts_not_translated&post_language="+t(".post_lang_choice").val()+"&translation_language="+a+"&post_type="+t("#post_type").val()+"&_pll_nonce="+t("#_pll_nonce").val(),select:function(n,e){t("#htr_lang_"+a).val(e.item.id),l.html(e.item.link)}}),t(this).blur(function(){t(this).val()||(t("#htr_lang_"+a).val(0),l.html(l.siblings(".hidden").children().clone()))})})}t(".categorydiv").each(function(){var l,n;(l=t(this).attr("id").split("-")).shift(),n=l.join("-"),a.push(n),t("#"+n+"-add-submit").before(t("<input />").attr("type","hidden").attr("id",n+"-lang").attr("name","term_lang_choice").attr("value",t(".post_lang_choice").val()))}),t(".post_lang_choice").change(function(){var n=t(this).val(),e=t(this).children('option[value="'+n+'"]').attr("lang"),i=t('.pll-translation-column > span[lang="'+e+'"]').attr("dir"),s={action:"post_lang_choice",lang:n,post_type:t("#post_type").val(),taxonomies:a,post_id:t("#post_ID").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,s,function(a){var n=wpAjax.parseAjaxResponse(a,"ajax-response");t.each(n.responses,function(){switch(this.what){case"translations":t(".translations").html(this.data),l();break;case"taxonomy":var a=this.data;t("#"+a+"checklist").html(this.supplemental.all),t("#"+a+"checklist-pop").html(this.supplemental.populars),t("#new"+a+"_parent").replaceWith(this.supplemental.dropdown),t("#"+a+"-lang").val(t(".post_lang_choice").val());break;case"pages":t("#parent_id").html(this.data);break;case"flag":t(".pll-select-flag").html(this.data);break;case"permalink":var n=t("#edit-slug-box");"-1"!=this.data&&n.children().length&&n.html(this.data)}}),t(".tagcloud-link").each(function(){var a=t(this).attr("id");tagBox.get(a)}),t("body").removeClass("pll-dir-rtl").removeClass("pll-dir-ltr").addClass("pll-dir-"+i),t("#content_ifr").contents().find("html").attr("lang",e).attr("dir",i),t("#content_ifr").contents().find("body").attr("dir",i)})}),l(),t("#ml_box").on("click",".pll-button",function(){var a=t(this).hasClass("wp-ui-text-highlight"),l=t(this).attr("id"),n=t("#htr_lang_"+l.replace("pll_sync_post[","").replace("]","")).val();if(void 0===n||0==n||a||confirm(confirm_text)){var e={action:"toggle_"+l,value:a,post_type:t("#post_type").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,e,function(a){var n=wpAjax.parseAjaxResponse(a,"ajax-response");t.each(n.responses,function(){l=l.replace("[","\\[").replace("]","\\]"),t("#"+l).toggleClass("wp-ui-text-highlight").attr("title",this.data).children("span").text(this.data),t('input[name="'+l+'"]').val(!e.value)})})}})});
js/nav-menu.js CHANGED
@@ -1,68 +1,95 @@
1
- jQuery( document ).ready(function( $ ) {
2
- $( '#update-nav-menu' ).bind( 'click', function( e ) {
3
- if ( e.target && e.target.className && -1 != e.target.className.indexOf( 'item-edit' ) ) {
4
- $( "input[value='#pll_switcher'][type=text]" ).parent().parent().parent().each(function(){
5
- var item = $( this ).attr( 'id' ).substring( 19 );
6
- $( this ).children( 'p:not( .field-move )' ).remove(); // remove default fields we don't need
 
 
 
 
7
 
8
- h = $( '<input>' ).attr({
9
- type: 'hidden',
10
- id: 'edit-menu-item-title-' + item,
11
- name: 'menu-item-title[' + item + ']',
12
- value: pll_data.title
13
- });
14
- $( this ).append( h );
 
 
 
 
 
15
 
16
- h = $( '<input>' ).attr({
17
- type: 'hidden',
18
- id: 'edit-menu-item-url-' + item,
19
- name: 'menu-item-url[' + item + ']',
20
- value: '#pll_switcher'
21
- });
22
- $( this ).append( h );
 
 
23
 
24
- // a hidden field which exits only if our jQuery code has been executed
25
- h = $( '<input>' ).attr({
26
- type: 'hidden',
27
- id: 'edit-menu-item-pll-detect-' + item,
28
- name: 'menu-item-pll-detect[' + item + ']',
29
- value: 1
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',
43
- id: 'edit-menu-item-' + ids[ i ] + '-' + item,
44
- name: 'menu-item-' + ids[ i ] + '[' + item + ']',
45
- value: 1
46
- });
47
- if ( ( typeof( pll_data.val[ item ] ) != 'undefined' && pll_data.val[ item ][ ids[ i ] ] == 1 ) || ( typeof( pll_data.val[ item ] ) == 'undefined' && ids[ i ] == 'show_names' ) ) { // show_names as default value
48
- cb.prop( 'checked', true );
49
- }
50
- label.prepend( cb );
51
- }
52
- });
 
 
 
 
 
53
 
54
- // disallow unchecking both show names and show flags
55
- $( '.menu-item-data-object-id' ).each(function() {
56
- var id = $( this ).val();
57
- var options = ['names-', 'flags-'];
58
- $.each( options, function( i, v ) {
59
- $( '#edit-menu-item-show_' + v + id ).change(function() {
60
- if ( 'checked' != $( this ).attr( 'checked' ) ) {
61
- $( '#edit-menu-item-show_' + options[ 1 - i ] + id ).prop( 'checked', true );
 
 
 
 
 
 
 
 
 
62
  }
63
- });
64
- });
65
- });
66
- }
67
- });
68
- });
1
+ jQuery( document ).ready(
2
+ function( $ ) {
3
+ $( '#update-nav-menu' ).bind(
4
+ 'click',
5
+ function( e ) {
6
+ if ( e.target && e.target.className && -1 != e.target.className.indexOf( 'item-edit' ) ) {
7
+ $( "input[value='#pll_switcher'][type=text]" ).parent().parent().parent().each(
8
+ function(){
9
+ var item = $( this ).attr( 'id' ).substring( 19 );
10
+ $( this ).children( 'p:not( .field-move )' ).remove(); // remove default fields we don't need
11
 
12
+ // item is a number part of id of parent menu item built by WordPress
13
+ // pll_data is built server side with i18n strings without HTML and data retrieved from post meta
14
+ // the usage of attr method is safe before append call.
15
+ h = $( '<input>' ).attr(
16
+ {
17
+ type: 'hidden',
18
+ id: 'edit-menu-item-title-' + item,
19
+ name: 'menu-item-title[' + item + ']',
20
+ value: pll_data.title
21
+ }
22
+ );
23
+ $( this ).append( h ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
24
 
25
+ h = $( '<input>' ).attr(
26
+ {
27
+ type: 'hidden',
28
+ id: 'edit-menu-item-url-' + item,
29
+ name: 'menu-item-url[' + item + ']',
30
+ value: '#pll_switcher'
31
+ }
32
+ );
33
+ $( this ).append( h ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
34
 
35
+ // a hidden field which exits only if our jQuery code has been executed
36
+ h = $( '<input>' ).attr(
37
+ {
38
+ type: 'hidden',
39
+ id: 'edit-menu-item-pll-detect-' + item,
40
+ name: 'menu-item-pll-detect[' + item + ']',
41
+ value: 1
42
+ }
43
+ );
44
+ $( this ).append( h ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
45
 
46
+ ids = Array( 'hide_if_no_translation', 'hide_current', 'force_home', 'show_flags', 'show_names', 'dropdown' ); // reverse order
47
 
48
+ // add the fields
49
+ for ( var i = 0, idsLength = ids.length; i < idsLength; i++ ) {
50
+ p = $( '<p>' ).attr( 'class', 'description' );
51
+ $( this ).prepend( p );
52
+ // item is a number part of id of parent menu item built by WordPress
53
+ // pll_data is built server side with i18n strings without HTML
54
+ label = $( '<label>' ).attr( 'for', 'edit-menu-item-' + ids[ i ] + '-' + item ).text( ' ' + pll_data.strings[ ids[ i ] ] );
55
+ p.append( label ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
56
+ cb = $( '<input>' ).attr(
57
+ {
58
+ type: 'checkbox',
59
+ id: 'edit-menu-item-' + ids[ i ] + '-' + item,
60
+ name: 'menu-item-' + ids[ i ] + '[' + item + ']',
61
+ value: 1
62
+ }
63
+ );
64
+ if ( ( typeof( pll_data.val[ item ] ) != 'undefined' && pll_data.val[ item ][ ids[ i ] ] == 1 ) || ( typeof( pll_data.val[ item ] ) == 'undefined' && ids[ i ] == 'show_names' ) ) { // show_names as default value
65
+ cb.prop( 'checked', true );
66
+ }
67
+ label.prepend( cb );
68
+ }
69
+ }
70
+ );
71
 
72
+ // disallow unchecking both show names and show flags
73
+ $( '.menu-item-data-object-id' ).each(
74
+ function() {
75
+ var id = $( this ).val();
76
+ var options = ['names-', 'flags-'];
77
+ $.each(
78
+ options,
79
+ function( i, v ) {
80
+ $( '#edit-menu-item-show_' + v + id ).change(
81
+ function() {
82
+ if ( 'checked' != $( this ).attr( 'checked' ) ) {
83
+ $( '#edit-menu-item-show_' + options[ 1 - i ] + id ).prop( 'checked', true );
84
+ }
85
+ }
86
+ );
87
+ }
88
+ );
89
  }
90
+ );
91
+ }
92
+ }
93
+ );
94
+ }
95
+ );
js/nav-menu.min.js CHANGED
@@ -1 +1 @@
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)}})})})}})});
1
+ jQuery(document).ready(function(e){e("#update-nav-menu").bind("click",function(t){t.target&&t.target.className&&-1!=t.target.className.indexOf("item-edit")&&(e("input[value='#pll_switcher'][type=text]").parent().parent().parent().each(function(){var t=e(this).attr("id").substring(19);e(this).children("p:not( .field-move )").remove(),h=e("<input>").attr({type:"hidden",id:"edit-menu-item-title-"+t,name:"menu-item-title["+t+"]",value:pll_data.title}),e(this).append(h),h=e("<input>").attr({type:"hidden",id:"edit-menu-item-url-"+t,name:"menu-item-url["+t+"]",value:"#pll_switcher"}),e(this).append(h),h=e("<input>").attr({type:"hidden",id:"edit-menu-item-pll-detect-"+t,name:"menu-item-pll-detect["+t+"]",value:1}),e(this).append(h),ids=Array("hide_if_no_translation","hide_current","force_home","show_flags","show_names","dropdown");for(var i=0,a=ids.length;i<a;i++)p=e("<p>").attr("class","description"),e(this).prepend(p),label=e("<label>").attr("for","edit-menu-item-"+ids[i]+"-"+t).text(" "+pll_data.strings[ids[i]]),p.append(label),cb=e("<input>").attr({type:"checkbox",id:"edit-menu-item-"+ids[i]+"-"+t,name:"menu-item-"+ids[i]+"["+t+"]",value:1}),(void 0!==pll_data.val[t]&&1==pll_data.val[t][ids[i]]||void 0===pll_data.val[t]&&"show_names"==ids[i])&&cb.prop("checked",!0),label.prepend(cb)}),e(".menu-item-data-object-id").each(function(){var t=e(this).val(),i=["names-","flags-"];e.each(i,function(a,n){e("#edit-menu-item-show_"+n+t).change(function(){"checked"!=e(this).attr("checked")&&e("#edit-menu-item-show_"+i[1-a]+t).prop("checked",!0)})})}))})});
js/post.js CHANGED
@@ -1,68 +1,91 @@
1
  /**
2
  * Tag suggest in quick edit
3
  */
 
4
  (function( $ ){
5
- $.ajaxPrefilter(function( options, originalOptions, jqXHR ) {
6
- if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=ajax-tag-search' ) && ( lang = $( ':input[name="inline_lang_choice"]' ).val() ) ) {
7
- options.data = 'lang=' + lang + '&' + options.data;
 
 
8
  }
9
- });
10
  })( jQuery );
11
 
12
  /**
13
  * Quick edit
14
  */
15
  (function( $ ) {
16
- $( document ).bind( 'DOMNodeInserted', function( e ) {
17
- var t = $( e.target );
18
-
19
- // WP inserts the quick edit from
20
- if ( 'inline-edit' == t.attr( 'id' ) ) {
21
- var post_id = t.prev().attr( 'id' ).replace( "post-", "" );
22
-
23
- if ( post_id > 0 ) {
24
- // language dropdown
25
- var select = t.find( ':input[name="inline_lang_choice"]' );
26
- var lang = $( '#lang_' + post_id ).html();
27
- select.val( lang ); // populates the dropdown
28
-
29
- filter_terms( lang ); // initial filter for category checklist
30
- filter_pages( lang ); // initial filter for parent dropdown
31
-
32
- // modify category checklist an parent dropdown on language change
33
- select.change(function() {
34
- filter_terms( $( this ).val() );
35
- filter_pages( $( this ).val() );
36
- });
 
 
 
 
 
37
  }
38
- }
39
 
40
- // filter category checklist
41
- function filter_terms( lang ) {
42
- if ( "undefined" != typeof( pll_term_languages ) ) {
43
- $.each( pll_term_languages, function( lg, term_tax ) {
44
- $.each( term_tax, function( tax, terms ) {
45
- $.each( terms, function( i ) {
46
- id = '#' + tax + '-' + pll_term_languages[ lg ][ tax ][ i ];
47
- lang == lg ? $( id ).show() : $( id ).hide();
48
- });
49
- });
50
- });
 
 
 
 
 
 
 
 
 
 
51
  }
52
- }
53
 
54
- // filter parent page dropdown list
55
- function filter_pages( lang ) {
56
- if ( "undefined" != typeof( pll_page_languages ) ) {
57
- $.each( pll_page_languages, function( lg, pages ) {
58
- $.each( pages, function( i ) {
59
- v = $( '#post_parent option[value="' + pll_page_languages[ lg ][ i ] + '"]' );
60
- lang == lg ? v.show() : v.hide();
61
- });
62
- });
 
 
 
 
 
 
 
63
  }
64
  }
65
- });
66
  })( jQuery );
67
 
68
  /**
@@ -70,43 +93,54 @@
70
  * Acts on ajaxSuccess event
71
  */
72
  (function( $ ) {
73
- $( document ).ajaxSuccess(function( event, xhr, settings ) {
74
- function update_rows( post_id ) {
75
- // collect old translations
76
- var translations = new Array;
77
- $( '.translation_' + post_id ).each(function() {
78
- translations.push( $( this ).parent().parent().attr( 'id' ).substring( 5 ) );
79
- });
80
-
81
- var data = {
82
- action: 'pll_update_post_rows',
83
- post_id: post_id,
84
- translations: translations.join( ',' ),
85
- post_type: $( "input[name='post_type']" ).val(),
86
- screen: $( "input[name='screen']" ).val(),
87
- _pll_nonce: $( "input[name='_inline_edit']" ).val() // reuse quick edit nonce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
 
90
- // get the modified rows in ajax and update them
91
- $.post( ajaxurl, data, function( response ) {
92
- if ( response ) {
93
- var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
94
- $.each( res.responses, function() {
95
- if ( 'row' == this.what ) {
96
- $( "#post-" + this.supplemental.post_id ).replaceWith( this.data );
97
- }
98
- });
99
  }
100
- });
101
- }
102
-
103
- if ( 'string' == typeof( settings.data ) ) { // Need to check the type due to block editor sometime sending FormData objects
104
- var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
105
- if ( 'undefined' != typeof( data['action'] ) && 'inline-save' == data['action'] ) {
106
- update_rows( data['post_ID'] );
107
  }
108
  }
109
- });
110
  })( jQuery );
111
 
112
  /**
@@ -114,61 +148,90 @@
114
  * When clicking on attach link, filters find post list per media language
115
  */
116
  (function( $ ){
117
- $.ajaxPrefilter( function ( options, originalOptions, jqXHR ) {
118
- if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=find_posts' ) ) {
119
- options.data = 'pll_post_id=' + $( '#affected' ).val() + '&' + options.data;
 
 
120
  }
121
- });
122
- })( jQuery )
123
-
124
 
125
  /**
126
  * Bulk translate
127
  */
128
- jQuery( document ).ready( function( $ ) {
129
- var t = this;
 
130
 
131
- $( '.editinline' ).click( function(){
132
- $( '#pll-translate' ).find( '.cancel' ).click(); // Close the form on quick edit
133
- } );
134
-
135
- $( '#doaction, #doaction2' ).click( function( e ){
136
- t.whichBulkButtonId = $( this ).attr( 'id' );
137
- var n = t.whichBulkButtonId.substr( 2 );
138
 
139
- if ( 'pll_translate' === $( 'select[name="' + n + '"]' ).val() ) {
140
- e.preventDefault();
 
 
141
 
142
- if ( typeof inlineEditPost !== 'undefined' ) { // Not available for media.
143
- inlineEditPost.revert(); // Close Bulk edit and Quick edit if open.
144
- }
145
 
146
- $( '#pll-translate td' ).attr( 'colspan', $( 'th:visible, td:visible', '.widefat:first thead' ).length );
147
- $( 'table.widefat tbody' ).prepend( $( '#pll-translate' ) ).prepend( '<tr class="hidden"></tr>' ); // The hidden tr allows to keep the background color
148
- } else {
149
- $( '#pll-translate' ).find( '.cancel' ).click(); // Close the form on any other bulk action
150
- }
151
- } );
152
-
153
- // Cancel
154
- $( '#pll-translate' ).on( 'click', '.cancel', function(){
155
- $( '#pll-translate' ).siblings( '.hidden' ).remove();
156
- $( '#pll-bulk-translate' ).append( $( '#pll-translate' ) );
157
-
158
- // Move focus back to the Bulk Action button that was activated.
159
- $( '#' + t.whichBulkButtonId ).focus();
160
- } );
161
-
162
- // Act when pressing enter or esc
163
- $( '#pll-translate' ).keydown( function( event ){
164
- if ( 13 === event.keyCode && ! $( event.target ).hasClass( 'cancel' ) ) {
165
- event.preventDefault();
166
- $( this ).find( 'input[type=submit]' ).click();
167
- }
168
 
169
- if ( 27 === event.keyCode ) {
170
- event.preventDefault();
171
- $( this ).find( '.cancel' ).click();
172
- }
173
- } );
174
- } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  /**
2
  * Tag suggest in quick edit
3
  */
4
+
5
  (function( $ ){
6
+ $.ajaxPrefilter(
7
+ function( options, originalOptions, jqXHR ) {
8
+ if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=ajax-tag-search' ) && ( lang = $( ':input[name="inline_lang_choice"]' ).val() ) ) {
9
+ options.data = 'lang=' + lang + '&' + options.data;
10
+ }
11
  }
12
+ );
13
  })( jQuery );
14
 
15
  /**
16
  * Quick edit
17
  */
18
  (function( $ ) {
19
+ $( document ).bind(
20
+ 'DOMNodeInserted',
21
+ function( e ) {
22
+ var t = $( e.target );
23
+
24
+ // WP inserts the quick edit from
25
+ if ( 'inline-edit' == t.attr( 'id' ) ) {
26
+ var post_id = t.prev().attr( 'id' ).replace( "post-", "" );
27
+
28
+ if ( post_id > 0 ) {
29
+ // language dropdown
30
+ var select = t.find( ':input[name="inline_lang_choice"]' );
31
+ var lang = $( '#lang_' + post_id ).html();
32
+ select.val( lang ); // populates the dropdown
33
+
34
+ filter_terms( lang ); // initial filter for category checklist
35
+ filter_pages( lang ); // initial filter for parent dropdown
36
+
37
+ // modify category checklist an parent dropdown on language change
38
+ select.change(
39
+ function() {
40
+ filter_terms( $( this ).val() );
41
+ filter_pages( $( this ).val() );
42
+ }
43
+ );
44
+ }
45
  }
 
46
 
47
+ // filter category checklist
48
+ function filter_terms( lang ) {
49
+ if ( "undefined" != typeof( pll_term_languages ) ) {
50
+ $.each(
51
+ pll_term_languages,
52
+ function( lg, term_tax ) {
53
+ $.each(
54
+ term_tax,
55
+ function( tax, terms ) {
56
+ $.each(
57
+ terms,
58
+ function( i ) {
59
+ id = '#' + tax + '-' + pll_term_languages[ lg ][ tax ][ i ];
60
+ lang == lg ? $( id ).show() : $( id ).hide();
61
+ }
62
+ );
63
+ }
64
+ );
65
+ }
66
+ );
67
+ }
68
  }
 
69
 
70
+ // filter parent page dropdown list
71
+ function filter_pages( lang ) {
72
+ if ( "undefined" != typeof( pll_page_languages ) ) {
73
+ $.each(
74
+ pll_page_languages,
75
+ function( lg, pages ) {
76
+ $.each(
77
+ pages,
78
+ function( i ) {
79
+ v = $( '#post_parent option[value="' + pll_page_languages[ lg ][ i ] + '"]' );
80
+ lang == lg ? v.show() : v.hide();
81
+ }
82
+ );
83
+ }
84
+ );
85
+ }
86
  }
87
  }
88
+ );
89
  })( jQuery );
90
 
91
  /**
93
  * Acts on ajaxSuccess event
94
  */
95
  (function( $ ) {
96
+ $( document ).ajaxSuccess(
97
+ function( event, xhr, settings ) {
98
+ function update_rows( post_id ) {
99
+ // collect old translations
100
+ var translations = new Array();
101
+ $( '.translation_' + post_id ).each(
102
+ function() {
103
+ translations.push( $( this ).parent().parent().attr( 'id' ).substring( 5 ) );
104
+ }
105
+ );
106
+
107
+ var data = {
108
+ action: 'pll_update_post_rows',
109
+ post_id: post_id,
110
+ translations: translations.join( ',' ),
111
+ post_type: $( "input[name='post_type']" ).val(),
112
+ screen: $( "input[name='screen']" ).val(),
113
+ _pll_nonce: $( "input[name='_inline_edit']" ).val() // reuse quick edit nonce
114
+ };
115
+
116
+ // get the modified rows in ajax and update them
117
+ $.post(
118
+ ajaxurl,
119
+ data,
120
+ function( response ) {
121
+ if ( response ) {
122
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
123
+ $.each(
124
+ res.responses,
125
+ function() {
126
+ if ( 'row' == this.what ) {
127
+ $( "#post-" + this.supplemental.post_id ).replaceWith( this.data );
128
+ }
129
+ }
130
+ );
131
+ }
132
+ }
133
+ );
134
  }
135
 
136
+ if ( 'string' == typeof( settings.data ) ) { // Need to check the type due to block editor sometime sending FormData objects
137
+ var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
138
+ if ( 'undefined' != typeof( data['action'] ) && 'inline-save' == data['action'] ) {
139
+ update_rows( data['post_ID'] );
 
 
 
 
 
140
  }
 
 
 
 
 
 
 
141
  }
142
  }
143
+ );
144
  })( jQuery );
145
 
146
  /**
148
  * When clicking on attach link, filters find post list per media language
149
  */
150
  (function( $ ){
151
+ $.ajaxPrefilter(
152
+ function ( options, originalOptions, jqXHR ) {
153
+ if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=find_posts' ) ) {
154
+ options.data = 'pll_post_id=' + $( '#affected' ).val() + '&' + options.data;
155
+ }
156
  }
157
+ );
158
+ })( jQuery );
 
159
 
160
  /**
161
  * Bulk translate
162
  */
163
+ jQuery( document ).ready(
164
+ function( $ ) {
165
+ var t = this;
166
 
167
+ $( '.editinline' ).click(
168
+ function(){
169
+ $( '#pll-translate' ).find( '.cancel' ).click(); // Close the form on quick edit
170
+ }
171
+ );
 
 
172
 
173
+ $( '#doaction, #doaction2' ).click(
174
+ function( e ){
175
+ t.whichBulkButtonId = $( this ).attr( 'id' );
176
+ var n = t.whichBulkButtonId.substr( 2 );
177
 
178
+ if ( 'pll_translate' === $( 'select[name="' + n + '"]' ).val() ) {
179
+ e.preventDefault();
 
180
 
181
+ if ( typeof inlineEditPost !== 'undefined' ) { // Not available for media.
182
+ inlineEditPost.revert(); // Close Bulk edit and Quick edit if open.
183
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
+ $( '#pll-translate td' ).attr( 'colspan', $( 'th:visible, td:visible', '.widefat:first thead' ).length );
186
+ $( 'table.widefat tbody' ).prepend( $( '#pll-translate' ) ).prepend( '<tr class="hidden"></tr>' ); // The hidden tr allows to keep the background color
187
+ } else {
188
+ $( '#pll-translate' ).find( '.cancel' ).click();
189
+ }
190
+ }
191
+ );
192
+
193
+ // Cancel
194
+ $( '#pll-translate' ).on(
195
+ 'click',
196
+ '.cancel',
197
+ function(){
198
+ // Close the form on any other bulk action
199
+ $( '#pll-translate' ).siblings( '.hidden' ).remove();
200
+ // #pll-translate is built and come from server side and is well escaped when necessary
201
+ $( '#pll-bulk-translate' ).append( $( '#pll-translate' ) ); //phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
202
+
203
+ // Move focus back to the Bulk Action button that was activated.
204
+ $( '#' + t.whichBulkButtonId ).focus();
205
+ }
206
+ );
207
+
208
+ // Act when pressing enter or esc
209
+ $( '#pll-translate' ).keydown(
210
+ function( event ){
211
+ if ( 13 === event.keyCode && ! $( event.target ).hasClass( 'cancel' ) ) {
212
+ event.preventDefault();
213
+ $( this ).find( 'input[type=submit]' ).click();
214
+ }
215
+ if ( 27 === event.keyCode ) {
216
+ event.preventDefault();
217
+ $( this ).find( '.cancel' ).click();
218
+ }
219
+ }
220
+ );
221
+
222
+ // Clean DOM in case of file download
223
+ $( '#posts-filter' ).on(
224
+ 'submit',
225
+ function() {
226
+ $( '.settings-error' ).remove();
227
+ setTimeout(
228
+ function() {
229
+ $( 'input[type=checkbox]:checked' ).attr( 'checked', false );
230
+ $( '#pll-translate' ).find( '.cancel' ).trigger( 'click' );
231
+ },
232
+ 500
233
+ );
234
+ }
235
+ );
236
+ }
237
+ );
js/post.min.js CHANGED
@@ -1 +1 @@
1
- !function(t){t.ajaxPrefilter(function(n){"string"==typeof n.data&&-1!==n.data.indexOf("action=ajax-tag-search")&&(lang=t(':input[name="inline_lang_choice"]').val())&&(n.data="lang="+lang+"&"+n.data)})}(jQuery),function(t){t(document).bind("DOMNodeInserted",function(n){function a(n){"undefined"!=typeof pll_term_languages&&t.each(pll_term_languages,function(a,e){t.each(e,function(e,i){t.each(i,function(i){id="#"+e+"-"+pll_term_languages[a][e][i],n==a?t(id).show():t(id).hide()})})})}function e(n){"undefined"!=typeof pll_page_languages&&t.each(pll_page_languages,function(a,e){t.each(e,function(e){v=t('#post_parent option[value="'+pll_page_languages[a][e]+'"]'),n==a?v.show():v.hide()})})}var i=t(n.target);if("inline-edit"==i.attr("id")){var l=i.prev().attr("id").replace("post-","");if(l>0){var c=i.find(':input[name="inline_lang_choice"]'),s=t("#lang_"+l).html();c.val(s),a(s),e(s),c.change(function(){a(t(this).val()),e(t(this).val())})}}})}(jQuery),function(t){t(document).ajaxSuccess(function(n,a,e){function i(n){var a=new Array;t(".translation_"+n).each(function(){a.push(t(this).parent().parent().attr("id").substring(5))});var e={action:"pll_update_post_rows",post_id:n,translations:a.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(n){if(n){var a=wpAjax.parseAjaxResponse(n,"ajax-response");t.each(a.responses,function(){"row"==this.what&&t("#post-"+this.supplemental.post_id).replaceWith(this.data)})}})}if("string"==typeof e.data){var l=wpAjax.unserialize(e.data);"undefined"!=typeof l.action&&"inline-save"==l.action&&i(l.post_ID)}})}(jQuery),function(t){t.ajaxPrefilter(function(n){"string"==typeof n.data&&-1!==n.data.indexOf("action=find_posts")&&(n.data="pll_post_id="+t("#affected").val()+"&"+n.data)})}(jQuery),jQuery(document).ready(function(t){var n=this;t(".editinline").click(function(){t("#pll-translate").find(".cancel").click()}),t("#doaction, #doaction2").click(function(a){n.whichBulkButtonId=t(this).attr("id");var e=n.whichBulkButtonId.substr(2);"pll_translate"===t('select[name="'+e+'"]').val()?(a.preventDefault(),"undefined"!=typeof inlineEditPost&&inlineEditPost.revert(),t("#pll-translate td").attr("colspan",t("th:visible, td:visible",".widefat:first thead").length),t("table.widefat tbody").prepend(t("#pll-translate")).prepend('<tr class="hidden"></tr>')):t("#pll-translate").find(".cancel").click()}),t("#pll-translate").on("click",".cancel",function(){t("#pll-translate").siblings(".hidden").remove(),t("#pll-bulk-translate").append(t("#pll-translate")),t("#"+n.whichBulkButtonId).focus()}),t("#pll-translate").keydown(function(n){13!==n.keyCode||t(n.target).hasClass("cancel")||(n.preventDefault(),t(this).find("input[type=submit]").click()),27===n.keyCode&&(n.preventDefault(),t(this).find(".cancel").click())})});
1
+ !function(t){t.ajaxPrefilter(function(n){"string"==typeof n.data&&-1!==n.data.indexOf("action=ajax-tag-search")&&(lang=t(':input[name="inline_lang_choice"]').val())&&(n.data="lang="+lang+"&"+n.data)})}(jQuery),function(t){t(document).bind("DOMNodeInserted",function(n){function e(n){"undefined"!=typeof pll_term_languages&&t.each(pll_term_languages,function(e,a){t.each(a,function(a,i){t.each(i,function(i){id="#"+a+"-"+pll_term_languages[e][a][i],n==e?t(id).show():t(id).hide()})})})}function a(n){"undefined"!=typeof pll_page_languages&&t.each(pll_page_languages,function(e,a){t.each(a,function(a){v=t('#post_parent option[value="'+pll_page_languages[e][a]+'"]'),n==e?v.show():v.hide()})})}var i=t(n.target);if("inline-edit"==i.attr("id")){var l=i.prev().attr("id").replace("post-","");if(l>0){var c=i.find(':input[name="inline_lang_choice"]'),o=t("#lang_"+l).html();c.val(o),e(o),a(o),c.change(function(){e(t(this).val()),a(t(this).val())})}}})}(jQuery),function(t){t(document).ajaxSuccess(function(n,e,a){function i(n){var e=new Array;t(".translation_"+n).each(function(){e.push(t(this).parent().parent().attr("id").substring(5))});var a={action:"pll_update_post_rows",post_id:n,translations:e.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,a,function(n){if(n){var e=wpAjax.parseAjaxResponse(n,"ajax-response");t.each(e.responses,function(){"row"==this.what&&t("#post-"+this.supplemental.post_id).replaceWith(this.data)})}})}if("string"==typeof a.data){var l=wpAjax.unserialize(a.data);"undefined"!=typeof l.action&&"inline-save"==l.action&&i(l.post_ID)}})}(jQuery),function(t){t.ajaxPrefilter(function(n){"string"==typeof n.data&&-1!==n.data.indexOf("action=find_posts")&&(n.data="pll_post_id="+t("#affected").val()+"&"+n.data)})}(jQuery),jQuery(document).ready(function(t){var n=this;t(".editinline").click(function(){t("#pll-translate").find(".cancel").click()}),t("#doaction, #doaction2").click(function(e){n.whichBulkButtonId=t(this).attr("id");var a=n.whichBulkButtonId.substr(2);"pll_translate"===t('select[name="'+a+'"]').val()?(e.preventDefault(),"undefined"!=typeof inlineEditPost&&inlineEditPost.revert(),t("#pll-translate td").attr("colspan",t("th:visible, td:visible",".widefat:first thead").length),t("table.widefat tbody").prepend(t("#pll-translate")).prepend('<tr class="hidden"></tr>')):t("#pll-translate").find(".cancel").click()}),t("#pll-translate").on("click",".cancel",function(){t("#pll-translate").siblings(".hidden").remove(),t("#pll-bulk-translate").append(t("#pll-translate")),t("#"+n.whichBulkButtonId).focus()}),t("#pll-translate").keydown(function(n){13!==n.keyCode||t(n.target).hasClass("cancel")||(n.preventDefault(),t(this).find("input[type=submit]").click()),27===n.keyCode&&(n.preventDefault(),t(this).find(".cancel").click())}),t("#posts-filter").on("submit",function(){t(".settings-error").remove(),setTimeout(function(){t("input[type=checkbox]:checked").attr("checked",!1),t("#pll-translate").find(".cancel").trigger("click")},500)})});
js/term.js CHANGED
@@ -1,167 +1,203 @@
1
  // quick edit
2
  (function( $ ) {
3
- $( document ).bind( 'DOMNodeInserted', function( e ) {
4
- var t = $( e.target );
5
-
6
- // WP inserts the quick edit from
7
- if ( 'inline-edit' == t.attr( 'id' ) ) {
8
- var term_id = t.prev().attr( 'id' ).replace( "tag-", "" );
9
-
10
- if ( term_id > 0 ) {
11
- // language dropdown
12
- var select = t.find( ':input[name="inline_lang_choice"]' );
13
- var lang = $( '#lang_' + term_id ).html();
14
- select.val( lang ); // populates the dropdown
15
-
16
- // disable the language dropdown for default categories
17
- var default_cat = $( '#default_cat_' + term_id ).html();
18
- if ( term_id == default_cat ) {
19
- select.prop( 'disabled', true );
 
 
 
20
  }
21
  }
22
  }
23
- });
24
  })( jQuery );
25
 
26
 
27
  // update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit
28
  // acts on ajaxSuccess event
29
  (function( $ ) {
30
- $( document ).ajaxSuccess(function( event, xhr, settings ) {
31
- function update_rows( term_id ) {
32
- // collect old translations
33
- var translations = new Array;
34
- $( '.translation_' + term_id ).each(function() {
35
- translations.push( $( this ).parent().parent().attr( 'id' ).substring( 4 ) );
36
- });
37
-
38
- var data = {
39
- action: 'pll_update_term_rows',
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
- }
47
-
48
- // get the modified rows in ajax and update them
49
- $.post( ajaxurl, data, function( response ) {
50
- if ( response ) {
51
- var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
52
- $.each( res.responses, function() {
53
- if ( 'row' == this.what ) {
54
- $( "#tag-" + this.supplemental.term_id ).replaceWith( this.data );
55
- }
56
- });
57
- }
58
- });
59
- }
60
-
61
- var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
62
- if ( 'undefined' != typeof( data['action'] ) ) {
63
- switch ( data['action'] ) {
64
- // when adding a term, the new term_id is in the ajax response
65
- case 'add-tag':
66
- res = wpAjax.parseAjaxResponse( xhr.responseXML, 'ajax-response' );
67
- $.each( res.responses, function() {
68
- if ( 'term' == this.what ) {
69
- update_rows( this.supplemental.term_id );
70
  }
71
- });
 
 
72
 
73
- // and also reset translations hidden input fields
74
- $( '.htr_lang' ).val( 0 );
75
- break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
- // when deleting a term
78
- case 'delete-tag':
79
- update_rows( data['tag_ID'] );
80
- break;
81
 
82
- // in case the language is modified in quick edit and breaks translations
83
- case 'inline-save-tax':
84
- update_rows( data['tax_ID'] );
85
- break;
 
86
  }
87
  }
88
- });
89
  })( jQuery );
90
 
91
- jQuery( document ).ready(function( $ ) {
92
- // translations autocomplete input box
93
- function init_translations() {
94
- $( '.tr_lang' ).each(function(){
95
- var tr_lang = $( this ).attr( 'id' ).substring( 8 );
96
- var td = $( this ).parent().parent().siblings( '.pll-edit-column' );
97
-
98
- $( this ).autocomplete({
99
- minLength: 0,
100
-
101
- source: ajaxurl + '?action=pll_terms_not_translated' +
102
- '&term_language=' + $( '#term_lang_choice' ).val() +
103
- '&term_id=' + $( "input[name='tag_ID']" ).val() +
104
- '&taxonomy=' + $( "input[name='taxonomy']" ).val() +
105
- '&translation_language=' + tr_lang +
106
- '&post_type=' + typenow +
107
- '&_pll_nonce=' + $( '#_pll_nonce' ).val(),
108
-
109
- select: function( event, ui ) {
110
- $( '#htr_lang_' + tr_lang ).val( ui.item.id );
111
- td.html( ui.item.link );
112
- },
113
- });
114
-
115
- // when the input box is emptied
116
- $( this ).blur(function() {
117
- if ( ! $( this ).val() ) {
118
- $( '#htr_lang_' + tr_lang ).val( 0 );
119
- td.html( td.siblings( '.hidden' ).children().clone() );
 
 
 
 
 
 
 
 
120
  }
121
- });
122
- });
123
- }
124
-
125
- init_translations();
126
-
127
- // ajax for changing the term's language
128
- $( '#term_lang_choice' ).change(function() {
129
- var value = $( this ).val();
130
- var lang = $( this ).children( 'option[value="' + value + '"]' ).attr( 'lang' );
131
- var dir = $( '.pll-translation-column > span[lang="' + lang + '"]' ).attr( 'dir' );
132
-
133
- var data = {
134
- action: 'term_lang_choice',
135
- lang: value,
136
- from_tag: $( "input[name='from_tag']" ).val(),
137
- term_id: $( "input[name='tag_ID']" ).val(),
138
- taxonomy: $( "input[name='taxonomy']" ).val(),
139
- post_type: typenow,
140
- _pll_nonce: $( '#_pll_nonce' ).val()
141
  }
142
 
143
- $.post( ajaxurl, data, function( response ) {
144
- var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
145
- $.each( res.responses, function() {
146
- switch ( this.what ) {
147
- case 'translations': // translations fields
148
- $( "#term-translations" ).html( this.data );
149
- init_translations();
150
- break;
151
- case 'parent': // parent dropdown list for hierarchical taxonomies
152
- $( '#parent' ).replaceWith( this.data );
153
- break;
154
- case 'tag_cloud': // popular items
155
- $( '.tagcloud' ).replaceWith( this.data );
156
- break;
157
- case 'flag': // flag in front of the select dropdown
158
- $( '.pll-select-flag' ).html( this.data );
159
- break;
160
- }
161
- });
162
-
163
- // Modifies the text direction
164
- $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
165
- });
166
- });
167
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  // quick edit
2
  (function( $ ) {
3
+ $( document ).bind(
4
+ 'DOMNodeInserted',
5
+ function( e ) {
6
+ var t = $( e.target );
7
+
8
+ // WP inserts the quick edit from
9
+ if ( 'inline-edit' == t.attr( 'id' ) ) {
10
+ var term_id = t.prev().attr( 'id' ).replace( "tag-", "" );
11
+
12
+ if ( term_id > 0 ) {
13
+ // language dropdown
14
+ var select = t.find( ':input[name="inline_lang_choice"]' );
15
+ var lang = $( '#lang_' + term_id ).html();
16
+ select.val( lang ); // populates the dropdown
17
+
18
+ // disable the language dropdown for default categories
19
+ var default_cat = $( '#default_cat_' + term_id ).html();
20
+ if ( term_id == default_cat ) {
21
+ select.prop( 'disabled', true );
22
+ }
23
  }
24
  }
25
  }
26
+ );
27
  })( jQuery );
28
 
29
 
30
  // update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit
31
  // acts on ajaxSuccess event
32
  (function( $ ) {
33
+ $( document ).ajaxSuccess(
34
+ function( event, xhr, settings ) {
35
+ function update_rows( term_id ) {
36
+ // collect old translations
37
+ var translations = new Array();
38
+ $( '.translation_' + term_id ).each(
39
+ function() {
40
+ translations.push( $( this ).parent().parent().attr( 'id' ).substring( 4 ) );
41
+ }
42
+ );
43
+
44
+ var data = {
45
+ action: 'pll_update_term_rows',
46
+ term_id: term_id,
47
+ translations: translations.join( ',' ),
48
+ taxonomy: $( "input[name='taxonomy']" ).val(),
49
+ post_type: $( "input[name='post_type']" ).val(),
50
+ screen: $( "input[name='screen']" ).val(),
51
+ _pll_nonce: $( '#_pll_nonce' ).val()
52
+ };
53
+
54
+ // get the modified rows in ajax and update them
55
+ $.post(
56
+ ajaxurl,
57
+ data,
58
+ function( response ) {
59
+ if ( response ) {
60
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
61
+ $.each(
62
+ res.responses,
63
+ function() {
64
+ if ( 'row' == this.what ) {
65
+ $( "#tag-" + this.supplemental.term_id ).replaceWith( this.data );
66
+ }
67
+ }
68
+ );
 
 
 
 
69
  }
70
+ }
71
+ );
72
+ }
73
 
74
+ var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
75
+ if ( 'undefined' != typeof( data['action'] ) ) {
76
+ switch ( data['action'] ) {
77
+ // when adding a term, the new term_id is in the ajax response
78
+ case 'add-tag':
79
+ res = wpAjax.parseAjaxResponse( xhr.responseXML, 'ajax-response' );
80
+ $.each(
81
+ res.responses,
82
+ function() {
83
+ if ( 'term' == this.what ) {
84
+ update_rows( this.supplemental.term_id );
85
+ }
86
+ }
87
+ );
88
+
89
+ // and also reset translations hidden input fields
90
+ $( '.htr_lang' ).val( 0 );
91
+ break;
92
 
93
+ // when deleting a term
94
+ case 'delete-tag':
95
+ update_rows( data['tag_ID'] );
96
+ break;
97
 
98
+ // in case the language is modified in quick edit and breaks translations
99
+ case 'inline-save-tax':
100
+ update_rows( data['tax_ID'] );
101
+ break;
102
+ }
103
  }
104
  }
105
+ );
106
  })( jQuery );
107
 
108
+ jQuery( document ).ready(
109
+ function( $ ) {
110
+ // translations autocomplete input box
111
+ function init_translations() {
112
+ $( '.tr_lang' ).each(
113
+ function(){
114
+ var tr_lang = $( this ).attr( 'id' ).substring( 8 );
115
+ var td = $( this ).parent().parent().siblings( '.pll-edit-column' );
116
+
117
+ $( this ).autocomplete(
118
+ {
119
+ minLength: 0,
120
+ source: ajaxurl + '?action=pll_terms_not_translated' +
121
+ '&term_language=' + $( '#term_lang_choice' ).val() +
122
+ '&term_id=' + $( "input[name='tag_ID']" ).val() +
123
+ '&taxonomy=' + $( "input[name='taxonomy']" ).val() +
124
+ '&translation_language=' + tr_lang +
125
+ '&post_type=' + typenow +
126
+ '&_pll_nonce=' + $( '#_pll_nonce' ).val(),
127
+ select: function( event, ui ) {
128
+ $( '#htr_lang_' + tr_lang ).val( ui.item.id );
129
+ // ui.item.link is built and come from server side and is well escaped when necessary
130
+ td.html( ui.item.link ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
131
+ },
132
+ }
133
+ );
134
+
135
+ // when the input box is emptied
136
+ $( this ).blur(
137
+ function() {
138
+ if ( ! $( this ).val() ) {
139
+ $( '#htr_lang_' + tr_lang ).val( 0 );
140
+ // Value is retrieved from HTML already generated server side
141
+ td.html( td.siblings( '.hidden' ).children().clone() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
142
+ }
143
+ }
144
+ );
145
  }
146
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  }
148
 
149
+ init_translations();
150
+
151
+ // ajax for changing the term's language
152
+ $( '#term_lang_choice' ).change(
153
+ function() {
154
+ var value = $( this ).val();
155
+ var lang = $( this ).children( 'option[value="' + value + '"]' ).attr( 'lang' );
156
+ var dir = $( '.pll-translation-column > span[lang="' + lang + '"]' ).attr( 'dir' );
157
+
158
+ var data = {
159
+ action: 'term_lang_choice',
160
+ lang: value,
161
+ from_tag: $( "input[name='from_tag']" ).val(),
162
+ term_id: $( "input[name='tag_ID']" ).val(),
163
+ taxonomy: $( "input[name='taxonomy']" ).val(),
164
+ post_type: typenow,
165
+ _pll_nonce: $( '#_pll_nonce' ).val()
166
+ };
167
+
168
+ $.post(
169
+ ajaxurl,
170
+ data,
171
+ function( response ) {
172
+ var res = wpAjax.parseAjaxResponse( response, 'ajax-response' );
173
+ $.each(
174
+ res.responses,
175
+ function() {
176
+ switch ( this.what ) {
177
+ case 'translations': // translations fields
178
+ // Data is built and come from server side and is well escaped when necessary
179
+ $( "#term-translations" ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
180
+ init_translations();
181
+ break;
182
+ case 'parent': // parent dropdown list for hierarchical taxonomies
183
+ $( '#parent' ).replaceWith( this.data );
184
+ break;
185
+ case 'tag_cloud': // popular items
186
+ $( '.tagcloud' ).replaceWith( this.data );
187
+ break;
188
+ case 'flag': // flag in front of the select dropdown
189
+ // Data is built and come from server side and is well escaped when necessary
190
+ $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
191
+ break;
192
+ }
193
+ }
194
+ );
195
+
196
+ // Modifies the text direction
197
+ $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
198
+ }
199
+ );
200
+ }
201
+ );
202
+ }
203
+ );
js/term.min.js CHANGED
@@ -1 +1 @@
1
- !function(a){a(document).bind("DOMNodeInserted",function(t){var n=a(t.target);if("inline-edit"==n.attr("id")){var e=n.prev().attr("id").replace("tag-","");if(e>0){var l=n.find(':input[name="inline_lang_choice"]'),i=a("#lang_"+e).html();l.val(i);var r=a("#default_cat_"+e).html();e==r&&l.prop("disabled",!0)}}})}(jQuery),function(a){a(document).ajaxSuccess(function(t,n,e){function l(t){var n=new Array;a(".translation_"+t).each(function(){n.push(a(this).parent().parent().attr("id").substring(4))});var e={action:"pll_update_term_rows",term_id:t,translations:n.join(","),taxonomy:a("input[name='taxonomy']").val(),post_type:a("input[name='post_type']").val(),screen:a("input[name='screen']").val(),_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,e,function(t){if(t){var n=wpAjax.parseAjaxResponse(t,"ajax-response");a.each(n.responses,function(){"row"==this.what&&a("#tag-"+this.supplemental.term_id).replaceWith(this.data)})}})}var i=wpAjax.unserialize(e.data);if("undefined"!=typeof i.action)switch(i.action){case"add-tag":res=wpAjax.parseAjaxResponse(n.responseXML,"ajax-response"),a.each(res.responses,function(){"term"==this.what&&l(this.supplemental.term_id)}),a(".htr_lang").val(0);break;case"delete-tag":l(i.tag_ID);break;case"inline-save-tax":l(i.tax_ID)}})}(jQuery),jQuery(document).ready(function(a){function t(){a(".tr_lang").each(function(){var t=a(this).attr("id").substring(8),n=a(this).parent().parent().siblings(".pll-edit-column");a(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_terms_not_translated&term_language="+a("#term_lang_choice").val()+"&term_id="+a("input[name='tag_ID']").val()+"&taxonomy="+a("input[name='taxonomy']").val()+"&translation_language="+t+"&post_type="+typenow+"&_pll_nonce="+a("#_pll_nonce").val(),select:function(e,l){a("#htr_lang_"+t).val(l.item.id),n.html(l.item.link)}}),a(this).blur(function(){a(this).val()||(a("#htr_lang_"+t).val(0),n.html(n.siblings(".hidden").children().clone()))})})}t(),a("#term_lang_choice").change(function(){var n=a(this).val(),e=a(this).children('option[value="'+n+'"]').attr("lang"),l=a('.pll-translation-column > span[lang="'+e+'"]').attr("dir"),i={action:"term_lang_choice",lang:n,from_tag:a("input[name='from_tag']").val(),term_id:a("input[name='tag_ID']").val(),taxonomy:a("input[name='taxonomy']").val(),post_type:typenow,_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,i,function(n){var e=wpAjax.parseAjaxResponse(n,"ajax-response");a.each(e.responses,function(){switch(this.what){case"translations":a("#term-translations").html(this.data),t();break;case"parent":a("#parent").replaceWith(this.data);break;case"tag_cloud":a(".tagcloud").replaceWith(this.data);break;case"flag":a(".pll-select-flag").html(this.data)}}),a("body").removeClass("pll-dir-rtl").removeClass("pll-dir-ltr").addClass("pll-dir-"+l)})})});
1
+ !function(a){a(document).bind("DOMNodeInserted",function(t){var n=a(t.target);if("inline-edit"==n.attr("id")){var e=n.prev().attr("id").replace("tag-","");if(e>0){var l=n.find(':input[name="inline_lang_choice"]'),i=a("#lang_"+e).html();l.val(i),e==a("#default_cat_"+e).html()&&l.prop("disabled",!0)}}})}(jQuery),function(a){a(document).ajaxSuccess(function(t,n,e){function l(t){var n=new Array;a(".translation_"+t).each(function(){n.push(a(this).parent().parent().attr("id").substring(4))});var e={action:"pll_update_term_rows",term_id:t,translations:n.join(","),taxonomy:a("input[name='taxonomy']").val(),post_type:a("input[name='post_type']").val(),screen:a("input[name='screen']").val(),_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,e,function(t){if(t){var n=wpAjax.parseAjaxResponse(t,"ajax-response");a.each(n.responses,function(){"row"==this.what&&a("#tag-"+this.supplemental.term_id).replaceWith(this.data)})}})}var i=wpAjax.unserialize(e.data);if(void 0!==i.action)switch(i.action){case"add-tag":res=wpAjax.parseAjaxResponse(n.responseXML,"ajax-response"),a.each(res.responses,function(){"term"==this.what&&l(this.supplemental.term_id)}),a(".htr_lang").val(0);break;case"delete-tag":l(i.tag_ID);break;case"inline-save-tax":l(i.tax_ID)}})}(jQuery),jQuery(document).ready(function(a){function t(){a(".tr_lang").each(function(){var t=a(this).attr("id").substring(8),n=a(this).parent().parent().siblings(".pll-edit-column");a(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_terms_not_translated&term_language="+a("#term_lang_choice").val()+"&term_id="+a("input[name='tag_ID']").val()+"&taxonomy="+a("input[name='taxonomy']").val()+"&translation_language="+t+"&post_type="+typenow+"&_pll_nonce="+a("#_pll_nonce").val(),select:function(e,l){a("#htr_lang_"+t).val(l.item.id),n.html(l.item.link)}}),a(this).blur(function(){a(this).val()||(a("#htr_lang_"+t).val(0),n.html(n.siblings(".hidden").children().clone()))})})}t(),a("#term_lang_choice").change(function(){var n=a(this).val(),e=a(this).children('option[value="'+n+'"]').attr("lang"),l=a('.pll-translation-column > span[lang="'+e+'"]').attr("dir"),i={action:"term_lang_choice",lang:n,from_tag:a("input[name='from_tag']").val(),term_id:a("input[name='tag_ID']").val(),taxonomy:a("input[name='taxonomy']").val(),post_type:typenow,_pll_nonce:a("#_pll_nonce").val()};a.post(ajaxurl,i,function(n){var e=wpAjax.parseAjaxResponse(n,"ajax-response");a.each(e.responses,function(){switch(this.what){case"translations":a("#term-translations").html(this.data),t();break;case"parent":a("#parent").replaceWith(this.data);break;case"tag_cloud":a(".tagcloud").replaceWith(this.data);break;case"flag":a(".pll-select-flag").html(this.data)}}),a("body").removeClass("pll-dir-rtl").removeClass("pll-dir-ltr").addClass("pll-dir-"+l)})})});
js/user.js CHANGED
@@ -1,20 +1,27 @@
1
- jQuery( document ).ready(function( $ ) {
2
- // biography
3
- // FIXME there is probably a more efficient way to do this
4
- var td = $( '#description' ).parent();
5
- var d = $( '#description' ).clone();
6
- var span = td.children( '.description' ).clone();
7
- td.children().remove();
 
8
 
9
- $( '.biography' ).each(function(){
10
- lang = $( this ).attr( 'name' ).split( '___' );
11
- desc = d.clone();
12
- desc.attr( 'name', 'description_' + lang[0] );
13
- desc.html( $( this ).val() );
14
- td.append( '<div>' + lang[1] + '</div' );
15
- td.append( desc );
16
- });
 
 
 
 
17
 
18
- td.append( '<br />' );
19
- td.append( span );
20
- });
 
 
1
+ jQuery( document ).ready(
2
+ function( $ ) {
3
+ // biography
4
+ // FIXME there is probably a more efficient way to do this
5
+ var td = $( '#description' ).parent();
6
+ var d = $( '#description' ).clone();
7
+ var span = td.children( '.description' ).clone();
8
+ td.children().remove();
9
 
10
+ $( '.biography' ).each(
11
+ function(){
12
+ lang = $( this ).attr( 'name' ).split( '___' );
13
+ desc = d.clone();
14
+ desc.attr( 'name', 'description_' + lang[0] );
15
+ desc.attr( 'id', 'description_' + lang[0] );
16
+ // Whitelist because description and lang value is already escaped by the side of PHP
17
+ desc.html( $( this ).val() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
18
+ td.append( $( '<div></div>' ).text( lang[1] ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
19
+ td.append( desc ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
20
+ }
21
+ );
22
 
23
+ td.append( '<br />' );
24
+ // Whitelist because description come from html code generated by WordPress
25
+ td.append( span ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
26
+ }
27
+ );
js/user.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(document).ready(function(e){var n=e("#description").parent(),d=e("#description").clone(),i=n.children(".description").clone();n.children().remove(),e(".biography").each(function(){lang=e(this).attr("name").split("___"),desc=d.clone(),desc.attr("name","description_"+lang[0]),desc.html(e(this).val()),n.append("<div>"+lang[1]+"</div"),n.append(desc)}),n.append("<br />"),n.append(i)});
1
+ jQuery(document).ready(function(e){var n=e("#description").parent(),d=e("#description").clone(),i=n.children(".description").clone();n.children().remove(),e(".biography").each(function(){lang=e(this).attr("name").split("___"),desc=d.clone(),desc.attr("name","description_"+lang[0]),desc.attr("id","description_"+lang[0]),desc.html(e(this).val()),n.append(e("<div></div>").text(lang[1])),n.append(desc)}),n.append("<br />"),n.append(i)});
js/widgets.js CHANGED
@@ -1,83 +1,93 @@
1
- jQuery( function( $ ) {
2
- var widgets_container, widgets_selector, flags;
 
3
 
4
- if ( 'undefined' !== typeof pll_widgets && pll_widgets.hasOwnProperty( 'flags' ) ) {
5
- flags = pll_widgets.flags;
6
- }
7
-
8
- /**
9
- * Prepend widget titles with a flag once a language is selected.
10
- * @param {object} widget The widget element.
11
- * @return {void} Nothing.
12
- */
13
- function add_flag( widget ) {
14
- if ( ! flags ) {
15
- return;
16
  }
17
- widget = $( widget );
18
- var title = $( '.widget-top .widget-title h3', widget ),
19
- locale = $( '.pll-lang-choice option:selected', widget ).val(),
20
- icon = ( locale && flags.hasOwnProperty( locale ) ) ? flags[ locale ] : null;
21
-
22
- if ( icon ) {
23
- icon += ' &nbsp; ';
24
- var current = $( '.pll-lang', title );
25
- if ( current.length ) {
26
- current.html( icon );
27
- } else {
28
- flag = '<span class="pll-lang">' + icon + '</span>';
29
- title.prepend( flag );
30
- }
31
- } else {
32
- $( '.pll-lang', title ).remove();
33
- }
34
- }
35
-
36
- if ( 'undefined' !== typeof wp.customize ) {
37
-
38
- widgets_container = $( '#customize-controls' );
39
- widgets_selector = '.customize-control .widget';
40
 
41
  /**
42
- * WP Customizer add control listener.
43
- *
44
- * @link https://wordpress.stackexchange.com/questions/256536/callback-after-wordpress-customizer-complete-loading
45
- *
46
- * @param {object} control The control type.
47
  * @return {void} Nothing.
48
  */
49
- function customize_add_flag( control ) {
50
- if ( ! control.extended( wp.customize.Widgets.WidgetControl ) ) {
51
  return;
52
  }
 
 
 
 
 
53
 
54
- /*
55
- * Make sure the widget's contents are embedded; normally this is done
56
- * when the control is expanded, for DOM performance reasons.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  */
58
- control.embedWidgetContent();
 
 
 
59
 
60
- // Now we know for sure the widget is fully embedded.
61
- add_flag( control.container.find( '.widget' ) );
62
- }
63
- wp.customize.control.each( customize_add_flag );
64
- wp.customize.control.bind( 'add', customize_add_flag );
 
 
 
 
 
 
65
 
66
- } else {
67
 
68
- widgets_container = $( '#widgets-right' );
69
- widgets_selector = '.widget';
70
 
71
- }
72
 
73
- // Add flags on load.
74
- $( widgets_selector, widgets_container ).each( function() {
75
- add_flag( this );
76
- } );
 
 
77
 
78
- // Update flags.
79
- widgets_container.on( 'change', '.pll-lang-choice', function() {
80
- add_flag( $( this ).parents( '.widget' ) );
81
- } );
 
 
 
 
82
 
83
- } );
 
1
+ jQuery(
2
+ function( $ ) {
3
+ var widgets_container, widgets_selector, flags;
4
 
5
+ if ( 'undefined' !== typeof pll_widgets && pll_widgets.hasOwnProperty( 'flags' ) ) {
6
+ flags = pll_widgets.flags;
 
 
 
 
 
 
 
 
 
 
7
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
  /**
10
+ * Prepend widget titles with a flag once a language is selected.
11
+ *
12
+ * @param {object} widget The widget element.
 
 
13
  * @return {void} Nothing.
14
  */
15
+ function add_flag( widget ) {
16
+ if ( ! flags ) {
17
  return;
18
  }
19
+ widget = $( widget );
20
+ var title = $( '.widget-top .widget-title h3', widget ),
21
+ locale = $( '.pll-lang-choice option:selected', widget ).val(),
22
+ // Icon is HTML built and come from server side and is well escaped when necessary
23
+ icon = ( locale && flags.hasOwnProperty( locale ) ) ? flags[ locale ] : null;
24
 
25
+ if ( icon ) {
26
+ icon += ' &nbsp; ';
27
+ var current = $( '.pll-lang', title );
28
+ if ( current.length ) {
29
+ current.html( icon ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
30
+ } else {
31
+ flag = $( '<span />' ).addClass( 'pll-lang' ).html( icon ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
32
+ title.prepend( flag );
33
+ }
34
+ } else {
35
+ $( '.pll-lang', title ).remove();
36
+ }
37
+ }
38
+
39
+ if ( 'undefined' !== typeof wp.customize ) {
40
+
41
+ widgets_container = $( '#customize-controls' );
42
+ widgets_selector = '.customize-control .widget';
43
+
44
+ /**
45
+ * WP Customizer add control listener.
46
+ *
47
+ * @link https://wordpress.stackexchange.com/questions/256536/callback-after-wordpress-customizer-complete-loading
48
+ *
49
+ * @param {object} control The control type.
50
+ * @return {void} Nothing.
51
  */
52
+ function customize_add_flag( control ) {
53
+ if ( ! control.extended( wp.customize.Widgets.WidgetControl ) ) {
54
+ return;
55
+ }
56
 
57
+ /*
58
+ * Make sure the widget's contents are embedded; normally this is done
59
+ * when the control is expanded, for DOM performance reasons.
60
+ */
61
+ control.embedWidgetContent();
62
+
63
+ // Now we know for sure the widget is fully embedded.
64
+ add_flag( control.container.find( '.widget' ) );
65
+ }
66
+ wp.customize.control.each( customize_add_flag );
67
+ wp.customize.control.bind( 'add', customize_add_flag );
68
 
69
+ } else {
70
 
71
+ widgets_container = $( '#widgets-right' );
72
+ widgets_selector = '.widget';
73
 
74
+ }
75
 
76
+ // Add flags on load.
77
+ $( widgets_selector, widgets_container ).each(
78
+ function() {
79
+ add_flag( this );
80
+ }
81
+ );
82
 
83
+ // Update flags.
84
+ widgets_container.on(
85
+ 'change',
86
+ '.pll-lang-choice',
87
+ function() {
88
+ add_flag( $( this ).parents( '.widget' ) );
89
+ }
90
+ );
91
 
92
+ }
93
+ );
js/widgets.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(function(e){function t(t){if(o){t=e(t);var n=e(".widget-top .widget-title h3",t),l=e(".pll-lang-choice option:selected",t).val(),i=l&&o.hasOwnProperty(l)?o[l]:null;if(i){i+=" &nbsp; ";var d=e(".pll-lang",n);d.length?d.html(i):(flag='<span class="pll-lang">'+i+"</span>",n.prepend(flag))}else e(".pll-lang",n).remove()}}function n(e){e.extended(wp.customize.Widgets.WidgetControl)&&(e.embedWidgetContent(),t(e.container.find(".widget")))}var l,i,o;"undefined"!=typeof pll_widgets&&pll_widgets.hasOwnProperty("flags")&&(o=pll_widgets.flags),"undefined"!=typeof wp.customize?(l=e("#customize-controls"),i=".customize-control .widget",wp.customize.control.each(n),wp.customize.control.bind("add",n)):(l=e("#widgets-right"),i=".widget"),e(i,l).each(function(){t(this)}),l.on("change",".pll-lang-choice",function(){t(e(this).parents(".widget"))})});
1
+ jQuery(function(e){var t,l,n;function i(t){if(n){t=e(t);var l=e(".widget-top .widget-title h3",t),i=e(".pll-lang-choice option:selected",t).val(),o=i&&n.hasOwnProperty(i)?n[i]:null;if(o){o+=" &nbsp; ";var d=e(".pll-lang",l);d.length?d.html(o):(flag=e("<span />").addClass("pll-lang").html(o),l.prepend(flag))}else e(".pll-lang",l).remove()}}if("undefined"!=typeof pll_widgets&&pll_widgets.hasOwnProperty("flags")&&(n=pll_widgets.flags),void 0!==wp.customize){function o(e){e.extended(wp.customize.Widgets.WidgetControl)&&(e.embedWidgetContent(),i(e.container.find(".widget")))}t=e("#customize-controls"),l=".customize-control .widget",wp.customize.control.each(o),wp.customize.control.bind("add",o)}else t=e("#widgets-right"),l=".widget";e(l,t).each(function(){i(this)}),t.on("change",".pll-lang-choice",function(){i(e(this).parents(".widget"))})});
lingotek/lingotek.php CHANGED
@@ -14,8 +14,6 @@ class PLL_Lingotek {
14
  * @since 1.7.7
15
  */
16
  public function init() {
17
- $options = get_option( 'polylang' );
18
-
19
  // The Lingotek tab
20
  add_filter( 'pll_settings_tabs', array( $this, 'add_tab' ) );
21
  add_action( 'pll_settings_active_tab_lingotek', array( $this, 'display_tab' ) );
@@ -30,7 +28,7 @@ class PLL_Lingotek {
30
  $content = __( 'You’ve just upgraded to the latest version of Polylang! Would you like to automatically translate your website for free?', 'polylang' );
31
 
32
  $buttons = sprintf(
33
- '<a href="%s" class="button button-primary" style="margin-right: 10px">%s</a>',
34
  admin_url( 'admin.php?page=mlang_lingotek' ),
35
  __( 'Learn more', 'polylang' )
36
  );
@@ -39,7 +37,7 @@ class PLL_Lingotek {
39
  $content .= ' ' . __( 'Click on Activate Lingotek to start translating.', 'polylang' );
40
 
41
  $buttons = sprintf(
42
- '<a href="%s" class="button button-primary" style="margin-right: 10px">%s</a>',
43
  $link,
44
  __( 'Activate Lingotek', 'polylang' )
45
  ) . $buttons;
14
  * @since 1.7.7
15
  */
16
  public function init() {
 
 
17
  // The Lingotek tab
18
  add_filter( 'pll_settings_tabs', array( $this, 'add_tab' ) );
19
  add_action( 'pll_settings_active_tab_lingotek', array( $this, 'display_tab' ) );
28
  $content = __( 'You’ve just upgraded to the latest version of Polylang! Would you like to automatically translate your website for free?', 'polylang' );
29
 
30
  $buttons = sprintf(
31
+ '<a href="%s" class="button button-primary">%s</a>',
32
  admin_url( 'admin.php?page=mlang_lingotek' ),
33
  __( 'Learn more', 'polylang' )
34
  );
37
  $content .= ' ' . __( 'Click on Activate Lingotek to start translating.', 'polylang' );
38
 
39
  $buttons = sprintf(
40
+ '<a href="%s" class="button button-primary">%s</a>',
41
  $link,
42
  __( 'Activate Lingotek', 'polylang' )
43
  ) . $buttons;
modules/plugins/featured-content.php CHANGED
@@ -61,7 +61,7 @@ class PLL_Featured_Content {
61
 
62
  $settings = Featured_Content::get_setting();
63
 
64
- if ( ! $term = wpcom_vip_get_term_by( 'name', $settings['tag-name'], 'post_tag' ) ) {
65
  return $featured_ids;
66
  }
67
 
61
 
62
  $settings = Featured_Content::get_setting();
63
 
64
+ if ( ! $term = get_term_by( 'name', $settings['tag-name'], 'post_tag' ) ) {
65
  return $featured_ids;
66
  }
67
 
modules/plugins/plugins-compat.php CHANGED
@@ -266,10 +266,18 @@ class PLL_Plugins_Compat {
266
  * @since 2.0.10
267
  */
268
  public function twenty_seventeen_init() {
269
- if ( 'twentyseventeen' === get_template() && function_exists( 'twentyseventeen_panel_count' ) && did_action( 'pll_init' ) && PLL() instanceof PLL_Frontend ) {
270
- $num_sections = twentyseventeen_panel_count();
271
- for ( $i = 1; $i < ( 1 + $num_sections ); $i++ ) {
272
- add_filter( 'theme_mod_panel_' . $i, 'pll_get_post' );
 
 
 
 
 
 
 
 
273
  }
274
  }
275
  }
266
  * @since 2.0.10
267
  */
268
  public function twenty_seventeen_init() {
269
+ if ( 'twentyseventeen' === get_template() && did_action( 'pll_init' ) ) {
270
+ if ( function_exists( 'twentyseventeen_panel_count' ) && PLL() instanceof PLL_Frontend ) {
271
+ $num_sections = twentyseventeen_panel_count();
272
+ for ( $i = 1; $i < ( 1 + $num_sections ); $i++ ) {
273
+ add_filter( 'theme_mod_panel_' . $i, 'pll_get_post' );
274
+ }
275
+ }
276
+
277
+ if ( PLL() instanceof PLL_Frontend ) {
278
+ add_filter( 'theme_mod_external_header_video', 'pll__' );
279
+ } else {
280
+ pll_register_string( __( 'Header video', 'polylang' ), get_theme_mod( 'external_header_video' ), 'Twenty Seventeen', false );
281
  }
282
  }
283
  }
modules/plugins/wp-import.php CHANGED
@@ -113,6 +113,8 @@ class PLL_WP_Import extends WP_Import {
113
  protected function remap_terms_relations( &$terms ) {
114
  global $wpdb;
115
 
 
 
116
  foreach ( $terms as $term ) {
117
  $translations = maybe_unserialize( $term['term_description'] );
118
  foreach ( $translations as $slug => $old_id ) {
@@ -161,6 +163,8 @@ class PLL_WP_Import extends WP_Import {
161
  protected function remap_translations( &$terms, &$processed_objects ) {
162
  global $wpdb;
163
 
 
 
164
  foreach ( $terms as $term ) {
165
  $translations = maybe_unserialize( $term['term_description'] );
166
  $new_translations = array();
113
  protected function remap_terms_relations( &$terms ) {
114
  global $wpdb;
115
 
116
+ $trs = array();
117
+
118
  foreach ( $terms as $term ) {
119
  $translations = maybe_unserialize( $term['term_description'] );
120
  foreach ( $translations as $slug => $old_id ) {
163
  protected function remap_translations( &$terms, &$processed_objects ) {
164
  global $wpdb;
165
 
166
+ $u = array();
167
+
168
  foreach ( $terms as $term ) {
169
  $translations = maybe_unserialize( $term['term_description'] );
170
  $new_translations = array();
modules/sync/admin-sync.php CHANGED
@@ -189,7 +189,7 @@ class PLL_Admin_Sync extends PLL_Sync {
189
 
190
  if ( is_object( $this->$obj ) && method_exists( $this->$obj, 'copy' ) ) {
191
  if ( WP_DEBUG ) {
192
- $debug = debug_backtrace(); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
193
  $i = 1 + empty( $debug[1]['line'] ); // The file and line are in $debug[2] if the function was called using call_user_func
194
 
195
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
@@ -205,7 +205,7 @@ class PLL_Admin_Sync extends PLL_Sync {
205
  return call_user_func_array( array( $this->$obj, 'copy' ), $args );
206
  }
207
 
208
- $debug = debug_backtrace(); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
209
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
210
  sprintf(
211
  'Call to undefined function PLL()->sync->%1$s() in %2$s on line %3$s' . "\nError handler",
189
 
190
  if ( is_object( $this->$obj ) && method_exists( $this->$obj, 'copy' ) ) {
191
  if ( WP_DEBUG ) {
192
+ $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
193
  $i = 1 + empty( $debug[1]['line'] ); // The file and line are in $debug[2] if the function was called using call_user_func
194
 
195
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
205
  return call_user_func_array( array( $this->$obj, 'copy' ), $args );
206
  }
207
 
208
+ $debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
209
  trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
210
  sprintf(
211
  'Call to undefined function PLL()->sync->%1$s() in %2$s on line %3$s' . "\nError handler",
modules/sync/settings-sync.php CHANGED
@@ -65,7 +65,7 @@ class PLL_Settings_Sync extends PLL_Settings_Module {
65
  * @param array $options
66
  */
67
  protected function update( $options ) {
68
- $newoptions['sync'] = empty( $options['sync'] ) ? array() : array_keys( $options['sync'], 1 );
69
  return $newoptions; // take care to return only validated options
70
  }
71
 
65
  * @param array $options
66
  */
67
  protected function update( $options ) {
68
+ $newoptions = array( 'sync' => empty( $options['sync'] ) ? array() : array_keys( $options['sync'], 1 ) );
69
  return $newoptions; // take care to return only validated options
70
  }
71
 
modules/sync/sync-metas.php CHANGED
@@ -359,8 +359,6 @@ abstract class PLL_Sync_Metas {
359
  * @param array $translations The list of translations object ids
360
  */
361
  public function save_object( $object_id, $obj, $translations ) {
362
- $src_lang = array_search( $object_id, $translations );
363
-
364
  foreach ( $translations as $tr_lang => $tr_id ) {
365
  if ( $tr_id != $object_id ) {
366
  $this->copy( $object_id, $tr_id, $tr_lang, true );
359
  * @param array $translations The list of translations object ids
360
  */
361
  public function save_object( $object_id, $obj, $translations ) {
 
 
362
  foreach ( $translations as $tr_lang => $tr_id ) {
363
  if ( $tr_id != $object_id ) {
364
  $this->copy( $object_id, $tr_id, $tr_lang, true );
modules/sync/sync.php CHANGED
@@ -83,7 +83,6 @@ class PLL_Sync {
83
  public function can_sync_post_parent( $post_parent, $post_id, $postarr ) {
84
  if ( ! empty( $postarr['ID'] ) && ! $this->model->post->current_user_can_synchronize( $postarr['ID'] ) ) {
85
  $tr_ids = $this->model->post->get_translations( $postarr['ID'] );
86
- $orig_lang = array_search( $postarr['ID'], $tr_ids );
87
  foreach ( $tr_ids as $tr_id ) {
88
  if ( $tr_id !== $postarr['ID'] && $post = get_post( $tr_id ) ) {
89
  $post_parent = $post->post_parent;
83
  public function can_sync_post_parent( $post_parent, $post_id, $postarr ) {
84
  if ( ! empty( $postarr['ID'] ) && ! $this->model->post->current_user_can_synchronize( $postarr['ID'] ) ) {
85
  $tr_ids = $this->model->post->get_translations( $postarr['ID'] );
 
86
  foreach ( $tr_ids as $tr_id ) {
87
  if ( $tr_id !== $postarr['ID'] && $post = get_post( $tr_id ) ) {
88
  $post_parent = $post->post_parent;
modules/wpml/wpml-api.php CHANGED
@@ -9,6 +9,7 @@
9
  * @since 2.0
10
  */
11
  class PLL_WPML_API {
 
12
 
13
  /**
14
  * Constructor
@@ -29,7 +30,7 @@ class PLL_WPML_API {
29
  add_filter( 'wpml_language_is_active', array( $this, 'wpml_language_is_active' ), 10, 2 );
30
  add_filter( 'wpml_is_rtl', array( $this, 'wpml_is_rtl' ) );
31
  // wpml_language_form_input_field => See wpml_add_language_form_field
32
- // wpml_language_has_switched => not implemented
33
  // wpml_element_trid => not implemented
34
  // wpml_get_element_translations => not implemented
35
  // wpml_language_switcher => not implemented
@@ -42,7 +43,7 @@ class PLL_WPML_API {
42
  // Retrieving Language Information for Content
43
 
44
  add_filter( 'wpml_post_language_details', 'wpml_get_language_information', 10, 2 );
45
- // wpml_switch_language => not implemented
46
  add_filter( 'wpml_element_language_code', array( $this, 'wpml_element_language_code' ), 10, 3 );
47
  // wpml_element_language_details => not applicable
48
 
@@ -175,6 +176,35 @@ class PLL_WPML_API {
175
  return pll_current_language( 'is_rtl' );
176
  }
177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  /**
179
  * Get the language code for a translatable element
180
  *
@@ -188,7 +218,7 @@ class PLL_WPML_API {
188
  $type = $args['element_type'];
189
  $id = $args['element_id'];
190
  $pll_type = ( 'post' == $type || pll_is_translated_post_type( $type ) ) ? 'post' : ( 'term' == $type || pll_is_translated_taxonomy( $type ) ? 'term' : false );
191
- if ( 'term' === $pll_type && $term = wpcom_vip_get_term_by( 'term_taxonomy_id', $id ) ) {
192
  $id = $term->term_id;
193
  }
194
  return $pll_type ? call_user_func( "pll_get_{$pll_type}_language", $id ) : $language_code;
9
  * @since 2.0
10
  */
11
  class PLL_WPML_API {
12
+ private static $original_language = null;
13
 
14
  /**
15
  * Constructor
30
  add_filter( 'wpml_language_is_active', array( $this, 'wpml_language_is_active' ), 10, 2 );
31
  add_filter( 'wpml_is_rtl', array( $this, 'wpml_is_rtl' ) );
32
  // wpml_language_form_input_field => See wpml_add_language_form_field
33
+ // wpml_language_has_switched => See wpml_switch_language
34
  // wpml_element_trid => not implemented
35
  // wpml_get_element_translations => not implemented
36
  // wpml_language_switcher => not implemented
43
  // Retrieving Language Information for Content
44
 
45
  add_filter( 'wpml_post_language_details', 'wpml_get_language_information', 10, 2 );
46
+ add_action( 'wpml_switch_language', array( __CLASS__, 'wpml_switch_language' ), 10, 2 );
47
  add_filter( 'wpml_element_language_code', array( $this, 'wpml_element_language_code' ), 10, 3 );
48
  // wpml_element_language_details => not applicable
49
 
176
  return pll_current_language( 'is_rtl' );
177
  }
178
 
179
+ /**
180
+ * Switches whole site to the given language or restores the language that was set when first calling this function.
181
+ * Unlike the WPML original action, it is not possible to set the current language and the cookie to different values.
182
+ *
183
+ * @since 2.7
184
+ *
185
+ * @param null|string $lang Language code to switch into, restores the original language if null.
186
+ * @param bool|string $cookie Optionally also switches the cookie.
187
+ */
188
+ public static function wpml_switch_language( $lang = null, $cookie = false ) {
189
+ if ( null === self::$original_language ) {
190
+ self::$original_language = PLL()->curlang;
191
+ }
192
+
193
+ if ( empty( $lang ) ) {
194
+ PLL()->curlang = self::$original_language;
195
+ } elseif ( 'all' === $lang ) {
196
+ PLL()->curlang = null;
197
+ } elseif ( in_array( $lang, pll_languages_list() ) ) {
198
+ PLL()->curlang = PLL()->model->get_language( $lang );
199
+ }
200
+
201
+ if ( $cookie && isset( PLL()->choose_lang ) ) {
202
+ PLL()->choose_lang->maybe_setcookie();
203
+ }
204
+
205
+ do_action( 'wpml_language_has_switched', $lang, $cookie, self::$original_language );
206
+ }
207
+
208
  /**
209
  * Get the language code for a translatable element
210
  *
218
  $type = $args['element_type'];
219
  $id = $args['element_id'];
220
  $pll_type = ( 'post' == $type || pll_is_translated_post_type( $type ) ) ? 'post' : ( 'term' == $type || pll_is_translated_taxonomy( $type ) ? 'term' : false );
221
+ if ( 'term' === $pll_type && $term = get_term_by( 'term_taxonomy_id', $id ) ) {
222
  $id = $term->term_id;
223
  }
224
  return $pll_type ? call_user_func( "pll_get_{$pll_type}_language", $id ) : $language_code;
modules/wpml/wpml-compat.php CHANGED
@@ -102,7 +102,8 @@ class PLL_WPML_Compat {
102
  // Registers the string if it does not exist yet (multiline as in WPML).
103
  $to_register = array( 'context' => $context, 'name' => $name, 'string' => $string, 'multiline' => true, 'icl' => true );
104
  if ( ! in_array( $to_register, self::$strings ) && $to_register['string'] ) {
105
- self::$strings[] = $to_register;
 
106
  update_option( 'polylang_wpml_strings', self::$strings );
107
  }
108
  }
@@ -112,15 +113,14 @@ class PLL_WPML_Compat {
112
  *
113
  * @since 1.0.2
114
  *
115
- * @param string $context the group in which the string is registered, defaults to 'polylang'
116
- * @param string $name a unique name for the string
117
  */
118
  public function unregister_string( $context, $name ) {
119
- foreach ( self::$strings as $key => $string ) {
120
- if ( $string['context'] == $context && $string['name'] == $name ) {
121
- unset( self::$strings[ $key ] );
122
- update_option( 'polylang_wpml_strings', self::$strings );
123
- }
124
  }
125
  }
126
 
@@ -141,16 +141,12 @@ class PLL_WPML_Compat {
141
  *
142
  * @since 2.0
143
  *
144
- * @param string $context the group in which the string is registered
145
- * @param string $name a unique name for the string
146
- * @return bool|string the registered string, false if none was found
147
  */
148
  public function get_string_by_context_and_name( $context, $name ) {
149
- foreach ( self::$strings as $string ) {
150
- if ( $string['context'] == $context && $string['name'] == $name ) {
151
- return $string['string'];
152
- }
153
- }
154
- return false;
155
  }
156
  }
102
  // Registers the string if it does not exist yet (multiline as in WPML).
103
  $to_register = array( 'context' => $context, 'name' => $name, 'string' => $string, 'multiline' => true, 'icl' => true );
104
  if ( ! in_array( $to_register, self::$strings ) && $to_register['string'] ) {
105
+ $key = md5( "$context | $name" );
106
+ self::$strings[ $key ] = $to_register;
107
  update_option( 'polylang_wpml_strings', self::$strings );
108
  }
109
  }
113
  *
114
  * @since 1.0.2
115
  *
116
+ * @param string $context The group in which the string is registered.
117
+ * @param string $name A unique name for the string.
118
  */
119
  public function unregister_string( $context, $name ) {
120
+ $key = md5( "$context | $name" );
121
+ if ( isset( self::$strings[ $key ] ) ) {
122
+ unset( self::$strings[ $key ] );
123
+ update_option( 'polylang_wpml_strings', self::$strings );
 
124
  }
125
  }
126
 
141
  *
142
  * @since 2.0
143
  *
144
+ * @param string $context The group in which the string is registered.
145
+ * @param string $name A unique name for the string.
146
+ * @return bool|string The registered string, false if none was found.
147
  */
148
  public function get_string_by_context_and_name( $context, $name ) {
149
+ $key = md5( "$context | $name" );
150
+ return isset( self::$strings[ $key ] ) ? self::$strings[ $key ]['string'] : false;
 
 
 
 
151
  }
152
  }
modules/wpml/wpml-config.php CHANGED
@@ -84,7 +84,7 @@ class PLL_WPML_Config {
84
  $this->options[ $name ] = $key;
85
  add_filter( 'option_' . $name, array( $this, 'translate_strings' ) );
86
  } else {
87
- $this->register_string_recursive( $context, get_option( $name ), $key );
88
  }
89
  }
90
  }
@@ -200,47 +200,42 @@ class PLL_WPML_Config {
200
  * Recursively registers strings for a serialized option
201
  *
202
  * @since 1.0
 
203
  *
204
- * @param string $context the group in which the strings will be registered
205
- * @param array $options
206
- * @param object $key XML node
 
207
  */
208
- protected function register_string_recursive( $context, $options, $key ) {
209
  $children = $key->children();
210
  if ( count( $children ) ) {
211
  foreach ( $children as $child ) {
212
  $attributes = $child->attributes();
213
  $name = (string) $attributes['name'];
214
- if ( '*' === $name && is_array( $options ) ) {
215
- foreach ( $options as $n => $option ) {
216
- $this->register_wildcard_options_recursive( $context, $option, $n );
 
 
 
 
 
 
 
 
217
  }
218
- } elseif ( isset( $options[ $name ] ) ) {
219
- $this->register_string_recursive( $context, $options[ $name ], $child );
220
  }
221
  }
222
- } else {
223
- $attributes = $key->attributes();
224
- pll_register_string( (string) $attributes['name'], $options, $context, true ); // Multiline as in WPML
225
- }
226
- }
227
-
228
- /**
229
- * Recursively registers strings with a wildcard
230
- *
231
- * @since 2.1
232
- *
233
- * @param string $context the group in which the strings will be registered
234
- * @param array $options
235
- * @param string $name Option name
236
- */
237
- protected function register_wildcard_options_recursive( $context, $options, $name ) {
238
- if ( is_array( $options ) ) {
239
- foreach ( $options as $n => $option ) {
240
- $this->register_wildcard_options_recursive( $context, $option, $n );
241
  }
242
  } else {
243
- pll_register_string( $name, $options, $context );
244
  }
245
  }
246
 
@@ -249,9 +244,9 @@ class PLL_WPML_Config {
249
  *
250
  * @since 1.0
251
  *
252
- * @param array|string $values either a string to translate or a list of strings to translate
253
- * @param object $key XML node
254
- * @return array|string translated string(s)
255
  */
256
  protected function translate_strings_recursive( $values, $key ) {
257
  $children = $key->children();
@@ -260,36 +255,29 @@ class PLL_WPML_Config {
260
  $attributes = $child->attributes();
261
  $name = (string) $attributes['name'];
262
  if ( '*' === $name && is_array( $values ) ) {
263
- foreach ( $values as $n => $v ) {
264
- $values[ $n ] = $this->translate_wildcard_options_recursive( $v, $n );
 
 
 
 
 
 
 
 
265
  }
266
  } elseif ( isset( $values[ $name ] ) ) {
267
  $values[ $name ] = $this->translate_strings_recursive( $values[ $name ], $child );
268
  }
269
  }
 
 
 
 
 
270
  } else {
271
  $values = pll__( $values );
272
  }
273
  return $values;
274
  }
275
-
276
- /**
277
- * Recursively translates strings registered by a wildcard
278
- *
279
- * @since 2.1
280
- *
281
- * @param array|string $options Either a string to translate or a list of strings to translate
282
- * @param string $name Option name
283
- * @return array|string Translated string(s)
284
- */
285
- protected function translate_wildcard_options_recursive( $options, $name ) {
286
- if ( is_array( $options ) ) {
287
- foreach ( $options as $n => $option ) {
288
- $options[ $n ] = $this->translate_wildcard_options_recursive( $option, $n );
289
- }
290
- } else {
291
- $options = pll__( $options );
292
- }
293
- return $options;
294
- }
295
  }
84
  $this->options[ $name ] = $key;
85
  add_filter( 'option_' . $name, array( $this, 'translate_strings' ) );
86
  } else {
87
+ $this->register_string_recursive( $context, $name, get_option( $name ), $key );
88
  }
89
  }
90
  }
200
  * Recursively registers strings for a serialized option
201
  *
202
  * @since 1.0
203
+ * @since 2.7 Signature modified
204
  *
205
+ * @param string $context The group in which the strings will be registered.
206
+ * @param string $option Option name.
207
+ * @param array $values Option value.
208
+ * @param object $key XML node.
209
  */
210
+ protected function register_string_recursive( $context, $option, $values, $key ) {
211
  $children = $key->children();
212
  if ( count( $children ) ) {
213
  foreach ( $children as $child ) {
214
  $attributes = $child->attributes();
215
  $name = (string) $attributes['name'];
216
+ if ( '*' === $name && is_array( $values ) ) {
217
+ // This case could be handled by the next one, but we avoid calls to preg_match here.
218
+ foreach ( $values as $n => $value ) {
219
+ $this->register_string_recursive( $context, $n, $value, $child );
220
+ }
221
+ } elseif ( false !== strpos( $name, '*' ) ) {
222
+ $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
223
+ foreach ( $values as $n => $value ) {
224
+ if ( preg_match( $pattern, $n ) ) {
225
+ $this->register_string_recursive( $context, $n, $value, $child );
226
+ }
227
  }
228
+ } elseif ( isset( $values[ $name ] ) ) {
229
+ $this->register_string_recursive( $context, $name, $values[ $name ], $child );
230
  }
231
  }
232
+ } elseif ( is_array( $values ) ) {
233
+ // Parent key is a wildcard and no sub-key has been whitelisted.
234
+ foreach ( $values as $n => $value ) {
235
+ $this->register_string_recursive( $context, $n, $value, $key );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  }
237
  } else {
238
+ pll_register_string( $option, $values, $context, true ); // Multiline as in WPML.
239
  }
240
  }
241
 
244
  *
245
  * @since 1.0
246
  *
247
+ * @param array|string $values Either a string to translate or a list of strings to translate.
248
+ * @param object $key XML node.
249
+ * @return array|string Translated string(s)
250
  */
251
  protected function translate_strings_recursive( $values, $key ) {
252
  $children = $key->children();
255
  $attributes = $child->attributes();
256
  $name = (string) $attributes['name'];
257
  if ( '*' === $name && is_array( $values ) ) {
258
+ // This case could be handled by the next one, but we avoid calls to preg_match here.
259
+ foreach ( $values as $n => $value ) {
260
+ $values[ $n ] = $this->translate_strings_recursive( $value, $child );
261
+ }
262
+ } elseif ( false !== strpos( $name, '*' ) ) {
263
+ $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
264
+ foreach ( $values as $n => $value ) {
265
+ if ( preg_match( $pattern, $n ) ) {
266
+ $values[ $n ] = $this->translate_strings_recursive( $value, $child );
267
+ }
268
  }
269
  } elseif ( isset( $values[ $name ] ) ) {
270
  $values[ $name ] = $this->translate_strings_recursive( $values[ $name ], $child );
271
  }
272
  }
273
+ } elseif ( is_array( $values ) ) {
274
+ // Parent key is a wildcard and no sub-key has been whitelisted.
275
+ foreach ( $values as $n => $value ) {
276
+ $values[ $n ] = $this->translate_strings_recursive( $value, $key );
277
+ }
278
  } else {
279
  $values = pll__( $values );
280
  }
281
  return $values;
282
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  }
modules/wpml/wpml-legacy-api.php CHANGED
@@ -128,7 +128,7 @@ if ( ! function_exists( 'icl_link_to_element' ) ) {
128
  $text = get_the_title( $id );
129
  }
130
  } elseif ( taxonomy_exists( $type ) ) {
131
- $link = wpcom_vip_get_term_link( $id, $type );
132
  if ( empty( $text ) && ( $term = get_term( $id, $type ) ) && ! empty( $term ) && ! is_wp_error( $term ) ) {
133
  $text = $term->name;
134
  }
@@ -174,7 +174,7 @@ if ( ! function_exists( 'icl_object_id' ) ) {
174
  if ( 'nav_menu' === $type ) {
175
  $theme = get_option( 'stylesheet' );
176
  if ( isset( PLL()->options['nav_menus'][ $theme ] ) ) {
177
- foreach ( PLL()->options['nav_menus'][ $theme ] as $loc => $menu ) {
178
  if ( array_search( $id, $menu ) && ! empty( $menu[ $lang ] ) ) {
179
  $tr_id = $menu[ $lang ];
180
  break;
@@ -329,7 +329,7 @@ if ( ! function_exists( 'wpml_get_copied_fields_for_post_edit' ) ) {
329
  return array();
330
  }
331
 
332
- $arr['original_post_id'] = (int) $_GET['from_post']; // phpcs:ignore WordPress.Security.NonceVerification
333
 
334
  // Don't know what WPML does but Polylang does copy all public meta keys by default
335
  foreach ( $keys = array_unique( array_keys( get_post_custom( $arr['original_post_id'] ) ) ) as $k => $meta_key ) {
128
  $text = get_the_title( $id );
129
  }
130
  } elseif ( taxonomy_exists( $type ) ) {
131
+ $link = get_term_link( $id, $type );
132
  if ( empty( $text ) && ( $term = get_term( $id, $type ) ) && ! empty( $term ) && ! is_wp_error( $term ) ) {
133
  $text = $term->name;
134
  }
174
  if ( 'nav_menu' === $type ) {
175
  $theme = get_option( 'stylesheet' );
176
  if ( isset( PLL()->options['nav_menus'][ $theme ] ) ) {
177
+ foreach ( PLL()->options['nav_menus'][ $theme ] as $menu ) {
178
  if ( array_search( $id, $menu ) && ! empty( $menu[ $lang ] ) ) {
179
  $tr_id = $menu[ $lang ];
180
  break;
329
  return array();
330
  }
331
 
332
+ $arr = array( 'original_post_id' => (int) $_GET['from_post'] ); // phpcs:ignore WordPress.Security.NonceVerification
333
 
334
  // Don't know what WPML does but Polylang does copy all public meta keys by default
335
  foreach ( $keys = array_unique( array_keys( get_post_custom( $arr['original_post_id'] ) ) ) as $k => $meta_key ) {
polylang.php CHANGED
@@ -3,7 +3,7 @@
3
  /**
4
  Plugin Name: Polylang
5
  Plugin URI: https://polylang.pro
6
- Version: 2.6.10
7
  Author: WP SYNTEX
8
  Author uri: https://polylang.pro
9
  Description: Adds multilingual capability to WordPress
@@ -51,8 +51,8 @@ if ( defined( 'POLYLANG_BASENAME' ) ) {
51
  }
52
  } else {
53
  // Go on loading the plugin
54
- define( 'POLYLANG_VERSION', '2.6.10' );
55
- define( 'PLL_MIN_WP_VERSION', '4.7' );
56
  define( 'PLL_MIN_PHP_VERSION', '5.6' );
57
 
58
  define( 'POLYLANG_FILE', __FILE__ ); // this file
3
  /**
4
  Plugin Name: Polylang
5
  Plugin URI: https://polylang.pro
6
+ Version: 2.7
7
  Author: WP SYNTEX
8
  Author uri: https://polylang.pro
9
  Description: Adds multilingual capability to WordPress
51
  }
52
  } else {
53
  // Go on loading the plugin
54
+ define( 'POLYLANG_VERSION', '2.7' );
55
+ define( 'PLL_MIN_WP_VERSION', '4.9' );
56
  define( 'PLL_MIN_PHP_VERSION', '5.6' );
57
 
58
  define( 'POLYLANG_FILE', __FILE__ ); // this file
readme.txt CHANGED
@@ -1,12 +1,13 @@
1
  === Polylang ===
2
- Contributors: Chouby, manooweb
3
  Donate link: https://polylang.pro
4
  Tags: multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
- Requires at least: 4.7
6
- Tested up to: 5.3
7
  Requires PHP: 5.6
8
- Stable tag: 2.6.10
9
  License: GPLv3 or later
 
10
 
11
  Making WordPress multilingual
12
 
@@ -42,7 +43,7 @@ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-
42
 
43
  == Installation ==
44
 
45
- 1. Make sure you are using WordPress 4.7 or later and that your server is running PHP 5.6 or later (same requirement as WordPress itself)
46
  1. If you tried other multilingual plugins, deactivate them before activating Polylang, otherwise, you may get unexpected results!
47
  1. Install and activate the plugin as usual from the 'Plugins' menu in WordPress.
48
  1. Go to the languages settings page and create the languages you need
@@ -77,6 +78,35 @@ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-
77
 
78
  == Changelog ==
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  = 2.6.10 (2020-02-19) =
81
 
82
  * Pro: Fix sticky posts not filtered in REST API (introduced in 2.6.9)
1
  === Polylang ===
2
+ Contributors: Chouby, manooweb, raaaahman, marianne38
3
  Donate link: https://polylang.pro
4
  Tags: multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
+ Requires at least: 4.9
6
+ Tested up to: 5.4
7
  Requires PHP: 5.6
8
+ Stable tag: 2.7
9
  License: GPLv3 or later
10
+ License URI: https://www.gnu.org/licenses/gpl-3.0.html
11
 
12
  Making WordPress multilingual
13
 
43
 
44
  == Installation ==
45
 
46
+ 1. Make sure you are using WordPress 4.9 or later and that your server is running PHP 5.6 or later (same requirement as WordPress itself)
47
  1. If you tried other multilingual plugins, deactivate them before activating Polylang, otherwise, you may get unexpected results!
48
  1. Install and activate the plugin as usual from the 'Plugins' menu in WordPress.
49
  1. Go to the languages settings page and create the languages you need
78
 
79
  == Changelog ==
80
 
81
+ = 2.7 (2020-04-06) =
82
+
83
+ * Minimum WordPress version is now 4.9
84
+ * Pro: Strings translations can now be exported and imported (in PO format)
85
+ * Pro: Allow to decide individually which ACF fields to copy or synchronize
86
+ * Pro: Add action pll_inactive_language_requested
87
+ * Pro; Fix fatal error in The Events Calendar compatibility when no language is defined yet
88
+ * Pro: Fix bulk translate when a post has no language
89
+ * Pro: Fix reusable block saved without language
90
+ * Pro: Fix post requested by slug not filtered in REST API, when the slug is shared
91
+ * Add a setup wizard
92
+ * Add Swahili, Upper Sorbian, Sindhi and Spanish from Uruguay to the list of predefined languages
93
+ * Add flags in the predefined list of languages
94
+ * Allow to hide the metaboxes from the screen options
95
+ * The deletion of the plugin's data at uninstall is now controlled by a PHP constant instead of an option #456
96
+ * Add parent in ajax response when selecting a term in autocomplete field #328
97
+ * Add Vary: Accept-Language http header in home page redirect. Props @chesio #452
98
+ * Improve performance to register/unregister WPML strings
99
+ * Add support for the action wpml_switch_language
100
+ * Add post_status to the list of accepted args of pll_count_posts()
101
+ * Apply the filter pll_preferred_language in wp-login.php
102
+ * Use filtered wrappers to create meta when creating media translations #231
103
+ * Allow to translate the Twenty Seventeen header video Youtube url #460
104
+ * Notices are now dismissed per site instead of per user #478
105
+ * Fix terms not visible in the quick edit when only one language is defined and teh admin language filter is active
106
+ * Fix post state not displayed for translations of the privacy policy page #395
107
+ * Fix wildcards not correctly interpreted in wpml-config.xml
108
+ * Fix product categories with special characters duplicated when importing WooCommerce products #474
109
+
110
  = 2.6.10 (2020-02-19) =
111
 
112
  * Pro: Fix sticky posts not filtered in REST API (introduced in 2.6.9)
settings/languages.php CHANGED
@@ -368,6 +368,14 @@ return array(
368
  'flag' => 'pe',
369
  'facebook' => 'es_LA',
370
  ),
 
 
 
 
 
 
 
 
371
  'es_VE' => array(
372
  'code' => 'es',
373
  'locale' => 'es_VE',
@@ -537,6 +545,13 @@ return array(
537
  'flag' => 'hu',
538
  'facebook' => 'hu_HU',
539
  ),
 
 
 
 
 
 
 
540
  'hy' => array(
541
  'code' => 'hy',
542
  'locale' => 'hy',
@@ -923,6 +938,13 @@ return array(
923
  'sna' => array(
924
  'facebook' => 'sn_ZW',
925
  ),
 
 
 
 
 
 
 
926
  'so_SO' => array(
927
  'code' => 'so',
928
  'locale' => 'so_SO',
@@ -967,6 +989,11 @@ return array(
967
  'facebook' => 'sv_SE',
968
  ),
969
  'sw' => array(
 
 
 
 
 
970
  'facebook' => 'sw_KE',
971
  ),
972
  'syr' => array(
368
  'flag' => 'pe',
369
  'facebook' => 'es_LA',
370
  ),
371
+ 'es_UY' => array(
372
+ 'code' => 'es',
373
+ 'locale' => 'es_UY',
374
+ 'name' => 'Español',
375
+ 'dir' => 'ltr',
376
+ 'flag' => 'uy',
377
+ 'facebook' => 'es_LA',
378
+ ),
379
  'es_VE' => array(
380
  'code' => 'es',
381
  'locale' => 'es_VE',
545
  'flag' => 'hu',
546
  'facebook' => 'hu_HU',
547
  ),
548
+ 'hsb' => array(
549
+ 'code' => 'hsb',
550
+ 'locale' => 'hsb',
551
+ 'name' => 'Hornjoserbšćina',
552
+ 'dir' => 'ltr',
553
+ 'flag' => 'de',
554
+ ),
555
  'hy' => array(
556
  'code' => 'hy',
557
  'locale' => 'hy',
938
  'sna' => array(
939
  'facebook' => 'sn_ZW',
940
  ),
941
+ 'snd' => array(
942
+ 'code' => 'sd',
943
+ 'locale' => 'snd',
944
+ 'name' => 'سنڌي',
945
+ 'dir' => 'rtl',
946
+ 'flag' => 'pk',
947
+ ),
948
  'so_SO' => array(
949
  'code' => 'so',
950
  'locale' => 'so_SO',
989
  'facebook' => 'sv_SE',
990
  ),
991
  'sw' => array(
992
+ 'code' => 'sw',
993
+ 'locale' => 'sw',
994
+ 'name' => 'Kiswahili',
995
+ 'dir' => 'ltr',
996
+ 'flag' => 'ke',
997
  'facebook' => 'sw_KE',
998
  ),
999
  'syr' => array(
settings/settings-cpt.php CHANGED
@@ -116,6 +116,8 @@ class PLL_Settings_CPT extends PLL_Settings_Module {
116
  * @param array $options
117
  */
118
  protected function update( $options ) {
 
 
119
  foreach ( array( 'post_types', 'taxonomies' ) as $key ) {
120
  $newoptions[ $key ] = empty( $options[ $key ] ) ? array() : array_keys( $options[ $key ], 1 );
121
  }
116
  * @param array $options
117
  */
118
  protected function update( $options ) {
119
+ $newoptions = array();
120
+
121
  foreach ( array( 'post_types', 'taxonomies' ) as $key ) {
122
  $newoptions[ $key ] = empty( $options[ $key ] ) ? array() : array_keys( $options[ $key ], 1 );
123
  }
settings/settings-licenses.php CHANGED
@@ -70,109 +70,7 @@ class PLL_Settings_Licenses extends PLL_Settings_Module {
70
  * @return string
71
  */
72
  protected function get_row( $item ) {
73
- if ( ! empty( $item->license_data ) ) {
74
- $license = $item->license_data;
75
- }
76
-
77
- $class = 'license-null';
78
-
79
- $out = sprintf(
80
- '<td><label for="pll-licenses[%1$s]">%2$s</label></td>' .
81
- '<td><input name="licenses[%1$s]" id="pll-licenses[%1$s]" type="text" value="%3$s" class="regular-text code" />',
82
- esc_attr( $item->id ),
83
- esc_attr( $item->name ),
84
- esc_html( $item->license_key )
85
- );
86
-
87
- if ( ! empty( $license ) && is_object( $license ) ) {
88
- $now = current_time( 'timestamp' );
89
- $expiration = strtotime( $license->expires, $now );
90
-
91
- // Special case: the license expired after the last check
92
- if ( $license->success && $expiration < $now ) {
93
- $license->success = false;
94
- $license->error = 'expired';
95
- }
96
-
97
- if ( false === $license->success ) {
98
- $class = 'notice-error notice-alt';
99
-
100
- switch ( $license->error ) {
101
- case 'expired':
102
- $message = sprintf(
103
- /* translators: %1$s is a date, %2$s is link start tag, %3$s is link end tag. */
104
- __( 'Your license key expired on %1$s. Please %2$srenew your license key%3$s.', 'polylang' ),
105
- date_i18n( get_option( 'date_format' ), strtotime( $license->expires, current_time( 'timestamp' ) ) ),
106
- sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/checkout/?edd_license_key=' . $item->license_key ),
107
- '</a>'
108
- );
109
- break;
110
-
111
- case 'missing':
112
- $message = sprintf(
113
- /* translators: %1$s is link start tag, %2$s is link end tag. */
114
- __( 'Invalid license. Please %1$svisit your account page%2$s and verify it.', 'polylang' ),
115
- sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account' ),
116
- '</a>'
117
- );
118
- break;
119
-
120
- case 'invalid':
121
- case 'site_inactive':
122
- $message = sprintf(
123
- /* translators: %1$s is a product name, %2$s is link start tag, %3$s is link end tag. */
124
- __( 'Your %1$s license key is not active for this URL. Please %2$svisit your account page%3$s to manage your license key URLs.', 'polylang' ),
125
- $item->name,
126
- sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account' ),
127
- '</a>'
128
- );
129
- break;
130
-
131
- case 'item_name_mismatch':
132
- /* translators: %s is a product name */
133
- $message = sprintf( __( 'This is not a %s license key.', 'polylang' ), $item->name );
134
- break;
135
-
136
- case 'no_activations_left':
137
- $message = sprintf(
138
- /* translators: %1$s is link start tag, %2$s is link end tag */
139
- __( 'Your license key has reached its activation limit. %1$sView possible upgrades%2$s now.', 'polylang' ),
140
- sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/account' ),
141
- '</a>'
142
- );
143
- break;
144
- }
145
- } else {
146
- $class = 'license-valid';
147
-
148
- $out .= sprintf( '<button id="deactivate_%s" type="button" class="button button-secondary pll-deactivate-license">%s</button>', $item->id, __( 'Deactivate', 'polylang' ) );
149
-
150
- if ( 'lifetime' === $license->expires ) {
151
- $message = __( 'The license key never expires.', 'polylang' );
152
- } elseif ( $expiration > $now && $expiration - $now < ( DAY_IN_SECONDS * 30 ) ) {
153
- $class = 'notice-warning notice-alt';
154
- $message = sprintf(
155
- /* translators: %1$s is a date, %2$s is link start tag, %3$s is link end tag. */
156
- __( 'Your license key will expire soon! Precisely, it will expire on %1$s. %2$sRenew your license key today!%3$s.', 'polylang' ),
157
- date_i18n( get_option( 'date_format' ), strtotime( $license->expires, $now ) ),
158
- sprintf( '<a href="%s" target="_blank">', 'https://polylang.pro/checkout/?edd_license_key=' . $item->license_key ),
159
- '</a>'
160
- );
161
- } else {
162
- $message = sprintf(
163
- /* translators: %s is a date */
164
- __( 'Your license key expires on %s.', 'polylang' ),
165
- date_i18n( get_option( 'date_format' ), strtotime( $license->expires, $now ) )
166
- );
167
- }
168
- }
169
- }
170
-
171
- if ( ! empty( $message ) ) {
172
- $out .= '<p>' . $message . '</p>';
173
- }
174
-
175
- return sprintf( '<tr id="pll-license-%s" class="%s">%s</tr>', $item->id, $class, $out );
176
  }
177
 
178
  /**
70
  * @return string
71
  */
72
  protected function get_row( $item ) {
73
+ return $item->get_form_field();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  }
75
 
76
  /**
settings/settings-module.php CHANGED
@@ -194,6 +194,8 @@ class PLL_Settings_Module {
194
  * @return array
195
  */
196
  protected function get_actions() {
 
 
197
  if ( $this->is_active() && $this->get_form() ) {
198
  $actions[] = 'configure';
199
  }
194
  * @return array
195
  */
196
  protected function get_actions() {
197
+ $actions = array();
198
+
199
  if ( $this->is_active() && $this->get_form() ) {
200
  $actions[] = 'configure';
201
  }
settings/settings-tools.php DELETED
@@ -1,52 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Settings class for tools
5
- *
6
- * @since 1.8
7
- */
8
- class PLL_Settings_Tools extends PLL_Settings_Module {
9
-
10
- /**
11
- * Constructor
12
- *
13
- * @since 1.8
14
- *
15
- * @param object $polylang polylang object
16
- */
17
- public function __construct( &$polylang ) {
18
- parent::__construct(
19
- $polylang,
20
- array(
21
- 'module' => 'tools',
22
- 'title' => __( 'Tools', 'polylang' ),
23
- 'description' => __( 'Decide whether to remove all data when deleting Polylang.', 'polylang' ),
24
- )
25
- );
26
- }
27
-
28
- /**
29
- * Displays the settings form
30
- *
31
- * @since 1.8
32
- */
33
- protected function form() {
34
- printf(
35
- '<label for="uninstall"><input id="uninstall" name="uninstall" type="checkbox" value="1" %s /> %s</label>',
36
- checked( empty( $this->options['uninstall'] ), false, false ),
37
- esc_html__( 'Remove all Polylang data upon using the "Delete" action in the "Plugins" admin page.', 'polylang' )
38
- );
39
- }
40
-
41
- /**
42
- * Sanitizes the settings before saving
43
- *
44
- * @since 1.8
45
- *
46
- * @param array $options
47
- */
48
- protected function update( $options ) {
49
- $newoptions['uninstall'] = isset( $options['uninstall'] ) ? 1 : 0;
50
- return $newoptions; // Take care to return only validated options
51
- }
52
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
settings/settings-url.php CHANGED
@@ -223,6 +223,8 @@ class PLL_Settings_Url extends PLL_Settings_Module {
223
  * @param array $options
224
  */
225
  protected function update( $options ) {
 
 
226
  foreach ( array( 'force_lang', 'rewrite' ) as $key ) {
227
  $newoptions[ $key ] = isset( $options[ $key ] ) ? (int) $options[ $key ] : 0;
228
  }
223
  * @param array $options
224
  */
225
  protected function update( $options ) {
226
+ $newoptions = array();
227
+
228
  foreach ( array( 'force_lang', 'rewrite' ) as $key ) {
229
  $newoptions[ $key ] = isset( $options[ $key ] ) ? (int) $options[ $key ] : 0;
230
  }
settings/settings.php CHANGED
@@ -17,7 +17,27 @@
17
  * @since 1.2
18
  */
19
  class PLL_Settings extends PLL_Admin_Base {
20
- protected $active_tab, $modules;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  /**
23
  * Constructor
@@ -35,6 +55,10 @@ class PLL_Settings extends PLL_Admin_Base {
35
 
36
  PLL_Admin_Strings::init();
37
 
 
 
 
 
38
  // FIXME put this as late as possible
39
  add_action( 'admin_init', array( $this, 'register_settings_modules' ) );
40
 
@@ -53,7 +77,6 @@ class PLL_Settings extends PLL_Admin_Base {
53
  */
54
  public function register_settings_modules() {
55
  $modules = array(
56
- 'PLL_Settings_Tools',
57
  'PLL_Settings_Licenses',
58
  );
59
 
@@ -108,7 +131,7 @@ class PLL_Settings extends PLL_Admin_Base {
108
  'pll-about-box',
109
  __( 'About Polylang', 'polylang' ),
110
  array( $this, 'metabox_about' ),
111
- 'settings_page_mlang', // FIXME not shown in screen options
112
  'normal'
113
  );
114
  }
@@ -298,7 +321,8 @@ class PLL_Settings extends PLL_Admin_Base {
298
  // Handle user input
299
  $action = isset( $_REQUEST['pll_action'] ) ? sanitize_key( $_REQUEST['pll_action'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
300
  if ( 'edit' === $action && ! empty( $_GET['lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
301
- $edit_lang = $this->model->get_language( (int) $_GET['lang'] ); // phpcs:ignore WordPress.Security.NonceVerification
 
302
  } else {
303
  $this->handle_actions( $action );
304
  }
@@ -360,7 +384,7 @@ class PLL_Settings extends PLL_Admin_Base {
360
  *
361
  * @since 2.3
362
  */
363
- public function get_predefined_languages() {
364
  require_once ABSPATH . 'wp-admin/includes/translation-install.php';
365
 
366
  $languages = include PLL_SETTINGS_INC . '/languages.php';
17
  * @since 1.2
18
  */
19
  class PLL_Settings extends PLL_Admin_Base {
20
+
21
+ /**
22
+ * Name of the active module
23
+ *
24
+ * @var string $active_tab
25
+ */
26
+ protected $active_tab;
27
+
28
+ /**
29
+ * Array of modules classes
30
+ *
31
+ * @var array $modules
32
+ */
33
+ protected $modules;
34
+
35
+ /**
36
+ * Reference to PLL_Import_Export
37
+ *
38
+ * @var PLL_Import_Export $import_export
39
+ */
40
+ protected $import_export;
41
 
42
  /**
43
  * Constructor
55
 
56
  PLL_Admin_Strings::init();
57
 
58
+ if ( class_exists( 'PLL_Import_Export' ) ) {
59
+ $this->import_export = new PLL_Import_Export( $this );
60
+ }
61
+
62
  // FIXME put this as late as possible
63
  add_action( 'admin_init', array( $this, 'register_settings_modules' ) );
64
 
77
  */
78
  public function register_settings_modules() {
79
  $modules = array(
 
80
  'PLL_Settings_Licenses',
81
  );
82
 
131
  'pll-about-box',
132
  __( 'About Polylang', 'polylang' ),
133
  array( $this, 'metabox_about' ),
134
+ 'toplevel_page_mlang',
135
  'normal'
136
  );
137
  }
321
  // Handle user input
322
  $action = isset( $_REQUEST['pll_action'] ) ? sanitize_key( $_REQUEST['pll_action'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
323
  if ( 'edit' === $action && ! empty( $_GET['lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
324
+ // phpcs:ignore WordPress.Security.NonceVerification, WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable
325
+ $edit_lang = $this->model->get_language( (int) $_GET['lang'] );
326
  } else {
327
  $this->handle_actions( $action );
328
  }
384
  *
385
  * @since 2.3
386
  */
387
+ public static function get_predefined_languages() {
388
  require_once ABSPATH . 'wp-admin/includes/translation-install.php';
389
 
390
  $languages = include PLL_SETTINGS_INC . '/languages.php';
settings/table-languages.php CHANGED
@@ -128,7 +128,6 @@ class PLL_Table_Languages extends WP_List_Table {
128
  /* translators: accessibility text */
129
  esc_html__( 'Default language', 'polylang' )
130
  );
131
- $actions = array();
132
  }
133
 
134
  return $s;
128
  /* translators: accessibility text */
129
  esc_html__( 'Default language', 'polylang' )
130
  );
 
131
  }
132
 
133
  return $s;
settings/table-settings.php CHANGED
@@ -92,24 +92,21 @@ class PLL_Table_Settings extends WP_List_Table {
92
  * @param object $item The current item
93
  */
94
  protected function single_row_columns( $item ) {
95
- list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
 
 
96
 
97
- foreach ( $columns as $column_name => $column_display_name ) {
98
  $classes = "$column_name column-$column_name";
99
  if ( $primary === $column_name ) {
100
  $classes .= ' column-primary';
101
  }
102
 
103
- if ( in_array( $column_name, $hidden ) ) {
104
- $classes .= ' hidden';
105
- }
106
-
107
  if ( 'cb' == $column_name ) {
108
  echo '<th scope="row" class="check-column">';
109
  echo $this->column_cb( $item ); // phpcs:ignore WordPress.Security.EscapeOutput
110
  echo '</th>';
111
- }
112
- else {
113
  printf( '<td class="%s">', esc_attr( $classes ) );
114
  echo $this->column_default( $item, $column_name ); // phpcs:ignore WordPress.Security.EscapeOutput
115
  echo '</td>';
92
  * @param object $item The current item
93
  */
94
  protected function single_row_columns( $item ) {
95
+ $column_info = $this->get_column_info();
96
+ $columns = $column_info[0];
97
+ $primary = $column_info[3];
98
 
99
+ foreach ( array_keys( $columns ) as $column_name ) {
100
  $classes = "$column_name column-$column_name";
101
  if ( $primary === $column_name ) {
102
  $classes .= ' column-primary';
103
  }
104
 
 
 
 
 
105
  if ( 'cb' == $column_name ) {
106
  echo '<th scope="row" class="check-column">';
107
  echo $this->column_cb( $item ); // phpcs:ignore WordPress.Security.EscapeOutput
108
  echo '</th>';
109
+ } else {
 
110
  printf( '<td class="%s">', esc_attr( $classes ) );
111
  echo $this->column_default( $item, $column_name ); // phpcs:ignore WordPress.Security.EscapeOutput
112
  echo '</td>';
settings/table-string.php CHANGED
@@ -216,6 +216,7 @@ class PLL_Table_String extends WP_List_Table {
216
  }
217
 
218
  // Load translations
 
219
  foreach ( $languages as $language ) {
220
  $mo[ $language->slug ] = new PLL_MO();
221
  $mo[ $language->slug ]->import_from_db( $language );
@@ -350,16 +351,18 @@ class PLL_Table_String extends WP_List_Table {
350
  $mo = new PLL_MO();
351
  $mo->import_from_db( $language );
352
 
353
- foreach ( $_POST['translation'][ $language->slug ] as $key => $translation ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
 
354
  /**
355
  * Filter the string translation before it is saved in DB
356
  * Allows to sanitize strings registered with pll_register_string
357
  *
358
  * @since 1.6
 
359
  *
360
- * @param string $translation the string translation
361
- * @param string $name the name as defined in pll_register_string
362
- * @param string $context the context as defined in pll_register_string
363
  */
364
  $translation = apply_filters( 'pll_sanitize_string_translation', $translation, $this->strings[ $key ]['name'], $this->strings[ $key ]['context'] );
365
  $mo->add_entry( $mo->make_entry( $this->strings[ $key ]['string'], $translation ) );
216
  }
217
 
218
  // Load translations
219
+ $mo = array();
220
  foreach ( $languages as $language ) {
221
  $mo[ $language->slug ] = new PLL_MO();
222
  $mo[ $language->slug ]->import_from_db( $language );
351
  $mo = new PLL_MO();
352
  $mo->import_from_db( $language );
353
 
354
+ $translations = array_map( 'trim', wp_unslash( $_POST['translation'][ $language->slug ] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
355
+ foreach ( $translations as $key => $translation ) {
356
  /**
357
  * Filter the string translation before it is saved in DB
358
  * Allows to sanitize strings registered with pll_register_string
359
  *
360
  * @since 1.6
361
+ * @since 2.7 The translation passed to the filter is unslashed.
362
  *
363
+ * @param string $translation The string translation.
364
+ * @param string $name The name as defined in pll_register_string.
365
+ * @param string $context The context as defined in pll_register_string.
366
  */
367
  $translation = apply_filters( 'pll_sanitize_string_translation', $translation, $this->strings[ $key ]['name'], $this->strings[ $key ]['context'] );
368
  $mo->add_entry( $mo->make_entry( $this->strings[ $key ]['string'], $translation ) );
settings/view-languages.php CHANGED
@@ -18,7 +18,7 @@ require ABSPATH . 'wp-admin/options-head.php'; // Displays the errors messages a
18
  case 'strings': // String translations tab
19
  case 'settings': // Settings tab
20
  include PLL_SETTINGS_INC . '/view-tab-' . $this->active_tab . '.php';
21
- break;
22
 
23
  default:
24
  /**
18
  case 'strings': // String translations tab
19
  case 'settings': // Settings tab
20
  include PLL_SETTINGS_INC . '/view-tab-' . $this->active_tab . '.php';
21
+ // Intentional fall-through to upgrade to fire the action below.
22
 
23
  default:
24
  /**
settings/view-tab-lang.php CHANGED
@@ -18,7 +18,7 @@ if ( ! defined( 'ABSPATH' ) ) {
18
  <div class="metabox-holder">
19
  <?php
20
  wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
21
- do_meta_boxes( 'settings_page_mlang', 'normal', array() );
22
  ?>
23
  </div>
24
  </div><!-- col-wrap -->
@@ -54,13 +54,17 @@ if ( ! defined( 'ABSPATH' ) ) {
54
  <option value=""></option>
55
  <?php
56
  foreach ( $this->get_predefined_languages() as $lg ) {
 
 
 
57
  printf(
58
- '<option value="%1$s:%2$s:%3$s:%4$s">%5$s - %2$s</option>' . "\n",
59
  esc_attr( $lg['code'] ),
60
  esc_attr( $lg['locale'] ),
61
  'rtl' == $lg['dir'] ? '1' : '0',
62
  esc_attr( $lg['flag'] ),
63
- esc_html( $lg['name'] )
 
64
  );
65
  }
66
  ?>
@@ -125,15 +129,10 @@ if ( ! defined( 'ABSPATH' ) ) {
125
  <?php
126
  $flags = include PLL_SETTINGS_INC . '/flags.php';
127
  foreach ( $flags as $code => $label ) {
128
- /** This filter is documented in include/language.php */
129
- $flag = apply_filters( 'pll_flag', array( 'url' => plugins_url( "/flags/{$code}.png", POLYLANG_FILE ) ), $code );
130
-
131
  printf(
132
- '<option value="%s" data-url="%s"%s%s%s>%s</option>' . "\n",
133
  esc_attr( $code ),
134
- esc_url( $flag['url'] ),
135
- ( empty( $flag['width'] ) ? '' : sprintf( ' data-width="%s"', (int) $flag['width'] ) ),
136
- ( empty( $flag['height'] ) ? '' : sprintf( ' data-height="%s"', (int) $flag['height'] ) ),
137
  selected( isset( $edit_lang->flag_code ) && $edit_lang->flag_code === $code, true, false ),
138
  esc_html( $label )
139
  );
@@ -179,14 +178,3 @@ if ( ! defined( 'ABSPATH' ) ) {
179
  </div><!-- col-wrap -->
180
  </div><!-- col-left -->
181
  </div><!-- col-container -->
182
-
183
- <script type="text/javascript">
184
- //<![CDATA[
185
- jQuery( document ).ready( function( $ ) {
186
- // close postboxes that should be closed
187
- $( '.if-js-closed' ).removeClass( 'if-js-closed' ).addClass( 'closed' );
188
- // postboxes setup
189
- postboxes.add_postbox_toggles( 'settings_page_mlang' );
190
- } );
191
- //]]>
192
- </script>
18
  <div class="metabox-holder">
19
  <?php
20
  wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
21
+ do_meta_boxes( 'toplevel_page_mlang', 'normal', array() );
22
  ?>
23
  </div>
24
  </div><!-- col-wrap -->
54
  <option value=""></option>
55
  <?php
56
  foreach ( $this->get_predefined_languages() as $lg ) {
57
+ $lg['flag_code'] = $lg['flag'];
58
+ $language = new PLL_Language( $lg );
59
+ $language->set_flag();
60
  printf(
61
+ '<option value="%1$s:%2$s:%3$s:%4$s" data-flag-html="%6$s">%5$s - %2$s</option>' . "\n",
62
  esc_attr( $lg['code'] ),
63
  esc_attr( $lg['locale'] ),
64
  'rtl' == $lg['dir'] ? '1' : '0',
65
  esc_attr( $lg['flag'] ),
66
+ esc_html( $lg['name'] ),
67
+ esc_html( $language->flag )
68
  );
69
  }
70
  ?>
129
  <?php
130
  $flags = include PLL_SETTINGS_INC . '/flags.php';
131
  foreach ( $flags as $code => $label ) {
 
 
 
132
  printf(
133
+ '<option value="%s" data-flag-html="%s"%s>%s</option>' . "\n",
134
  esc_attr( $code ),
135
+ esc_html( PLL_Language::get_flag_html( PLL_Language::get_flag_informations( $code ) ) ),
 
 
136
  selected( isset( $edit_lang->flag_code ) && $edit_lang->flag_code === $code, true, false ),
137
  esc_html( $label )
138
  );
178
  </div><!-- col-wrap -->
179
  </div><!-- col-left -->
180
  </div><!-- col-container -->
 
 
 
 
 
 
 
 
 
 
 
settings/view-tab-strings.php CHANGED
@@ -22,4 +22,11 @@ if ( ! defined( 'ABSPATH' ) ) {
22
  submit_button(); // Since WP 3.1
23
  ?>
24
  </form>
 
 
 
 
 
 
 
25
  </div>
22
  submit_button(); // Since WP 3.1
23
  ?>
24
  </form>
25
+ <div class="metabox-holder">
26
+ <?php
27
+ wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
28
+ do_meta_boxes( 'languages_page_mlang_strings', 'bottom', array() );
29
+ ?>
30
+ </div>
31
+
32
  </div>
uninstall.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) { // If uninstall not called from WordPress exit
4
- exit();
5
  }
6
 
7
  /**
@@ -20,6 +20,11 @@ class PLL_Uninstall {
20
  public function __construct() {
21
  global $wpdb;
22
 
 
 
 
 
 
23
  // Check if it is a multisite uninstall - if so, run the uninstall function for each blog id
24
  if ( is_multisite() ) {
25
  foreach ( $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ) as $blog_id ) {
@@ -40,12 +45,6 @@ class PLL_Uninstall {
40
  * @since 0.5
41
  */
42
  public function uninstall() {
43
- $options = get_option( 'polylang' );
44
-
45
- if ( empty( $options['uninstall'] ) ) {
46
- return;
47
- }
48
-
49
  global $wpdb;
50
 
51
  // Executes each module's uninstall script, if it exists
@@ -55,7 +54,7 @@ class PLL_Uninstall {
55
  if ( substr( $module, 0, 1 ) !== '.' ) {
56
  $uninstall_script = $pll_modules_dir . '/' . $module . '/uninstall.php';
57
  if ( file_exists( $uninstall_script ) ) {
58
- require $uninstall_script;
59
  }
60
  }
61
  }
@@ -84,6 +83,7 @@ class PLL_Uninstall {
84
  // Delete users options
85
  foreach ( get_users( array( 'fields' => 'ID' ) ) as $user_id ) {
86
  delete_user_meta( $user_id, 'pll_filter_content' );
 
87
  foreach ( $languages as $lang ) {
88
  delete_user_meta( $user_id, 'description_' . $lang->slug );
89
  }
@@ -126,6 +126,9 @@ class PLL_Uninstall {
126
  }
127
 
128
  // Delete all what is related to languages and translations
 
 
 
129
  foreach ( get_terms( $pll_taxonomies, array( 'hide_empty' => false ) ) as $term ) {
130
  $term_ids[] = (int) $term->term_id;
131
  $tt_ids[] = (int) $term->term_taxonomy_id;
@@ -147,6 +150,7 @@ class PLL_Uninstall {
147
  delete_option( 'widget_polylang' ); // Automatically created by WP
148
  delete_option( 'polylang_wpml_strings' ); // Strings registered with icl_register_string
149
  delete_option( 'polylang_licenses' );
 
150
 
151
  // Delete transients
152
  delete_transient( 'pll_languages_list' );
1
  <?php
2
 
3
  if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) { // If uninstall not called from WordPress exit
4
+ exit;
5
  }
6
 
7
  /**
20
  public function __construct() {
21
  global $wpdb;
22
 
23
+ // Don't do anything except if the constant PLL_REMOVE_ALL_DATA is explicitely defined and true.
24
+ if ( ! defined( 'PLL_REMOVE_ALL_DATA' ) || ! PLL_REMOVE_ALL_DATA ) {
25
+ return;
26
+ }
27
+
28
  // Check if it is a multisite uninstall - if so, run the uninstall function for each blog id
29
  if ( is_multisite() ) {
30
  foreach ( $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ) as $blog_id ) {
45
  * @since 0.5
46
  */
47
  public function uninstall() {
 
 
 
 
 
 
48
  global $wpdb;
49
 
50
  // Executes each module's uninstall script, if it exists
54
  if ( substr( $module, 0, 1 ) !== '.' ) {
55
  $uninstall_script = $pll_modules_dir . '/' . $module . '/uninstall.php';
56
  if ( file_exists( $uninstall_script ) ) {
57
+ require $uninstall_script; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
58
  }
59
  }
60
  }
83
  // Delete users options
84
  foreach ( get_users( array( 'fields' => 'ID' ) ) as $user_id ) {
85
  delete_user_meta( $user_id, 'pll_filter_content' );
86
+ delete_user_meta( $user_id, 'pll_dismissed_notices' ); // Legacy meta.
87
  foreach ( $languages as $lang ) {
88
  delete_user_meta( $user_id, 'description_' . $lang->slug );
89
  }
126
  }
127
 
128
  // Delete all what is related to languages and translations
129
+ $term_ids = array();
130
+ $tt_ids = array();
131
+
132
  foreach ( get_terms( $pll_taxonomies, array( 'hide_empty' => false ) ) as $term ) {
133
  $term_ids[] = (int) $term->term_id;
134
  $tt_ids[] = (int) $term->term_taxonomy_id;
150
  delete_option( 'widget_polylang' ); // Automatically created by WP
151
  delete_option( 'polylang_wpml_strings' ); // Strings registered with icl_register_string
152
  delete_option( 'polylang_licenses' );
153
+ delete_option( 'pll_dismissed_notices' );
154
 
155
  // Delete transients
156
  delete_transient( 'pll_languages_list' );