PluginProbe ʕ •ᴥ•ʔ
WPML Multilingual & Multicurrency for WooCommerce / 5.5.7
WPML Multilingual & Multicurrency for WooCommerce v5.5.7
5.5.7 5.3.8 5.3.9 5.4.3 5.4.4 5.4.5 5.5.1 5.5.1.1 5.5.2.2 5.5.2.3 5.5.3 5.5.3.1 5.5.4 5.5.5 5.5.6 trunk 0.9 1.0 1.1 1.2 1.3 1.4 1.5 2.0 2.2 2.3 2.3.1 2.3.2 3.0 3.0.1 3.1 3.2 3.2.1 3.2.2 3.3 3.3.1 3.3.2 3.3.3 3.3.4 3.4 3.4.1 3.4.2 3.4.3 3.5 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.6 3.6.1 3.6.10 3.6.11 3.6.2 3.6.3 3.6.4 3.6.5 3.6.5.1 3.6.6 3.6.7 3.6.8 3.6.9 3.7 3.7.1 3.7.10 3.7.11 3.7.12 3.7.13 3.7.14 3.7.15 3.7.16 3.7.2 3.7.3 3.7.4 3.7.5 3.7.6 3.7.7 3.7.8 3.7.9 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.9.0 3.9.1 3.9.1.1 3.9.2 3.9.3 3.9.4 3.9.5 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10.0 4.10.1 4.10.2 4.10.3 4.10.4 4.11.2 4.11.3.2 4.11.5 4.11.6 4.12.1 4.12.4 4.12.5 4.12.6 4.2.0 4.2.0.1 4.2.1 4.2.1.1 4.2.10 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.7.1 4.2.8 4.2.8.1 4.2.9 4.3.0 4.3.1 4.3.2 4.3.2.1 4.3.3 4.3.4 4.3.5 4.3.6 4.3.7 4.4.0 4.4.1 4.4.2 4.4.2.1 4.5.0 4.6.0 4.6.1 4.6.2 4.6.2.1 4.6.3 4.6.5 4.6.6 4.6.7 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.7.5 4.7.6 4.7.7 4.7.8 4.7.9 4.8.0 4.9.0 4.9.1 5.0.1 5.0.2 5.1.1 5.1.2 5.1.3 5.2.0 5.2.1 5.3.2 5.3.3.1 5.3.4 5.3.5 5.3.6 5.3.7
woocommerce-multilingual / inc / translation-editor / class-wcml-translation-editor.php
woocommerce-multilingual / inc / translation-editor Last commit date
class-wcml-downloadable-products.php 1 month ago class-wcml-editor-save-filters.php 1 month ago class-wcml-editor-ui-product-job.php 1 month ago class-wcml-editor-ui-wysiwyg-field.php 5 years ago class-wcml-page-builders.php 1 month ago class-wcml-synchronize-product-data.php 1 month ago class-wcml-synchronize-variations-data.php 1 month ago class-wcml-translation-editor.php 1 month ago class-wcml-wc-admin-duplicate-product.php 1 month ago
class-wcml-translation-editor.php
395 lines
1 <?php
2
3 use WCML\TranslationJob\Hooks;
4 use WCML\Utilities\DB;
5 use WCML\Utilities\SyncHash;
6
7 use function WCML\functions\getSetting;
8
9 class WCML_Translation_Editor {
10
11 const AUTO_SLUG_NONCE = 'wcml_editor_auto_slug';
12
13 private $woocommerce_wpml;
14 private $sitepress;
15 private $wpdb;
16
17 public function __construct( woocommerce_wpml $woocommerce_wpml, $sitepress, wpdb $wpdb ) {
18 $this->woocommerce_wpml = $woocommerce_wpml;
19 $this->sitepress = $sitepress;
20 $this->wpdb = $wpdb;
21 }
22
23 public function add_hooks() {
24
25 add_filter( 'wpml-translation-editor-fetch-job', [ $this, 'fetch_translation_job_for_editor' ], 10, 2 );
26 add_filter( 'wpml-translation-editor-job-data', [ $this, 'get_translation_job_data_for_editor' ], 10 );
27 add_action( 'admin_print_scripts', [ $this, 'preselect_product_type_in_admin_screen' ], 11 );
28
29 add_filter( 'icl_post_alternative_languages', [ $this, 'hide_post_translation_links' ] );
30
31 if ( getSetting( 'set_up_wizard_run' ) ) {
32 add_filter( 'manage_product_posts_columns', [ $this, 'add_languages_column' ], 100 );
33 }
34 add_action( 'woocommerce_product_after_variable_attributes', [ $this, 'lock_variable_fields' ], 10 );
35
36 add_filter( 'wpml_use_tm_editor', [ $this, 'force_woocommerce_native_editor_for_wcml_products_screen' ], 100 );
37
38 if ( $this->woocommerce_wpml->is_wpml_prior_4_2() ) {
39 add_filter( 'wpml_use_tm_editor', [ $this, 'force_woocommerce_native_editor' ], 100 );
40 add_action(
41 'wpml_pre_status_icon_display',
42 [
43 $this,
44 'force_remove_wpml_translation_editor_links',
45 ],
46 100
47 );
48 }
49
50 add_action( 'wp_ajax_wcml_editor_auto_slug', [ $this, 'auto_generate_slug' ] );
51
52 add_filter( 'wpml_tm_show_page_builders_translation_editor_warning', [ $this, 'show_page_builders_translation_editor_warning' ], 10, 2 );
53 add_filter( 'wpml_translation_editor_save_job_data', [ $this, 'set_ctp_as_editor_for_this_product' ] );
54 }
55
56 public function set_ctp_as_editor_for_this_product( $data ) {
57 if ( 'post_product' === $data['job_post_type'] ) {
58 add_action( 'wpml_save_job_fields_from_post', function ( $job_id ) {
59 wpml_tm_load_old_jobs_editor()->set( $job_id, 'wpml' );
60 } );
61 }
62
63 return $data;
64 }
65
66 public function fetch_translation_job_for_editor( $job, $job_details ) {
67
68 if ( 'post_product' === $job_details['job_type'] ) {
69 $job = new WCML_Editor_UI_Product_Job( $job_details, $this->woocommerce_wpml, $this->sitepress, $this->wpdb );
70 }
71
72 return $job;
73 }
74
75 public function get_translation_job_data_for_editor( $job_data ) {
76 global $iclTranslationManagement;
77
78 $job = $iclTranslationManagement->get_translation_job( $job_data['job_id'] );
79 if ( $job && Hooks::isProduct( $job ) ) {
80 $job_data['job_type'] = 'wc_product';
81 $job_data['job_id'] = $job->original_doc_id;
82 }
83
84 return $job_data;
85 }
86
87 public function preselect_product_type_in_admin_screen() {
88 global $pagenow;
89
90 if ( 'post-new.php' === $pagenow && isset( $_GET['post_type'], $_GET['trid'] ) && $_GET['post_type'] === 'product' ) {
91 $translations = $this->sitepress->get_element_translations( (int) $_GET['trid'], 'post_product_type' );
92 foreach ( $translations as $translation ) {
93 if ( $translation->original ) {
94 $source_lang = $translation->language_code;
95 break;
96 }
97 }
98
99 if ( isset( $source_lang ) ) {
100 $terms = get_the_terms( $translations[ $source_lang ]->element_id, 'product_type' );
101 echo '<script type="text/javascript">';
102 echo PHP_EOL . '// <![CDATA[' . PHP_EOL;
103 echo 'addLoadEvent(function(){' . PHP_EOL;
104 echo "jQuery('#product-type option').prop('selected', false);" . PHP_EOL;
105 echo "jQuery('#product-type option[value=\"" . esc_js( $terms[0]->slug ) . "\"]').prop('selected', true);" . PHP_EOL;
106 echo '});' . PHP_EOL;
107 echo PHP_EOL . '// ]]>' . PHP_EOL;
108 echo '</script>';
109 }
110 }
111 }
112
113 public function hide_post_translation_links( $output ) {
114 global $post;
115
116 if ( null === $post ) {
117 return $output;
118 }
119
120 $post_type = get_post_type( $post->ID );
121 $checkout_page_id = wc_get_page_id( 'checkout' );
122
123 if ( $post_type === 'product' || is_page( $checkout_page_id ) ) {
124 $output = '';
125 }
126
127 return $output;
128 }
129
130 public function create_product_translation_package( $product_id, $trid, $language, $status ) {
131 global $iclTranslationManagement;
132 $translation_id = $this->wpdb->get_var(
133 $this->wpdb->prepare(
134 "
135 SELECT translation_id FROM {$this->wpdb->prefix}icl_translations WHERE trid=%d AND language_code='%s'
136 ",
137 $trid,
138 $language
139 )
140 );
141
142 $md5 = $iclTranslationManagement->post_md5( get_post( $product_id ) );
143 $translation_package = $iclTranslationManagement->create_translation_package( $product_id );
144
145 $current_user = wp_get_current_user();
146 $user_id = $current_user->ID;
147
148 list( $rid, $update ) = $iclTranslationManagement->update_translation_status(
149 [
150 'translation_id' => $translation_id,
151 'status' => $status,
152 'translator_id' => $user_id,
153 'needs_update' => 0,
154 'md5' => $md5,
155 'translation_service' => 'local',
156 'translation_package' => serialize( $translation_package ),
157 ]
158 );
159
160 if ( ! $update ) {
161 wpml_tm_add_translation_job( $rid, $user_id, $translation_package, [] );
162 }
163
164 }
165
166 public function add_languages_column( $columns ) {
167
168 if ( array_key_exists( 'icl_translations', $columns ) ) {
169 return $columns;
170 }
171 $active_languages = $this->sitepress->get_active_languages();
172
173 if ( count( $active_languages ) <= 1 || get_query_var( 'post_status' ) === 'trash' ) {
174 return $columns;
175 }
176 $languages = [];
177
178 foreach ( $active_languages as $v ) {
179 if ( $v['code'] === $this->sitepress->get_current_language() ) {
180 continue;
181 }
182 $languages[] = $v['code'];
183 }
184 $res = $this->wpdb->get_results(
185 $this->wpdb->prepare(
186 "SELECT f.lang_code, f.flag, f.from_template, l.name
187 FROM {$this->wpdb->prefix}icl_flags f
188 JOIN {$this->wpdb->prefix}icl_languages_translations l ON f.lang_code = l.language_code
189 WHERE l.display_language_code = %s AND f.lang_code IN (" . DB::prepareIn( $languages ) . ')',
190 $this->sitepress->get_admin_language()
191 )
192 );
193
194 foreach ( $res as $r ) {
195 if ( $r->from_template ) {
196 $wp_upload_dir = wp_upload_dir();
197 $flag_path = $wp_upload_dir['baseurl'] . '/flags/';
198 } else {
199 $flag_path = ICL_PLUGIN_URL . '/res/flags/';
200 }
201 $flags[ $r->lang_code ] = '<img src="' . $flag_path . $r->flag . '" width="18" height="12" alt="' . $r->name . '" title="' . $r->name . '" />';
202 }
203
204 $flags_column = '';
205 foreach ( $active_languages as $v ) {
206 if ( isset( $flags[ $v['code'] ] ) ) {
207 $flags_column .= $flags[ $v['code'] ];
208 }
209 }
210
211 $new_columns = [];
212 $added = false;
213 foreach ( $columns as $k => $v ) {
214 $new_columns[ $k ] = $v;
215 if ( $k === 'name' ) {
216 $new_columns['icl_translations'] = $flags_column;
217 $added = true;
218 }
219 }
220
221 if ( ! $added ) {
222 $new_columns['icl_translations'] = $flags_column;
223 }
224
225 return $new_columns;
226 }
227
228 public function lock_variable_fields( $loop ) {
229 $product_id = false;
230
231 if ( isset( $_GET['post'] ) && 'product' === get_post_type( $_GET['post'] ) ) {
232 $product_id = $_GET['post'];
233 } elseif ( isset( $_POST['action'], $_POST['product_id'] ) && 'woocommerce_load_variations' === $_POST['action'] ) {
234 $product_id = $_POST['product_id'];
235 }
236
237 if ( ! $product_id ) {
238 return;
239 } elseif (
240 ! $this->woocommerce_wpml->products->is_original_product( $product_id ) &&
241 'auto-draft' !== get_post_status( $product_id )
242 ) {
243 $args = [
244 'post_type' => 'product_variation',
245 'post_status' => [ 'private', 'publish' ],
246 'posts_per_page' => ! empty( $_POST['per_page'] ) ? absint( $_POST['per_page'] ) : 10,
247 'paged' => ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1,
248 'orderby' => [
249 'menu_order' => 'ASC',
250 'ID' => 'DESC',
251 ],
252 'post_parent' => $product_id,
253 ];
254
255 $variations = get_posts( $args );
256 $file_path_sync = [];
257
258 if ( $variations ) {
259 foreach ( $variations as $variation ) {
260 $variation_id = absint( $variation->ID );
261 $original_id = $this->woocommerce_wpml->products->get_original_product_id( $variation_id );
262
263 $file_path_sync[ $variation_id ] = WCML_Downloadable_Products::isDownloadableFilesSetToUseSame( $original_id );
264 }
265 }
266
267 ?>
268 <script type="text/javascript">
269 jQuery(function() {
270 wcml_lock_variation_fields( <?php echo json_encode( $file_path_sync ); ?> );
271 });
272 </script>
273 <?php
274 }
275 }
276
277 public function force_woocommerce_native_editor( $use_tm_editor ) {
278
279 if ( function_exists( 'get_current_screen' ) ) {
280
281 $current_screen = get_current_screen();
282 if ( ! is_null( $current_screen ) ) {
283 if ( $current_screen->id === 'edit-product' || $current_screen->id === 'product' ) {
284 $use_tm_editor = 1;
285 if ( ! $this->woocommerce_wpml->settings['trnsl_interface'] ) {
286 $use_tm_editor = 0;
287 }
288 }
289 }
290 }
291
292 return $use_tm_editor;
293 }
294
295 public function force_woocommerce_native_editor_for_wcml_products_screen( $use_tm_editor ) {
296
297 if ( function_exists( 'get_current_screen' ) ) {
298 $current_screen = get_current_screen();
299 if ( $current_screen && $current_screen->id === 'woocommerce_page_wpml-wcml' ) {
300 $use_tm_editor = 1;
301 }
302 }
303
304 return $use_tm_editor;
305 }
306
307
308
309 public function force_remove_wpml_translation_editor_links() {
310 global $wpml_tm_status_display_filter;
311
312 if ( function_exists( 'get_current_screen' ) ) {
313 $current_screen = get_current_screen();
314
315 $is_edit_product = ! is_null( $current_screen ) && ( $current_screen->id === 'edit-product' || $current_screen->id === 'product' );
316
317 if ( $is_edit_product && ! $this->woocommerce_wpml->settings['trnsl_interface'] ) {
318 remove_filter( 'wpml_link_to_translation', [ $wpml_tm_status_display_filter, 'filter_status_link' ], 10 );
319 }
320 }
321
322 }
323
324 public function auto_generate_slug() {
325 global $wpdb;
326
327 if ( ! check_ajax_referer( self::AUTO_SLUG_NONCE, 'wcml_nonce', false ) ) {
328 wp_send_json_error( 'Invalid nonce' );
329 }
330
331 $title = filter_input( INPUT_POST, 'title' );
332
333 $post_name = urldecode( sanitize_title( $title ) );
334 $job_id = intval( $_POST['job_id'] );
335
336 $post_id_sql = "
337 SELECT t.element_id
338 FROM {$wpdb->prefix}icl_translations t
339 LEFT JOIN {$wpdb->prefix}icl_translation_status s ON t.translation_id = s.translation_id
340 LEFT JOIN {$wpdb->prefix}icl_translate_job j ON s.rid = j.rid
341 WHERE j.job_id = %d
342 ";
343 $post_id = $wpdb->get_var( $wpdb->prepare( $post_id_sql, $job_id ) );
344
345 if ( $post_id ) {
346 $slug = wp_unique_post_slug( $post_name, $post_id, 'publish', 'product', 0 );
347 } else {
348 $lang_sql = "
349 SELECT t.language_code
350 FROM {$wpdb->prefix}icl_translations t
351 LEFT JOIN {$wpdb->prefix}icl_translation_status s ON t.translation_id = s.translation_id
352 LEFT JOIN {$wpdb->prefix}icl_translate_job j ON s.rid = j.rid
353 WHERE j.job_id = %d
354 ";
355 $lang = $wpdb->get_var( $wpdb->prepare( $lang_sql, $job_id ) );
356
357 $check_sql = "
358 SELECT p.post_name
359 FROM {$wpdb->posts} p
360 LEFT JOIN {$wpdb->prefix}icl_translations t
361 ON t.element_id = p.ID AND t.element_type='post_product'
362 WHERE p.post_name = %s AND t.language_code = %s LIMIT 1
363 ";
364 $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $post_name, $lang ) );
365
366 if ( $post_name_check ) {
367 $suffix = 2;
368 do {
369
370 $alt_post_name = _truncate_post_slug( $post_name, 200 - ( strlen( (string) $suffix ) + 1 ) ) . "-$suffix";
371 $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $lang ) );
372 $suffix++;
373
374 } while ( $post_name_check );
375 $slug = $alt_post_name;
376 } else {
377 $slug = $post_name;
378 }
379 }
380
381 wp_send_json( [ 'slug' => $slug ] );
382
383 }
384
385 public function show_page_builders_translation_editor_warning( $display, $post_id ) {
386
387 if ( 'product' === get_post_type( $post_id ) ) {
388 $display = false;
389 }
390
391 return $display;
392 }
393
394 }
395