PluginProbe
Polylang / 1.4
Polylang v1.4
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 / model.php

model.php in Polylang 1.4, at include/model.php

735 lines 24.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * setups the language and translations model based on WordPress taxonomies
5 *
6 * @since 1.2
7 */
8 class PLL_Model {
9 public $options;
10 private $languages; // used to cache the list of languages
11
12 /*
13 * constructor: registers custom taxonomies and setups filters and actions
14 *
15 * @since 1.2
16 *
17 * @param array $options Polylang options
18 */
19 public function __construct(&$options) {
20 $this->options = &$options;
21
22 // register our taxonomies as soon as possible
23 // this is early registration, not ready for rewrite rules as wp_rewrite will be setup later
24 // FIXME should I supply an 'update_count_callback' for taxonomies other than 'language' (currently not needed by PLL)?
25 foreach (array('language', 'term_language', 'post_translations', 'term_translations') as $tax) {
26 register_taxonomy($tax,
27 false !== strpos($tax, 'term_') ? 'term' : null ,
28 array('label' => false, 'public' => false, 'query_var' => false, 'rewrite' => false, '_pll' => true)
29 );
30 }
31
32 add_filter('get_terms', array(&$this, '_prime_terms_cache'), 10, 2);
33 add_filter('wp_get_object_terms', array(&$this, 'wp_get_object_terms'), 10, 3);
34
35 // we need to clean languages cache when editing a language,
36 // when editing page of front or when modifying the permalink structure
37 add_action('edited_term_taxonomy', array(&$this, 'clean_languages_cache'), 10, 2);
38 add_action('update_option_page_on_front', array(&$this, 'clean_languages_cache'));
39 add_action('update_option_permalink_structure', array(&$this, 'clean_languages_cache'));
40
41 // registers completely the language taxonomy
42 add_action('setup_theme', array(&$this, 'register_taxonomy'), 1);
43
44 // setups post types to translate
45 add_action('registered_post_type', array(&$this, 'registered_post_type'));
46
47 // just in case someone would like to display the language description ;-)
48 add_filter('language_description', create_function('$v', "return '';"));
49
50 // adds cache domain when querying terms
51 add_filter('get_terms_args', array(&$this, 'get_terms_args'));
52 }
53
54 /*
55 * cache language and translations when terms are queried by get_terms
56 *
57 * @since 1.2
58 *
59 * @param array $terms queried terms
60 * @param array $taxonomies queried taxonomies
61 * @return array unmodified $terms
62 */
63 public function _prime_terms_cache($terms, $taxonomies) {
64 if ($this->is_translated_taxonomy($taxonomies)) {
65 foreach ($terms as $term) {
66 $term_ids[] = is_object($term) ? $term->term_id : $term;
67 }
68 }
69
70 if (!empty($term_ids))
71 update_object_term_cache(array_unique($term_ids), 'term'); // adds language and translation of terms to cache
72 return $terms;
73 }
74
75 /*
76 * when terms are found for posts, add their language and translations to cache
77 *
78 * @since 1.2
79 *
80 * @param array $terms terms found
81 * @param array $object_ids not used
82 * @param array $taxonomies terms taxonomies
83 * @return array unmodified $terms
84 */
85 // FIXME is that useful? or even desirable?
86 public function wp_get_object_terms($terms, $object_ids, $taxonomies) {
87 $taxonomies = explode("', '", trim($taxonomies, "'"));
88 if (!in_array('term_translations', $taxonomies))
89 $this->_prime_terms_cache($terms, $taxonomies);
90 return $terms;
91 }
92
93 /*
94 * wrap wp_get_object_terms to cache it and return only one object
95 * inspired by the function get_the_terms
96 *
97 * @since 1.2
98 *
99 * @param int $object_id post_id or term_id
100 * @param string $taxonomy Polylang taxonomy depending if we are looking for a post (or term) language (or translation)
101 * @return bool|object the term associated to the object in the requested taxonomy if exists, false otherwise
102 */
103 protected function get_object_term($object_id, $taxonomy) {
104 $term = get_object_term_cache($object_id, $taxonomy);
105
106 if ( false === $term ) {
107 // query language and translations at the same time
108 $taxonomies = (false !== strpos($taxonomy, 'term_')) ?
109 array('term_language', 'term_translations') :
110 array('language', 'post_translations');
111
112 foreach (wp_get_object_terms($object_id, $taxonomies) as $t) {
113 wp_cache_add($object_id, array($t), $t->taxonomy . '_relationships'); // store it the way WP wants it
114 if ($t->taxonomy == $taxonomy)
115 $term = $t;
116 }
117 }
118 else {
119 $term = reset($term);
120 }
121
122 return empty($term) ? false : $term;
123 }
124
125 /*
126 * returns the list of available languages
127 * caches the list in a db transient (except flags)
128 * caches the list (with flags) in the private property $languages
129 *
130 * list of parameters accepted in $args:
131 *
132 * hide_empty => hides languages with no posts if set to true (defaults to false)
133 * fields => return only that field if set (see PLL_Language for a list of fields)
134 *
135 * @since 0.1
136 *
137 * @param array $args
138 * @return array|string|int list of PLL_Language objects or PLL_Language object properties
139 */
140 public function get_languages_list($args = array()) {
141 if (empty($this->languages)) {
142 if (false === ($languages = get_transient('pll_languages_list'))) {
143 $languages = get_terms('language', array('hide_empty' => false, 'orderby'=> 'term_group'));
144 $languages = empty($languages) || is_wp_error($languages) ? array() : $languages;
145
146 $term_languages = get_terms('term_language', array('hide_empty' => false));
147 $term_languages = empty($term_languages) || is_wp_error($term_languages) ?
148 array() : array_combine(wp_list_pluck($term_languages, 'name'), $term_languages);
149
150 if (!empty($languages) && !empty($term_languages)) {
151 array_walk($languages, create_function('&$v, $k, $term_languages', '$v = new PLL_Language($v, $term_languages[$v->name]);'), $term_languages);
152 }
153 else {
154 $languages = array(); // in case something went wrong
155 }
156 }
157
158 $this->languages = $languages;
159
160 // need to wait for $wp_rewrite availibility to set homepage urls
161 did_action('setup_theme') ? $this->_languages_list() : add_action('setup_theme', array(&$this, '_languages_list'));
162 }
163
164 $args = wp_parse_args($args, array('hide_empty' => false));
165 extract($args);
166
167 // remove empty languages if requested
168 $languages = array_filter($this->languages, create_function('$v', sprintf('return $v->count || !%d;', $hide_empty)));
169
170 return empty($fields) ? $languages : wp_list_pluck($languages, $fields);
171 }
172
173 /*
174 * fills home urls and flags in language list and set transient in db
175 * delayed to be sure we have access to $wp_rewrite for home urls
176 * home urls are not cached in db if PLL_CACHE_HOME_URL is set to false
177 *
178 * @since 1.4
179 */
180 public function _languages_list() {
181 if (false === get_transient('pll_languages_list')) {
182 if (!defined('PLL_CACHE_HOME_URL') || PLL_CACHE_HOME_URL) {
183 foreach ($this->languages as $language)
184 $language->set_home_url();
185 }
186 set_transient('pll_languages_list', $this->languages);
187 }
188
189 foreach ($this->languages as $language) {
190 if (defined('PLL_CACHE_HOME_URL') && !PLL_CACHE_HOME_URL)
191 $language->set_home_url();
192
193 // add flags (not in db cache as they may be different on frontend and admin)
194 $language->set_flag();
195 }
196 }
197
198 /*
199 * cleans language cache
200 * can be called directly with no parameter
201 * called by the 'edited_term_taxonomy' filter with 2 parameters when count needs to be updated
202 *
203 * @since 1.2
204 *
205 * @param int $term not used
206 * @param string $taxonomy taxonomy name
207 */
208 public function clean_languages_cache($term = 0, $taxonomy = null) {
209 if (empty($taxonomy->name) || 'language' == $taxonomy->name) {
210 delete_transient('pll_languages_list');
211 $this->languages = array();
212 }
213 }
214
215 /*
216 * returns the language by its term_id, tl_term_id, slug or locale
217 *
218 * @since 0.1
219 *
220 * @param int|string term_id, tl_term_id, slug or locale of the queried language
221 * @return object|bool PLL_Language object, false if no language found
222 */
223 public function get_language($value) {
224 static $language;
225
226 if (is_object($value))
227 return $this->get_language($value->term_id); // will force cast to PLL_Language
228
229 if (empty($language[$value])) {
230 foreach ($this->get_languages_list() as $lang)
231 $language[$lang->term_id] = $language[$lang->tl_term_id] = $language[$lang->slug] = $language[$lang->locale] = $lang;
232 }
233
234 return empty($language[$value]) ? false : $language[$value];
235 }
236
237 /*
238 * saves translations for posts or terms
239 *
240 * @since 0.5
241
242 * @param string $type either 'post' or 'term'
243 * @param int $id post id or term id
244 * @param array $translations: an associative array of translations with language code as key and translation id as value
245 */
246 public function save_translations($type, $id, $translations) {
247 if (($lang = call_user_func(array(&$this, 'get_'.$type.'_language'), $id)) && isset($translations) && is_array($translations)) {
248 // first unlink this object
249 foreach ($this->get_translations($type, $id) as $object_id)
250 $this->delete_translation($type, $object_id);
251
252 $translations = array_merge(array($lang->slug => $id), $translations); // make sure this object is in tranlations
253 $translations = array_diff($translations, array(0)); // don't keep non translated languages
254
255 // don't create a translation group for untranslated posts as it is useless
256 // but we need one for terms to allow relationships remap when importing from a WXR file
257 if ('term' == $type || count($translations) > 1) {
258 $terms = wp_get_object_terms($translations, $type . '_translations');
259 $term = array_pop($terms);
260
261 // create a new term if necessary
262 empty($term) ?
263 wp_insert_term($group = uniqid('pll_'), $type . '_translations', array('description' => serialize($translations))) :
264 wp_update_term($group = (int) $term->term_id, $type . '_translations', array('description' => serialize($translations)));
265
266 // link all translations to the new term
267 foreach($translations as $p)
268 wp_set_object_terms($p, $group, $type . '_translations');
269 }
270 }
271 }
272
273 /*
274 * deletes a translation of a post or term
275 *
276 * @since 0.5
277 *
278 * @param string $type either 'post' or 'term'
279 * @param int $id post id or term id
280 */
281 public function delete_translation($type, $id) {
282 $term = $this->get_object_term($id, $type . '_translations');
283
284 if (!empty($term)) {
285 $translations = unserialize($term->description);
286
287 if (is_array($translations)) {
288 $slug = array_search($id, $translations);
289 unset($translations[$slug]);
290
291 empty($translations) || 1 == count($translations) ?
292 wp_delete_term((int) $term->term_id, $type . '_translations') :
293 wp_update_term((int) $term->term_id, $type . '_translations', array('description' => serialize($translations)));
294
295 wp_set_object_terms($id, null, $type . '_translations');
296 }
297 }
298 }
299
300 /*
301 * returns the id of the translation of a post or term
302 *
303 * @since 0.5
304 *
305 * @param string $type either 'post' or 'term'
306 * @param int $id post id or term id
307 * @param object|string $lang object or slug
308 * @return bool|int post id or term id of the translation, flase if there is none
309 */
310 public function get_translation($type, $id, $lang) {
311 if (!$lang = $this->get_language($lang))
312 return false;
313
314 $translations = $this->get_translations($type, $id);
315
316 return isset($translations[$lang->slug]) ? (int) $translations[$lang->slug] : false;
317 }
318
319 /*
320 * returns an array of translations of a post or term
321 *
322 * @since 0.5
323 *
324 * @param string $type either 'post' or 'term'
325 * @param int $id post id or term id
326 * @return array an associative array of translations with language code as key and translation id as value
327 */
328 public function get_translations($type, $id) {
329 $type = ($type == 'post' || $this->is_translated_post_type($type)) ? 'post' : (($type == 'term' || $this->is_translated_taxonomy($type)) ? 'term' : false);
330 return $type && ($term = $this->get_object_term($id, $type . '_translations')) && !empty($term) ? unserialize($term->description) : array();
331 }
332
333 /*
334 * store the post language in the database
335 *
336 * @since 0.6
337 *
338 * @param int $post_id post id
339 * @param int|string|object language (term_id or slug or object)
340 */
341 public function set_post_language($post_id, $lang) {
342 wp_set_post_terms($post_id, $lang ? $this->get_language($lang)->slug : '', 'language' );
343 }
344
345 /*
346 * returns the language of a post
347 *
348 * @since 0.1
349 *
350 * @param int $post_id post id
351 * @return bool|object PLL_Language object, false if no language is associated to that post
352 */
353 public function get_post_language($post_id) {
354 $lang = $this->get_object_term($post_id, 'language' );
355 return ($lang) ? $this->get_language($lang) : false;
356 }
357
358 /*
359 * among the post and its translations, returns the id of the post which is in $lang
360 *
361 * @since 0.1
362 *
363 * @param int $post_id post id
364 * @param int|string|object language (term_id or slug or object)
365 * @return bool|int the translation post id if exists, otherwise the post id, false if the post has no language
366 */
367 public function get_post($post_id, $lang) {
368 $post_lang = $this->get_post_language($post_id); // FIXME is this necessary?
369 if (!$lang || !$post_lang)
370 return false;
371
372 $lang = $this->get_language($lang);
373 return $post_lang->term_id == $lang->term_id ? $post_id : $this->get_translation('post', $post_id, $lang);
374 }
375
376 /*
377 * stores the term language in the database
378 *
379 * @since 0.6
380 *
381 * @param int $term_id term id
382 * @param int|string|object language (term_id or slug or object)
383 */
384 public function set_term_language($term_id, $lang) {
385 wp_set_object_terms($term_id, $lang ? $this->get_language($lang)->tl_term_id : '', 'term_language');
386 }
387
388 /*
389 * removes the term language in database
390 *
391 * @since 0.5
392 *
393 * @param int $term_id term id
394 */
395 public function delete_term_language($term_id) {
396 wp_delete_object_term_relationships($term_id, 'term_language');
397 }
398
399 /*
400 * returns the language of a term
401 *
402 * @since 0.1
403 *
404 * @param int|string $value term id or term slug
405 * @param string $taxonomy optional taxonomy needed when the term slug is passed as first parameter
406 * @return bool|object PLL_Language object, false if no language is associated to that term
407 */
408 public function get_term_language($value, $taxonomy = '') {
409 if (is_numeric($value))
410 $term_id = $value;
411
412 // get_term_by still not cached in WP 3.5.1 but internally, the function is always called by term_id
413 elseif (is_string($value) && $taxonomy)
414 $term_id = get_term_by('slug', $value , $taxonomy)->term_id;
415
416 $lang = $this->get_object_term($term_id, 'term_language');
417
418 // switch to PLL_Language
419 return ($lang) ? $this->get_language($lang->term_id) : false;
420 }
421
422 /*
423 * among the term and its translations, returns the id of the term which is in $lang
424 *
425 * @since 0.1
426 *
427 * @param int $term_id term id
428 * @param int|string|object language (term_id or slug or object)
429 * @return bool|int the translation term id if exists, otherwise the term id, false if the term has no language
430 */
431 public function get_term($term_id, $lang) {
432 $lg = $this->get_term_language($term_id); // FIXME is this necessary?
433 if (!$lang || !$lg)
434 return false;
435
436 $lang = $this->get_language($lang);
437 return $lg->term_id == $lang->term_id ? $term_id : $this->get_translation('term', $term_id, $lang);
438 }
439
440 /*
441 * a join clause to add to sql queries when filtering by language is needed directly in query
442 *
443 * @since 1.2
444 *
445 * @param string $type either 'post' or 'term'
446 * @return string join clause
447 */
448 public function join_clause($type) {
449 global $wpdb;
450 return " INNER JOIN $wpdb->term_relationships AS pll_tr ON pll_tr.object_id = " . ('term' == $type ? "t.term_id" : "ID");
451 }
452
453 /*
454 * a where clause to add to sql queries when filtering by language is needed directly in query
455 *
456 * @since 1.2
457 *
458 * @param object|array|string $lang a PLL_Language object or a comma separated list of languag slug or an array of language slugs
459 * @param string $type either 'post' or 'term'
460 * @return string where clause
461 */
462 public function where_clause($lang, $type) {
463 global $wpdb;
464 $tt_id = 'term' == $type ? 'tl_term_taxonomy_id' : 'term_taxonomy_id';
465
466 // $lang is an object
467 // generally the case if the query is coming from Polylang
468 if (is_object($lang))
469 return $wpdb->prepare(" AND pll_tr.term_taxonomy_id = %d", $lang->$tt_id);
470
471 // $lang is a comma separated list of slugs (or an array of slugs)
472 // generally the case is the query is coming from outside with 'lang' parameter
473 $slugs = is_array($lang) ? $lang : explode(',', $lang);
474 foreach ($slugs as $slug)
475 $languages[] = (int) $this->get_language($slug)->$tt_id;
476
477 return " AND pll_tr.term_taxonomy_id IN (" . implode(',', $languages) . ")";
478 }
479
480 /*
481 * adds terms clauses to get_terms to filter them by languages - used in both frontend and admin
482 *
483 * @since 1.2
484 *
485 * @param array $clauses the list of sql clauses in terms query
486 * @param object $lang PLL_Language object
487 * @return array modifed list of clauses
488 */
489 public function terms_clauses($clauses, $lang) {
490 if (!empty($lang)) {
491 $clauses['join'] .= $this->join_clause('term');
492 $clauses['where'] .= $this->where_clause($lang, 'term');
493 }
494 return $clauses;
495 }
496
497 /*
498 * register the language taxonomy
499 *
500 * @since 1.2
501 */
502 public function register_taxonomy() {
503 // registers the language taxonomy
504 register_taxonomy('language', $this->get_translated_post_types(), array(
505 'labels' => array(
506 'name' => __('Languages', 'polylang'),
507 'singular_name' => __('Language', 'polylang'),
508 'all_items' => __('All languages', 'polylang'),
509 ),
510 'public' => false, // avoid displaying the 'like post tags text box' in the quick edit
511 'query_var' => 'lang',
512 '_pll' => true // polylang taxonomy
513 ));
514 }
515
516 /*
517 * returns post types that need to be translated
518 *
519 * @since 1.2
520 *
521 * @param bool $filter true if we should return only valid registered post types
522 * @return array post type names for which Polylang manages languages and translations
523 */
524 public function get_translated_post_types($filter = true) {
525 static $post_types = array();
526
527 // the post types list is cached for better better performance
528 // wait for 'after_setup_theme' to apply the cache to allow themes adding the filter in functions.php
529 if (!$post_types || !did_action('after_setup_theme')) {
530 $post_types = array('post' => 'post', 'page' => 'page');
531
532 if (!empty($this->options['media_support']))
533 $post_types['attachement'] = 'attachment';
534
535 if (is_array($this->options['post_types']))
536 $post_types = array_merge($post_types, $this->options['post_types']);
537
538 $post_types = apply_filters('pll_get_post_types', $post_types , false);
539 }
540
541 return $filter ? array_intersect($post_types, get_post_types()) : $post_types;
542 }
543
544 /*
545 * check if registered post type must be translated
546 *
547 * @since 1.2
548 *
549 * @param string $post_type post type name
550 */
551 public function registered_post_type($post_type) {
552 if ($this->is_translated_post_type($post_type)) {
553 register_taxonomy_for_object_type('language', $post_type);
554 register_taxonomy_for_object_type('post_translations', $post_type);
555 }
556 }
557
558 /*
559 * returns true if Polylang manages languages and translations for this post type
560 *
561 * @since 1.2
562 *
563 * @param string|array $post_type post type name or array of post type names
564 */
565 public function is_translated_post_type($post_type) {
566 $post_types = $this->get_translated_post_types(false);
567 return (is_array($post_type) && array_intersect($post_type, $post_types) || in_array($post_type, $post_types));
568 }
569
570 /*
571 * return taxonomies that need to be translated
572 *
573 * @since 1.2
574 *
575 * @param bool $filter true if we should return only valid registered taxonmies
576 * @return array array of registered taxonomy names for which Polylang manages languages and translations
577 */
578 public function get_translated_taxonomies($filter = true) {
579 static $taxonomies = array();
580
581 if (!$taxonomies || !did_action('after_setup_theme')) {
582 $taxonomies = array('category' => 'category', 'post_tag' => 'post_tag');
583
584 if (is_array($this->options['taxonomies']))
585 $taxonomies = array_merge($taxonomies, $this->options['taxonomies']);
586
587 $taxonomies = apply_filters('pll_get_taxonomies', $taxonomies, false);
588 }
589
590 return $filter ? array_intersect($taxonomies, get_taxonomies()) : $taxonomies;
591 }
592
593 /*
594 * returns true if Polylang manages languages and translations for this post type
595 *
596 * @since 1.2
597 *
598 * @param string|array $tax taxonomy name or array of taxonomy names
599 */
600 public function is_translated_taxonomy($tax) {
601 $taxonomies = $this->get_translated_taxonomies(false);
602 return (is_array($tax) && array_intersect($tax, $taxonomies) || in_array($tax, $taxonomies));
603 }
604
605 /*
606 * it is possible to have several terms with the same name in the same taxonomy (one per language)
607 * but the native term_exists will return true even if only one exists
608 * so here the function adds the language parameter
609 *
610 * @since 1.4
611 *
612 * @param string $term_name the term name
613 * @param string $taxonomy taxonomy name
614 * @param int $parent parent term id
615 * @param string|object $language the language slug or object
616 * @return null|int the term_id of the found term
617 */
618 public function term_exists($term_name, $taxonomy, $parent, $language) {
619 global $wpdb;
620
621 $select = "SELECT t.term_id FROM $wpdb->terms AS t";
622 $join = " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
623 $join .= $this->join_clause('term');
624 $where = $wpdb->prepare(" WHERE tt.taxonomy = %s AND t.name = %s", $taxonomy, $term_name);
625 $where .= $this->where_clause($this->get_language($language), 'term');
626
627 if ($parent > 0)
628 $where .= $wpdb->prepare(" AND tt.parent = %d", $parent);
629
630 return $wpdb->get_var($select . $join . $where);
631 }
632
633 /*
634 * gets the number of posts per language in a date, author or post type archive
635 *
636 * @since 1.2
637 *
638 * @param object lang
639 * @param array $args WP_Query arguments (accepted: post_type, m, year, monthnum, day, author, author_name)
640 * @return int
641 */
642 public function count_posts($lang, $args = array()) {
643 global $wpdb;
644
645 $q = wp_parse_args($args, array('post_type' => 'post'));
646
647 $cache_key = md5(serialize($q));
648 $counts = wp_cache_get($cache_key, 'pll_count_posts');
649
650 if (false === $counts) {
651 $select = "SELECT pll_tr.term_taxonomy_id, COUNT(*) AS num_posts FROM {$wpdb->posts} AS p";
652 $join = $this->join_clause('post');
653 $where = " WHERE post_status = 'publish'";
654 $where .= $wpdb->prepare(" AND p.post_type = %s", $q['post_type']);
655 $where .= $this->where_clause($this->get_languages_list(), 'post');
656 $groupby = " GROUP BY pll_tr.term_taxonomy_id";
657
658 if (!empty($q['m'])) {
659 $q['m'] = '' . preg_replace('|[^0-9]|', '', $q['m']);
660 $where .= $wpdb->prepare(" AND YEAR(p.post_date) = %d", substr($q['m'], 0, 4));
661 if ( strlen($q['m']) > 5 )
662 $where .= $wpdb->prepare(" AND MONTH(p.post_date) = %d", substr($q['m'], 4, 2));
663 if ( strlen($q['m']) > 7 )
664 $where .= $wpdb->prepare(" AND DAYOFMONTH(p.post_date) = %d", substr($q['m'], 6, 2));
665 }
666
667 if (!empty($q['year']))
668 $where .= $wpdb->prepare(" AND YEAR(p.post_date) = %d", $q['year']);
669
670 if (!empty($q['monthnum']))
671 $where .= $wpdb->prepare(" AND MONTH(p.post_date) = %d", $q['monthnum']);
672
673 if (!empty($q['day']))
674 $where .= $wpdb->prepare(" AND DAYOFMONTH(p.post_date) = %d", $q['day']);
675
676 if (!empty($q['author_name'])) {
677 $author = get_user_by('slug', sanitize_title_for_query($q['author_name']));
678 if ($author)
679 $q['author'] = $author->ID;
680 }
681
682 if (!empty($q['author']))
683 $where .= $wpdb->prepare(" AND p.post_author = %d", $q['author']);
684
685 if (!empty($q['post_format'])) {
686 $join .= " INNER JOIN {$wpdb->term_relationships} AS tr ON tr.object_id = p.ID";
687 $join .= " INNER JOIN {$wpdb->term_taxonomy} AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id";
688 $join .= " INNER JOIN {$wpdb->terms} AS t ON t.term_id = tt.term_id";
689 $where .= $wpdb->prepare(" AND t.slug = %s", $q['post_format']);
690 }
691
692 $res = $wpdb->get_results($select . $join . $where . $groupby, ARRAY_A);
693 foreach ((array) $res as $row)
694 $counts[$row['term_taxonomy_id']] = $row['num_posts'];
695
696 wp_cache_set($cache_key, $counts, 'pll_count_posts');
697 }
698
699 return empty($counts[$lang->term_taxonomy_id]) ? 0 : $counts[$lang->term_taxonomy_id];
700 }
701
702 /*
703 * adds language dependent cache domain when explicitely querying terms per language
704 * useful as the 'lang' parameter is not included in cache key by WordPress
705 *
706 * @since 1.3
707 */
708 public function get_terms_args($args) {
709 if (isset($args['lang'])) {
710 $key = '_' . (is_array($args['lang']) ? implode(',', $args['lang']) : $args['lang']);
711 $args['cache_domain'] = empty($args['cache_domain']) ? 'pll' . $key : $args['cache_domain'] . $key;
712 }
713
714 return $args;
715 }
716
717 /*
718 * returns ids of objects in a language similarly to get_objects_in_term for a taxonomy
719 * faster than get_objects_in_term as it avoids a JOIN
720 *
721 * @since 1.4
722 *
723 * @param object $lang a PLL_Language object
724 * @param string $type optional, either 'post' or 'term', defaults to 'post'
725 * @return array
726 */
727 public function get_objects_in_language($lang, $type = 'post') {
728 global $wpdb;
729 return $wpdb->get_col($wpdb->prepare("
730 SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d",
731 'term' == $type ? $lang->tl_term_taxonomy_id : $lang->term_taxonomy_id
732 ));
733 }
734 }
735