PluginProbe
Polylang / 2.3.6
Polylang v2.3.6
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.3.6, at modules/sync/sync-tax.php

251 lines 8.1 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 * When assigning terms to a post, assign translated terms to the translated posts (synchronisation)
105 *
106 * @since 2.3
107 *
108 * @param int $object_id Object ID.
109 * @param array $terms An array of object terms.
110 * @param array $tt_ids An array of term taxonomy IDs.
111 * @param string $taxonomy Taxonomy slug.
112 * @param bool $append Whether to append new terms to the old terms.
113 */
114 public function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append ) {
115 static $avoid_recursion = false;
116 $taxonomy_object = get_taxonomy( $taxonomy );
117
118 // Make sure that the taxonomy is registered for a post type
119 if ( ! $avoid_recursion && array_filter( $taxonomy_object->object_type, 'post_type_exists' ) ) {
120 $avoid_recursion = true;
121
122 $tr_ids = $this->model->post->get_translations( $object_id );
123
124 foreach ( $tr_ids as $lang => $tr_id ) {
125 if ( $tr_id !== $object_id ) {
126 $to_copy = $this->get_taxonomies_to_copy( true, $object_id, $tr_id, $lang );
127
128 if ( in_array( $taxonomy, $to_copy ) ) {
129 $newterms = $this->maybe_translate_terms( $object_id, $terms, $taxonomy, $lang );
130
131 // For some reasons, the user may have untranslated terms in the translation. Don't forget them.
132 if ( $this->model->is_translated_taxonomy( $taxonomy ) ) {
133 $tr_terms = get_the_terms( $tr_id, $taxonomy );
134 if ( is_array( $tr_terms ) ) {
135 foreach ( $tr_terms as $term ) {
136 if ( ! $this->model->term->get_translation( $term->term_id, $this->model->post->get_language( $object_id ) ) ) {
137 $newterms[] = (int) $term->term_id;
138 }
139 }
140 }
141 }
142
143 wp_set_object_terms( $tr_id, $newterms, $taxonomy, $append );
144 }
145 }
146 }
147
148 $avoid_recursion = false;
149 }
150 }
151
152 /**
153 * Copy terms fron one post to a translation, does not sync
154 *
155 * @since 2.3
156 *
157 * @param int $from Id of the source post
158 * @param int $to Id of the target post
159 * @param string $lang Language slug
160 */
161 public function copy( $from, $to, $lang ) {
162 remove_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 6 );
163
164 // Get taxonomies to sync for this post type
165 $taxonomies = array_intersect( get_post_taxonomies( $from ), $this->get_taxonomies_to_copy( false, $from, $to, $lang ) );
166
167 // Update the term cache to reduce the number of queries in the loop
168 update_object_term_cache( $from, get_post_type( $from ) );
169
170 // Copy
171 foreach ( $taxonomies as $tax ) {
172 if ( $terms = get_the_terms( $from, $tax ) ) {
173 $terms = array_map( 'intval', wp_list_pluck( $terms, 'term_id' ) );
174 $newterms = $this->maybe_translate_terms( $from, $terms, $tax, $lang );
175
176 if ( ! empty( $newterms ) ) {
177 wp_set_object_terms( $to, $newterms, $tax );
178 }
179 }
180 }
181
182 add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 6 );
183 }
184
185 /**
186 * When creating a new term, associate it to posts having translations associated to the translated terms
187 *
188 * @since 2.3
189 *
190 * @param int $term_id Id of the created term
191 * @param string $taxonomy Taxonomy
192 * @param array $translations Ids of the translations of the created term
193 */
194 public function create_term( $term_id, $taxonomy, $translations ) {
195 if ( doing_action( 'create_term' ) && in_array( $taxonomy, $this->get_taxonomies_to_copy( true ) ) ) {
196 // Get all posts associated to the translated terms
197 $tr_posts = get_posts( array(
198 'numberposts' => -1,
199 'nopaging' => true,
200 'post_type' => 'any',
201 'post_status' => 'any',
202 'fields' => 'ids',
203 'tax_query' => array(
204 array(
205 'taxonomy' => $taxonomy,
206 'field' => 'id',
207 'terms' => array_merge( array( $term_id ), array_values( $translations ) ),
208 'include_children' => false,
209 ),
210 ),
211 ) );
212
213 $lang = $this->model->term->get_language( $term_id ); // Language of the created term
214 $posts = array();
215
216 foreach ( $tr_posts as $post_id ) {
217 $post = $this->model->post->get_translation( $post_id, $lang );
218
219 if ( $post ) {
220 $posts[] = $post;
221 }
222 }
223
224 $posts = array_unique( $posts );
225
226 foreach ( $posts as $post_id ) {
227 wp_set_object_terms( $post_id, $term_id, $taxonomy, true );
228 }
229 }
230 }
231
232 /**
233 * Deactivate the synchronization of terms before deleting a term
234 * to avoid translated terms to be removed from translated posts
235 *
236 * @since 2.3.2
237 */
238 public function pre_delete_term() {
239 remove_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
240 }
241
242 /**
243 * Re-activate the synchronization of terms after a term is deleted
244 *
245 * @since 2.3.2
246 */
247 public function delete_term() {
248 add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
249 }
250 }
251