PluginProbe
Polylang / 2.7.2
Polylang v2.7.2
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 / modules / sync / sync-tax.php

sync-tax.php in Polylang 2.7.2, at modules/sync/sync-tax.php

288 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * A class to manage the sychronization of taxonomy terms across posts translations
5 *
6 * @since 2.3
7 */
8 class PLL_Sync_Tax {
9
10 /**
11 * Constructor
12 *
13 * @since 2.3
14 *
15 * @param object $polylang
16 */
17 public function __construct( &$polylang ) {
18 $this->model = &$polylang->model;
19 $this->options = &$polylang->options;
20
21 add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
22 add_action( 'pll_save_term', array( $this, 'create_term' ), 10, 3 );
23 add_action( 'pre_delete_term', array( $this, 'pre_delete_term' ) );
24 add_action( 'delete_term', array( $this, 'delete_term' ) );
25 }
26
27 /**
28 * Get the list of taxonomies to copy or to synchronize
29 *
30 * @since 1.7
31 * @since 2.1 The `$from`, `$to`, `$lang` parameters were added.
32 *
33 * @param bool $sync True if it is synchronization, false if it is a copy
34 * @param int $from Id of the post from which we copy informations, optional, defaults to null
35 * @param int $to Id of the post to which we paste informations, optional, defaults to null
36 * @param string $lang Language slug, optional, defaults to null
37 * @return array List of taxonomy names
38 */
39 protected function get_taxonomies_to_copy( $sync, $from = null, $to = null, $lang = null ) {
40 $taxonomies = ! $sync || in_array( 'taxonomies', $this->options['sync'] ) ? $this->model->get_translated_taxonomies() : array();
41 if ( ! $sync || in_array( 'post_format', $this->options['sync'] ) ) {
42 $taxonomies[] = 'post_format';
43 }
44
45 /**
46 * Filter the taxonomies to copy or synchronize
47 *
48 * @since 1.7
49 * @since 2.1 The `$from`, `$to`, `$lang` parameters were added.
50 *
51 * @param array $taxonomies List of taxonomy names
52 * @param bool $sync True if it is synchronization, false if it is a copy
53 * @param int $from Id of the post from which we copy informations
54 * @param int $to Id of the post to which we paste informations
55 * @param string $lang Language slug
56 */
57 return array_unique( apply_filters( 'pll_copy_taxonomies', $taxonomies, $sync, $from, $to, $lang ) );
58 }
59
60 /**
61 * When copying or synchronizing terms, translate terms in translatable taxonomies
62 *
63 * @since 2.3
64 *
65 * @param array $object_id Object ID
66 * @param array $terms List of terms ids assigned to the source post
67 * @param string $taxonomy Taxonomy name
68 * @param string $lang Language slug
69 * @return array List of terms ids to assign to the target post
70 */
71 protected function maybe_translate_terms( $object_id, $terms, $taxonomy, $lang ) {
72 if ( is_array( $terms ) && $this->model->is_translated_taxonomy( $taxonomy ) ) {
73 $newterms = array();
74
75 // Convert to term ids if we got tag names
76 $strings = array_map( 'is_string', $terms );
77 if ( in_array( true, $strings, true ) ) {
78 $terms = get_the_terms( $object_id, $taxonomy );
79 $terms = wp_list_pluck( $terms, 'term_id' );
80 }
81
82 foreach ( $terms as $term ) {
83 /**
84 * Filter the translated term when a post translation is created or synchronized
85 *
86 * @since 2.3
87 *
88 * @param int $tr_term Translated term id
89 * @param int $term Source term id
90 * @param string $lang Language slug
91 */
92 if ( $term_id = apply_filters( 'pll_maybe_translate_term', $this->model->term->get_translation( $term, $lang ), $term, $lang ) ) {
93 $newterms[] = (int) $term_id; // Cast is important otherwise we get 'numeric' tags
94 }
95 }
96
97 return $newterms;
98 }
99
100 return $terms; // Empty $terms or untranslated taxonomy
101 }
102
103 /**
104 * Maybe copy taxonomy terms from one post to the other
105 *
106 * @since 2.6
107 *
108 * @param int $object_id Source object ID.
109 * @param int $tr_id Target object ID.
110 * @param string $lang Target language.
111 * @param array $terms An array of object terms.
112 * @param string $taxonomy Taxonomy slug.
113 * @param bool $append Whether to append new terms to the old terms.
114 */
115 protected function copy_object_terms( $object_id, $tr_id, $lang, $terms, $taxonomy, $append ) {
116 $to_copy = $this->get_taxonomies_to_copy( true, $object_id, $tr_id, $lang );
117
118 if ( in_array( $taxonomy, $to_copy ) ) {
119 $newterms = $this->maybe_translate_terms( $object_id, $terms, $taxonomy, $lang );
120
121 // For some reasons, the user may have untranslated terms in the translation. Don't forget them.
122 if ( $this->model->is_translated_taxonomy( $taxonomy ) ) {
123 $tr_terms = get_the_terms( $tr_id, $taxonomy );
124 if ( is_array( $tr_terms ) ) {
125 foreach ( $tr_terms as $term ) {
126 if ( ! $this->model->term->get_translation( $term->term_id, $this->model->post->get_language( $object_id ) ) ) {
127 $newterms[] = (int) $term->term_id;
128 }
129 }
130 }
131 }
132
133 wp_set_object_terms( $tr_id, $newterms, $taxonomy, $append );
134 }
135
136 }
137
138 /**
139 * When assigning terms to a post, assign translated terms to the translated posts (synchronisation)
140 *
141 * @since 2.3
142 *
143 * @param int $object_id Object ID.
144 * @param array $terms An array of object terms.
145 * @param array $tt_ids An array of term taxonomy IDs.
146 * @param string $taxonomy Taxonomy slug.
147 * @param bool $append Whether to append new terms to the old terms.
148 */
149 public function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append ) {
150 static $avoid_recursion = false;
151 $taxonomy_object = get_taxonomy( $taxonomy );
152
153 // Make sure that the taxonomy is registered for a post type
154 if ( ! $avoid_recursion && array_filter( $taxonomy_object->object_type, 'post_type_exists' ) ) {
155 $avoid_recursion = true;
156
157 $tr_ids = $this->model->post->get_translations( $object_id );
158
159 foreach ( $tr_ids as $lang => $tr_id ) {
160 if ( $tr_id !== $object_id ) {
161 if ( $this->model->post->current_user_can_synchronize( $object_id ) ) {
162 $this->copy_object_terms( $object_id, $tr_id, $lang, $terms, $taxonomy, $append );
163 } else {
164 // No permission to synchronize, so let's synchronize in reverse order
165 $orig_lang = array_search( $object_id, $tr_ids );
166 $tr_terms = get_the_terms( $tr_id, $taxonomy );
167
168 if ( false === $tr_terms ) {
169 $tr_terms = array();
170 }
171
172 if ( is_array( $tr_terms ) ) {
173 $tr_terms = wp_list_pluck( $tr_terms, 'term_id' );
174 $this->copy_object_terms( $tr_id, $object_id, $orig_lang, $tr_terms, $taxonomy, $append );
175 }
176 break;
177 }
178 }
179 }
180
181 $avoid_recursion = false;
182 }
183 }
184
185 /**
186 * Copy terms fron one post to a translation, does not sync
187 *
188 * @since 2.3
189 *
190 * @param int $from Id of the source post
191 * @param int $to Id of the target post
192 * @param string $lang Language slug
193 */
194 public function copy( $from, $to, $lang ) {
195 remove_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 6 );
196
197 // Get taxonomies to sync for this post type
198 $taxonomies = array_intersect( get_post_taxonomies( $from ), $this->get_taxonomies_to_copy( false, $from, $to, $lang ) );
199
200 // Update the term cache to reduce the number of queries in the loop
201 update_object_term_cache( $from, get_post_type( $from ) );
202
203 // Copy
204 foreach ( $taxonomies as $tax ) {
205 if ( $terms = get_the_terms( $from, $tax ) ) {
206 $terms = array_map( 'intval', wp_list_pluck( $terms, 'term_id' ) );
207 $newterms = $this->maybe_translate_terms( $from, $terms, $tax, $lang );
208
209 if ( ! empty( $newterms ) ) {
210 wp_set_object_terms( $to, $newterms, $tax );
211 }
212 }
213 }
214
215 add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 6 );
216 }
217
218 /**
219 * When creating a new term, associate it to posts having translations associated to the translated terms
220 *
221 * @since 2.3
222 *
223 * @param int $term_id Id of the created term
224 * @param string $taxonomy Taxonomy
225 * @param array $translations Ids of the translations of the created term
226 */
227 public function create_term( $term_id, $taxonomy, $translations ) {
228 if ( doing_action( 'create_term' ) && in_array( $taxonomy, $this->get_taxonomies_to_copy( true ) ) ) {
229 // Get all posts associated to the translated terms
230 $tr_posts = get_posts(
231 array(
232 'numberposts' => -1,
233 'nopaging' => true,
234 'post_type' => 'any',
235 'post_status' => 'any',
236 'fields' => 'ids',
237 'tax_query' => array(
238 array(
239 'taxonomy' => $taxonomy,
240 'field' => 'id',
241 'terms' => array_merge( array( $term_id ), array_values( $translations ) ),
242 'include_children' => false,
243 ),
244 ),
245 )
246 );
247
248 $lang = $this->model->term->get_language( $term_id ); // Language of the created term
249 $posts = array();
250
251 foreach ( $tr_posts as $post_id ) {
252 $post = $this->model->post->get_translation( $post_id, $lang );
253
254 if ( $post ) {
255 $posts[] = $post;
256 }
257 }
258
259 $posts = array_unique( $posts );
260
261 foreach ( $posts as $post_id ) {
262 if ( current_user_can( 'assign_term', $term_id ) ) {
263 wp_set_object_terms( $post_id, $term_id, $taxonomy, true );
264 }
265 }
266 }
267 }
268
269 /**
270 * Deactivate the synchronization of terms before deleting a term
271 * to avoid translated terms to be removed from translated posts
272 *
273 * @since 2.3.2
274 */
275 public function pre_delete_term() {
276 remove_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
277 }
278
279 /**
280 * Re-activate the synchronization of terms after a term is deleted
281 *
282 * @since 2.3.2
283 */
284 public function delete_term() {
285 add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
286 }
287 }
288