Lingotek Translation - Version 1.4.4

Version Description

(2019-11-7) * Changed upload process to allow user to select targets on source upload

Download this release

Release Info

Developer elliothanna
Plugin Icon 128x128 Lingotek Translation
Version 1.4.4
Comparing to
See all releases

Code changes from version 1.4.3 to 1.4.4

admin/actions.php CHANGED
@@ -147,6 +147,12 @@ abstract class Lingotek_Actions {
147
  'icon' => 'clock',
148
  ),
149
 
 
 
 
 
 
 
150
  'uploaded' => array(
151
  'title' => __( 'Source uploaded', 'lingotek-translation' ),
152
  'icon' => 'yes',
@@ -215,6 +221,8 @@ abstract class Lingotek_Actions {
215
  }
216
  add_action( 'wp_ajax_lingotek_progress_' . $this->type . '_' . $action , array( &$this, 'ajax_' . $action ) );
217
  }
 
 
218
  }
219
 
220
  /**
@@ -371,7 +379,7 @@ abstract class Lingotek_Actions {
371
  self::link_to_settings_if_not_connected($link);
372
  return self::display_icon( $document->translations[ $language->locale ], $link );
373
  } elseif ( 'not-current' === $document->translations[ $language->locale ] ) {
374
- return '<div class="lingotek-color dashicons dashicons-no"></div>';
375
  } elseif ('current' !== $document->translations[ $language->locale ] && $custom_icon = $document->get_custom_in_progress_icon($language)) {
376
  return $custom_icon;
377
  } else {
@@ -386,6 +394,24 @@ abstract class Lingotek_Actions {
386
  }
387
  }
388
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  /**
390
  * Creates an html action link
391
  *
@@ -404,7 +430,7 @@ abstract class Lingotek_Actions {
404
  }
405
  $link = wp_nonce_url( defined( 'DOING_AJAX' ) && DOING_AJAX ? add_query_arg( $args, wp_get_referer() ) : add_query_arg( $args ), 'lingotek-' . $action );
406
  self::link_to_settings_if_not_connected($link);
407
-
408
  switch($action){
409
  case 'cancel':
410
  $message = self::$confirm_cancel;
@@ -444,7 +470,7 @@ abstract class Lingotek_Actions {
444
  *
445
  * @since 0.2
446
  *
447
- * @param array $actions list of action links
448
  * @param $id object id
449
  * @return array
450
  */
@@ -454,7 +480,7 @@ abstract class Lingotek_Actions {
454
  return $actions;
455
  }
456
  $document = $this->lgtm->get_group( $this->type, $id );
457
-
458
  $target_id = $id;
