PluginProbe
Polylang / 1.9.2
Polylang v1.9.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 / admin-sync.php

admin-sync.php in Polylang 1.9.2, at modules/sync/admin-sync.php

355 lines 12.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * manages copy and synchronization of terms and post metas
5 *
6 * @since 1.2
7 */
8 class PLL_Admin_Sync {
9
10 /**
11 * constructor
12 *
13 * @since 1.2
14 *
15 * @param object $polylang
16 */
17 public function __construct( &$polylang ) {
18 $this->model = &$polylang->model;
19 $this->options = &$polylang->options;
20
21 add_filter( 'wp_insert_post_parent', array( &$this, 'wp_insert_post_parent' ) );
22 add_action( 'add_meta_boxes', array( &$this, 'add_meta_boxes' ), 5, 2 ); // before Types which populates custom fields in same hook with priority 10
23
24 add_action( 'pll_save_post', array( &$this, 'pll_save_post' ), 10, 3 );
25 add_action( 'pll_save_term', array( &$this, 'pll_save_term' ), 10, 3 );
26
27 if ( $this->options['media_support'] ) {
28 add_action( 'pll_translate_media', array( &$this, 'copy_taxonomies' ), 10, 3 );
29 add_action( 'pll_translate_media', array( &$this, 'copy_post_metas' ), 10, 3 );
30 add_action( 'edit_attachment', array( &$this, 'edit_attachment' ) );
31 }
32 }
33
34 /**
35 * translate post parent if exists when using "Add new" ( translation )
36 *
37 * @since 0.6
38 *
39 * @param int $post_parent
40 * @return int
41 */
42 public function wp_insert_post_parent( $post_parent ) {
43 return isset( $_GET['from_post'], $_GET['new_lang'] ) && ( $id = wp_get_post_parent_id( (int) $_GET['from_post'] ) ) && ( $parent = $this->model->post->get_translation( $id, $_GET['new_lang'] ) ) ? $parent : $post_parent;
44 }
45
46 /**
47 * copy post metas, menu order, comment and ping status when using "Add new" ( translation )
48 * formerly used dbx_post_advanced deprecated in WP 3.7
49 *
50 * @since 1.2
51 *
52 * @param string $post_type unused
53 * @param object $post current post object
54 */
55 public function add_meta_boxes( $post_type, $post ) {
56 if ( 'post-new.php' == $GLOBALS['pagenow'] && isset( $_GET['from_post'], $_GET['new_lang'] ) && $this->model->is_translated_post_type( $post->post_type ) ) {
57 // capability check already done in post-new.php
58 $from_post_id = (int) $_GET['from_post'];
59 $from_post = get_post( $from_post_id );
60 $lang = $this->model->get_language( $_GET['new_lang'] );
61
62 if ( ! $from_post || ! $lang ) {
63 return;
64 }
65
66 $this->copy_taxonomies( $from_post_id, $post->ID, $lang->slug );
67 $this->copy_post_metas( $from_post_id, $post->ID, $lang->slug );
68
69 foreach ( array( 'menu_order', 'comment_status', 'ping_status' ) as $property ) {
70 $post->$property = $from_post->$property;
71 }
72
73 if ( is_sticky( $from_post_id ) ) {
74 stick_post( $post->ID );
75 }
76 }
77 }
78
79 /**
80 * get the list of taxonomies to copy or to synchronize
81 *
82 * @since 1.7
83 *
84 * @param bool $sync true if it is synchronization, false if it is a copy
85 * @return array list of taxonomy names
86 */
87 public function get_taxonomies_to_copy( $sync ) {
88 $taxonomies = ! $sync || in_array( 'taxonomies', $this->options['sync'] ) ? $this->model->get_translated_taxonomies() : array();
89 if ( ! $sync || in_array( 'post_format', $this->options['sync'] ) ) {
90 $taxonomies[] = 'post_format';
91 }
92
93 /**
94 * Filter the taxonomies to copy or synchronize
95 *
96 * @since 1.7
97 *
98 * @param array $taxonomies list of taxonomy names
99 * @param bool $sync true if it is synchronization, false if it is a copy
100 */
101 return array_unique( apply_filters( 'pll_copy_taxonomies', $taxonomies, $sync ) );
102 }
103
104 /**
105 * copy or synchronize terms
106 *
107 * @since 1.8
108 *
109 * @param int $from id of the post from which we copy informations
110 * @param int $to id of the post to which we paste informations
111 * @param string $lang language slug
112 * @param bool $sync true if it is synchronization, false if it is a copy, defaults to false
113 */
114 public function copy_taxonomies( $from, $to, $lang, $sync = false ) {
115 // get taxonomies to sync for this post type
116 $taxonomies = array_intersect( get_post_taxonomies( $from ), $this->get_taxonomies_to_copy( $sync ) );
117
118 // copy or synchronize terms
119 // FIXME quite a lot of query in foreach
120 foreach ( $taxonomies as $tax ) {
121 $terms = get_the_terms( $from, $tax );
122
123 // translated taxonomy
124 if ( $this->model->is_translated_taxonomy( $tax ) ) {
125 $newterms = array();
126 if ( is_array( $terms ) ) {
127 foreach ( $terms as $term ) {
128 if ( $term_id = $this->model->term->get_translation( $term->term_id, $lang ) ) {
129 $newterms[] = (int) $term_id; // cast is important otherwise we get 'numeric' tags
130 }
131 }
132 }
133
134 // for some reasons, the user may have untranslated terms in the translation. don't forget them.
135 if ( $sync ) {
136 $tr_terms = get_the_terms( $to, $tax );
137 if ( is_array( $tr_terms ) ) {
138 foreach ( $tr_terms as $term ) {
139 if ( ! $this->model->term->get_translation( $term->term_id, $this->model->post->get_language( $from ) ) ) {
140 $newterms[] = (int) $term->term_id;
141 }
142 }
143 }
144 }
145
146 if ( ! empty( $newterms ) || $sync ) {
147 wp_set_object_terms( $to, $newterms, $tax ); // replace terms in translation
148 }
149 }
150
151 // untranslated taxonomy ( post format )
152 // don't use simple get_post_format / set_post_format to generalize the case to other taxonomies
153 else {
154 wp_set_object_terms( $to, is_array( $terms ) ? array_map( 'intval', wp_list_pluck( $terms, 'term_id' ) ) : null, $tax );
155 }
156 }
157 }
158
159 /**
160 * copy or synchronize metas (custom fields)
161 *
162 * @since 0.9
163 *
164 * @param int $from id of the post from which we copy informations
165 * @param int $to id of the post to which we paste informations
166 * @param string $lang language slug
167 * @param bool $sync true if it is synchronization, false if it is a copy, defaults to false
168 */
169 public function copy_post_metas( $from, $to, $lang, $sync = false ) {
170 // copy or synchronize post metas and allow plugins to do the same
171 $metas = get_post_custom( $from );
172 $keys = array();
173
174 // get public meta keys ( including from translated post in case we just deleted a custom field )
175 if ( ! $sync || in_array( 'post_meta', $this->options['sync'] ) ) {
176 foreach ( $keys = array_unique( array_merge( array_keys( $metas ), array_keys( get_post_custom( $to ) ) ) ) as $k => $meta_key ) {
177 if ( is_protected_meta( $meta_key ) ) {
178 unset( $keys[ $k ] );
179 }
180 }
181 }
182
183 // add page template and featured image
184 foreach ( array( '_wp_page_template', '_thumbnail_id' ) as $meta ) {
185 if ( ! $sync || in_array( $meta, $this->options['sync'] ) ) {
186 $keys[] = $meta;
187 }
188 }
189
190 /**
191 * Filter the custom fields to copy or synchronize
192 *
193 * @since 0.6
194 * @since 1.9.2 The `$from`, `$to`, `$lang` parameters were added.
195 *
196 * @param array $keys list of custom fields names
197 * @param bool $sync true if it is synchronization, false if it is a copy
198 * @param int $from id of the post from which we copy informations
199 * @param int $to id of the post to which we paste informations
200 * @param string $lang language slug
201 */
202 $keys = array_unique( apply_filters( 'pll_copy_post_metas', $keys, $sync, $from, $to, $lang ) );
203
204 // and now copy / synchronize
205 foreach ( $keys as $key ) {
206 delete_post_meta( $to, $key ); // the synchronization process of multiple values custom fields is easier if we delete all metas first
207 if ( isset( $metas[ $key ] ) ) {
208 foreach ( $metas[ $key ] as $value ) {
209 // important: always maybe_unserialize value coming from get_post_custom. See codex.
210 // thanks to goncalveshugo http://wordpress.org/support/topic/plugin-polylang-pll_copy_post_meta
211 $value = maybe_unserialize( $value );
212 // special case for featured images which can be translated
213 add_post_meta( $to, $key, ( '_thumbnail_id' == $key && $tr_value = $this->model->post->get_translation( $value, $lang ) ) ? $tr_value : $value );
214 }
215 }
216 }
217 }
218
219 /**
220 * synchronizes terms and metas in translations
221 *
222 * @since 1.2
223 *
224 * @param int $post_id post id
225 * @param object $post post object
226 * @param array $translations post translations
227 */
228 public function pll_save_post( $post_id, $post, $translations ) {
229 global $wpdb;
230
231 // prepare properties to synchronize
232 foreach ( array( 'comment_status', 'ping_status', 'menu_order', 'post_date' ) as $property ) {
233 if ( in_array( $property, $this->options['sync'] ) ) {
234 $postarr[ $property ] = $post->$property;
235 }
236 }
237
238 if ( in_array( 'post_date', $this->options['sync'] ) ) {
239 $postarr['post_date_gmt'] = $post->post_date_gmt;
240 }
241
242 // synchronize terms and metas in translations
243 foreach ( $translations as $lang => $tr_id ) {
244 if ( ! $tr_id || $tr_id === $post_id ) {
245 continue;
246 }
247
248 // synchronize terms and metas
249 $this->copy_taxonomies( $post_id, $tr_id, $lang, true );
250 $this->copy_post_metas( $post_id, $tr_id, $lang, true );
251
252 // sticky posts
253 if ( in_array( 'sticky_posts', $this->options['sync'] ) ) {
254 isset( $_REQUEST['sticky'] ) ? stick_post( $tr_id ) : unstick_post( $tr_id );
255 }
256
257 // add comment status, ping status, menu order... to synchronization
258 $tr_arr = empty( $postarr ) ? array() : $postarr;
259
260 // add post parent to synchronization
261 // do not udpate the translation parent if the user set a parent with no translation
262 if ( in_array( 'post_parent', $this->options['sync'] ) ) {
263 $post_parent = ( $parent_id = wp_get_post_parent_id( $post_id ) ) ? $this->model->post->get_translation( $parent_id, $lang ) : 0;
264 if ( ! ( $parent_id && ! $post_parent ) ) {
265 $tr_arr['post_parent'] = $post_parent;
266 }
267 }
268
269 // update all the row at once
270 // don't use wp_update_post to avoid infinite loop
271 if ( ! empty( $tr_arr ) ) {
272 $wpdb->update( $wpdb->posts, $tr_arr, array( 'ID' => $tr_id ) );
273 clean_post_cache( $tr_id );
274 }
275 }
276 }
277
278 /**
279 * synchronize translations of a term in all posts
280 *
281 * @since 1.2
282 *
283 * @param int $term_id term id
284 * @param string $taxonomy taxonomy name of the term
285 * @param array $translations translations of the term
286 */
287 public function pll_save_term( $term_id, $taxonomy, $translations ) {
288 // check if the taxonomy is synchronized
289 if ( ! $this->model->is_translated_taxonomy( $taxonomy ) || ! in_array( $taxonomy, $this->get_taxonomies_to_copy( true ) ) ) {
290 return;
291 }
292
293 // get all posts associated to this term
294 $posts = get_posts( array(
295 'numberposts' => -1,
296 'nopaging' => true,
297 'post_type' => 'any',
298 'post_status' => 'any',
299 'fields' => 'ids',
300 'tax_query' => array( array(
301 'taxonomy' => $taxonomy,
302 'field' => 'id',
303 'terms' => array_merge( array( $term_id ), array_values( $translations ) ),
304 ) ),
305 ) );
306
307 // associate translated term to translated post
308 // FIXME quite a lot of query in foreach
309 foreach ( $this->model->get_languages_list() as $language ) {
310 if ( $translated_term = $this->model->term->get( $term_id, $language ) ) {
311 foreach ( $posts as $post_id ) {
312 if ( $translated_post = $this->model->post->get( $post_id, $language ) ) {
313 wp_set_object_terms( $translated_post, $translated_term, $taxonomy, true );
314 }
315 }
316 }
317 }
318
319 // synchronize parent in translations
320 // calling clean_term_cache *after* this is mandatory otherwise the $taxonomy_children option is not correctly updated
321 // before WP 3.9 clean_term_cache could be called ( efficiently ) only one time due to static array which prevented to update the option more than once
322 // this is the reason to use the edit_term filter and not edited_term
323 // take care that $_POST contains the only valid values for the current term
324 // FIXME can I synchronize parent without using $_POST instead?
325 if ( isset( $_POST['term_tr_lang'] ) ) {
326 foreach ( $_POST['term_tr_lang'] as $lang => $tr_id ) {
327 if ( $tr_id ) {
328 if ( isset( $_POST['parent'] ) && -1 != $_POST['parent'] ) { // since WP 3.1
329 $term_parent = $this->model->term->get_translation( (int) $_POST['parent'], $lang );
330 }
331
332 global $wpdb;
333 $wpdb->update( $wpdb->term_taxonomy,
334 array( 'parent' => isset( $term_parent ) ? $term_parent : 0 ),
335 array( 'term_taxonomy_id' => get_term( (int) $tr_id, $taxonomy )->term_taxonomy_id )
336 );
337
338 clean_term_cache( $tr_id, $taxonomy ); // OK since WP 3.9
339 }
340 }
341 }
342 }
343
344 /**
345 * synchronizes terms and metas in translations for media
346 *
347 * @since 1.8
348 *
349 * @param int $post_id post id
350 */
351 public function edit_attachment( $post_id ) {
352 $this->pll_save_post( $post_id, get_post( $post_id ), $this->model->post->get_translations( $post_id ) );
353 }
354 }
355