PluginProbe
Polylang / 2.3
Polylang v2.3
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 / frontend / frontend-filters.php

frontend-filters.php in Polylang 2.3, at frontend/frontend-filters.php

362 lines 12.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Filters content by language on frontend
5 *
6 * @since 1.2
7 */
8 class PLL_Frontend_Filters extends PLL_Filters {
9
10 /**
11 * Constructor: setups filters and actions
12 *
13 * @since 1.2
14 *
15 * @param object $polylang
16 */
17 public function __construct( &$polylang ) {
18 parent::__construct( $polylang );
19
20 // Filters the WordPress locale
21 add_filter( 'locale', array( $this, 'get_locale' ) );
22
23 // Filter sticky posts by current language
24 add_filter( 'option_sticky_posts', array( $this, 'option_sticky_posts' ) );
25
26 // Adds cache domain when querying terms
27 add_filter( 'get_terms_args', array( $this, 'get_terms_args' ) );
28
29 // Filters categories and post tags by language
30 add_filter( 'terms_clauses', array( $this, 'terms_clauses' ), 10, 3 );
31
32 // Rewrites archives links to filter them by language
33 add_filter( 'getarchives_join', array( $this, 'getarchives_join' ), 10, 2 );
34 add_filter( 'getarchives_where', array( $this, 'getarchives_where' ), 10, 2 );
35
36 // Filters the widgets according to the current language
37 add_filter( 'widget_display_callback', array( $this, 'widget_display_callback' ), 10, 2 );
38 add_filter( 'sidebars_widgets', array( $this, 'sidebars_widgets' ) );
39
40 if ( $this->options['media_support'] ) {
41 add_filter( 'widget_media_image_instance', array( $this, 'widget_media_instance' ), 1 ); // Since WP 4.8
42 }
43
44 // Strings translation ( must be applied before WordPress applies its default formatting filters )
45 foreach ( array( 'widget_text', 'widget_title', 'option_blogname', 'option_blogdescription', 'option_date_format', 'option_time_format' ) as $filter ) {
46 add_filter( $filter, 'pll__', 1 );
47 }
48
49 // Translates biography
50 add_filter( 'get_user_metadata', array( $this, 'get_user_metadata' ), 10, 4 );
51
52 // Set posts and terms language when created from frontend ( ex with P2 theme )
53 add_action( 'save_post', array( $this, 'save_post' ), 200, 2 );
54 add_action( 'create_term', array( $this, 'save_term' ), 10, 3 );
55 add_action( 'edit_term', array( $this, 'save_term' ), 10, 3 );
56
57 if ( $this->options['media_support'] ) {
58 add_action( 'add_attachment', array( $this, 'set_default_language' ) );
59 }
60
61 // Support theme customizer
62 // FIXME of course does not work if 'transport' is set to 'postMessage'
63 if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) {
64 add_filter( 'pre_option_blogname', 'pll__', 20 );
65 add_filter( 'pre_option_blogdescription', 'pll__', 20 );
66 }
67
68 // FIXME test get_user_locale for backward compatibility with WP < 4.7
69 if ( Polylang::is_ajax_on_front() && function_exists( 'get_user_locale' ) ) {
70 add_filter( 'load_textdomain_mofile', array( $this, 'load_textdomain_mofile' ) );
71 }
72 }
73
74 /**
75 * Returns the locale based on current language
76 *
77 * @since 0.1
78 *
79 * @param string $locale
80 * @return string
81 */
82 public function get_locale( $locale ) {
83 return $this->curlang->locale;
84 }
85
86 /**
87 * Filters sticky posts by current language
88 *
89 * @since 0.8
90 *
91 * @param array $posts list of sticky posts ids
92 * @return array modified list of sticky posts ids
93 */
94 public function option_sticky_posts( $posts ) {
95 global $wpdb;
96
97 if ( $this->curlang && ! empty( $posts ) ) {
98 $_posts = wp_cache_get( 'sticky_posts', 'options' ); // This option is usually cached in 'all_options' by WP
99
100 if ( empty( $_posts ) || ! is_array( $_posts[ $this->curlang->term_taxonomy_id ] ) ) {
101 $posts = array_map( 'intval', $posts );
102 $posts = implode( ',', $posts );
103
104 $languages = $this->model->get_languages_list( array( 'fields' => 'term_taxonomy_id' ) );
105 $_posts = array_fill_keys( $languages, array() ); // Init with empty arrays
106 $languages = implode( ',', $languages );
107
108 $relations = $wpdb->get_results( "SELECT object_id, term_taxonomy_id FROM {$wpdb->term_relationships} WHERE object_id IN ({$posts}) AND term_taxonomy_id IN ({$languages})" );
109
110 foreach ( $relations as $relation ) {
111 $_posts[ $relation->term_taxonomy_id ][] = $relation->object_id;
112 }
113 wp_cache_add( 'sticky_posts', $_posts, 'options' );
114 }
115
116 $posts = $_posts[ $this->curlang->term_taxonomy_id ];
117 }
118
119 return $posts;
120 }
121
122 /**
123 * Adds language dependent cache domain when querying terms
124 * useful as the 'lang' parameter is not included in cache key by WordPress
125 *
126 * @since 1.3
127 *
128 * @param array $args
129 * @return array
130 */
131 public function get_terms_args( $args ) {
132 $lang = isset( $args['lang'] ) ? $args['lang'] : $this->curlang->slug;
133 $key = '_' . ( is_array( $lang ) ? implode( ',', $lang ) : $lang );
134 $args['cache_domain'] = empty( $args['cache_domain'] ) ? 'pll' . $key : $args['cache_domain'] . $key;
135 return $args;
136 }
137
138 /**
139 * Filters categories and post tags by language when needed
140 *
141 * @since 0.2
142 *
143 * @param array $clauses sql clauses
144 * @param array $taxonomies
145 * @param array $args get_terms arguments
146 * @return array modified sql clauses
147 */
148 public function terms_clauses( $clauses, $taxonomies, $args ) {
149 // Does nothing except on taxonomies which are filterable
150 // Since WP 4.7, make sure not to filter wp_get_object_terms()
151 if ( ! $this->model->is_translated_taxonomy( $taxonomies ) || ! empty( $args['object_ids'] ) ) {
152 return $clauses;
153 }
154
155 // Ugly hack to fix the issue introduced by WP 4.9. See also https://core.trac.wordpress.org/ticket/42104
156 if ( version_compare( $GLOBALS['wp_version'], '4.9', '>=' ) ) {
157 $traces = version_compare( PHP_VERSION, '5.2.5', '>=' ) ? debug_backtrace( false ) : debug_backtrace();
158
159 // PHP 7 does not include call_user_func
160 $n = version_compare( PHP_VERSION, '7', '>=' ) ? 5 : 6;
161 if ( isset( $traces[ $n ]['function'] ) && 'transform_query' === $traces[ $n ]['function'] ) {
162 return $clauses;
163 }
164 }
165
166 // Adds our clauses to filter by language
167 return $this->model->terms_clauses( $clauses, isset( $args['lang'] ) ? $args['lang'] : $this->curlang );
168 }
169
170 /**
171 * Modifies the sql request for wp_get_archives to filter by the current language
172 *
173 * @since 1.9
174 *
175 * @param string $sql JOIN clause
176 * @param array $r wp_get_archives arguments
177 * @return string modified JOIN clause
178 */
179 public function getarchives_join( $sql, $r ) {
180 return ! empty( $r['post_type'] ) && $this->model->is_translated_post_type( $r['post_type'] ) ? $sql . $this->model->post->join_clause() : $sql;
181 }
182
183 /**
184 * Modifies the sql request for wp_get_archives to filter by the current language
185 *
186 * @since 1.9
187 *
188 * @param string $sql WHERE clause
189 * @param array $r wp_get_archives arguments
190 * @return string modified WHERE clause
191 */
192 public function getarchives_where( $sql, $r ) {
193 return ! empty( $r['post_type'] ) && $this->model->is_translated_post_type( $r['post_type'] ) ? $sql . $this->model->post->where_clause( $this->curlang ) : $sql;
194 }
195
196 /**
197 * Filters the widgets according to the current language
198 * Don't display if a language filter is set and this is not the current one
199 *
200 * @since 0.3
201 *
202 * @param array $instance widget settings
203 * @param object $widget WP_Widget object
204 * @return bool|array false if we hide the widget, unmodified $instance otherwise
205 */
206 public function widget_display_callback( $instance, $widget ) {
207 return ! empty( $instance['pll_lang'] ) && $instance['pll_lang'] != $this->curlang->slug ? false : $instance;
208 }
209
210 /**
211 * Remove widgets from sidebars if they are not visible in the current language
212 * Needed to allow is_active_sidebar() to return false if all widgets are not for the current language. See #54
213 *
214 * @since 2.1
215 *
216 * @param array $sidebars_widgets An associative array of sidebars and their widgets
217 * @return array
218 */
219 public function sidebars_widgets( $sidebars_widgets ) {
220 global $wp_registered_widgets;
221
222 foreach ( $sidebars_widgets as $sidebar => $widgets ) {
223 if ( 'wp_inactive_widgets' == $sidebar || empty( $widgets ) ) {
224 continue;
225 }
226
227 foreach ( $widgets as $key => $widget ) {
228 // Nothing can be done if the widget is created using pre WP2.8 API :(
229 // There is no object, so we can't access it to get the widget options
230 if ( ! isset( $wp_registered_widgets[ $widget ]['callback'] ) || ! is_array( $wp_registered_widgets[ $widget ]['callback'] ) || ! isset( $wp_registered_widgets[ $widget ]['callback'][0] ) || ! is_object( $wp_registered_widgets[ $widget ]['callback'][0] ) || ! method_exists( $wp_registered_widgets[ $widget ]['callback'][0], 'get_settings' ) ) {
231 continue;
232 }
233
234 $widget_settings = $wp_registered_widgets[ $widget ]['callback'][0]->get_settings();
235 $number = $wp_registered_widgets[ $widget ]['params'][0]['number'];
236
237 // Remove the widget if not visible in the current language
238 if ( ! empty( $widget_settings[ $number ]['pll_lang'] ) && $widget_settings[ $number ]['pll_lang'] !== $this->curlang->slug ) {
239 unset( $sidebars_widgets[ $sidebar ][ $key ] );
240 }
241 }
242 }
243
244 return $sidebars_widgets;
245 }
246
247 /**
248 * Translates media in media widgets
249 *
250 * @since 2.1.5
251 *
252 * @param array $instance Widget instance data
253 * @return array
254 */
255 public function widget_media_instance( $instance ) {
256 if ( empty( $instance['pll_lang'] ) && $instance['attachment_id'] && $tr_id = pll_get_post( $instance['attachment_id'] ) ) {
257 $instance['attachment_id'] = $tr_id;
258 $attachment = get_post( $tr_id );
259
260 if ( $instance['caption'] && ! empty( $attachment->post_excerpt ) ) {
261 $instance['caption'] = $attachment->post_excerpt;
262 }
263
264 if ( $instance['alt'] && $alt_text = get_post_meta( $tr_id, '_wp_attachment_image_alt', true ) ) {
265 $instance['alt'] = $alt_text;
266 }
267
268 if ( $instance['image_title'] && ! empty( $attachment->post_title ) ) {
269 $instance['image_title'] = $attachment->post_title;
270 }
271 }
272 return $instance;
273 }
274
275 /**
276 * Translates biography
277 *
278 * @since 0.9
279 *
280 * @param null $null
281 * @param int $id User id
282 * @param string $meta_key
283 * @param bool $single Whether to return only the first value of the specified $meta_key
284 * @return null|string
285 */
286 public function get_user_metadata( $null, $id, $meta_key, $single ) {
287 return 'description' === $meta_key && $this->curlang->slug !== $this->options['default_lang'] ? get_user_meta( $id, 'description_' . $this->curlang->slug, $single ) : $null;
288 }
289
290 /**
291 * Allows to set a language by default for posts if it has no language yet
292 *
293 * @since 1.5.4
294 *
295 * @param int $post_id
296 */
297 public function set_default_language( $post_id ) {
298 if ( ! $this->model->post->get_language( $post_id ) ) {
299 if ( isset( $_REQUEST['lang'] ) ) {
300 $this->model->post->set_language( $post_id, $_REQUEST['lang'] );
301 } elseif ( ( $parent_id = wp_get_post_parent_id( $post_id ) ) && $parent_lang = $this->model->post->get_language( $parent_id ) ) {
302 $this->model->post->set_language( $post_id, $parent_lang );
303 } else {
304 $this->model->post->set_language( $post_id, $this->curlang );
305 }
306 }
307 }
308
309 /**
310 * Called when a post ( or page ) is saved, published or updated
311 * Does nothing except on post types which are filterable
312 * Sets the language but does not allow to modify it
313 *
314 * @since 1.1
315 *
316 * @param int $post_id
317 * @param object $post
318 */
319 public function save_post( $post_id, $post ) {
320 if ( $this->model->is_translated_post_type( $post->post_type ) ) {
321 $this->set_default_language( $post_id );
322 }
323 }
324
325 /**
326 * Called when a category or post tag is created or edited
327 * Does nothing except on taxonomies which are filterable
328 * Sets the language but does not allow to modify it
329 *
330 * @since 1.1
331 *
332 * @param int $term_id
333 * @param int $tt_id Term taxonomy id
334 * @param string $taxonomy
335 */
336 public function save_term( $term_id, $tt_id, $taxonomy ) {
337 if ( $this->model->is_translated_taxonomy( $taxonomy ) && ! $this->model->term->get_language( $term_id ) ) {
338 if ( isset( $_REQUEST['lang'] ) ) {
339 $this->model->term->set_language( $term_id, $_REQUEST['lang'] );
340 } elseif ( ( $term = get_term( $term_id, $taxonomy ) ) && ! empty( $term->parent ) && $parent_lang = $this->model->term->get_language( $term->parent ) ) {
341 $this->model->term->set_language( $term_id, $parent_lang );
342 } else {
343 $this->model->term->set_language( $term_id, $this->curlang );
344 }
345 }
346 }
347
348 /**
349 * Filters the translation files to load when doing ajax on front
350 * This is needed because WP the language files associated to the user locale when a user is logged in
351 *
352 * @since 2.2.6
353 *
354 * @param string $mofile Translation file name
355 * @return string
356 */
357 public function load_textdomain_mofile( $mofile ) {
358 $user_locale = get_user_locale();
359 return str_replace( "{$user_locale}.mo", "{$this->curlang->locale}.mo", $mofile );
360 }
361 }
362