459
  if ( $this->type !== 'string' && isset( $document->desc_array['lingotek']['source'] ) ) {
460
  $id = $document->desc_array['lingotek']['source'];
@@ -464,10 +490,14 @@ abstract class Lingotek_Actions {
464
  if ( $document ) {
465
  $desc_array = $document->desc_array;
466
  unset( $desc_array['lingotek'] );
 
 
 
 
467
  if ( count( $desc_array ) >= 2 ) {
468
- $actions['lingotek-upload'] = $this->get_action_link( array( $this->type => $id, 'action' => 'upload' ), true );
469
  } else {
470
- $actions['lingotek-upload'] = $this->get_action_link( array( $this->type => $id, 'action' => 'upload' ) );
471
  }
472
 
473
  /**
@@ -482,7 +512,7 @@ abstract class Lingotek_Actions {
482
  $actions['lingotek-download'] = $this->get_action_link( array( 'document_id' => $document->document_id, 'action' => 'download' ) );
483
  }
484
  } else {
485
- $actions['lingotek-upload'] = $this->get_action_link( array( $this->type => $id, 'action' => 'upload' ) );
486
  }
487
  } elseif ( isset( $document->translations ) ) {
488
  // translations to download ?
@@ -494,7 +524,7 @@ abstract class Lingotek_Actions {
494
  $actions['lingotek-status'] = $this->get_action_link( array( 'document_id' => $document->document_id, 'action' => 'status' ) );
495
  }
496
 
497
- // need to request translations ?
498
  $language = $this->get_language( $document->source );
499
  $all_locales = array_flip( $this->pllm->get_languages_list( array( 'fields' => 'locale' ) ) );
500
  if ( ! empty( $language ) ) { // in case a language has been deleted
@@ -504,14 +534,8 @@ abstract class Lingotek_Actions {
504
 
505
  // remove disabled target language from untranslated languages list
506
  foreach ( $untranslated as $k => $v ) {
507
- if ( $this->type === 'term' ) {
508
- if ( $document->is_disabled_target( $language, $this->pllm->get_language( $k ) ) ) {
509
- unset( $untranslated[ $k ] );
510
- }
511
- } else {
512
- if ( $document->is_disabled_target( $language, $this->pllm->get_language( $k ) ) ) {
513
- unset( $untranslated[ $k ] );
514
- }
515
  }
516
  }
517
 
@@ -524,7 +548,7 @@ abstract class Lingotek_Actions {
524
  $actions['lingotek-status'] = $this->get_action_link( array( 'document_id' => $document->document_id, 'action' => 'status' ) );
525
  }
526
  } elseif ( empty( $document->source ) ) {
527
- $actions['lingotek-upload'] = $this->get_action_link( array( $this->type => $id, 'action' => 'upload' ), true );
528
  }
529
 
530
  $target_locale = $this->get_language($target_id)->locale;
@@ -559,25 +583,58 @@ abstract class Lingotek_Actions {
559
  if ( ! empty( $text ) ) {
560
  printf( '<div id="lingotek-progressdialog" style="display:none" title="%s"><div id="lingotek-progressbar"></div></div>', esc_html( $text ) );
561
  }
 
562
 
563
  return $bulk_actions;
564
  }
565
 
566
  /**
567
- * Outputs javascript data for progress.js
 
 
568
  *
569
  * @since 0.1
570
  */
571
  public function admin_enqueue_scripts() {
572
  foreach ( array_keys( self::$actions ) as $action ) {
573
- if ( null !== filter_input( INPUT_GET, "bulk-lingotek-$action" ) ) {
574
- wp_localize_script('lingotek_progress', 'lingotek_data', array(
 
 
 
 
 
 
 
 
 
575
  'action' => null === filter_input( INPUT_GET, 'page' ) ? (null === filter_input( INPUT_GET, 'taxonomy' ) ? "post_$action" : "term_$action") : "string_$action",
576
  'taxonomy' => null === filter_input( INPUT_GET, 'taxonomy' ) || ! taxonomy_exists( wp_unslash( filter_input( INPUT_GET, 'taxonomy' ) ) ) ? '' : filter_input( INPUT_GET, 'taxonomy' ),
577
- 'sendback' => remove_query_arg( array( "bulk-lingotek-$action", 'ids', 'lingotek_warning' ), wp_get_referer() ),
578
  'ids' => array_map( 'intval', explode( ',', filter_input( INPUT_GET, 'ids' ) ) ),
579
  'warning' => null === filter_input( INPUT_GET, 'lingotek_warning' ) ? (null === filter_input(INPUT_GET, 'lingotek_remove') ? '' : __("You are about to $action existing translations from your Lingotek community. Are you sure?", 'lingotek-translation')) : __( 'You are about to overwrite existing translations. Are you sure?', 'lingotek-translation' ),
580
  'nonce' => wp_create_nonce( 'lingotek_progress' ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
  ));
582
  return;
583
  }
@@ -585,7 +642,7 @@ abstract class Lingotek_Actions {
585
  }
586
 
587
  /**
588
- * Manages actions driven by dcoument_id
589
  *
590
  * @since 0.2
591
  *
@@ -673,6 +730,41 @@ abstract class Lingotek_Actions {
673
  return true;
674
  }
675
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
676
  /**
677
  * Ajax response to download translations and showing progress
678
  *
147
  'icon' => 'clock',
148
  ),
149
 
150
+ 'failed' => array(
151
+ 'title' => __( 'Upload Failed. Click to re-upload', 'lingotek-translation' ),
152
+ 'icon' => 'no',
153
+ 'class' => 'lingotek-failed-color'
154
+ ),
155
+
156
  'uploaded' => array(
157
  'title' => __( 'Source uploaded', 'lingotek-translation' ),
158
  'icon' => 'yes',
221
  }
222
  add_action( 'wp_ajax_lingotek_progress_' . $this->type . '_' . $action , array( &$this, 'ajax_' . $action ) );
223
  }
224
+
225
+ add_action( 'wp_ajax_lingotek_upload_with_target', array(&$this, 'ajax_upload_with_target'));
226
  }
227
 
228
  /**
379
  self::link_to_settings_if_not_connected($link);
380
  return self::display_icon( $document->translations[ $language->locale ], $link );
381
  } elseif ( 'not-current' === $document->translations[ $language->locale ] ) {
382
+ return '<div class="lingotek-color dashicons dashicons-no"></div>';
383
  } elseif ('current' !== $document->translations[ $language->locale ] && $custom_icon = $document->get_custom_in_progress_icon($language)) {
384
  return $custom_icon;
385
  } else {
394
  }
395
  }
396
 
397
+ /**
398
+ * Outputs icon for failed import callback
399
+ *
400
+ * @since 1.4.3
401
+ *
402
+ */
403
+ public function failed_import_icon($name, $object_id) {
404
+ $args = array( $this->type => $object_id, 'action' => 'lingotek-upload', 'noheader' => true );
405
+ if (isset($args['string'])) {
406
+ $args['string'] = urlencode($args['string']);
407
+ }
408
+ $link = wp_nonce_url( defined( 'DOING_AJAX' ) && DOING_AJAX ? add_query_arg( $args, wp_get_referer() ) : add_query_arg( $args ), 'lingotek-upload' );
409
+ self::link_to_settings_if_not_connected($link);
410
+
411
+ return sprintf('<a class="%s dashicons dashicons-%s" title="%s" href="%s"></a>',
412
+ self::$icons[ $name ]['class'], self::$icons[ $name ]['icon'], self::$icons[ $name ]['title'], esc_url($link));
413
+ }
414
+
415
  /**
416
  * Creates an html action link
417
  *
430
  }
431
  $link = wp_nonce_url( defined( 'DOING_AJAX' ) && DOING_AJAX ? add_query_arg( $args, wp_get_referer() ) : add_query_arg( $args ), 'lingotek-' . $action );
432
  self::link_to_settings_if_not_connected($link);
433
+
434
  switch($action){
435
  case 'cancel':
436
  $message = self::$confirm_cancel;
470
  *
471
  * @since 0.2
472
  *
473
+ * @param array $actions list of action links
474
  * @param $id object id
475
  * @return array
476
  */
480
  return $actions;
481
  }
482
  $document = $this->lgtm->get_group( $this->type, $id );
483
+
484
  $target_id = $id;
485
  if ( $this->type !== 'string' && isset( $document->desc_array['lingotek']['source'] ) ) {
486
  $id = $document->desc_array['lingotek']['source'];
490
  if ( $document ) {
491
  $desc_array = $document->desc_array;
492
  unset( $desc_array['lingotek'] );
493
+ $data = array( $this->type => $id, 'action' => 'upload' );
494
+ if ('edited' !== $document->status){
495
+ $data['show_target_dialog'] = 1;
496
+ }
497
  if ( count( $desc_array ) >= 2 ) {
498
+ $actions['lingotek-upload'] = $this->get_action_link( $data , true );
499
  } else {
500
+ $actions['lingotek-upload'] = $this->get_action_link( $data );
501
  }
502
 
503
  /**
512
  $actions['lingotek-download'] = $this->get_action_link( array( 'document_id' => $document->document_id, 'action' => 'download' ) );
513
  }
514
  } else {
515
+ $actions['lingotek-upload'] = $this->get_action_link( array( $this->type => $id, 'show_target_dialog' => 1, 'action' => 'upload' ) );
516
  }
517
  } elseif ( isset( $document->translations ) ) {
518
  // translations to download ?
524
  $actions['lingotek-status'] = $this->get_action_link( array( 'document_id' => $document->document_id, 'action' => 'status' ) );
525
  }
526
 
527
+ //need to request translations?
528
  $language = $this->get_language( $document->source );
529
  $all_locales = array_flip( $this->pllm->get_languages_list( array( 'fields' => 'locale' ) ) );
530
  if ( ! empty( $language ) ) { // in case a language has been deleted
534
 
535
  // remove disabled target language from untranslated languages list
536
  foreach ( $untranslated as $k => $v ) {
537
+ if ( $document->is_disabled_target( $language, $this->pllm->get_language( $k ) ) ) {
538
+ unset( $untranslated[ $k ] );
 
 
 
 
 
 
539
  }
540
  }
541
 
548
  $actions['lingotek-status'] = $this->get_action_link( array( 'document_id' => $document->document_id, 'action' => 'status' ) );
549
  }
550
  } elseif ( empty( $document->source ) ) {
551
+ $actions['lingotek-upload'] = $this->get_action_link( array( $this->type => $id, 'show_target_dialog' => 1, 'action' => 'upload' ), true );
552
  }
553
 
554
  $target_locale = $this->get_language($target_id)->locale;
583
  if ( ! empty( $text ) ) {
584
  printf( '<div id="lingotek-progressdialog" style="display:none" title="%s"><div id="lingotek-progressbar"></div></div>', esc_html( $text ) );
585
  }
586
+ printf( '<div id="lingotek-targetselector" style="display:none" title="New Document">Choose which targets you wish to upload with this document<select id="target-select"></select></div>');
587
 
588
  return $bulk_actions;
589
  }
590
 
591
  /**
592
+ * Outputs javascript data for progress.js and target-dialog.js
593
+ * wp_localize_script($handle, $name, $data) $handle needs to be lingotek_myhandle
594
+ * where myhandle is handle of script you loaded previously with wp_enqueue_script (probably in admin.php)
595
  *
596
  * @since 0.1
597
  */
598
  public function admin_enqueue_scripts() {
599
  foreach ( array_keys( self::$actions ) as $action ) {
600
+ if (null !== filter_input( INPUT_GET, "show_target_dialog") && $action === 'upload' && null!== filter_input( INPUT_GET, "bulk-lingotek-upload")){
601
+ wp_localize_script('lingotek_target-dialog', 'lingotek_upload_data', array(
602
+ 'sendback' => remove_query_arg( array( "bulk-lingotek-$action", 'ids', 'lingotek_warning', 'show_target_dialog' ), wp_get_referer() ),
603
+ 'show' => null !== filter_input( INPUT_GET, 'show_target_dialog'),
604
+ 'locales' => array_keys(array_flip( $this->pllm->get_languages_list( array( 'fields' => 'locale' ) ))),
605
+ 'bulk' => true,
606
+ ));
607
+ return;
608
+ }
609
+ else if ( null !== filter_input( INPUT_GET, "bulk-lingotek-$action" ) ) {
610
+ $data = array(
611
  'action' => null === filter_input( INPUT_GET, 'page' ) ? (null === filter_input( INPUT_GET, 'taxonomy' ) ? "post_$action" : "term_$action") : "string_$action",
612
  'taxonomy' => null === filter_input( INPUT_GET, 'taxonomy' ) || ! taxonomy_exists( wp_unslash( filter_input( INPUT_GET, 'taxonomy' ) ) ) ? '' : filter_input( INPUT_GET, 'taxonomy' ),
613
+ 'sendback' => remove_query_arg( array( "bulk-lingotek-$action", 'ids', 'lingotek_warning', 'show_target_dialog' , 'locales'), wp_get_referer() ),
614
  'ids' => array_map( 'intval', explode( ',', filter_input( INPUT_GET, 'ids' ) ) ),
615
  'warning' => null === filter_input( INPUT_GET, 'lingotek_warning' ) ? (null === filter_input(INPUT_GET, 'lingotek_remove') ? '' : __("You are about to $action existing translations from your Lingotek community. Are you sure?", 'lingotek-translation')) : __( 'You are about to overwrite existing translations. Are you sure?', 'lingotek-translation' ),
616
  'nonce' => wp_create_nonce( 'lingotek_progress' ),
617
+ );
618
+ if (null!== filter_input( INPUT_GET, 'locales')){
619
+ $data['locales'] = explode( ',', filter_input( INPUT_GET, 'locales'));
620
+ }
621
+ wp_localize_script('lingotek_progress', 'lingotek_data', $data);
622
+ return;
623
+ }
624
+ else if (null !== filter_input( INPUT_GET, "show_target_dialog") && $action === 'upload'){
625
+ $locales = array_keys(array_flip( $this->pllm->get_languages_list( array( 'fields' => 'locale' ) )));
626
+ $source_locale = filter_input( INPUT_GET, 'source');
627
+ if (($key = array_search($source_locale, $locales)) !== false){
628
+ array_splice($locales, $key, 1);
629
+ }
630
+ wp_localize_script('lingotek_target-dialog', 'lingotek_upload_data', array(
631
+ 'taxonomy' => null === filter_input( INPUT_GET, 'taxonomy' ) || ! taxonomy_exists( wp_unslash( filter_input( INPUT_GET, 'taxonomy' ) ) ) ? '' : filter_input( INPUT_GET, 'taxonomy' ),
632
+ 'sendback' => remove_query_arg( array( 'lingotek-upload', 'show_target_dialog' ), wp_get_referer() ),
633
+ 'show' => null !== filter_input( INPUT_GET, 'show_target_dialog'),
634
+ 'id' => filter_input( INPUT_GET, 'id'),
635
+ 'type' => filter_input( INPUT_GET, 'type'),
636
+ 'locales' => $locales,
637
+ 'nonce' => wp_create_nonce('lingotek_target_upload'),
638
  ));
639
  return;
640
  }
642
  }
643
 
644
  /**
645
+ * Manages actions driven by document_id
646
  *
647
  * @since 0.2
648
  *
730
  return true;
731
  }
732
 
733
+ /**
734
+ * Ajax request to tms for upload doc with target locales
735
+ * Requested by target-dialog.js
736
+ * Passes in by POST a data object (all string)
737
+ * id: document id
738
+ * type: page type - post (page, posts), term(category, tag), string (doesn't work currently)
739
+ * locales: json array of selectedLocales ex ['zh-CN', 'ja', 'es-AR']
740
+ * taxonomy: taxonomy of page (empty for posts, for term its category or post_tag)
741
+ *
742
+ */
743
+ public function ajax_upload_with_target(){
744
+ check_ajax_referer( 'lingotek_target_upload', '_lingotek_target_nonce');
745
+ $id = (int)filter_input(INPUT_POST, 'id');
746
+ $type = filter_input(INPUT_POST, 'type');
747
+ //in the received post, locales are in format "['ja','lv]"
748
+ //wp locales for changing status of locales to pending
749
+ $wp_locales = json_decode(filter_input( INPUT_POST, 'locales'));
750
+ //ltk locales for upload api request
751
+ $lingotek_locales = array_map('Lingotek::map_to_lingotek_locale', $wp_locales);
752
+ $lingotek_locales = array('translation_locale_code' => $lingotek_locales);
753
+
754
+ // not sure if pll_locale is ever different from the one selected but it does this in request translation as well
755
+ $wp_locales = array_map(array($this->pllm, 'get_language'), $wp_locales);
756
+ $wp_locales = array_column($wp_locales, 'locale');
757
+
758
+ switch($type){
759
+ case 'post':
760
+ $this->lgtm->upload_post($id, $lingotek_locales, $wp_locales);
761
+ case 'term':
762
+ $taxonomy = filter_input( INPUT_POST, 'taxonomy');
763
+ $this->lgtm->upload_term($id, $taxonomy, $lingotek_locales, $wp_locales);
764
+ }
765
+ die();
766
+ }
767
+
768
  /**
769
  * Ajax response to download translations and showing progress
770
  *
admin/admin.php CHANGED
@@ -138,7 +138,6 @@ class Lingotek_Admin {
138
  $content_metadata['download_nonce'] = $this->lingotek_get_matching_nonce( 'lingotek-download' );
139
  $content_metadata['upload_nonce'] = $this->lingotek_get_matching_nonce( 'lingotek-upload' );
140
  $content_metadata['status_nonce'] = $this->lingotek_get_matching_nonce( 'lingotek-status' );
141
-
142
  wp_send_json( $content_metadata );
143
  }
144
 
@@ -190,13 +189,15 @@ class Lingotek_Admin {
190
  $suffix = '';
191
  // $suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';.
192
  // for each script:.
193
- // 0 => the pages on which to load the script.
194
- // 1 => the scripts it needs to work.
195
- // 2 => 1 if loaded in footer.
 
196
  // FIXME: check if I can load more scripts in footer.
197
  $scripts = array(
198
  'progress' => array( array( 'edit', 'upload', 'edit-tags', 'translation_page_lingotek-translation_manage', 'translation_page_lingotek-translation_settings' ), array( 'jquery-ui-progressbar', 'jquery-ui-dialog', 'wp-ajax-response' ), 1 ),
199
  'updater' => array( array( 'edit', 'upload', 'edit-tags' ), array( 'jquery-ui-progressbar', 'jquery-ui-dialog', 'wp-ajax-response' ), 1 ),
 
200
  );
201
 
202
  $styles = array(
@@ -217,6 +218,12 @@ class Lingotek_Admin {
217
  wp_enqueue_style( 'lingotek_' . $style, LINGOTEK_URL . '/css/' . $style . $suffix . '.css', $v[1], LINGOTEK_VERSION );
218
  }
219
  }
 
 
 
 
 
 
220
  }
221
 
222
  /**
138
  $content_metadata['download_nonce'] = $this->lingotek_get_matching_nonce( 'lingotek-download' );
139
  $content_metadata['upload_nonce'] = $this->lingotek_get_matching_nonce( 'lingotek-upload' );
140
  $content_metadata['status_nonce'] = $this->lingotek_get_matching_nonce( 'lingotek-status' );
 
141
  wp_send_json( $content_metadata );
142
  }
143
 
189
  $suffix = '';
190
  // $suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';.
191
  // for each script:.
192
+ // js_filename => array([0],[1],[2])
193
+ // 0 => array of pages on which to load the script.
194
+ // 1 => array of registered script handles this script depends on (see wp_enqueue_script in wp doc)
195
+ // 2 => int,bool 1 to enqueue script before </body> instead of in the head. Default is false (see wp_enqueue_script in wp doc)
196
  // FIXME: check if I can load more scripts in footer.
197
  $scripts = array(
198
  'progress' => array( array( 'edit', 'upload', 'edit-tags', 'translation_page_lingotek-translation_manage', 'translation_page_lingotek-translation_settings' ), array( 'jquery-ui-progressbar', 'jquery-ui-dialog', 'wp-ajax-response' ), 1 ),
199
  'updater' => array( array( 'edit', 'upload', 'edit-tags' ), array( 'jquery-ui-progressbar', 'jquery-ui-dialog', 'wp-ajax-response' ), 1 ),
200
+ 'target-dialog' => array( array( 'edit', 'upload', 'edit-tags' ), array( 'jquery-ui-dialog', 'jquery-ui-autocomplete' ), 1),
201
  );
202
 
203
  $styles = array(
218
  wp_enqueue_style( 'lingotek_' . $style, LINGOTEK_URL . '/css/' . $style . $suffix . '.css', $v[1], LINGOTEK_VERSION );
219
  }
220
  }
221
+
222
+ //for target select dropdown modal
223
+ if (in_array($screen->base, array('edit', 'upload', 'edit-tags'), true)){
224
+ wp_enqueue_script('lingotek_select2_js', 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.10/js/select2.min.js', array());
225
+ wp_enqueue_style('lingotek_select2_css', 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.10/css/select2.min.css', array());
226
+ }
227
  }
228
 
229
  /**
admin/filters-columns.php CHANGED
@@ -118,47 +118,56 @@ class Lingotek_Filters_Columns extends PLL_Admin_Filters_Columns {
118
  // error_log('bool: ' . print_r($disabled, true) . PHP_EOL, 3, '/var/www/html/wp-content/plugins/lingotek-translation/error.log');
119
  // post ready for upload.
120
  if ( $this->lgtm->can_upload( $type, $object_id ) && $object_id === $id ) {
121
-
122
  return $disabled ? ('post' === $type ? parent::post_column( $column, $object_id ) : parent::term_column( '', $column, $object_id ))
123
  : ($document && (count( $document->desc_array ) >= 3) ? $actions->upload_icon( $object_id, true ) : $actions->upload_icon( $object_id ));
124
  } // if language is set to copy and profile is manual.
125
- elseif ( ('post' === $type || 'term' === $type) && ((isset( $source_profile['targets'][ $language->slug ] ) && 'copy' === $source_profile['targets'][ $language->slug ]) || (isset( $profile['targets'][ $language->slug ] ) && 'copy' === $profile['targets'][ $language->slug ] ) && isset( $document->source )) ) {
126
-
127
- if ( isset( $document->desc_array[ $language->slug ] ) ) {
 
128
  return ('post' === $type) ? parent::post_column( $column, $object_id ) : parent::term_column( '', $column, $object_id );
129
- } else {
 
130
  if ( $document ) {
131
  return $actions->copy_icon( $document->source, $language->slug );
132
- } else {
 
133
  return $actions->copy_icon( $object_id, $language->slug );
134
  }
135
  }
136
  } // translation disabled.
137
- elseif ( ( isset( $document->source ) && $document->is_disabled_target( $source_language, $language ) && ! isset( $document->translations[ $language->locale ] ) ) || !Lingotek::is_allowed_tms_locale($language->lingotek_locale) ) {
 
138
  return 'post' === $type ? parent::post_column( $column, $object_id ) : parent::term_column( '', $column, $object_id );
139
  } // source post is uploaded.
140
- elseif ( isset( $document->source ) && $document->source === $id ) {
141
  // source ready for upload.
142
  if ( $this->lgtm->can_upload( $type, $id ) ) {
143
  return $actions->upload_icon( $id );
144
- }
145
-
146
  // importing source.
147
  if ( $id === $object_id && 'importing' === $document->status ) {
148
  return Lingotek_Actions::importing_icon( $document );
149
  }
150
-
151
- // uploaded.
 
 
152
  return 'post' === $type ? Lingotek_Post_actions::uploaded_icon( $id ) : Lingotek_Term_actions::uploaded_icon( $id );
153
- } // translations.
154
  elseif ( isset( $document->translations[ $language->locale ] ) || (isset( $document->source ) && 'current' === $document->status) ) {
155
  return Lingotek_Actions::translation_icon( $document, $language );
156
- } elseif ( ( 'term' === $type && ! isset( $document->translations[ $language->locale ] ) && $document->source !== $object_id ) || !Lingotek::is_allowed_tms_locale($language->lingotek_locale) ) {
 
157
  return parent::term_column( '', $column, $object_id );
158
  } // translations exist but are not managed by Lingotek TMS.
159
  elseif ( empty( $document->source ) ) {
160
  return $object_id === $id && ! $disabled ? $actions->upload_icon( $object_id, true ) : ('post' === $type ? parent::post_column( $column, $object_id ) : parent::term_column( '', $column, $object_id ));
161
- } // no translation.
 
 
 
 
162
  else {
163
  return '<div class="lingotek-color dashicons dashicons-no"></div>';
164
  }
118
  // error_log('bool: ' . print_r($disabled, true) . PHP_EOL, 3, '/var/www/html/wp-content/plugins/lingotek-translation/error.log');
119
  // post ready for upload.
120
  if ( $this->lgtm->can_upload( $type, $object_id ) && $object_id === $id ) {
 
121
  return $disabled ? ('post' === $type ? parent::post_column( $column, $object_id ) : parent::term_column( '', $column, $object_id ))
122
  : ($document && (count( $document->desc_array ) >= 3) ? $actions->upload_icon( $object_id, true ) : $actions->upload_icon( $object_id ));
123
  } // if language is set to copy and profile is manual.
124
+ elseif (('post' === $type || 'term' === $type) && ((isset( $source_profile['targets'][ $language->slug ] ) &&
125
+ 'copy' === $source_profile['targets'][ $language->slug ]) || (isset( $profile['targets'][ $language->slug ] ) &&
126
+ 'copy' === $profile['targets'][ $language->slug ] ) && isset( $document->source ))) {
127
+ if (isset( $document->desc_array[ $language->slug ])) {
128
  return ('post' === $type) ? parent::post_column( $column, $object_id ) : parent::term_column( '', $column, $object_id );
129
+ }
130
+ else {
131
  if ( $document ) {
132
  return $actions->copy_icon( $document->source, $language->slug );
133
+ }
134
+ else {
135
  return $actions->copy_icon( $object_id, $language->slug );
136
  }
137
  }
138
  } // translation disabled.
139
+ elseif (( isset( $document->source ) && $document->is_disabled_target( $source_language, $language ) &&
140
+ ! isset( $document->translations[ $language->locale ] ) ) || !Lingotek::is_allowed_tms_locale($language->lingotek_locale) ) {
141
  return 'post' === $type ? parent::post_column( $column, $object_id ) : parent::term_column( '', $column, $object_id );
142
  } // source post is uploaded.
143
+ elseif ( isset( $document->source ) && $document->source === $id ) {
144
  // source ready for upload.
145
  if ( $this->lgtm->can_upload( $type, $id ) ) {
146
  return $actions->upload_icon( $id );
147
+ }
 
148
  // importing source.
149
  if ( $id === $object_id && 'importing' === $document->status ) {
150
  return Lingotek_Actions::importing_icon( $document );
151
  }
152
+ // source failed
153
+ if ($id === $object_id && 'failed' === $document->status) {
154
+ return $actions->failed_import_icon($document->status, $object_id);
155
+ }
156
  return 'post' === $type ? Lingotek_Post_actions::uploaded_icon( $id ) : Lingotek_Term_actions::uploaded_icon( $id );
157
+ } // translations.
158
  elseif ( isset( $document->translations[ $language->locale ] ) || (isset( $document->source ) && 'current' === $document->status) ) {
159
  return Lingotek_Actions::translation_icon( $document, $language );
160
+ }
161
+ elseif ( ( 'term' === $type && ! isset( $document->translations[ $language->locale ] ) && $document->source !== $object_id ) || !Lingotek::is_allowed_tms_locale($language->lingotek_locale) ) {
162
  return parent::term_column( '', $column, $object_id );
163
  } // translations exist but are not managed by Lingotek TMS.
164
  elseif ( empty( $document->source ) ) {
165
  return $object_id === $id && ! $disabled ? $actions->upload_icon( $object_id, true ) : ('post' === $type ? parent::post_column( $column, $object_id ) : parent::term_column( '', $column, $object_id ));
166
+ }
167
+ elseif ($document->status == 'failed') {
168
+ return $actions->failed_import_icon($document->status, $object_id);
169
+ }
170
+ // no translation.
171
  else {
172
  return '<div class="lingotek-color dashicons dashicons-no"></div>';
173
  }
admin/post-actions.php CHANGED
@@ -24,7 +24,7 @@ class Lingotek_Post_actions extends Lingotek_Actions {
24
  // add bulk actions.
25
  add_filter( 'bulk_actions-edit-post', array( &$this, 'add_bulk_actions' ) );
26
  add_filter( 'bulk_actions-edit-page', array( &$this, 'add_bulk_actions' ) );
27
-
28
  foreach(PLL()->model->get_translated_taxonomies() as $taxonomy) {
29
  add_filter( "bulk_actions-edit-$taxonomy", array( &$this, 'add_bulk_actions' ) );
30
  }
@@ -195,6 +195,7 @@ class Lingotek_Post_actions extends Lingotek_Actions {
195
  }
196
  }
197
  }
 
198
 
199
  case 'bulk-lingotek-request':
200
  case 'bulk-lingotek-download':
@@ -222,7 +223,17 @@ class Lingotek_Post_actions extends Lingotek_Actions {
222
 
223
  case 'lingotek-upload':
224
  check_admin_referer( 'lingotek-upload' );
225
- $this->lgtm->upload_post( (int) filter_input( INPUT_GET, 'post' ) );
 
 
 
 
 
 
 
 
 
 
226
  break;
227
 
228
  case 'lingotek-copy':
@@ -250,7 +261,15 @@ class Lingotek_Post_actions extends Lingotek_Actions {
250
  */
251
  public function ajax_upload() {
252
  check_ajax_referer( 'lingotek_progress', '_lingotek_nonce' );
253
- $this->lgtm->upload_post( (int) filter_input( INPUT_POST, 'id' ) );
 
 
 
 
 
 
 
 
254
  die();
255
  }
256
 
24
  // add bulk actions.
25
  add_filter( 'bulk_actions-edit-post', array( &$this, 'add_bulk_actions' ) );
26
  add_filter( 'bulk_actions-edit-page', array( &$this, 'add_bulk_actions' ) );
27
+
28
  foreach(PLL()->model->get_translated_taxonomies() as $taxonomy) {
29
  add_filter( "bulk_actions-edit-$taxonomy", array( &$this, 'add_bulk_actions' ) );
30
  }
195
  }
196
  }
197
  }
198
+ $redirect = add_query_arg( 'show_target_dialog', 1, $redirect);
199
 
200
  case 'bulk-lingotek-request':
201
  case 'bulk-lingotek-download':
223
 
224
  case 'lingotek-upload':
225
  check_admin_referer( 'lingotek-upload' );
226
+ $id = filter_input( INPUT_GET, 'post');
227
+ $document = $this->lgtm->get_group('post', $id);
228
+ if (!$document || 'edited' !== $document->status){
229
+ $redirect = add_query_arg( 'show_target_dialog', 1, $redirect);
230
+ } else{
231
+ $this->lgtm->upload_post($id);
232
+ break;
233
+ }
234
+ $redirect = add_query_arg( 'id', $id, $redirect);
235
+ $redirect = add_query_arg( 'type', 'post', $redirect);
236
+ $redirect = add_query_arg( 'source', $this->pllm->post->get_language($id)->locale, $redirect);
237
  break;
238
 
239
  case 'lingotek-copy':
261
  */
262
  public function ajax_upload() {
263
  check_ajax_referer( 'lingotek_progress', '_lingotek_nonce' );
264
+ $id = (int)filter_input(INPUT_POST, 'id');
265
+ $wp_locales = json_decode(filter_input( INPUT_POST, 'locales'));
266
+
267
+ $lingotek_locales = array_map('Lingotek::map_to_lingotek_locale', $wp_locales);
268
+ $lingotek_locales = array('translation_locale_code' => $lingotek_locales);
269
+
270
+ $wp_locales = array_map(array($this->pllm, 'get_language'), $wp_locales);
271
+ $wp_locales = array_column($wp_locales, 'locale');
272
+ $this->lgtm->upload_post($id, $lingotek_locales, $wp_locales);
273
  die();
274
  }
275
 
admin/term-actions.php CHANGED
@@ -114,7 +114,7 @@ class Lingotek_Term_actions extends Lingotek_Actions {
114
  foreach (array_map('intval', $_REQUEST['delete_tags']) as $term_id) {
115
  // safe upload
116
  if ($this->lgtm->can_upload('term', $term_id))
117
- $terms_ids[] = $term_id;
118
 
119
  // the document is already translated so will be overwritten
120
  elseif(($document = $this->lgtm->get_group('term', $term_id)) && empty($document->source)) {
@@ -136,7 +136,7 @@ class Lingotek_Term_actions extends Lingotek_Actions {
136
  unset($term_ids[$key]);
137
  }
138
  }
139
-
140
  case 'bulk-lingotek-request':
141
  case 'bulk-lingotek-download':
142
  case 'bulk-lingotek-status':
@@ -156,10 +156,20 @@ class Lingotek_Term_actions extends Lingotek_Actions {
156
 
157
  case 'lingotek-upload':
158
  check_admin_referer('lingotek-upload');
159
- $this->lgtm->upload_term((int) $_GET['term'], $taxnow);
 
 
 
 
 
 
 
 
 
 
160
  break;
161
 
162
- case 'lingotek-copy':
163
  check_admin_referer( 'lingotek-copy' );
164
  $term_to_copy = get_term( (int) filter_input( INPUT_GET, 'term' ) );
165
  $target = filter_input( INPUT_GET, 'target' );
@@ -184,8 +194,18 @@ class Lingotek_Term_actions extends Lingotek_Actions {
184
  */
185
  public function ajax_upload() {
186
  check_ajax_referer('lingotek_progress', '_lingotek_nonce');
 
 
 
 
 
 
 
 
 
 
187
  if (taxonomy_exists($_POST['taxonomy']))
188
- $this->lgtm->upload_term((int) $_POST['id'], $_POST['taxonomy']);
189
  die();
190
  }
191
  }
114
  foreach (array_map('intval', $_REQUEST['delete_tags']) as $term_id) {
115
  // safe upload
116
  if ($this->lgtm->can_upload('term', $term_id))
117
+ $term_ids[] = $term_id;
118
 
119
  // the document is already translated so will be overwritten
120
  elseif(($document = $this->lgtm->get_group('term', $term_id)) && empty($document->source)) {
136
  unset($term_ids[$key]);
137
  }
138
  }
139
+ $redirect = add_query_arg( 'show_target_dialog', 1, $redirect);
140
  case 'bulk-lingotek-request':
141
  case 'bulk-lingotek-download':
142
  case 'bulk-lingotek-status':
156
 
157
  case 'lingotek-upload':
158
  check_admin_referer('lingotek-upload');
159
+ $id = filter_input( INPUT_GET, 'term');
160
+ $document = $this->lgtm->get_group('term', $id);
161
+ if (!$document || 'edited' !== $document->status){
162
+ $redirect = add_query_arg( 'show_target_dialog', 1, $redirect);
163
+ } else{
164
+ $this->lgtm->upload_post($id);
165
+ break;
166
+ }
167
+ $redirect = add_query_arg( 'id', filter_input( INPUT_GET, 'term'), $redirect);
168
+ $redirect = add_query_arg( 'type', 'term', $redirect);
169
+ $redirect = add_query_arg( 'source', $this->pllm->term->get_language($id)->locale, $redirect);
170
  break;
171
 
172
+ case 'lingotek-copy':
173
  check_admin_referer( 'lingotek-copy' );
174
  $term_to_copy = get_term( (int) filter_input( INPUT_GET, 'term' ) );
175
  $target = filter_input( INPUT_GET, 'target' );
194
  */
195
  public function ajax_upload() {
196
  check_ajax_referer('lingotek_progress', '_lingotek_nonce');
197
+ $id = (int)filter_input(INPUT_POST, 'id');
198
+ $wp_locales = json_decode(filter_input( INPUT_POST, 'locales'));
199
+
200
+ $lingotek_locales = array_map('Lingotek::map_to_lingotek_locale', $wp_locales);
201
+ $lingotek_locales = array('translation_locale_code' => $lingotek_locales);
202
+
203
+ $wp_locales = array_map(array($this->pllm, 'get_language'), $wp_locales);
204
+ $wp_locales = array_column($wp_locales, 'locale');
205
+
206
+ $taxonomy = filter_input( INPUT_POST, 'taxonomy');
207
  if (taxonomy_exists($_POST['taxonomy']))
208
+ $this->lgtm->upload_term($id, $taxonomy, $lingotek_locales, $wp_locales);
209
  die();
210
  }
211
  }
css/admin.css CHANGED
@@ -14,11 +14,13 @@
14
  #lingotek-progressdialog .ui-dialog-titlebar-close {
15
  display: none;
16
  }
 
17
  .ui-dialog {
18
  box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.24),0 17px 50px 0 rgba(0, 0, 0, 0.19);
19
- padding: 10px;
20
- font-weight: 700;
21
  }
 
22
  .ui-dialog button {
23
  right: 0;
24
  top: 0;
@@ -31,6 +33,73 @@
31
  .ui-dialog button:hover {
32
  background-color: #007fb4;
33
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  /* icons color */
35
  .lingotek-color {
36
  color: #ff7b12; /* lingotek color */
@@ -54,6 +123,16 @@ a.lingotek-interim-color:hover {
54
  color: #dbdbdb;
55
  }
56
 
 
 
 
 
 
 
 
 
 
 
57
  a.dashicons {
58
  cursor: pointer;
59
  }
14
  #lingotek-progressdialog .ui-dialog-titlebar-close {
15
  display: none;
16
  }
17
+
18
  .ui-dialog {
19
  box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.24),0 17px 50px 0 rgba(0, 0, 0, 0.19);
20
+ padding: 12px;
21
+ font-weight: 700;
22
  }
23
+
24
  .ui-dialog button {
25
  right: 0;
26
  top: 0;
33
  .ui-dialog button:hover {
34
  background-color: #007fb4;
35
  }
36
+
37
+ /* target select modal text */
38
+ .ui-dialog.targetSelector .ui-dialog-content{
39
+ font-weight:normal;
40
+ font-style :italic;
41
+ font-size :11px;
42
+ }
43
+
44
+ .ui-dialog.targetSelector .ui-dialog-title{
45
+ font-size:16px;
46
+ }
47
+
48
+ .ui-dialog.targetSelector {
49
+ padding: 12px 0px 68px 12px;
50
+ background-color: white;
51
+ }
52
+
53
+ .ui-dialog.targetSelector button{
54
+ background-color: #0091CD;
55
+ color: white;
56
+ font-size:12px;
57
+ text-transform: uppercase;
58
+ margin-top: 25px;
59
+ height: 32px;
60
+ width: 68px;
61
+ }
62
+
63
+ .requestButton{
64
+ margin-left : 8px;
65
+ margin-right: 12px;
66
+ }
67
+
68
+ .ui-dialog.targetSelector button:hover {
69
+ background-color: #007fb4;
70
+ }
71
+
72
+
73
+ .ui-dialog.targetSelector button:disabled {
74
+ background-color: #b0b0b0;
75
+ }
76
+
77
+ .ui-dialog.targetSelector .ui-dialog-titlebar-close{
78
+ display: none;
79
+ }
80
+
81
+ .select2-selection--multiple {
82
+ font-size:12px;
83
+ font-style:normal;
84
+ border-width: 0px 0px 1px 0px !important;
85
+ border-radius: 0px !important;
86
+ margin: 15px 0px 0px;
87
+ padding: 0px;
88
+ }
89
+
90
+ .select2-selection__rendered {
91
+ padding-left: 0px !important;
92
+ }
93
+
94
+ .select2-selection__choice {
95
+ font-weight:700;
96
+ margin-bottom: 0px !important;
97
+ }
98
+ /*tag fonts
99
+ .targetSelect{
100
+
101
+ }*/
102
+
103
  /* icons color */
104
  .lingotek-color {
105
  color: #ff7b12; /* lingotek color */
123
  color: #dbdbdb;
124
  }
125
 
126
+ .lingotek-failed-color {
127
+ color: #b71c1c;
128
+ }
129
+ a.lingotek-failed-color {
130
+ color: #b71c1c;
131
+ }
132
+ a.lingotek-failed-color:hover {
133
+ color: #b71c1c;
134
+ }
135
+
136
  a.dashicons {
137
  cursor: pointer;
138
  }
include/api.php CHANGED
@@ -118,7 +118,7 @@ class Lingotek_API extends Lingotek_HTTP {
118
 
119
  if (!is_wp_error($response) && 202 == wp_remote_retrieve_response_code($response)) {
120
  $b = json_decode(wp_remote_retrieve_body($response));
121
- Lingotek_Logger::info('Document uploaded', array('document_id' => $b->properties->id, 'wp_id' => $wp_id));
122
  return $b->properties->id;
123
  }
124
  return false;
118
 
119
  if (!is_wp_error($response) && 202 == wp_remote_retrieve_response_code($response)) {
120
  $b = json_decode(wp_remote_retrieve_body($response));
121
+ Lingotek_Logger::info('Document uploaded', array('document_id' => $b->properties->id, 'wp_id' => $wp_id));
122
  return $b->properties->id;
123
  }
124
  return false;
include/callback.php CHANGED
@@ -27,8 +27,9 @@ class Lingotek_Callback {
27
  * @return array unmodified query vars if the request is not a Lingotek callback
28
  */
29
  public function request($query_vars) {
30
- if (empty($query_vars['lingotek']))
31
  return $query_vars;
 
32
 
33
  if (isset($_GET['type'], $_GET['document_id']) && $document = $this->lgtm->get_group_by_id($_GET['document_id'])) {
34
  // url for in context review
@@ -95,10 +96,18 @@ class Lingotek_Callback {
95
  $document->is_automatic_download($locale) ? $document->create_translation($locale, true, $_GET['type']) : $document->translation_ready($locale);
96
  }
97
 
 
 
 
 
 
 
 
 
 
98
  status_header(200); // useless as it the default value
99
  die();
100
  }
101
-
102
  status_header(404); // no document found
103
  die();
104
  }
27
  * @return array unmodified query vars if the request is not a Lingotek callback
28
  */
29
  public function request($query_vars) {
30
+ if (empty($query_vars['lingotek'])) {
31
  return $query_vars;
32
+ }
33
 
34
  if (isset($_GET['type'], $_GET['document_id']) && $document = $this->lgtm->get_group_by_id($_GET['document_id'])) {
35
  // url for in context review
96
  $document->is_automatic_download($locale) ? $document->create_translation($locale, true, $_GET['type']) : $document->translation_ready($locale);
97
  }
98
 
99
+ if (isset($_GET['type'], $_GET['document_id']) && $_GET['type'] == 'import_failure' && $document = $this->lgtm->get_group_by_id($_GET['document_id'])) {
100
+ $callback_parameters = array(
101
+ 'Document ID' => isset($_GET['document_id']) ? $_GET['document_id'] : NULL,
102
+ 'Type' => isset($_GET['type']) ? $_GET['type'] : NULL,
103
+ );
104
+ Lingotek_Logger::info('Callback received', array('Callback Parameters' => $callback_parameters));
105
+ $document->source_failed();
106
+ }
107
+
108
  status_header(200); // useless as it the default value
109
  die();
110
  }
 
111
  status_header(404); // no document found
112
  die();
113
  }
include/dashboard.php CHANGED
@@ -27,8 +27,6 @@ class Lingotek_Dashboard {
27
  'method' => $request_method
28
  );
29
 
30
- error_log(print_r($request_method, true));
31
-
32
  switch ($request_method) {
33
  case 'POST':
34
  if (isset($_REQUEST['code'], $_REQUEST['native'], $_REQUEST['direction'])) {
27
  'method' => $request_method
28
  );
29
 
 
 
30
  switch ($request_method) {
31
  case 'POST':
32
  if (isset($_REQUEST['code'], $_REQUEST['native'], $_REQUEST['direction'])) {
include/group.php CHANGED
@@ -133,7 +133,7 @@ abstract class Lingotek_Group {
133
  $client = new Lingotek_API();
134
  $client->delete_translation($this->document_id, $language->lingotek_locale, $id);
135
  unset($this->desc_array['lingotek']['translations'][$language->locale]);
136
- unset($this->desc_array[$locale]);
137
  $this->save();
138
  }
139
 
@@ -376,6 +376,16 @@ abstract class Lingotek_Group {
376
  $this->save();
377
  }
378
 
 
 
 
 
 
 
 
 
 
 
379
  /*
380
  * returns true if at least one of the translations has the requested status
381
  *
133
  $client = new Lingotek_API();
134
  $client->delete_translation($this->document_id, $language->lingotek_locale, $id);
135
  unset($this->desc_array['lingotek']['translations'][$language->locale]);
136
+ unset($this->desc_array[$language->locale]);
137
  $this->save();
138
  }
139
 
376
  $this->save();
377
  }
378
 
379
+ /*
380
+ * sets document status to failed_import
381
+ *
382
+ * @since 1.4.3
383
+ */
384
+ public function source_failed() {
385
+ $this->status = 'failed';
386
+ $this->save();
387
+ }
388
+
389
  /*
390
  * returns true if at least one of the translations has the requested status
391
  *
include/http.php CHANGED
@@ -24,10 +24,13 @@ class Lingotek_HTTP {
24
 
25
  foreach ($body as $key => $value) {
26
  if (is_array($value)) {
27
- // FIXME is this block useful for Lingotek ?
28
  foreach($value as $k => $v) {
29
  $data .= '--' . $boundary . "\r\n";
30
- $data .= 'Content-Disposition: form-data; name="' . $key . '[' . $k . ']"' . "\r\n\r\n";
 
 
 
 
31
  $data .= $v . "\r\n";
32
  }
33
  }
24
 
25
  foreach ($body as $key => $value) {
26
  if (is_array($value)) {
 
27
  foreach($value as $k => $v) {
28
  $data .= '--' . $boundary . "\r\n";
29
+ $data .= 'Content-Disposition: form-data; name="' . $key;
30
+ if ($key != 'translation_locale_code'){ //request targets cannot be in an array, they need to have the same key
31
+ $data .= '[' . $k . ']';
32
+ }
33
+ $data.= "\"\r\n\r\n";
34
  $data .= $v . "\r\n";
35
  }
36
  }
include/model.php CHANGED
@@ -278,13 +278,15 @@ class Lingotek_Model {
278
  }
279
 
280
  /*
281
- * uploads a new post to Lingotek TMS
282
  *
283
  * @since 0.1
284
  *
285
  * @param int $post_id
 
 
286
  */
287
- public function upload_post($post_id) {
288
  $post = get_post($post_id);
289
  $language = PLL()->model->post->get_language($post_id);
290
  if (empty($post) || empty($language))
@@ -326,7 +328,7 @@ class Lingotek_Model {
326
  $filter_ids['fprm_subfilter_id'] = self::get_profile_option('secondary_filter_id',$post->post_type, $language, false, $post_id);
327
  }
328
  $params = array_merge($params, $filter_ids);
329
-
330
  if (($document = $this->get_group('post', $post_id)) && 'edited' == $document->status) {
331
  $document->patch($post->post_title, $post, $external_url, $filter_ids);
332
  }
@@ -335,8 +337,12 @@ class Lingotek_Model {
335
  $document_id = $client->upload_document($params, $post->ID);
336
 
337
  if ($document_id) {
338
- Lingotek_Group_Post::create($post->ID , $language, $document_id);
339
-
 
 
 
 
340
  // If a translation profile has targets set to copy then copy them
341
  $targets_to_copy = $this->targets_to_be_copied($profile);
342
  $upload = self::get_profile_option('upload', $post->post_type, $language, false, $post_id);
@@ -356,8 +362,9 @@ class Lingotek_Model {
356
  *
357
  * @param int $term_id
358
  * @param string $taxonomy
 
359
  */
360
- public function upload_term($term_id, $taxonomy) {
361
  $term = get_term($term_id, $taxonomy);
362
  $language = PLL()->model->term->get_language($term_id);
363
  if (empty($term) || empty($language))
@@ -393,6 +400,7 @@ class Lingotek_Model {
393
  $filter_ids['fprm_subfilter_id'] = self::get_profile_option('secondary_filter_id', $taxonomy, $language);
394
  }
395
  $params = array_merge($params, $filter_ids);
 
396
 
397
  if (($document = $this->get_group('term', $term_id)) && 'edited' == $document->status) {
398
  $document->patch($term->name, $term, '', $filter_ids);
@@ -402,8 +410,13 @@ class Lingotek_Model {
402
  $document_id = $client->upload_document($params, $term_id);
403
 
404
  if ($document_id) {
405
- Lingotek_Group_Term::create($term_id, $taxonomy , $language, $document_id);
406
-
 
 
 
 
 
407
  // If a translation profile has targets set to copy then copy them
408
  $targets_to_copy = $this->targets_to_be_copied($profile);
409
  if (!empty($targets_to_copy) && $upload == 'automatic') {
@@ -481,7 +494,9 @@ class Lingotek_Model {
481
  // FIXME should I check for disabled profile here?
482
  public function can_upload($type, $object_id) {
483
  $document = $this->get_group($type, $object_id);
484
-
 
 
485
  switch ($type) {
486
  case 'string':
487
  /*
278
  }
279
 
280
  /*
281
+ * uploads a new post to Lingotek TMS and requests the targets specified
282
  *
283
  * @since 0.1
284
  *
285
  * @param int $post_id
286
+ * @param array $target_locales ('translation_locale_code' => lingotek_locale_codes)
287
+ * @param array $wp_target_locales (['ja','zh_CN'])
288
  */
289
+ public function upload_post($post_id, $target_locales = array(), $wp_target_locales = array()) {
290
  $post = get_post($post_id);
291
  $language = PLL()->model->post->get_language($post_id);
292
  if (empty($post) || empty($language))
328
  $filter_ids['fprm_subfilter_id'] = self::get_profile_option('secondary_filter_id',$post->post_type, $language, false, $post_id);
329
  }
330
  $params = array_merge($params, $filter_ids);
331
+ $params = array_merge($params, $target_locales);
332
  if (($document = $this->get_group('post', $post_id)) && 'edited' == $document->status) {
333
  $document->patch($post->post_title, $post, $external_url, $filter_ids);
334
  }
337
  $document_id = $client->upload_document($params, $post->ID);
338
 
339
  if ($document_id) {
340
+ Lingotek_Group_Post::create($post->ID , $language, $document_id, $wp_target_locales);
341
+ $document = $this->get_group_by_id( $document_id );
342
+ foreach ($wp_target_locales as $locale){
343
+ $document->translations[$locale] = 'pending';
344
+ }
345
+ $document->save();
346
  // If a translation profile has targets set to copy then copy them
347
  $targets_to_copy = $this->targets_to_be_copied($profile);
348
  $upload = self::get_profile_option('upload', $post->post_type, $language, false, $post_id);
362
  *
363
  * @param int $term_id
364
  * @param string $taxonomy
365
+ * @param array $target_locales ('translation_locale_code' => lingotek_locale_codes)
366
  */
367
+ public function upload_term($term_id, $taxonomy, $target_locales = array(), $wp_target_locales = array()) {
368
  $term = get_term($term_id, $taxonomy);
369
  $language = PLL()->model->term->get_language($term_id);
370
  if (empty($term) || empty($language))
400
  $filter_ids['fprm_subfilter_id'] = self::get_profile_option('secondary_filter_id', $taxonomy, $language);
401
  }
402
  $params = array_merge($params, $filter_ids);
403
+ $params = array_merge($params, $target_locales);
404
 
405
  if (($document = $this->get_group('term', $term_id)) && 'edited' == $document->status) {
406
  $document->patch($term->name, $term, '', $filter_ids);
410
  $document_id = $client->upload_document($params, $term_id);
411
 
412
  if ($document_id) {
413
+ Lingotek_Group_Term::create($term_id, $taxonomy , $language, $document_id, $target_locales);
414
+ $document = $this->get_group_by_id( $document_id );
415
+ foreach ($wp_target_locales as $locale){
416
+ $document->translations[$locale] = 'pending';
417
+ }
418
+ $document->save();
419
+
420
  // If a translation profile has targets set to copy then copy them
421
  $targets_to_copy = $this->targets_to_be_copied($profile);
422
  if (!empty($targets_to_copy) && $upload == 'automatic') {
494
  // FIXME should I check for disabled profile here?
495
  public function can_upload($type, $object_id) {
496
  $document = $this->get_group($type, $object_id);
497
+ if ($document && $document->status === 'failed') {
498
+ return false;
499
+ }
500
  switch ($type) {
501
  case 'string':
502
  /*
js/progress.js CHANGED
@@ -7,7 +7,9 @@ jQuery(document).ready(function($) {
7
  id: lingotek_data.ids[i],
8
  _lingotek_nonce: lingotek_data.nonce
9
  }
10
-
 
 
11
  $.post(ajaxurl, data , function(response) {
12
  $("#lingotek-progressbar").progressbar({
13
  value: ++i / lingotek_data.ids.length * 100
7
  id: lingotek_data.ids[i],
8
  _lingotek_nonce: lingotek_data.nonce
9
  }
10
+ if (lingotek_data.locales){
11
+ data.locales = JSON.stringify(lingotek_data.locales);
12
+ }
13
  $.post(ajaxurl, data , function(response) {
14
  $("#lingotek-progressbar").progressbar({
15
  value: ++i / lingotek_data.ids.length * 100
js/target-dialog.js ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+ function singleUpload() {
3
+ var data = {
4
+ action: "lingotek_upload_with_target",
5
+ taxonomy: lingotek_upload_data.taxonomy,
6
+ id: lingotek_upload_data.id,
7
+ type: lingotek_upload_data.type,
8
+ locales: JSON.stringify($("#target-select").select2('data').map(a => a.text)), //select2('data') gets selected objects
9
+ _lingotek_target_nonce: lingotek_upload_data.nonce,
10
+ }
11
+ $.ajax({
12
+ type: "POST",
13
+ url: ajaxurl,
14
+ data: data,
15
+ contentType: "application/x-www-form-urlencoded; charset=UTF-8",
16
+ success: function(data){
17
+ jQuery(location).attr('href', lingotek_upload_data.sendback);
18
+ }
19
+ });
20
+ }
21
+
22
+ function bulkUpload(){
23
+ var urlParts = window.location.href.split('?');
24
+ var params = new URLSearchParams(urlParts[1]);
25
+ params.delete('show_target_dialog');
26
+ params.append('locales', $("#target-select").select2('data').map(a => a.text));
27
+ var redirect = urlParts[0] + '?' + params.toString();
28
+ window.location.replace(redirect);
29
+ }
30
+
31
+ if ('undefined' != typeof(lingotek_upload_data)) {
32
+ if (lingotek_upload_data.show) {
33
+ var d = $("#lingotek-targetselector");
34
+ if (d.length) {
35
+ d.dialog({
36
+ width: 410,
37
+ dialogClass: "targetSelector",
38
+ buttons: [
39
+ {
40
+ id: "request-button",
41
+ class: "requestButton",
42
+ text: "Save",
43
+ click: function(){
44
+ lingotek_upload_data.bulk ? bulkUpload() : singleUpload();
45
+ },
46
+ disabled: true,
47
+ },
48
+ {
49
+ text:"Cancel",
50
+ click: function(){
51
+ $(this).dialog('close');
52
+ }
53
+ },
54
+ ]
55
+ });
56
+ d.bind("dialogclose", function(e) {
57
+ window.location = lingotek_upload_data.sendback;
58
+ });
59
+
60
+ var locales = lingotek_upload_data.locales.map((x, i) => {
61
+ var obj = {};
62
+ obj.id = i;
63
+ obj.text = x;
64
+ return obj;
65
+ });
66
+
67
+ //Make backspace delete tags instead of changing text thanks ghilios (select2 github issue 3354)
68
+ $.fn.select2.amd.require(['select2/selection/search'], function (Search) {
69
+ Search.prototype.searchRemoveChoice = function (decorated, item) {
70
+ this.trigger('unselect', {
71
+ data: item
72
+ });
73
+
74
+ this.$search.val('');
75
+ this.handleSearch();
76
+ };
77
+ }, null, true);
78
+
79
+ let $select2 = $("#target-select").select2({
80
+ multiple: true,
81
+ data: locales,
82
+ placeholder: "Select Targets",
83
+ width: 398,
84
+ closeOnSelect: false,
85
+ sorter: data => data.sort((a, b) => a.text.localeCompare(b.text)),
86
+ templateResult: function(value){
87
+ if (value && !value.selected){
88
+ return $('<span>' + value.text + "</span>");
89
+ }
90
+ },
91
+ });
92
+
93
+ //make selected tags always append, but preserve order when removed. Thanks vol7ron for this fix (select2 github issue 3106)
94
+ //modified a bit since didn't work properly for this case
95
+ /**
96
+ * select2_renderselections
97
+ * @param {jQuery Select2 object}
98
+ * @return {null}
99
+ */
100
+ function select2_renderSelections($select2){
101
+ const order = $select2.data('preserved-order') || [];
102
+ const $container = $select2.next('.select2-container');
103
+ const $tags = $container.find('li.select2-selection__choice');
104
+ const $input = $tags.last().next();
105
+
106
+ // apply tag order
107
+ order.forEach(val=>{
108
+ for (let i =0; i < $tags.length; i++){
109
+ $el = $tags[i];
110
+ if ($el.title === val.text){
111
+ break;
112
+ }
113
+ }
114
+ $input.before( $el );
115
+ });
116
+ }
117
+ /**
118
+ * selectionHandler
119
+ * @param {Select2 Event Object}
120
+ * @return {null}
121
+ */
122
+ function selectionHandler(e){
123
+ const $select2 = $(this);
124
+ const val = e.params.data.id;
125
+ const text = e.params.data.text;
126
+ const order = $select2.data('preserved-order') || [];
127
+
128
+ switch (e.type){
129
+ case 'select2:select':
130
+ order[order.length] = { id: val, text: text};
131
+ break;
132
+ case 'select2:unselect':
133
+ let found_index = order.map(a => a.id).indexOf(val);
134
+ if (found_index >= 0 ) {
135
+ order.splice(found_index,1);
136
+ }
137
+ break;
138
+ }
139
+ $select2.data('preserved-order', order); // store it for later
140
+ select2_renderSelections($select2);
141
+ }
142
+
143
+ $select2.on('select2:select select2:unselect', selectionHandler);
144
+
145
+ //disable button until at least one target selected
146
+ $select2.on('change', function(e){
147
+ if ($(this).find(':selected').length == 0){
148
+ $('#request-button').prop("disabled",true);
149
+ } else{
150
+ $('#request-button').prop("disabled", false);
151
+ }
152
+ //refresh after each choice instead of highlighting
153
+ $select2.select2("close");
154
+ $select2.select2("open");
155
+ })
156
+ }
157
+
158
+ } else {
159
+ jQuery(location).attr('href', lingotek_upload_data.sendback);
160
+ }
161
+ }
162
+ });
js/updater.js CHANGED
@@ -113,6 +113,9 @@ jQuery(document).ready(function($) {
113
  updateGenericBulkLink(tr, data, key, 'status' , 'Update translations status of this item in Lingotek TMS', 'Update translations status ');
114
  updateIndicator(td, data, key, locale, 'cancelled', 'Item has been cancelled', 'warning');
115
  break;
 
 
 
116
  case 'disabled':
117
  break;
118
  default:
@@ -128,6 +131,10 @@ jQuery(document).ready(function($) {
128
  else if ($(td).find('.pll_icon_add').length > 0 && data[key][data[key]['source']]['status'] === 'none'){
129
  break;
130
  }
 
 
 
 
131
  else if(source !== false && data[key]['source_status'] === 'current'){
132
  $(td).find('.pll_icon_add').remove();
133
  $(td).find('.pll_icon_tick').remove();
@@ -279,6 +286,18 @@ jQuery(document).ready(function($) {
279
  $(td).prepend(request_link);
280
  }
281
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  function updateIndicator(td, data, key, locale, action, title, dashicon){
283
  if ('download' === dashicon && $(td).find('.lingotek-professional-icon').length > 0)
284
  {
113
  updateGenericBulkLink(tr, data, key, 'status' , 'Update translations status of this item in Lingotek TMS', 'Update translations status ');
114
  updateIndicator(td, data, key, locale, 'cancelled', 'Item has been cancelled', 'warning');
115
  break;
116
+ case 'failed':
117
+ updateFailedIndicator(td, data, key, locale);
118
+ break;
119
  case 'disabled':
120
  break;
121
  default:
131
  else if ($(td).find('.pll_icon_add').length > 0 && data[key][data[key]['source']]['status'] === 'none'){
132
  break;
133
  }
134
+ else if (data[key]['source_status'] === 'failed') {
135
+ updateFailedIndicator(td, data, key, locale);
136
+ break;
137
+ }
138
  else if(source !== false && data[key]['source_status'] === 'current'){
139
  $(td).find('.pll_icon_add').remove();
140
  $(td).find('.pll_icon_tick').remove();
286
  $(td).prepend(request_link);
287
  }
288
 
289
+ function updateFailedIndicator(td, data, key, locale) {
290
+ $(td).find('.lingotek-color').remove();
291
+ $(td).find('.lingotek-failed-color').remove();
292
+ var reupload_failed_doc = $('<a></a>').attr('title', 'Upload Failed. Click to re-upload')
293
+ .attr('href', relative_url
294
+ + page_params + 'post=' + key
295
+ + '&locale=' + locale + '&action=lingotek-upload'
296
+ + '&noheader=1&_wpnonce=' + data['upload_nonce'])
297
+ .addClass('lingotek-failed-color dashicons dashicons-no');
298
+ $(td).prepend(reupload_failed_doc);
299
+ }
300
+
301
  function updateIndicator(td, data, key, locale, action, title, dashicon){
302
  if ('download' === dashicon && $(td).find('.lingotek-professional-icon').length > 0)
303
  {
lingotek.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  Plugin name: Lingotek Translation
4
  Plugin URI: http://lingotek.com/wordpress#utm_source=wpadmin&utm_medium=plugin&utm_campaign=wplingotektranslationplugin
5
- Version: 1.4.3
6
  Author: Lingotek and Frédéric Demarle
7
  Author uri: http://lingotek.com
8
  Description: Lingotek offers convenient cloud-based localization and translation.
@@ -16,7 +16,7 @@ if ( ! function_exists( 'add_action' ) ) {
16
  exit();
17
  }
18
 
19
- define( 'LINGOTEK_VERSION', '1.4.3' ); // plugin version (should match above meta).
20
  define( 'LINGOTEK_MIN_PLL_VERSION', '1.8' );
21
  define( 'LINGOTEK_BASENAME', plugin_basename( __FILE__ ) ); // plugin name as known by WP.
22
  define( 'LINGOTEK_PLUGIN_SLUG', 'lingotek-translation' );// plugin slug (should match above meta: Text Domain).
2
  /**
3
  Plugin name: Lingotek Translation
4
  Plugin URI: http://lingotek.com/wordpress#utm_source=wpadmin&utm_medium=plugin&utm_campaign=wplingotektranslationplugin
5
+ Version: 1.4.4
6
  Author: Lingotek and Frédéric Demarle
7
  Author uri: http://lingotek.com
8
  Description: Lingotek offers convenient cloud-based localization and translation.
16
  exit();
17
  }
18
 
19
+ define( 'LINGOTEK_VERSION', '1.4.4' ); // plugin version (should match above meta).
20
  define( 'LINGOTEK_MIN_PLL_VERSION', '1.8' );
21
  define( 'LINGOTEK_BASENAME', plugin_basename( __FILE__ ) ); // plugin name as known by WP.
22
  define( 'LINGOTEK_PLUGIN_SLUG', 'lingotek-translation' );// plugin slug (should match above meta: Text Domain).
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: http://lingotek.com/
4
  Tags: automation, bilingual, international, language, Lingotek, localization, multilanguage, multilingual, translate, translation
5
  Requires at least: 3.8
6
  Tested up to: 5.2.2
7
- Stable tag: 1.4.3
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -122,6 +122,8 @@ For more, visit the [Lingotek documentation site](https://lingotek.atlassian.net
122
  5. The Lingotek Translation plugin provides the ability to Copy, Translate, and Ignore each specific custom field. Our plugin supports Wordpress custom fields and advanced custom fields.
123
 
124
  == Changelog ==
 
 
125
 
126
  = 1.4.3 (2019-7-23) =
127
 
4
  Tags: automation, bilingual, international, language, Lingotek, localization, multilanguage, multilingual, translate, translation
5
  Requires at least: 3.8
6
  Tested up to: 5.2.2
7
+ Stable tag: 1.4.4
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
122
  5. The Lingotek Translation plugin provides the ability to Copy, Translate, and Ignore each specific custom field. Our plugin supports Wordpress custom fields and advanced custom fields.
123
 
124
  == Changelog ==
125
+ = 1.4.4 (2019-11-7)
126
+ * Changed upload process to allow user to select targets on source upload
127
 
128
  = 1.4.3 (2019-7-23) =
129