PluginProbe
Polylang / 1.0.1
Polylang v1.0.1
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 / base.php

base.php in Polylang 1.0.1, at include/base.php

367 lines 13.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // setups basic functions used for admin and frontend
4 abstract class Polylang_Base {
5 public $post_types; // post types to filter by language
6 public $taxonomies; // taxonomies to filter by language
7
8 protected $options;
9 protected $home;
10 protected $strings = array(); // strings to translate
11
12 // used to cache results
13 // FIXME use wp_cache ?
14 private $languages_list = array();
15 private $language = array();
16 private $links = array();
17
18 function __construct() {
19 // init options often needed
20 $this->options = get_option('polylang');
21 $this->home = get_option('home');
22
23 add_action('wp_loaded', array(&$this, 'add_post_types_taxonomies'), 1); // must come after 'init' to be sure that all post types and taxonomies are registered
24 }
25
26 // init post types and taxonomies to filter by language
27 function add_post_types_taxonomies() {
28 $post_types = array('post', 'page');
29 if (!empty($this->options['media_support']))
30 $post_types[] = 'attachment';
31 if (!empty($this->options['post_types']))
32 $post_types = array_merge($post_types, $this->options['post_types']);
33 $this->post_types = apply_filters('pll_get_post_types', $post_types, false);
34
35 $taxonomies = array('category', 'post_tag');
36 if (!empty($this->options['taxonomies']))
37 $taxonomies = array_merge($taxonomies, $this->options['taxonomies']);
38 $this->taxonomies = apply_filters('pll_get_taxonomies', $taxonomies, false);
39 }
40
41 // returns the list of available languages
42 function get_languages_list($args = array()) {
43 // although get_terms is cached, it is efficient to add our own cache
44 // FIXME don't use on admin in case we modify the list of languages!
45 if (!is_admin() && isset($this->languages_list[$cache_key = md5(serialize($args))]))
46 return $this->languages_list[$cache_key];
47
48 $args = wp_parse_args($args, array('hide_empty' => false, 'orderby'=> 'term_group'));
49 return is_admin() ? get_terms('language', $args) : $this->languages_list[$cache_key] = get_terms('language', $args);
50 }
51
52 // retrieves the dropdown list of the languages
53 function dropdown_languages($args = array()) {
54 $args = apply_filters('pll_dropdown_language_args', $args);
55 $defaults = array('name' => 'lang_choice', 'class' => '', 'add_options' => array(), 'hide_empty' => false, 'value' => 'slug', 'selected' => '');
56 extract(wp_parse_args($args, $defaults));
57
58 // sort $add_options by value
59 uasort($add_options, create_function('$a, $b', "return \$a['value'] > \$b['value'];" ));
60
61 $out = sprintf(
62 '<select name="%1$s" id="%1$s"%2$s>'."\n",
63 esc_attr($name),
64 $class ? ' class="'.esc_attr($class).'"' : ''
65 );
66
67 foreach ($add_options as $option)
68 $out .= sprintf(
69 '<option value="%s">%s</option>'."\n",
70 esc_attr($option['value']),
71 esc_html($option['text'])
72 );
73
74 foreach ($this->get_languages_list($args) as $language) {
75 $out .= sprintf(
76 '<option value="%s"%s>%s</option>\n',
77 esc_attr($language->$value),
78 $language->$value == $selected ? ' selected="selected"' : '',
79 esc_html($language->name)
80 );
81 }
82 $out .= "</select>\n";
83 return $out;
84 }
85
86 // returns the language by its id or its slug
87 // Note: it seems that get_term_by slug is not cached (3.2.1)
88 function get_language($value) {
89 if (is_object($value))
90 return $value;
91
92 if (isset($this->language[$value]))
93 return $this->language[$value];
94
95 $lang = (is_numeric($value) || (int) $value) ? get_term((int) $value, 'language') :
96 (is_string($value) ? get_term_by('slug', $value , 'language') : false);
97
98 return !empty($lang) && !is_wp_error($lang) ? ($this->language[$value] = $lang) : false;
99 }
100
101 // saves translations for posts or terms
102 // the underscore in '_lang' hides the post meta in the Custom Fields metabox in the Edit Post screen
103 // $type: either 'post' or 'term'
104 // $id: post id or term id
105 // $translations: an associative array of translations with language code as key and translation id as value
106 function save_translations($type, $id, $translations) {
107 $lang = call_user_func(array(&$this, 'get_'.$type.'_language'), $id);
108 if (!$lang)
109 return;
110
111 if (isset($translations) && is_array($translations)) {
112 $translations = array_merge(array($lang->slug => $id), $translations);
113 foreach($translations as $p)
114 update_metadata($type, (int) $p, '_translations', $translations);
115 }
116 }
117
118 // deletes a translation of a post or term
119 function delete_translation($type, $id) {
120 $translations = $this->get_translations($type, $id);
121 if (is_array($translations)) {
122 $slug = array_search($id, $translations);
123 unset($translations[$slug]);
124 foreach($translations as $p)
125 update_metadata($type, (int) $p, '_translations', $translations);
126 delete_metadata($type, $id, '_translations');
127 }
128 }
129
130 // returns the id of the translation of a post or term
131 // $type: either 'post' or 'term'
132 // $id: post id or term id
133 // $lang: object or slug (in the order of preference latest to avoid)
134 function get_translation($type, $id, $lang) {
135 $translations = $this->get_translations($type, $id);
136 $slug = $this->get_language($lang)->slug;
137 return isset($translations[$slug]) ? (int) $translations[$slug] : false;
138 }
139
140 // returns an array of translations of a post or term
141 function get_translations($type, $id) {
142 $type = ($type == 'post' || in_array($type, $this->post_types)) ? 'post' : (($type == 'term' || in_array($type, $this->taxonomies)) ? 'term' : false);
143 // maybe_unserialize due to useless serialization in versions < 0.9
144 return $type ? maybe_unserialize(get_metadata($type, $id, '_translations', true)) : array();
145 }
146
147 // store the post language in the database
148 function set_post_language($post_id, $lang) {
149 wp_set_post_terms($post_id, $lang ? $this->get_language($lang)->slug : '', 'language' );
150 }
151
152 // returns the language of a post
153 function get_post_language($post_id) {
154 $lang = get_the_terms($post_id, 'language' );
155 return ($lang) ? reset($lang) : false; // there's only one language per post : first element of the array returned
156 }
157
158 // among the post and its translations, returns the id of the post which is in $lang
159 function get_post($post_id, $lang) {
160 $post_lang = $this->get_post_language($post_id);
161 if (!$lang || !$post_lang)
162 return false;
163
164 $lang = $this->get_language($lang);
165 return $post_lang->term_id == $lang->term_id ? $post_id : $this->get_translation('post', $post_id, $lang);
166 }
167
168 // store the term language in the database
169 function set_term_language($term_id, $lang) {
170 update_metadata('term', $term_id, '_language', $lang ? $this->get_language($lang)->term_id : 0);
171 }
172
173 // remove the term language in the database
174 function delete_term_language($term_id) {
175 delete_metadata('term', $term_id, '_language');
176 }
177
178 // returns the language of a term
179 function get_term_language($value, $taxonomy = '') {
180 if (is_numeric($value))
181 $term_id = $value;
182
183 elseif (is_string($value) && $taxonomy)
184 $term_id = get_term_by('slug', $value , $taxonomy)->term_id;
185
186 return empty($term_id) ? false : $this->get_language(get_metadata('term', $term_id, '_language', true));
187 }
188
189 // among the term and its translations, returns the id of the term which is in $lang
190 function get_term($term_id, $lang) {
191 $lg = $this->get_term_language($term_id);
192 if (!$lang || !$lg)
193 return false;
194
195 $lang = $this->get_language($lang);
196 return $lg->term_id == $lang->term_id ? $term_id : $this->get_translation('term', $term_id, $lang);
197 }
198
199 // adds language information to a link when using pretty permalinks
200 function add_language_to_link($url, $lang) {
201 if (empty($lang))
202 return $url;
203
204 global $wp_rewrite;
205 if ($wp_rewrite->using_permalinks()) {
206 $base = $this->options['rewrite'] ? '' : 'language/';
207 $slug = $this->options['default_lang'] == $lang->slug && $this->options['hide_default'] ? '' : $base.$lang->slug.'/';
208 return esc_url(str_replace($this->home.'/'.$wp_rewrite->root, $this->home.'/'.$wp_rewrite->root.$slug, $url));
209 }
210
211 // special case for pages which do not accept adding the lang parameter
212 elseif ('_get_page_link' != current_filter())
213 return add_query_arg( 'lang', $lang->slug, $url );
214
215 return $url;
216 }
217
218 // optionally rewrite posts, pages links to filter them by language
219 // rewrite post format (and optionally categories and post tags) archives links to filter them by language
220 function add_post_term_link_filters() {
221 if ($this->options['force_lang'] && $GLOBALS['wp_rewrite']->using_permalinks()) {
222 foreach (array('post_link', '_get_page_link', 'post_type_link') as $filter)
223 add_filter($filter, array(&$this, 'post_link'), 10, 2);
224 }
225
226 add_filter('term_link', array(&$this, 'term_link'), 10, 3);
227 }
228
229 // modifies post & page links
230 function post_link($link, $post) {
231 if (isset($this->links[$link]))
232 return $this->links[$link];
233
234 if ('post_type_link' == current_filter() && !in_array($post->post_type, $this->post_types))
235 return $this->links[$link] = $link;
236
237 if ('_get_page_link' == current_filter()) // this filter uses the ID instead of the post object
238 $post = get_post($post);
239
240 // /!\ when post_status in not "publish", WP does not use pretty permalinks
241 return $this->links[$link] = $post->post_status != 'publish' ? $link : $this->add_language_to_link($link, $this->get_post_language($post->ID));
242 }
243
244 // modifies term link
245 function term_link($link, $term, $tax) {
246 if (isset($this->links[$link]))
247 return $this->links[$link];
248
249 return $this->links[$link] = ($tax == 'post_format' || ($this->options['force_lang'] && $GLOBALS['wp_rewrite']->using_permalinks() && in_array($tax, $this->taxonomies))) ?
250 $this->add_language_to_link($link, $this->get_term_language($term->term_id)) : $link;
251 }
252
253 // returns the html link to the flag if exists
254 // $lang: object
255 function get_flag($lang, $url_only = false) {
256 if (file_exists(POLYLANG_DIR.($file = '/flags/'.$lang->description.'.png')))
257 $url = POLYLANG_URL.$file;
258
259 // overwrite with custom flags
260 // never use custom flags on admin side
261 if (!is_admin() && ( file_exists(PLL_LOCAL_DIR.($file = '/'.$lang->description.'.png')) || file_exists(PLL_LOCAL_DIR.($file = '/'.$lang->description.'.jpg')) ))
262 $url = PLL_LOCAL_URL.$file;
263
264 return empty($url) ? '' :
265 ($url_only ? $url :
266 sprintf(
267 '<img src="%s" title="%s" alt="%s" />',
268 esc_url($url),
269 esc_attr(apply_filters('pll_flag_title', $lang->name, $lang->slug, $lang->description)),
270 esc_attr($lang->name)
271 ));
272 }
273
274 // adds clauses to comments query - used in both frontend and admin
275 function _comments_clauses($clauses, $lang) {
276 global $wpdb;
277
278 // if this clause is not already added by WP
279 if (!strpos($clauses['join'], '.ID'))
280 $clauses['join'] .= " JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
281
282 $clauses['join'] .= " INNER JOIN $wpdb->term_relationships AS pll_tr ON pll_tr.object_id = ID";
283 $clauses['where'] .= $wpdb->prepare(" AND pll_tr.term_taxonomy_id = %d", $lang->term_taxonomy_id);
284
285 return $clauses;
286 }
287
288 // adds terms clauses to get_terms - used in both frontend and admin
289 function _terms_clauses($clauses, $lang) {
290 global $wpdb;
291 if (!empty($lang)) {
292 // the query is coming from Polylang and the $lang is an object
293 if (is_object($lang))
294 $languages = $lang->term_id;
295
296 // the query is coming from outside with 'lang' parameter and $lang is a comma separated list of slugs (or an array of slugs)
297 else {
298 $languages = is_array($lang) ? $lang : explode(',', $lang);
299 $languages = "'" . implode("','", array_map( 'sanitize_title_for_query', $languages)) . "'";
300 $languages = $wpdb->get_col($wpdb->prepare("
301 SELECT $wpdb->term_taxonomy.term_id FROM $wpdb->term_taxonomy
302 INNER JOIN $wpdb->terms USING (term_id)
303 WHERE taxonomy = 'language' AND $wpdb->terms.slug IN (%s)",
304 $languages
305 )); // get ids from slugs
306 $languages = implode(',', $languages);
307 }
308
309 $clauses['join'] .= " LEFT JOIN $wpdb->termmeta AS pll_tm ON t.term_id = pll_tm.term_id";
310 $clauses['where'] .= $wpdb->prepare(" AND pll_tm.meta_key = '_language' AND pll_tm.meta_value IN (%s)", $languages);
311 }
312 return $clauses;
313 }
314
315 // returns all page ids *not in* language defined by $lang_id
316 function exclude_pages($lang_id) {
317 // 'suppress_filter' is true by default so this query is not filtered by our pre_get_post filter in core.php
318 return get_posts(array(
319 'numberposts' => -1,
320 'post_type' => array_intersect(get_post_types(array('hierarchical' => 1)), $this->post_types),
321 'fields' => 'ids',
322 'tax_query' => array(array(
323 'taxonomy' => 'language',
324 'fields' => 'id',
325 'terms' => $lang_id,
326 'operator' => 'NOT IN'
327 ))
328 ));
329 }
330
331 // export a mo object in options
332 function mo_export($mo, $lang) {
333 $strings = array();
334 foreach ($mo->entries as $entry)
335 $strings[] = array($entry->singular, $mo->translate($entry->singular));
336 update_option('polylang_mo'.$lang->term_id, $strings);
337 }
338
339 // import a mo object from options
340 function mo_import($lang) {
341 $mo = new MO();
342 if ($strings = get_option('polylang_mo'.$lang->term_id)) {
343 foreach ($strings as $msg)
344 $mo->add_entry($mo->make_entry($msg[0], $msg[1]));
345 }
346 return $mo;
347 }
348
349 // list the post metas to synchronize
350 static function list_metas_to_sync() {
351 return array(
352 'taxonomies' => __('Taxonomies', 'polylang'),
353 'post_meta' => __('Custom fields', 'polylang'),
354 'comment_status' => __('Comment status', 'polylang'),
355 'ping_status' => __('Ping status', 'polylang'),
356 'sticky_posts' => __('Sticky posts', 'polylang'),
357 'post_date' => __('Published date', 'polylang'),
358 'post_format' => __('Post format', 'polylang'),
359 'post_parent' => __('Page parent', 'polylang'),
360 '_wp_page_template' => __('Page template', 'polylang'),
361 'menu_order' => __('Page order', 'polylang'),
362 '_thumbnail_id' => __('Featured image', 'polylang'),
363 );
364 }
365
366 } //class Polylang_Base
367