PluginProbe
Polylang / 1.5
Polylang v1.5
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.5, at include/model.php

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