Lingotek Translation - Version 1.3.3

Version Description

(2017-9-15) =

  • Fixed issue that prevented slugs from being translated
  • Added better error logging (Thanks to Soluto for contributing this feature!)
  • Fixed minor CSS issues
  • Added the option to use your project's default workflow
Download this release

Release Info

Developer erichie
Plugin Icon 128x128 Lingotek Translation
Version 1.3.3
Comparing to
See all releases

Code changes from version 1.3.2 to 1.3.3

admin/actions.php CHANGED
@@ -314,7 +314,7 @@ abstract class Lingotek_Actions {
314
  return self::display_icon( $document->translations[ $language->locale ], $link, ' target="_blank"' );
315
  }
316
  } else {
317
- $link = wp_nonce_url( add_query_arg( array( 'document_id' => $document->document_id, 'locale' => $language->locale, 'action' => 'lingotek-request', 'noheader' => true ), wp_get_referer() ), 'lingotek-request' );
318
  self::link_to_settings_if_not_connected($link);
319
  return self::display_icon( 'request', $link );
320
  }
@@ -500,7 +500,8 @@ abstract class Lingotek_Actions {
500
  */
501
  protected function _manage_actions( $action ) {
502
  if ( null !== filter_input( INPUT_GET, 'document_id' ) ) {
503
- $document = $this->lgtm->get_group_by_id( filter_input( INPUT_GET, 'document_id' ) );
 
504
  }
505
 
506
  switch ( $action ) {
@@ -512,11 +513,13 @@ abstract class Lingotek_Actions {
512
 
513
  case 'lingotek-request':
514
  check_admin_referer( 'lingotek-request' );
 
515
  null !== filter_input( INPUT_GET, 'locale' ) ? $document->request_translation( filter_input( INPUT_GET, 'locale' ) ) : $document->request_translations();
516
  break;
517
 
518
  case 'lingotek-download':
519
  check_admin_referer( 'lingotek-download' );
 
520
  null !== filter_input( INPUT_GET, 'locale' ) ? $document->create_translation( filter_input( INPUT_GET, 'locale' ) ) : $document->create_translations();
521
  break;
522
 
314
  return self::display_icon( $document->translations[ $language->locale ], $link, ' target="_blank"' );
315
  }
316
  } else {
317
+ $link = wp_nonce_url( add_query_arg( array( 'document_id' => $document->document_id, 'locale' => $language->locale, 'action' => 'lingotek-request', 'noheader' => true ), defined( 'DOING_AJAX' ) && DOING_AJAX ? wp_get_referer() : null ), 'lingotek-request' );
318
  self::link_to_settings_if_not_connected($link);
319
  return self::display_icon( 'request', $link );
320
  }
500
  */
501
  protected function _manage_actions( $action ) {
502
  if ( null !== filter_input( INPUT_GET, 'document_id' ) ) {
503
+ $document_id = filter_input( INPUT_GET, 'document_id' );
504
+ $document = $this->lgtm->get_group_by_id( $document_id );
505
  }
506
 
507
  switch ( $action ) {
513
 
514
  case 'lingotek-request':
515
  check_admin_referer( 'lingotek-request' );
516
+ Lingotek_Logger::info("User requested to translate an item", array("document_id" => isset($document_id) ? $document_id : "", "locale" => filter_input( INPUT_GET, 'locale' )));
517
  null !== filter_input( INPUT_GET, 'locale' ) ? $document->request_translation( filter_input( INPUT_GET, 'locale' ) ) : $document->request_translations();
518
  break;
519
 
520
  case 'lingotek-download':
521
  check_admin_referer( 'lingotek-download' );
522
+ Lingotek_Logger::info("User requested to download translation", array("document_id"=> isset($document_id) ? $document_id : "", "locale" => filter_input( INPUT_GET, 'locale' )));
523
  null !== filter_input( INPUT_GET, 'locale' ) ? $document->create_translation( filter_input( INPUT_GET, 'locale' ) ) : $document->create_translations();
524
  break;
525
 
admin/admin.php CHANGED
@@ -50,7 +50,7 @@ class Lingotek_Admin {
50
  if ( null === $object_ids ) {
51
  return;
52
  }
53
- $terms_translations = $post_vars['terms_translations'];
54
  $terms = ! empty( $terms_translations );
55
 
56
  // The main array consists of ids and nonces. Each id has a source language, languages with statuses, and a workbench link.
@@ -58,7 +58,7 @@ class Lingotek_Admin {
58
  foreach ( $object_ids as $object_id ) {
59
  $id = $object_id;
60
  $type = $terms ? 'term' : 'post';
61
- $taxonomy = $post_vars['taxonomy'];
62
  if ( ! empty( $taxonomy ) ) {
63
  if ( strpos( $taxonomy, '&' ) ) {
64
  $taxonomy = strstr( $taxonomy, '&', true );
@@ -343,13 +343,6 @@ class Lingotek_Admin {
343
  */
344
  public function get_profiles_settings( $defaults = false ) {
345
 
346
- /**
347
- * All specialized modals are echo'd to the page.
348
- * a .js file is loaded that attaches listeners to each of the specialized workflows
349
- * and launches a modal when one of them is selected in the default list.
350
- */
351
- Lingotek_Workflow_Factory::echo_info_modals();
352
-
353
  $resources = get_option( 'lingotek_community_resources' );
354
  $options = array(
355
  'manual' => __( 'Manual', 'lingotek-translation' ),
@@ -484,13 +477,14 @@ class Lingotek_Admin {
484
  'c675bd20-0688-11e2-892e-0800200c9a66' => 'Machine Translation',
485
  );
486
  }
 
487
  if (Lingotek_Professional_Translation_Workflow::is_allowed_user()) {
488
  $workflows['ltk-professional-translation'] = 'Lingotek Professional Translation';
489
  }
490
- natcasesort( $workflows ); // order by title (case-insensitive).
491
  $refresh_success['workflows'] = true;
492
  }
493
-
494
  $api_data = $client->get_filters();
495
  $filters = array();
496
  if ( $api_data && $api_data->properties->total > 0 ) {
@@ -711,4 +705,8 @@ class Lingotek_Admin {
711
  }
712
  }
713
 
 
 
 
 
714
  }
50
  if ( null === $object_ids ) {
51
  return;
52
  }
53
+ $terms_translations = $this->get_if_exists($post_vars,'terms_translations');
54
  $terms = ! empty( $terms_translations );
55
 
56
  // The main array consists of ids and nonces. Each id has a source language, languages with statuses, and a workbench link.
58
  foreach ( $object_ids as $object_id ) {
59
  $id = $object_id;
60
  $type = $terms ? 'term' : 'post';
61
+ $taxonomy = $this->get_if_exists($post_vars,'taxonomy');
62
  if ( ! empty( $taxonomy ) ) {
63
  if ( strpos( $taxonomy, '&' ) ) {
64
  $taxonomy = strstr( $taxonomy, '&', true );
343
  */
344
  public function get_profiles_settings( $defaults = false ) {
345
 
 
 
 
 
 
 
 
346
  $resources = get_option( 'lingotek_community_resources' );
347
  $options = array(
348
  'manual' => __( 'Manual', 'lingotek-translation' ),
477
  'c675bd20-0688-11e2-892e-0800200c9a66' => 'Machine Translation',
478
  );
479
  }
480
+ natcasesort( $workflows ); // order by title (case-insensitive).
481
  if (Lingotek_Professional_Translation_Workflow::is_allowed_user()) {
482
  $workflows['ltk-professional-translation'] = 'Lingotek Professional Translation';
483
  }
484
+ $workflows = array_merge(array('project-default' => 'Use Project Default'), $workflows);
485
  $refresh_success['workflows'] = true;
486
  }
487
+
488
  $api_data = $client->get_filters();
489
  $filters = array();
490
  if ( $api_data && $api_data->properties->total > 0 ) {
705
  }
706
  }
707
 
708
+ private function get_if_exists($array, $key){
709
+ return is_array($array) && array_key_exists($key, $array) ? $array[$key] : null;
710
+ }
711
+
712
  }
admin/manage/view-edit-profile.php CHANGED
@@ -207,3 +207,5 @@ if (!empty($profile['profile']) && !in_array($profile['profile'], array('automat
207
  );
208
  ?> <a href="admin.php?page=lingotek-translation_manage&amp;sm=profiles" class="button"> <?php _e('Cancel', 'lingotek-translation'); ?></a>
209
  </form>
 
 
207
  );
208
  ?> <a href="admin.php?page=lingotek-translation_manage&amp;sm=profiles" class="button"> <?php _e('Cancel', 'lingotek-translation'); ?></a>
209
  </form>
210
+
211
+ <?php Lingotek_Workflow_Factory::echo_info_modals(); ?>
admin/manage/view-profiles.php CHANGED
@@ -85,8 +85,16 @@ if (!empty($_POST)) {
85
  ?>
86
  <h3><?php _e('Translation Profiles', 'lingotek-translation'); ?></h3>
87
  <p class="description"><?php _e('Translation profiles allow you to quickly configure and re-use translation settings.', 'lingotek-translation'); ?></p><?php
 
88
  $table = new Lingotek_Profiles_Table();
89
  $table->prepare_items($profiles);
 
 
 
 
 
 
 
90
  $table->display();
91
  printf(
92
  '<a href="%s" class="button button-primary">%s</a>',
85
  ?>
86
  <h3><?php _e('Translation Profiles', 'lingotek-translation'); ?></h3>
87
  <p class="description"><?php _e('Translation profiles allow you to quickly configure and re-use translation settings.', 'lingotek-translation'); ?></p><?php
88
+
89
  $table = new Lingotek_Profiles_Table();
90
  $table->prepare_items($profiles);
91
+ ?>
92
+ <style>
93
+ .tablenav {
94
+ clear: none !important;
95
+ }
96
+ </style>
97
+ <?php
98
  $table->display();
99
  printf(
100
  '<a href="%s" class="button button-primary">%s</a>',
admin/settings.php CHANGED
@@ -59,3 +59,4 @@
59
  }
60
  ?>
61
  </div>
 
59
  }
60
  ?>
61
  </div>
62
+ <script>jQuery(document).ready(function($) { $('#wpfooter').remove(); } );</script>
admin/settings/view-defaults.php CHANGED
@@ -136,6 +136,7 @@ if ( $default_filters_exist ) { ?>
136
  <p>
137
  <?php submit_button( __( 'Save Changes', 'lingotek-translation' ), 'primary', 'submit', false ); ?>
138
  <?php submit_button( __( 'Refresh Resources', 'lingotek-translation' ), 'secondary', 'refresh', false ); ?>
139
- <script>jQuery(document).ready(function($) { $('#wpfooter').remove(); } );</script>
140
  </p>
141
  </form>
 
 
136
  <p>
137
  <?php submit_button( __( 'Save Changes', 'lingotek-translation' ), 'primary', 'submit', false ); ?>
138
  <?php submit_button( __( 'Refresh Resources', 'lingotek-translation' ), 'secondary', 'refresh', false ); ?>
 
139
  </p>
140
  </form>
141
+
142
+ <?php Lingotek_Workflow_Factory::echo_info_modals(); ?>
admin/view-manage.php CHANGED
@@ -40,3 +40,5 @@
40
  ?>
41
 
42
  </div>
 
 
40
  ?>
41
 
42
  </div>
43
+
44
+ <script>jQuery(document).ready(function($) { $('#wpfooter').remove(); } );</script>
css/admin.css CHANGED
@@ -98,3 +98,7 @@ a.dashicons {
98
  .form-table {
99
  clear: none !important;
100
  }
 
 
 
 
98
  .form-table {
99
  clear: none !important;
100
  }
101
+
102
+ .lingotek-profiles {
103
+ clear: none !important;
104
+ }
include/api.php CHANGED
@@ -40,7 +40,7 @@ class Lingotek_API extends Lingotek_HTTP {
40
 
41
  public function get_token_details($access_token) {
42
  $url = $this->base_url . "/auth/oauth2/access_token_info?access_token=" . $access_token;
43
- Lingotek::log("GET " . $url . " (" . __METHOD__ . ")");
44
  $response = wp_remote_get($url);
45
  $response_code = wp_remote_retrieve_response_code($response);
46
 
@@ -51,6 +51,7 @@ class Lingotek_API extends Lingotek_HTTP {
51
  else {
52
  $token_details = FALSE;
53
  }
 
54
  return $token_details;
55
  }
56
 
@@ -68,6 +69,8 @@ class Lingotek_API extends Lingotek_HTTP {
68
  public function update_callback_url($project_id) {
69
  $args = array('callback_url' => add_query_arg('lingotek', 1, site_url()));
70
  $response = $this->patch($this->api_url . '/project/' . $project_id, $args);
 
 
71
  return !is_wp_error($response) && 204 == wp_remote_retrieve_response_code($response);
72
  }
73
 
@@ -82,18 +85,19 @@ class Lingotek_API extends Lingotek_HTTP {
82
  $args = array(
83
  'title' => $title,
84
  'community_id' => $community_id,
85
- 'workflow_id' => $this->defaults['workflow_id'],
86
  'callback_url' => add_query_arg('lingotek', 1, site_url()),
87
  );
88
 
89
  $response = $this->post($this->api_url . '/project', $args);
90
  if(!is_wp_error($response) && 201 == wp_remote_retrieve_response_code($response)) {
91
  $new_id = json_decode(wp_remote_retrieve_body($response));
 
92
  return $new_id->properties->id;
93
  }
94
- else {
95
- return false;
96
- }
97
  }
98
 
99
  /**
@@ -105,31 +109,15 @@ class Lingotek_API extends Lingotek_HTTP {
105
  * @returns bool|string document_id, false if something got wrong
106
  */
107
  public function upload_document($args, $wp_id = null) {
108
- $args = wp_parse_args($args, array('format' => 'JSON', 'project_id' => $this->defaults['project_id'], 'workflow_id' => $this->defaults['workflow_id']));
109
  $this->format_as_multipart($args);
110
  $response = $this->post($this->api_url . '/document', $args);
111
 
112
- if ($wp_id){
113
- $arr = get_option('lingotek_log_errors');
114
-
115
- if (202 == wp_remote_retrieve_response_code($response)) {
116
- if (isset($arr[$wp_id])) {
117
- unset($arr[$wp_id]);
118
- }
119
- }
120
- else if (is_wp_error($response)) {
121
- $arr[$wp_id]['wp_error'] = __('Make sure you have internet connectivity', 'lingotek-translation');
122
- }
123
- else if (400 == wp_remote_retrieve_response_code($response)) {
124
- $arr[$wp_id]['upload_document'] = sprintf(
125
- __('There was an error uploading WordPress item %1$s', 'lingotek-translation'), $wp_id
126
- );
127
- }
128
- update_option('lingotek_log_errors', $arr);
129
- }
130
 
131
  if (!is_wp_error($response) && 202 == wp_remote_retrieve_response_code($response)) {
132
  $b = json_decode(wp_remote_retrieve_body($response));
 
133
  return $b->properties->id;
134
  }
135
  return false;
@@ -149,26 +137,12 @@ class Lingotek_API extends Lingotek_HTTP {
149
  $this->format_as_multipart($args);
150
  $response = $this->patch($this->api_url . '/document/' . $id, $args);
151
 
152
- if ($wp_id) {
153
- $arr = get_option('lingotek_log_errors');
154
-
155
- if (202 == wp_remote_retrieve_response_code($response)) {
156
- if (isset($arr[$wp_id])) {
157
- unset($arr[$wp_id]);
158
- }
159
- }
160
- else if (is_wp_error($response)) {
161
- $arr[$wp_id]['wp_error'] = __('Make sure you have internet connectivity', 'lingotek-translation');
162
- }
163
- else if (400 == wp_remote_retrieve_response_code($response) || 404 == wp_remote_retrieve_response_code($response)) {
164
- $arr[$wp_id]['patch_document'] = sprintf(
165
- __('There was an error updating WordPress item %1$s', 'lingotek-translation') ,$wp_id
166
- );
167
- }
168
- update_option('lingotek_log_errors', $arr);
169
- }
170
 
171
- return !is_wp_error($response) && 202 == wp_remote_retrieve_response_code($response);
172
  }
173
 
174
  /**
@@ -189,7 +163,11 @@ class Lingotek_API extends Lingotek_HTTP {
189
  }
190
  }
191
 
192
- return !is_wp_error($response) && (204 == wp_remote_retrieve_response_code($response) || 202 == wp_remote_retrieve_response_code($response));
 
 
 
 
193
  }
194
 
195
 
@@ -209,6 +187,7 @@ class Lingotek_API extends Lingotek_HTTP {
209
  }
210
  }
211
 
 
212
  return $ids;
213
  }
214
 
@@ -221,6 +200,7 @@ class Lingotek_API extends Lingotek_HTTP {
221
  return $response->properties->total;
222
  }
223
 
 
224
  return null;
225
  }
226
 
@@ -235,11 +215,12 @@ class Lingotek_API extends Lingotek_HTTP {
235
 
236
  if (!is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response)) {
237
  $documents = json_decode(wp_remote_retrieve_body($response));
238
- foreach ($documents->entities as $doc) {
239
  $docs[] = $doc;
240
  }
241
  }
242
 
 
243
  return $docs;
244
  }
245
 
@@ -255,6 +236,7 @@ class Lingotek_API extends Lingotek_HTTP {
255
  $document = json_decode(wp_remote_retrieve_body($response));
256
  }
257
 
 
258
  return $document;
259
  }
260
 
@@ -267,6 +249,7 @@ class Lingotek_API extends Lingotek_HTTP {
267
  $imported = TRUE;
268
  }
269
 
 
270
  return $imported;
271
  }
272
 
@@ -285,6 +268,8 @@ class Lingotek_API extends Lingotek_HTTP {
285
  if (!is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response)) {
286
  $content = wp_remote_retrieve_body( $response );
287
  }
 
 
288
  return $content;
289
  }
290
 
@@ -305,24 +290,14 @@ class Lingotek_API extends Lingotek_HTTP {
305
  }
306
  }
307
 
308
- if($wp_id) {
309
- $arr = get_option('lingotek_log_errors');
310
-
311
- if (200 == wp_remote_retrieve_response_code($response)) {
312
- if (isset($arr[$wp_id])) {
313
- unset($arr[$wp_id]);
314
- }
315
- }
316
- else if (is_wp_error($response)) {
317
- $arr[$wp_id]['wp_error'] = __('Make sure you have internet connectivity', 'lingotek-translation');
318
- }
319
- else if (400 == wp_remote_retrieve_response_code($response)) {
320
- $arr[$wp_id]['get_translations_status'] = sprintf(
321
- __('There was an error updating the translations status for WordPress item %1$s', 'lingotek-translation'), $wp_id
322
- );
323
- }
324
- update_option('lingotek_log_errors', $arr);
325
- }
326
 
327
  return empty($translations) ? array() : $translations;
328
  }
@@ -354,6 +329,8 @@ class Lingotek_API extends Lingotek_HTTP {
354
  $success = 200 === wp_remote_retrieve_response_code($response);
355
 
356
  $this->end_bridge_call();
 
 
357
  return array('success' => $success, 'data' => $this->get_response_body_from_bridge($response));
358
  }
359
 
@@ -376,6 +353,10 @@ class Lingotek_API extends Lingotek_HTTP {
376
  $success = 200 === wp_remote_retrieve_response_code($response);
377
 
378
  $this->end_bridge_call();
 
 
 
 
379
  return array('success' => $success, 'data' => $this->get_response_body_from_bridge($response));
380
  }
381
 
@@ -399,6 +380,10 @@ class Lingotek_API extends Lingotek_HTTP {
399
  $success = 200 === wp_remote_retrieve_response_code($response);
400
 
401
  $this->end_bridge_call();
 
 
 
 
402
  return array('data' => $this->get_response_body_from_bridge($response));
403
  }
404
 
@@ -408,6 +393,8 @@ class Lingotek_API extends Lingotek_HTTP {
408
  $response = $this->get(BRIDGE_URL . '/api/v2/transaction/terms/');
409
  $success = 200 === wp_remote_retrieve_response_code($response);
410
  $this->end_bridge_call();
 
 
411
  return array('success' => $success, 'data' => $this->get_response_body_from_bridge($response));
412
  }
413
 
@@ -421,6 +408,8 @@ class Lingotek_API extends Lingotek_HTTP {
421
  $success = 200 === wp_remote_retrieve_response_code($response);
422
 
423
  $this->end_bridge_call();
 
 
424
  return array('success' => $success, 'payment_info' => $this->get_response_body_from_bridge($response));
425
  }
426
 
@@ -447,7 +436,7 @@ class Lingotek_API extends Lingotek_HTTP {
447
  */
448
  public function request_translation($id, $locale, $args = array(), $wp_id = null) {
449
  $locale = Lingotek::map_to_lingotek_locale($locale);
450
- $args = wp_parse_args($args, array('workflow_id' => $this->defaults['workflow_id']));
451
  $args = array_merge(array('locale_code' => $locale), $args);
452
  $response = $this->post($this->api_url . '/document/' . $id . '/translation', $args);
453
 
@@ -607,4 +596,93 @@ class Lingotek_API extends Lingotek_HTTP {
607
  unset($this->headers['Authorization-Lingotek']);
608
  $this->headers['Authorization'] = $this->auth_temp;
609
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
610
  }
40
 
41
  public function get_token_details($access_token) {
42
  $url = $this->base_url . "/auth/oauth2/access_token_info?access_token=" . $access_token;
43
+ Lingotek_Logger::debug( "GET" , array( "url" => $url, "method" => __METHOD__));
44
  $response = wp_remote_get($url);
45
  $response_code = wp_remote_retrieve_response_code($response);
46
 
51
  else {
52
  $token_details = FALSE;
53
  }
54
+ $this->log_error_on_response_failure($response, "GetTokenDetails: Error occured");
55
  return $token_details;
56
  }
57
 
69
  public function update_callback_url($project_id) {
70
  $args = array('callback_url' => add_query_arg('lingotek', 1, site_url()));
71
  $response = $this->patch($this->api_url . '/project/' . $project_id, $args);
72
+ Lingotek_Logger::info('Request to update callback url', array('project_id' => $project_id));
73
+ $this->log_error_on_response_failure($response, 'UpdateCallbackUrl: Error occured', array('project_id' => $project_id));
74
  return !is_wp_error($response) && 204 == wp_remote_retrieve_response_code($response);
75
  }
76
 
85
  $args = array(
86
  'title' => $title,
87
  'community_id' => $community_id,
88
+ 'workflow_id' => $this->get_workflow_id(),
89
  'callback_url' => add_query_arg('lingotek', 1, site_url()),
90
  );
91
 
92
  $response = $this->post($this->api_url . '/project', $args);
93
  if(!is_wp_error($response) && 201 == wp_remote_retrieve_response_code($response)) {
94
  $new_id = json_decode(wp_remote_retrieve_body($response));
95
+ Lingotek_Logger::info('Project created', array('id' => $new_id->properties->id, 'title' => $title));
96
  return $new_id->properties->id;
97
  }
98
+
99
+ $this->log_error_on_response_failure($response, 'CreateProject: Error occured', array('title' => $title, 'community_id' => $community_id));
100
+ return false;
101
  }
102
 
103
  /**
109
  * @returns bool|string document_id, false if something got wrong
110
  */
111
  public function upload_document($args, $wp_id = null) {
112
+ $args = wp_parse_args($args, array('format' => 'JSON', 'project_id' => $this->defaults['project_id'], 'workflow_id' => $this->get_workflow_id()));
113
  $this->format_as_multipart($args);
114
  $response = $this->post($this->api_url . '/document', $args);
115
 
116
+ $this->update_lingotek_error_option($response, $wp_id, 'upload_document', sprintf(__('There was an error uploading WordPress item %1$s', 'lingotek-translation'), $wp_id));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
  if (!is_wp_error($response) && 202 == wp_remote_retrieve_response_code($response)) {
119
  $b = json_decode(wp_remote_retrieve_body($response));
120
+ Lingotek_Logger::info('Document uploaded', array('document_id' => $b->properties->id, 'wp_id' => $wp_id));
121
  return $b->properties->id;
122
  }
123
  return false;
137
  $this->format_as_multipart($args);
138
  $response = $this->patch($this->api_url . '/document/' . $id, $args);
139
 
140
+ $this->update_lingotek_error_option($response, $wp_id, 'patch_document', sprintf(__('There was an error updating WordPress item %1$s', 'lingotek-translation'), $wp_id), array('document_id' => $id));
141
+
142
+ $is_success = !is_wp_error($response) && 202 == wp_remote_retrieve_response_code($response);
143
+ if ($is_success) { Lingotek_Logger::info('Document updated', array('document_id' => $id, 'wp_id' => $wp_id)); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
+ return $is_success;
146
  }
147
 
148
  /**
163
  }
164
  }
165
 
166
+ $this->log_error_on_response_failure($response, 'DeleteDocument: Error occured', array('id' => $id, 'wordpress_id' => $wp_id));
167
+ $is_success = !is_wp_error($response) && (204 == wp_remote_retrieve_response_code($response) || 202 == wp_remote_retrieve_response_code($response));
168
+ if ($is_success) { Lingotek_Logger::info('Document deleted', array('document_id' => $id, 'wp_id' => $wp_id)); }
169
+
170
+ return $is_success;
171
  }
172
 
173
 
187
  }
188
  }
189
 
190
+ $this->log_error_on_response_failure($response, 'GetDocumentIds: Error occured');
191
  return $ids;
192
  }
193
 
200
  return $response->properties->total;
201
  }
202
 
203
+ $this->log_error_on_response_failure($response, 'GetDocumentCount: Error occured');
204
  return null;
205
  }
206
 
215
 
216
  if (!is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response)) {
217
  $documents = json_decode(wp_remote_retrieve_body($response));
218
+ foreach ($documents->entities as $doc) {
219
  $docs[] = $doc;
220
  }
221
  }
222
 
223
+ $this->log_error_on_response_failure($response, 'GetDocuments: Error occured');
224
  return $docs;
225
  }
226
 
236
  $document = json_decode(wp_remote_retrieve_body($response));
237
  }
238
 
239
+ $this->log_error_on_response_failure($response, 'GetDocument: Error occured');
240
  return $document;
241
  }
242
 
249
  $imported = TRUE;
250
  }
251
 
252
+ $this->log_error_on_response_failure($response, 'GetDocumentStatus: Error occured', array('document_id' => $doc_id));
253
  return $imported;
254
  }
255
 
268
  if (!is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response)) {
269
  $content = wp_remote_retrieve_body( $response );
270
  }
271
+
272
+ $this->log_error_on_response_failure($response, 'GetDocumentContent: Error occured', array('document_id' => $doc_id));
273
  return $content;
274
  }
275
 
290
  }
291
  }
292
 
293
+ Lingotek_Logger::info('Translation status requested', array('document_id' => $doc_id, 'wp_id' => $wp_id, 'translations' => isset($translations) ? $translations : '' ));
294
+
295
+ $this->update_lingotek_error_option(
296
+ $response,
297
+ $wp_id,
298
+ 'get_translations_status',
299
+ sprintf(__('There was an error updating the translations status for WordPress item %1$s', 'lingotek-translation'), $wp_id),
300
+ array('document_id'=>$doc_id));
 
 
 
 
 
 
 
 
 
 
301
 
302
  return empty($translations) ? array() : $translations;
303
  }
329
  $success = 200 === wp_remote_retrieve_response_code($response);
330
 
331
  $this->end_bridge_call();
332
+
333
+ $this->log_error_on_response_failure($response, 'GetCostEstimate: Error occured', array('document_id' => $document_id, 'locale' => $locale));
334
  return array('success' => $success, 'data' => $this->get_response_body_from_bridge($response));
335
  }
336
 
353
  $success = 200 === wp_remote_retrieve_response_code($response);
354
 
355
  $this->end_bridge_call();
356
+
357
+ if ($success) { Lingotek_Logger::info('Professional translation requested', array('document_id' => $document_id, 'locale' => $locale, 'wordflow_id' => $workflow_id)); }
358
+ $this->log_error_on_response_failure($response, 'RequestProfessionalTranslation: Error occured', array('document_id' => $document_id, 'locale' => $locale, 'workflow_id' => $workflow_id));
359
+
360
  return array('success' => $success, 'data' => $this->get_response_body_from_bridge($response));
361
  }
362
 
380
  $success = 200 === wp_remote_retrieve_response_code($response);
381
 
382
  $this->end_bridge_call();
383
+
384
+ if ($success) { Lingotek_Logger::info('Professional translation (bulk) requested', array('translations' => $translations, 'wordflow_id' => $workflow_id)); }
385
+ $this->log_error_on_response_failure($response, 'RequestProfessionalTranslationBulk: Error occured', array('translations' => $translations, 'total_estimate' => $total_estimate, 'workflow_id' => $workflow_id));
386
+
387
  return array('data' => $this->get_response_body_from_bridge($response));
388
  }
389
 
393
  $response = $this->get(BRIDGE_URL . '/api/v2/transaction/terms/');
394
  $success = 200 === wp_remote_retrieve_response_code($response);
395
  $this->end_bridge_call();
396
+
397
+ $this->log_error_on_response_failure($response, 'GetLingotekTermsAndConditions: Error occured');
398
  return array('success' => $success, 'data' => $this->get_response_body_from_bridge($response));
399
  }
400
 
408
  $success = 200 === wp_remote_retrieve_response_code($response);
409
 
410
  $this->end_bridge_call();
411
+
412
+ $this->log_error_on_response_failure($response, "GetUserPaymentInformation: Error occured");
413
  return array('success' => $success, 'payment_info' => $this->get_response_body_from_bridge($response));
414
  }
415
 
436
  */
437
  public function request_translation($id, $locale, $args = array(), $wp_id = null) {
438
  $locale = Lingotek::map_to_lingotek_locale($locale);
439
+ $args = wp_parse_args($args, array('workflow_id' => $this->get_workflow_id()));
440
  $args = array_merge(array('locale_code' => $locale), $args);
441
  $response = $this->post($this->api_url . '/document/' . $id . '/translation', $args);
442
 
596
  unset($this->headers['Authorization-Lingotek']);
597
  $this->headers['Authorization'] = $this->auth_temp;
598
  }
599
+
600
+ private function get_workflow_id() {
601
+ return 'project-default' === $this->defaults['workflow_id'] ? null : $this->defaults['workflow_id'];
602
+ }
603
+
604
+ /**
605
+ * Helper function to update lingotek errors as 'lingotek_log_errors' option in case of translation actions (get / request).
606
+ *
607
+ * @param $response the response from the action (wp_remote_*).
608
+ * @param $wp_id id that represents the object in the WP world (post_id / term_id / etc)
609
+ * @param $action the name of the action performed (for ex 'reqeust_translation')
610
+ * @param $error_message the message to write in case the response indicates failure
611
+ * @param $locale the locale the translation action was performed on
612
+ * @param $extra_data (optional) array of key/value pairs that will be sent as part of the error
613
+ */
614
+ private function update_lingotek_error_option_for_translation($response, $wp_id, $action, $error_message, $locale, $extra_data = array()){
615
+ if ($wp_id) {
616
+ $arr = get_option('lingotek_log_errors');
617
+ $response_message_code = wp_remote_retrieve_response_code($response);
618
+ if (200 == $response_message_code || 201 == $response_message_code) {
619
+ if (isset($arr[$wp_id])) {
620
+ unset($arr[$wp_id]['wp_error']);
621
+ unset($arr[$wp_id][$action][$locale]);
622
+ if (empty($arr[$wp_id])) {
623
+ unset($arr[$wp_id]);
624
+ }
625
+ }
626
+ }
627
+ else if (is_wp_error($response)) {
628
+ $arr[$wp_id]['wp_error'] = __('Make sure you have internet connectivity', 'lingotek-translation');
629
+ Lingotek_Logger::error($action.": Wordpress error occured, please make sure you have internet connectivity", array_merge(array('http_status'=>$response_message_code, 'wordpress_id'=>$wp_id), $extra_data));
630
+ }
631
+ else if (400 == $response_message_code || 404 == $response_message_code) {
632
+ $arr[$wp_id][$action][$locale] = $error_message;
633
+ Lingotek_Logger::error($action.": Error occured", array_merge(array('response_message_code'=>$response_message_code, 'wordpress_id'=>$wp_id, 'response_message'=>$this->get_error_message_from_response($response)), $extra_data));
634
+ }
635
+ update_option('lingotek_log_errors', $arr);
636
+ }
637
+ }
638
+
639
+ /**
640
+ * Helper function to update lingotek errors as 'lingotek_log_errors' option.
641
+ *
642
+ * @param $response the response from the action (wp_remote_*).
643
+ * @param $wp_id id that represents the object in the WP world (post_id / term_id / etc)
644
+ * @param $action the name of the action performed (for ex 'reqeust_translation')
645
+ * @param $error_message the message to write in case the response indicates failure
646
+ * @param $extra_data (optional) array of key/value pairs that will be sent as part of the error
647
+ */
648
+ private function update_lingotek_error_option($response, $wp_id, $action, $error_message, $extra_data = array()){
649
+ if ($wp_id){
650
+ $arr = get_option('lingotek_log_errors');
651
+ $response_message_code = wp_remote_retrieve_response_code($response);
652
+
653
+ if (200 == $response_message_code || 202 == $response_message_code) {
654
+ if (isset($arr[$wp_id])) {
655
+ unset($arr[$wp_id]);
656
+ }
657
+ }
658
+ else if (is_wp_error($response)) {
659
+ $arr[$wp_id]['wp_error'] = __('Make sure you have internet connectivity', 'lingotek-translation');
660
+ Lingotek_Logger::error($action.": Wordpress error occured, please make sure you have internet connectivity", array_merge(array('http_status'=>$response_message_code, 'wordpress_id'=>$wp_id), $extra_data));
661
+ }
662
+ else if (400 == $response_message_code || 404 == $response_message_code) {
663
+ $arr[$wp_id][$action] = $error_message;
664
+ Lingotek_Logger::error($action.": Error occured", array_merge(array('http_status'=>$response_message_code, 'wordpress_id'=>$wp_id, 'response_message'=>$this->get_error_message_from_response($response)), $extra_data));
665
+ }
666
+ update_option('lingotek_log_errors', $arr);
667
+ }
668
+ }
669
+
670
+ /**
671
+ * Helper function to send error log entry to Lingotek_Logger in case the response indicates failure.
672
+ * Failure response has http status different than 200/201/202/204 and is not wp_error response
673
+ *
674
+ * @param $response the response from the action (wp_remote_*).
675
+ * @param $error_message the message to write in case the response indicates failure
676
+ * @param $extra_data (optional) array of key/value pairs that will be sent as part of the error
677
+ */
678
+ private function log_error_on_response_failure($response, $error_message, $extra_data = array()){
679
+ $http_code = wp_remote_retrieve_response_code($response);
680
+ $success = 200 === $http_code || 201 === $http_code || 202 === $http_code || 204 === $http_code;
681
+ if (!$success || is_wp_error($response)) Lingotek_Logger::error($error_message, array_merge(array('http_status'=>$http_code, $extra_data)));
682
+ }
683
+
684
+ private function get_error_message_from_response($response){
685
+ $responseBody = json_decode(wp_remote_retrieve_body($response));
686
+ return property_exists($responseBody, "messages") && is_array($responseBody->messages) ? implode(" ",$responseBody->messages) : false;
687
+ }
688
  }
include/group-post.php CHANGED
@@ -248,7 +248,7 @@ class Lingotek_Group_Post extends Lingotek_Group {
248
  // send slug for translation only if it has been modified
249
  elseif('post_name' == $key && empty($content_types[$post->post_type]['fields'][$key])) {
250
  $default_slug = sanitize_title($post->post_title); // default slug created by WP
251
- if ($default_slug != $post->post_name)
252
  $arr['post'][$key] = $post->$key;
253
  }
254
 
@@ -293,7 +293,7 @@ class Lingotek_Group_Post extends Lingotek_Group {
293
  remove_filter('content_filtered_save_pre', 'wp_filter_post_kses');
294
 
295
  $client = new Lingotek_API();
296
-
297
  $translation = $client->get_translation($this->document_id, $locale, $this->source);
298
  if (!$translation || $this->translation_not_ready( json_decode($translation, true) )) return; // If the request failed.
299
  $translation = json_decode($translation, true); // wp_insert_post expects array
@@ -302,6 +302,9 @@ class Lingotek_Group_Post extends Lingotek_Group {
302
  $prefs = Lingotek_Model::get_prefs(); // need an array by default
303
 
304
  $tr_post = $translation['post'];
 
 
 
305
 
306
  $post = get_post($this->source); // source post
307
  $tr_post['post_status'] = ($prefs['download_post_status'] === self::SAME_AS_SOURCE)? $post->post_status : $prefs['download_post_status']; // status
@@ -367,7 +370,7 @@ class Lingotek_Group_Post extends Lingotek_Group {
367
  self::copy_translated_metas($translation['metas'], $tr_id);
368
  }
369
 
370
- if (class_exists('PLL_Share_Post_Slug', true) && isset($content_type_options[$post->post_type]['fields']['post_name'])) {
371
  wp_update_post(array('ID' => $tr_id ,'post_name' => $post->post_name));
372
  }
373
  }
248
  // send slug for translation only if it has been modified
249
  elseif('post_name' == $key && empty($content_types[$post->post_type]['fields'][$key])) {
250
  $default_slug = sanitize_title($post->post_title); // default slug created by WP
251
+ // if ($default_slug != $post->post_name)
252
  $arr['post'][$key] = $post->$key;
253
  }
254
 
293
  remove_filter('content_filtered_save_pre', 'wp_filter_post_kses');
294
 
295
  $client = new Lingotek_API();
296
+
297
  $translation = $client->get_translation($this->document_id, $locale, $this->source);
298
  if (!$translation || $this->translation_not_ready( json_decode($translation, true) )) return; // If the request failed.
299
  $translation = json_decode($translation, true); // wp_insert_post expects array
302
  $prefs = Lingotek_Model::get_prefs(); // need an array by default
303
 
304
  $tr_post = $translation['post'];
305
+ if (isset($tr_post['post_name'])) {
306
+ $tr_post['post_name'] = sanitize_title($tr_post['post_name']);
307
+ }
308
 
309
  $post = get_post($this->source); // source post
310
  $tr_post['post_status'] = ($prefs['download_post_status'] === self::SAME_AS_SOURCE)? $post->post_status : $prefs['download_post_status']; // status
370
  self::copy_translated_metas($translation['metas'], $tr_id);
371
  }
372
 
373
+ if (class_exists('PLL_Share_Post_Slug', true) && $content_type_options[$post->post_type]['fields']['post_name'] == 1) {
374
  wp_update_post(array('ID' => $tr_id ,'post_name' => $post->post_name));
375
  }
376
  }
include/group.php CHANGED
@@ -183,6 +183,9 @@ abstract class Lingotek_Group {
183
  $client = new Lingotek_API();
184
  $language = $this->pllm->get_language($locale);
185
  $workflow = Lingotek_Model::get_profile_option('workflow_id', $this->type, $this->get_source_language(), $language, $this->source);
 
 
 
186
  $args = $workflow ? array('workflow_id' => $workflow) : array();
187
 
188
  if (!$this->is_disabled_target($language) && empty($this->translations[$language->locale])) {
@@ -216,6 +219,9 @@ abstract class Lingotek_Group {
216
  } else {
217
  if ($source_language->slug != $lang->slug && !$this->is_disabled_target($source_language, $lang) && empty($this->translations[$lang->locale])) {
218
  $workflow = Lingotek_Model::get_profile_option('workflow_id', $this->type, $source_language, $lang, $this->source);
 
 
 
219
  $args = $workflow ? array('workflow_id' => $workflow) : array();
220
 
221
  if ($this->type == 'string') {
183
  $client = new Lingotek_API();
184
  $language = $this->pllm->get_language($locale);
185
  $workflow = Lingotek_Model::get_profile_option('workflow_id', $this->type, $this->get_source_language(), $language, $this->source);
186
+ if ('project-default' === $workflow) {
187
+ $workflow = null;
188
+ }
189
  $args = $workflow ? array('workflow_id' => $workflow) : array();
190
 
191
  if (!$this->is_disabled_target($language) && empty($this->translations[$language->locale])) {
219
  } else {
220
  if ($source_language->slug != $lang->slug && !$this->is_disabled_target($source_language, $lang) && empty($this->translations[$lang->locale])) {
221
  $workflow = Lingotek_Model::get_profile_option('workflow_id', $this->type, $source_language, $lang, $this->source);
222
+ if ('project-default' === $workflow) {
223
+ $workflow = null;
224
+ }
225
  $args = $workflow ? array('workflow_id' => $workflow) : array();
226
 
227
  if ($this->type == 'string') {
include/logger.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This class handles logging events by giving the option to report / subscribe to logging events
5
+ *
6
+ * @since 1.3.1
7
+ */
8
+ class Lingotek_Logger {
9
+
10
+ protected static $logging = false;
11
+
12
+ public static function fatal($message, $extra_data = null, $error = null){ Lingotek_Logger::log("fatal", $message, $extra_data, $error); }
13
+ public static function error($message, $extra_data = null, $error = null){ Lingotek_Logger::log("error", $message, $extra_data, $error); }
14
+ public static function warning($message, $extra_data = null, $error = null){ Lingotek_Logger::log("warning", $message, $extra_data, $error); }
15
+ public static function info($message, $extra_data = null){ Lingotek_Logger::log("info", $message, $extra_data); }
16
+ public static function debug($message, $extra_data = null){ Lingotek_Logger::log("debug", $message, $extra_data); }
17
+
18
+ private static function log($level, $message, $extra_data, $error = null){
19
+ try{
20
+ do_action( 'lingotek_log', $level, $message, $extra_data, $error );
21
+ do_action( 'lingotek_log_'.$level, $message, $extra_data, $error );
22
+
23
+ if (Lingotek_Logger::$logging){
24
+ $serialized_extra_data = Lingotek_Logger::serialize_extra_data($extra_data);
25
+ $errorMessage = isset($error) && !isempty($error) ? ", Error: ".$error : "";
26
+ error_log($level.": ".$message.$errorMessage.", ExtraData: ".$serialized_extra_data);
27
+ }
28
+ }
29
+ catch (Exception $e){
30
+ error_log('error occured while trying to write Lingotek log entry');
31
+ }
32
+ }
33
+
34
+ private static function serialize_extra_data($extra_data){
35
+ try
36
+ {
37
+ if (is_string($extra_data)) return $extra_data;
38
+ return json_encode($extra_data, JSON_PRETTY_PRINT);
39
+ }
40
+ catch (Exception $e){
41
+ return "";
42
+ }
43
+ }
44
+ }
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.3.2
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.3.2' ); // 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.3.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
  exit();
17
  }
18
 
19
+ define( 'LINGOTEK_VERSION', '1.3.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).
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: 4.8
7
- Stable tag: 1.3.2
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -122,6 +122,13 @@ For more, visit the [Lingotek documentation site](https://lingotek.atlassian.net
122
 
123
  == Changelog ==
124
 
 
 
 
 
 
 
 
125
  = 1.3.2 (2017-8-8) =
126
 
127
  * Updated string group action links to support multi-word titles
4
  Tags: automation, bilingual, international, language, Lingotek, localization, multilanguage, multilingual, translate, translation
5
  Requires at least: 3.8
6
  Tested up to: 4.8
7
+ Stable tag: 1.3.3
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
122
 
123
  == Changelog ==
124
 
125
+ = 1.3.3 (2017-9-15) =
126
+
127
+ * Fixed issue that prevented slugs from being translated
128
+ * Added better error logging (Thanks to Soluto for contributing this feature!)
129
+ * Fixed minor CSS issues
130
+ * Added the option to use your project's default workflow
131
+
132
  = 1.3.2 (2017-8-8) =
133
 
134
  * Updated string group action links to support multi-word titles