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

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