PluginProbe
Polylang / 2.5.4
Polylang v2.5.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 / translated-object.php

translated-object.php in Polylang 2.5.4, at include/translated-object.php

276 lines 9.5 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 objects languages and translations model
5 *
6 * @since 1.8
7 */
8 abstract class PLL_Translated_Object {
9 public $model;
10 protected $object_type, $tax_language, $tax_translations, $tax_tt;
11
12 /**
13 * Constructor
14 *
15 * @since 1.8
16 *
17 * @param object $model
18 */
19 public function __construct( &$model ) {
20 $this->model = &$model;
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( $this->tax_language, $this->object_type, $args );
26 $args['update_count_callback'] = '_update_generic_term_count'; // count *all* posts to avoid deleting in clean_translations_terms
27 register_taxonomy( $this->tax_translations, $this->object_type, $args );
28 }
29
30 /**
31 * Wrap wp_get_object_terms to cache it and return only one object
32 * inspired by the function get_the_terms
33 *
34 * @since 1.2
35 *
36 * @param int $object_id post_id or term_id
37 * @param string $taxonomy Polylang taxonomy depending if we are looking for a post ( or term ) language ( or translation )
38 * @return bool|object the term associated to the object in the requested taxonomy if exists, false otherwise
39 */
40 public function get_object_term( $object_id, $taxonomy ) {
41 if ( empty( $object_id ) || is_wp_error( $object_id ) ) {
42 return false;
43 }
44
45 $object_id = (int) $object_id;
46 $term = get_object_term_cache( $object_id, $taxonomy );
47
48 if ( false === $term ) {
49 // query language and translations at the same time
50 $taxonomies = array( $this->tax_language, $this->tax_translations );
51
52 // query terms
53 foreach ( wp_get_object_terms( $object_id, $taxonomies, array( 'update_term_meta_cache' => false ) ) as $t ) {
54 $terms[ $t->taxonomy ] = $t;
55 if ( $t->taxonomy == $taxonomy ) {
56 $term = $t;
57 }
58 }
59
60 // store it the way WP wants it
61 // set an empty cache if no term found in the taxonomy
62 foreach ( $taxonomies as $tax ) {
63 wp_cache_add( $object_id, empty( $terms[ $tax ] ) ? array() : array( $terms[ $tax ] ), $tax . '_relationships' );
64 }
65 }
66 else {
67 $term = reset( $term );
68 }
69
70 return empty( $term ) ? false : $term;
71 }
72
73 /**
74 * Tells whether a translation term must be updated
75 *
76 * @since 2.3
77 *
78 * @param array $id Post id or term id
79 * @param array $translations An associative array of translations with language code as key and translation id as value
80 */
81 protected function should_update_translation_group( $id, $translations ) {
82 // Don't do anything if no translations have been added to the group
83 $old_translations = $this->get_translations( $id ); // Includes at least $id itself
84 return count( array_diff_assoc( $translations, $old_translations ) ) > 0;
85 }
86
87 /**
88 * Saves translations for posts or terms
89 *
90 * @since 0.5
91 *
92 * @param int $id Post id or term id
93 * @param array $translations An associative array of translations with language code as key and translation id as value
94 */
95 public function save_translations( $id, $translations ) {
96 $id = (int) $id;
97
98 if ( ( $lang = $this->get_language( $id ) ) && isset( $translations ) && is_array( $translations ) ) {
99 // sanitize the translations array
100 $translations = array_map( 'intval', $translations );
101 $translations = array_merge( array( $lang->slug => $id ), $translations ); // make sure this object is in translations
102 $translations = array_diff( $translations, array( 0 ) ); // don't keep non translated languages
103 $translations = array_intersect_key( $translations, array_flip( $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) ); // keep only valid languages slugs as keys
104
105 // unlink removed translations
106 $old_translations = $this->get_translations( $id );
107 foreach ( array_diff_assoc( $old_translations, $translations ) as $object_id ) {
108 $this->delete_translation( $object_id );
109 }
110
111 // Check id we need to create or update the translation group
112 if ( $this->should_update_translation_group( $id, $translations ) ) {
113 $terms = wp_get_object_terms( $translations, $this->tax_translations );
114 $term = reset( $terms );
115
116 // create a new term if necessary
117 if ( empty( $term ) ) {
118 wp_insert_term( $group = uniqid( 'pll_' ), $this->tax_translations, array( 'description' => serialize( $translations ) ) );
119 }
120 else {
121 // take care not to overwrite extra data stored in description field, if any
122 $d = unserialize( $term->description );
123 $d = is_array( $d ) ? array_diff_key( $d, $old_translations ) : array(); // remove old translations
124 $d = array_merge( $d, $translations ); // add new one
125 wp_update_term( $group = (int) $term->term_id, $this->tax_translations, array( 'description' => serialize( $d ) ) );
126 }
127
128 // link all translations to the new term
129 foreach ( $translations as $p ) {
130 wp_set_object_terms( $p, $group, $this->tax_translations );
131 }
132
133 // clean now unused translation groups
134 foreach ( wp_list_pluck( $terms, 'term_id' ) as $term_id ) {
135 $term = get_term( $term_id, $this->tax_translations );
136 if ( empty( $term->count ) ) {
137 wp_delete_term( $term_id, $this->tax_translations );
138 }
139 }
140 }
141 }
142 }
143
144 /**
145 * Deletes a translation of a post or term
146 *
147 * @since 0.5
148 *
149 * @param int $id post id or term id
150 */
151 public function delete_translation( $id ) {
152 $id = (int) $id;
153 $term = $this->get_object_term( $id, $this->tax_translations );
154
155 if ( ! empty( $term ) ) {
156 $d = unserialize( $term->description );
157 $slug = array_search( $id, $this->get_translations( $id ) ); // in case some plugin stores the same value with different key
158 unset( $d[ $slug ] );
159
160 if ( empty( $d ) ) {
161 wp_delete_term( (int) $term->term_id, $this->tax_translations );
162 }
163 else {
164 wp_update_term( (int) $term->term_id, $this->tax_translations, array( 'description' => serialize( $d ) ) );
165 }
166 }
167 }
168
169 /**
170 * Returns an array of translations of a post or term
171 *
172 * @since 0.5
173 *
174 * @param int $id post id or term id
175 * @return array an associative array of translations with language code as key and translation id as value
176 */
177 public function get_translations( $id ) {
178 $term = $this->get_object_term( $id, $this->tax_translations );
179 $translations = empty( $term ) ? array() : unserialize( $term->description );
180
181 // make sure we return only translations ( thus we allow plugins to store other information in the array )
182 if ( is_array( $translations ) ) {
183 $translations = array_intersect_key( $translations, array_flip( $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) );
184 }
185
186 // make sure to return at least the passed post or term in its translation array
187 if ( empty( $translations ) && $lang = $this->get_language( $id ) ) {
188 $translations = array( $lang->slug => $id );
189 }
190
191 return $translations;
192 }
193
194 /**
195 * Returns the id of the translation of a post or term
196 *
197 * @since 0.5
198 *
199 * @param int $id post id or term id
200 * @param object|string $lang object or slug
201 * @return bool|int post id or term id of the translation, false if there is none
202 */
203 public function get_translation( $id, $lang ) {
204 if ( ! $lang = $this->model->get_language( $lang ) ) {
205 return false;
206 }
207
208 $translations = $this->get_translations( $id );
209
210 return isset( $translations[ $lang->slug ] ) ? $translations[ $lang->slug ] : false;
211 }
212
213 /**
214 * Among the object and its translations, returns the id of the object which is in $lang
215 *
216 * @since 0.1
217 *
218 * @param int $id post id or term id
219 * @param int|string|object $lang language ( term_id or slug or object )
220 * @return bool|int the translation post id or term id if exists, otherwise the post id or term id, false if the post has no language
221 */
222 public function get( $id, $lang ) {
223 $id = (int) $id;
224 $obj_lang = $this->get_language( $id ); // FIXME is this necessary?
225 if ( ! $lang || ! $obj_lang ) {
226 return false;
227 }
228
229 $lang = $this->model->get_language( $lang );
230 return $obj_lang->term_id == $lang->term_id ? $id : $this->get_translation( $id, $lang );
231 }
232
233 /**
234 * A where clause to add to sql queries when filtering by language is needed directly in query
235 *
236 * @since 1.2
237 *
238 * @param object|array|string $lang a PLL_Language object or a comma separated list of language slug or an array of language slugs
239 * @return string where clause
240 */
241 public function where_clause( $lang ) {
242 global $wpdb;
243 $tt_id = $this->tax_tt;
244
245 // $lang is an object
246 // generally the case if the query is coming from Polylang
247 if ( is_object( $lang ) ) {
248 return ' AND pll_tr.term_taxonomy_id = ' . absint( $lang->$tt_id );
249 }
250
251 // $lang is a comma separated list of slugs ( or an array of slugs )
252 // generally the case is the query is coming from outside with 'lang' parameter
253 $slugs = is_array( $lang ) ? $lang : explode( ',', $lang );
254 foreach ( $slugs as $slug ) {
255 $languages[] = absint( $this->model->get_language( $slug )->$tt_id );
256 }
257
258 return ' AND pll_tr.term_taxonomy_id IN ( ' . implode( ',', $languages ) . ' )';
259 }
260
261 /**
262 * Returns ids of objects in a language similarly to get_objects_in_term for a taxonomy
263 * faster than get_objects_in_term as it avoids a JOIN
264 *
265 * @since 1.4
266 *
267 * @param object $lang a PLL_Language object
268 * @return array
269 */
270 public function get_objects_in_language( $lang ) {
271 global $wpdb;
272 $tt_id = $this->tax_tt;
273 return $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $lang->$tt_id ) );
274 }
275 }
276