PluginProbe
Polylang / 1.8.2
Polylang v1.8.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.8.2, at modules/sync/admin-sync.php

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