PluginProbe
Polylang / 2.3.6
Polylang v2.3.6
3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0.3 All 233 releases
polylang / include / filters.php

filters.php in Polylang 2.3.6, at include/filters.php

347 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Setup filters common to admin and frontend
5 *
6 * @since 1.4
7 */
8 class PLL_Filters {
9 public $links_model, $model, $options, $curlang;
10
11 /**
12 * Constructor: setups filters
13 *
14 * @since 1.4
15 *
16 * @param object $polylang
17 */
18 public function __construct( &$polylang ) {
19 $this->links_model = &$polylang->links_model;
20 $this->model = &$polylang->model;
21 $this->options = &$polylang->options;
22 $this->curlang = &$polylang->curlang;
23
24 // Filters the comments according to the current language
25 add_action( 'parse_comment_query', array( $this, 'parse_comment_query' ) );
26 add_filter( 'comments_clauses', array( $this, 'comments_clauses' ), 10, 2 );
27
28 // Filters the get_pages function according to the current language
29 add_filter( 'get_pages', array( $this, 'get_pages' ), 10, 2 );
30
31 // Rewrites next and previous post links to filter them by language
32 add_filter( 'get_previous_post_join', array( $this, 'posts_join' ), 10, 5 );
33 add_filter( 'get_next_post_join', array( $this, 'posts_join' ), 10, 5 );
34 add_filter( 'get_previous_post_where', array( $this, 'posts_where' ), 10, 5 );
35 add_filter( 'get_next_post_where', array( $this, 'posts_where' ), 10, 5 );
36
37 // Converts the locale to a valid W3C locale
38 add_filter( 'language_attributes', array( $this, 'language_attributes' ) );
39
40 // Prevents deleting all the translations of the default category
41 add_filter( 'map_meta_cap', array( $this, 'fix_delete_default_category' ), 10, 4 );
42
43 // Translate the site title in emails sent to users
44 add_filter( 'password_change_email', array( $this, 'translate_user_email' ) );
45 add_filter( 'email_change_email', array( $this, 'translate_user_email' ) );
46
47 // Translates the privacy policy page
48 add_filter( 'option_wp_page_for_privacy_policy', array( $this, 'translate_page_for_privacy_policy' ), 20 ); // Since WP 4.9.6
49
50 // Personal data exporter
51 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'register_personal_data_exporter' ), 0 ); // Since WP 4.9.6
52 }
53
54 /**
55 * Get the language to filter a comments query
56 *
57 * @since 2.0
58 *
59 * @param object $query
60 * @return object|bool the language(s) to use in the filter, false otherwise
61 */
62 protected function get_comments_queried_language( $query ) {
63 // Don't filter comments if comment ids or post ids are specified
64 $plucked = wp_array_slice_assoc( $query->query_vars, array( 'comment__in', 'parent', 'post_id', 'post__in', 'post_parent' ) );
65 $fields = array_filter( $plucked );
66 if ( ! empty( $fields ) ) {
67 return false;
68 }
69
70 // Don't filter comments if a non translated post type is specified
71 if ( ! empty( $query->query_vars['post_type'] ) && ! $this->model->is_translated_post_type( $query->query_vars['post_type'] ) ) {
72 return false;
73 }
74
75 return empty( $query->query_vars['lang'] ) ? $this->curlang : $this->model->get_language( $query->query_vars['lang'] );
76 }
77
78 /**
79 * Adds language dependent cache domain when querying comments
80 * Useful as the 'lang' parameter is not included in cache key by WordPress
81 * Needed since WP 4.6 as comments have been added to persistent cache. See #36906, #37419
82 *
83 * @since 2.0
84 *
85 * @param object $query
86 */
87 public function parse_comment_query( $query ) {
88 if ( $lang = $this->get_comments_queried_language( $query ) ) {
89 $key = '_' . ( is_array( $lang ) ? implode( ',', $lang ) : $this->model->get_language( $lang )->slug );
90 $query->query_vars['cache_domain'] = empty( $query->query_vars['cache_domain'] ) ? 'pll' . $key : $query->query_vars['cache_domain'] . $key;
91 }
92 }
93
94 /**
95 * Filters the comments according to the current language
96 * Used by the recent comments widget and admin language filter
97 *
98 * @since 0.2
99 *
100 * @param array $clauses sql clauses
101 * @param object $query WP_Comment_Query object
102 * @return array modified $clauses
103 */
104 public function comments_clauses( $clauses, $query ) {
105 global $wpdb;
106
107 $lang = $this->get_comments_queried_language( $query );
108
109 if ( ! empty( $lang ) ) {
110 // If this clause is not already added by WP
111 if ( ! strpos( $clauses['join'], '.ID' ) ) {
112 $clauses['join'] .= " JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
113 }
114
115 $clauses['join'] .= $this->model->post->join_clause();
116 $clauses['where'] .= $this->model->post->where_clause( $lang );
117 }
118 return $clauses;
119 }
120
121 /**
122 * Filters get_pages per language
123 *
124 * @since 1.4
125 *
126 * @param array $pages an array of pages already queried
127 * @param array $args get_pages arguments
128 * @return array modified list of pages
129 */
130 public function get_pages( $pages, $args ) {
131 if ( isset( $args['lang'] ) && empty( $args['lang'] ) ) {
132 return $pages;
133 }
134
135 $language = empty( $args['lang'] ) ? $this->curlang : $this->model->get_language( $args['lang'] );
136
137 if ( empty( $language ) || empty( $pages ) || ! $this->model->is_translated_post_type( $args['post_type'] ) ) {
138 return $pages;
139 }
140
141 static $once = false;
142
143 // Obliged to redo the get_pages query if we want to get the right number
144 if ( ! empty( $args['number'] ) && ! $once ) {
145 $once = true; // avoid infinite loop
146
147 $r = array(
148 'lang' => 0, // So this query is not filtered
149 'numberposts' => -1,
150 'nopaging' => true,
151 'post_type' => $args['post_type'],
152 'fields' => 'ids',
153 'tax_query' => array(
154 array(
155 'taxonomy' => 'language',
156 'field' => 'term_taxonomy_id', // Since WP 3.5
157 'terms' => $language->term_taxonomy_id,
158 'operator' => 'NOT IN',
159 ),
160 ),
161 );
162
163 // Take care that 'exclude' argument accepts integer or strings too
164 $args['exclude'] = array_merge( wp_parse_id_list( $args['exclude'] ), get_posts( $r ) );
165 $pages = get_pages( $args );
166 }
167
168 $ids = wp_list_pluck( $pages, 'ID' );
169
170 // Filters the queried list of pages by language
171 if ( ! $once ) {
172 $ids = array_intersect( $ids, $this->model->post->get_objects_in_language( $language ) );
173
174 foreach ( $pages as $key => $page ) {
175 if ( ! in_array( $page->ID, $ids ) ) {
176 unset( $pages[ $key ] );
177 }
178 }
179
180 $pages = array_values( $pages ); // In case 3rd parties suppose the existence of $pages[0]
181 }
182
183 // Not done by WP but extremely useful for performance when manipulating taxonomies
184 update_object_term_cache( $ids, $args['post_type'] );
185
186 $once = false; // In case get_pages is called another time
187 return $pages;
188 }
189
190 /**
191 * Modifies the sql request for get_adjacent_post to filter by the current language
192 *
193 * @since 0.1
194 *
195 * @param string $sql The JOIN clause in the SQL.
196 * @param bool $in_same_term Whether post should be in a same taxonomy term.
197 * @param array $excluded_terms Array of excluded term IDs.
198 * @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true.
199 * @param WP_Post $post WP_Post object.
200 * @return string modified JOIN clause
201 */
202 public function posts_join( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) {
203 return $this->model->is_translated_post_type( $post->post_type ) && ! empty( $this->curlang ) ? $sql . $this->model->post->join_clause( 'p' ) : $sql;
204 }
205
206 /**
207 * Modifies the sql request for wp_get_archives and get_adjacent_post to filter by the current language
208 *
209 * @since 0.1
210 *
211 * @param string $sql The WHERE clause in the SQL.
212 * @param bool $in_same_term Whether post should be in a same taxonomy term.
213 * @param array $excluded_terms Array of excluded term IDs.
214 * @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true.
215 * @param WP_Post $post WP_Post object.
216 * @return string modified WHERE clause
217 */
218 public function posts_where( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) {
219 return $this->model->is_translated_post_type( $post->post_type ) && ! empty( $this->curlang ) ? $sql . $this->model->post->where_clause( $this->curlang ) : $sql;
220 }
221
222 /**
223 * Converts WordPress locale to valid W3 locale in html language attributes
224 *
225 * @since 1.8
226 *
227 * @param string $output language attributes
228 * @return string
229 */
230 public function language_attributes( $output ) {
231 if ( $language = $this->model->get_language( get_locale() ) ) {
232 $output = str_replace( '"' . get_bloginfo( 'language' ) . '"', '"' . $language->get_locale( 'display' ) . '"', $output );
233 }
234 return $output;
235 }
236
237
238 /**
239 * Prevents deleting all the translations of the default category
240 *
241 * @since 2.1
242 *
243 * @param array $caps The user's actual capabilities.
244 * @param string $cap Capability name.
245 * @param int $user_id The user ID.
246 * @param array $args Adds the context to the cap. The category id.
247 * @return array
248 */
249 public function fix_delete_default_category( $caps, $cap, $user_id, $args ) {
250 if ( 'delete_term' === $cap ) {
251 $term = get_term( reset( $args ) ); // Since WP 4.4, we can get the term to get the taxonomy
252 if ( $term instanceof WP_Term ) {
253 $default_cat = get_option( 'default_' . $term->taxonomy );
254 if ( $default_cat && array_intersect( $args, $this->model->term->get_translations( $default_cat ) ) ) {
255 $caps[] = 'do_not_allow';
256 }
257 }
258 }
259
260 return $caps;
261 }
262
263 /**
264 * Translates the site title in emails sent to the user (change email, reset password)
265 * It is necessary to filter the email because WP evaluates the site title before calling switch_to_locale()
266 *
267 * @since 2.1.3
268 *
269 * @param array $email
270 * @return array
271 */
272 public function translate_user_email( $email ) {
273 $blog_name = wp_specialchars_decode( pll__( get_option( 'blogname' ) ), ENT_QUOTES );
274 $email['subject'] = sprintf( $email['subject'], $blog_name );
275 $email['message'] = str_replace( '###SITENAME###', $blog_name, $email['message'] );
276 return $email;
277 }
278
279 /**
280 * Translates the privacy policy page, on both frontend and admin
281 *
282 * @since 2.3.6
283 *
284 * @param int $id Privacy policy page id
285 * @return int
286 */
287 public function translate_page_for_privacy_policy( $id ) {
288 return empty( $this->curlang ) ? $id : $this->model->post->get( $id, $this->curlang );
289 }
290
291 /**
292 * Register our personal data exporter
293 *
294 * @since 2.3.6
295 *
296 * @param array $exporters Personal data exporters
297 * @retun array
298 */
299 public function register_personal_data_exporter( $exporters ) {
300 $exporters[] = array(
301 'exporter_friendly_name' => __( 'Translated user descriptions', 'polylang' ),
302 'callback' => array( $this, 'user_data_exporter' ),
303 );
304 return $exporters;
305 }
306
307 /**
308 * Export translated user description as WP exports only the description in the default language
309 *
310 * @since 2.3.6
311 *
312 * @param string $email_address User email address
313 * @return array Personal data
314 */
315 public function user_data_exporter( $email_address ) {
316 $email_address = trim( $email_address );
317
318 $data_to_export = array();
319
320 if ( $user = get_user_by( 'email', $email_address ) ) {
321 foreach ( $this->model->get_languages_list() as $lang ) {
322 if ( $lang->slug !== $this->options['default_lang'] && $value = get_user_meta( $user->ID, 'description_' . $lang->slug, true ) ) {
323 $user_data_to_export[] = array(
324 /* translators: %s is a language native name */
325 'name' => sprintf( __( 'User description - %s', 'polylang' ), $lang->name ),
326 'value' => $value,
327 );
328 }
329 }
330
331 if ( ! empty( $user_data_to_export ) ) {
332 $data_to_export[] = array(
333 'group_id' => 'user',
334 'group_label' => __( 'User' ),
335 'item_id' => "user-{$user->ID}",
336 'data' => $user_data_to_export,
337 );
338 }
339 }
340
341 return array(
342 'data' => $data_to_export,
343 'done' => true,
344 );
345 }
346 }
347