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

230 lines 7.5 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 }
24
25 /**
26 * Get the list of taxonomies to copy or to synchronize
27 *
28 * @since 1.7
29 * @since 2.1 The `$from`, `$to`, `$lang` parameters were added.
30 *
31 * @param bool $sync True if it is synchronization, false if it is a copy
32 * @param int $from Id of the post from which we copy informations, optional, defaults to null
33 * @param int $to Id of the post to which we paste informations, optional, defaults to null
34 * @param string $lang Language slug, optional, defaults to null
35 * @return array List of taxonomy names
36 */
37 protected function get_taxonomies_to_copy( $sync, $from = null, $to = null, $lang = null ) {
38 $taxonomies = ! $sync || in_array( 'taxonomies', $this->options['sync'] ) ? $this->model->get_translated_taxonomies() : array();
39 if ( ! $sync || in_array( 'post_format', $this->options['sync'] ) ) {
40 $taxonomies[] = 'post_format';
41 }
42
43 /**
44 * Filter the taxonomies to copy or synchronize
45 *
46 * @since 1.7
47 * @since 2.1 The `$from`, `$to`, `$lang` parameters were added.
48 *
49 * @param array $taxonomies List of taxonomy names
50 * @param bool $sync True if it is synchronization, false if it is a copy
51 * @param int $from Id of the post from which we copy informations
52 * @param int $to Id of the post to which we paste informations
53 * @param string $lang Language slug
54 */
55 return array_unique( apply_filters( 'pll_copy_taxonomies', $taxonomies, $sync, $from, $to, $lang ) );
56 }
57
58 /**
59 * When copying or synchronizing terms, translate terms in translatable taxonomies
60 *
61 * @since 2.3
62 *
63 * @param array $object_id Object ID
64 * @param array $terms List of terms ids assigned to the source post
65 * @param string $taxonomy Taxonomy name
66 * @param string $lang Language slug
67 * @return array List of terms ids to assign to the target post
68 */
69 protected function maybe_translate_terms( $object_id, $terms, $taxonomy, $lang ) {
70 if ( is_array( $terms ) && $this->model->is_translated_taxonomy( $taxonomy ) ) {
71 $newterms = array();
72
73 // Convert to term ids if we got tag names
74 $strings = array_map( 'is_string', $terms );
75 if ( in_array( true, $strings, true ) ) {
76 $terms = get_the_terms( $object_id, $taxonomy );
77 $terms = wp_list_pluck( $terms, 'term_id' );
78 }
79
80 foreach ( $terms as $term ) {
81 /**
82 * Filter the translated term when a post translation is created or synchronized
83 *
84 * @since 2.3
85 *
86 * @param int $tr_term Translated term id
87 * @param int $term Source term id
88 * @param string $lang Language slug
89 */
90 if ( $term_id = apply_filters( 'pll_maybe_translate_term', $this->model->term->get_translation( $term, $lang ), $term, $lang ) ) {
91 $newterms[] = (int) $term_id; // Cast is important otherwise we get 'numeric' tags
92 }
93 }
94
95 return $newterms;
96 }
97
98 return $terms; // Empty $terms or untranslated taxonomy
99 }
100
101 /**
102 * When assigning terms to a post, assign translated terms to the translated posts (synchronisation)
103 *
104 * @since 2.3
105 *
106 * @param int $object_id Object ID.
107 * @param array $terms An array of object terms.
108 * @param array $tt_ids An array of term taxonomy IDs.
109 * @param string $taxonomy Taxonomy slug.
110 * @param bool $append Whether to append new terms to the old terms.
111 */
112 public function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append ) {
113 static $avoid_recursion = false;
114 $taxonomy_object = get_taxonomy( $taxonomy );
115
116 // Make sure that the taxonomy is registered for a post type
117 if ( ! $avoid_recursion && array_filter( $taxonomy_object->object_type, 'post_type_exists' ) ) {
118 $avoid_recursion = true;
119
120 $tr_ids = $this->model->post->get_translations( $object_id );
121
122 foreach ( $tr_ids as $lang => $tr_id ) {
123 if ( $tr_id !== $object_id ) {
124 $to_copy = $this->get_taxonomies_to_copy( true, $object_id, $tr_id, $lang );
125
126 if ( in_array( $taxonomy, $to_copy ) ) {
127 $newterms = $this->maybe_translate_terms( $object_id, $terms, $taxonomy, $lang );
128
129 // For some reasons, the user may have untranslated terms in the translation. Don't forget them.
130 if ( $this->model->is_translated_taxonomy( $taxonomy ) ) {
131 $tr_terms = get_the_terms( $tr_id, $taxonomy );
132 if ( is_array( $tr_terms ) ) {
133 foreach ( $tr_terms as $term ) {
134 if ( ! $this->model->term->get_translation( $term->term_id, $this->model->post->get_language( $object_id ) ) ) {
135 $newterms[] = (int) $term->term_id;
136 }
137 }
138 }
139 }
140
141 wp_set_object_terms( $tr_id, $newterms, $taxonomy, $append );
142 }
143 }
144 }
145
146 $avoid_recursion = false;
147 }
148 }
149
150 /**
151 * Copy terms fron one post to a translation, does not sync
152 *
153 * @since 2.3
154 *
155 * @param int $from Id of the source post
156 * @param int $to Id of the target post
157 * @param string $lang Language slug
158 */
159 public function copy( $from, $to, $lang ) {
160 remove_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 6 );
161
162 // Get taxonomies to sync for this post type
163 $taxonomies = array_intersect( get_post_taxonomies( $from ), $this->get_taxonomies_to_copy( false, $from, $to, $lang ) );
164
165 // Update the term cache to reduce the number of queries in the loop
166 update_object_term_cache( $from, get_post_type( $from ) );
167
168 // Copy
169 foreach ( $taxonomies as $tax ) {
170 if ( $terms = get_the_terms( $from, $tax ) ) {
171 $terms = array_map( 'intval', wp_list_pluck( $terms, 'term_id' ) );
172 $newterms = $this->maybe_translate_terms( $from, $terms, $tax, $lang );
173
174 if ( ! empty( $newterms ) ) {
175 wp_set_object_terms( $to, $newterms, $tax );
176 }
177 }
178 }
179
180 add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 6 );
181 }
182
183 /**
184 * When creating a new term, associate it to posts having translations associated to the translated terms
185 *
186 * @since 2.3
187 *
188 * @param int $term_id Id of the created term
189 * @param string $taxonomy Taxonomy
190 * @param array $translations Ids of the translations of the created term
191 */
192 public function create_term( $term_id, $taxonomy, $translations ) {
193 if ( doing_action( 'create_term' ) && in_array( $taxonomy, $this->get_taxonomies_to_copy( true ) ) ) {
194 // Get all posts associated to the translated terms
195 $tr_posts = get_posts( array(
196 'numberposts' => -1,
197 'nopaging' => true,
198 'post_type' => 'any',
199 'post_status' => 'any',
200 'fields' => 'ids',
201 'tax_query' => array(
202 array(
203 'taxonomy' => $taxonomy,
204 'field' => 'id',
205 'terms' => array_merge( array( $term_id ), array_values( $translations ) ),
206 'include_children' => false,
207 ),
208 ),
209 ) );
210
211 $lang = $this->model->term->get_language( $term_id ); // Language of the created term
212 $posts = array();
213
214 foreach ( $tr_posts as $post_id ) {
215 $post = $this->model->post->get_translation( $post_id, $lang );
216
217 if ( $post ) {
218 $posts[] = $post;
219 }
220 }
221
222 $posts = array_unique( $posts );
223
224 foreach ( $posts as $post_id ) {
225 wp_set_object_terms( $post_id, $term_id, $taxonomy, true );
226 }
227 }
228 }
229 }
230