Lingotek Translation - Version 1.0

Version Description

(2015-06-22) =

  • Released on June 22, 2015

=

Download this release

Release Info

Developer smithworx
Plugin Icon 128x128 Lingotek Translation
Version 1.0
Comparing to
See all releases

Version 1.0

Files changed (73) hide show
  1. admin/actions.php +457 -0
  2. admin/admin.php +500 -0
  3. admin/content-table.php +200 -0
  4. admin/filters-columns.php +165 -0
  5. admin/filters-media.php +56 -0
  6. admin/filters-post.php +183 -0
  7. admin/filters-term.php +131 -0
  8. admin/manage/view-string-groups.php +36 -0
  9. admin/manage/view-strings.php +49 -0
  10. admin/post-actions.php +193 -0
  11. admin/profiles-table.php +133 -0
  12. admin/settings.php +64 -0
  13. admin/settings/connect-account.php +31 -0
  14. admin/settings/view-account.php +120 -0
  15. admin/settings/view-content.php +93 -0
  16. admin/settings/view-defaults.php +141 -0
  17. admin/settings/view-edit-profile.php +200 -0
  18. admin/settings/view-preferences.php +84 -0
  19. admin/settings/view-profiles.php +95 -0
  20. admin/settings/view-utilities.php +78 -0
  21. admin/string-actions.php +144 -0
  22. admin/strings-table.php +170 -0
  23. admin/table-string.php +31 -0
  24. admin/term-actions.php +184 -0
  25. admin/tutorial/content.php +95 -0
  26. admin/tutorial/credits.php +61 -0
  27. admin/tutorial/faq.php +64 -0
  28. admin/tutorial/features.php +39 -0
  29. admin/tutorial/img/add-languages.png +0 -0
  30. admin/tutorial/img/add-page.png +0 -0
  31. admin/tutorial/img/add-page2.png +0 -0
  32. admin/tutorial/img/automatic-translation.gif +0 -0
  33. admin/tutorial/img/automatic-translation.png +0 -0
  34. admin/tutorial/img/check-status.png +0 -0
  35. admin/tutorial/img/content-types.png +0 -0
  36. admin/tutorial/img/dashboard.png +0 -0
  37. admin/tutorial/img/polylang-compatible.png +0 -0
  38. admin/tutorial/img/pro-translation.png +0 -0
  39. admin/tutorial/img/professional-translation.png +0 -0
  40. admin/tutorial/img/ready-to-upload.png +0 -0
  41. admin/tutorial/img/request-translations.png +0 -0
  42. admin/tutorial/img/translation-profiles.png +0 -0
  43. admin/tutorial/img/translations-downloaded.png +0 -0
  44. admin/tutorial/img/translations-ready-for-download.png +0 -0
  45. admin/tutorial/img/translations-underway.png +0 -0
  46. admin/tutorial/img/workbench-full.png +0 -0
  47. admin/tutorial/img/workbench.png +0 -0
  48. admin/utilities.php +132 -0
  49. admin/view-dashboard.php +9 -0
  50. admin/view-manage.php +39 -0
  51. admin/view-network.php +167 -0
  52. admin/view-tutorial.php +49 -0
  53. css/admin.css +77 -0
  54. img/lingotek-chevrons-blue.png +0 -0
  55. img/lingotek-icon.png +0 -0
  56. img/lingotek-white.png +0 -0
  57. include/api.php +292 -0
  58. include/callback.php +85 -0
  59. include/dashboard.php +307 -0
  60. include/group-post.php +219 -0
  61. include/group-string.php +188 -0
  62. include/group-term.php +167 -0
  63. include/group.php +294 -0
  64. include/http.php +94 -0
  65. include/model.php +563 -0
  66. include/pointer.php +107 -0
  67. js/defaults.js +18 -0
  68. js/progress.js +39 -0
  69. languages/wp-lingotek-fr_FR.mo +0 -0
  70. languages/wp-lingotek-fr_FR.po +1203 -0
  71. lingotek.php +524 -0
  72. readme.txt +98 -0
  73. uninstall.php +47 -0
admin/actions.php ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Base class to add row and bulk actions to posts, media and terms list
5
+ * Bulk actions management is inspired by http://www.foxrunsoftware.net/articles/wordpress/add-custom-bulk-action/
6
+ *
7
+ * @since 0.2
8
+ */
9
+ abstract class Lingotek_Actions {
10
+ public $pllm, $lgtm; // Polylang and Lingotek models
11
+ public $type; // *must* be defined in child class: 'post' or 'term'
12
+ public static $actions, $icons, $confirm_message;
13
+
14
+ /*
15
+ * Constructor
16
+ *
17
+ * @since 0.2
18
+ */
19
+ public function __construct($type) {
20
+ // confirm message
21
+ self::$confirm_message = sprintf(' onclick = "return confirm(\'%s\');"', __('You are about to overwrite existing translations. Are you sure?', 'wp-lingotek'));
22
+
23
+ // row actions
24
+ self::$actions = array(
25
+ 'upload' => array(
26
+ 'action' => __('Upload to Lingotek', 'wp-lingotek'),
27
+ 'progress' => __('Uploading...', 'wp-lingotek'),
28
+ 'description' => __('Upload this item to Lingotek TMS', 'wp-lingotek' ),
29
+ ),
30
+
31
+ 'request' => array(
32
+ 'action' => __('Request translations', 'wp-lingotek'),
33
+ 'progress' => __('Requesting translations...', 'wp-lingotek'),
34
+ 'description' => __('Request translations of this item to Lingotek TMS', 'wp-lingotek' ),
35
+ ),
36
+
37
+ 'status' => array(
38
+ 'action' => __('Update translations status', 'wp-lingotek'),
39
+ 'progress' => __('Updating translations status...', 'wp-lingotek'),
40
+ 'description' => __('Update translations status of this item in Lingotek TMS', 'wp-lingotek' ),
41
+ ),
42
+
43
+ 'download' => array(
44
+ 'action' => __('Download translations', 'wp-lingotek'),
45
+ 'progress' => __('Downloading translations...', 'wp-lingotek'),
46
+ 'description' => __('Download translations of this item from Lingotek TMS', 'wp-lingotek' ),
47
+ ),
48
+
49
+ 'delete' => array(
50
+ 'action' => __('Disassociate translations', 'wp-lingotek'),
51
+ 'progress' => __('Disassociating translations...', 'wp-lingotek'),
52
+ 'description' => __('Disassociate the translations of this item from Lingotek TMS', 'wp-lingotek' ),
53
+ ),
54
+ );
55
+
56
+ // action icons
57
+ self::$icons = array(
58
+ 'upload' => array(
59
+ 'title' => __('Upload Now', 'wp-lingotek'),
60
+ 'icon' => 'upload'
61
+ ),
62
+
63
+ 'importing' => array(
64
+ 'title' => __('Importing source', 'wp-lingotek'),
65
+ 'icon' => 'clock'
66
+ ),
67
+
68
+ 'uploaded' => array(
69
+ 'title' => __('Source uploaded', 'wp-lingotek'),
70
+ 'icon' => 'yes'
71
+ ),
72
+
73
+ 'request' => array(
74
+ 'title' => __('Request a translation', 'wp-lingotek'),
75
+ 'icon' => 'plus'
76
+ ),
77
+
78
+ 'pending' => array(
79
+ 'title' => __('In Progress', 'wp-lingotek'),
80
+ 'icon' => 'clock'
81
+ ),
82
+
83
+ 'ready' => array(
84
+ 'title' => __('Ready to download', 'wp-lingotek'),
85
+ 'icon' => 'download'
86
+ ),
87
+
88
+ 'current' => array(
89
+ 'title' => __('current', 'wp-lingotek'),
90
+ 'icon' => 'edit'
91
+ ),
92
+
93
+ 'not-current' => array(
94
+ 'title' => __('The target translation is no longer current as the source content has been updated', 'wp-lingotek'),
95
+ 'icon' => 'edit'
96
+ ),
97
+ );
98
+
99
+ $this->type = $type;
100
+ $this->pllm = $GLOBALS['polylang']->model;
101
+ $this->lgtm = $GLOBALS['wp_lingotek']->model;
102
+
103
+ add_action('admin_enqueue_scripts', array(&$this, 'admin_enqueue_scripts'));
104
+
105
+ foreach (array_keys(self::$actions) as $action)
106
+ add_action('wp_ajax_lingotek_progress_' . $this->type . '_'. $action , array(&$this, 'ajax_' . $action));
107
+ }
108
+
109
+ /*
110
+ * generates a workbench link
111
+ *
112
+ * @since 0.1
113
+ *
114
+ * @param string $document_id
115
+ * @param string $locale Lingotek locale
116
+ * @return string workbench link
117
+ */
118
+ public static function workbench_link($document_id, $locale) {
119
+ $client_id = Lingotek_API::CLIENT_ID;
120
+ $token_details = get_option('lingotek_token');
121
+ $user = wp_get_current_user();
122
+ $base_url = get_option('lingotek_base_url');
123
+
124
+ $acting_login_id = $user->user_email; //user_nicename;
125
+
126
+ return self::generate_workbench_link(
127
+ $document_id,
128
+ $locale,
129
+ $client_id,
130
+ $token_details['access_token'],
131
+ $token_details['login_id'],
132
+ $acting_login_id,
133
+ $base_url
134
+ );
135
+ }
136
+
137
+ /*
138
+ * generates a workbench link
139
+ * function provided by Matt Smith from Lingotek
140
+ *
141
+ * @since 0.1
142
+ *
143
+ * @param string $document_id
144
+ * @param string $locale_code
145
+ * @param string $client_id
146
+ * @param string $access_token
147
+ * @param string $login_id
148
+ * @param string $acting_login_id
149
+ * @param string $base_url
150
+ * @param int|null $expiration
151
+ * @return string workbench link
152
+ */
153
+ public static function generate_workbench_link($document_id, $locale_code, $client_id, $access_token, $login_id, $acting_login_id = "anonymous", $base_url = "https://myaccount.lingotek.com", $expiration = NULL) {
154
+ $expiration_default = time() + (60 * 30); // 30-minute default, otherwise use $expiration as passed in
155
+ $expiration = is_null($expiration) ? $expiration_default : $expiration;
156
+ $data = array(
157
+ 'document_id' => $document_id,
158
+ 'locale_code' => $locale_code,
159
+ 'client_id' => $client_id,
160
+ 'login_id' => $login_id,
161
+ 'acting_login_id' => $acting_login_id,
162
+ 'expiration' => $expiration
163
+ );
164
+ $query_data = utf8_encode(http_build_query($data));
165
+ $hmac = urlencode(base64_encode(hash_hmac('sha1', $query_data, $access_token, TRUE)));
166
+ $workbench_url = $base_url . '/lingopoint/portal/wb.action?' . $query_data . "&hmac=" . $hmac;
167
+ return $workbench_url;
168
+ }
169
+
170
+ /*
171
+ * outputs an action icon
172
+ *
173
+ * @since 0.2
174
+ *
175
+ * @param string $name
176
+ * @param string $link
177
+ * @param string $additional parameters to add (js, target)
178
+ */
179
+ public static function display_icon($name, $link, $additional = '') {
180
+ return sprintf('<a class="lingotek-color dashicons dashicons-%s" title="%s" href="%s"%s></a>',
181
+ self::$icons[$name]['icon'], self::$icons[$name]['title'], esc_url($link), $additional);
182
+ }
183
+
184
+ /*
185
+ * outputs an upload icon
186
+ *
187
+ * @since 0.2
188
+ *
189
+ * @param int|string $object_id
190
+ * @param bool $warning
191
+ */
192
+ public function upload_icon($object_id, $confirm = false) {
193
+ $args = array($this->type => $object_id, 'action' => 'lingotek-upload', 'noheader' => true);
194
+ $link = wp_nonce_url(defined('DOING_AJAX') && DOING_AJAX ? add_query_arg($args, wp_get_referer()) : add_query_arg($args), 'lingotek-upload');
195
+ return self::display_icon('upload', $link, $confirm ? self::$confirm_message : '');
196
+ }
197
+
198
+ /*
199
+ * outputs an importing icon
200
+ *
201
+ * @since 0.2
202
+ *
203
+ * @param object $document
204
+ */
205
+ public static function importing_icon($document) {
206
+ $args = array('document_id' => $document->document_id, 'action' => 'lingotek-status', 'noheader' => true);
207
+ $link = wp_nonce_url(defined('DOING_AJAX') && DOING_AJAX ? add_query_arg($args, wp_get_referer()) : add_query_arg($args), 'lingotek-status');
208
+ return self::display_icon('importing', $link);
209
+ }
210
+
211
+ /*
212
+ * outputs icons for translations
213
+ *
214
+ * @since 0.2
215
+ *
216
+ * @param object $document
217
+ * @param object $language
218
+ */
219
+ public static function translation_icon($document, $language) {
220
+ if (isset($document->translations[$language->locale])) {
221
+ if ('ready' == $document->translations[$language->locale]) {
222
+ $link = wp_nonce_url(add_query_arg(array('document_id' => $document->document_id, 'locale' => $language->locale, 'action' => 'lingotek-download', 'noheader' => true)), 'lingotek-download');
223
+ return self::display_icon($document->translations[$language->locale], $link);
224
+ }
225
+ else {
226
+ $link = self::workbench_link($document->document_id, $language->lingotek_locale);
227
+ return self::display_icon($document->translations[$language->locale], $link, ' target="_blank"');
228
+ }
229
+
230
+ }
231
+ else {
232
+ $link = wp_nonce_url(add_query_arg(array('document_id' => $document->document_id, 'locale' => $language->locale, 'action' => 'lingotek-request', 'noheader' => true)), 'lingotek-request');
233
+ return self::display_icon('request', $link);
234
+ }
235
+ }
236
+
237
+ /*
238
+ * creates an html action link
239
+ *
240
+ * @since 0.2
241
+ *
242
+ * @param array $args parameters to add to the link
243
+ * @param bool $warning whether to display an alert or not, optional, defaults to false
244
+ * @return string
245
+ */
246
+ protected function get_action_link($args, $warning = false) {
247
+ $action = $args['action'];
248
+ $args['action'] = 'lingotek-' . $action;
249
+ $args['noheader'] = true;
250
+
251
+ return sprintf(
252
+ '<a class="lingotek-color" title="%s" href="%s"%s>%s</a>',
253
+ self::$actions[$action]['description'],
254
+ wp_nonce_url(add_query_arg($args, defined('DOING_AJAX') && DOING_AJAX ? wp_get_referer() : ''), 'lingotek-' .$action),
255
+ empty($warning) ? '' : self::$confirm_message,
256
+ self::$actions[$action]['action']
257
+ );
258
+ }
259
+
260
+ /*
261
+ * adds a row action link
262
+ *
263
+ * @since 0.2
264
+ *
265
+ * @param array $actions list of action links
266
+ * @param $id object id
267
+ * @return array
268
+ */
269
+ protected function _row_actions($actions, $id) {
270
+ // first check that a language is associated to this object
271
+ if (!$this->get_language($id))
272
+ return $actions;
273
+
274
+ $document = $this->lgtm->get_group($this->type, $id);
275
+
276
+ if ($this->lgtm->can_upload($this->type, $id) || (isset($document->source) && 'string' != $this->type && $this->lgtm->can_upload($this->type, $document->source))) {
277
+ $actions['lingotek-upload'] = $this->get_action_link(array($this->type => $id, 'action' => 'upload'));
278
+ }
279
+
280
+ elseif (isset($document->translations)) {
281
+ // translations to download ?
282
+ if ($document->has_translation_status('ready'))
283
+ $actions['lingotek-download'] = $this->get_action_link(array('document_id' => $document->document_id, 'action' => 'download'));
284
+
285
+ // need to request translations ?
286
+ $language = $this->get_language($document->source);
287
+ $all_locales = array_flip($this->pllm->get_languages_list(array('fields' => 'locale')));
288
+ if (!empty($language)) // in case a language has been deleted
289
+ unset($all_locales[$language->locale]);
290
+ $untranslated = array_diff_key($all_locales, $document->translations);
291
+
292
+ // remove disabled target language from untranslated languages list
293
+ foreach ($untranslated as $k => $v) {
294
+ if ($document->is_disabled_target($this->pllm->get_language($k)))
295
+ unset($untranslated[$k]);
296
+ }
297
+
298
+ if ('current' == $document->status && !empty($untranslated))
299
+ $actions['lingotek-request'] = $this->get_action_link(array('document_id' => $document->document_id, 'action' => 'request'));
300
+
301
+ // offers to update translations status
302
+ if ('importing' == $document->status || $document->has_translation_status('pending'))
303
+ $actions['lingotek-status'] = $this->get_action_link(array('document_id' => $document->document_id, 'action' => 'status'));
304
+ }
305
+
306
+ elseif (empty($document->source)) {
307
+ $actions['lingotek-upload'] = $this->get_action_link(array($this->type => $id, 'action' => 'upload'), true);
308
+ }
309
+
310
+ // offers to disassociate translations
311
+ if (!empty($document->translations))
312
+ $actions['lingotek-delete'] = $this->get_action_link(array('document_id' => $document->document_id, 'action' => 'delete'));
313
+
314
+ return $actions;
315
+ }
316
+
317
+ /*
318
+ * adds actions to bulk dropdown list table using a javascript hack
319
+ * as the existing filter does not allow to *add* actions
320
+ * also displays the progress dialog placeholder
321
+ *
322
+ * @since 0.2
323
+ */
324
+ protected function _add_bulk_actions() {
325
+ $js = '';
326
+
327
+ foreach (self::$actions as $action => $strings) {
328
+ foreach (array('', '2') as $i)
329
+ $js .= sprintf('jQuery("<option>").val("bulk-lingotek-%s").text("%s").appendTo("select[name=\'action%s\']");', $action, $strings['action'], $i);
330
+
331
+ if (!empty($_GET['bulk-lingotek-' . $action]))
332
+ $text = $strings['progress'];
333
+ }
334
+
335
+ echo '<script type="text/javascript">jQuery(document).ready(function() {' . $js . '});</script>';
336
+
337
+ if (!empty($text))
338
+ printf('<div id="lingotek-progressdialog" title="%s"><div id="lingotek-progressbar"></div></div>', $text);
339
+ }
340
+
341
+ /*
342
+ * outputs javascript data for progress.js
343
+ *
344
+ * @since 0.1
345
+ */
346
+ public function admin_enqueue_scripts() {
347
+ foreach (array_keys(self::$actions) as $action) {
348
+ if (!empty($_GET['bulk-lingotek-' . $action])) {
349
+ wp_localize_script('lingotek_progress', 'lingotek_data', array(
350
+ 'action' => empty($_GET['page']) ? (empty($_GET['taxonomy']) ? 'post_' . $action : 'term_' . $action) : 'string_' . $action,
351
+ 'taxonomy' => empty($_GET['taxonomy']) || !taxonomy_exists($_GET['taxonomy']) ? '' : $_GET['taxonomy'],
352
+ 'sendback' => remove_query_arg( array('bulk-lingotek-' . $action, 'ids', 'lingotek_warning'), wp_get_referer() ),
353
+ 'ids' => array_map('intval', explode(',', $_GET['ids'])),
354
+ 'warning' => empty($_GET['lingotek_warning']) ? '' : __('You are about to overwrite existing translations. Are you sure?', 'wp-lingotek'),
355
+ 'nonce' => wp_create_nonce('lingotek_progress')
356
+ ));
357
+ return;
358
+ }
359
+ }
360
+ }
361
+
362
+ /*
363
+ * manages actions driven by dcoument_id
364
+ *
365
+ * @since 0.2
366
+ *
367
+ * @param string $action action name
368
+ * @return bool true if the action was managed, false otherwise
369
+ */
370
+ protected function _manage_actions($action) {
371
+ if (!empty($_GET['document_id']))
372
+ $document = $this->lgtm->get_group_by_id($_GET['document_id']);
373
+
374
+ switch($action) {
375
+ case 'lingotek-status':
376
+ check_admin_referer('lingotek-status');
377
+ $document->source_status();
378
+ $document->translations_status();
379
+ break;
380
+
381
+ case 'lingotek-request':
382
+ check_admin_referer('lingotek-request');
383
+ isset($_GET['locale']) ? $document->request_translation($_GET['locale']) : $document->request_translations();
384
+ break;
385
+
386
+ case 'lingotek-download':
387
+ check_admin_referer('lingotek-download');
388
+ isset($_GET['locale']) ? $document->create_translation($_GET['locale']) : $document->create_translations();
389
+ break;
390
+
391
+ case 'lingotek-delete':
392
+ check_admin_referer('lingotek-delete');
393
+ $document->disassociate();
394
+ break;
395
+
396
+ default:
397
+ return false;
398
+ }
399
+
400
+ return true;
401
+ }
402
+
403
+ /*
404
+ * ajax response to download translations and showing progress
405
+ *
406
+ * @since 0.1
407
+ */
408
+ public function ajax_download() {
409
+ check_ajax_referer('lingotek_progress', '_lingotek_nonce');
410
+
411
+ if ($document = $this->lgtm->get_group($this->type, $_POST['id'])) {
412
+ foreach ($document->translations as $locale => $status) {
413
+ if ('pending' == $status || 'ready' == $status)
414
+ $document->create_translation($locale);
415
+ }
416
+ }
417
+ die();
418
+ }
419
+
420
+ /*
421
+ * ajax response to request translations and showing progress
422
+ *
423
+ * @since 0.2
424
+ */
425
+ public function ajax_request() {
426
+ check_ajax_referer('lingotek_progress', '_lingotek_nonce');
427
+ if ($document = $this->lgtm->get_group($this->type, $_POST['id']))
428
+ $document->request_translations();
429
+ die();
430
+ }
431
+
432
+ /*
433
+ * ajax response to check translation status and showing progress
434
+ *
435
+ * @since 0.1
436
+ */
437
+ public function ajax_status() {
438
+ check_ajax_referer('lingotek_progress', '_lingotek_nonce');
439
+ if ($document = $this->lgtm->get_group($this->type, $_POST['id'])) {
440
+ $document->source_status();
441
+ $document->translations_status();
442
+ }
443
+ die();
444
+ }
445
+
446
+ /*
447
+ * ajax response disassociate translations and showing progress
448
+ *
449
+ * @since 0.2
450
+ */
451
+ public function ajax_delete() {
452
+ check_ajax_referer('lingotek_progress', '_lingotek_nonce');
453
+ if ($document = $this->lgtm->get_group($this->type, $_POST['id']))
454
+ $document->disassociate();
455
+ die();
456
+ }
457
+ }
admin/admin.php ADDED
@@ -0,0 +1,500 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Lingotek_Admin {
4
+ private $ajax_dashboard_language_endpoint = "lingotek_language";
5
+ /*
6
+ * setups filters and action needed on all admin pages and on plugins page
7
+ *
8
+ * @since 0.1
9
+ */
10
+ public function __construct() {
11
+
12
+ $plugin = Lingotek::get_instance();
13
+ $this->plugin_slug = $plugin->get_plugin_slug();
14
+ $this->dashboard = new Lingotek_Dashboard($plugin);
15
+
16
+ $this->pllm = $GLOBALS['polylang']->model;
17
+
18
+ add_action('admin_enqueue_scripts', array(&$this, 'admin_enqueue_scripts'));
19
+
20
+ // adds a 'settings' link in the plugins table
21
+ add_filter('plugin_action_links_' . LINGOTEK_BASENAME, array(&$this, 'plugin_action_links'));
22
+
23
+ // adds the link to the languages panel in the wordpress admin menu
24
+ add_action('admin_menu', array(&$this, 'add_menus'));
25
+
26
+ add_action('load-translation_page_wp-lingotek_manage', array(&$this, 'load_manage_page'));
27
+ add_filter('set-screen-option', array($this, 'set_screen_option'), 10, 3);
28
+
29
+ add_action('wp_ajax_'.$this->ajax_dashboard_language_endpoint, array(&$this->dashboard, 'ajax_language_dashboard'));
30
+
31
+ //Network admin menu
32
+ add_action('network_admin_menu', array($this, 'add_network_admin_menu'));
33
+ }
34
+
35
+ public function get_dashboard_endpoint(){
36
+ return site_url("wp-admin/admin-ajax.php?action=".$this->ajax_dashboard_language_endpoint);
37
+ }
38
+
39
+ /*
40
+ * setup js scripts & css styles (only on the relevant pages)
41
+ *
42
+ * @since 0.1
43
+ */
44
+ public function admin_enqueue_scripts() {
45
+ $screen = get_current_screen();
46
+
47
+ // FIXME no minified file for now
48
+ $suffix = '';
49
+ // $suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
50
+
51
+ // for each script:
52
+ // 0 => the pages on which to load the script
53
+ // 1 => the scripts it needs to work
54
+ // 2 => 1 if loaded in footer
55
+ // FIXME: check if I can load more scripts in footer
56
+ $scripts = array(
57
+ 'progress' => array(array('edit', 'upload', 'edit-tags', 'translation_page_wp-lingotek_manage', 'translation_page_wp-lingotek_settings'), array('jquery-ui-progressbar', 'jquery-ui-dialog', 'wp-ajax-response'), 1),
58
+ );
59
+
60
+ $styles = array(
61
+ 'admin' => array(array('edit', 'upload', 'edit-tags', 'translation_page_wp-lingotek_manage', 'translation_page_wp-lingotek_settings'), array('wp-jquery-ui-dialog')),
62
+ );
63
+
64
+ foreach ($scripts as $script => $v)
65
+ if (in_array($screen->base, $v[0]))
66
+ wp_enqueue_script('lingotek_'.$script, LINGOTEK_URL .'/js/'.$script.$suffix.'.js', $v[1], LINGOTEK_VERSION, $v[2]);
67
+
68
+ foreach ($styles as $style => $v)
69
+ if (in_array($screen->base, $v[0]))
70
+ wp_enqueue_style('lingotek_'.$style, LINGOTEK_URL .'/css/'.$style.$suffix.'.css', $v[1], LINGOTEK_VERSION);
71
+ }
72
+
73
+ /*
74
+ * adds a 'settings' link in the plugins table
75
+ *
76
+ * @since 0.1
77
+ *
78
+ * @param array $links list of links associated to the plugin
79
+ * @return array modified list of links
80
+ */
81
+ public function plugin_action_links($links) {
82
+ array_unshift($links, '<a href="admin.php?page=wp-lingotek">' . __('Settings', 'wp-lingotek') . '</a>');
83
+ return $links;
84
+ }
85
+
86
+ /*
87
+ * adds the links to the Lingotek panels in the wordpress admin menu
88
+ *
89
+ * @since 0.0.1
90
+ */
91
+ public function add_menus() {
92
+ add_menu_page(
93
+ $title = __('Translation', 'wp-lingotek'),
94
+ $title,
95
+ 'manage_options',
96
+ $this->plugin_slug,
97
+ array($this, 'display_dashboard_page'), 'dashicons-translation'
98
+ );
99
+
100
+ add_submenu_page($this->plugin_slug, __('Translation Dashboard', 'wp-lingotek'), __('Dashboard', 'wp-lingotek'), 'manage_options', $this->plugin_slug, array($this, 'display_dashboard_page'));
101
+ add_submenu_page($this->plugin_slug, __('Translation Management', 'wp-lingotek'), __('Manage', 'wp-lingotek'), 'manage_options', $this->plugin_slug . '_manage', array($this, 'display_manage_page'));
102
+ add_submenu_page($this->plugin_slug, __('Translation Settings', 'wp-lingotek'), __('Settings', 'wp-lingotek'), 'manage_options', $this->plugin_slug . '_settings', array($this, 'display_settings_page'));
103
+ add_submenu_page($this->plugin_slug, __('Lingotek Tutorial', 'wp-lingotek'), __('Tutorial', 'wp-lingotek'), 'manage_options', $this->plugin_slug . '_tutorial', array($this, 'display_tutorial_page'));
104
+ }
105
+
106
+ /*
107
+ * displays the settings page
108
+ *
109
+ * @since 0.0.1
110
+ */
111
+ public function display_settings_page() {
112
+
113
+ // disconnect Lingotek account
114
+ if (array_key_exists('delete_access_token', $_GET) && $_GET['delete_access_token']) {
115
+ delete_option('lingotek_token');
116
+ delete_option('lingotek_community');
117
+ delete_option('lingotek_defaults');
118
+ }
119
+
120
+ // connect Lingotek account
121
+ if (array_key_exists('access_token', $_GET)) {
122
+ // set and get token details
123
+ $client = new Lingotek_API();
124
+ $token_details = $client->get_token_details($_GET['access_token']);
125
+ if ($token_details && strlen($token_details->login_id)) {
126
+ update_option('lingotek_token', array('access_token' => $_GET['access_token'], 'login_id' => $token_details->login_id));
127
+ add_settings_error( 'lingotek_token', 'account-connection', __('Your Lingotek account has been successfully connected.', 'wp-lingotek'), 'updated' );
128
+ }
129
+ else {
130
+ add_settings_error('lingotek_token', 'account-connection', __('Your Lingotek account was not connected. The Access Token received was invalid.', 'wp-lingotek'), 'error');
131
+ }
132
+ }
133
+
134
+ // set page key primarily used for form submissions
135
+ $page_key = $this->plugin_slug.'_settings';
136
+ if(isset($_GET['sm'])){
137
+ $page_key .= '&sm='.$_GET['sm'];
138
+ }
139
+
140
+ // set community
141
+ if (!empty($_POST) && key_exists('lingotek_community', $_POST) && strlen($_POST['lingotek_community'])) {
142
+ check_admin_referer($page_key, '_wpnonce_' . $page_key);
143
+ update_option('lingotek_community', $_POST['lingotek_community']);
144
+ add_settings_error('lingotek_community', 'update', __('Your community has been successfully saved.', 'wp-lingotek'), 'updated');
145
+ $this->set_community_resources($_POST['lingotek_community']);
146
+ }
147
+ $community_id = get_option('lingotek_community');
148
+ if (!$community_id) {
149
+ add_settings_error('lingotek_community', 'error', __('Select and save the community that you would like to use.', 'wp-lingotek'), 'error');
150
+ }
151
+
152
+ $token_details = self::has_token_details();
153
+ $redirect_url = admin_url('admin.php?page=' . $this->plugin_slug . '_settings&sm=account');
154
+
155
+ if($token_details){
156
+ $access_token = $token_details['access_token'];
157
+ $login_id = $token_details['login_id'];
158
+ $base_url = get_option('lingotek_base_url');
159
+ include(LINGOTEK_ADMIN_INC . '/settings.php');
160
+ }
161
+ else {
162
+ $connect_url = "";
163
+ // connect cloak redirect
164
+ if(isset($_GET['connect'])){
165
+ // set sandbox or production (after button clicked)
166
+ if(strcasecmp($_GET['connect'],'sandbox')==0) {
167
+ update_option('lingotek_base_url', Lingotek_API::SANDBOX_URL);
168
+ }
169
+ else {
170
+ update_option('lingotek_base_url', Lingotek_API::PRODUCTION_URL);
171
+ }
172
+ $client = new Lingotek_API();
173
+ echo '<div class="wrap"><p class="description">'.__("Redirecting to Lingotek to connect your account...",'wp-lingotek').'</p></div>';
174
+
175
+ $connect_url = (strcasecmp($_GET['connect'],'new')==0) ? $client->get_new_url($redirect_url) : $client->get_connect_url($redirect_url);
176
+ }
177
+ $connect_account_cloak_url_new = admin_url('admin.php?page=' . $this->plugin_slug . '_settings&connect=new');
178
+ $connect_account_cloak_url_test = admin_url('admin.php?page=' . $this->plugin_slug . '_settings&connect=sandbox');
179
+ $connect_account_cloak_url_prod = admin_url('admin.php?page=' . $this->plugin_slug . '_settings&connect=production');
180
+ include(LINGOTEK_ADMIN_INC . '/settings/connect-account.php');
181
+ }
182
+ }
183
+
184
+ /*
185
+ * get possible settings for defaults or translation profiles
186
+ *
187
+ * @since 0.2
188
+ *
189
+ * @return array
190
+ */
191
+ public function get_profiles_settings($defaults = false) {
192
+ $resources = get_option('lingotek_community_resources');
193
+ $options = array(
194
+ 'manual' => __('Manual', 'wp-lingotek'),
195
+ 'automatic' => __('Automatic', 'wp-lingotek')
196
+ );
197
+
198
+ return array(
199
+ 'upload' => array(
200
+ 'label' => __('Upload content', 'wp-lingotek'),
201
+ 'options' => $options,
202
+ 'description' => __('How should new and modified content be uploaded to Lingotek?', 'wp-lingotek')
203
+ ),
204
+ 'download' => array(
205
+ 'label' => __('Download translations', 'wp-lingotek'),
206
+ 'options' => $options,
207
+ 'description' => __('How should completed translations be downloaded to WordPress?', 'wp-lingotek')
208
+ ),
209
+ 'project_id' => array(
210
+ 'label' => $defaults ? __('Default Project', 'wp-lingotek') : __('Project', 'wp-lingotek'),
211
+ 'options' => $resources['projects'],
212
+ 'description' => __('Changes will affect new entities only', 'wp-lingotek')
213
+ ),
214
+ 'workflow_id' => array(
215
+ 'label' => $defaults ? __('Default Workflow', 'wp-lingotek') : __('Workflow', 'wp-lingotek'),
216
+ 'options' => $resources['workflows'],
217
+ ),
218
+ 'primary_filter_id' => array(
219
+ 'label' => $defaults ? __('Primary Filter', 'wp-lingotek') : __('Primary Filter', 'wp-lingotek'),
220
+ 'options' => $resources['filters'],
221
+ ),
222
+ 'secondary_filter_id' => array(
223
+ 'label' => $defaults ? __('Secondary Filter', 'wp-lingotek') : __('Secondary Filter', 'wp-lingotek'),
224
+ 'options' => $resources['filters'],
225
+ ),
226
+ );
227
+ }
228
+
229
+ /*
230
+ * get usage for all profiles
231
+ *
232
+ * @since 0.2
233
+ *
234
+ * @param arry $profiles
235
+ * @return array profiles with added usage column
236
+ */
237
+ public function get_profiles_usage($profiles) {
238
+ $content_types = get_option('lingotek_content_type');
239
+
240
+ // initialize usage column
241
+ foreach ($profiles as $key => $profile)
242
+ $profiles[$key]['usage'] = 0;
243
+
244
+ // fill usage column
245
+ foreach (array_merge($this->pllm->get_translated_post_types(), $this->pllm->get_translated_taxonomies(), array('string')) as $type) {
246
+ if (isset($content_types[$type]['profile']))
247
+ $profiles[$content_types[$type]['profile']]['usage'] += 1;
248
+ elseif ('post' == $type)
249
+ $profiles['automatic']['usage'] += 1;
250
+ else
251
+ $profiles['manual']['usage'] += 1;
252
+
253
+ if (isset($content_types[$type]['sources'])) {
254
+ foreach ($content_types[$type]['sources'] as $profile)
255
+ $profiles[$profile]['usage'] +=1;
256
+ }
257
+ }
258
+ return $profiles;
259
+ }
260
+
261
+ /*
262
+ * store community options and set any missing defaults to the first available option
263
+ *
264
+ * @since 0.1.0
265
+ */
266
+ public function set_community_resources($community_id, $update_first_project_callback = FALSE) {
267
+ $client = new Lingotek_API();
268
+ $refresh_success = array(
269
+ 'projects' => FALSE,
270
+ 'workflows' => FALSE,
271
+ );
272
+
273
+ $api_data = $client->get_projects($community_id);
274
+ $projects = array();
275
+ if ($api_data && $api_data != 'empty') {
276
+ foreach ($api_data->entities as $project) {
277
+ $projects[$project->properties->id] = $project->properties->title;
278
+ }
279
+ if($update_first_project_callback){
280
+ $client = new Lingotek_API();
281
+ $project_id = current(array_keys($projects));//update callback for 1st project
282
+ $client->update_callback_url($project_id);
283
+ }
284
+ natcasesort($projects); //order by title (case-insensitive)
285
+ $refresh_success['projects'] = TRUE;
286
+ }
287
+ else if ($api_data == 'empty') {
288
+ add_settings_error('lingotek_community_resources', 'error', __('Your Community currently has no projects.', 'wp-lingotek'), 'error');
289
+ }
290
+ else {
291
+ add_settings_error('lingotek_community_resources', 'error', __('Projects could not be refreshed', 'wp-lingotek'), 'error');
292
+ }
293
+
294
+ $api_data = $client->get_workflows($community_id);
295
+ $workflows = array();
296
+ if ($api_data) {
297
+ foreach ($api_data->entities as $workflow) {
298
+ $workflows[$workflow->properties->id] = $workflow->properties->title;
299
+ }
300
+ natcasesort($workflows); //order by title (case-insensitive)
301
+ $refresh_success['workflows'] = TRUE;
302
+ }
303
+ else {
304
+ add_settings_error('lingotek_community_resources', 'error', __('Workflows could not be refreshed', 'wp-lingotek'), 'error');
305
+ }
306
+
307
+ $api_data = $client->get_filters();
308
+ $filters = array();
309
+ if ($api_data->properties->total > 0) {
310
+ foreach ($api_data->entities as $filter) {
311
+ if (!$filter->properties->is_public) {
312
+ $filters[$filter->properties->id] = $filter->properties->title;
313
+ }
314
+ if ($filter->properties->title == 'okf_json@with-html-subfilter.fprm' || $filter->properties->title == 'okf_html@wordpress.fprm') {
315
+ $filters[$filter->properties->id] = $filter->properties->title;
316
+ }
317
+ }
318
+ $primary_filter_id = array_search('okf_json@with-html-subfilter.fprm', $filters);
319
+ $secondary_filter_id = array_search('okf_html@wordpress.fprm', $filters);
320
+ $defaults = get_option('lingotek_defaults');
321
+ if($defaults == NULL) {
322
+ $defaults['primary_filter_id'] = $primary_filter_id;
323
+ $defaults['secondary_filter_id'] = $secondary_filter_id;
324
+ update_option('lingotek_defaults', $defaults);
325
+ }
326
+ }
327
+
328
+ $resources = array(
329
+ 'projects' => $projects,
330
+ 'workflows' => $workflows,
331
+ 'filters' => $filters,
332
+ );
333
+
334
+ if ($refresh_success['projects'] == TRUE || $refresh_success['workflows'] == TRUE) {
335
+ update_option('lingotek_community_resources', $resources);
336
+ $this->ensure_valid_defaults();
337
+ }
338
+ return $refresh_success;
339
+ }
340
+
341
+ public function ensure_valid_defaults() {
342
+ $resources = get_option('lingotek_community_resources');
343
+ $defaults = get_option('lingotek_defaults');
344
+ $valid_default = array();
345
+ foreach ($resources as $resource_key => $options) {
346
+ $key = substr($resource_key,0,strlen($resource_key)-1)."_id";
347
+ $valid_default[$key] = 0;
348
+ if(!is_array($defaults)) {
349
+ continue;
350
+ }
351
+ foreach ($options as $option_key => $option_val) {
352
+ if(!array_key_exists($key, $defaults)){
353
+ continue;
354
+ }
355
+ if ($option_key === $defaults[$key]) {
356
+ $valid_default[$key] = 1;
357
+ break;
358
+ }
359
+ }
360
+ }
361
+ foreach ($valid_default as $key => $valid) {
362
+ $resource_key = substr($key,0,strpos($key,'_'))."s";
363
+ if ($valid) {
364
+ continue;
365
+ }
366
+ else {
367
+ $defaults[$key] = current(array_keys($resources[$resource_key]));
368
+ }
369
+ }
370
+ $num_valid_defaults = array_sum($valid_default);
371
+
372
+ if($num_valid_defaults < count($valid_default)){
373
+ add_settings_error( 'lingotek_defaults', 'community-selected', sprintf(__('Your <a href="%s"><i>Defaults</i></a> have been updated to valid options for this community.', 'wp-lingotek'), admin_url('admin.php?page=wp-lingotek_settings&sm=defaults')), 'updated' );
374
+ }
375
+ unset($defaults['filter_id']);
376
+ update_option('lingotek_defaults', $defaults);
377
+ }
378
+
379
+ /*
380
+ * displays the admin manage page
381
+ *
382
+ * @since 0.1.0
383
+ */
384
+ public function display_manage_page() {
385
+ if (self::has_token_details()) {
386
+ include(LINGOTEK_ADMIN_INC . '/view-manage.php');
387
+ }
388
+ else {
389
+ $this->display_settings_page();
390
+ }
391
+ }
392
+
393
+ /*
394
+ * add screen option on translations->manage page
395
+ *
396
+ * @since 0.2
397
+ */
398
+ public function load_manage_page() {
399
+ add_screen_option('per_page', array(
400
+ 'label' => __('Strings groups', 'wp-lingotek'),
401
+ 'default' => 10,
402
+ 'option' => 'lingotek_strings_per_page'
403
+ ));
404
+ }
405
+
406
+ /*
407
+ * save screen option from translations->manage page
408
+ *
409
+ * @since 0.2
410
+ */
411
+ public function set_screen_option($status, $option, $value) {
412
+ if ('lingotek_strings_per_page' == $option)
413
+ return $value;
414
+ return $status;
415
+ }
416
+
417
+ /*
418
+ * displays the admin manage page
419
+ *
420
+ * @since 0.1.0
421
+ */
422
+ public function display_dashboard_page() {
423
+ $token_details = self::has_token_details();
424
+ if ($token_details) {
425
+ $community_id = get_option('lingotek_community');
426
+ $defaults = get_option('lingotek_defaults');
427
+ $user = wp_get_current_user();
428
+
429
+ // The data that will be passed to the Lingotek GMC dashboard
430
+ $cms_data = array(
431
+ // lingotek
432
+ "community_id"=> $community_id,
433
+ "external_id"=> $token_details['login_id'],
434
+ //"vault_id"=> $defaults['vault_id'],
435
+ "workflow_id"=> $defaults['workflow_id'],
436
+ "project_id"=> $defaults['project_id'],
437
+ "first_name"=> $user->display_name,
438
+ "last_name"=> '',
439
+ "email"=> get_bloginfo('admin_email'),
440
+ // cms
441
+ "cms_site_id"=> site_url(),
442
+ "cms_site_key"=> site_url(),
443
+ "cms_site_name"=> get_bloginfo('name'),
444
+ "cms_type"=> "Wordpress",
445
+ "cms_version"=> get_bloginfo('version'),
446
+ "cms_tag"=> LINGOTEK_PLUGIN_SLUG,
447
+ "locale"=> pll_current_language('lingotek_locale'),
448
+ "module_version"=> LINGOTEK_VERSION,
449
+ "endpoint_url" => $this->get_dashboard_endpoint()
450
+ );
451
+ include(LINGOTEK_ADMIN_INC . '/view-dashboard.php');
452
+ }
453
+ else {
454
+ $this->display_settings_page();
455
+ }
456
+ }
457
+
458
+ /*
459
+ * returns the access token when present, otherwise returns FALSE
460
+ *
461
+ * @since 0.1.0
462
+ */
463
+ public static function has_token_details() {
464
+ $token_details = get_option('lingotek_token');
465
+ $has_token = FALSE;
466
+ if ($token_details !== FALSE && key_exists('access_token', $token_details) && key_exists('login_id', $token_details) && strlen($token_details['access_token']) && strlen($token_details['login_id'])) {
467
+ $has_token = TRUE;
468
+ return $token_details;
469
+ }
470
+ return $has_token;
471
+ }
472
+
473
+ public function display_network_settings_page() {
474
+ if (is_multisite() && self::has_token_details()) {
475
+ include(LINGOTEK_ADMIN_INC . '/view-network.php');
476
+ }
477
+ else {
478
+ $this->display_settings_page();
479
+ }
480
+ }
481
+
482
+ /*
483
+ * adds a Lingotek settings option in the network admin page
484
+ *
485
+ * @since 0.1.0
486
+ */
487
+ public function add_network_admin_menu() {
488
+ add_submenu_page('settings.php', __('Lingotek Settings', 'wp-lingotek'), __('Lingotek Settings', 'wp-lingotek'), 'manage_network_options', $this->plugin_slug . '_network', array($this, 'display_network_settings_page'), 'dashicons-translation');
489
+ }
490
+
491
+ public function display_tutorial_page() {
492
+ if (self::has_token_details()) {
493
+ include(LINGOTEK_ADMIN_INC . '/view-tutorial.php');
494
+ }
495
+ else {
496
+ $this->display_settings_page();
497
+ }
498
+ }
499
+
500
+ }
admin/content-table.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if(!class_exists('WP_List_Table')){
4
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' ); // since WP 3.1
5
+ }
6
+
7
+ class Lingotek_Content_Table extends WP_List_Table {
8
+ protected $profiles, $content_types;
9
+
10
+ /*
11
+ * constructor
12
+ *
13
+ * @since 0.2
14
+ */
15
+ function __construct($content_types) {
16
+ parent::__construct(array(
17
+ 'plural' => 'lingotek-content', // do not translate (used for css class)
18
+ 'ajax' => false
19
+ ));
20
+
21
+ $this->profiles = Lingotek::get_profiles();
22
+ $this->content_types = $content_types;
23
+ }
24
+
25
+ /*
26
+ * displays the item information in a column (default case)
27
+ *
28
+ * @since 0.2
29
+ *
30
+ * @param array $item
31
+ * @param string $column_name
32
+ * @return string
33
+ */
34
+ function column_name($item) {
35
+ global $polylang;
36
+
37
+ printf('<span class="content-type-name">%s</span>', esc_html($item['name']));
38
+
39
+ // the source language for strings is always the default language
40
+ if ('string' != $item['type']) {
41
+ printf('<a id="id[%s]" class="dashicons dashicons-arrow-right" onclick="%s" href="#"></a>',
42
+ esc_attr($item['type']),
43
+ "
44
+ d1 = document.getElementById('sources-name[{$item['type']}]');
45
+ d2 = document.getElementById('sources-profile[{$item['type']}]');
46
+ c = 'dashicons dashicons-arrow-';
47
+ if (c+'right' == this.className) {
48
+ this.className = c+'down';
49
+ d1.style.display = d2.style.display = '';
50
+ }
51
+ else {
52
+ this.className = c+'right';
53
+ d1.style.display = d2.style.display = 'none';
54
+ }
55
+ return false;
56
+ "
57
+ );
58
+
59
+ printf('<ul class="sources-name" id="sources-name[%s]" style="display:none;">', $item['type']);
60
+ foreach ($polylang->model->get_languages_list() as $language) {
61
+ printf('<li>%s</li>', sprintf(__('%s source', 'wp-lingotek'), esc_html($language->name)));
62
+ }
63
+ echo '</ul>';
64
+ }
65
+ }
66
+
67
+ /*
68
+ * displays the item profile dropdown list
69
+ *
70
+ * @since 0.2
71
+ *
72
+ * @param array $item
73
+ * @return string
74
+ */
75
+ function column_profile($item) {
76
+ global $polylang;
77
+
78
+ printf('<select class="content-type-profile" name="%1$s" id="%1$s">', $item['type'] . '[profile]');
79
+ foreach ($this->profiles as $key => $profile) {
80
+ $selected = (isset($item['profile']) && $key == $item['profile']) ? 'selected="selected"' : '';
81
+ echo "\n\t<option value='" . esc_attr($key) . "' $selected>" . esc_html($profile['name']) . '</option>';
82
+ }
83
+ echo '</select>';
84
+
85
+ $options = array_merge(array('default' => array('name' => __('Use content type default', 'wp-lingotek'))), $this->profiles);
86
+
87
+ // the source language for strings is always the default language
88
+ if ('string' != $item['type']) {
89
+ printf('<ul class="sources-profile" id="sources-profile[%s]" style="display:none;">', $item['type']);
90
+ foreach ($polylang->model->get_languages_list() as $language) {
91
+ printf('<li><select name="%1$s" id="%1$s">', $item['type'] . '[sources][' . $language->slug . ']' );
92
+ foreach ($options as $key => $profile) {
93
+ $selected = (isset($item['sources'][$language->slug]) && $key == $item['sources'][$language->slug]) ? 'selected="selected"' : '';
94
+ echo "\n\t<option value='" . esc_attr($key) . "' $selected>" . esc_html($profile['name']) . '</option>';
95
+ }
96
+ echo '</select></li>';
97
+ }
98
+ echo '</ul>';
99
+ }
100
+ }
101
+
102
+ /*
103
+ * displays checkboxes in fields columns
104
+ * can handle fields with one or two keys
105
+ *
106
+ * @since 0.2
107
+ *
108
+ * @param array $labels
109
+ * @param array $values
110
+ * @param string $parent
111
+ */
112
+ protected function display_fields($labels, $values, $name) {
113
+ foreach ($labels as $key => $str) {
114
+ if (is_array($str)) {
115
+ $this->display_fields($str, isset($values[$key]) ? $values[$key] : array(), $name . "[$key]");
116
+ }
117
+ else {
118
+ printf(
119
+ '<li><label><input name="%s" type="checkbox" value="1" %s /> %s</label></li>',
120
+ esc_attr($name . "[$key]"),
121
+ empty($values[$key]) ? 'checked="checked"' : '',
122
+ esc_html($str)
123
+ );
124
+ }
125
+ }
126
+ }
127
+
128
+ /*
129
+ * displays the item fields checkboxes
130
+ *
131
+ * @since 0.2
132
+ *
133
+ * @param array $item
134
+ * @return string
135
+ */
136
+ function column_fields($item) {
137
+ if (!empty($item['fields'])) {
138
+ echo '<ul class="content-type-fields">';
139
+ $this->display_fields($item['fields']['label'], isset($item['fields']['value']) ? $item['fields']['value'] : array() , $item['type'] . '[fields]');
140
+ echo '</ul>';
141
+ }
142
+ }
143
+
144
+ /*
145
+ * gets the list of columns
146
+ *
147
+ * @since 0.2
148
+ *
149
+ * @return array the list of column titles
150
+ */
151
+ function get_columns() {
152
+ return array(
153
+ 'name' => __('Content Type', 'wp-lingotek'),
154
+ 'profile' => __('Profile', 'wp-lingotek'),
155
+ 'fields' => __('Fields', 'wp-lingotek'),
156
+ );
157
+ }
158
+
159
+ /*
160
+ * gets the list of sortable columns
161
+ *
162
+ * @since 0.2
163
+ *
164
+ * @return array
165
+ */
166
+ function get_sortable_columns() {
167
+ return array(
168
+ 'name' => array('name', false),
169
+ );
170
+ }
171
+
172
+ /*
173
+ * prepares the list of items ofr displaying
174
+ *
175
+ * @since 0.2
176
+ *
177
+ * @param array $data
178
+ */
179
+ function prepare_items($data = array()) {
180
+ $per_page = $this->get_items_per_page('lingotek_content_per_page');
181
+ $this->_column_headers = array($this->get_columns(), array(), $this->get_sortable_columns());
182
+
183
+ function usort_reorder($a, $b){
184
+ $result = strcmp($a[$_REQUEST['orderby']], $b[$_REQUEST['orderby']]); // determine sort order
185
+ return (empty($_REQUEST['order']) || $_REQUEST['order'] == 'asc') ? $result : -$result; // send final sort direction to usort
186
+ };
187
+
188
+ if (!empty($_REQUEST['orderby'])) // no sort by default
189
+ usort($data, 'usort_reorder');
190
+
191
+ $total_items = count($data);
192
+ $this->items = array_slice($data, ($this->get_pagenum() - 1) * $per_page, $per_page);
193
+
194
+ $this->set_pagination_args(array(
195
+ 'total_items' => $total_items,
196
+ 'per_page' => $per_page,
197
+ 'total_pages' => ceil($total_items/$per_page)
198
+ ));
199
+ }
200
+ }
admin/filters-columns.php ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Modifies Polylang filters
5
+ *
6
+ * @since 0.1
7
+ */
8
+ class Lingotek_Filters_Columns extends PLL_Admin_Filters_Columns {
9
+ public $lgtm; // Lingotek model
10
+
11
+ /*
12
+ * cosntructor
13
+ *
14
+ * @since 0.1
15
+ *
16
+ * @param object $polylang Polylang object
17
+ */
18
+ public function __construct(&$polylang) {
19
+ parent::__construct($polylang);
20
+
21
+ $this->lgtm = &$GLOBALS['wp_lingotek']->model;
22
+
23
+ // FIXME remove quick edit and bulk edit for now waiting for a solution to remove it only for uploaded documents
24
+ remove_filter('quick_edit_custom_box', array(&$this, 'quick_edit_custom_box'), 10, 2);
25
+ remove_filter('bulk_edit_custom_box', array(&$this, 'quick_edit_custom_box'), 10, 2);
26
+ }
27
+
28
+ /*
29
+ * adds languages and translations columns in posts, pages, media, categories and tags tables
30
+ * overrides Polylang method to display all languages including the filtered one
31
+ * as well as displaying a tooltip with the language name and locale when there is no flag
32
+ *
33
+ * @since 0.2
34
+ *
35
+ * @param array $columns list of table columns
36
+ * @param string $before the column before which we want to add our languages
37
+ * @return array modified list of columns
38
+ */
39
+ protected function add_column($columns, $before) {
40
+ if ($n = array_search($before, array_keys($columns))) {
41
+ $end = array_slice($columns, $n);
42
+ $columns = array_slice($columns, 0, $n);
43
+ }
44
+
45
+ foreach ($this->model->get_languages_list() as $language) {
46
+ $columns['language_'.$language->slug] = $language->flag ? $language->flag :
47
+ sprintf('<a href="" title="%s">%s</a>',
48
+ esc_html("$language->name ($language->locale)"),
49
+ esc_html($language->slug)
50
+ );
51
+ }
52
+
53
+ return isset($end) ? array_merge($columns, $end) : $columns;
54
+ }
55
+
56
+ /*
57
+ * fills the language and translations columns in the posts and taxonomies lists tables
58
+ * take care that when doing ajax inline edit, the post or term may not be updated in database yet
59
+ *
60
+ * @since 0.2
61
+ *
62
+ * @param string $type 'post' or 'term'
63
+ * @param string $column column name
64
+ * @param int $object_id id of the current object in row
65
+ */
66
+ protected function _column($type, $column, $object_id) {
67
+ $action = 'post' == $type ? 'inline-save' : 'inline-save-tax';
68
+ $inline = defined('DOING_AJAX') && $_REQUEST['action'] == $action && isset($_POST['inline_lang_choice']);
69
+ $lang = $inline ?
70
+ $this->model->get_language($_POST['inline_lang_choice']) :
71
+ call_user_func(array($this->model, 'get_' . $type . '_language'), $object_id);
72
+
73
+ if (false === strpos($column, 'language_') || !$lang)
74
+ return '';
75
+
76
+ $language = $this->model->get_language(substr($column, 9));
77
+
78
+ // FIXME should I suppress quick edit?
79
+ // yes for uploaded posts, but I will need js as the field is built for all posts
80
+ // /!\ also take care not add this field two times when translations are managed by Polylang
81
+
82
+ // hidden field containing the post language for quick edit (used to filter categories since Polylang 1.7)
83
+ if ($column == $this->get_first_language_column() /*&& !$this->model->get_translation_id('post', $post_id)*/)
84
+ printf('<div class="hidden" id="lang_%d">%s</div>', esc_attr($object_id), esc_html($lang->slug));
85
+
86
+ $id = ($inline && $lang->slug != $this->model->get_language($_POST['old_lang'])->slug) ?
87
+ ($language->slug == $lang->slug ? $object_id : 0) :
88
+ call_user_func(array($this->model, 'get_' . $type), $object_id, $language);
89
+
90
+ $document = $this->lgtm->get_group($type, $object_id);
91
+
92
+ // FIXME not very clean
93
+ $actions = 'post' == $type ? $GLOBALS['wp_lingotek']->post_actions : $GLOBALS['wp_lingotek']->term_actions;
94
+
95
+ $profile = Lingotek_Model::get_profile($this->content_type, $language);
96
+ $disabled = 'disabled' == $profile['profile'];
97
+
98
+ // post ready for upload
99
+ if ($this->lgtm->can_upload($type, $object_id) && $object_id == $id)
100
+ return $disabled ?
101
+ ('post' == $type ? parent::post_column($column, $object_id) : parent::term_column('', $column, $object_id)) :
102
+ $actions->upload_icon($object_id);
103
+
104
+ // translation disabled
105
+ elseif (isset($document->source) && $document->is_disabled_target($language))
106
+ return 'post' == $type ? parent::post_column($column, $object_id) : parent::term_column('', $column, $object_id);
107
+
108
+ // source post is uploaded
109
+ elseif (isset($document->source) && $document->source == $id) {
110
+ // source ready for upload
111
+ if ($this->lgtm->can_upload($type, $id))
112
+ return $actions->upload_icon($id);
113
+
114
+ // importing source
115
+ if ($id == $object_id && 'importing' == $document->status)
116
+ return Lingotek_Actions::importing_icon($document);
117
+
118
+ // uploaded
119
+ return 'post' == $type ? Lingotek_Post_actions::uploaded_icon($id) : Lingotek_Term_actions::uploaded_icon($id);
120
+ }
121
+
122
+ // translations
123
+ elseif (isset($document->translations[$language->locale]) || (isset($document->source) && 'current' == $document->status))
124
+ return Lingotek_Actions::translation_icon($document, $language);
125
+
126
+ // translations exist but are not managed by Lingotek TMS
127
+ elseif (empty($document->source))
128
+ return $object_id == $id && !$disabled ?
129
+ $actions->upload_icon($object_id, true) :
130
+ ('post' == $type ? parent::post_column($column, $object_id) : parent::term_column('', $column, $object_id));
131
+
132
+ // no translation
133
+ else
134
+ return '<div class="lingotek-color dashicons dashicons-no"></div>';
135
+ }
136
+
137
+ /*
138
+ * fills the language and translations columns in the posts, pages and media library tables
139
+ * take care that when doing ajax inline edit, the post may not be updated in database yet
140
+ *
141
+ * @since 0.1
142
+ *
143
+ * @param string $column column name
144
+ * @param int $post_id
145
+ */
146
+ public function post_column($column, $post_id) {
147
+ $this->content_type = get_post_type($post_id);
148
+ echo $this->_column('post', $column, $post_id);
149
+ }
150
+
151
+ /*
152
+ * fills the language and translations columns in the categories and post tags tables
153
+ * take care that when doing ajax inline edit, the term may not be updated in database yet
154
+ *
155
+ * @since 0.2
156
+ *
157
+ * @param string $empty not used
158
+ * @param string $column column name
159
+ * @param int term_id
160
+ */
161
+ public function term_column($empty, $column, $term_id) {
162
+ $this->content_type = $GLOBALS['taxonomy'];
163
+ return $this->_column('term', $column, $term_id);
164
+ }
165
+ }
admin/filters-media.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Manages automatic upload
5
+ *
6
+ * @since 0.2
7
+ */
8
+ class Lingotek_Filters_Media extends PLL_Admin_Filters_Media {
9
+ public $lgtm; // Lingotek model
10
+
11
+ /*
12
+ * Constructor
13
+ *
14
+ * @since 0.2
15
+ */
16
+ public function __construct(&$polylang) {
17
+ parent::__construct($polylang);
18
+
19
+ $this->lgtm = &$GLOBALS['wp_lingotek']->model;
20
+
21
+ add_action('edit_attachment', array(&$this, 'edit_attachment'));
22
+ add_action('add_attachment', array(&$this, 'add_attachment'), 11); // after Polylang
23
+ }
24
+
25
+ /*
26
+ * marks the attachment as edited if needed
27
+ *
28
+ * @since 0.2
29
+ *
30
+ * @param int $post_id
31
+ */
32
+ public function edit_attachment($post_id) {
33
+ $document = $this->lgtm->get_group('post', $post_id);
34
+
35
+ // FIXME how to check if the attachment is really modified?
36
+ if ($document && $post_id == $document->source) {
37
+ $document->source_edited();
38
+
39
+ if ($document->is_automatic_upload()) {
40
+ $this->lgtm->upload_post($post_id);
41
+ }
42
+ }
43
+ }
44
+
45
+ /*
46
+ * uploads an attachment when saved for the first time
47
+ *
48
+ * @since 0.2
49
+
50
+ * @param int $post_id
51
+ */
52
+ public function add_attachment($post_id) {
53
+ if ($this->model->is_translated_post_type('attachment') && 'automatic' == Lingotek_Model::get_profile_option('upload', 'attachment', $this->model->get_post_language($post_id)) && $this->lgtm->can_upload('post', $post_id))
54
+ $this->lgtm->upload_post($post_id);
55
+ }
56
+ }
admin/filters-post.php ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Modifies Polylang filters
5
+ * Manages automatic upload
6
+ * Manages delete / trash sync
7
+ *
8
+ * @since 0.1
9
+ */
10
+ class Lingotek_Filters_Post extends PLL_Admin_Filters_Post {
11
+ public $lgtm; // Lingotek model
12
+
13
+ /*
14
+ * Constructor
15
+ *
16
+ * @since 0.1
17
+ */
18
+ public function __construct(&$polylang) {
19
+ parent::__construct($polylang);
20
+
21
+ $this->lgtm = &$GLOBALS['wp_lingotek']->model;
22
+
23
+ // automatic upload
24
+ add_action('post_updated', array(&$this, 'post_updated'), 10, 3);
25
+
26
+ // trash sync
27
+ add_action('trashed_post', array(&$this, 'trash_post'));
28
+ add_action('untrashed_post', array(&$this, 'untrash_post'));
29
+ }
30
+
31
+ /*
32
+ * controls whether to display the language metabox or not
33
+ *
34
+ * @since 0.1
35
+ */
36
+ public function add_meta_boxes($post_type) {
37
+ global $post_ID;
38
+ if ($this->model->is_translated_post_type($post_type)) {
39
+ $document = $this->lgtm->get_group('post', $post_ID);
40
+ if (empty($document->source))
41
+ parent::add_meta_boxes($post_type);
42
+ }
43
+ }
44
+
45
+ /*
46
+ * uploads a post when saved for the first time
47
+ *
48
+ * @since 0.2
49
+
50
+ * @param int $post_id
51
+ * @param object $post
52
+ * @param bool $update whether it is an update or not
53
+ */
54
+ public function save_post($post_id, $post, $update) {
55
+ if (!$this->model->is_translated_post_type($post->post_type))
56
+ return;
57
+
58
+ parent::save_post($post_id, $post, $update);
59
+
60
+ if (!wp_is_post_revision($post_id) && 'auto-draft' != $post->post_status && Lingotek_Group_Post::is_valid_auto_upload_post_status($post->post_status) && 'automatic' == Lingotek_Model::get_profile_option('upload', $post->post_type, $this->model->get_post_language($post_id)) && !(isset($_POST['action']) && 'heartbeat' == $_POST['action']) && $this->lgtm->can_upload('post', $post_id)) {
61
+ $this->lgtm->upload_post($post_id);
62
+ }
63
+ }
64
+
65
+ /*
66
+ * checks if we can act when saving a post
67
+ *
68
+ * @since 0.1
69
+ *
70
+ * @param int $post_id
71
+ * @param object $post
72
+ * @param bool $update whether it is an update or not
73
+ * @return bool
74
+ */
75
+ protected function can_save_post_data($post_id, $post, $update) {
76
+ // does nothing except on post types which are filterable
77
+ // also don't act on revisions
78
+ if (!$this->model->is_translated_post_type($post->post_type) || wp_is_post_revision($post_id))
79
+ return false;
80
+
81
+ // capability check
82
+ // as 'wp_insert_post' can be called from outside WP admin
83
+ $post_type_object = get_post_type_object($post->post_type);
84
+ if (($update && !current_user_can($post_type_object->cap->edit_post, $post_id)) || (!$update && !current_user_can($post_type_object->cap->create_posts)))
85
+ return false;
86
+
87
+ return true;
88
+ }
89
+
90
+ /*
91
+ * marks the post as edited if needed
92
+ *
93
+ * @since 0.1
94
+ *
95
+ * @param int $post_id
96
+ * @param object $post_after
97
+ * @param object $post_before
98
+ */
99
+ public function post_updated($post_id, $post_after, $post_before) {
100
+ if ($this->can_save_post_data($post_id, $post_after, true)) {
101
+ $document = $this->lgtm->get_group('post', $post_id);
102
+
103
+ if ($document && $post_id == $document->source && md5(Lingotek_Group_Post::get_content($post_after)) != md5(Lingotek_Group_Post::get_content($post_before))) {
104
+ $document->source_edited();
105
+
106
+ if ($document->is_automatic_upload() && Lingotek_Group_Post::is_valid_auto_upload_post_status($post_after->post_status)) {
107
+ $this->lgtm->upload_post($post_id);
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ /*
114
+ * get translations ids to sync for delete / trash / untrash
115
+ * since we can't sync all translations as we get conflicts when attempting to act two times on the same
116
+ *
117
+ * @since 0.2
118
+ *
119
+ * @param int $post_id
120
+ * @return array
121
+ */
122
+ protected function get_translations_to_sync($post_id) {
123
+ // don't synchronize disassociated posts
124
+ $group = $this->lgtm->get_group('post', $post_id);
125
+ if (empty($group->source))
126
+ return array();
127
+
128
+ if (isset($_REQUEST['media']) && is_array($_REQUEST['media']))
129
+ $post_ids = array_map('intval', $_REQUEST['media']);
130
+ elseif (!empty($_REQUEST['post']) && is_array($_REQUEST['post']))
131
+ $post_ids = array_map('intval', $_REQUEST['post']);
132
+
133
+ $post_ids[] = $post_id;
134
+ return array_diff($this->model->get_translations('post', $post_id), $post_ids);
135
+ }
136
+
137
+ /*
138
+ * deletes the Lingotek document when a source document is deleted
139
+ *
140
+ * @since 0.1
141
+ *
142
+ * @param int $post_id
143
+ */
144
+ public function delete_post($post_id) {
145
+ static $avoid_recursion = array();
146
+
147
+ if (!wp_is_post_revision($post_id) && !in_array($post_id, $avoid_recursion)) {
148
+ // sync delete is not needed when emptying the bin as trash is synced
149
+ if (empty($_REQUEST['delete_all'])) {
150
+ $tr_ids = $this->get_translations_to_sync($post_id);
151
+ $avoid_recursion = array_merge($avoid_recursion, array_values($tr_ids));
152
+ foreach ($tr_ids as $tr_id) {
153
+ wp_delete_post($tr_id, true); // forces deletion for the translations which are not already in the list
154
+ }
155
+ }
156
+ $this->lgtm->delete_post($post_id);
157
+ }
158
+ }
159
+
160
+ /*
161
+ * sync trash
162
+ *
163
+ * @since 0.1
164
+ *
165
+ * @param int $post_id
166
+ */
167
+ public function trash_post($post_id) {
168
+ foreach ($this->get_translations_to_sync($post_id) as $tr_id)
169
+ wp_trash_post($tr_id);
170
+ }
171
+
172
+ /*
173
+ * sync untrash
174
+ *
175
+ * @since 0.1
176
+ *
177
+ * @param int $post_id
178
+ */
179
+ public function untrash_post($post_id) {
180
+ foreach ($this->get_translations_to_sync($post_id) as $tr_id)
181
+ wp_untrash_post($tr_id);
182
+ }
183
+ }
admin/filters-term.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Modifies Polylang filters
5
+ * Manages automatic upload
6
+ *
7
+ * @since 0.2
8
+ */
9
+ class Lingotek_Filters_Term extends PLL_Admin_Filters_Term {
10
+ public $lgtm; // Lingotek model
11
+ protected $old_term; // used to save old md5sum of a term when it is edited
12
+
13
+ /*
14
+ * Constructor
15
+ *
16
+ * @since 0.2
17
+ */
18
+ public function __construct(&$polylang) {
19
+ parent::__construct($polylang);
20
+
21
+ $this->lgtm = &$GLOBALS['wp_lingotek']->model;
22
+
23
+ add_action('edit_terms', array(&$this, 'save_old_term'), 10, 2);
24
+ add_action('edited_term', array(&$this, 'edited_term'), 10, 3);
25
+ }
26
+
27
+ /*
28
+ * controls whether to display the language metabox or not
29
+ *
30
+ * @since 0.2
31
+ */
32
+ public function edit_term_form($tag) {
33
+ if ($this->model->is_translated_taxonomy($tag->taxonomy)) {
34
+ $document = $this->lgtm->get_group('term', $tag->term_id);
35
+ if (empty($document->source))
36
+ parent::edit_term_form($tag);
37
+ }
38
+ }
39
+
40
+ /*
41
+ * uploads a term when saved for the first time
42
+ *
43
+ * @since 0.2
44
+
45
+ * @param int $term_id
46
+ * @param int $tt_id term taxononomy id
47
+ * @param string $taxonomy
48
+ */
49
+ public function save_term($term_id, $tt_id, $taxonomy) {
50
+ if (!$this->model->is_translated_taxonomy($taxonomy))
51
+ return;
52
+
53
+ parent::save_term($term_id, $tt_id, $taxonomy);
54
+
55
+ if ('automatic' == Lingotek_Model::get_profile_option('upload', $taxonomy, $this->model->get_term_language($term_id)) && $this->lgtm->can_upload('term', $term_id))
56
+ $this->lgtm->upload_term($term_id, $taxonomy);
57
+ }
58
+
59
+ /*
60
+ * saves the md5sum of a term before it is edited
61
+ *
62
+ * @since 0.2
63
+ *
64
+ * @param int $term_id
65
+ * @param string $taxonomy
66
+ */
67
+ public function save_old_term($term_id, $taxonomy) {
68
+ if (pll_is_translated_taxonomy($taxonomy))
69
+ $this->old_term = md5(Lingotek_Group_Term::get_content(get_term($term_id, $taxonomy)));
70
+ }
71
+
72
+ /*
73
+ * marks the term as edited if needed
74
+ *
75
+ * @since 0.2
76
+ *
77
+ * @param int $term_id
78
+ * @param int $tt_id not used
79
+ * @param string $taxonomy
80
+ */
81
+ public function edited_term($term_id, $tt_id, $taxonomy) {
82
+ if (pll_is_translated_taxonomy($taxonomy)) {
83
+ $document = $this->lgtm->get_group('term', $term_id);
84
+
85
+ if ($document && $term_id == $document->source && md5(Lingotek_Group_Term::get_content(get_term($term_id, $taxonomy))) != $this->old_term) {
86
+ $document->source_edited();
87
+
88
+ if ($document->is_automatic_upload()) {
89
+ $this->lgtm->upload_term($term_id, $taxonomy);
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ /*
96
+ * get translations ids to sync for delete
97
+ * since we can't sync all translations as we get conflicts when attempting to act two times on the same
98
+ *
99
+ * @since 0.2
100
+ *
101
+ * @param int $term_id
102
+ * @return array
103
+ */
104
+ protected function get_translations_to_sync($term_id) {
105
+ // don't synchronize disassociated terms
106
+ $group = $this->lgtm->get_group('term', $term_id);
107
+ if (empty($group->source))
108
+ return array();
109
+
110
+ if (isset($_REQUEST['delete_tags']) && is_array($_REQUEST['delete_tags']))
111
+ $term_ids = array_map('intval', $_REQUEST['delete_tags']);
112
+
113
+ $term_ids[] = $term_id;
114
+ return array_diff($this->model->get_translations('term', $term_id), $term_ids);
115
+ }
116
+
117
+ /*
118
+ * deletes the Lingotek document when a source document is deleted
119
+ *
120
+ * @since 0.2
121
+ *
122
+ * @param int $term_id
123
+ */
124
+ public function delete_term($term_id) {
125
+ $taxonomy = substr(current_filter(), 7);
126
+ foreach ($this->get_translations_to_sync($term_id) as $tr_id) {
127
+ wp_delete_term($tr_id, $taxonomy); // forces deletion for the translations which are not already in the list
128
+ }
129
+ $this->lgtm->delete_term($term_id);
130
+ }
131
+ }
admin/manage/view-string-groups.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h3><?php _e('String Groups', 'wp-lingotek'); ?></h3>
2
+ <p class="description"><?php printf(__('Manage group translation of system, widget, and plugin-specific strings. View individual strings on the <a href="%s"><b>Strings</b></a> page.', 'wp-lingotek'), 'admin.php?page=wp-lingotek_manage&sm=strings'); ?></p>
3
+ <?php
4
+
5
+ $profile = Lingotek_Model::get_profile('string', $this->pllm->get_language($this->pllm->options['default_lang']));
6
+
7
+ if ('disabled' == $profile['profile']) {
8
+ printf('<div class="error" style="border-left: 4px solid #ffba00;"><p>%s</p></div>', // no warning class in WP 4.0, color form .update-nag
9
+ sprintf(__('The strings translation is disabled in %sContent Type Configuration%s.', 'wp-lingotek'),
10
+ '<a href="' . admin_url('admin.php?page=wp-lingotek_settings&sm=content') . '">',
11
+ '</a>'
12
+ )
13
+ );
14
+ }
15
+ else {
16
+ $string_actions = $GLOBALS['wp_lingotek']->string_actions;
17
+ $table = new Lingotek_Strings_Table($string_actions);
18
+ $action = $table->current_action();
19
+ if (!empty($action))
20
+ $string_actions->manage_actions($action);
21
+
22
+ $data = Lingotek_Model::get_strings();
23
+ foreach ($data as $key => $row)
24
+ $data[$key]['row'] = $key; // store the row number for convenience
25
+
26
+ $table->prepare_items($data); ?>
27
+
28
+ <form id="lingotek-strings" method="post" action="admin.php?page=wp-lingotek_manage&amp;noheader=true"><?php
29
+ $table->display(); ?>
30
+ </form><?php
31
+
32
+ foreach (Lingotek_String_actions::$actions as $action => $strings) {
33
+ if (!empty($_GET['bulk-lingotek-' . $action]))
34
+ printf('<div id="lingotek-progressdialog" title="%s"><div id="lingotek-progressbar"></div></div>', $strings['progress']);
35
+ }
36
+ }
admin/manage/view-strings.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h3 style="padding-top:0; margin-top:0; margin-bottom:-25px;"><?php _e('Strings', 'wp-lingotek'); ?> <a href="options-general.php?page=mlang&amp;tab=strings" title="<?php _e('Edit on Polylang Strings Translation page', 'wp-lingotek'); ?>" class="dashicons dashicons-edit"></a></h3>
2
+
3
+ <?php
4
+ $listlanguages = $GLOBALS['polylang']->model->get_languages_list();
5
+
6
+ // FIXME now mainly copy paste of Polylang
7
+
8
+ // get the strings to translate
9
+ $data = PLL_Admin_Strings::get_strings();
10
+
11
+ $selected = empty($_REQUEST['group']) ? -1 : $_REQUEST['group'];
12
+ foreach ($data as $key=>$row) {
13
+ $groups[] = $row['context']; // get the groups
14
+
15
+ // filter for search string
16
+ if (($selected !=-1 && $row['context'] != $selected) || (!empty($_REQUEST['s']) && stripos($row['name'], $_REQUEST['s']) === false && stripos($row['string'], $_REQUEST['s']) === false))
17
+ unset ($data[$key]);
18
+ }
19
+
20
+ $groups = array_unique($groups);
21
+
22
+ // load translations
23
+ foreach ($listlanguages as $language) {
24
+ // filters by language if requested
25
+ if (($lg = get_user_meta(get_current_user_id(), 'pll_filter_content', true)) && $language->slug != $lg)
26
+ continue;
27
+
28
+ $mo = new PLL_MO();
29
+ $mo->import_from_db($language);
30
+ foreach ($data as $key=>$row) {
31
+ $data[$key]['translations'][$language->slug] = $mo->translate($row['string']);
32
+ $data[$key]['row'] = $key; // store the row number for convenience
33
+ }
34
+ }
35
+
36
+ // get an array with language slugs as keys, names as values
37
+ $languages = array_combine(wp_list_pluck($listlanguages, 'slug'), wp_list_pluck($listlanguages, 'name'));
38
+
39
+ $string_table = new Lingotek_Table_String(compact('languages', 'groups', 'selected'));
40
+ $string_table->prepare_items($data); ?>
41
+
42
+ <div class="form-wrap">
43
+ <form id="string-translation" method="post" action="admin.php?page=mlang&amp;tab=strings&amp;noheader=true">
44
+ <input type="hidden" name="pll_action" value="string-translation" /><?php
45
+ $string_table->search_box(__('Search translations', 'wp-lingotek'), 'translations' );
46
+ wp_nonce_field('string-translation', '_wpnonce_string-translation');
47
+ $string_table->display(); ?>
48
+ </form>
49
+ </div>
admin/post-actions.php ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Adds row and bulk actions to posts, pages and media list
5
+ * Manages actions which trigger communication with Lingotek TMS
6
+
7
+ *
8
+ * @since 0.2
9
+ */
10
+ class Lingotek_Post_actions extends Lingotek_Actions {
11
+
12
+ /*
13
+ * Constructor
14
+ *
15
+ * @since 0.2
16
+ */
17
+ public function __construct() {
18
+ parent::__construct('post');
19
+
20
+ // row actions
21
+ add_filter('post_row_actions', array(&$this, 'post_row_actions'), 10, 2);
22
+ add_filter('page_row_actions', array(&$this, 'post_row_actions'), 10, 2); // hierarchical post types
23
+ add_filter('media_row_actions', array(&$this, 'post_row_actions'), 10, 2);
24
+
25
+ // add bulk actions
26
+ add_action('admin_footer-edit.php', array(&$this, 'add_bulk_actions')); // FIXME admin_print_footer_scripts instead?
27
+ add_action('admin_footer-upload.php', array(&$this, 'add_bulk_actions'));
28
+
29
+ // manage bulk actions, row actions and icon actions
30
+ add_action('load-edit.php', array(&$this, 'manage_actions'));
31
+ add_action('load-upload.php', array(&$this, 'manage_actions'));
32
+ }
33
+
34
+ /*
35
+ * get the language of a post
36
+ *
37
+ * @since 0.2
38
+ *
39
+ * @param int $post_id
40
+ * @return object
41
+ */
42
+ protected function get_language($post_id) {
43
+ return $this->pllm->get_post_language($post_id);
44
+ }
45
+
46
+ /*
47
+ * displays the icon of an uploaded post with the relevant link
48
+ *
49
+ * @since 0.2
50
+ *
51
+ * @param int $id
52
+ */
53
+ public static function uploaded_icon($id) {
54
+ return self::display_icon('uploaded', get_edit_post_link($id));
55
+ }
56
+
57
+ /*
58
+ * adds a row action link
59
+ *
60
+ * @since 0.1
61
+ *
62
+ * @param array $actions list of action links
63
+ * @param object $post
64
+ * @return array
65
+ */
66
+ public function post_row_actions($actions, $post) {
67
+ if ($this->pllm->is_translated_post_type($post->post_type)) {
68
+ $actions = $this->_row_actions($actions, $post->ID);
69
+
70
+ $language = $this->pllm->get_post_language($post->ID);
71
+ if (!empty($language)) {
72
+ $profile = Lingotek_Model::get_profile($post->post_type, $language);
73
+ if ('disabled' == $profile['profile'])
74
+ unset($actions['lingotek-upload']);
75
+ }
76
+ }
77
+ return $actions;
78
+ }
79
+
80
+ /*
81
+ * adds actions to bulk dropdown in posts list table
82
+ *
83
+ * @since 0.1
84
+ */
85
+ public function add_bulk_actions() {
86
+ if (isset($GLOBALS['post_type']) && $this->pllm->is_translated_post_type($GLOBALS['post_type']))
87
+ $this->_add_bulk_actions();
88
+ }
89
+
90
+ /*
91
+ * manages Lingotek specific actions before WordPress acts
92
+ *
93
+ * @since 0.1
94
+ */
95
+ public function manage_actions() {
96
+ global $typenow;
97
+ $post_type = 'load-upload.php' == current_filter() ? 'attachment' : $typenow;
98
+
99
+ if (!$this->pllm->is_translated_post_type($post_type))
100
+ return;
101
+
102
+ // get the action
103
+ // $typenow is empty for media
104
+ $wp_list_table = _get_list_table(empty($typenow) ? 'WP_Media_List_Table' : 'WP_Posts_List_Table');
105
+ $action = $wp_list_table->current_action();
106
+
107
+ if (empty($action))
108
+ return;
109
+
110
+ $redirect = remove_query_arg( array('action', 'action2', 'tags_input', 'post_author', 'comment_status', 'ping_status', '_status', 'post', 'bulk_edit', 'post_view'), wp_get_referer() );
111
+ if (!$redirect)
112
+ $redirect = admin_url("edit.php?post_type=$typenow");
113
+
114
+ switch($action) {
115
+ case 'bulk-lingotek-upload':
116
+ $type = empty($typenow) ? 'media' : 'post';
117
+ if (empty($_REQUEST[$type]))
118
+ return;
119
+
120
+ $post_ids = array();
121
+
122
+ foreach (array_map('intval', $_REQUEST[$type]) as $post_id) {
123
+ // safe upload
124
+ if ($this->lgtm->can_upload('post', $post_id))
125
+ $post_ids[] = $post_id;
126
+
127
+ // the document is already translated so will be overwritten
128
+ elseif(($document = $this->lgtm->get_group('post', $post_id)) && empty($document->source)) {
129
+ // take care to upload only one post in a translation group
130
+ $intersect = array_intersect($post_ids, $this->pllm->get_translations('post', $post_id));
131
+ if (empty($intersect)) {
132
+ $post_ids[] = $post_id;
133
+ $redirect = add_query_arg('lingotek_warning', 1, $redirect);
134
+ }
135
+ }
136
+ }
137
+
138
+ // check if translation is disabled
139
+ if (!empty($post_ids)) {
140
+ foreach ($post_ids as $key => $post_id) {
141
+ $language = $this->pllm->get_post_language($post_id);
142
+ $profile = Lingotek_Model::get_profile($post_type, $language);
143
+ if ('disabled' == $profile['profile'])
144
+ unset($post_ids[$key]);
145
+ }
146
+ }
147
+
148
+ case 'bulk-lingotek-request':
149
+ case 'bulk-lingotek-download':
150
+ case 'bulk-lingotek-status':
151
+ case 'bulk-lingotek-delete':
152
+ if (empty($post_ids)) {
153
+ $type = empty($typenow) ? 'media' : 'post';
154
+ if (empty($_REQUEST[$type]))
155
+ return;
156
+
157
+ $post_ids = array_map('intval', $_REQUEST[$type]);
158
+ }
159
+
160
+ empty($typenow) ? check_admin_referer('bulk-media') : check_admin_referer('bulk-posts');
161
+
162
+ $redirect = add_query_arg($action, 1, $redirect);
163
+ $redirect = add_query_arg('ids', implode(',', $post_ids), $redirect);
164
+
165
+ break;
166
+
167
+ case 'lingotek-upload':
168
+ check_admin_referer('lingotek-upload');
169
+ $this->lgtm->upload_post((int) $_GET['post']);
170
+ break;
171
+
172
+ default:
173
+ if (!$this->_manage_actions($action))
174
+ return; // do not redirect if this is not one of our actions
175
+
176
+ }
177
+
178
+ wp_redirect($redirect);
179
+ exit();
180
+
181
+ }
182
+
183
+ /*
184
+ * ajax response to upload documents and showing progress
185
+ *
186
+ * @since 0.1
187
+ */
188
+ public function ajax_upload() {
189
+ check_ajax_referer('lingotek_progress', '_lingotek_nonce');
190
+ $this->lgtm->upload_post((int) $_POST['id']);
191
+ die();
192
+ }
193
+ }
admin/profiles-table.php ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if(!class_exists('WP_List_Table')){
4
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' ); // since WP 3.1
5
+ }
6
+
7
+ class Lingotek_Profiles_Table extends WP_List_Table {
8
+
9
+ /*
10
+ * constructor
11
+ *
12
+ * @since 0.2
13
+ */
14
+ function __construct() {
15
+ parent::__construct(array(
16
+ 'plural' => 'lingotek-profiles', // do not translate (used for css class)
17
+ 'ajax' => false
18
+ ));
19
+ }
20
+
21
+ /*
22
+ * displays the item information in a column (default case)
23
+ *
24
+ * @since 0.2
25
+ *
26
+ * @param array $item
27
+ * @param string $column_name
28
+ * @return string
29
+ */
30
+ function column_default($item, $column_name) {
31
+ return isset($item[$column_name]) ? esc_html($item[$column_name]) : '';
32
+ }
33
+
34
+ /*
35
+ * displays the edit link in actions column
36
+ *
37
+ * @since 0.2
38
+ *
39
+ * @param array $item
40
+ * @return string
41
+ */
42
+ function column_usage($item){
43
+ return empty($item['usage']) ?
44
+ __('No content types', 'wp-lingotek') :
45
+ sprintf(_n('1 content type', '%d content types', $item['usage'], 'wp-lingotek'), number_format_i18n($item['usage']));
46
+ }
47
+
48
+ /*
49
+ * displays the edit link in actions column
50
+ *
51
+ * @since 0.2
52
+ *
53
+ * @param array $item
54
+ * @return string
55
+ */
56
+ function column_actions($item){
57
+ $actions = array();
58
+
59
+ if ('disabled' != $item['profile'])
60
+ $actions[] = sprintf(
61
+ '<a href=%s>%s</a>',
62
+ esc_url(admin_url('admin.php?page=wp-lingotek_settings&sm=edit-profile&profile='.$item['profile'])),
63
+ __('Edit', 'wp-lingotek')
64
+ );
65
+
66
+ if (!in_array($item['profile'], array('automatic', 'manual', 'disabled')) && empty($item['usage']))
67
+ $actions[] = sprintf(
68
+ '<a href="%s" onclick = "return confirm(\'%s\');">%s</a>',
69
+ esc_url(wp_nonce_url('admin.php?page=wp-lingotek_settings&sm=profiles&lingotek_action=delete-profile&noheader=true&profile='.$item['profile'], 'delete-profile')),
70
+ __('You are about to permanently delete this profile. Are you sure?', 'wp-lingotek'),
71
+ __('Delete', 'wp-lingotek')
72
+ );
73
+
74
+ return implode(' | ', $actions);
75
+ }
76
+
77
+ /*
78
+ * gets the list of columns
79
+ *
80
+ * @since 0.2
81
+ *
82
+ * @return array the list of column titles
83
+ */
84
+ function get_columns() {
85
+ return array(
86
+ 'name' => __('Profile name', 'wp-lingotek'),
87
+ 'usage' => __('Usage', 'wp-lingotek'),
88
+ 'actions' => __('Actions', 'wp-lingotek'),
89
+ );
90
+ }
91
+
92
+ /*
93
+ * gets the list of sortable columns
94
+ *
95
+ * @since 0.2
96
+ *
97
+ * @return array
98
+ */
99
+ function get_sortable_columns() {
100
+ return array(
101
+ 'name' => array('name', false),
102
+ );
103
+ }
104
+
105
+ /*
106
+ * prepares the list of items ofr displaying
107
+ *
108
+ * @since 0.2
109
+ *
110
+ * @param array $data
111
+ */
112
+ function prepare_items($data = array()) {
113
+ $per_page = $this->get_items_per_page('lingotek_profiles_per_page');
114
+ $this->_column_headers = array($this->get_columns(), array(), $this->get_sortable_columns());
115
+
116
+ function usort_reorder($a, $b){
117
+ $result = strcmp($a[$_REQUEST['orderby']], $b[$_REQUEST['orderby']]); // determine sort order
118
+ return (empty($_REQUEST['order']) || $_REQUEST['order'] == 'asc') ? $result : -$result; // send final sort direction to usort
119
+ };
120
+
121
+ if (!empty($_REQUEST['orderby'])) // no sort by default
122
+ usort($data, 'usort_reorder');
123
+
124
+ $total_items = count($data);
125
+ $this->items = array_slice($data, ($this->get_pagenum() - 1) * $per_page, $per_page);
126
+
127
+ $this->set_pagination_args(array(
128
+ 'total_items' => $total_items,
129
+ 'per_page' => $per_page,
130
+ 'total_pages' => ceil($total_items/$per_page)
131
+ ));
132
+ }
133
+ }
admin/settings.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap">
2
+ <h2><?php _e('Settings', 'wp-lingotek'); ?></h2>
3
+
4
+ <?php
5
+ if (strlen($access_token)) {
6
+ ?>
7
+
8
+ <?php
9
+
10
+ $menu_items = array(
11
+ 'account' => __('Account', 'wp-lingotek'),
12
+ );
13
+
14
+ $community_required_menu_items = array(
15
+ 'defaults' => __('Defaults', 'wp-lingotek'),
16
+ 'profiles' => __('Translation Profiles', 'wp-lingotek'),
17
+ 'content' => __('Content Type Configuration', 'wp-lingotek'),
18
+ 'preferences' => __('Preferences', 'wp-lingotek'),
19
+ //'advanced' => __('Advanced', 'wp-lingotek'),
20
+ //'logging' => __('Logging', 'wp-lingotek'),
21
+ 'utilities' => __('Utilities', 'wp-lingotek'),
22
+ );
23
+
24
+ if($community_id !== FALSE){
25
+ $menu_items = array_merge($menu_items, $community_required_menu_items);
26
+ }
27
+
28
+ ?>
29
+
30
+ <h3 class="nav-tab-wrapper">
31
+ <?php
32
+ $menu_item_index = 0;
33
+ foreach ($menu_items as $menu_item_key => $menu_item_label) {
34
+ $use_as_default = ($menu_item_index === 0 && !isset($_GET['sm'])) ? TRUE : FALSE;
35
+ $alias = NULL;
36
+ // custom sub sub-menus
37
+ if(isset($_GET['sm']) && $_GET['sm'] == "edit-profile") {
38
+ $alias = "profiles";
39
+ }
40
+ ?>
41
+
42
+ <a class="nav-tab <?php if ($use_as_default || (isset($_GET['sm']) && $_GET['sm'] == $menu_item_key) || $alias == $menu_item_key): ?> nav-tab-active<?php endif; ?>"
43
+ href="admin.php?page=<?php echo $_GET['page']; ?>&amp;sm=<?php echo $menu_item_key; ?>"><?php echo $menu_item_label; ?></a>
44
+ <?php
45
+ $menu_item_index++;
46
+ }
47
+ ?>
48
+ </h3>
49
+
50
+ <?php
51
+ settings_errors();
52
+ $submenu = isset($_GET['sm']) ? $_GET['sm'] : 'account';
53
+ $dir = dirname(__FILE__) . '/settings/';
54
+ $filename = $dir . 'view-' . $submenu . ".php";
55
+ if (file_exists($filename))
56
+ include $filename;
57
+ else
58
+ echo "TO-DO: create <i>" . 'settings/view-' . $submenu . ".php</i>";
59
+ ?>
60
+
61
+ <?php
62
+ }
63
+ ?>
64
+ </div>
admin/settings/connect-account.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /* Redirect Access Token */ ?>
2
+ <script>
3
+ var hash = window.location.hash;
4
+ if (hash.length && hash.indexOf("access_token") !== -1) {
5
+ var parts = hash.substring(1).split('=');
6
+ var access_token = parts[1];
7
+ var url_with_access_token = window.location.origin + window.location.pathname + window.location.search + '&access_token=' + access_token;
8
+ window.location.href = url_with_access_token;
9
+ }
10
+ else if (window.location.search.indexOf("connect") != -1) {
11
+ window.location.href = "<?php echo $connect_url ?>";
12
+ }
13
+ </script>
14
+ <?php /* Connect Your Account Button */ ?>
15
+ <div class="wrap">
16
+ <h2><?php _e('Connect Your Account', 'wp-lingotek') ?></h2>
17
+ <div>
18
+ <p class="description">
19
+ <?php _e('Get started by clicking the button below to connect your Lingotek account to this Wordpress installation.', 'wp-lingotek') ?>
20
+ </p>
21
+ <hr/>
22
+ <p>
23
+ <a class="button button-large button-hero" href="<?php echo $connect_account_cloak_url_new ?>">
24
+ <img src="<?php echo LINGOTEK_URL; ?>/img/lingotek-icon.png" style="padding: 0 4px 2px 0;" align="absmiddle"/> <?php _e('Connect New Account', 'wp-lingotek') ?>
25
+ </a>
26
+ </p>
27
+ <hr/>
28
+ <p class="description"><?php echo sprintf( __('Do you already have a Lingotek account? <a href="%s">Connect Lingotek Account</a>', 'wp-lingotek'), esc_attr($connect_account_cloak_url_prod)) ?></p>
29
+ <p class="description"><?php echo sprintf( __('Do you have a Lingotek sandbox account? <a href="%s">Connect Sandbox Account</a>', 'wp-lingotek'), esc_attr($connect_account_cloak_url_test)) ?></p>
30
+ </div>
31
+ </div>
admin/settings/view-account.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if (!$community_id) {
3
+ $ltk_client = new Lingotek_API();
4
+ $ltk_communities = $ltk_client->get_communities();
5
+ $ltk_num_communities = $ltk_communities->properties->total;
6
+ if ($ltk_num_communities == 1) {
7
+ $ltk_community_id = $ltk_communities->entities[0]->properties->id;
8
+ $this->set_community_resources($ltk_community_id, TRUE);
9
+ echo '<script type="text/javascript">document.body.innerHTML = ""; window.location = "admin.php?page=wp-lingotek_tutorial";</script>';
10
+ }
11
+ }
12
+ ?>
13
+
14
+ <h3><?php _e('Account', 'wp-lingotek'); ?></h3>
15
+ <p class="description"><?php _e('Lingotek account connection and community selection.', 'wp-lingotek'); ?></p>
16
+
17
+ <table class="form-table">
18
+ <tr>
19
+ <th scope="row">
20
+ <?php _e('Connected', 'wp-lingotek') ?>
21
+ <a id="cd-show-link" class="dashicons dashicons-arrow-right" onclick="document.getElementById('connection-details').style.display = ''; document.getElementById('cd-hide-link').style.display = ''; this.style.display = 'none'; return false;"></a>
22
+ <a id="cd-hide-link" class="dashicons dashicons-arrow-down" onclick="document.getElementById('connection-details').style.display = 'none'; document.getElementById('cd-show-link').style.display = ''; this.style.display = 'none'; return false;" style="display: none;"></a>
23
+ </th>
24
+ <td>
25
+ <?php _e('Yes', 'wp-lingotek') ?><span title="<?php _e('Connected', 'wp-lingotek') ?>" class="dashicons dashicons-yes" style="color: green;"></span>
26
+ </td>
27
+ </tr>
28
+ <tbody id="connection-details" style="display: none;">
29
+ <tr>
30
+ <th scope="row"><?php echo __('Login ID', 'wp-lingotek') ?></th>
31
+ <td>
32
+ <label>
33
+ <?php
34
+ printf(
35
+ '<input name="%s" class="regular-text" type="text" value="%s" disabled="disabled" />', 'login_id', $token_details['login_id']
36
+ );
37
+ ?>
38
+ </label>
39
+ </td>
40
+ </tr>
41
+ <tr>
42
+ <th scope="row"><?php echo __('Access Token', 'wp-lingotek') ?></th>
43
+ <td>
44
+ <label>
45
+ <?php
46
+ printf(
47
+ '<input name="%s" class="regular-text" type="password" value="%s" disabled="disabled" style="display: none;" />', 'access_token', $token_details['access_token']
48
+ );
49
+ printf(
50
+ '<input name="%s" class="regular-text" type="text" value="%s" disabled="disabled" />', 'access_token', $token_details['access_token']
51
+ );
52
+ ?>
53
+ </label>
54
+ </td>
55
+ </tr>
56
+ <tr>
57
+ <th scope="row"><?php echo __('API Endpoint', 'wp-lingotek') ?></th>
58
+ <td>
59
+ <label>
60
+ <?php
61
+ printf(
62
+ '<input name="%s" class="regular-text" type="text" value="%s" disabled="disabled" />', 'base_url', $base_url
63
+ );
64
+ ?>
65
+ </label>
66
+ </td>
67
+ </tr>
68
+ <tr>
69
+ <th></th>
70
+ <td>
71
+ <?php
72
+ $confirm_message = __('Are you sure you would like to disconnect your Lingotek account? \n\nAfter disconnecting, you will need to re-connect an account to continue using Lingotek.', 'wp-lingotek');
73
+ echo '<a class="button" href="' . $redirect_url . '&delete_access_token=true" onclick="return confirm(\'' . $confirm_message . '\')">' . __('Disconnect', 'wp-lingotek') . '</a>';
74
+ ?>
75
+ </td>
76
+ </tr>
77
+ </tbody>
78
+ </table>
79
+
80
+ <hr/>
81
+
82
+ <form method="post" action="admin.php?page=<?php echo $page_key; ?>" class="validate"> <?php /* &amp;noheader=true */ ?>
83
+ <?php wp_nonce_field($page_key, '_wpnonce_' . $page_key); ?>
84
+
85
+ <table class="form-table">
86
+ <tr>
87
+ <th scope="row"><label for="lingotek_community"><?php _e('Community', 'wp-lingotek') ?></label></th>
88
+ <td>
89
+ <select name="lingotek_community" id="lingotek_community">
90
+ <?php
91
+ $default_community_id = $community_id;
92
+
93
+ $client = new Lingotek_API();
94
+
95
+ // Community
96
+ $api_communities = $client->get_communities();
97
+ $communities = array();
98
+ foreach ($api_communities->entities as $community) {
99
+ $communities[$community->properties->id] = $community->properties->title; // . ' (' . $community->properties->id . ')';
100
+ }
101
+
102
+ $num_communities = count($communities);
103
+ if($num_communities == 1 && !$community_id){
104
+ update_option('lingotek_community', current(array_keys($communities)));
105
+ }
106
+ if(!$community_id && $num_communities > 1) {
107
+ echo "\n\t" . '<option value="">'.__('Select', 'wp-lingotek').'...</option>';
108
+ }
109
+ foreach ($communities as $community_id_option => $community_title) {
110
+ $selected = ($default_community_id == $community_id_option) ? 'selected="selected"' : '';
111
+ echo "\n\t" . '<option value="' . esc_attr($community_id_option) . '" '.$selected.'>' . $community_title . '</option>';
112
+ }
113
+ ?>
114
+ </select>
115
+ </td>
116
+ </tr>
117
+ </table>
118
+
119
+ <?php submit_button(__('Save Changes', 'wp-lingotek'), 'primary', 'submit', false); ?>
120
+ </form>
admin/settings/view-content.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ global $polylang;
4
+
5
+ foreach ($polylang->model->get_translated_post_types() as $post_type) {
6
+ $post_type_object = get_post_type_object($post_type);
7
+ $data[$post_type] = array(
8
+ 'type' => $post_type,
9
+ 'name' => $post_type_object->labels->name,
10
+ 'fields' => array(
11
+ 'label' => Lingotek_Group_Post::get_content_type_fields($post_type)
12
+ )
13
+ );
14
+ }
15
+
16
+ foreach ($polylang->model->get_translated_taxonomies() as $tax) {
17
+ $taxonomy = get_taxonomy($tax);
18
+ $data[$tax] = array(
19
+ 'type' => $tax,
20
+ 'name' => $taxonomy->labels->name,
21
+ 'fields' => array(
22
+ 'label' => Lingotek_Group_Term::get_content_type_fields($tax)
23
+ )
24
+ );
25
+ }
26
+
27
+ $data['string'] = array(
28
+ 'type' => 'string',
29
+ 'name' => __('Strings', 'wp-lingotek'),
30
+ );
31
+
32
+ if (empty($_POST)) {
33
+ $content_types = get_option('lingotek_content_type');
34
+ }
35
+
36
+ else {
37
+ check_admin_referer('lingotek-content-types', '_wpnonce_lingotek-content-types');
38
+
39
+ $profiles = array_keys(get_option('lingotek_profiles'));
40
+
41
+ foreach ($data as $key => $item) {
42
+ if (isset($data[$key]['name'])) {
43
+ if (in_array($_POST[$key]['profile'], $profiles))
44
+ $content_types[$key]['profile'] = $_POST[$key]['profile'];
45
+
46
+ foreach ($polylang->model->get_languages_list() as $language) {
47
+ if (isset($_POST[$key]['sources'][$language->slug]) && in_array($_POST[$key]['sources'][$language->slug], $profiles))
48
+ $content_types[$key]['sources'][$language->slug] = $_POST[$key]['sources'][$language->slug];
49
+ }
50
+
51
+ }
52
+ if (isset($data[$key]['fields'])) {
53
+ foreach ($data[$key]['fields']['label'] as $key1 => $arr) {
54
+ if (is_array($arr)) {
55
+ foreach (array_keys($arr) as $key2) {
56
+ if(empty($_POST[$key]['fields'][$key1][$key2]))
57
+ $content_types[$key]['fields'][$key1][$key2] = 1;
58
+ }
59
+ }
60
+ elseif (empty($_POST[$key]['fields'][$key1])) {
61
+ $content_types[$key]['fields'][$key1] = 1;
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ update_option('lingotek_content_type', $content_types);
68
+ add_settings_error('lingotek_content_types', 'default', __('Your content types were sucessfully saved.', 'wp-lingotek'), 'updated');
69
+ settings_errors();
70
+ }
71
+
72
+ foreach ($data as $key => $item) {
73
+ // default profile is manual except for post
74
+ $data[$key]['profile'] = empty($content_types[$key]['profile']) ? ('post' == $key ? 'automatic' : 'manual') : $content_types[$key]['profile'];
75
+ $data[$key]['sources'] = empty($content_types[$key]['sources']) ? array() : $content_types[$key]['sources'];
76
+ if (!empty($content_types[$key]['fields']))
77
+ $data[$key]['fields']['value'] = $content_types[$key]['fields'];
78
+ }
79
+
80
+ ?>
81
+ <h3><?php _e('Content Type Configuration', 'wp-lingotek'); ?></h3>
82
+ <p class="description"><?php _e('Content types can be configured to use any translation profile. Additionally, translation profiles can be set based on the language the content was authored in.', 'wp-lingotek'); ?></p>
83
+
84
+ <form id="lingotek-content-types" method="post" action="admin.php?page=wp-lingotek_settings&amp;sm=content" class="validate"><?php
85
+ wp_nonce_field('lingotek-content-types', '_wpnonce_lingotek-content-types');
86
+
87
+ $table = new Lingotek_Content_Table($content_types);
88
+ $table->prepare_items($data);
89
+ $table->display();
90
+
91
+ submit_button(__('Save Changes', 'wp-lingotek'), 'primary', 'submit', false);
92
+ ?>
93
+ </form>
admin/settings/view-defaults.php ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $page_key = $this->plugin_slug . '_settings&sm=defaults';
4
+
5
+ wp_enqueue_script('defaults', LINGOTEK_URL . '/js/defaults.js');
6
+
7
+ if (!empty($_POST)) {
8
+ check_admin_referer($page_key, '_wpnonce_' . $page_key);
9
+
10
+ if (array_key_exists('refresh', $_POST)) {
11
+ $refresh_success = $this->set_community_resources($community_id);
12
+ if ($refresh_success['projects'] == TRUE && $refresh_success['workflows'] == TRUE) {
13
+ add_settings_error('lingotek_community_resources', 'options', __('Resources from Lingotek were successfully updated for projects and workflows.', 'wp-lingotek'), 'updated');
14
+ }
15
+ else if ($refresh_success['projects'] == TRUE) {
16
+ add_settings_error('lingotek_community_resources', 'error', __('Resources from Lingotek were successfully updated for projects.', 'wp-lingotek'), 'updated');
17
+ }
18
+ else if ($refresh_success['workflows'] == TRUE) {
19
+ add_settings_error('lingotek_community_resources', 'error', __('Resources from Lingotek were successfully updated for workflows.', 'wp-lingotek'), 'updated');
20
+ }
21
+ }
22
+ else {
23
+ $options = array();
24
+ $settings = $this->get_profiles_settings(true);
25
+ foreach ($settings as $key => $setting) {
26
+ if (isset($_POST[$key])) {
27
+ $options[$key] = $_POST[$key];
28
+ }
29
+ }
30
+ update_option('lingotek_defaults', $options);
31
+ add_settings_error('lingotek_defaults', 'defaultgs', __('Your <i>Defaults</i> were sucessfully saved.', 'wp-lingotek'), 'updated');
32
+
33
+ if (isset($_POST['update_callback'])) {
34
+ $client = new Lingotek_API();
35
+ if ($client->update_callback_url($options['project_id']))
36
+ add_settings_error('lingotek_defaults', 'defaultgs', __('Your callback url was successfully updated.', 'wp-lingotek'), 'updated');
37
+ }
38
+
39
+ //adds new project if text box is filled out
40
+ if (!empty($_POST['new_project'])) {
41
+ $client = new Lingotek_API();
42
+ $title = stripslashes($_POST['new_project']);
43
+
44
+ if ($new_id = $client->create_project($title, $community_id)) {
45
+ add_settings_error('lingotek_defaults', 'defaultgs', __('Your new project was successfully created.', 'wp-lingotek'), 'updated');
46
+ $this->set_community_resources($community_id);// updates the cache to include the newly created project
47
+ $options['project_id'] = $new_id;
48
+ update_option('lingotek_defaults', $options);
49
+ }
50
+ }
51
+ }
52
+ settings_errors();
53
+ }
54
+ $settings = $this->get_profiles_settings(true);
55
+ $options = get_option('lingotek_defaults');
56
+
57
+ // Code to determine which filter scenario will be displayed (Not configured, defaults, custom filters)
58
+ $primary_filter_id = array_search('okf_json@with-html-subfilter.fprm', $settings['primary_filter_id']['options']);
59
+ $secondary_filter_id = array_search('okf_html@wordpress.fprm', $settings['secondary_filter_id']['options']);
60
+ $default_filters = array($primary_filter_id => 'okf_json@with-html-subfilter.fprm', $secondary_filter_id => 'okf_html@wordpress.fprm');
61
+ $default_filters_exist = FALSE;
62
+ $extra_filters_exist = FALSE;
63
+ $no_filters_exist = FALSE;
64
+
65
+ if ($settings['primary_filter_id']['options'] == $default_filters) {
66
+ $default_filters_exist = TRUE;
67
+ $options['primary_filter_id'] = $primary_filter_id;
68
+ $options['secondary_filter_id'] = $secondary_filter_id;
69
+ update_option('lingotek_defaults', $options);
70
+ }
71
+ else {
72
+ $num = count(array_diff_assoc($settings['primary_filter_id']['options'], $default_filters));
73
+ if ($num > 0) {
74
+ $extra_filters_exist = TRUE;
75
+ }
76
+ else {
77
+ $options['primary_filter_id'] = '';
78
+ $options['secondary_filter_id'] = '';
79
+ update_option('lingotek_defaults', $options);
80
+ $no_filters_exist = TRUE;
81
+ }
82
+ }
83
+ unset($settings['primary_filter_id']['options'][$secondary_filter_id]);
84
+ unset($settings['secondary_filter_id']['options'][$primary_filter_id]);
85
+ ?>
86
+
87
+ <h3><?php _e('Defaults', 'wp-lingotek'); ?></h3>
88
+ <p class="description"><?php _e('The default automation settings and resources that should be used for this site. These settings can be overriden using translation profiles and content type configuration.', 'wp-lingotek'); ?></p>
89
+
90
+
91
+ <form id="lingotek-settings" method="post" action="admin.php?page=<?php echo $page_key; ?>" class="validate">
92
+ <?php wp_nonce_field($page_key, '_wpnonce_' . $page_key); ?>
93
+
94
+ <table class="form-table"><?php foreach ($settings as $key => $setting) { ?>
95
+ <tr id="<?php echo $key.'_row'?>">
96
+ <th scope="row"><label for="<?php echo $key; ?>"><?php echo $setting['label'] ?></label></th>
97
+ <td>
98
+ <select name="<?php echo $key ?>" id="<?php echo $key ?>"><?php
99
+ foreach ($setting['options'] as $id => $title) {
100
+ $selected = array_key_exists($key, $options) && ($options[$key] == $id) ? 'selected="selected"' : '';
101
+ echo "\n\t<option value='" . esc_attr($id) . "' $selected>" . $title . '</option>';
102
+ } ?>
103
+ </select><?php
104
+ if ('project_id' == $key) { ?>
105
+ <?php
106
+ $client = new Lingotek_API();
107
+
108
+ if ($client->get_projects($community_id) == 'empty') { ?>
109
+ <script> document.getElementById('project_id').style.display = 'none';</script>
110
+ <input type="text" name="new_project" id="new_project" placeholder="<?php _e('Enter new project name', 'wp-lingotek') ?>" />
111
+ <?php }
112
+ else { ?>
113
+
114
+ <input type="text" style="display:none" name="new_project" id="new_project" placeholder="<?php _e('Enter new project name', 'wp-lingotek') ?>" />
115
+ <input type="checkbox" name="update_callback" id="update_callback"/>
116
+ <label for="update_callback" id="callback_label"><?php _e('Update the callback url for this project.', 'wp-lingotek') ?></label>
117
+
118
+ <br/><a href="#" id="create" onclick="toggleTextbox()" style="padding-left:3px; color:#999; font-size:80%; text-decoration:none"><b>+</b> <?php echo _e('Create New Project', 'wp-lingotek') ?></a>
119
+ <?php } ?>
120
+ <?php } ?>
121
+ <!-- Code to handle displaying of Primary and Secondary Filters -->
122
+ <?php if($no_filters_exist) { ?>
123
+ <script> document.getElementById("primary_filter_id_row").style.display = "none";</script>
124
+ <script> document.getElementById("secondary_filter_id_row").style.display = "none";</script> <?php
125
+ if ('primary_filter_id' == $key) { ?>
126
+ <tr id="filters_row"><th><?php _e('Filters', 'wp-lingotek') ?></th><td><i><?php _e('Not configured', 'wp-lingotek') ?></i></td></tr>
127
+ <?php }
128
+ }
129
+ if ($default_filters_exist) { ?>
130
+ <script> document.getElementById("primary_filter_id_row").style.display = "none";</script>
131
+ <script> document.getElementById("secondary_filter_id_row").style.display = "none";</script>
132
+ <?php } ?>
133
+ <!-- End of filter code -->
134
+ </tr><?php } ?>
135
+ </table>
136
+
137
+ <p>
138
+ <?php submit_button(__('Save Changes', 'wp-lingotek'), 'primary', 'submit', false); ?>
139
+ <?php submit_button(__( 'Refresh Resources', 'wp-lingotek'), 'secondary', 'refresh', false ); ?>
140
+ </p>
141
+ </form>
admin/settings/view-edit-profile.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $settings = $this->get_profiles_settings();
4
+
5
+ $defaults = get_option('lingotek_defaults');
6
+ $default = __('Use global default (%s)', 'wp-lingotek');
7
+ foreach ($settings as $key => $setting) {
8
+ if (isset($defaults[$key]) && array_key_exists($defaults[$key], $settings[$key]['options'])) {
9
+ $default_arr = array('default' => sprintf($default, $settings[$key]['options'][$defaults[$key]]));
10
+ $settings[$key]['options'] = array_merge($default_arr, $settings[$key]['options']);
11
+ }
12
+ }
13
+
14
+ $target_settings = array(
15
+ 'default' => __('Use default settings', 'wp-lingotek'),
16
+ 'custom' => __('Use custom settings', 'wp-lingotek'),
17
+ 'disabled' => __('Disabled', 'wp-lingotek')
18
+ );
19
+
20
+ $profiles = $this->get_profiles_usage(get_option('lingotek_profiles'));
21
+
22
+ $profile = isset($_GET['profile']) && array_key_exists($_GET['profile'], $profiles) ? $profiles[$_GET['profile']] : array();
23
+ $disabled = isset($profile['profile']) && in_array($profile['profile'], array('automatic', 'manual')) ? 'disabled="disabled"' : '';
24
+
25
+ // Code to determine which filter scenario will be displayed. (Not configured, defaults, custom filters)
26
+ $primary_filter_id = array_search('okf_json@with-html-subfilter.fprm', $settings['primary_filter_id']['options']);
27
+ $secondary_filter_id = array_search('okf_html@wordpress.fprm', $settings['secondary_filter_id']['options']);
28
+ $default_filters = array($primary_filter_id => 'okf_json@with-html-subfilter.fprm', $secondary_filter_id => 'okf_html@wordpress.fprm');
29
+ $default_filters_exist = FALSE;
30
+ $extra_filters_exist = FALSE;
31
+ $no_filters_exist = FALSE;
32
+
33
+ if (array_key_exists('default', array_diff_assoc($settings['primary_filter_id']['options'], $default_filters)) && count(array_diff_assoc($settings['primary_filter_id']['options'], $default_filters)) == 1) {
34
+ unset($settings['primary_filter_id']['options']['default']);
35
+ }
36
+ if ($settings['primary_filter_id']['options'] == $default_filters) {
37
+ $default_filters_exist = TRUE;
38
+ $defaults['primary_filter_id'] = $primary_filter_id;
39
+ $defaults['secondary_filter_id'] = $secondary_filter_id;
40
+ update_option('lingotek_defaults', $defaults);
41
+ }
42
+ else {
43
+ $num = count(array_diff_assoc($settings['primary_filter_id']['options'], $default_filters));
44
+ if ($num > 0) {
45
+ $extra_filters_exist = TRUE;
46
+ }
47
+ else {
48
+ $defaults['primary_filter_id'] = '';
49
+ $defaults['secondary_filter_id'] = '';
50
+ update_option('lingotek_defaults', $defaults);
51
+ $no_filters_exist = TRUE;
52
+ }
53
+ }
54
+ unset($settings['primary_filter_id']['options'][$secondary_filter_id]);
55
+ unset($settings['secondary_filter_id']['options'][$primary_filter_id]);
56
+ ?>
57
+
58
+ <form id="lingotek-edit-profile" method="post" action="admin.php?page=wp-lingotek_settings&sm=profiles" class="validate">
59
+ <?php wp_nonce_field('lingotek-edit-profile', '_wpnonce_lingotek-edit-profile');?>
60
+ <input name="profile" type="hidden" value="<?php echo empty($profile['profile']) ? '' : esc_attr($profile['profile']); ?>">
61
+
62
+ <table class="form-table">
63
+ <tr>
64
+ <th scope="row"><?php printf('<label for="%s">%s</label>', 'name' , __('Profile name', 'wp-lingotek')); ?></th>
65
+ <td><?php
66
+ $profile_name = $disabled ? __($profile['name'],'wp-lingotek') : $profile['name'];// localize canned profile names
67
+ printf('<input name="name" id="name" type="text" value="%s" %s>',
68
+ empty($profile['name']) ? '' : esc_attr($profile_name),
69
+ $disabled
70
+ ); ?>
71
+ </td>
72
+ </tr>
73
+ </table>
74
+
75
+ <h3><?php _e('Default settings', 'wp-lingotek'); ?></h3>
76
+
77
+ <table class="form-table"><?php
78
+ foreach ($settings as $key => $setting) { ?>
79
+ <tr id="<?php echo $key.'_row'?>">
80
+ <th scope="row"><?php printf('<label for="%s">%s</label>', $key , $setting['label']); ?></th>
81
+ <td><?php
82
+ printf('<select name="%1$s" id="%1$s" %2$s>', $key, in_array($key, array('upload', 'download')) ? $disabled : '');
83
+ foreach ($setting['options'] as $id => $title) {
84
+ $selected = isset($profile[$key]) && $profile[$key] == $id ? 'selected="selected"' : '';
85
+ echo "\n\t<option value='" . esc_attr($id) . "' $selected>" . esc_html($title) . '</option>';
86
+ } ?>
87
+ </select><?php
88
+
89
+ if ('project_id' == $key) { ?>
90
+ <input type="checkbox" name="update_callback" id="update_callback"/>
91
+ <label for="update_callback"><?php _e('Update the callback url for this project.', 'wp-lingotek') ?></label><?php
92
+ }
93
+
94
+ if (isset($setting['description']))
95
+ printf('<p class="description">%s</p>', $setting['description']);?>
96
+
97
+ <!-- Code to handle displaying of Primary and Secondary Filters -->
98
+ <?php if($no_filters_exist) { ?>
99
+ <script> document.getElementById("primary_filter_id_row").style.display = "none";</script>
100
+ <script> document.getElementById("secondary_filter_id_row").style.display = "none";</script> <?php
101
+ if ('primary_filter_id' == $key) { ?>
102
+ <tr id="filters_row"><th><?php _e('Filters', 'wp-lingotek') ?></th><td><i><?php _e('Not configured', 'wp-lingotek') ?></i></td></tr>
103
+ <?php }
104
+ }
105
+ if ($default_filters_exist) { ?>
106
+ <script> document.getElementById("primary_filter_id_row").style.display = "none";</script>
107
+ <script> document.getElementById("secondary_filter_id_row").style.display = "none";</script>
108
+ <?php } ?>
109
+ <!-- End of filter code -->
110
+ </td>
111
+ </tr><?php
112
+ } ?>
113
+ </table>
114
+
115
+ <h3><?php _e('Target languages', 'wp-lingotek'); ?></h3>
116
+
117
+ <table class="form-table"><?php
118
+ unset($settings['upload']); // we don't want this for target languages
119
+ unset($settings['project_id']); // FIXME disable the possibility to have a different project per target language for now
120
+
121
+ foreach ($this->pllm->get_languages_list() as $language) { ?>
122
+ <tr>
123
+ <th scope="row"><?php printf('<label for="%s">%s</label>', esc_attr($language->slug) , esc_html($language->name)); ?><?php
124
+
125
+ printf('<a id="%1$s_details_link" %2$s class="dashicons dashicons-arrow-right" onclick="%3$s">%4$s</a>',
126
+ esc_attr($language->slug),
127
+ isset($profile['targets'][$language->slug]) && 'custom' == $profile['targets'][$language->slug] ? '' : 'style="display:none;"',
128
+ "
129
+ d = document.getElementById('{$language->slug}_details');
130
+ if ('none' == d.style.display) {
131
+ d.style.display = '';
132
+ this.className = 'dashicons dashicons-arrow-down';
133
+ }
134
+ else {
135
+ d.style.display = 'none';
136
+ this.className = 'dashicons dashicons-arrow-right';
137
+ }",
138
+ '' //__('Show', 'wp-lingotek')
139
+
140
+ ); ?></th>
141
+ <td><?php
142
+ printf('<select name="targets[%1$s]" id="targets[%1$s]" onchange="%2$s">',
143
+ esc_attr($language->slug),
144
+ "
145
+ dl = document.getElementById('{$language->slug}_details_link');
146
+ d = document.getElementById('{$language->slug}_details');
147
+ if ('custom' == this.value) {
148
+ d.style.display = dl.style.display = '';
149
+ dl.className = 'dashicons dashicons-arrow-down';
150
+ }
151
+ else {
152
+ d.style.display = dl.style.display = 'none';
153
+ dl.className = 'dashicons dashicons-arrow-right';
154
+ }"
155
+ );
156
+ foreach ($target_settings as $id => $title) {
157
+ $selected = (empty($profile['targets'][$language->slug]) && 'default' == $id) || (isset($profile['targets'][$language->slug]) && $profile['targets'][$language->slug] == $id) ? 'selected="selected"' : '';
158
+ echo "\n\t<option value='" . $id . "' $selected>" . $title . '</option>';
159
+ } ?>
160
+ </select>
161
+
162
+ </td>
163
+ </tr>
164
+ <tr id="<?php echo esc_attr($language->slug); ?>_details" style="display:none;">
165
+ <td colspan="2" style="padding:0;">
166
+ <table class="form-table" style="background: #ffffff; margin:0; padding: 10px;"><?php
167
+ foreach ($settings as $key => $setting) {
168
+ $custom_key = 'custom['.$key.']['.esc_attr($language->slug).']'; ?>
169
+ <tr>
170
+ <th scope="row">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<?php printf('<label for="%s">%s</label>', $custom_key , $setting['label']); ?></th>
171
+ <td><?php
172
+ printf('<select name="%1$s" id="%1$s" %2$s>', $custom_key, in_array($key, array('upload', 'download')) ? $disabled : '');
173
+ foreach ($setting['options'] as $id => $title) {
174
+ $selected = isset($profile['custom'][$key][$language->slug]) && $profile['custom'][$key][$language->slug] == $id ? 'selected="selected"' : '';
175
+ echo "\n\t<option value='" . esc_attr($id) . "' $selected>" . esc_html($title) . '</option>';
176
+ } ?>
177
+ </select><?php
178
+ //if (isset($setting['description']))
179
+ //printf('<p class="description">%s</p>', $setting['description']);?>
180
+ </td>
181
+ </tr><?php
182
+ } ?>
183
+ </table>
184
+ </td>
185
+ </tr>
186
+
187
+ <?php
188
+ } ?>
189
+ </table><?php submit_button(__('Save Changes', 'wp-lingotek'), 'primary', 'submit', false); ?>
190
+
191
+ <?php
192
+ if (!empty($profile['profile']) && !in_array($profile['profile'], array('automatic', 'manual', 'disabled')) && empty($profile['usage']))
193
+ printf(
194
+ '<a href="%s" class="button" onclick = "return confirm(\'%s\');">%s</a>',
195
+ esc_url(wp_nonce_url('admin.php?page=wp-lingotek_settings&sm=profiles&lingotek_action=delete-profile&noheader=true&profile='.$profile['profile'], 'delete-profile')),
196
+ __('You are about to permanently delete this profile. Are you sure?', 'wp-lingotek'),
197
+ __('Delete', 'wp-lingotek')
198
+ );
199
+ ?> <a href="admin.php?page=wp-lingotek_settings&amp;sm=profiles" class="button"> <?php _e('Cancel', 'wp-lingotek'); ?></a>
200
+ </form>
admin/settings/view-preferences.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $setting_details = array(
4
+ 'download_post_status' => array(
5
+ 'type' => 'dropdown',
6
+ 'label' => __('Download translation status', 'wp-lingotek'),
7
+ 'description' => __('The post status for newly downloaded translations', 'wp-lingotek'),
8
+ 'values' => array(
9
+ Lingotek_Group_Post::SAME_AS_SOURCE => __('Same as source post', 'wp-lingotek'),
10
+ 'draft' => __('Draft', 'wp-lingotek'),
11
+ 'pending' => __('Pending Review', 'wp-lingotek'),
12
+ 'publish' => __('Published', 'wp-lingotek'),
13
+ //'future' => __('Scheduled', 'wp-lingotek'),
14
+ 'private' => __('Privately Published', 'wp-lingotek'),
15
+ )),
16
+ 'auto_upload_post_statuses' => array( // blacklist
17
+ 'type' => 'checkboxes',
18
+ 'label' => __('Auto upload statuses', 'wp-lingotek'),
19
+ 'description' => __('The post statuses checked above are enabled for automatic upload (when using automatic uploading translation profiles).', 'wp-lingotek'),
20
+ 'values' => array(
21
+ 'draft' => __('Draft', 'wp-lingotek'),
22
+ 'pending' => __('Pending Review', 'wp-lingotek'),
23
+ 'publish' => __('Published', 'wp-lingotek'),
24
+ 'future' => __('Scheduled', 'wp-lingotek'),
25
+ 'private' => __('Privately Published', 'wp-lingotek'),
26
+ )
27
+ )
28
+ );
29
+
30
+ $page_key = $this->plugin_slug . '_settings&sm=preferences';
31
+
32
+ if (!empty($_POST)) {
33
+ check_admin_referer($page_key, '_wpnonce_' . $page_key);
34
+ $options = array();
35
+ foreach ($setting_details as $key => $setting) {
36
+ $options[$key] = $_POST[$key];
37
+ }
38
+ update_option('lingotek_prefs', $options);
39
+
40
+ add_settings_error('lingotek_prefs', 'prefs', __('Your preferences were successfully updated.', 'wp-lingotek'), 'updated');
41
+ settings_errors();
42
+ }
43
+
44
+ $selected_options = Lingotek_Model::get_prefs();
45
+
46
+ ?>
47
+
48
+ <h3><?php _e('Preferences', 'wp-lingotek'); ?></h3>
49
+ <p class="description"><?php _e('These are your preferred settings.', 'wp-lingotek'); ?></p>
50
+
51
+
52
+ <form id="lingotek-settings" method="post" action="admin.php?page=<?php echo $page_key; ?>" class="validate">
53
+ <?php wp_nonce_field($page_key, '_wpnonce_' . $page_key); ?>
54
+
55
+ <table class="form-table"><?php foreach ($setting_details as $key => $setting) { ?>
56
+
57
+ <tr>
58
+ <th scope="row"><label for="<?php echo $key; ?>"><?php echo $setting['label'] ?></label></th>
59
+ <td>
60
+ <?php if ($setting['type'] == 'dropdown') { ?>
61
+ <select name="<?php echo $key ?>" id="<?php echo $key ?>">
62
+ <?php
63
+ foreach ($setting['values'] as $id => $title) {
64
+ echo "\n\t" . '<option value="' . esc_attr($id) . '" ' . selected($selected_options[$key], $id) . '>' . $title . '</option>';
65
+ }
66
+ ?>
67
+ </select>
68
+ <?php } else if ($setting['type'] == 'checkboxes') {
69
+ echo '<ul class="pref-statuses">';
70
+ foreach ($setting['values'] as $id => $title) {
71
+ $cb_name = $key.'['.esc_attr($id) . ']';
72
+ $checked = checked('1', (isset($selected_options[$key][$id]) && $selected_options[$key][$id]), false);
73
+ echo '<li><input type="checkbox" id="'.$cb_name.'" name="'.$cb_name.'" value="1" ' . $checked. '><label for="'.$cb_name.'">' . $title . '</label></li>';
74
+ }
75
+ echo '</ul>';
76
+ } ?>
77
+ <p class="description">
78
+ <?php echo $setting['description']; ?>
79
+ </p>
80
+ </tr><?php } ?>
81
+ </table>
82
+
83
+ <?php submit_button(__('Save Changes', 'wp-lingotek'), 'primary', 'submit', false); ?>
84
+ </form>
admin/settings/view-profiles.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ global $polylang;
3
+
4
+ $profiles = Lingotek::get_profiles();
5
+ $profiles = $this->get_profiles_usage($profiles);
6
+ $settings = $this->get_profiles_settings();
7
+
8
+ if (isset($_GET['lingotek_action']) && 'delete-profile' == $_GET['lingotek_action']) {
9
+ check_admin_referer('delete-profile');
10
+
11
+ // check again that usage empty
12
+ if (!empty($profiles[$_GET['profile']]) && empty($profiles[$_GET['profile']]['usage'])) {
13
+ unset($profiles[$_GET['profile']]);
14
+ update_option('lingotek_profiles', $profiles);
15
+ add_settings_error('lingotek_profile', 'default', __('Your translation profile was sucessfully deleted.', 'wp-lingotek'), 'updated');
16
+ set_transient('settings_errors', get_settings_errors(), 30);
17
+ wp_redirect(admin_url('admin.php?page=wp-lingotek_settings&sm=profiles&settings-updated=1'));
18
+ exit;
19
+ }
20
+ }
21
+
22
+ if (!empty($_POST)) {
23
+ check_admin_referer('lingotek-edit-profile', '_wpnonce_lingotek-edit-profile');
24
+
25
+ $defaults = get_option('lingotek_defaults');
26
+
27
+ if (empty($_POST['name']) && empty($_POST['profile'])) {
28
+ add_settings_error('lingotek_profile', 'default', __('You must provide a name for your translation profile.', 'wp-lingotek'), 'error');
29
+ }
30
+ else {
31
+ $profile = sanitize_title(empty($_POST['profile']) ? $_POST['name'] : $_POST['profile']);
32
+ $profiles[$profile]['profile'] = $profile;
33
+ if (!empty($_POST['name']))
34
+ $profiles[$profile]['name'] = strip_tags($_POST['name']);
35
+
36
+ foreach (array('upload', 'download', 'project_id', 'workflow_id') as $key) {
37
+ if (isset($_POST[$key]) && in_array($_POST[$key], array_keys($settings[$key]['options'])))
38
+ $profiles[$profile][$key] = $_POST[$key];
39
+
40
+ if (empty($_POST[$key]) || 'default' == $_POST[$key])
41
+ unset($profiles[$profile][$key]);
42
+ }
43
+
44
+ foreach ($this->pllm->get_languages_list() as $language) {
45
+ switch($_POST['targets'][$language->slug]) {
46
+ case 'custom':
47
+ foreach (array('download', 'project_id', 'workflow_id') as $key) {
48
+ if (isset($_POST['custom'][$key][$language->slug]) && in_array($_POST['custom'][$key][$language->slug], array_keys($settings[$key]['options']))) {
49
+ $profiles[$profile]['custom'][$key][$language->slug] = $_POST['custom'][$key][$language->slug];
50
+ }
51
+
52
+ if (empty($_POST['custom'][$key][$language->slug]) || 'default' == $_POST['custom'][$key][$language->slug]) {
53
+ unset($profiles[$profile]['custom'][$key][$language->slug]);
54
+ }
55
+ }
56
+
57
+ case 'disabled':
58
+ $profiles[$profile]['targets'][$language->slug] = $_POST['targets'][$language->slug];
59
+ break;
60
+
61
+ case 'default':
62
+ unset($profiles[$profile]['targets'][$language->slug]);
63
+ }
64
+ }
65
+
66
+ // hardcode default values for automatic and manual profiles as the process above emptied them
67
+ $profiles['automatic']['upload'] = $profiles['automatic']['download'] = 'automatic';
68
+ $profiles['manual']['upload'] = $profiles['manual']['download'] = 'manual';
69
+ $profiles['automatic']['name'] = 'Automatic'; $profiles['manual']['name'] = 'Manual'; $profiles['disabled']['name'] = 'Disabled';// do not localize names here
70
+
71
+ update_option('lingotek_profiles', $profiles);
72
+ add_settings_error('lingotek_profile', 'default', __('Your translation profile was sucessfully saved.', 'wp-lingotek'), 'updated');
73
+
74
+ if (isset($_POST['update_callback'])) {
75
+ $project_id = isset($profiles[$profile]['project_id']) ? $profiles[$profile]['project_id'] : $defaults['project_id'];
76
+ $client = new Lingotek_API();
77
+ if ($client->update_callback_url($project_id))
78
+ add_settings_error('lingotek_profile', 'default', __('Your callback url was successfully updated.', 'wp-lingotek'), 'updated');
79
+ }
80
+ }
81
+ settings_errors();
82
+ }
83
+
84
+ ?>
85
+ <h3><?php _e('Translation Profiles', 'wp-lingotek'); ?></h3>
86
+ <p class="description"><?php _e('Translation profiles allow you to quickly configure and re-use translation settings.', 'wp-lingotek'); ?></p><?php
87
+ $table = new Lingotek_Profiles_Table();
88
+ $table->prepare_items($profiles);
89
+ $table->display();
90
+ printf(
91
+ '<a href="%s" class="button button-primary">%s</a>',
92
+ admin_url('admin.php?page=wp-lingotek_settings&sm=edit-profile'),
93
+ __('Add New Profile', 'wp-lingotek')
94
+ );
95
+
admin/settings/view-utilities.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $page_key = $this->plugin_slug . '_settings&sm=utilities';
3
+
4
+ if (!empty($_POST)) {
5
+ check_admin_referer($page_key, '_wpnonce_' . $page_key);
6
+
7
+ // progress dialog placeholder
8
+ if (!empty($_POST['utility_disassociate'])) {
9
+ $ids = Lingotek_Utilities::get_all_document_ids();
10
+ if (!empty($ids))
11
+ printf('<div id="lingotek-progressdialog" title="%s"><div id="lingotek-progressbar"></div></div>', __('Disassociating content...', 'wp-lingotek'));
12
+ }
13
+
14
+ $utilities = array();
15
+ if(array_key_exists('utility_set_default_language', $_POST) && $_POST['utility_set_default_language'] == 'on'){
16
+ $utilities[] = 'utility_set_default_language';
17
+ }
18
+
19
+ $GLOBALS['wp_lingotek']->utilities->run_utilities($utilities);
20
+
21
+ settings_errors();
22
+ }
23
+
24
+ ?>
25
+ <h3><?php _e('Utilities', 'wp-lingotek'); ?></h3>
26
+ <p class="description"><?php _e('These utilities are designed to help you prepare and maintain your multilingual content.', 'wp-lingotek'); ?></p>
27
+
28
+ <h4><?php _e('Language', 'wp-lingotek'); ?></h4>
29
+ <form id="lingotek-utilities" method="post" action="admin.php?page=<?php echo $page_key; ?>" class="validate"><?php
30
+ wp_nonce_field($page_key, '_wpnonce_' . $page_key);
31
+
32
+ printf(
33
+ '<p><input type="checkbox" name="%1$s" id="%1$s"/><label for="%1$s">%2$s</label></p>',
34
+ 'utility_set_default_language',
35
+ __('Set <i>default language</i> as the language for all content that has not been assigned a language.', 'wp-lingotek')
36
+ );
37
+
38
+ ?>
39
+ <h4><?php _e('Disassociation', 'wp-lingotek'); ?></h4>
40
+ <?php
41
+
42
+ printf(
43
+ '<p><input type="checkbox" name="%1$s" id="%1$s" onclick="%3$s"/><label for="%1$s">%2$s</label></p>',
44
+ 'utility_disassociate',
45
+ __('Disassociate all the content from Lingotek TMS.', 'wp-lingotek'),
46
+ "
47
+ if (this.checked == false) {
48
+ document.getElementById('utility_delete_documents').checked = false;
49
+ }"
50
+ );
51
+
52
+ printf(
53
+ '<p><input type="checkbox" name="%1$s" id="%1$s" onclick="%3$s"/><label for="%1$s">%2$s</label></p>',
54
+ 'utility_delete_documents',
55
+ __('Delete all documents in Lingotek TMS.', 'wp-lingotek'),
56
+ "
57
+ if (this.checked == true) {
58
+ document.getElementById('utility_disassociate').checked = true;
59
+ }"
60
+ );
61
+
62
+ $confirm_disassociate = __('You are about to disassociate all your content from Lingotek TMS. Are you sure ?', 'wp-lingotek');
63
+ $confirm_delete = __('You are about to disassociate all your content and delete all documents in Lingotek TMS. Are you sure ?', 'wp-lingotek');
64
+
65
+ $confirm_js = "
66
+ d = document.getElementById('utility_disassociate');
67
+ dd = document.getElementById('utility_delete_documents');
68
+ if (dd.checked == true) {
69
+ return confirm('$confirm_delete');
70
+ }
71
+ if (d.checked == true) {
72
+ return confirm('$confirm_disassociate');
73
+ }";
74
+
75
+ submit_button(__('Run Utilities', 'wp-lingotek'), 'primary', 'submit', true, sprintf('onclick="%s"', $confirm_js)); ?>
76
+ </form><?php
77
+
78
+
admin/string-actions.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Lingotek_String_actions extends Lingotek_Actions {
4
+
5
+ /*
6
+ * Constructor
7
+ *
8
+ * @since 0.2
9
+ */
10
+ public function __construct() {
11
+ parent::__construct('string');
12
+
13
+ if (isset($this->pllm->options['default_lang']) && 'automatic' == Lingotek_Model::get_profile_option('upload', 'string', $this->pllm->get_language($this->pllm->options['default_lang'])))
14
+ add_action('updated_option', array(&$this, 'updated_option'));
15
+ }
16
+
17
+ /*
18
+ * get the language of strings sources
19
+ *
20
+ * @since 0.2
21
+ *
22
+ * @param string $name
23
+ * @return object
24
+ */
25
+ protected function get_language($name) {
26
+ return $this->pllm->get_language($this->pllm->options['default_lang']);;
27
+ }
28
+
29
+
30
+ /*
31
+ * displays the icon of an uploaded strings group (no link)
32
+ *
33
+ * @since 0.2
34
+ *
35
+ * @param int $id
36
+ */
37
+ public static function uploaded_icon($id) {
38
+ return self::display_icon('uploaded', '#');
39
+ }
40
+
41
+ /*
42
+ * creates an html action link
43
+ *
44
+ * @since 0.2
45
+ *
46
+ * @param array $args parameters to add to the link
47
+ * @param bool $warning whether to display an alert or not, optional, defaults to false
48
+ * @return string
49
+ */
50
+ protected function get_action_link($args, $warning = false) {
51
+ $args['page'] = 'wp-lingotek_manage';
52
+ $args['noheader'] = true;
53
+ return parent::get_action_link($args, $warning);
54
+ }
55
+
56
+ /*
57
+ * adds a row actions links
58
+ *
59
+ * @since 0.2
60
+ *
61
+ * @param string $name strings group name
62
+ * @return array
63
+ */
64
+ public function row_actions($name) {
65
+ return $this->_row_actions(array(), $name);
66
+ }
67
+
68
+ /*
69
+ * manages Lingotek actions
70
+ *
71
+ * @since 0.2
72
+ */
73
+ public function manage_actions($action) {
74
+
75
+ $redirect = remove_query_arg( array('action', 'action2'), wp_get_referer() );
76
+ if (!$redirect)
77
+ $redirect = admin_url("admin.php?page=wp-lingotek_manage&sm=strings");
78
+
79
+ switch($action) {
80
+ case 'bulk-lingotek-upload':
81
+ $ids = array();
82
+
83
+ foreach ($_REQUEST['strings'] as $id) {
84
+ // safe upload
85
+ if ($this->lgtm->can_upload('string', $id))
86
+ $ids[] = $id;
87
+ }
88
+
89
+ case 'bulk-lingotek-request':
90
+ case 'bulk-lingotek-download':
91
+ case 'bulk-lingotek-status':
92
+ case 'bulk-lingotek-delete':
93
+ if (empty($ids)) {
94
+ if (empty($_REQUEST['strings']))
95
+ return;
96
+
97
+ $ids = $_REQUEST['strings'];
98
+ }
99
+
100
+ check_admin_referer('bulk-lingotek-strings-translations');
101
+ $redirect = add_query_arg($action, 1, $redirect);
102
+ $redirect = add_query_arg('ids', implode(',', array_map('intval', $ids)), $redirect);
103
+
104
+ break;
105
+
106
+ case 'lingotek-upload':
107
+ check_admin_referer('lingotek-upload');
108
+ $this->lgtm->upload_strings($_GET['string']);
109
+ break;
110
+
111
+ default:
112
+ if (!$this->_manage_actions($action))
113
+ return; // do not redirect if this is not one of our actions
114
+
115
+ }
116
+
117
+ wp_redirect($redirect);
118
+ exit();
119
+
120
+ }
121
+
122
+ /*
123
+ * ajax response to upload documents and showing progress
124
+ *
125
+ * @since 0.2
126
+ */
127
+ public function ajax_upload() {
128
+ check_ajax_referer('lingotek_progress', '_lingotek_nonce');
129
+ $this->lgtm->upload_strings($_POST['id']);
130
+ die();
131
+ }
132
+
133
+ /*
134
+ * automatic upload of strings when an option is updated
135
+ *
136
+ * @since 0.2
137
+ */
138
+ public function updated_option() {
139
+ foreach (Lingotek_Model::get_strings() as $id) {
140
+ if ($this->lgtm->can_upload('string', $id['context']))
141
+ $this->lgtm->upload_strings($id['context']);
142
+ }
143
+ }
144
+ }
admin/strings-table.php ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if(!class_exists('WP_List_Table')){
4
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' ); // since WP 3.1
5
+ }
6
+
7
+ class Lingotek_Strings_Table extends WP_List_Table {
8
+ public $pllm, $lgtm, $string_actions;
9
+
10
+ /*
11
+ * constructor
12
+ *
13
+ * @since 0.2
14
+ */
15
+ function __construct($string_actions) {
16
+ parent::__construct(array(
17
+ 'plural' => 'lingotek-strings-translations', // do not translate (used for css class)
18
+ 'ajax' => false
19
+ ));
20
+ $this->pllm = $GLOBALS['polylang']->model;
21
+ $this->lgtm = $GLOBALS['wp_lingotek']->model;
22
+ $this->string_actions = $string_actions;
23
+ }
24
+
25
+ /*
26
+ * displays the item information in a column (default case)
27
+ *
28
+ * @since 0.2
29
+ *
30
+ * @param array $item
31
+ * @param string $column_name
32
+ * @return string
33
+ */
34
+ function column_default($item, $column_name) {
35
+ // generic case (count)
36
+ if (false === strpos($column_name, 'language_'))
37
+ return $item[$column_name];
38
+
39
+ // language column
40
+ $language = $this->pllm->get_language(substr($column_name, 9));
41
+ $document = $this->lgtm->get_group('string', $item['context']); // FIXME
42
+
43
+ // post ready for upload
44
+ if ($this->lgtm->can_upload('string', $item['context']) && $language->slug == $this->pllm->options['default_lang'])
45
+ echo $this->string_actions->upload_icon($item['context']);
46
+
47
+ // translation disabled
48
+ elseif (isset($document->source) && $document->is_disabled_target($language))
49
+ echo '<div class="lingotek-color dashicons dashicons-no"></div>';
50
+
51
+ // source post is uploaded
52
+ elseif (isset($document->source) && $document->source == $language->mo_id)
53
+ echo 'importing' == $document->status ? Lingotek_Actions::importing_icon($document) : Lingotek_String_actions::uploaded_icon($item['context']);
54
+
55
+ // translations
56
+ elseif (isset($document->translations[$language->locale]) || (isset($document->source) && 'current' == $document->status))
57
+ echo Lingotek_Actions::translation_icon($document, $language);
58
+
59
+ // no translation
60
+ else
61
+ echo '<div class="lingotek-color dashicons dashicons-no"></div>';
62
+ }
63
+
64
+ /*
65
+ * displays the checkbox in first column
66
+ *
67
+ * @since 0.2
68
+ *
69
+ * @param array $item
70
+ * @return string
71
+ */
72
+ function column_cb($item){
73
+ return sprintf('<input type="checkbox" name="strings[]" value="%d" />', esc_attr($item['row']));
74
+ }
75
+
76
+ /*
77
+ * displays the item information in the column 'group'
78
+ * displays the row actions links
79
+ *
80
+ * @since 0.2
81
+ *
82
+ * @param object $item
83
+ * @return string
84
+ */
85
+ function column_context($item) {
86
+ return $item['context'] . $this->row_actions($this->string_actions->row_actions($item['context']));
87
+ }
88
+
89
+ /*
90
+ * gets the list of columns
91
+ *
92
+ * @since 0.2
93
+ *
94
+ * @return array the list of column titles
95
+ */
96
+ function get_columns() {
97
+ $columns = array(
98
+ 'cb' => '<input type="checkbox" />', //checkbox
99
+ 'context' => __('Group', 'wp-lingotek'),
100
+ 'count' => __('Count', 'wp-lingotek'),
101
+ );
102
+
103
+ foreach ($GLOBALS['polylang']->model->get_languages_list() as $lang) {
104
+ if (!$lang->flag) {
105
+ $columns['language_' . $lang->slug] = $lang->slug;
106
+ }
107
+ else {
108
+ $columns['language_' . $lang->slug] = $lang->flag;
109
+ }
110
+ }
111
+
112
+ return $columns;
113
+ }
114
+
115
+ /*
116
+ * gets the list of sortable columns
117
+ *
118
+ * @since 0.2
119
+ *
120
+ * @return array
121
+ */
122
+ function get_sortable_columns() {
123
+ return array(
124
+ 'context' => array('context', false),
125
+ 'count' => array('count', false),
126
+ );
127
+ }
128
+
129
+ /*
130
+ * prepares the list of items ofr displaying
131
+ *
132
+ * @since 0.2
133
+ *
134
+ * @param array $data
135
+ */
136
+ function prepare_items($data = array()) {
137
+ $per_page = $this->get_items_per_page('lingotek_strings_per_page');
138
+ $this->_column_headers = array($this->get_columns(), array(), $this->get_sortable_columns());
139
+
140
+ function usort_reorder($a, $b){
141
+ $result = strcmp($a[$_REQUEST['orderby']], $b[$_REQUEST['orderby']]); // determine sort order
142
+ return (empty($_REQUEST['order']) || $_REQUEST['order'] == 'asc') ? $result : -$result; // send final sort direction to usort
143
+ };
144
+
145
+ if (!empty($_REQUEST['orderby'])) // no sort by default
146
+ usort($data, 'usort_reorder');
147
+
148
+ $total_items = count($data);
149
+ $this->items = array_slice($data, ($this->get_pagenum() - 1) * $per_page, $per_page);
150
+
151
+ $this->set_pagination_args(array(
152
+ 'total_items' => $total_items,
153
+ 'per_page' => $per_page,
154
+ 'total_pages' => ceil($total_items/$per_page)
155
+ ));
156
+ }
157
+
158
+ /*
159
+ * get the list of possible bulk actions
160
+ *
161
+ * @since 0.2
162
+ *
163
+ * @return array
164
+ */
165
+ function get_bulk_actions() {
166
+ foreach (Lingotek_String_actions::$actions as $action => $strings)
167
+ $arr['bulk-lingotek-' . $action] = $strings['action'];
168
+ return $arr;
169
+ }
170
+ }
admin/table-string.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * extends the Polylang class to disable the input fields
5
+ *
6
+ * @since 0.3
7
+ */
8
+ class Lingotek_Table_String extends PLL_Table_String {
9
+ /*
10
+ * displays the translations to edit (disabled)
11
+ *
12
+ * @since 0.3
13
+ *
14
+ * @param array $item
15
+ * @return string
16
+ */
17
+ function column_translations($item) {
18
+ $out = '';
19
+ foreach($item['translations'] as $key => $translation) {
20
+ $input_type = $item['multiline'] ?
21
+ '<textarea name="translation[%1$s][%2$s]" id="%1$s-%2$s" disabled="disabled">%4$s</textarea>' :
22
+ '<input type="text" name="translation[%1$s][%2$s]" id="%1$s-%2$s" value="%4$s" disabled="disabled" />';
23
+ $out .= sprintf('<div class="translation"><label for="%1$s-%2$s">%3$s</label>'.$input_type.'</div>'."\n",
24
+ esc_attr($key),
25
+ esc_attr($item['row']),
26
+ esc_html($this->languages[$key]),
27
+ format_to_edit($translation)); // don't interpret special chars
28
+ }
29
+ return $out;
30
+ }
31
+ }
admin/term-actions.php ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Adds row and bulk actions to categories and post tags list
5
+ * Manages actions which trigger communication with Lingotek TMS
6
+ *
7
+ * @since 0.2
8
+ */
9
+ class Lingotek_Term_actions extends Lingotek_Actions {
10
+
11
+ /*
12
+ * Constructor
13
+ *
14
+ * @since 0.2
15
+ */
16
+ public function __construct() {
17
+ parent::__construct('term');
18
+
19
+ foreach ($this->pllm->get_translated_taxonomies() as $taxonomy)
20
+ add_filter( $taxonomy . '_row_actions', array(&$this, 'term_row_actions'), 10, 2);
21
+
22
+ add_action('admin_footer-edit-tags.php', array(&$this, 'add_bulk_actions')); // FIXME admin_print_footer_scripts instead?
23
+ add_action('load-edit-tags.php', array(&$this, 'manage_actions'));
24
+ }
25
+
26
+ /*
27
+ * get the language of a term
28
+ *
29
+ * @since 0.2
30
+ *
31
+ * @param int $term_id
32
+ * @return object
33
+ */
34
+ protected function get_language($term_id) {
35
+ return $this->pllm->get_term_language($term_id);
36
+ }
37
+
38
+ /*
39
+ * displays the icon of an uploaded term with the relevant link
40
+ *
41
+ * @since 0.2
42
+ *
43
+ * @param int $id
44
+ */
45
+ public static function uploaded_icon($id) {
46
+ $post_type = isset($GLOBALS['post_type']) ? $GLOBALS['post_type'] : $_REQUEST['post_type']; // 2nd case for quick edit
47
+ $taxonomy = isset($GLOBALS['taxonomy']) ? $GLOBALS['taxonomy'] : $_REQUEST['taxonomy'];
48
+ $link = get_edit_term_link($id, $taxonomy, $post_type);
49
+ return self::display_icon('uploaded', $link);
50
+ }
51
+
52
+ /*
53
+ * adds a row action link
54
+ *
55
+ * @since 0.2
56
+ *
57
+ * @param array $actions list of action links
58
+ * @param object $term
59
+ * @return array
60
+ */
61
+ public function term_row_actions($actions, $term) {
62
+ if ($this->pllm->is_translated_taxonomy($term->taxonomy)) {
63
+ $actions = $this->_row_actions($actions, $term->term_id);
64
+
65
+ $language = $this->pllm->get_term_language($term->term_id);
66
+ if (!empty($language)) {
67
+ $profile = Lingotek_Model::get_profile($term->taxonomy, $language);
68
+ if ('disabled' == $profile['profile'])
69
+ unset($actions['lingotek-upload']);
70
+ }
71
+ }
72
+ return $actions;
73
+ }
74
+
75
+ /*
76
+ * adds actions to bulk dropdown in posts list table
77
+ *
78
+ * @since 0.1
79
+ */
80
+ public function add_bulk_actions() {
81
+ if ($this->pllm->is_translated_taxonomy($GLOBALS['taxnow']))
82
+ $this->_add_bulk_actions();
83
+ }
84
+
85
+ /*
86
+ * manages Lingotek specific actions before WordPress acts
87
+ *
88
+ * @since 0.2
89
+ */
90
+ public function manage_actions() {
91
+ global $taxnow, $post_type;
92
+
93
+ if (!$this->pllm->is_translated_taxonomy($taxnow))
94
+ return;
95
+
96
+ // get the action
97
+ $wp_list_table = _get_list_table('WP_Terms_List_Table');
98
+ $action = $wp_list_table->current_action();
99
+
100
+ if (empty($action))
101
+ return;
102
+
103
+ $redirect = remove_query_arg( array('action', 'action2', 'delete_tags'), wp_get_referer() );
104
+ if (!$redirect)
105
+ $redirect = admin_url("edit-tags.php?taxonomy=$taxnow&post_type=$post_type");
106
+
107
+ switch($action) {
108
+ case 'bulk-lingotek-upload':
109
+ if (empty($_REQUEST['delete_tags']))
110
+ return;
111
+
112
+ $term_ids = array();
113
+
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)) {
121
+ // take care to upload only one post in a translation group
122
+ $intersect = array_intersect($term_ids, $this->pllm->get_translations('term', $term_id));
123
+ if (empty($intersect)) {
124
+ $term_ids[] = $term_id;
125
+ $redirect = add_query_arg('lingotek_warning', 1, $redirect);
126
+ }
127
+ }
128
+ }
129
+
130
+ // check if translation is disabled
131
+ if (!empty($term_ids)) {
132
+ foreach ($term_ids as $key => $term_id) {
133
+ $language = $this->pllm->get_term_language($term_id);
134
+ $profile = Lingotek_Model::get_profile($taxnow, $language);
135
+ if ('disabled' == $profile['profile'])
136
+ unset($term_ids[$key]);
137
+ }
138
+ }
139
+
140
+ case 'bulk-lingotek-request':
141
+ case 'bulk-lingotek-download':
142
+ case 'bulk-lingotek-status':
143
+ case 'bulk-lingotek-delete':
144
+ if (empty($_REQUEST['delete_tags']))
145
+ return;
146
+
147
+ if (empty($term_ids))
148
+ $term_ids = array_map('intval', $_REQUEST['delete_tags']);
149
+
150
+ check_admin_referer('bulk-tags');
151
+
152
+ $redirect = add_query_arg($action, 1, $redirect);
153
+ $redirect = add_query_arg('ids', implode(',', $term_ids), $redirect);
154
+
155
+ break;
156
+
157
+ case 'lingotek-upload':
158
+ check_admin_referer('lingotek-upload');
159
+ $this->lgtm->upload_term((int) $_GET['term'], $taxnow);
160
+ break;
161
+
162
+ default:
163
+ if (!$this->_manage_actions($action))
164
+ return; // do not redirect if this is not one of our actions
165
+
166
+ }
167
+
168
+ wp_redirect($redirect);
169
+ exit();
170
+
171
+ }
172
+
173
+ /*
174
+ * ajax response to upload documents and showing progress
175
+ *
176
+ * @since 0.1
177
+ */
178
+ public function ajax_upload() {
179
+ check_ajax_referer('lingotek_progress', '_lingotek_nonce');
180
+ if (taxonomy_exists($_POST['taxonomy']))
181
+ $this->lgtm->upload_term((int) $_POST['id'], $_POST['taxonomy']);
182
+ die();
183
+ }
184
+ }
admin/tutorial/content.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <style>
2
+ .tutorial-photo-right {
3
+ width: 50%;
4
+ height: auto;
5
+ float: right;
6
+ padding-left: 3px;
7
+ }
8
+
9
+ .img-caption {
10
+ font-size: 8px;
11
+ color: #999;
12
+ font-style: italic;
13
+ padding-left: 20px;
14
+ }
15
+
16
+ th {
17
+ text-align: left;
18
+ padding-left: 10px;
19
+ }
20
+ </style>
21
+
22
+ <p><?php _e('', 'wp-lingotek') ?></p>
23
+
24
+ <div>
25
+ <h4><?php _e('1. Create content', 'wp-lingotek') ?></h4>
26
+ <p><?php _e('Whether you write a blog post, create a page for your site, or have existing posts and pages, any of your Wordpress content can be uploaded to <i>Lingotek</i>.', 'wp-lingotek') ?>
27
+ <?php _e('The examples shown below are for Pages but translation for other content types works the same way!', 'wp-lingotek') ?></p>
28
+ <img class="lingotek-bordered" src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/add-page.png'; ?>">
29
+ <p class="img-caption"><?php _e('Create a new page for translation.', 'wp-lingotek') ?></p>
30
+ </div>
31
+ <div>
32
+ <h4><?php _e('2. Upload content to Lingotek', 'wp-lingotek') ?></h4>
33
+ <p><?php _e('Your Wordpress content can be uploaded to <i>Lingotek</i> with the simple push of a button.', 'wp-lingotek') ?></p>
34
+ <img class="lingotek-bordered" src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/ready-to-upload.png'; ?>">
35
+ <p class="img-caption"><?php _e('Content has been created and is ready for upload to Lingotek.', 'wp-lingotek') ?></p>
36
+ </div>
37
+ <div>
38
+ <h4><?php _e('3. Request translations for target languages', 'wp-lingotek') ?></h4>
39
+ <p><?php _e('Request translation for a specific language by clicking on the orange plus icon, for all languages at once, or in bulk by using the <i>Bulk Actions</i> dropdown.', 'wp-lingotek') ?></p>
40
+ <img class="lingotek-bordered" src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/request-translations.png'; ?>">
41
+ <p class="img-caption"><?php _e('The source content is uploaded and ready for target languages.', 'wp-lingotek') ?></p>
42
+ </div>
43
+ <div>
44
+ <h4><?php _e('4. Translate your content', 'wp-lingotek') ?></h4>
45
+ <p><?php _e('Your content will now be translated into your selected target languages by free machine translation or, if you contract with <i>Lingotek</i>, professional translation services.', 'wp-lingotek') ?></p>
46
+ <img class="lingotek-bordered" src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/translations-underway.png'; ?>">
47
+ <p class="img-caption"><?php _e('Your translations are underway.', 'wp-lingotek') ?></p>
48
+ </div>
49
+ <div>
50
+ <h4><?php _e('5. Download translations', 'wp-lingotek') ?></h4>
51
+ <p><?php _e('Once your translations are complete they will be marked ready for download. You can download translations for all languages, each language individually, or in bulk (using the <i>Bulk Actions</i> dropdown).', 'wp-lingotek') ?></p>
52
+ <img class="lingotek-bordered" src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/translations-ready-for-download.png'; ?>">
53
+ <p class="img-caption"><?php _e('Your translations are ready for download.', 'wp-lingotek') ?></p>
54
+ </div>
55
+ <div>
56
+ <h4><?php _e('6. Your content is translated!', 'wp-lingotek') ?></h4>
57
+ <p><?php _e('The orange pencil icons indicate that your translations are finished, downloaded, and current within your Wordpress site. Clicking on any one of the pencils will direct you to the Lingotek Workbench for that specific language. Here you can make updates and changes to your translations if necessary.', 'wp-lingotek') ?></p>
58
+ <img class="lingotek-bordered" src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/translations-downloaded.png'; ?>">
59
+ <p class="img-caption"><?php _e('Your content has been translated.', 'wp-lingotek') ?></p>
60
+ </div>
61
+
62
+ <h2><?php _e('What do all the icons mean?', 'wp-lingotek') ?></h2>
63
+
64
+ <table>
65
+ <tr>
66
+ <td><span class="lingotek-color dashicons dashicons-upload"></span></td>
67
+ <th><?php _e('Upload Source', 'wp-lingotek') ?></th>
68
+ <td><?php _e('There is content ready to be uploaded to Lingotek.', 'wp-lingotek') ?></td>
69
+ </tr>
70
+ <tr>
71
+ <td><span class="lingotek-color dashicons dashicons-clock"></span></td>
72
+ <th><?php _e('In Progress', 'wp-lingotek') ?></th>
73
+ <td><?php _e('Content is importing to Lingotek or a target language is being added to source content.', 'wp-lingotek') ?></td>
74
+ </tr>
75
+ <tr>
76
+ <td><span class="lingotek-color dashicons dashicons-yes"></span></td>
77
+ <th><?php _e('Source Uploaded', 'wp-lingotek') ?></th>
78
+ <td><?php _e('The source content has been uploaded to Lingotek.', 'wp-lingotek') ?></td>
79
+ </tr>
80
+ <tr>
81
+ <td><span class="lingotek-color dashicons dashicons-plus"></span></td>
82
+ <th><?php _e('Request Translation', 'wp-lingotek') ?></th>
83
+ <td><?php _e('Request a translation of the source content. (Add a target language)', 'wp-lingotek') ?></td>
84
+ </tr>
85
+ <tr>
86
+ <td><span class="lingotek-color dashicons dashicons-download"></span></td>
87
+ <th><?php _e('Download Translation', 'wp-lingotek') ?></th>
88
+ <td><?php _e('Download the translated content to Wordpress.', 'wp-lingotek') ?></td>
89
+ </tr>
90
+ <tr>
91
+ <td><span class="lingotek-color dashicons dashicons-edit"></span></td>
92
+ <th><?php _e('Translation Current', 'wp-lingotek') ?></th>
93
+ <td><?php _e('The translation is complete. (Clicking on this icon will allow you to edit translations in the Lingotek Workbench)', 'wp-lingotek') ?></td>
94
+ </tr>
95
+ </table>
admin/tutorial/credits.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $people = array(
4
+ 'fred'=>array(
5
+ 'name'=>'Frédéric Demarle',
6
+ 'title'=>'Lead Polylang Developer',
7
+ 'image_url'=>'https://www.gravatar.com/avatar/132157ff7a533c8e9a272795a1b5c2b9',
8
+ 'url'=>'https://profiles.wordpress.org/chouby'),
9
+ 'matt'=>array(
10
+ 'name'=>'Matt Smith',
11
+ 'title'=>'Lead Developer',
12
+ 'image_url'=>'https://www.gravatar.com/avatar/d79b46c94a52b4679c308986ef05eac2',
13
+ 'url'=>'https://profiles.wordpress.org/smithworx'),
14
+ 'brian'=>array(
15
+ 'name'=>'Brian Isle',
16
+ 'title'=>'Quality Assurance',
17
+ 'image_url'=>'https://www.gravatar.com/avatar/5f43658c382412d8f120cb5595d9bf03',
18
+ 'url'=>'https://profiles.wordpress.org/bisle'),
19
+ 'edward'=>array(
20
+ 'name'=>'Edward Richards',
21
+ 'title'=>'Developer',
22
+ 'image_url'=>'https://www.gravatar.com/avatar/a0ab415173b16d2ac476077d587bea96',
23
+ 'url'=>'https://profiles.wordpress.org/erichie'),
24
+ 'calvin'=>array(
25
+ 'name'=>'Calvin Scharffs',
26
+ 'title'=>'Marketing Guru',
27
+ 'image_url'=>'https://www.gravatar.com/avatar/d18e8bf783f63bf893e143cf04e0034d',
28
+ 'url'=>'https://profiles.wordpress.org/cscharffs'),
29
+ 'brad'=>array(
30
+ 'name'=>'Brad Ross',
31
+ 'title'=>'Product Management',
32
+ 'image_url'=>'https://www.gravatar.com/avatar/477601d2c0c8c8dd31c021e3bae3841c',
33
+ 'url'=>'https://profiles.wordpress.org/bradross12/'),
34
+ 'laura'=>array(
35
+ 'name'=>'Laura White',
36
+ 'title'=>'Tech Writer',
37
+ 'image_url'=>'https://www.gravatar.com/avatar/56c44e12c3431aca766d06c6019201ff',
38
+ 'url'=>'https://profiles.wordpress.org/laurakaysc'),
39
+ );
40
+
41
+ shuffle($people);
42
+
43
+ ?>
44
+
45
+ <p class="about-description"><?php _e('The Lingotek plugin for WordPress is created with love.', 'wp-lingotek'); ?></p>
46
+
47
+ <h4 class="wp-people-group"><?php _e('Team', 'wp-lingotek'); ?></h4>
48
+
49
+ <ul class="wp-people-group">
50
+ <?php
51
+
52
+ foreach($people as $person_key=>$person){
53
+ printf('<li class="wp-person" id="wp-person-%s">
54
+ <a href="%s"><img src="%s?s=60&d=mm&r=G" srcset="%s?s=120&d=mm&r=G 2x" class="gravatar" alt="%s"></a>
55
+ <a class="web" href="%s">%s</a>
56
+ <span class="title">%s</span>
57
+ </li>',$person_key,$person['url'],$person['image_url'],$person['image_url'],$person['name'],$person['url'],$person['name'],$person['title']);
58
+ }
59
+
60
+ ?>
61
+ </ul>
admin/tutorial/faq.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <style>
2
+ .tutorial-photo-right {
3
+ width: 50%;
4
+ height: auto;
5
+ float: right;
6
+ padding-left: 3px;
7
+ }
8
+ </style>
9
+
10
+ <p class="about-description"><?php _e('Questions and answers...', 'wp-lingotek'); ?></p>
11
+
12
+ <b><?php _e('How does this really work?', 'wp-lingotek') ?></b>
13
+ <p><?php _e('<i>Polylang</i> puts in the framework to make WordPress multilingual and <i>Lingotek</i> leverages that framework to provide localization through machine translation and professional translation services.', 'wp-lingotek') ?></p>
14
+
15
+ <b><?php _e('What does the Lingotek plugin do?', 'wp-lingotek') ?></b>
16
+ <p><?php _e('<i>Lingotek</i> has teamed up with <i>Polylang</i> to offer a simple way to make your WordPress site truly multilingual. Manage all your multilingual content in the same site. No need to have a different site for each language!', 'wp-lingotek') ?></p>
17
+
18
+ <b><?php _e('How can I add a language?', 'wp-lingotek') ?></b>
19
+ <p><?php _e('On the <i>translation dashboard</i>, click on the <i>Translate my site into...</i> textbox and choose from the list or start typing to quickly find a language.', 'wp-lingotek') ?></p>
20
+
21
+ <b><?php _e('How can I remove a language?', 'wp-lingotek') ?></b>
22
+ <p>
23
+ <?php _e('On the <i>translation dashboard</i>, click on the blue check mark for the language you would like to remove.', 'wp-lingotek') ?>
24
+ <i><?php _e('Note: If you have translated content in a language you will not be able to remove that language until that content has been deleted.', 'wp-lingotek') ?></i>
25
+ </p>
26
+
27
+ <b><?php _e("Can I use my own translation agency with Lingotek?", 'wp-lingotek') ?></b>
28
+ <p><?php _e( "Use your own translation agency or tap into Lingotek's network of more than 5,000+ in-country translators. Content transfer is fully automated between WordPress and Lingotek. You'll have full visibility into the translation process every step of the way. And once the translations are completed, they'll automatically download and publish to your website according to the preferences you've set." , 'wp-lingotek'); ?></p>
29
+
30
+ <b><?php _e('How can I check the overall translation progress of my site?', 'wp-lingotek') ?></b>
31
+ <p><?php _e('On the <i>translation dashboard</i>, the bars under <i>Completed Percentage</i> show the translation progress of content for each language. You can filter by content type or show progress for all content.', 'wp-lingotek') ?></p>
32
+ <ul>
33
+ <li>
34
+ <b><?php _e('Why are there two different shades of blue in the progress bar?', 'wp-lingotek') ?></b>
35
+ <p><?php _e('The <i>translation dashboard</i> not only shows how much of your content is translated, but also indicates the language that your source content was authored in.', 'wp-lingotek') ?></p>
36
+ </li>
37
+ <ul style="list-style-type:disc; padding-left:30px">
38
+ <li><?php _e('<i>Dark Blue:</i> Indicates that this is a source language that the content was authored in.', 'wp-lingotek') ?></li>
39
+ <li><?php _e('<i>Light Blue:</i> Indicates that this is a target language that the content was translated into.', 'wp-lingotek') ?></li>
40
+ </ul>
41
+ </ul>
42
+
43
+ <b><?php _e('What happens when I <i>Disassociate Translations</i>?', 'wp-lingotek') ?></b>
44
+ <p><?php _e('When content is disassociated the connection between WordPress and <i>Lingotek</i> safely disconnected from Lingotek, so that translations can be solely managed inside of WordPress.', 'wp-lingotek') ?></p>
45
+
46
+ <b><?php _e("How do I translate strings within widgets or general WordPress settings (e.g., Site Title, Tagline)?", 'wp-lingotek') ?></b>
47
+ <p><?php printf(__( "Groups of strings can be sent for translation and managed using the <a href='%s'>String Groups</a> page. Individual strings may be viewed on the <a href='%s'>Strings</a> page." , 'wp-lingotek'),'admin.php?page=wp-lingotek_manage&sm=string-groups','admin.php?page=wp-lingotek_manage&sm=strings'); ?></p>
48
+
49
+
50
+ <div class="changelog feature-section two-col">
51
+ <div>
52
+ <img class="workbench-image" src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/workbench-full.png'; ?>">
53
+ <h3><?php _e( 'Lingotek Workbench', 'wp-lingotek'); ?></h3>
54
+ <p><?php _e( 'Lingotek Translation is the only WordPress plugin to integrate a Translation Management System directly into WordPress, thus allowing the WordPress community to use professional-grade translation technologies (e.g. machine translation, translation memory, CAT tool) without ever having to leave the comfort of the WordPress environment.', 'wp-lingotek'); ?></p>
55
+ </div>
56
+ <div class="last-feature">
57
+ <a href="admin.php?page=wp-lingotek"><img src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/dashboard.png'; ?>"></a>
58
+ <h3><?php _e( 'Translation Dashboard', 'wp-lingotek'); ?></h3>
59
+ <p><?php _e( "The <i>translation dashboard</i> allows you to view your site languages, add and remove new languages, check the overall translation progress of your site, see site analytics, and connect with Lingotek support.", 'wp-lingotek'); ?></p>
60
+ </div>
61
+ </div>
62
+
63
+
64
+
admin/tutorial/features.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="changelog feature-section three-col">
3
+ <div>
4
+ <a href="admin.php?page=wp-lingotek"><img src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/add-languages.png'; ?>"></a>
5
+ <h3><?php _e( 'Translate into new languages', 'wp-lingotek' ); ?></h3>
6
+ <p><?php printf( __( 'Easily translate your site into new languages by adding the desired language to your site. Lingotek allows you to quickly add any of the most frequently used locales using the <a href="%s" title="Translation &gt; Dashboard">Translation Dashboard</a>.', 'wp-lingotek' ), 'admin.php?page=wp-lingotek' ); ?></p>
7
+ </div>
8
+ <div>
9
+ <img class="lingotek-bordered" src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/automatic-translation.gif'; ?>">
10
+ <h3><?php _e( 'Automatically translate content', 'wp-lingotek' ); ?></h3>
11
+ <p><?php _e( "Machine translation is an excellent option if you're on a tight budget, looking for near-instant results, and are okay with less-than-perfect quality. The plugin allows you to quickly and automatically translate your site (the cost is covered by Lingotek for up to 100,000 characters).", 'wp-lingotek'); ?></p>
12
+ </div>
13
+ <div class="last-feature">
14
+ <img src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/polylang-compatible.png'; ?>">
15
+ <h3><?php _e( 'Fully compatible with Polylang', 'wp-lingotek' ); ?></h3>
16
+ <p><?php printf( __( 'Polylang and Lingotek work together seamlessly. Continue to use Polylang for content that you want to translate inside WordPress, while sending other content to be translated by Lingotek. The <b style="color: %s">orange</b> icons indicate Lingotek statuses/actions while the <b style="color: %s">blue</b> icons continue to represent Polylang actions.', 'wp-lingotek' ), 'darkorange', '#0473a8' ); ?></p>
17
+ </div>
18
+
19
+ </div>
20
+
21
+
22
+ <div class="changelog feature-section three-col">
23
+ <div>
24
+ <a href="admin.php?page=wp-lingotek_settings&amp;sm=profiles"><img src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/translation-profiles.png'; ?>"></a>
25
+ <h3><?php _e( 'Use translation profiles', 'wp-lingotek'); ?></h3>
26
+ <p><?php _e( 'One of the most time-consuming activities of any multilingual web-site project is managing the ongoing flow of changes and additions to site content and configurations. Translation profiles were created to allow you to create and save and re-use your translation settings.', 'wp-lingotek'); ?></p>
27
+ </div>
28
+ <div>
29
+ <a href="admin.php?page=wp-lingotek_settings&amp;sm=content"><img src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/content-types.png'; ?>"></a>
30
+ <h3><?php _e( 'Content type profiles', 'wp-lingotek'); ?></h3>
31
+ <p><?php _e( 'Content type profiles. Manually choosing which content to upload and download is rarely what a content administrator wants to do, and automating the upload of every change is not workable because there are various types of content. Each type of translatable content can be assigned to a customizable profile. For example, by default, we like to have Posts use an <i>Automatic</i> profile so that content will automatically be uploaded for translation and the resulting translations automatically be downloaded back into WordPress.', 'wp-lingotek'); ?></p>
32
+ </div>
33
+ <div class="last-feature">
34
+ <!-- <img style="" class="lingotek-bordered" src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/pro-translation.png'; ?>"> -->
35
+ <img src="<?php echo LINGOTEK_URL . '/admin/tutorial/img/professional-translation.png'; ?>">
36
+ <h3><?php _e( 'Request professional translation', 'wp-lingotek'); ?></h3>
37
+ <p><?php _e( "Use your own translation agency or tap into Lingotek's network of more than 5,000+ in-country translators. Content transfer is fully automated between WordPress and Lingotek. You'll have full visibility into the translation process every step of the way. And once the translations are completed, they'll automatically download and publish to your website according to the preferences you've set.", 'wp-lingotek'); ?></p>
38
+ </div>
39
+ </div>
admin/tutorial/img/add-languages.png ADDED
Binary file
admin/tutorial/img/add-page.png ADDED
Binary file
admin/tutorial/img/add-page2.png ADDED
Binary file
admin/tutorial/img/automatic-translation.gif ADDED
Binary file
admin/tutorial/img/automatic-translation.png ADDED
Binary file
admin/tutorial/img/check-status.png ADDED
Binary file
admin/tutorial/img/content-types.png ADDED
Binary file
admin/tutorial/img/dashboard.png ADDED
Binary file
admin/tutorial/img/polylang-compatible.png ADDED
Binary file
admin/tutorial/img/pro-translation.png ADDED
Binary file
admin/tutorial/img/professional-translation.png ADDED
Binary file
admin/tutorial/img/ready-to-upload.png ADDED
Binary file
admin/tutorial/img/request-translations.png ADDED
Binary file
admin/tutorial/img/translation-profiles.png ADDED
Binary file
admin/tutorial/img/translations-downloaded.png ADDED
Binary file
admin/tutorial/img/translations-ready-for-download.png ADDED
Binary file
admin/tutorial/img/translations-underway.png ADDED
Binary file
admin/tutorial/img/workbench-full.png ADDED
Binary file
admin/tutorial/img/workbench.png ADDED
Binary file
admin/utilities.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * A class to manage utilities
5
+ *
6
+ * @since 0.2
7
+ */
8
+ class Lingotek_Utilities {
9
+ public $pllm, $lgtm; // Polylang and Lingotek models
10
+
11
+ /*
12
+ * Constructor
13
+ *
14
+ * @since 0.2
15
+ */
16
+ public function __construct() {
17
+ $this->pllm = $GLOBALS['polylang']->model;
18
+ $this->lgtm = $GLOBALS['wp_lingotek']->model;
19
+
20
+ add_action('admin_enqueue_scripts', array(&$this, 'admin_enqueue_scripts'));
21
+ add_action('wp_ajax_lingotek_progress_disassociate' , array(&$this, 'ajax_utility_disassociate'));
22
+ add_action('wp_ajax_lingotek_progress_disassociate_and_delete' , array(&$this, 'ajax_utility_disassociate'));
23
+ }
24
+
25
+ /*
26
+ * run an array of utilities
27
+ *
28
+ * @since 0.2
29
+ *
30
+ * @param array $utilities array of utility names to run
31
+ * @return array
32
+ */
33
+ public function run_utilities($utilities){
34
+ $results = array();
35
+ if(!empty($utilities) && is_array($utilities)){
36
+ foreach($utilities as $utility_name) {
37
+ $results[] = $this->run_utility($utility_name);
38
+ }
39
+ }
40
+ return $results;
41
+ }
42
+
43
+ /*
44
+ * run a specified utility by name
45
+ *
46
+ * @since 0.2
47
+ *
48
+ * @param string $utilty_name
49
+ * @return boolean $result
50
+ */
51
+ public function run_utility($utility_name) {
52
+ $result = 0;
53
+ switch ($utility_name) {
54
+ case "set_default_language":
55
+ case "utility_set_default_language":
56
+ $result = $this->utility_set_default_language();
57
+ break;
58
+ default:
59
+ break;
60
+ }
61
+ return $result;
62
+ }
63
+
64
+ // Utilities
65
+
66
+ /*
67
+ * fills existing posts & terms with default language
68
+ *
69
+ * @since 0.2
70
+ */
71
+ public function utility_set_default_language() {
72
+ if ($nolang = $this->pllm->get_objects_with_no_lang()) {
73
+ if (!empty($nolang['posts']))
74
+ $this->pllm->set_language_in_mass('post', $nolang['posts'], $this->pllm->options['default_lang']);
75
+ if (!empty($nolang['terms']))
76
+ $this->pllm->set_language_in_mass('term', $nolang['terms'], $this->pllm->options['default_lang']);
77
+ }
78
+ add_settings_error('lingotek_utilities', 'utilities', __('The language update utility ran successfully.', 'wp-lingotek'), 'updated');
79
+ return 0;
80
+ }
81
+
82
+ /*
83
+ * get all (associated) Lingotek document ids
84
+ *
85
+ * @since 0.2
86
+ *
87
+ * @return array
88
+ */
89
+ static public function get_all_document_ids() {
90
+ $terms = get_terms(array('post_translations', 'term_translations'));
91
+ foreach ($terms as $term) {
92
+ $desc_arr = unserialize($term->description);
93
+ if (!empty($desc_arr['lingotek']))
94
+ $ids[] = $term->slug;
95
+ }
96
+
97
+ return empty($ids) ? array() : $ids;
98
+ }
99
+
100
+ /*
101
+ * outputs javascript data for progress.js
102
+ *
103
+ * @since 0.2
104
+ */
105
+ public function admin_enqueue_scripts() {
106
+ if (!empty($_POST['utility_disassociate'])) {
107
+ $ids = self::get_all_document_ids();
108
+ if (!empty($ids)) {
109
+ wp_localize_script('lingotek_progress', 'lingotek_data', array(
110
+ 'action' => 'disassociate' . ('on' == $_POST['utility_delete_documents'] ? '_and_delete' : ''),
111
+ 'taxonomy' => '',
112
+ 'sendback' => wp_get_referer(),
113
+ 'ids' => $ids, // Lingotek document ids
114
+ 'warning' => '',
115
+ 'nonce' => wp_create_nonce('lingotek_progress')
116
+ ));
117
+ }
118
+ }
119
+ }
120
+
121
+ /*
122
+ * ajax response to disassociate documents and showing progress
123
+ *
124
+ * @since 0.2
125
+ */
126
+ public function ajax_utility_disassociate() {
127
+ check_ajax_referer('lingotek_progress', '_lingotek_nonce');
128
+ if ($group = $this->lgtm->get_group_by_id($_POST['id']))
129
+ $group->disassociate('lingotek_progress_disassociate_and_delete' == $_POST['action']);
130
+ die();
131
+ }
132
+ }
admin/view-dashboard.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap">
2
+ <h2><?php _e('Dashboard', 'wp-lingotek'); ?></h2>
3
+ <script>
4
+ var cms_data = <?php echo json_encode($cms_data); ?>
5
+ </script>
6
+ <link rel="stylesheet" href="http://gmc.lingotek.com/v2/styles/ltk.css">
7
+ <script src="http://gmc.lingotek.com/v2/ltk.min.js"></script>
8
+ <div ltk-dashboard ng-app="LingotekApp" style="margin-top: -15px;"></div>
9
+ </div>
admin/view-manage.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap">
2
+ <h2><?php _e('Manage', 'wp-lingotek'); ?></h2>
3
+
4
+ <?php
5
+
6
+ $menu_items = array(
7
+ 'string-groups' => __('String Groups', 'wp-lingotek'),
8
+ 'strings' => __('Strings', 'wp-lingotek'),
9
+ );
10
+
11
+ ?>
12
+
13
+ <h3 class="nav-tab-wrapper">
14
+ <?php
15
+ $menu_item_index = 0;
16
+ foreach ($menu_items as $menu_item_key => $menu_item_label) {
17
+ $use_as_default = ($menu_item_index === 0 && !isset($_GET['sm'])) ? TRUE : FALSE;
18
+ ?>
19
+
20
+ <a class="nav-tab <?php if ($use_as_default || (isset($_GET['sm']) && $_GET['sm'] == $menu_item_key)): ?> nav-tab-active<?php endif; ?>"
21
+ href="admin.php?page=<?php echo $_GET['page']; ?>&amp;sm=<?php echo $menu_item_key; ?>"><?php echo $menu_item_label; ?></a>
22
+ <?php
23
+ $menu_item_index++;
24
+ }
25
+ ?>
26
+ </h3>
27
+
28
+ <?php
29
+ settings_errors();
30
+ $submenu = isset($_GET['sm']) ? $_GET['sm'] : current(array_keys($menu_items));
31
+ $dir = dirname(__FILE__) . '/manage/';
32
+ $filename = $dir . 'view-' . $submenu . ".php";
33
+ if (file_exists($filename))
34
+ include $filename;
35
+ else
36
+ echo "TO-DO: create <i>" . 'manage/view-' . $submenu . ".php</i>";
37
+ ?>
38
+
39
+ </div>
admin/view-network.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $sites = wp_get_sites();
3
+ $site_data = array();
4
+
5
+ foreach ($sites as $site)
6
+ {
7
+ switch_to_blog($site['blog_id']);
8
+ $details = get_blog_details($site['blog_id'])->blogname;
9
+ $temp = array("blog_id" => $site['blog_id'], "blogname" => $details);
10
+ array_push($site_data, $temp);
11
+ $ltk = new Lingotek();
12
+ $ltk->admin_init();
13
+ restore_current_blog();
14
+ }
15
+
16
+ if (!empty($_POST)) {
17
+ $source_site = $_POST['source'];
18
+
19
+ if (isset($_POST['destination'])) {
20
+ $destination_site = $_POST['destination'];
21
+
22
+ foreach ($destination_site as $destination) {
23
+
24
+ if (!empty($_POST['settings'])) {
25
+ $selected_settings = $_POST['settings'];
26
+
27
+ foreach ($selected_settings as $setting) {
28
+ //Updates account options for access token and the base url to connect to Lingotek
29
+ if ($setting == 'token') {
30
+ $lingotek_option = 'lingotek_' . $setting;
31
+
32
+ $source_options = get_blog_option($source_site, $lingotek_option);
33
+ update_blog_option($destination, $lingotek_option, $source_options);
34
+
35
+ $source_options = get_blog_option($source_site, 'lingotek_base_url');
36
+ update_blog_option($destination, 'lingotek_base_url', $source_options);
37
+ }
38
+
39
+ //Updates the chosen option
40
+ $lingotek_option = 'lingotek_' . $setting;
41
+
42
+ $source_options = get_blog_option($source_site, $lingotek_option);
43
+ update_blog_option($destination, $lingotek_option, $source_options);
44
+ }
45
+ }
46
+
47
+ //Creates a new project based on the name of the selected site
48
+ if (isset($_POST['new_project'])) {
49
+ $options = get_blog_option($destination, 'lingotek_defaults');
50
+ $client = new Lingotek_API();
51
+ $title = htmlspecialchars_decode(get_blog_details($destination)->blogname, ENT_QUOTES);
52
+
53
+ if ($new_id = $client->create_project($title, $community_id = get_blog_option($destination, 'lingotek_community'))) {
54
+ $options['project_id'] = $new_id;
55
+ // Adds correct callback URL for new project
56
+ $args = array('callback_url' => add_query_arg('lingotek', 1, get_blog_details($destination)->siteurl));
57
+ $response = $client->patch($client->get_api_url() . '/project/' . $new_id, $args);
58
+ update_blog_option($destination, 'lingotek_defaults', $options);
59
+ }
60
+ }
61
+
62
+ if (isset($_POST['preferences'])) {
63
+ switch_to_blog($source_site);
64
+ $preferences = Lingotek_Model::get_prefs();
65
+ update_blog_option($destination, 'lingotek_prefs', $preferences);
66
+ restore_current_blog();
67
+ }
68
+
69
+ if (isset($_POST['utility_set_default_language'])) {
70
+ switch_to_blog($destination);
71
+ $GLOBALS['wp_lingotek']->utilities->run_utility('utility_set_default_language');
72
+ restore_current_blog();
73
+ }
74
+
75
+ }
76
+
77
+ if (isset($_POST['utility_set_default_language'])) {
78
+ add_settings_error('network', 'utilities', __('The language utility ran successfully.', 'wp-lingotek'), 'updated');
79
+ }
80
+ add_settings_error('network', 'destination', __('Your chosen settings have updated successfully for all selected sites.', 'wp-lingotek'), 'updated');
81
+ }
82
+ else {
83
+ add_settings_error('network', 'destination', __('Please choose at least one destination site.', 'wp-lingotek'), 'error');
84
+ }
85
+
86
+ //Refreshes community resources so that the defaults are set when you visit the sites translation settings
87
+ if (isset($_POST['new_project']) && isset($_POST['destination'])) {
88
+ $this->set_community_resources(get_option('lingotek_community'));
89
+ foreach ($destination_site as $destination) {
90
+ $source_options = get_option('lingotek_community_resources');
91
+ update_blog_option($destination, 'lingotek_community_resources', $source_options);
92
+ }
93
+ $num = count($destination_site);
94
+ if ($num > 1) {
95
+ add_settings_error('network', 'projects', __('Your new projects were successfully created.', 'wp-lingotek'), 'updated');
96
+ }
97
+ else {
98
+ add_settings_error('network', 'projects', __('Your new project was successfully created.', 'wp-lingotek'), 'updated');
99
+ }
100
+ }
101
+ }
102
+ settings_errors('network');
103
+ ?>
104
+
105
+ <style>
106
+ input[type=checkbox] {
107
+ margin: 0px 7px;
108
+ }
109
+ </style>
110
+
111
+ <div class="wrap">
112
+ <h2><?php _e('Lingotek Network Settings', 'wp-lingotek'); ?></h2>
113
+ <p><?php _e('Copy Lingotek settings from the source site to multiple sites', 'wp-lingotek'); ?></p>
114
+
115
+ <form id="network-settings" method="post" action="admin.php?page=wp-lingotek_network" class="validate" onsubmit="return confirm('Are you sure you want to submit this request? It will overwrite any current settings you have for the destination sites.');">
116
+
117
+ <table class="form-table">
118
+ <tr>
119
+ <th><?php echo _e('Source Site', 'wp-lingotek'); ?></th>
120
+ <td>
121
+ <select name="source" id="source"><?php foreach ($site_data as $site) {
122
+ echo "\n\t<option value='" . esc_attr($site['blog_id']) . "'>" . $site['blogname'] . '</option>';
123
+ } ?>
124
+ </select>
125
+ </td>
126
+ </tr>
127
+ <tr>
128
+ <th><?php echo _e('Destination Site', 'wp-lingotek'); ?></th>
129
+ <td>
130
+ <select multiple="multiple" name="destination[]" id="destination"><?php foreach ($site_data as $site) {
131
+ echo "\n\t<option value='" . esc_attr($site['blog_id']) . "'>" . $site['blogname'] . '</option>';
132
+ } ?>
133
+ </select>
134
+ </td>
135
+ </tr>
136
+ <tr>
137
+ <th><?php echo _e('Settings to copy', 'wp-lingotek'); ?></th>
138
+ <td>
139
+ <input checked type="checkbox" id="account" name="settings[]" value="token"><label for="account"><?php echo _e('Account', 'wp-lingotek'); ?></label>
140
+ <input checked type="checkbox" id="community" name="settings[]" value="community"><label for="community"><?php echo _e('Community', 'wp-lingotek'); ?></label>
141
+ <input checked type="checkbox" id="defaults" name="settings[]" value="defaults"><label for="defaults"><?php echo _e('Defaults', 'wp-lingotek'); ?></label>
142
+ <input checked type="checkbox" id="resources" name="settings[]" value="community_resources"><label for="resources"><?php echo _e('Resources', 'wp-lingotek'); ?></label>
143
+ <input checked type="checkbox" id="profiles" name="settings[]" value="profiles"><label for="profiles"><?php echo _e('Profiles', 'wp-lingotek'); ?></label>
144
+ <input checked type="checkbox" id="content_types" name="settings[]" value="content_type"><label for="content_types"><?php echo _e('Content Types', 'wp-lingotek'); ?></label>
145
+ <input checked type="checkbox" id="preferences" name="preferences" value="preferences"><label for="preferences"><?php echo _e('Preferences', 'wp-lingotek'); ?></label>
146
+ </td>
147
+ </tr>
148
+ <tr>
149
+ <th><?php echo _e('New Project', 'wp-lingotek'); ?></th>
150
+ <td>
151
+ <input checked type="checkbox" name="new_project" id="new_project" ><label for="new_project"><?php echo _e('Create a new project using the name of the selected site (Recommended for a newly created site)', 'wp-lingotek'); ?></label>
152
+ </td>
153
+ </tr>
154
+ <tr>
155
+ <th><?php echo _e('Language', 'wp-lingotek'); ?></th>
156
+ <td>
157
+ <input checked type="checkbox" name="utility_set_default_language" id="utility_set_default_language" ><label for="utility_set_default_language"><?php echo _e('Set <i>default language</i> as the language for all existing content that has not been assigned a language.', 'wp-lingotek'); ?></label>
158
+ </td>
159
+ </tr>
160
+ </table>
161
+
162
+ <p>
163
+ <?php submit_button(__('Update Options', 'wp-lingotek'), 'primary', 'submit', false); ?>
164
+ </p>
165
+
166
+ </form>
167
+ </div>
admin/view-tutorial.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $menu_items = array(
4
+ 'features' => __("Features", 'wp-lingotek'),
5
+ 'content' => __('Tutorial', 'wp-lingotek'),
6
+ 'faq' => __('FAQ', 'wp-lingotek'),
7
+ 'credits' => __('Credits', 'wp-lingotek'),
8
+ );
9
+
10
+ ?>
11
+
12
+
13
+ <div class="wrap about-wrap">
14
+
15
+ <h1><?php printf( __( 'Welcome to Lingotek' , 'wp-lingotek') ); ?></h1>
16
+
17
+ <div class="about-text"><?php printf( __( 'Thank you for updating! Lingotek offers convenient cloud-based localization and translation.' , 'wp-lingotek'), LINGOTEK_VERSION ); ?></div>
18
+
19
+
20
+ <div class="wp-badge" style="background: url(<?php echo LINGOTEK_URL ?>/img/lingotek-chevrons-blue.png) center 24px/85px 80px no-repeat #fff; color: #666;"><?php printf( __( 'Version %s' , 'wp-lingotek'), LINGOTEK_VERSION ); ?></div>
21
+
22
+ <h2 class="nav-tab-wrapper">
23
+ <?php
24
+ $menu_item_index = 0;
25
+ foreach ($menu_items as $menu_item_key => $menu_item_label) {
26
+ $use_as_default = ($menu_item_index === 0 && !isset($_GET['sm'])) ? TRUE : FALSE;
27
+ ?>
28
+
29
+ <a class="nav-tab <?php if ($use_as_default || (isset($_GET['sm']) && $_GET['sm'] == $menu_item_key)): ?> nav-tab-active<?php endif; ?>"
30
+ href="admin.php?page=<?php echo $_GET['page']; ?>&sm=<?php echo $menu_item_key; ?>"><?php echo $menu_item_label; ?></a>
31
+ <?php
32
+ $menu_item_index++;
33
+ }
34
+ ?>
35
+ </h2>
36
+
37
+
38
+ <?php
39
+ settings_errors();
40
+ $submenu = isset($_GET['sm']) ? $_GET['sm'] : current(array_keys($menu_items));
41
+ $dir = dirname(__FILE__) . '/tutorial/';
42
+ $filename = $dir . $submenu . ".php";
43
+ if (file_exists($filename))
44
+ include $filename;
45
+ else
46
+ echo "TO-DO: create <i>" . 'tutorial/' . $submenu . ".php</i>";
47
+ ?>
48
+
49
+ </div>
css/admin.css ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* progress dialog */
2
+ #lingotek-progressbar {
3
+ margin: 20px;
4
+ height: 2em;
5
+ background-color: #dddddd;
6
+ }
7
+
8
+ #lingotek-progressbar .ui-progressbar-value {
9
+ margin: -1px;
10
+ height: 100%;
11
+ background-color: #0074a2;
12
+ }
13
+
14
+ #lingotek-progressdialog .ui-dialog-titlebar-close {
15
+ display: none;
16
+ }
17
+
18
+ /* icons color */
19
+ .lingotek-color {
20
+ color: #ff7b12; /* lingotek color */
21
+ }
22
+ a.lingotek-color {
23
+ color: #ff7b12; /* lingotek color */
24
+ }
25
+
26
+ a.lingotek-color:hover {
27
+ color: #ffab42; /* lighter orange */
28
+ }
29
+
30
+ a.dashicons {
31
+ cursor: pointer;
32
+ }
33
+
34
+ /* content type configuration */
35
+ .lingotek-content .column-name, .lingotek-content .column-profile {
36
+ width: 25%;
37
+ }
38
+
39
+ .sources-name {
40
+ margin: 0 0 0 20px;
41
+ }
42
+
43
+ .sources-profile {
44
+ margin: 0;
45
+ }
46
+
47
+ .content-type-name, .content-type-profile {
48
+ margin-bottom: 6px;
49
+ }
50
+
51
+ .content-type-name, .sources-name li, .lingotek-content .dashicons {
52
+ line-height: 28px;
53
+ height: 28px;
54
+ padding: 2px;
55
+ vertical-align: middle;
56
+ }
57
+
58
+ .content-type-fields li {
59
+ display: inline-block;
60
+ width: 150px;
61
+ }
62
+
63
+ .pref-statuses li {
64
+ display: inline-block;
65
+ padding-right: 15px;
66
+ }
67
+
68
+ .lingotek-bordered {
69
+ border: 1px solid #e5e5e5;
70
+ }
71
+
72
+
73
+ /* utilities */
74
+
75
+ #utility_delete_documents {
76
+ margin-left: 20px;
77
+ }
img/lingotek-chevrons-blue.png ADDED
Binary file
img/lingotek-icon.png ADDED
Binary file
img/lingotek-white.png ADDED
Binary file
include/api.php ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once('http.php');
4
+
5
+ /*
6
+ * manages communication with Lingotek TMS
7
+ * uses Lingotek API V5
8
+ *
9
+ * @since 0.1
10
+ */
11
+ class Lingotek_API extends Lingotek_HTTP {
12
+ protected $base_url;
13
+ protected $api_url;
14
+ protected $client_id;
15
+
16
+ const PRODUCTION_URL = "https://myaccount.lingotek.com";
17
+ const SANDBOX_URL = "https://cms.lingotek.com";
18
+ const CLIENT_ID = "780966c9-f9c8-4691-96e2-c0aaf47f62ff";// Lingotek App ID
19
+
20
+ /*
21
+ * constructor
22
+ *
23
+ * @since 0.1
24
+ */
25
+ public function __construct() {
26
+ $base_url = get_option('lingotek_base_url');
27
+ $this->base_url = $base_url ? $base_url : self::PRODUCTION_URL;
28
+ $this->api_url = $this->base_url.'/api';
29
+ $this->client_id = self::CLIENT_ID;
30
+ $token_details = get_option('lingotek_token');
31
+ $this->headers['Authorization'] = 'bearer ' . $token_details['access_token'];
32
+ $this->defaults = get_option('lingotek_defaults');
33
+ }
34
+
35
+ public function get_token_details($access_token) {
36
+ $url = $this->base_url . "/auth/oauth2/access_token_info?access_token=" . $access_token;
37
+ Lingotek::log("GET " . $url . " (" . __METHOD__ . ")");
38
+ $response = wp_remote_get($url);
39
+ $response_code = wp_remote_retrieve_response_code($response);
40
+
41
+ if ($response_code == 200) {
42
+ $response_body = json_decode(wp_remote_retrieve_body($response));
43
+ $token_details = $response_body;
44
+ }
45
+ else {
46
+ $token_details = FALSE;
47
+ }
48
+ return $token_details;
49
+ }
50
+
51
+ public function get_api_url() {
52
+ return $this->api_url;
53
+ }
54
+
55
+ /*
56
+ * updates the projet callback
57
+ *
58
+ * @since 0.2
59
+ *
60
+ * @param string $project_id
61
+ */
62
+ public function update_callback_url($project_id) {
63
+ $args = array('callback_url' => add_query_arg('lingotek', 1, site_url()));
64
+ $response = $this->patch($this->api_url . '/project/' . $project_id, $args);
65
+ return !is_wp_error($response) && 204 == wp_remote_retrieve_response_code($response);
66
+ }
67
+
68
+ /*
69
+ * creates a new project
70
+ *
71
+ * @since 0.2
72
+ *
73
+ * @param string $title
74
+ */
75
+ public function create_project($title, $community_id) {
76
+ $args = array(
77
+ 'title' => $title,
78
+ 'community_id' => $community_id,
79
+ 'workflow_id' => $this->defaults['workflow_id'],
80
+ 'callback_url' => add_query_arg('lingotek', 1, site_url()),
81
+ );
82
+
83
+ $response = $this->post($this->api_url . '/project', $args);
84
+ if(!is_wp_error($response) && 201 == wp_remote_retrieve_response_code($response)) {
85
+ $new_id = json_decode(wp_remote_retrieve_body($response));
86
+ return $new_id->properties->id;
87
+ }
88
+ else {
89
+ return false;
90
+ }
91
+ }
92
+
93
+ /*
94
+ * uploads a document
95
+ *
96
+ * @since 0.1
97
+ *
98
+ * @param array $args expects array with title, content and locale_code
99
+ * @returns bool|string document_id, false if something got wrong
100
+ */
101
+ public function upload_document($args) {
102
+ $args = wp_parse_args($args, array('format' => 'JSON', 'project_id' => $this->defaults['project_id'], 'workflow_id' => $this->defaults['workflow_id']));
103
+
104
+ $this->format_as_multipart($args);
105
+ $response = $this->post($this->api_url . '/document', $args);
106
+
107
+ if (!is_wp_error($response) && 202 == wp_remote_retrieve_response_code($response)) {
108
+ $b = json_decode(wp_remote_retrieve_body($response));
109
+ return $b->properties->id;
110
+ }
111
+ return false;
112
+ }
113
+
114
+ /*
115
+ * modifies a document
116
+ *
117
+ * @since 0.1
118
+ *
119
+ * @param string $id document id
120
+ * @param array $args expects array with content
121
+ * @return bool false if something got wrong
122
+ */
123
+ public function patch_document($id, $args) {
124
+ $args = wp_parse_args($args, array('format' => 'JSON'));
125
+ $this->format_as_multipart($args);
126
+ $response = $this->patch($this->api_url . '/document/' . $id, $args);
127
+ return !is_wp_error($response) && 202 == wp_remote_retrieve_response_code($response);
128
+ }
129
+
130
+ /*
131
+ * deletes a document
132
+ *
133
+ * @since 0.1
134
+ *
135
+ * @param string $id document id
136
+ */
137
+ public function delete_document($id) {
138
+ $response = $this->delete($this->api_url . '/document/' . $id);
139
+ return !is_wp_error($response) && 204 == wp_remote_retrieve_response_code($response);
140
+ }
141
+
142
+ /*
143
+ * get documents ids
144
+ *
145
+ * @since 0.1
146
+ */
147
+ public function get_documents($args = array()) {
148
+ $response = $this->get(add_query_arg($args, $this->api_url . '/document'));
149
+ $ids = array();
150
+
151
+ if (!is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response)) {
152
+ $documents = json_decode(wp_remote_retrieve_body($response));
153
+ foreach ($documents->entities as $doc) {
154
+ $ids[] = $doc->properties->id;
155
+ }
156
+ }
157
+
158
+ return $ids;
159
+ }
160
+
161
+ /*
162
+ * check if a document is existing
163
+ *
164
+ * @since 0.1
165
+ *
166
+ * @param string $id document id
167
+ * @return bool
168
+ */
169
+ public function document_exists($id) {
170
+ $response = $this->get($this->api_url . '/document/' . $id);
171
+ return !is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response);
172
+ }
173
+
174
+ /*
175
+ * check translations status of a document
176
+ *
177
+ * @since 0.1
178
+ *
179
+ * @param string $id document id
180
+ * @return array with locale as key and status as value
181
+ */
182
+ public function get_translations_status($id) {
183
+ $response = $this->get($this->api_url . '/document/' . $id . '/translation');
184
+ if (!is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response)) {
185
+ $b = json_decode(wp_remote_retrieve_body($response));
186
+ foreach ($b->entities as $e) {
187
+ $translations[$e->properties->locale_code] = $e->properties->percent_complete;
188
+ }
189
+ }
190
+
191
+ return empty($translations) ? array() : $translations;
192
+ }
193
+
194
+ /*
195
+ * requests a new translation of a document
196
+ *
197
+ * @since 0.1
198
+ *
199
+ * @param string $id document id
200
+ * @param string $locale Lingotek locale
201
+ * @param array $args optional arguments (only workflow_id at the moment)
202
+ * @return bool true if the request succeeded
203
+ */
204
+ public function request_translation($id, $locale, $args = array()) {
205
+ $args = wp_parse_args($args, array('workflow_id' => $this->defaults['workflow_id']));
206
+ $args = array_merge(array('locale_code' => $locale), $args);
207
+ $response = $this->post($this->api_url . '/document/' . $id . '/translation', $args);
208
+ return !is_wp_error($response) && 201 == wp_remote_retrieve_response_code($response);
209
+ }
210
+
211
+ /*
212
+ * get a translation
213
+ *
214
+ * @since 0.1
215
+ *
216
+ * @param string $id document id
217
+ * @param string $locale Lingotek locale
218
+ * @return string|bool the translation, false if there is none
219
+ */
220
+ public function get_translation($id, $locale) {
221
+ $response = $this->get(add_query_arg(array('locale_code' => $locale, 'auto_format' => 'true') , $this->api_url . '/document/' . $id . '/content'));
222
+ return !is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response) ? wp_remote_retrieve_body($response) : false;
223
+ }
224
+
225
+ /*
226
+ * deletes a translation
227
+ *
228
+ * @since 0.1
229
+ *
230
+ * @param string $id document id
231
+ * @param string $locale Lingotek locale
232
+ */
233
+ public function delete_translation($id, $locale) {
234
+ $response = $this->delete($this->api_url . '/document/' . $id . '/translation/' . $locale);
235
+ // FIXME send a response
236
+ }
237
+
238
+ /*
239
+ * get connect account url
240
+ *
241
+ * @param string $redirect_uri the location where to redirect to after account has been connected
242
+ * @return string the complete url for the connect account link
243
+ */
244
+ public function get_connect_url($redirect_uri, $env = NULL) {
245
+ $base_url = $this->base_url;
246
+ $client_id = $this->client_id;
247
+ if(!is_null($env)){
248
+ $base_url = (strcasecmp($env,'SANDBOX') == 0) ? self::SANDBOX_URL : self::PRODUCTION_URL;
249
+ }
250
+ return $base_url . "/auth/authorize.html?client_id=" . $client_id . "&redirect_uri=" . urlencode($redirect_uri) . "&response_type=token";
251
+ }
252
+
253
+ public function get_new_url($redirect_uri) {
254
+ $base_url = self::PRODUCTION_URL;
255
+ $client_id = $this->client_id;
256
+ return $base_url . "/lingopoint/portal/requestAccount.action?client_id=" . $client_id . "&app=" . urlencode($redirect_uri) . "&response_type=token";
257
+
258
+ }
259
+
260
+ public function get_communities() {
261
+ $response = $this->get(add_query_arg(array('limit' => 100), $this->api_url . '/community'));
262
+ return !is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response) ? json_decode(wp_remote_retrieve_body($response)) : false;
263
+ }
264
+
265
+ public function get_projects($community_id) {
266
+ $response = $this->get(add_query_arg(array('community_id' => $community_id, 'limit' => 100), $this->api_url . '/project'));
267
+ if (wp_remote_retrieve_response_code($response) == 204) {
268
+ return 'empty';
269
+ }
270
+ return !is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response) ? json_decode(wp_remote_retrieve_body($response)) : false;
271
+ }
272
+
273
+ public function get_vaults($community_id) {
274
+ $response = $this->get(add_query_arg(array('community_id' => $community_id, 'limit' => 100), $this->api_url . '/vault'));
275
+ return !is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response) ? json_decode(wp_remote_retrieve_body($response)) : false;
276
+ }
277
+
278
+ public function get_workflows($community_id) {
279
+ $response = $this->get(add_query_arg(array('community_id' => $community_id, 'limit' => 100), $this->api_url . '/workflow'));
280
+ return !is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response) ? json_decode(wp_remote_retrieve_body($response)) : false;
281
+ }
282
+
283
+ public function get_filters() {
284
+ $response = $this->get(add_query_arg(array('limit' => 100), $this->api_url . '/filter'));
285
+ return !is_wp_error($response) && 200 == wp_remote_retrieve_response_code($response) ? json_decode(wp_remote_retrieve_body($response)) : false;
286
+ }
287
+
288
+ public function upload_filter($name, $type, $content) {
289
+ $args = array('name' => $name, 'type' => $type, 'content' => $content);
290
+ $response = $this->post($this->api_url . '/filter', $args);
291
+ }
292
+ }
include/callback.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * a class to handle Lingotek callbacks
5
+ *
6
+ * @since 0.1
7
+ */
8
+ class Lingotek_Callback {
9
+ public $lgtm;
10
+
11
+ /*
12
+ * Constructor
13
+ *
14
+ * @since 0.1
15
+ */
16
+ public function __construct(&$model) {
17
+ $this->lgtm = &$model;
18
+
19
+ add_filter('request', array(&$this, 'request'));
20
+ }
21
+
22
+ /*
23
+ * dispatches the Lingotek callback and dies
24
+ *
25
+ * @since 0.1
26
+ *
27
+ * @param array $query_vars query vars known to WordPres
28
+ * @return array unmodified query vars if the request is not a Lingotek callback
29
+ */
30
+ public function request($query_vars) {
31
+ if (empty($query_vars['lingotek']))
32
+ return $query_vars;
33
+
34
+ if (isset($_GET['type'], $_GET['document_id']) && $document = $this->lgtm->get_group_by_id($_GET['document_id'])) {
35
+
36
+ // url for in context review
37
+ if (isset($_GET['locale']) && 'get' == $_GET['type']) {
38
+ $locale = Lingotek::map_to_wp_locale($_GET['locale']); // map to WP locale
39
+
40
+ // posts
41
+ if (post_type_exists($document->type)) {
42
+ if ($id = $document->pllm->get_post($document->source, $locale)) {
43
+ wp_redirect(get_permalink($id), 301);
44
+ exit();
45
+ }
46
+ else {
47
+ wp_redirect(get_permalink($document->source), 302);
48
+ exit();
49
+ }
50
+ }
51
+
52
+ // taxonomy terms
53
+ elseif (taxonomy_exists($document->type) && $id = $document->pllm->get_term($document->source, $locale)) {
54
+ wp_redirect(get_term_link($id, $document->type), 301);
55
+ exit();
56
+ }
57
+
58
+ status_header(404); // no document found
59
+ die();
60
+ }
61
+
62
+ if ('document_uploaded' == $_GET['type']) {
63
+ $document->source_ready();
64
+
65
+ if ($document->is_automatic_upload())
66
+ $document->request_translations();
67
+ }
68
+
69
+ if (isset($_GET['locale']) && 'target' == $_GET['type']) {
70
+ // We will need access to PLL_Admin_Sync::copy_post_metas
71
+ global $polylang;
72
+ $polylang->sync = new PLL_Admin_Sync($polylang);
73
+
74
+ $locale = Lingotek::map_to_wp_locale($_GET['locale']); // map to WP locale
75
+ $document->is_automatic_download($locale) ? $document->create_translation($locale) : $document->translation_ready($locale);
76
+ }
77
+
78
+ status_header(200); // useless as it the default value
79
+ die();
80
+ }
81
+
82
+ status_header(404); // no document found
83
+ die();
84
+ }
85
+ }
include/dashboard.php ADDED
@@ -0,0 +1,307 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * a class to handle Lingotek dashboard requests
5
+ *
6
+ * @since 0.1
7
+ */
8
+ class Lingotek_Dashboard {
9
+
10
+ /*
11
+ * Constructor
12
+ *
13
+ * @since 0.1
14
+ */
15
+ public function __construct() {
16
+ }
17
+
18
+ /**
19
+ * Lingotek
20
+ */
21
+ function ajax_language_dashboard() {
22
+ global $polylang;
23
+
24
+ $request_method = isset($_REQUEST['_method']) ? $_REQUEST['_method'] : (isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET');
25
+
26
+ $response = array(
27
+ 'method' => $request_method
28
+ );
29
+ switch ($request_method) {
30
+ case 'POST':
31
+ if (isset($_REQUEST['code'], $_REQUEST['native'], $_REQUEST['direction'])) {
32
+ $name = $_REQUEST['native'];
33
+ $slug = substr($_REQUEST['code'], 0, strpos($_REQUEST['code'], '_')); // 3rd parameter of strstr needs PHP 5.3
34
+ $locale = Lingotek::map_to_wp_locale($_REQUEST['code']);
35
+
36
+ // avoid conflicts between language slugs
37
+ $existing_slugs = $polylang->model->get_languages_list(array('fields' => 'slug'));
38
+ if (!empty($existing_slugs) && in_array($slug, $existing_slugs))
39
+ $slug = strtolower(str_replace('_', '-', $locale));
40
+
41
+ $rtl = $_REQUEST['direction'] == 'RTL';
42
+ $term_group = 0;
43
+
44
+ // adds the language
45
+ $polylang->model->add_language(compact('name', 'slug', 'locale', 'rtl', 'term_group'));
46
+
47
+ // attempts to install the language pack
48
+ require_once(ABSPATH . 'wp-admin/includes/translation-install.php');
49
+ wp_download_language_pack($locale);
50
+
51
+ // force checking for themes and plugins translations updates
52
+ wp_update_themes();
53
+ wp_update_plugins();
54
+
55
+ $response = array(
56
+ 'request' => 'POST: add target language to CMS and Lingotek Project Language',
57
+ 'locale' => $_REQUEST['code'],
58
+ 'xcode' => $locale,
59
+ 'active' => 1,
60
+ 'enabled' => 1,
61
+ 'source' => self::get_counts_by_type($locale, 'sources'),
62
+ 'target' => self::get_counts_by_type($locale, 'targets')
63
+ );
64
+ status_header(200);
65
+ }
66
+ break;
67
+
68
+ case 'DELETE':
69
+ $body = file_get_contents("php://input");
70
+ $code = str_replace('code=', '', $body);
71
+ $lang = $polylang->model->get_language(Lingotek::map_to_wp_locale($code)); // map code to WP locales to find the language
72
+
73
+ // prevents deleting the last language as it would break the Lingotek dashboard
74
+ if (1 == count($polylang->model->get_languages_list())) {
75
+ $response = array (
76
+ 'request' => sprintf('DELETE: remove language from CMS and project (%s)', $code),
77
+ 'code' => $code,
78
+ 'success' => false,
79
+ 'message' => __('You must keep at least one language.', 'wp-lingotek')
80
+ );
81
+ status_header(403);
82
+ }
83
+
84
+ elseif (!self::has_language_content($lang)) {
85
+ $default_category = pll_get_term(get_option('default_category'), $lang->slug);
86
+ $polylang->model->delete_language((int) $lang->term_id);
87
+ wp_delete_term( $default_category, 'category' ); // delete the default category after the language
88
+
89
+ $response = array (
90
+ 'request' => sprintf('DELETE: remove language from CMS and project (%s)', $code),
91
+ 'code' => $code,
92
+ 'active' => false,
93
+ 'success' => true
94
+ );
95
+ status_header(204);
96
+ }
97
+
98
+ else {
99
+ $response = array (
100
+ 'request' => sprintf('DELETE: remove language from CMS and project (%s)', $code),
101
+ 'code' => $code,
102
+ 'success' => false,
103
+ 'message' => __('The language can only be removed when no existing content is using it. If you would like to remove this language from the site, then first remove any content assigned to this language.', 'wp-lingotek')
104
+ );
105
+ status_header(403);
106
+ }
107
+ break;
108
+
109
+ case 'GET':
110
+ default:
111
+ $locale_code = isset($_REQUEST['code']) ? $_REQUEST['code'] : NULL;
112
+ $response = $response + $this->get_language_details($locale_code);
113
+ break;
114
+ }
115
+
116
+ wp_send_json($response);
117
+ }
118
+
119
+ /**
120
+ * Lingotek - get the details of each language
121
+ */
122
+ function get_language_details($locale_requested = NULL) {
123
+ global $polylang;
124
+
125
+ $response = array();
126
+ $available_languages = $polylang->model->get_languages_list();
127
+ $source_total = 0;
128
+ $target_total = 0;
129
+ $source_totals = array();
130
+ $target_totals = array();
131
+
132
+ // If we get a parameter, only return that language. Otherwise return all languages.
133
+ foreach ($available_languages as $lang_details) {
134
+ $wordpress_lang_code = $lang_details->slug;
135
+ $locale = $lang_details->locale;
136
+ if (!is_null($locale_requested) && $locale_requested != $locale) {
137
+ continue;
138
+ }
139
+
140
+ $lingotek_locale = str_replace('-', '_', $lang_details->lingotek_locale);
141
+ $source_counts = self::get_source_counts($wordpress_lang_code);
142
+ $target_counts = self::get_target_counts($wordpress_lang_code);
143
+ self::standardize_and_sort($source_counts['types'], $target_counts['types']);
144
+ $source_count = $source_counts['total']; //unset($source_counts['total']);
145
+ $target_count = $target_counts['total'];
146
+
147
+ $target_status = array(
148
+ 'locale' => $lingotek_locale, // Return this language code as the Lingotek language code.
149
+ 'xcode' => $wordpress_lang_code,
150
+ 'active' => 1, //TO-DO: lingotek_enabled?
151
+ 'enabled' => 1, //intval($lang_details->active), // wordpress enabled
152
+ 'source' => $source_counts,
153
+ 'target' => $target_counts
154
+ );
155
+ if ($locale_requested == $locale) {
156
+ $response = $target_status;
157
+ }
158
+ else {
159
+ $response[$lingotek_locale] = $target_status;
160
+ }
161
+ //$source_total += $source_count;
162
+ $target_total += $target_count;
163
+ //$source_totals = self::array_sum_values($source_totals, $source_counts['types']);
164
+ $target_totals = self::array_sum_values($target_totals, $target_counts['types']);
165
+ }
166
+ if (is_null($locale_requested)) {
167
+ $source_totals_package = self::get_unique_source_counts_by_type();
168
+ $source_totals = $source_totals_package['types'];
169
+ $source_total = $source_totals_package['total'];
170
+ self::standardize_and_sort($source_totals, $target_totals);
171
+
172
+ $response = array(
173
+ 'languages' => $response,
174
+ 'source' => array('types' => $source_totals, 'total' => $source_total),
175
+ 'target' => array('types' => $target_totals, 'total' => $target_total),
176
+ 'count' => count($available_languages),
177
+ );
178
+ }
179
+ return $response;
180
+ }
181
+
182
+ /**
183
+ * Lingotek - standardize and sort helper function
184
+ */
185
+ static function standardize_and_sort(&$a1, &$a2) {
186
+ $merged_totals = array_fill_keys(array_keys($a1 + $a2), 0);
187
+ $a1 = $a1 + $merged_totals;
188
+ $a2 = $a2 + $merged_totals;
189
+ ksort($a1);
190
+ ksort($a2);
191
+ }
192
+
193
+ /**
194
+ * Lingotek - get_source_counts
195
+ */
196
+ function get_source_counts($locale) {
197
+ return self::get_counts_by_type($locale, 'sources');
198
+ }
199
+
200
+ /**
201
+ * Lingotek - get_target_counts
202
+ */
203
+ function get_target_counts($locale) {
204
+ return self::get_counts_by_type($locale, 'targets');
205
+ }
206
+
207
+ /**
208
+ * Lingotek - get_counts_by_type
209
+ */
210
+ static function get_counts_by_type($locale, $condition) {
211
+ global $polylang;
212
+
213
+ $lgtm = new Lingotek_Model(); // FIXME not created by Lingotek as Polylang believes we are doing ajax on frontend
214
+
215
+ foreach ($polylang->model->get_translated_post_types() as $post_type) {
216
+ $count = $lgtm->count_posts($post_type);
217
+ $post_type_object = get_post_type_object($post_type);
218
+ $response['types'][$post_type_object->labels->name] = isset($count[$condition][$locale]) ? $count[$condition][$locale] : 0;
219
+ }
220
+
221
+ foreach ($polylang->model->get_translated_taxonomies() as $tax) {
222
+ $count = $lgtm->count_terms($tax);
223
+ $taxonomy = get_taxonomy($tax);
224
+ $response['types'][$taxonomy->labels->name] = isset($count[$condition][$locale]) ? $count[$condition][$locale] : 0;
225
+ }
226
+
227
+ $response['total'] = array_sum($response['types']);
228
+
229
+ return $response;
230
+ }
231
+
232
+ static function get_unique_source_counts_by_type(){
233
+
234
+ global $polylang;
235
+
236
+ $lgtm = new Lingotek_Model(); // FIXME not created by Lingotek as Polylang believes we are doing ajax on frontend
237
+
238
+ foreach ($polylang->model->get_translated_post_types() as $post_type) {
239
+ $count = $lgtm->count_posts($post_type);
240
+ $post_type_object = get_post_type_object($post_type);
241
+ $response['types'][$post_type_object->labels->name] = isset($count['total']) ? $count['total'] : 0; //only count translation sets
242
+ }
243
+
244
+ foreach ($polylang->model->get_translated_taxonomies() as $tax) {
245
+ $count = $lgtm->count_terms($tax);
246
+ $taxonomy = get_taxonomy($tax);
247
+ $response['types'][$taxonomy->labels->name] = isset($count['total']) ? $count['total'] : 0; //only count translation sets
248
+ }
249
+
250
+ $response['total'] = array_sum($response['types']);
251
+
252
+ return $response;
253
+ }
254
+
255
+ /**
256
+ * Sums the values of the arrays be there keys (PHP 4, PHP 5)
257
+ * array array_sum_values ( array array1 [, array array2 [, array ...]] )
258
+ */
259
+ public static function array_sum_values() {
260
+ $return = array();
261
+ $intArgs = func_num_args();
262
+ $arrArgs = func_get_args();
263
+ if ($intArgs < 1) {
264
+ trigger_error('Warning: Wrong parameter count for arraySumValues()', E_USER_WARNING);
265
+ }
266
+
267
+ foreach ($arrArgs as $arrItem) {
268
+ if (!is_array($arrItem)) {
269
+ trigger_error('Warning: Wrong parameter values for arraySumValues()', E_USER_WARNING);
270
+ }
271
+ foreach ($arrItem as $k => $v) {
272
+ if (!key_exists($k, $return)) {
273
+ $return[$k] = 0;
274
+ }
275
+ $return[$k] += $v;
276
+ }
277
+ }
278
+ return $return;
279
+
280
+ $sumArray = array();
281
+ foreach ($myArray as $k => $subArray) {
282
+ foreach ($subArray as $id => $value) {
283
+ $sumArray[$id]+=$value;
284
+ }
285
+ }
286
+ return $sumArray;
287
+ }
288
+
289
+ /*
290
+ * checks if any content exists in the language
291
+ *
292
+ * @since 0.2
293
+ *
294
+ * @param object $language
295
+ * @return bool false if no content in the language or only the default category
296
+ */
297
+ static public function has_language_content($language) {
298
+ // posts
299
+ $objects = get_objects_in_term($language->term_id, 'language');
300
+ if (!empty($objects))
301
+ return true;
302
+
303
+ // terms, only the default category is accepted
304
+ $objects = get_objects_in_term($language->tl_term_id, 'term_language');
305
+ return count($objects) > 1 || (isset($objects[0]) && $objects[0] != pll_get_term(get_option('default_category'), $language->slug));
306
+ }
307
+ }
include/group-post.php ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Translations groups for posts, pages and custom post types
5
+ *
6
+ * @since 0.2
7
+ */
8
+ class Lingotek_Group_Post extends Lingotek_Group {
9
+
10
+ const SAME_AS_SOURCE = 'SAME_AS_SOURCE'; // pref constant used for downloaded translations
11
+
12
+ /*
13
+ * set a translation term for an object
14
+ *
15
+ * @since 0.2
16
+ *
17
+ * @param int $object_id post id
18
+ * @param object $language
19
+ * @param string $document_id translation term name (Lingotek document id)
20
+ */
21
+ public static function create($object_id, $language, $document_id) {
22
+ $data = array(
23
+ 'lingotek' => array(
24
+ 'type' => get_post_type($object_id),
25
+ 'source' => $object_id,
26
+ 'status' => 'importing',
27
+ 'translations' => array()
28
+ ),
29
+ $language->slug => $object_id // for Polylang
30
+ );
31
+
32
+ self::_create($object_id, $document_id, $data, 'post_translations');
33
+ }
34
+
35
+ /*
36
+ * returns content type fields
37
+ *
38
+ * @since 0.2
39
+ *
40
+ * @param string $post_type
41
+ * @return array
42
+ */
43
+ static public function get_content_type_fields($post_type) {
44
+ $arr = 'attachment' == $post_type ?
45
+ array(
46
+ 'post_title' => __('Title', 'wp-lingotek'),
47
+ 'post_excerpt' => __('Caption', 'wp-lingotek'),
48
+ 'metas' => array('_wp_attachment_image_alt' => __('Alternative Text', 'wp-lingotek')),
49
+ 'post_content' => __('Description', 'wp-lingotek'),
50
+ ) :
51
+ array(
52
+ 'post_title' => __('Title', 'wp-lingotek'),
53
+ 'post_name' => __('Slug', 'wp-lingotek'),
54
+ 'post_content' => __('Content', 'wp-lingotek'),
55
+ 'post_excerpt' => __('Excerpt', 'wp-lingotek')
56
+ );
57
+
58
+ // add the custom fields from wpml-config.xml <custom-fields> sections
59
+ $wpml_config = PLL_WPML_Config::instance();
60
+
61
+ if (isset($wpml_config->tags['custom-fields'])) {
62
+ foreach ($wpml_config->tags['custom-fields'] as $context) {
63
+ foreach ($context['custom-field'] as $cf) {
64
+ if ('translate' == $cf['attributes']['action'])
65
+ $arr['metas'][$cf['value']] = $cf['value'];
66
+ }
67
+ }
68
+ }
69
+
70
+ // allow plugins to modify the fields to translate
71
+ return apply_filters('lingotek_post_content_type_fields', $arr, $post_type);
72
+ }
73
+
74
+ /*
75
+ * returns the content to translate
76
+ *
77
+ * @since 0.2
78
+ *
79
+ * @param object $post
80
+ * @return string json encoded content to translate
81
+ */
82
+ public static function get_content($post) {
83
+ $fields = self::get_content_type_fields($post->post_type);
84
+ $content_types = get_option('lingotek_content_type');
85
+
86
+ foreach (array_keys($fields) as $key) {
87
+ if ('metas' == $key) {
88
+ foreach (array_keys($fields['metas']) as $meta) {
89
+ if (empty($content_types[$post->post_type]['fields']['metas'][$meta]) && $value = get_post_meta($post->ID, $meta, true))
90
+ $arr['metas'][$meta] = $value;
91
+ }
92
+ }
93
+
94
+ // send slug for translation only if it has been modified
95
+ elseif('post_name' == $key && empty($content_types[$post->post_type]['fields'][$key])) {
96
+ $default_slug = sanitize_title($post->post_title); // default slug created by WP
97
+ if ($default_slug != $post->post_name)
98
+ $arr['post'][$key] = $post->$key;
99
+ }
100
+
101
+ elseif (empty($content_types[$post->post_type]['fields'][$key])) {
102
+ $arr['post'][$key] = $post->$key;
103
+ }
104
+ }
105
+
106
+ return json_encode($arr);
107
+ }
108
+
109
+ public static function is_valid_auto_upload_post_status($post_status) {
110
+ $prefs = Lingotek_Model::get_prefs();
111
+ $valid_statuses = $prefs['auto_upload_post_statuses'];
112
+ $valid = array_key_exists($post_status, $valid_statuses) && $valid_statuses[$post_status];
113
+ return $valid;
114
+ }
115
+
116
+ /*
117
+ * requests translations to Lingotek TMS
118
+ *
119
+ * @since 0.1
120
+ */
121
+ public function request_translations() {
122
+ if (isset($this->source)) {
123
+ $language = $this->pllm->get_post_language((int) $this->source);
124
+ $this->_request_translations($language);
125
+ }
126
+ }
127
+
128
+ /*
129
+ * create a translation downloaded from Lingotek TMS
130
+ *
131
+ * @since 0.1
132
+ * @uses Lingotek_Group::safe_translation_status_update() as the status can be automatically set by the TMS callback
133
+ *
134
+ * @param string $locale
135
+ */
136
+ public function create_translation($locale) {
137
+ $client = new Lingotek_API();
138
+
139
+ if (false === ($translation = $client->get_translation($this->document_id, $locale)))
140
+ return;
141
+
142
+ self::$creating_translation = true;
143
+ $prefs = Lingotek_Model::get_prefs(); // need an array by default
144
+
145
+ $translation = json_decode($translation, true); // wp_insert_post expects array
146
+ $tr_post = $translation['post'];
147
+
148
+ $post = get_post($this->source); // source post
149
+ $tr_post['post_status'] = ($prefs['download_post_status'] === self::SAME_AS_SOURCE)? $post->post_status : $prefs['download_post_status']; // status
150
+
151
+ // update existing translation
152
+ if ($tr_id = $this->pllm->get_post($this->source, $locale)) {
153
+ $tr_post['ID'] = $tr_id;
154
+ wp_update_post($tr_post);
155
+
156
+ $this->safe_translation_status_update($locale, 'current');
157
+ }
158
+
159
+ // create new translation
160
+ else {
161
+ unset($post->post_name); // forces the creation of a new default slug if not translated by Lingotek
162
+ $tr_post = array_merge((array) $post , $tr_post); // copy all untranslated fields from the original post
163
+ $tr_post['ID'] = null; // will force the creation of a new post
164
+
165
+ // translate parent
166
+ $tr_post['post_parent'] = ($post->post_parent && $tr_parent = $this->pllm->get_translation('post', $post->post_parent, $locale)) ? $tr_parent : 0;
167
+
168
+ if ('attachment' == $post->post_type) {
169
+ $tr_id = wp_insert_attachment($tr_post);
170
+ add_post_meta($tr_id, '_wp_attachment_metadata', get_post_meta($this->source, '_wp_attachment_metadata', true));
171
+ add_post_meta($tr_id, '_wp_attached_file', get_post_meta($this->source, '_wp_attached_file', true));
172
+ }
173
+ else {
174
+ $tr_id = wp_insert_post($tr_post);
175
+ }
176
+
177
+ if ($tr_id) {
178
+ $tr_lang = $this->pllm->get_language($locale);
179
+ $this->pllm->set_post_language($tr_id, $tr_lang);
180
+
181
+ $this->safe_translation_status_update($locale, 'current', array($tr_lang->slug => $tr_id));
182
+ wp_set_object_terms($tr_id, $this->term_id, 'post_translations');
183
+
184
+ // assign terms and metas
185
+ $GLOBALS['polylang']->sync->copy_post_metas($this->source, $tr_id, $tr_lang->slug);
186
+
187
+ // translate metas
188
+ if (!empty($translation['metas'])) {
189
+ foreach ($translation['metas'] as $key => $meta)
190
+ update_post_meta($tr_id, $key, $meta);
191
+ }
192
+ }
193
+ }
194
+
195
+ self::$creating_translation = false;
196
+ }
197
+
198
+ /*
199
+ * checks if content should be automatically uploaded
200
+ *
201
+ * @since 0.2
202
+ *
203
+ * @return bool
204
+ */
205
+ public function is_automatic_upload() {
206
+ return 'automatic' == Lingotek_Model::get_profile_option('upload', get_post_type($this->source), $this->get_source_language());
207
+ }
208
+
209
+ /*
210
+ * get the the language of the source post
211
+ *
212
+ * @since 0.2
213
+ *
214
+ * @return object
215
+ */
216
+ public function get_source_language() {
217
+ return $this->pllm->get_post_language($this->source);
218
+ }
219
+ }
include/group-string.php ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Translations groups for strings
5
+ *
6
+ * @since 0.2
7
+ */
8
+ class Lingotek_Group_String extends Lingotek_Group {
9
+
10
+ /*
11
+ * assigns this object properties from the underlying term
12
+ *
13
+ * @since 0.2
14
+ *
15
+ * @param object $term term translation object
16
+ */
17
+ protected function load($term) {
18
+ parent::load($term);
19
+ $this->name = $term->name;
20
+ $this->md5 = &$this->desc_array['lingotek']['md5'];
21
+ }
22
+
23
+ /*
24
+ * updates the translation term in DB
25
+ *
26
+ * @since 0.2
27
+ */
28
+ public function save() {
29
+ wp_update_term((int) $this->term_id, $this->taxonomy, array('slug' => $this->document_id, 'name' => $this->name, 'description' => serialize($this->desc_array)));
30
+ }
31
+
32
+ /*
33
+ * set a translation term for a strings group
34
+ *
35
+ * @since 0.2
36
+ *
37
+ * @param string string group name
38
+ * @param object $language
39
+ * @param string $document_id translation term name (Lingotek document id)
40
+ */
41
+ public static function create($name, $language, $document_id) {
42
+ $desc = array(
43
+ 'lingotek' => array(
44
+ 'type' => 'string',
45
+ 'md5' => md5(self::get_content($name)),
46
+ 'source' => $language->mo_id,
47
+ 'status' => 'importing',
48
+ 'translations' => array()
49
+ ),
50
+ );
51
+
52
+ $terms = wp_get_object_terms($language->mo_id, 'post_translations');
53
+
54
+ // the translation already exists but was previously disassociated
55
+ if ($key = array_search($name, wp_list_pluck($terms, 'name'))) {
56
+ wp_update_term((int) $terms[$key]->term_id, 'post_translations', array('slug' => $document_id, 'name' => $name, 'description' => serialize($desc)));
57
+ }
58
+
59
+ else {
60
+ wp_insert_term($name, 'post_translations', array('slug' => $document_id, 'description' => serialize($desc)));
61
+ }
62
+
63
+ wp_set_object_terms($language->mo_id, $document_id, 'post_translations', true); // add terms
64
+
65
+ }
66
+
67
+ /*
68
+ * disassociates translations from the Lingotek TMS
69
+ *
70
+ * @since 0.2
71
+ *
72
+ * @param bool $delete whether to delete the Lingotek document or not
73
+ */
74
+ public function disassociate($delete = true) {
75
+ $client = new Lingotek_API();
76
+
77
+ if (!$delete || $client->delete_document($this->document_id)) {
78
+ wp_delete_term($this->term_id, 'post_translations');
79
+ }
80
+ }
81
+
82
+ /*
83
+ * uploads a modified source
84
+ *
85
+ * @since 0.2
86
+ *
87
+ * @param string $group group name
88
+ * @param string $empty used for compatibility with parent class
89
+ */
90
+ public function patch($group, $empty = '', $external_url = '', $filters = array()) {
91
+ $client = new Lingotek_API();
92
+ $content = $this->get_content($group);
93
+
94
+ $params = array(
95
+ 'title' => $title,
96
+ 'content' => $this->get_content($content),
97
+ 'external_url' => $external_url,
98
+ );
99
+ $params = array_merge($params, $filters);
100
+
101
+ $res = $client->patch_document($this->document_id, $params);
102
+
103
+ if ($res) {
104
+ $this->md5 = md5($content);
105
+ $this->status = 'importing';
106
+ $this->translations = array_fill_keys(array_keys($this->translations), 'pending');
107
+ $this->save();
108
+ }
109
+ }
110
+
111
+ /*
112
+ * returns the content to translate
113
+ *
114
+ * @since 0.2
115
+ *
116
+ * @param object $group string group name
117
+ * @return string json encoded content to translate
118
+ */
119
+ public static function get_content($group) {
120
+ foreach (PLL_Admin_Strings::get_strings() as $string) {
121
+ if ($string['context'] == $group)
122
+ $arr[$string['string']] = $string['string'];
123
+ }
124
+ return json_encode($arr);
125
+ }
126
+
127
+ /*
128
+ * requests translations to Lingotek TMS
129
+ *
130
+ * @since 0.2
131
+ */
132
+ public function request_translations() {
133
+ if (isset($this->source))
134
+ $this->_request_translations($this->get_source_language());
135
+ }
136
+
137
+ /*
138
+ * create a translation downloaded from Lingotek TMS
139
+ *
140
+ * @since 0.2
141
+ * @uses Lingotek_Group::safe_translation_status_update() as the status can be automatically set by the TMS callback
142
+ *
143
+ * @param string $locale
144
+ */
145
+ public function create_translation($locale) {
146
+ $client = new Lingotek_API();
147
+
148
+ if (false === ($translation = $client->get_translation($this->document_id, $locale)))
149
+ return;
150
+
151
+ $strings = wp_list_pluck(PLL_Admin_Strings::get_strings(), 'name', 'string'); // get the strings name for the filter
152
+ $translations = json_decode($translation, true); // wp_insert_post expects array
153
+ $language = $this->pllm->get_language($locale);
154
+
155
+ $mo = new PLL_MO();
156
+ $mo->import_from_db($language);
157
+
158
+ foreach ($translations as $key => $translation) {
159
+ $translation = apply_filters('pll_sanitize_string_translation', $translation, $strings[$key], $this->name);
160
+ $mo->add_entry($mo->make_entry($key, $translation));
161
+ }
162
+
163
+ $mo->export_to_db($language);
164
+ $this->safe_translation_status_update($locale, 'current');
165
+ }
166
+
167
+ /*
168
+ * checks if content should be automatically uploaded
169
+ *
170
+ * @since 0.2
171
+ *
172
+ * @return bool
173
+ */
174
+ public function is_automatic_upload() {
175
+ return 'automatic' == Lingotek_Model::get_profile_option('upload', 'string', $this->get_source_language());
176
+ }
177
+
178
+ /*
179
+ * get the the language of the source string (always the default language)
180
+ *
181
+ * @since 0.2
182
+ *
183
+ * @return object
184
+ */
185
+ public function get_source_language() {
186
+ return $this->pllm->get_language($this->pllm->options['default_lang']);
187
+ }
188
+ }
include/group-term.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Translations groups for categories, tags and custom taxonomies
5
+ *
6
+ * @since 0.2
7
+ */
8
+ class Lingotek_Group_Term extends Lingotek_Group {
9
+
10
+ /*
11
+ * set a translation term for an object
12
+ *
13
+ * @since 0.2
14
+ *
15
+ * @param int $object_id term id
16
+ * @param string $tax taxonomy name
17
+ * @param object $language
18
+ * @param string $document_id translation term name (Lingotek document id)
19
+ */
20
+ public static function create($object_id, $tax, $language, $document_id) {
21
+ $data = array(
22
+ 'lingotek' => array(
23
+ 'type' => $tax,
24
+ 'source' => $object_id,
25
+ 'status' => 'importing',
26
+ 'translations' => array()
27
+ ),
28
+ $language->slug => $object_id // for Polylang
29
+ );
30
+
31
+ self::_create($object_id, $document_id, $data, 'term_translations');
32
+ }
33
+
34
+ /*
35
+ * returns content type fields
36
+ *
37
+ * @since 0.2
38
+ *
39
+ * @param string $taxonomy
40
+ * @return array
41
+ */
42
+ static public function get_content_type_fields($taxonomy) {
43
+ $arr = array(
44
+ 'name' => __('Name', 'wp-lingotek'),
45
+ 'args' => array(
46
+ 'slug' => __('Slug', 'wp-lingotek'),
47
+ 'description' => __('Description', 'wp-lingotek')
48
+ )
49
+ );
50
+
51
+ return apply_filters('lingotek_term_content_type_fields', $arr, $taxonomy);
52
+ }
53
+
54
+ /*
55
+ * returns the content to translate
56
+ *
57
+ * @since 0.2
58
+ *
59
+ * @param object $term
60
+ * @return string json encoded content to translate
61
+ */
62
+ public static function get_content($term) {
63
+ $fields = self::get_content_type_fields($term->taxonomy);
64
+ $content_types = get_option('lingotek_content_type');
65
+
66
+ foreach (array_keys($fields) as $key) {
67
+ if ('args' == $key) {
68
+ foreach (array_keys($fields['args']) as $arg) {
69
+ if (empty($content_types[$term->taxonomy]['fields']['args'][$arg]))
70
+ $arr['args'][$arg] = $term->$arg;
71
+ }
72
+ }
73
+
74
+ elseif (empty($content_types[$term->taxonomy]['fields'][$key])) {
75
+ $arr[$key] = $term->$key;
76
+ }
77
+ }
78
+
79
+ return json_encode($arr);
80
+ }
81
+
82
+ /*
83
+ * requests translations to Lingotek TMS
84
+ *
85
+ * @since 0.2
86
+ */
87
+ public function request_translations() {
88
+ if (isset($this->source)) {
89
+ $language = $this->pllm->get_term_language((int) $this->source);
90
+ $this->_request_translations($language);
91
+ }
92
+ }
93
+
94
+ /*
95
+ * create a translation downloaded from Lingotek TMS
96
+ *
97
+ * @since 0.2
98
+ * @uses Lingotek_Group::safe_translation_status_update() as the status can be automatically set by the TMS callback
99
+ *
100
+ * @param string $locale
101
+ */
102
+ public function create_translation($locale) {
103
+ $client = new Lingotek_API();
104
+
105
+ if (false === ($translation = $client->get_translation($this->document_id, $locale)))
106
+ return;
107
+
108
+ self::$creating_translation = true;
109
+
110
+ $translation = json_decode($translation, true); // wp_insert_post expects array
111
+ $args = $translation['args'];
112
+
113
+ // update existing translation
114
+ if ($tr_id = $this->pllm->get_term($this->source, $locale)) {
115
+ $args['name'] = $translation['name'];
116
+ wp_update_term($tr_id, $this->type, $args);
117
+
118
+ $this->safe_translation_status_update($locale, 'current');
119
+ }
120
+
121
+ // create new translation
122
+ else {
123
+ $tr_lang = $this->pllm->get_language($locale);
124
+
125
+ // translate parent
126
+ $term = get_term($this->source, $this->type);
127
+ $args['parent'] = ($term->parent && $tr_parent = $this->model->get_translation('term', $term->parent, $locale)) ? $tr_parent : 0;
128
+
129
+ // attempt to get a unique slug in case it already exists in another language
130
+ if (isset($args['slug']) && term_exists($args['slug'])) {
131
+ $args['slug'] .= '-' . $tr_lang->slug;
132
+ }
133
+
134
+ $tr = wp_insert_term($translation['name'], $this->type, $args);
135
+
136
+ if (!is_wp_error($tr)) {
137
+ $this->pllm->set_term_language($tr['term_id'], $tr_lang);
138
+ $this->safe_translation_status_update($locale, 'current', array($tr_lang->slug => $tr['term_id']));
139
+ wp_set_object_terms($tr['term_id'], $this->term_id, 'term_translations');
140
+ }
141
+ }
142
+
143
+ self::$creating_translation = false;
144
+ }
145
+
146
+ /*
147
+ * checks if content should be automatically uploaded
148
+ *
149
+ * @since 0.2
150
+ *
151
+ * @return bool
152
+ */
153
+ public function is_automatic_upload() {
154
+ return 'automatic' == Lingotek_Model::get_profile_option('upload', $this->type, $this->get_source_language());
155
+ }
156
+
157
+ /*
158
+ * get the the language of the source term
159
+ *
160
+ * @since 0.2
161
+ *
162
+ * @return object
163
+ */
164
+ public function get_source_language() {
165
+ return $this->pllm->get_term_language($this->source);
166
+ }
167
+ }
include/group.php ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Abstract class for Translations groups objects
5
+ *
6
+ * @since 0.2
7
+ */
8
+ abstract class Lingotek_Group {
9
+ static public $creating_translation; // used to avoid uploading a translation when using automatinc upload
10
+
11
+ /*
12
+ * constructor
13
+ *
14
+ * @since 0.2
15
+ */
16
+ public function __construct($term, &$pllm) {
17
+ $this->pllm = &$pllm;
18
+ $this->load($term);
19
+ }
20
+
21
+ /*
22
+ * assigns this object properties from the underlying term
23
+ *
24
+ * @since 0.2
25
+ *
26
+ * @param object $term term translation object
27
+ */
28
+ protected function load($term) {
29
+ $this->term_id = (int) $term->term_id;
30
+ $this->tt_id = (int) $term->term_taxonomy_id;
31
+ $this->document_id = $term->slug;
32
+ $this->taxonomy = $term->taxonomy;
33
+ $this->desc_array = unserialize($term->description);
34
+
35
+ foreach (array('type', 'source', 'status', 'translations') as $prop)
36
+ $this->$prop = &$this->desc_array['lingotek'][$prop];
37
+ }
38
+
39
+ /*
40
+ * updates the translation term in DB
41
+ *
42
+ * @since 0.2
43
+ */
44
+ public function save() {
45
+ wp_update_term((int) $this->term_id, $this->taxonomy, array('slug' => $this->document_id, 'name' => $this->document_id, 'description' => serialize($this->desc_array)));
46
+ }
47
+
48
+ /*
49
+ * provides a safe way to update the translations statuses when receiving "simultaneous" TMS callbacks
50
+ *
51
+ * @since 0.2
52
+ *
53
+ * @param string $locale
54
+ * @param string $status
55
+ * @param array $arr translations to add
56
+ */
57
+ protected function safe_translation_status_update($locale, $status, $arr = array()) {
58
+ global $wpdb;
59
+ $wpdb->query("LOCK TABLES $wpdb->term_taxonomy WRITE");
60
+ $d = $wpdb->get_var($wpdb->prepare("SELECT description FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $this->tt_id));
61
+ $d = unserialize($d);
62
+ $this->translations[$locale] = $d['lingotek']['translations'][$locale] = $status;
63
+ $d = array_merge($d, $arr); // optionally add a new translation
64
+ $d = serialize($d);
65
+ $wpdb->query($wpdb->prepare("UPDATE $wpdb->term_taxonomy SET description = %s WHERE term_taxonomy_id = %d", $d, $this->tt_id));
66
+ $wpdb->query("UNLOCK TABLES");
67
+ }
68
+
69
+ /*
70
+ * creates a new term translation object in DB
71
+ *
72
+ * @since 0.2
73
+ *
74
+ * @param int $object_id the id of the object to translate
75
+ * @param string $document_id Lingotek document id
76
+ * @param array $desc data to store in the Lingotek array
77
+ * @param string $taxonomy either 'post_translations' or 'term_translations'
78
+ */
79
+ protected static function _create($object_id, $document_id, $desc, $taxonomy) {
80
+ $terms = wp_get_object_terms($object_id, $taxonomy);
81
+ $term = array_pop($terms);
82
+
83
+ if (empty($term)) {
84
+ wp_insert_term($document_id, $taxonomy, array('description' => serialize($desc)));
85
+ }
86
+
87
+ // the translation already exists but was not managed by Lingotek
88
+ else {
89
+ if (is_array($old_desc = maybe_unserialize($term->description)))
90
+ $desc = array_merge($old_desc, $desc);
91
+ wp_update_term((int) $term->term_id, $taxonomy, array('slug' => $document_id, 'name' => $document_id, 'description' => serialize($desc)));
92
+ }
93
+
94
+ wp_set_object_terms($object_id, $document_id, $taxonomy);
95
+ }
96
+
97
+ /*
98
+ * disassociates translations from the Lingotek TMS
99
+ *
100
+ * @since 0.2
101
+ *
102
+ * @param bool $delete whether to delete the Lingotek document or not
103
+ */
104
+ public function disassociate($delete = true) {
105
+ $client = new Lingotek_API();
106
+
107
+ if (!$delete || $client->delete_document($this->document_id)) {
108
+ unset($this->desc_array['lingotek']);
109
+ $this->save();
110
+ }
111
+ }
112
+
113
+ /*
114
+ * uploads a modified source
115
+ *
116
+ * @since 0.2
117
+ *
118
+ * @param string $title
119
+ * @param object $content can be a post object, a term object
120
+ */
121
+ public function patch($title, $content, $external_url = '', $filters = array()) {
122
+ $client = new Lingotek_API();
123
+
124
+ $params = array(
125
+ 'title' => $title,
126
+ 'content' => $this->get_content($content),
127
+ 'external_url' => $external_url,
128
+ );
129
+ $params = array_merge($params, $filters);
130
+
131
+ $res = $client->patch_document($this->document_id, $params);
132
+
133
+ if ($res) {
134
+ $this->status = 'importing';
135
+ $this->translations = array_fill_keys(array_keys($this->translations), 'pending');
136
+ $this->save();
137
+ }
138
+ }
139
+
140
+ /*
141
+ * checks the status of source document
142
+ *
143
+ * @since 0.2
144
+ */
145
+ public function source_status() {
146
+ $client = new Lingotek_API();
147
+
148
+ if ('importing' == $this->status && $client->document_exists($this->document_id)) {
149
+ $this->status = 'current';
150
+ $this->save();
151
+ }
152
+ }
153
+
154
+ /*
155
+ * sets source status to ready
156
+ *
157
+ * @since 0.2
158
+ */
159
+ public function source_ready() {
160
+ $this->status = 'current';
161
+ $this->save();
162
+ }
163
+
164
+ /*
165
+ * requests a translation to Lingotek TMS
166
+ *
167
+ * @since 0.2
168
+ *
169
+ * @param string $locale
170
+ */
171
+ public function request_translation($locale) {
172
+ $client = new Lingotek_API();
173
+ $language = $this->pllm->get_language($locale);
174
+ $workflow = Lingotek_Model::get_profile_option('workflow_id', $this->type, $this->get_source_language(), $language);
175
+ $args = $workflow ? array('workflow_id' => $workflow) : array();
176
+
177
+ if (!$this->is_disabled_target($language) && empty($this->translations[$language->locale]) && $client->request_translation($this->document_id, $language->locale, $args)) {
178
+ $this->status = 'current';
179
+ $this->translations[$language->locale] = 'pending';
180
+ $this->save();
181
+ }
182
+ }
183
+
184
+ /*
185
+ * requests translations to Lingotek TMS
186
+ *
187
+ * @since 0.2
188
+ *
189
+ * @param object $source_language language of the source
190
+ */
191
+ protected function _request_translations($source_language) {
192
+ $client = new Lingotek_API();
193
+
194
+ foreach ($this->pllm->get_languages_list() as $lang) {
195
+ if ($source_language->slug != $lang->slug && !$this->is_disabled_target($lang) && empty($this->translations[$lang->locale])) {
196
+ $workflow = Lingotek_Model::get_profile_option('workflow_id', $this->type, $source_language, $lang);
197
+ $args = $workflow ? array('workflow_id' => $workflow) : array();
198
+ $client->request_translation($this->document_id, $lang->lingotek_locale, $args);
199
+ $this->status = 'current';
200
+ $this->translations[$lang->locale] = 'pending';
201
+ }
202
+ }
203
+
204
+ $this->save();
205
+ }
206
+
207
+ /*
208
+ * checks the translations status of a document
209
+ *
210
+ * @since 0.1
211
+ */
212
+ public function translations_status() {
213
+ $client = new Lingotek_API();
214
+ $translations = $client->get_translations_status($this->document_id); // key are Lingotek locales
215
+ foreach($this->translations as $locale => $status) {
216
+ $lingotek_locale = $this->pllm->get_language($locale)->lingotek_locale;
217
+ if ('current' != $status && isset($translations[$lingotek_locale]) && 100 == $translations[$lingotek_locale])
218
+ $this->translations[$locale] = 'ready';
219
+ }
220
+
221
+ $this->save();
222
+ }
223
+
224
+ /*
225
+ * sets translation status to ready
226
+ *
227
+ * @since 0.1
228
+ * @uses Lingotek_Group::safe_translation_status_update() as the status can be automatically set by the TMS callback
229
+ */
230
+ public function translation_ready($locale) {
231
+ $this->safe_translation_status_update($locale, 'ready');
232
+ }
233
+
234
+ /*
235
+ * attempts to create all translations from an object
236
+ *
237
+ * @since 0.2
238
+ */
239
+ public function create_translations() {
240
+ if (isset($this->translations)) {
241
+ foreach ($this->translations as $locale => $status)
242
+ if ('pending' == $status || 'ready' == $status)
243
+ $this->create_translation($locale);
244
+ }
245
+ }
246
+
247
+ /*
248
+ * sets document status to edited
249
+ *
250
+ * @since 0.1
251
+ */
252
+ public function source_edited() {
253
+ $this->status = 'edited';
254
+ $this->translations = array_fill_keys(array_keys($this->translations), 'not-current');
255
+ $this->save();
256
+ }
257
+
258
+ /*
259
+ * returns true if at least one of the translations has the requested status
260
+ *
261
+ * @since 0.2
262
+ *
263
+ * @param string $status
264
+ * @return bool
265
+ */
266
+ public function has_translation_status($status) {
267
+ return array_intersect(array_keys($this->translations, $status), $this->pllm->get_languages_list(array('fields' => 'locale')));
268
+ }
269
+
270
+ /*
271
+ * checks if target should be automatically downloaded
272
+ *
273
+ * @since 0.2
274
+ *
275
+ * @param string $locale
276
+ * @return bool
277
+ */
278
+ public function is_automatic_download($locale) {
279
+ return 'automatic' == Lingotek_Model::get_profile_option('download', $this->type, $this->get_source_language(), $this->pllm->get_language($locale));
280
+ }
281
+
282
+ /*
283
+ * checks if translation is disabled for a target language
284
+ *
285
+ * @since 0.2
286
+ *
287
+ * @param string $type post type or taxonomy
288
+ * @param object $language
289
+ */
290
+ public function is_disabled_target($language) {
291
+ $profile = Lingotek_Model::get_profile($this->type, $language);
292
+ return isset($profile['targets'][$language->slug]) && 'disabled' == $profile['targets'][$language->slug];
293
+ }
294
+ }
include/http.php ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * a simple wrapper to wp http functions
5
+ *
6
+ * @since 0.1
7
+ */
8
+ class Lingotek_HTTP {
9
+ protected $headers = array();
10
+
11
+ /*
12
+ * formats a request as multipart
13
+ * greatly inspired from mailgun wordpress plugin
14
+ *
15
+ * @since 0.1
16
+ */
17
+ public function format_as_multipart(&$body) {
18
+ $boundary = '----------------------------32052ee8fd2c'; // arbitrary boundary
19
+
20
+ $this->headers['Content-Type'] = 'multipart/form-data; boundary=' . $boundary;
21
+
22
+ $data = '';
23
+
24
+ foreach ($body as $key => $value) {
25
+ if (is_array($value)) {
26
+ // FIXME is this block useful for Lingotek ?
27
+ foreach($value as $k => $v) {
28
+ $data .= '--' . $boundary . "\r\n";
29
+ $data .= 'Content-Disposition: form-data; name="' . $key . '[' . $k . ']"' . "\r\n\r\n";
30
+ $data .= $v . "\r\n";
31
+ }
32
+ }
33
+ else {
34
+ $data .= '--' . $boundary ."\r\n";
35
+ $data .= 'Content-Disposition: form-data; name="' . $key . '"' . "\r\n\r\n";
36
+ $data .= $value . "\r\n";
37
+ }
38
+ }
39
+
40
+ $body = $data . '--' . $boundary . '--';
41
+ }
42
+
43
+ /*
44
+ * send a POST request
45
+ *
46
+ * @since 0.1
47
+ */
48
+ public function post($url, $args = array()) {
49
+ Lingotek::log("POST " . $url);
50
+ if (!empty($args)) {
51
+ Lingotek::log($args);
52
+ }
53
+ return wp_remote_post($url, array('headers' => $this->headers, 'body' => $args));
54
+ }
55
+
56
+ /*
57
+ * send a GET request
58
+ *
59
+ * @since 0.1
60
+ */
61
+ public function get($url, $args = array()) {
62
+ Lingotek::log("GET " . $url);
63
+ if (!empty($args)) {
64
+ Lingotek::log($args);
65
+ }
66
+ return wp_remote_get($url, array('headers' => $this->headers, 'body' => $args, 'timeout' => 8));
67
+ }
68
+
69
+ /*
70
+ * send a DELETE request
71
+ *
72
+ * @since 0.1
73
+ */
74
+ public function delete($url, $args = array()) {
75
+ Lingotek::log("DELETE " . $url);
76
+ if (!empty($args)) {
77
+ Lingotek::log($args);
78
+ }
79
+ return wp_remote_request($url, array('method' => 'DELETE', 'headers' => $this->headers, 'body' => $args));
80
+ }
81
+
82
+ /*
83
+ * send a PATCH request
84
+ *
85
+ * @since 0.1
86
+ */
87
+ public function patch($url, $args = array()) {
88
+ Lingotek::log("PATCH " . $url);
89
+ if (!empty($args)) {
90
+ Lingotek::log($args);
91
+ }
92
+ return wp_remote_request($url, array('method' => 'PATCH', 'headers' => $this->headers, 'body' => $args));
93
+ }
94
+ }
include/model.php ADDED
@@ -0,0 +1,563 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Manages interactions with database
5
+ * Factory for Lingotek_Group objects
6
+ *
7
+ * @since 0.1
8
+ */
9
+ class Lingotek_Model {
10
+ public $pllm; // Polylang model
11
+
12
+ /*
13
+ * constructor
14
+ *
15
+ * @since 0.1
16
+ */
17
+ public function __construct() {
18
+ $this->pllm = $GLOBALS['polylang']->model;
19
+ }
20
+
21
+ /*
22
+ * get the strings groups as well as their count
23
+ *
24
+ * @since 0.2
25
+ *
26
+ * @return array
27
+ */
28
+ public static function get_strings() {
29
+ static $strings = array();
30
+ if (empty($strings)) {
31
+ PLL_Admin_Strings::init(); // enables sanitization filter
32
+
33
+ foreach (PLL_Admin_Strings::get_strings() as $string) {
34
+ $strings[$string['context']]['context'] = $string['context'];
35
+ $strings[$string['context']]['count'] = empty($strings[$string['context']]['count']) ? 1 : $strings[$string['context']]['count'] + 1;
36
+ }
37
+ $strings = array_values($strings);
38
+ }
39
+ return $strings;
40
+ }
41
+
42
+ /*
43
+ * create a translation group object from a translation term
44
+ *
45
+ * @since 0.2
46
+ *
47
+ * @param object $term
48
+ * @return object
49
+ */
50
+ protected function convert_term($term) {
51
+ switch($term->taxonomy) {
52
+ case 'term_translations':
53
+ return new Lingotek_Group_Term($term, $this->pllm);
54
+
55
+ case 'post_translations':
56
+ $class = $term->name == $term->slug ? 'Lingotek_Group_Post' : 'Lingotek_Group_String';
57
+ return new $class($term, $this->pllm);
58
+ }
59
+ }
60
+
61
+ /*
62
+ * get the translation term of an object
63
+ *
64
+ * @since 0.2
65
+ *
66
+ * @param string $type either 'post' or 'term' or 'string'
67
+ * @param int|string $id post id or term id or strings translations group name
68
+ * @return object translation term
69
+ */
70
+ public function get_group($type, $id) {
71
+ switch ($type) {
72
+ case 'post':
73
+ case 'term':
74
+ return ($term = $this->pllm->get_object_term((int) $id, $type . '_translations')) && !empty($term) ? $this->convert_term($term) : false;
75
+ case 'string':
76
+ if (is_numeric($id)) {
77
+ $strings = self::get_strings();
78
+ $id = $strings[$id]['context'];
79
+ }
80
+ return ($term = get_term_by('name', $id, 'post_translations')) && !empty($term) ? $this->convert_term($term) : false;
81
+ default:
82
+ return false;
83
+ }
84
+ }
85
+
86
+ /*
87
+ * get the translation term of an object by its Lingotek document id
88
+ *
89
+ * @since 0.2
90
+ *
91
+ * @param string|object $document_id
92
+ * @return object translation term
93
+ */
94
+ public function get_group_by_id($document_id) {
95
+ // we already passed a translation group object
96
+ if (is_object($document_id))
97
+ return $document_id;
98
+
99
+ $terms = get_terms(array('post_translations', 'term_translations'), array('slug' => $document_id));
100
+ return is_wp_error($terms) || empty($terms) ? false : $this->convert_term(reset($terms));
101
+ }
102
+
103
+ /*
104
+ * get a translation profile
105
+ *
106
+ * @since 0.2
107
+ *
108
+ * @param string $type post type or taxonomy
109
+ * @param object $language
110
+ * @return array
111
+ */
112
+ static public function get_profile($type, $language) {
113
+ $content_types = get_option('lingotek_content_type');
114
+ $profiles = get_option('lingotek_profiles');
115
+
116
+ // default profile is manual except for post
117
+ $default = 'post' == $type ? 'automatic' : 'manual';
118
+
119
+ $profile = isset($content_types[$type]['sources'][$language->slug]) ?
120
+ $content_types[$type]['sources'][$language->slug] :
121
+ (isset($content_types[$type]['profile']) ? $content_types[$type]['profile'] : $default);
122
+
123
+ return $profiles[$profile];
124
+ }
125
+
126
+ static public function get_prefs() {
127
+ $default = array(
128
+ 'download_post_status' => Lingotek_Group_Post::SAME_AS_SOURCE,
129
+ 'auto_upload_post_statuses' => array(
130
+ 'draft' => 0, // ignore auto-upload
131
+ 'pending' => 1, // auto-upload
132
+ 'publish' => 1,
133
+ 'future' => 1,
134
+ 'private' => 0,
135
+ )
136
+ );
137
+ $prefs = get_option('lingotek_prefs', $default);
138
+ return $prefs;
139
+ }
140
+
141
+ /*
142
+ * get a profile option
143
+ *
144
+ * @since 0.2
145
+ *
146
+ * @param string $item 'project_id' | 'workflow_id' | 'upload' | 'download'
147
+ * @param string $type post type or taxonomy
148
+ * @param object $source_language
149
+ * @param object $target_language optional, needed to get custom target informations 'workflow_id' | 'download'
150
+ * @return string | bool either the option or false if the translation is disabled
151
+ */
152
+ static public function get_profile_option($item, $type, $source_language, $target_language = false) {
153
+ $profile = self::get_profile($type, $source_language);
154
+ if ('disabled' == $profile['profile'])
155
+ return false;
156
+
157
+ if (!empty($target_language) && isset($profile['targets'][$target_language->slug]) && !empty($profile['custom'][$item][$target_language->slug]))
158
+ return $profile['custom'][$item][$target_language->slug];
159
+
160
+ if (!empty($profile[$item]))
161
+ return $profile[$item];
162
+
163
+ $defaults = get_option('lingotek_defaults');
164
+ return $defaults[$item];
165
+ }
166
+
167
+ /*
168
+ * uploads a new post to Lingotek TMS
169
+ *
170
+ * @since 0.1
171
+ *
172
+ * @param int $post_id
173
+ */
174
+ public function upload_post($post_id) {
175
+ $post = get_post($post_id);
176
+ $language = $this->pllm->get_post_language($post_id);
177
+ if (empty($post) || empty($language))
178
+ return;
179
+
180
+ $profile = self::get_profile($post->post_type, $language);
181
+ if ('disabled' == $profile['profile'])
182
+ return;
183
+
184
+ $client = new Lingotek_API();
185
+ $external_url = add_query_arg(array('lingotek' => 1, 'document_id' => '{document_id}', 'locale' => '{locale}', 'type' => 'get'), site_url());
186
+
187
+ $params = array(
188
+ 'title' => $post->post_title,
189
+ 'content' => Lingotek_Group_Post::get_content($post),
190
+ 'locale_code' => $language->lingotek_locale,
191
+ 'project_id' => self::get_profile_option('project_id', $post->post_type, $language),
192
+ 'workflow_id' => self::get_profile_option('workflow_id', $post->post_type, $language),
193
+ 'external_url' => $external_url,
194
+ );
195
+
196
+ $filter_ids = array();
197
+ if (self::get_profile_option('primary_filter_id', $post->post_type, $language)) {
198
+ $filter_ids['fprm_id'] = self::get_profile_option('primary_filter_id',$post->post_type, $language);
199
+ }
200
+ if (self::get_profile_option('secondary_filter_id', $post->post_type, $language)) {
201
+ $filter_ids['fprm_subfilter_id'] = self::get_profile_option('secondary_filter_id',$post->post_type, $language);
202
+ }
203
+ $params = array_merge($params, $filter_ids);
204
+
205
+ if (($document = $this->get_group('post', $post_id)) && 'edited' == $document->status) {
206
+ $document->patch($post->post_title, $post, $external_url, $filter_ids);
207
+ }
208
+
209
+ elseif (!Lingotek_Group::$creating_translation) {
210
+ $document_id = $client->upload_document($params);
211
+
212
+ if ($document_id) {
213
+ Lingotek_Group_Post::create($post->ID , $language, $document_id);
214
+ }
215
+ }
216
+ }
217
+
218
+ /*
219
+ * uploads a new term to Lingotek TMS
220
+ *
221
+ * @since 0.2
222
+ *
223
+ * @param int $term_id
224
+ * @param string $taxonomy
225
+ */
226
+ public function upload_term($term_id, $taxonomy) {
227
+ $term = get_term($term_id, $taxonomy);
228
+ $language = $this->pllm->get_term_language($term_id);
229
+ if (empty($term) || empty($language))
230
+ return;
231
+
232
+ $profile = self::get_profile($taxonomy, $language);
233
+ if ('disabled' == $profile['profile'])
234
+ return;
235
+
236
+ $client = new Lingotek_API();
237
+
238
+ $params = array(
239
+ 'title' => $term->name,
240
+ 'content' => Lingotek_Group_Term::get_content($term),
241
+ 'locale_code' => $language->lingotek_locale,
242
+ 'project_id' => self::get_profile_option('project_id', $taxonomy, $language),
243
+ 'workflow_id' => self::get_profile_option('workflow_id', $taxonomy, $language)
244
+ );
245
+
246
+ $filter_ids = array();
247
+ if (self::get_profile_option('primary_filter_id', $taxonomy, $language)) {
248
+ $filter_ids['fprm_id'] = self::get_profile_option('primary_filter_id', $taxonomy, $language);
249
+ }
250
+ if (self::get_profile_option('secondary_filter_id', $taxonomy, $language)) {
251
+ $filter_ids['fprm_subfilter_id'] = self::get_profile_option('secondary_filter_id', $taxonomy, $language);
252
+ }
253
+ $params = array_merge($params, $filter_ids);
254
+
255
+ if (($document = $this->get_group('term', $term_id)) && 'edited' == $document->status) {
256
+ $document->patch($term->name, $term, '', $filter_ids);
257
+ }
258
+
259
+ elseif (!Lingotek_Group::$creating_translation) {
260
+ $document_id = $client->upload_document($params);
261
+
262
+ if ($document_id) {
263
+ Lingotek_Group_Term::create($term_id, $taxonomy , $language, $document_id);
264
+ }
265
+ }
266
+ }
267
+
268
+ /*
269
+ * uploads a strings group to Lingotek TMS
270
+ *
271
+ * @since 0.2
272
+ *
273
+ * @param string $group
274
+ */
275
+ public function upload_strings($group) {
276
+ $language = $this->pllm->get_language($this->pllm->options['default_lang']);
277
+ $profile = self::get_profile('string', $language);
278
+
279
+ if ('disabled' == $profile['profile'])
280
+ return;
281
+
282
+ if (is_numeric($group)) {
283
+ $strings = self::get_strings();
284
+ $group = $strings[$group]['context'];
285
+ }
286
+
287
+ // check that we have a valid string group
288
+ if (!in_array($group, wp_list_pluck(self::get_strings(), 'context')))
289
+ return;
290
+
291
+ $client = new Lingotek_API();
292
+
293
+ $params = array(
294
+ 'title' => $group,
295
+ 'content' => Lingotek_Group_String::get_content($group),
296
+ 'locale_code' => $language->lingotek_locale,
297
+ 'project_id' => self::get_profile_option('project_id', 'string', $language),
298
+ 'workflow_id' => self::get_profile_option('workflow_id', 'string', $language)
299
+ );
300
+
301
+ $filter_ids = array();
302
+ if (self::get_profile_option('primary_filter_id', 'string', $language)) {
303
+ $filter_ids['fprm_id'] = self::get_profile_option('primary_filter_id', 'string', $language);
304
+ }
305
+ if (self::get_profile_option('secondary_filter_id', 'string', $language)) {
306
+ $filter_ids['fprm_subfilter_id'] = self::get_profile_option('secondary_filter_id', 'string', $language);
307
+ }
308
+ $params = array_merge($params, $filter_ids);
309
+
310
+ if (($document = $this->get_group('string', $group)) && 'edited' == $document->status) {
311
+ $document->patch($group);
312
+ }
313
+ else {
314
+ $document_id = $client->upload_document($params);
315
+
316
+ if ($document_id) {
317
+ Lingotek_Group_String::create($group, $language, $document_id);
318
+ }
319
+ }
320
+ }
321
+
322
+ /*
323
+ * checks if the document can be upload to Lingotek
324
+ *
325
+ * @since 0.1
326
+ *
327
+ * @param string $type either 'post' or 'term'
328
+ * @param int $object_id post id or term id
329
+ * @return bool
330
+ */
331
+ // FIXME should I check for disabled profile here?
332
+ public function can_upload($type, $object_id) {
333
+ $document = $this->get_group($type, $object_id);
334
+
335
+ switch ($type) {
336
+ case 'string':
337
+ /*
338
+ $profile = self::get_profile('string', $this->pllm->get_language($this->pllm->options['default_lang']));
339
+ if ('disabled' == $profile['profile'])
340
+ return false;
341
+ */
342
+ if (empty($document))
343
+ return true;
344
+
345
+ elseif ($document->md5 != md5(Lingotek_Group_String::get_content($object_id))) { // check if source strings have not been modified
346
+ $document->source_edited();
347
+ return true;
348
+ }
349
+
350
+ return false;
351
+
352
+ case 'post':
353
+ case 'term':
354
+ // first check that a language is associated to the object
355
+ $language = call_user_func(array(&$this->pllm, 'get_'.$type.'_language'), $object_id);
356
+
357
+ // FIXME how to get profile to check if disabled?
358
+
359
+ return !empty($language) && (empty($document) ||
360
+ (1 == count($this->pllm->get_translations($type, $object_id)) && empty($document->source)) || // specific for terms as document is never empty
361
+ (isset($document) && 'edited' == $document->status && $document->source == $object_id));
362
+ }
363
+ }
364
+
365
+ /*
366
+ * deletes a post
367
+ *
368
+ * @since 0.1
369
+ *
370
+ * @param int $object_id post id
371
+ */
372
+ public function delete_post($object_id) {
373
+ if ($document = $this->get_group('post', $object_id)) {
374
+ $client = new Lingotek_API();
375
+
376
+ if ($document->source == $object_id) {
377
+ $client->delete_document($document->document_id);
378
+ }
379
+ else {
380
+ $this->pllm->delete_translation('post', $object_id);
381
+ $lang = $this->pllm->get_post_language($object_id);
382
+ $client->delete_translation($document->document_id, $lang->lingotek_locale);
383
+ }
384
+ }
385
+ }
386
+
387
+ /*
388
+ * deletes a term
389
+ *
390
+ * @since 0.2
391
+ *
392
+ * @param int $object_id term id
393
+ */
394
+ public function delete_term($object_id) {
395
+ if ($document = $this->get_group('term', $object_id)) {
396
+ $client = new Lingotek_API();
397
+
398
+ if ($document->source == $object_id) {
399
+ $client->delete_document($document->document_id);
400
+ }
401
+ else {
402
+ $lang = $this->pllm->get_term_language($object_id);
403
+ $this->pllm->delete_term_language($object_id);
404
+ $this->pllm->delete_translation('term', $object_id);
405
+ $client->delete_translation($document->document_id, $lang->lingotek_locale);
406
+ }
407
+ }
408
+ }
409
+
410
+ /*
411
+ * counts the number of targets per language
412
+ *
413
+ * @since 0.2
414
+ *
415
+ * @param array $groups array of serialized 'post_translations' or 'term_translations' description
416
+ * @return array number of targets per language
417
+ */
418
+ protected function get_target_count($groups) {
419
+ $targets = array_fill_keys($this->pllm->get_languages_list(array('fields' => 'slug')), 0);
420
+
421
+ foreach ($groups as $group) {
422
+ $group = unserialize($group);
423
+ if (isset($group['lingotek']['translations'])) {
424
+ foreach ($group['lingotek']['translations'] as $locale => $status) {
425
+ if ('current' == $status && $language = $this->pllm->get_language($locale))
426
+ $targets[$language->slug]++;
427
+ }
428
+ }
429
+ }
430
+ return $targets;
431
+ }
432
+
433
+ /*
434
+ * counts the number of sources and targets per language for a certain post type
435
+ *
436
+ * @since 0.2
437
+ *
438
+ * @param string $post_type
439
+ * @return array
440
+ */
441
+ public function count_posts($post_type) {
442
+ global $wpdb;
443
+
444
+ static $r = array();
445
+ if (!empty($r[$post_type]))
446
+ return $r[$post_type];
447
+
448
+ if (!post_type_exists($post_type))
449
+ return;
450
+
451
+ // gets all translations groups for the post type
452
+ $groups = $wpdb->get_col($wpdb->prepare("
453
+ SELECT DISTINCT tt.description FROM $wpdb->term_taxonomy AS tt
454
+ INNER JOIN $wpdb->term_relationships AS tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
455
+ INNER JOIN $wpdb->posts AS p ON p.ID = tr.object_id
456
+ WHERE tt.taxonomy = %s
457
+ AND p.post_type = %s
458
+ AND p.post_status NOT IN ('trash', 'auto-draft')",
459
+ 'post_translations', $post_type
460
+ ));
461
+
462
+ $targets = $this->get_target_count($groups);
463
+
464
+ foreach ($this->pllm->get_languages_list() as $language) {
465
+ // counts all the posts in one language
466
+ $n = $wpdb->get_var($wpdb->prepare("
467
+ SELECT COUNT(*) FROM $wpdb->term_relationships AS tr
468
+ INNER JOIN $wpdb->posts AS p ON p.ID = tr.object_id
469
+ WHERE tr.term_taxonomy_id = %d
470
+ AND p.post_type = %s
471
+ AND p.post_status NOT IN ('trash', 'auto-draft')",
472
+ $language->term_taxonomy_id, $post_type
473
+ ));
474
+
475
+ // if a post is not a target, then it is source
476
+ $sources[$language->slug] = $n - $targets[$language->slug];
477
+ }
478
+
479
+ // untranslated posts have no associated translation group in DB
480
+ // so let's count them indirectly
481
+
482
+ // counts the number of translated posts
483
+ $n_translated = $wpdb->get_var($wpdb->prepare("
484
+ SELECT COUNT(*) FROM $wpdb->term_relationships AS tr
485
+ INNER JOIN $wpdb->posts AS p ON p.ID = tr.object_id
486
+ INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
487
+ WHERE tt.taxonomy = %s
488
+ AND p.post_type = %s
489
+ AND p.post_status NOT IN ('trash', 'auto-draft')",
490
+ 'post_translations', $post_type
491
+ ));
492
+
493
+
494
+
495
+ // untranslated = total - translated
496
+ // total of posts translations groups = untranslated + number of translation groups stored in DB
497
+ $count_posts = (array) wp_count_posts($post_type);
498
+ unset($count_posts['trash'], $count_posts['auto-draft']); // don't count trash and auto-draft
499
+ $total = array_sum($count_posts) - $n_translated + count($groups);
500
+
501
+ return $r[$post_type] = compact('sources', 'targets', 'total');
502
+ }
503
+
504
+ /*
505
+ * counts the number of sources and targets per language for a certain taxonomy
506
+ *
507
+ * @since 0.2
508
+ *
509
+ * @param string $taxonomy
510
+ * @return array
511
+ */
512
+ public function count_terms($taxonomy) {
513
+ global $wpdb;
514
+
515
+ static $r = array();
516
+ if (!empty($r[$taxonomy]))
517
+ return $r[$taxonomy];
518
+
519
+ if (!taxonomy_exists($taxonomy))
520
+ return;
521
+
522
+ // gets all translations groups for the taxonomy
523
+ $groups = $wpdb->get_col($wpdb->prepare("
524
+ SELECT DISTINCT tt1.description FROM $wpdb->term_taxonomy AS tt1
525
+ INNER JOIN $wpdb->term_relationships AS tr ON tt1.term_taxonomy_id = tr.term_taxonomy_id
526
+ INNER JOIN $wpdb->term_taxonomy AS tt2 ON tt2.term_id = tr.object_id
527
+ WHERE tt1.taxonomy = %s
528
+ AND tt2.taxonomy = %s",
529
+ 'term_translations', $taxonomy
530
+ ));
531
+
532
+ $targets = $this->get_target_count($groups);
533
+
534
+ foreach ($this->pllm->get_languages_list() as $language) {
535
+ // counts all the terms in one language
536
+ $n = $wpdb->get_var($wpdb->prepare("
537
+ SELECT COUNT(*) FROM $wpdb->term_relationships AS tr
538
+ INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = tr.object_id
539
+ WHERE tr.term_taxonomy_id = %d
540
+ AND tt.taxonomy = %s",
541
+ $language->tl_term_taxonomy_id, $taxonomy
542
+ ));
543
+
544
+ // if a term is not a target, then it is a source
545
+ $sources[$language->slug] = $n - $targets[$language->slug];
546
+ }
547
+
548
+ $total = count($groups);
549
+
550
+ // default categories are created by Polylang in all languages
551
+ // don't count them as sources if they are not associated to the TMS
552
+ if ('category' === $taxonomy) {
553
+ $term_id = get_option('default_category');
554
+ $group = $this->get_group('term', $term_id);
555
+ foreach($this->pllm->get_languages_list() as $language) {
556
+ if (empty($group->source) || ($group->get_source_language()->slug != $language->slug && empty($group->translations[$language->locale])))
557
+ $sources[$language->slug]--;
558
+ }
559
+ }
560
+
561
+ return $r[$taxonomy] = compact('sources', 'targets', 'total');
562
+ }
563
+ }
include/pointer.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // code borrowed from Polylang
3
+ class Lingotek_Pointer {
4
+ protected $args;
5
+
6
+ /**
7
+ * Constructor
8
+ * Enqueues the pointer script
9
+ *
10
+ * List of parameters accepted in $args:
11
+ *
12
+ * pointer => required, unique identifier of the pointer
13
+ * id => required, the pointer will be attached to this html id
14
+ * position => optional array, if used both sub parameters are required
15
+ * edge => 'top' or 'bottom'
16
+ * align => 'right' or 'left'
17
+ * width => optional, the width in px
18
+ * title => required, title
19
+ * content => required, content
20
+ * buttons => optional array of arrays, by default the pointer uses the standard dismiss button offered by WP
21
+ * label => the label of the button
22
+ * link => optional link for the button. By default, the button just dismisses the pointer
23
+ *
24
+ * @since 1.0.1
25
+ *
26
+ * @param array $args
27
+ */
28
+ public function __construct($args) {
29
+ $this->args = $args;
30
+ add_action('admin_enqueue_scripts', array(&$this, 'enqueue_scripts'));
31
+ }
32
+
33
+ /**
34
+ * Enqueue JavaScript and styles if the pointer has not been dismissed
35
+ *
36
+ * @since 1.0.1
37
+ */
38
+ public function enqueue_scripts() {
39
+ $dismissed = explode(',', get_user_meta(get_current_user_id(), 'dismissed_wp_pointers', true));
40
+
41
+ // comment the two lines below to make the pointer non dismissable
42
+ if (in_array($this->args['pointer'], $dismissed) || !current_user_can('manage_options'))
43
+ return;
44
+
45
+ // Add pointer JavaScript
46
+ add_action('admin_print_footer_scripts', array(&$this, 'print_js'));
47
+
48
+ wp_enqueue_style('wp-pointer');
49
+ wp_enqueue_script('wp-pointer');
50
+ }
51
+
52
+ /**
53
+ * Adds the JavaScript of our pointer to the page
54
+ *
55
+ * @since 1.0.1
56
+ */
57
+ public function print_js() {
58
+
59
+ // add optional buttons
60
+ if (!empty($this->args['buttons'])) {
61
+ $b = "
62
+ var widget = pointer.pointer('widget');
63
+ var buttons = $('.wp-pointer-buttons', widget);
64
+ $('a.close', widget).remove();"; // removes the WP button
65
+
66
+ // all the buttons use the standard WP ajax action to remember the pointer has been dismissed
67
+ foreach ($this->args['buttons'] as $button) {
68
+ $b .= sprintf("
69
+ $('<a>').addClass('%s').html('%s').css('margin-left', '10px').click(function() {
70
+ $.post(ajaxurl, {
71
+ pointer: '%s',
72
+ action: 'dismiss-wp-pointer'
73
+ }, function(response) {
74
+ %s
75
+ });
76
+ }).appendTo(buttons);",
77
+ empty($button['link']) ? 'button' : 'button button-primary',
78
+ $button['label'],
79
+ $this->args['pointer'],
80
+ empty($button['link']) ? "pointer.pointer('close')" : sprintf("location.href = '%s'", $button['link'])
81
+ );
82
+ }
83
+ }
84
+
85
+ $js = sprintf("
86
+ //<![CDATA[
87
+ jQuery(document).ready(function($) {
88
+ var pointer = $('#%s').pointer({
89
+ content: '%s',
90
+ %s
91
+ %s
92
+ });
93
+ pointer.pointer('open');
94
+ %s
95
+ });
96
+ // ]]>",
97
+ $this->args['id'],
98
+ sprintf('<h3>%s</h3><p>%s</p>', $this->args['title'], $this->args['content']),
99
+ empty($this->args['position']) ? '' : sprintf('position: {edge: "%s", align: "%s",},', $this->args['position']['edge'], $this->args['position']['align']),
100
+ empty($this->args['width']) ? '' : sprintf('pointerWidth: %d,', $this->args['width']),
101
+ empty($b) ? '' : $b
102
+ );
103
+ echo "<script type='text/javascript'>" .$js. "</script>";
104
+ }
105
+ }
106
+
107
+ ?>
js/defaults.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function toggleTextbox () {
2
+ var box = document.getElementById('new_project');
3
+ if (box.style.display === 'none') {
4
+ box.style.display = 'inline';
5
+ document.getElementById('project_id').style.display = 'none';
6
+ document.getElementById('update_callback').style.visibility = 'hidden';
7
+ document.getElementById('callback_label').style.display = 'none';
8
+ document.getElementById('update_callback').checked = false;
9
+ document.getElementById('create').innerHTML = '- Use Existing Project';
10
+ }
11
+ else if (box.style.display === 'inline') {
12
+ box.style.display = 'none';
13
+ document.getElementById('project_id').style.display= 'initial';
14
+ document.getElementById('update_callback').style.visibility = 'visible';
15
+ document.getElementById('callback_label').style.display = 'initial';
16
+ document.getElementById('create').innerHTML = '+ Create New Project';
17
+ }
18
+ }
js/progress.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+
3
+ function lingotek_progress(i) {
4
+ if (i < lingotek_data.ids.length) {
5
+ var data = {
6
+ action: 'lingotek_progress_'+lingotek_data.action,
7
+ taxonomy: lingotek_data.taxonomy, // empty for posts
8
+ id: lingotek_data.ids[i],
9
+ _lingotek_nonce: lingotek_data.nonce
10
+ }
11
+
12
+ $.post(ajaxurl, data , function(response) {
13
+ $("#lingotek-progressbar").progressbar({
14
+ value: ++i / lingotek_data.ids.length * 100
15
+ });
16
+ lingotek_progress(i);
17
+ });
18
+ }
19
+
20
+ else
21
+ jQuery(location).attr('href', lingotek_data.sendback);
22
+ }
23
+
24
+ if ('undefined' != typeof(lingotek_data)) {
25
+ if ('' == lingotek_data.warning || confirm(lingotek_data.warning)) {
26
+ var d = $("#lingotek-progressdialog");
27
+ if (d.length) {
28
+ d.dialog({
29
+ dialogClass: "wp-dialog",
30
+ width: 400
31
+ });
32
+ $("#lingotek-progressbar").progressbar();
33
+ lingotek_progress(0);
34
+ }
35
+ }
36
+ else
37
+ jQuery(location).attr('href', lingotek_data.sendback);
38
+ }
39
+ });
languages/wp-lingotek-fr_FR.mo ADDED
Binary file
languages/wp-lingotek-fr_FR.po ADDED
@@ -0,0 +1,1203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2015 Lingotek Translation
2
+ # This file is distributed under the same license as the Lingotek Translation package.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: Lingotek Translation 1.0.0\n"
6
+ "Report-Msgid-Bugs-To: http://wordpress.org/support/plugin/wp-lingotek\n"
7
+ "POT-Creation-Date: 2015-06-29 15:40:38+00:00\n"
8
+ "MIME-Version: 1.0\n"
9
+ "Content-Type: text/plain; charset=UTF-8\n"
10
+ "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2015-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
+
15
+ #: admin/actions.php:21 admin/actions.php:354
16
+ msgid "You are about to overwrite existing translations. Are you sure?"
17
+ msgstr "Vous êtes sur le point d'écraser les traductions existantes. Es-tu sûr?"
18
+
19
+ #: admin/actions.php:26
20
+ msgid "Upload to Lingotek"
21
+ msgstr "Télécharger sur Lingotek"
22
+
23
+ #: admin/actions.php:27
24
+ msgid "Uploading..."
25
+ msgstr "Téléchargement..."
26
+
27
+ #: admin/actions.php:28
28
+ msgid "Upload this item to Lingotek TMS"
29
+ msgstr "Téléchargez cet article sur Lingotek TMS"
30
+
31
+ #: admin/actions.php:32
32
+ msgid "Request translations"
33
+ msgstr "Traductions de la demande"
34
+
35
+ #: admin/actions.php:33
36
+ msgid "Requesting translations..."
37
+ msgstr "Demander des traductions..."
38
+
39
+ #: admin/actions.php:34
40
+ msgid "Request translations of this item to Lingotek TMS"
41
+ msgstr "Demander des traductions de ce point de Lingotek TMS"
42
+
43
+ #: admin/actions.php:38
44
+ msgid "Update translations status"
45
+ msgstr "Mise à jour de statut de la traduction"
46
+
47
+ #: admin/actions.php:39
48
+ msgid "Updating translations status..."
49
+ msgstr "Mise à jour de statut de la traduction..."
50
+
51
+ #: admin/actions.php:40
52
+ msgid "Update translations status of this item in Lingotek TMS"
53
+ msgstr "Mise à jour de statut de la traduction de ce point de Lingotek TMS"
54
+
55
+ #: admin/actions.php:44 admin/admin.php:208
56
+ msgid "Download translations"
57
+ msgstr "Télécharger les traductions"
58
+
59
+ #: admin/actions.php:45
60
+ msgid "Downloading translations..."
61
+ msgstr "Téléchargement des traductions..."
62
+
63
+ #: admin/actions.php:46
64
+ msgid "Download translations of this item from Lingotek TMS"
65
+ msgstr "Télécharger les traductions de ce point de Lingotek TMS"
66
+
67
+ #: admin/actions.php:50
68
+ msgid "Disassociate translations"
69
+ msgstr "Dissocier les traductions"
70
+
71
+ #: admin/actions.php:51
72
+ msgid "Disassociating translations..."
73
+ msgstr "Association des traductions..."
74
+
75
+ #: admin/actions.php:52
76
+ msgid "Disassociate the translations of this item from Lingotek TMS"
77
+ msgstr "Dissocier les traductions de ce point de Lingotek TMS"
78
+
79
+ #: admin/actions.php:59
80
+ msgid "Upload Now"
81
+ msgstr "Télécharger maintenant"
82
+
83
+ #: admin/actions.php:64
84
+ msgid "Importing source"
85
+ msgstr "Importation de source"
86
+
87
+ #: admin/actions.php:69
88
+ msgid "Source uploaded"
89
+ msgstr "Source téléchargée"
90
+
91
+ #: admin/actions.php:74
92
+ msgid "Request a translation"
93
+ msgstr "Une demande de traduction"
94
+
95
+ #: admin/actions.php:79 admin/tutorial/content.php:72
96
+ msgid "In Progress"
97
+ msgstr "En cours"
98
+
99
+ #: admin/actions.php:84
100
+ msgid "Ready to download"
101
+ msgstr "Prêt à télécharger"
102
+
103
+ #: admin/actions.php:89
104
+ msgid "current"
105
+ msgstr "actuel"
106
+
107
+ #: admin/actions.php:94
108
+ msgid ""
109
+ "The target translation is no longer current as the source content has been "
110
+ "updated"
111
+ msgstr "La traduction de cible n'est plus actuelle que le contenu de la source a été mis à jour"
112
+
113
+ #: admin/admin.php:85 admin/admin.php:105 admin/settings.php:2
114
+ msgid "Settings"
115
+ msgstr "Paramètres"
116
+
117
+ #: admin/admin.php:96
118
+ msgid "Translation"
119
+ msgstr "Traduction"
120
+
121
+ #: admin/admin.php:103 admin/tutorial/faq.php:58
122
+ msgid "Translation Dashboard"
123
+ msgstr "Tableau de bord de traduction"
124
+
125
+ #: admin/admin.php:103 admin/view-dashboard.php:2
126
+ msgid "Dashboard"
127
+ msgstr "Tableau de bord"
128
+
129
+ #: admin/admin.php:104
130
+ msgid "Translation Management"
131
+ msgstr "Gestion des traductions"
132
+
133
+ #: admin/admin.php:104 admin/view-manage.php:2
134
+ msgid "Manage"
135
+ msgstr "Gérer"
136
+
137
+ #: admin/admin.php:105
138
+ msgid "Translation Settings"
139
+ msgstr "Paramètres de translation"
140
+
141
+ #: admin/admin.php:106
142
+ msgid "Lingotek Tutorial"
143
+ msgstr "Tutoriel de Lingotek"
144
+
145
+ #: admin/admin.php:106 admin/view-tutorial.php:5
146
+ msgid "Tutorial"
147
+ msgstr "Tutorial"
148
+
149
+ #: admin/admin.php:130
150
+ msgid "Your Lingotek account has been successfully connected."
151
+ msgstr "Votre compte de Lingotek a été connecté avec succès."
152
+
153
+ #: admin/admin.php:133
154
+ msgid ""
155
+ "Your Lingotek account was not connected.\tThe Access Token received was "
156
+ "invalid."
157
+ msgstr "Votre compte de Lingotek n'était pas connecté\t. Le jeton d'accès reçue n'était pas valide."
158
+
159
+ #: admin/admin.php:147
160
+ msgid "Your community has been successfully saved."
161
+ msgstr "Votre collectivité a été enregistrée avec succès."
162
+
163
+ #: admin/admin.php:152
164
+ msgid "Select and save the community that you would like to use."
165
+ msgstr "Sélectionnez et enregistrez la communauté que vous souhaitez utiliser."
166
+
167
+ #: admin/admin.php:176
168
+ msgid "Redirecting to Lingotek to connect your account..."
169
+ msgstr "Redirection vers Lingotek pour vous connecter à votre compte..."
170
+
171
+ #: admin/admin.php:197 lingotek.php:199
172
+ msgid "Manual"
173
+ msgstr "Manuelle"
174
+
175
+ #: admin/admin.php:198 lingotek.php:193
176
+ msgid "Automatic"
177
+ msgstr "Automatique"
178
+
179
+ #: admin/admin.php:203
180
+ msgid "Upload content"
181
+ msgstr "Télécharger du contenu"
182
+
183
+ #: admin/admin.php:205
184
+ msgid "How should new and modified content be uploaded to Lingotek?"
185
+ msgstr "Comment les nouveaux et mis à jour le contenu doit être téléchargé de Lingotek ?"
186
+
187
+ #: admin/admin.php:210
188
+ msgid "How should completed translations be downloaded to WordPress?"
189
+ msgstr "Comment les traductions dûment remplies doivent être téléchargées pour WordPress ?"
190
+
191
+ #: admin/admin.php:213
192
+ msgid "Default Project"
193
+ msgstr "Projet par défaut"
194
+
195
+ #: admin/admin.php:213
196
+ msgid "Project"
197
+ msgstr "Projet"
198
+
199
+ #: admin/admin.php:215
200
+ msgid "Changes will affect new entities only"
201
+ msgstr "Changements affecteront les nouvelles entités seulement"
202
+
203
+ #: admin/admin.php:218
204
+ msgid "Default Workflow"
205
+ msgstr "Flux de travail par défaut"
206
+
207
+ #: admin/admin.php:218
208
+ msgid "Workflow"
209
+ msgstr "Flux de travail"
210
+
211
+ #: admin/admin.php:222
212
+ msgid "Primary Filter"
213
+ msgstr "Filtre primaire"
214
+
215
+ #: admin/admin.php:226
216
+ msgid "Secondary Filter"
217
+ msgstr "Filtre secondaire"
218
+
219
+ #: admin/admin.php:286
220
+ msgid "Your Community currently has no projects."
221
+ msgstr "Votre collectivité n'a actuellement aucun projet."
222
+
223
+ #: admin/admin.php:289
224
+ msgid "Projects could not be refreshed"
225
+ msgstr "Impossible d'actualiser des projets"
226
+
227
+ #: admin/admin.php:302
228
+ msgid "Workflows could not be refreshed"
229
+ msgstr "Impossible d'actualiser les workflows"
230
+
231
+ #: admin/admin.php:371
232
+ msgid ""
233
+ "Your <a href=\"%s\"><i>Defaults</i></a> have been updated to valid options "
234
+ "for this community."
235
+ msgstr "Vos <a href=\"%s\"><i>paramètres par défaut</i></a> ont été upgradés dans options valides pour cette communauté."
236
+
237
+ #: admin/admin.php:398
238
+ msgid "Strings groups"
239
+ msgstr "Groupes de chaînes"
240
+
241
+ #: admin/admin.php:486
242
+ msgid "Lingotek Settings"
243
+ msgstr "Paramètres de Lingotek"
244
+
245
+ #: admin/content-table.php:61
246
+ msgid "%s source"
247
+ msgstr "%s source"
248
+
249
+ #: admin/content-table.php:85
250
+ msgid "Use content type default"
251
+ msgstr "Utilisez la valeur par défaut du type de contenu"
252
+
253
+ #: admin/content-table.php:153
254
+ msgid "Content Type"
255
+ msgstr "Type de contenu"
256
+
257
+ #: admin/content-table.php:154
258
+ msgid "Profile"
259
+ msgstr "Profil"
260
+
261
+ #: admin/content-table.php:155
262
+ msgid "Fields"
263
+ msgstr "Champs"
264
+
265
+ #: admin/manage/view-string-groups.php:1 admin/view-manage.php:7
266
+ msgid "String Groups"
267
+ msgstr "Groupes de chaîne"
268
+
269
+ #: admin/manage/view-string-groups.php:2
270
+ msgid ""
271
+ "Manage group translation of system, widget, and plugin-specific strings. "
272
+ "View individual strings on the <a href=\"%s\"><b>Strings</b></a> page."
273
+ msgstr "Gérer la traduction de groupe du système, widget et des chaînes spécifiques plugin. Affichage des chaînes individuelles sur la page <a href=\"%s\"><b>chaînes</b></a> ."
274
+
275
+ #: admin/manage/view-string-groups.php:9
276
+ msgid "The strings translation is disabled in %sContent Type Configuration%s."
277
+ msgstr "La traduction de chaînes est désactivée dans %sla Configuration de Type de contenu%s."
278
+
279
+ #: admin/manage/view-strings.php:1 admin/settings/view-content.php:29
280
+ #: admin/view-manage.php:8
281
+ msgid "Strings"
282
+ msgstr "Chaînes"
283
+
284
+ #: admin/manage/view-strings.php:1
285
+ msgid "Edit on Polylang Strings Translation page"
286
+ msgstr "Modifier sur la page de traduction de chaînes de Polylang"
287
+
288
+ #: admin/manage/view-strings.php:45
289
+ msgid "Search translations"
290
+ msgstr "Recherche de traductions"
291
+
292
+ #: admin/profiles-table.php:44
293
+ msgid "No content types"
294
+ msgstr "Aucun des types de contenu"
295
+
296
+ #: admin/profiles-table.php:45
297
+ msgid "1 content type"
298
+ msgid_plural "%d content types"
299
+ msgstr[0] "1 type de contenu"
300
+ msgstr[1] "%d types de contenu"
301
+
302
+ #: admin/profiles-table.php:63
303
+ msgid "Edit"
304
+ msgstr "Edit"
305
+
306
+ #: admin/profiles-table.php:70 admin/settings/view-edit-profile.php:195
307
+ msgid "You are about to permanently delete this profile. Are you sure?"
308
+ msgstr "Vous êtes sur le point de supprimer définitivement ce profil. Es-tu sûr?"
309
+
310
+ #: admin/profiles-table.php:71 admin/settings/view-edit-profile.php:196
311
+ msgid "Delete"
312
+ msgstr "Supprimer"
313
+
314
+ #: admin/profiles-table.php:86 admin/settings/view-edit-profile.php:64
315
+ msgid "Profile name"
316
+ msgstr "Nom du profil"
317
+
318
+ #: admin/profiles-table.php:87
319
+ msgid "Usage"
320
+ msgstr "Utilisation"
321
+
322
+ #: admin/profiles-table.php:88
323
+ msgid "Actions"
324
+ msgstr "Actions"
325
+
326
+ #: admin/settings/connect-account.php:16
327
+ msgid "Connect Your Account"
328
+ msgstr "Se connecter à votre compte"
329
+
330
+ #: admin/settings/connect-account.php:19
331
+ msgid ""
332
+ "Get started by clicking the button below to connect your Lingotek account to "
333
+ "this Wordpress installation."
334
+ msgstr "Commencez en cliquant sur le bouton ci-dessous pour vous connecter à votre compte de Lingotek pour cette installation de Wordpress."
335
+
336
+ #: admin/settings/connect-account.php:24
337
+ msgid "Connect New Account"
338
+ msgstr "Se connecter nouveau compte"
339
+
340
+ #: admin/settings/connect-account.php:28
341
+ msgid ""
342
+ "Do you already have a Lingotek account? <a href=\"%s\">Connect Lingotek "
343
+ "Account</a>"
344
+ msgstr "Vous possédez déjà un compte de Lingotek? <a href=\"%s\">Connecter compte Lingotek</a>"
345
+
346
+ #: admin/settings/connect-account.php:29
347
+ msgid ""
348
+ "Do you have a Lingotek sandbox account? <a href=\"%s\">Connect Sandbox "
349
+ "Account</a>"
350
+ msgstr "Vous avez un compte de bac à sable de Lingotek? <a href=\"%s\">Connecter compte Sandbox</a>"
351
+
352
+ #: admin/settings/view-account.php:14 admin/settings.php:11
353
+ #: admin/view-network.php:139
354
+ msgid "Account"
355
+ msgstr "Compte"
356
+
357
+ #: admin/settings/view-account.php:15
358
+ msgid "Lingotek account connection and community selection."
359
+ msgstr "Sélection de connexion et de la communauté Lingotek compte."
360
+
361
+ #: admin/settings/view-account.php:20 admin/settings/view-account.php:25
362
+ msgid "Connected"
363
+ msgstr "Connecté"
364
+
365
+ #: admin/settings/view-account.php:25
366
+ msgid "Yes"
367
+ msgstr "Oui"
368
+
369
+ #: admin/settings/view-account.php:30
370
+ msgid "Login ID"
371
+ msgstr "ID de connexion"
372
+
373
+ #: admin/settings/view-account.php:42
374
+ msgid "Access Token"
375
+ msgstr "Jeton d'accès"
376
+
377
+ #: admin/settings/view-account.php:57
378
+ msgid "API Endpoint"
379
+ msgstr "Point de terminaison API"
380
+
381
+ #: admin/settings/view-account.php:72
382
+ msgid ""
383
+ "Are you sure you would like to disconnect your Lingotek account? \\n\\nAfter "
384
+ "disconnecting, you will need to re-connect an account to continue using "
385
+ "Lingotek."
386
+ msgstr "Etes-vous sûr que vous souhaitez déconnecter votre compte Lingotek? \\n\\Anter déconnexion, vous aurez besoin de re-connecter un compte pour continuer à utiliser Lingotek."
387
+
388
+ #: admin/settings/view-account.php:73
389
+ msgid "Disconnect"
390
+ msgstr "Déconnecter"
391
+
392
+ #: admin/settings/view-account.php:87 admin/view-network.php:140
393
+ msgid "Community"
394
+ msgstr "Communauté"
395
+
396
+ #: admin/settings/view-account.php:107
397
+ msgid "Select"
398
+ msgstr "Sélectionnez"
399
+
400
+ #: admin/settings/view-account.php:119 admin/settings/view-content.php:90
401
+ #: admin/settings/view-defaults.php:138
402
+ #: admin/settings/view-edit-profile.php:188
403
+ #: admin/settings/view-preferences.php:83
404
+ msgid "Save Changes"
405
+ msgstr "Enregistrer les modifications"
406
+
407
+ #: admin/settings/view-content.php:68
408
+ msgid "Your content types were sucessfully saved."
409
+ msgstr "Vos types de contenu ont été correctement enregistrée."
410
+
411
+ #: admin/settings/view-content.php:81 admin/settings.php:17
412
+ msgid "Content Type Configuration"
413
+ msgstr "Configuration du Type de contenu"
414
+
415
+ #: admin/settings/view-content.php:82
416
+ msgid ""
417
+ "Content types can be configured to use any translation profile. "
418
+ "Additionally, translation profiles can be set based on the language the "
419
+ "content was authored in."
420
+ msgstr "Types de contenu peuvent être configurés pour utiliser n'importe quel profil de traduction. En outre, les profils de traduction peuvent être définies basées sur la langue en que le contenu a été rédigé."
421
+
422
+ #: admin/settings/view-defaults.php:13
423
+ msgid ""
424
+ "Resources from Lingotek were successfully updated for projects and workflows."
425
+ msgstr "Ressources de Lingotek ont été mis à jour pour les projets et les workflows."
426
+
427
+ #: admin/settings/view-defaults.php:16
428
+ msgid "Resources from Lingotek were successfully updated for projects."
429
+ msgstr "Ressources de Lingotek ont été mis à jour pour les projets."
430
+
431
+ #: admin/settings/view-defaults.php:19
432
+ msgid "Resources from Lingotek were successfully updated for workflows."
433
+ msgstr "Ressources de Lingotek ont été mis à jour pour les workflows."
434
+
435
+ #: admin/settings/view-defaults.php:31
436
+ msgid "Your <i>Defaults</i> were sucessfully saved."
437
+ msgstr "Vos <i>paramètres par défaut</i> ont été correctement enregistrée."
438
+
439
+ #: admin/settings/view-defaults.php:36 admin/settings/view-profiles.php:77
440
+ msgid "Your callback url was successfully updated."
441
+ msgstr "Votre url de callback a été mis à jour."
442
+
443
+ #: admin/settings/view-defaults.php:45 admin/view-network.php:98
444
+ msgid "Your new project was successfully created."
445
+ msgstr "Votre nouveau projet a été correctement créé."
446
+
447
+ #: admin/settings/view-defaults.php:87 admin/settings.php:15
448
+ #: admin/view-network.php:141
449
+ msgid "Defaults"
450
+ msgstr "Valeurs par défaut"
451
+
452
+ #: admin/settings/view-defaults.php:88
453
+ msgid ""
454
+ "The default automation settings and resources that should be used for this "
455
+ "site. These settings can be overriden using translation profiles and "
456
+ "content type configuration."
457
+ msgstr "Les paramètres d'automation par défaut et les ressources qui doivent être utilisés pour ce site. Ces paramètres peuvent être remplacé à l'aide de profils de traduction et de la configuration du type de contenu."
458
+
459
+ #: admin/settings/view-defaults.php:110 admin/settings/view-defaults.php:114
460
+ msgid "Enter new project name"
461
+ msgstr "Entrez le nom du nouveau projet"
462
+
463
+ #: admin/settings/view-defaults.php:116
464
+ #: admin/settings/view-edit-profile.php:90
465
+ msgid "Update the callback url for this project."
466
+ msgstr "Mettre à jour l'url de callback pour ce projet."
467
+
468
+ #: admin/settings/view-defaults.php:118
469
+ msgid "Create New Project"
470
+ msgstr "Créer le nouveau projet"
471
+
472
+ #: admin/settings/view-defaults.php:126
473
+ #: admin/settings/view-edit-profile.php:101
474
+ msgid "Filters"
475
+ msgstr "Filtres"
476
+
477
+ #: admin/settings/view-defaults.php:126
478
+ #: admin/settings/view-edit-profile.php:101
479
+ msgid "Not configured"
480
+ msgstr "Non configuré"
481
+
482
+ #: admin/settings/view-defaults.php:139
483
+ msgid "Refresh Resources"
484
+ msgstr "Actualiser les ressources"
485
+
486
+ #: admin/settings/view-edit-profile.php:6
487
+ msgid "Use global default (%s)"
488
+ msgstr "Utiliser par défaut global (%s)"
489
+
490
+ #: admin/settings/view-edit-profile.php:15
491
+ msgid "Use default settings"
492
+ msgstr "Utiliser les paramètres par défaut"
493
+
494
+ #: admin/settings/view-edit-profile.php:16
495
+ msgid "Use custom settings"
496
+ msgstr "Utiliser les paramètres personnalisés"
497
+
498
+ #: admin/settings/view-edit-profile.php:17 lingotek.php:205
499
+ msgid "Disabled"
500
+ msgstr "Handicapés"
501
+
502
+ #: admin/settings/view-edit-profile.php:74
503
+ msgid "Default settings"
504
+ msgstr "Paramètres par défaut"
505
+
506
+ #: admin/settings/view-edit-profile.php:114
507
+ msgid "Target languages"
508
+ msgstr "Langues cibles"
509
+
510
+ #: admin/settings/view-edit-profile.php:198
511
+ msgid "Cancel"
512
+ msgstr "Annuler"
513
+
514
+ #: admin/settings/view-preferences.php:6
515
+ msgid "Download translation status"
516
+ msgstr "Télécharger le statut de la traduction"
517
+
518
+ #: admin/settings/view-preferences.php:7
519
+ msgid "The post status for newly downloaded translations"
520
+ msgstr "Le statut de la poste pour les traductions nouvellement téléchargées"
521
+
522
+ #: admin/settings/view-preferences.php:9
523
+ msgid "Same as source post"
524
+ msgstr "Identique au poste source"
525
+
526
+ #: admin/settings/view-preferences.php:10
527
+ #: admin/settings/view-preferences.php:21
528
+ msgid "Draft"
529
+ msgstr "Projet"
530
+
531
+ #: admin/settings/view-preferences.php:11
532
+ #: admin/settings/view-preferences.php:22
533
+ msgid "Pending Review"
534
+ msgstr "Examen en attente"
535
+
536
+ #: admin/settings/view-preferences.php:12
537
+ #: admin/settings/view-preferences.php:23
538
+ msgid "Published"
539
+ msgstr "Publié"
540
+
541
+ #: admin/settings/view-preferences.php:14
542
+ #: admin/settings/view-preferences.php:25
543
+ msgid "Privately Published"
544
+ msgstr "Privé publié"
545
+
546
+ #: admin/settings/view-preferences.php:18
547
+ msgid "Auto upload statuses"
548
+ msgstr "Statuts de chargement automatique"
549
+
550
+ #: admin/settings/view-preferences.php:19
551
+ msgid ""
552
+ "The post statuses checked above are enabled for automatic upload (when using "
553
+ "automatic uploading translation profiles)."
554
+ msgstr "Les statuts de la poste vérifiés ci-dessus sont activés pour téléchargement automatique (lors de l'utilisation de profils de traduction téléchargement automatique)."
555
+
556
+ #: admin/settings/view-preferences.php:24
557
+ msgid "Scheduled"
558
+ msgstr "À la demande"
559
+
560
+ #: admin/settings/view-preferences.php:40
561
+ msgid "Your preferences were successfully updated."
562
+ msgstr "Vos préférences ont été correctement mis à jour."
563
+
564
+ #: admin/settings/view-preferences.php:48 admin/settings.php:18
565
+ #: admin/view-network.php:145
566
+ msgid "Preferences"
567
+ msgstr "Préférences"
568
+
569
+ #: admin/settings/view-preferences.php:49
570
+ msgid "These are your preferred settings."
571
+ msgstr "Ce sont vos réglages préférés."
572
+
573
+ #: admin/settings/view-profiles.php:15
574
+ msgid "Your translation profile was sucessfully deleted."
575
+ msgstr "Votre profil de traduction a été correctement supprimé."
576
+
577
+ #: admin/settings/view-profiles.php:28
578
+ msgid "You must provide a name for your translation profile."
579
+ msgstr "Vous devez fournir un nom pour votre profil de traduction."
580
+
581
+ #: admin/settings/view-profiles.php:71
582
+ msgid "Your translation profile was sucessfully saved."
583
+ msgstr "Votre profil de traduction a été correctement enregistré."
584
+
585
+ #: admin/settings/view-profiles.php:84 admin/settings.php:16
586
+ msgid "Translation Profiles"
587
+ msgstr "Traduction des profils"
588
+
589
+ #: admin/settings/view-profiles.php:85
590
+ msgid ""
591
+ "Translation profiles allow you to quickly configure and re-use translation "
592
+ "settings."
593
+ msgstr "Profils de translation permettent de configurer rapidement et ré-utiliser les paramètres de translation."
594
+
595
+ #: admin/settings/view-profiles.php:92
596
+ msgid "Add New Profile"
597
+ msgstr "Ajouter nouveau profil"
598
+
599
+ #: admin/settings/view-utilities.php:11
600
+ msgid "Disassociating content..."
601
+ msgstr "Dissocier le contenu..."
602
+
603
+ #: admin/settings/view-utilities.php:25 admin/settings.php:21
604
+ msgid "Utilities"
605
+ msgstr "Utilitaires"
606
+
607
+ #: admin/settings/view-utilities.php:26
608
+ msgid ""
609
+ "These utilities are designed to help you prepare and maintain your "
610
+ "multilingual content."
611
+ msgstr "Ces utilitaires sont conçus pour vous aider à préparer et conserver votre contenu multilingue."
612
+
613
+ #: admin/settings/view-utilities.php:28 admin/view-network.php:155
614
+ msgid "Language"
615
+ msgstr "Langue"
616
+
617
+ #: admin/settings/view-utilities.php:35
618
+ msgid ""
619
+ "Set <i>default language</i> as the language for all content that has not "
620
+ "been assigned a language."
621
+ msgstr "Définir la <i>langue par défaut</i> comme langue de tout le contenu qui n'a pas reçu une langue."
622
+
623
+ #: admin/settings/view-utilities.php:39
624
+ msgid "Disassociation"
625
+ msgstr "Dissociation"
626
+
627
+ #: admin/settings/view-utilities.php:45
628
+ msgid "Disassociate all the content from Lingotek TMS."
629
+ msgstr "Dissocier tout le contenu de Lingotek TMS."
630
+
631
+ #: admin/settings/view-utilities.php:55
632
+ msgid "Delete all documents in Lingotek TMS."
633
+ msgstr "Supprimez tous les documents de Lingotek TMS."
634
+
635
+ #: admin/settings/view-utilities.php:62
636
+ msgid ""
637
+ "You are about to disassociate all your content from Lingotek TMS. Are you "
638
+ "sure ?"
639
+ msgstr "Vous êtes sur le point de dissocier tous vos contenus de Lingotek TMS. Es-tu sûr?"
640
+
641
+ #: admin/settings/view-utilities.php:63
642
+ msgid ""
643
+ "You are about to disassociate all your content and delete all documents in "
644
+ "Lingotek TMS. Are you sure ?"
645
+ msgstr "Vous êtes sur le point de dissocier tous vos contenus et supprimer tous les documents de Lingotek TMS. Es-tu sûr?"
646
+
647
+ #: admin/settings/view-utilities.php:75
648
+ msgid "Run Utilities"
649
+ msgstr "Exécutez les utilitaires"
650
+
651
+ #: admin/strings-table.php:99
652
+ msgid "Group"
653
+ msgstr "Groupe"
654
+
655
+ #: admin/strings-table.php:100
656
+ msgid "Count"
657
+ msgstr "Comte"
658
+
659
+ #: admin/tutorial/content.php:25
660
+ msgid "1. Create content"
661
+ msgstr "1. créer un contenu"
662
+
663
+ #: admin/tutorial/content.php:26
664
+ msgid ""
665
+ "Whether you write a blog post, create a page for your site, or have existing "
666
+ "posts and pages, any of your Wordpress content can be uploaded to "
667
+ "<i>Lingotek</i>."
668
+ msgstr "Que vous écrivez un blog, créez une page pour votre site ou avez des pages et des postes existants, une partie de votre contenu de Wordpress peut être téléchargée sur <i>Lingotek</i>."
669
+
670
+ #: admin/tutorial/content.php:27
671
+ msgid ""
672
+ "The examples shown below are for Pages but translation for other content "
673
+ "types works the same way!"
674
+ msgstr "Les exemples ci-dessous sont pour les Pages mais la traduction pour les autres types de contenu fonctionne de la même manière !"
675
+
676
+ #: admin/tutorial/content.php:29
677
+ msgid "Create a new page for translation."
678
+ msgstr "Créez une nouvelle page pour la traduction."
679
+
680
+ #: admin/tutorial/content.php:32
681
+ msgid "2. Upload content to Lingotek"
682
+ msgstr "2. Télécharger du contenu à Lingotek"
683
+
684
+ #: admin/tutorial/content.php:33
685
+ msgid ""
686
+ "Your Wordpress content can be uploaded to <i>Lingotek</i> with the simple "
687
+ "push of a button."
688
+ msgstr "Le contenu de votre Wordpress peut être téléchargé vers <i>Lingotek</i> avec la simple pression d'un bouton."
689
+
690
+ #: admin/tutorial/content.php:35
691
+ msgid "Content has been created and is ready for upload to Lingotek."
692
+ msgstr "Contenu a été créé et est prêt pour le téléchargement de Lingotek."
693
+
694
+ #: admin/tutorial/content.php:38
695
+ msgid "3. Request translations for target languages"
696
+ msgstr "3. demander des traductions en langues cibles"
697
+
698
+ #: admin/tutorial/content.php:39
699
+ msgid ""
700
+ "Request translation for a specific language by clicking on the orange plus "
701
+ "icon, for all languages at once, or in bulk by using the <i>Bulk Actions</i> "
702
+ "dropdown."
703
+ msgstr "Demande de traduction pour une langue spécifique en cliquant sur l'icône, pour toutes les langues à la fois, ou en vrac, orange plus en utilisant le menu déroulant <i>Actions en vrac</i> ."
704
+
705
+ #: admin/tutorial/content.php:41
706
+ msgid "The source content is uploaded and ready for target languages."
707
+ msgstr "Le contenu source est téléchargé et prêt pour les langues cibles."
708
+
709
+ #: admin/tutorial/content.php:44
710
+ msgid "4. Translate your content"
711
+ msgstr "4. traduire votre contenu"
712
+
713
+ #: admin/tutorial/content.php:45
714
+ msgid ""
715
+ "Your content will now be translated into your selected target languages by "
716
+ "free machine translation or, if you contract with <i>Lingotek</i>, "
717
+ "professional translation services."
718
+ msgstr "Votre contenu sera maintenant traduit dans vos langues cibles sélectionnés par traduction machine libre ou, si vous contractez avec <i>Lingotek</i>, services de traduction professionnels."
719
+
720
+ #: admin/tutorial/content.php:47
721
+ msgid "Your translations are underway."
722
+ msgstr "Vos traductions sont en cours."
723
+
724
+ #: admin/tutorial/content.php:50
725
+ msgid "5. Download translations"
726
+ msgstr "5. Télécharger les traductions"
727
+
728
+ #: admin/tutorial/content.php:51
729
+ msgid ""
730
+ "Once your translations are complete they will be marked ready for download. "
731
+ "You can download translations for all languages, each language individually, "
732
+ "or in bulk (using the <i>Bulk Actions</i> dropdown)."
733
+ msgstr "Une fois que vos traductions sont faites elles seront marquées prêts pour le téléchargement. Vous pouvez télécharger les traductions en toutes langues, chaque langue, en bloc ou individuellement (en utilisant le menu déroulant <i>Actions en vrac</i> )."
734
+
735
+ #: admin/tutorial/content.php:53
736
+ msgid "Your translations are ready for download."
737
+ msgstr "Vos traductions sont prêtes pour le téléchargement."
738
+
739
+ #: admin/tutorial/content.php:56
740
+ msgid "6. Your content is translated!"
741
+ msgstr "6. votre contenu est traduit !"
742
+
743
+ #: admin/tutorial/content.php:57
744
+ msgid ""
745
+ "The orange pencil icons indicate that your translations are finished, "
746
+ "downloaded, and current within your Wordpress site. Clicking on any one of "
747
+ "the pencils will direct you to the Lingotek Workbench for that specific "
748
+ "language. Here you can make updates and changes to your translations if "
749
+ "necessary."
750
+ msgstr "Les icônes de crayon orange indiquent que vos traductions sont finis, téléchargé et actuelle au sein de votre site Wordpress. En cliquant sur l'un des crayons vous dirigera vers le Workbench de Lingotek pour la langue en question. Ici vous pouvez faire des mises à jour et modifications à vos traductions si nécessaire."
751
+
752
+ #: admin/tutorial/content.php:59
753
+ msgid "Your content has been translated."
754
+ msgstr "Votre contenu a été traduit."
755
+
756
+ #: admin/tutorial/content.php:62
757
+ msgid "What do all the icons mean?"
758
+ msgstr "Que signifient toutes les icônes ?"
759
+
760
+ #: admin/tutorial/content.php:67
761
+ msgid "Upload Source"
762
+ msgstr "Télécharger la Source"
763
+
764
+ #: admin/tutorial/content.php:68
765
+ msgid "There is content ready to be uploaded to Lingotek."
766
+ msgstr "Il y a contenu prêt à être transféré à Lingotek."
767
+
768
+ #: admin/tutorial/content.php:73
769
+ msgid ""
770
+ "Content is importing to Lingotek or a target language is being added to "
771
+ "source content."
772
+ msgstr "Contenu consiste à importer de Lingotek ou un langage cible est ajouté au contenu source."
773
+
774
+ #: admin/tutorial/content.php:77
775
+ msgid "Source Uploaded"
776
+ msgstr "Source téléchargée"
777
+
778
+ #: admin/tutorial/content.php:78
779
+ msgid "The source content has been uploaded to Lingotek."
780
+ msgstr "Le contenu de la source a été téléchargé à Lingotek."
781
+
782
+ #: admin/tutorial/content.php:82
783
+ msgid "Request Translation"
784
+ msgstr "Demande de traduction"
785
+
786
+ #: admin/tutorial/content.php:83
787
+ msgid "Request a translation of the source content. (Add a target language)"
788
+ msgstr "Demander une traduction du contenu source. (Ajouter une langue cible)"
789
+
790
+ #: admin/tutorial/content.php:87
791
+ msgid "Download Translation"
792
+ msgstr "Télécharger Translation"
793
+
794
+ #: admin/tutorial/content.php:88
795
+ msgid "Download the translated content to Wordpress."
796
+ msgstr "Télécharger le contenu traduit pour Wordpress."
797
+
798
+ #: admin/tutorial/content.php:92
799
+ msgid "Translation Current"
800
+ msgstr "Traduction actuelle"
801
+
802
+ #: admin/tutorial/content.php:93
803
+ msgid ""
804
+ "The translation is complete. (Clicking on this icon will allow you to edit "
805
+ "translations in the Lingotek Workbench)"
806
+ msgstr "La traduction est terminée. (En cliquant sur cette icône vous permet d'éditer les traductions dans l'établi Lingotek)"
807
+
808
+ #: admin/tutorial/credits.php:45
809
+ msgid "The Lingotek plugin for WordPress is created with love."
810
+ msgstr "Le plugin de Lingotek pour WordPress est créé avec amour."
811
+
812
+ #: admin/tutorial/credits.php:47
813
+ msgid "Team"
814
+ msgstr "Équipe"
815
+
816
+ #: admin/tutorial/faq.php:10
817
+ msgid "Questions and answers..."
818
+ msgstr "Questions et réponses..."
819
+
820
+ #: admin/tutorial/faq.php:12
821
+ msgid "How does this really work?"
822
+ msgstr "Comment ça marche vraiment ?"
823
+
824
+ #: admin/tutorial/faq.php:13
825
+ msgid ""
826
+ "<i>Polylang</i> puts in the framework to make WordPress multilingual and "
827
+ "<i>Lingotek</i> leverages that framework to provide localization through "
828
+ "machine translation and professional translation services."
829
+ msgstr "<i>Polylang</i> place dans le cadre de faire WordPress multilingue et <i>Lingotek</i> s'appuie sur ce cadre afin de fournir une localisation à travers la traduction automatique et de services de traduction professionnels."
830
+
831
+ #: admin/tutorial/faq.php:15
832
+ msgid "What does the Lingotek plugin do?"
833
+ msgstr "Que signifie le Lingotek plugin faire ?"
834
+
835
+ #: admin/tutorial/faq.php:16
836
+ msgid ""
837
+ "<i>Lingotek</i> has teamed up with <i>Polylang</i> to offer a simple way to "
838
+ "make your WordPress site truly multilingual. Manage all your multilingual "
839
+ "content in the same site. No need to have a different site for each language!"
840
+ msgstr "<i>Lingotek</i> collabore avec <i>Polylang</i> pour offrir un moyen simple de rendre votre site WordPress multilingue. Gérer tous vos contenus multilingues dans le même site. Pas besoin d'avoir un site différent pour chaque langue !"
841
+
842
+ #: admin/tutorial/faq.php:18
843
+ msgid "How can I add a language?"
844
+ msgstr "Comment puis-je ajouter une langue ?"
845
+
846
+ #: admin/tutorial/faq.php:19
847
+ msgid ""
848
+ "On the <i>translation dashboard</i>, click on the <i>Translate my site "
849
+ "into...</i> textbox and choose from the list or start typing to quickly find "
850
+ "a language."
851
+ msgstr "Sur le <i>tableau de bord de traduction</i>, cliquez sur la zone de texte de <i>mon site dans la traduction...</i> et choisissez dans la liste ou commencez à taper pour trouver rapidement une langue."
852
+
853
+ #: admin/tutorial/faq.php:21
854
+ msgid "How can I remove a language?"
855
+ msgstr "Comment puis-je supprimer une langue ?"
856
+
857
+ #: admin/tutorial/faq.php:23
858
+ msgid ""
859
+ "On the <i>translation dashboard</i>, click on the blue check mark for the "
860
+ "language you would like to remove."
861
+ msgstr "Sur le <i>tableau de bord de traduction</i>, cliquez sur la coche bleue pour la langue que vous souhaitez supprimer."
862
+
863
+ #: admin/tutorial/faq.php:24
864
+ msgid ""
865
+ "Note: If you have translated content in a language you will not be able to "
866
+ "remove that language until that content has been deleted."
867
+ msgstr "Remarque : Si vous avez traduit le contenu dans une langue vous ne serez pas en mesure de supprimer cette langue jusqu'à ce que le contenu a été supprimé."
868
+
869
+ #: admin/tutorial/faq.php:27
870
+ msgid "Can I use my own translation agency with Lingotek?"
871
+ msgstr "Puis-je utiliser ma propre agence de traduction avec Lingotek ?"
872
+
873
+ #: admin/tutorial/faq.php:28 admin/tutorial/features.php:37
874
+ msgid ""
875
+ "Use your own translation agency or tap into Lingotek's network of more than "
876
+ "5,000+ in-country translators. Content transfer is fully automated between "
877
+ "WordPress and Lingotek. You'll have full visibility into the translation "
878
+ "process every step of the way. And once the translations are completed, "
879
+ "they'll automatically download and publish to your website according to the "
880
+ "preferences you've set."
881
+ msgstr "Utilisez votre propre agence de traduction ou puiser dans réseau de Lingotek de plus de 5 000 + traducteurs dans le pays. Transfert de contenu est entièrement automatisé entre WordPress et Lingotek. Vous aurez une visibilité complète sur la traduction traiter chaque étape de la voie. Et une fois que les traductions sont terminées, ils vont automatiquement télécharger et publier sur votre site Web selon les préférences que vous avez défini."
882
+
883
+ #: admin/tutorial/faq.php:30
884
+ msgid "How can I check the overall translation progress of my site?"
885
+ msgstr "Comment puis-je vérifier la progression globale de la traduction de mon site ?"
886
+
887
+ #: admin/tutorial/faq.php:31
888
+ msgid ""
889
+ "On the <i>translation dashboard</i>, the bars under <i>Completed Percentage</"
890
+ "i> show the translation progress of content for each language. You can "
891
+ "filter by content type or show progress for all content."
892
+ msgstr "Sur le <i>tableau de bord de translation</i>, les barres sous le <i>Pourcentage achevé</i> affichant l'avancement de la traduction de contenu pour chaque langue. Vous pouvez filtrer par type de contenu ou afficher la progression de tout le contenu."
893
+
894
+ #: admin/tutorial/faq.php:34
895
+ msgid "Why are there two different shades of blue in the progress bar?"
896
+ msgstr "Pourquoi y a-t-il deux différentes nuances de bleu dans la barre de progression ?"
897
+
898
+ #: admin/tutorial/faq.php:35
899
+ msgid ""
900
+ "The <i>translation dashboard</i> not only shows how much of your content is "
901
+ "translated, but also indicates the language that your source content was "
902
+ "authored in."
903
+ msgstr "Le <i>tableau de bord traduction</i> montre non seulement combien de votre contenu est traduit, mais il indique également la langue votre contenu source créé dans."
904
+
905
+ #: admin/tutorial/faq.php:38
906
+ msgid ""
907
+ "<i>Dark Blue:</i> Indicates that this is a source language that the content "
908
+ "was authored in."
909
+ msgstr "<i>Bleu foncé :</i> Indique qu'il s'agit d'une langue source le contenu créé dans."
910
+
911
+ #: admin/tutorial/faq.php:39
912
+ msgid ""
913
+ "<i>Light Blue:</i> Indicates that this is a target language that the content "
914
+ "was translated into."
915
+ msgstr "<i>Bleu clair :</i> Indique qu'il s'agit d'une langue cible que le contenu a été traduit en."
916
+
917
+ #: admin/tutorial/faq.php:43
918
+ msgid "What happens when I <i>Disassociate Translations</i>?"
919
+ msgstr "Que se passe-t-il lorsque je <i>Dissocier les traductions</i>?"
920
+
921
+ #: admin/tutorial/faq.php:44
922
+ msgid ""
923
+ "When content is disassociated the connection between WordPress and "
924
+ "<i>Lingotek</i> safely disconnected from Lingotek, so that translations can "
925
+ "be solely managed inside of WordPress."
926
+ msgstr "Lorsque le contenu est dissocié la connexion entre WordPress et <i>Lingotek</i> déconnecté en toute sécurité de Lingotek, afin que les traductions peuvent être gérées exclusivement à l'intérieur de WordPress."
927
+
928
+ #: admin/tutorial/faq.php:46
929
+ msgid ""
930
+ "How do I translate strings within widgets or general WordPress settings (e."
931
+ "g., Site Title, Tagline)?"
932
+ msgstr "Comment traduire les chaînes dans les gadgets ou les paramètres généraux de WordPress (Site titre, slogan) ?"
933
+
934
+ #: admin/tutorial/faq.php:47
935
+ msgid ""
936
+ "Groups of strings can be sent for translation and managed using the <a "
937
+ "href='%s'>String Groups</a> page. Individual strings may be viewed on the <a "
938
+ "href='%s'>Strings</a> page."
939
+ msgstr "Groupes de chaînes peuvent être envoyés pour traduction et gérés à l'aide de la page <a href='%s'>Groupes de chaîne</a> . Chaînes individuelles peuvent être consultés sur la page <a href='%s'>chaînes</a> ."
940
+
941
+ #: admin/tutorial/faq.php:53
942
+ msgid "Lingotek Workbench"
943
+ msgstr "Lingotek Workbench"
944
+
945
+ #: admin/tutorial/faq.php:54
946
+ msgid ""
947
+ "Lingotek Translation is the only WordPress plugin to integrate a Translation "
948
+ "Management System directly into WordPress, thus allowing the WordPress "
949
+ "community to use professional-grade translation technologies (e.g. machine "
950
+ "translation, translation memory, CAT tool) without ever having to leave the "
951
+ "comfort of the WordPress environment."
952
+ msgstr "Traduction de Lingotek est le seul plugin WordPress pour intégrer un système de gestion de traduction directement dans WordPress, permettant ainsi à la communauté WordPress d'utiliser les technologies de traduction de qualité professionnelle (p. ex., traduction automatique, mémoire de traduction, outil de TAO) sans jamais avoir à quitter le confort de l'environnement WordPress."
953
+
954
+ #: admin/tutorial/faq.php:59
955
+ msgid ""
956
+ "The <i>translation dashboard</i> allows you to view your site languages, add "
957
+ "and remove new languages, check the overall translation progress of your "
958
+ "site, see site analytics, and connect with Lingotek support."
959
+ msgstr "Le <i>tableau de bord de traduction</i> permet de voir votre site langues, ajouter et supprimer des nouvelles langues, vérifier la progression globale de traduction de votre site, voir analytique de site et se connecter avec l'aide Lingotek."
960
+
961
+ #: admin/tutorial/features.php:5
962
+ msgid "Translate into new languages"
963
+ msgstr "Traduire dans nouvelles langues"
964
+
965
+ #: admin/tutorial/features.php:6
966
+ msgid ""
967
+ "Easily translate your site into new languages by adding the desired language "
968
+ "to your site. Lingotek allows you to quickly add any of the most frequently "
969
+ "used locales using the <a href=\"%s\" title=\"Translation &gt; Dashboard"
970
+ "\">Translation Dashboard</a>."
971
+ msgstr "Easily translate your site into new languages by adding the desired language to your site. Lingotek allows you to quickly add any of the most frequently used locales using the <a href=\"%s\" title=\"Translation &gt; Dashboard\">Translation Dashboard</a>."
972
+
973
+ #: admin/tutorial/features.php:10
974
+ msgid "Automatically translate content"
975
+ msgstr "Traduire automatiquement le contenu"
976
+
977
+ #: admin/tutorial/features.php:11
978
+ msgid ""
979
+ "Machine translation is an excellent option if you're on a tight budget, "
980
+ "looking for near-instant results, and are okay with less-than-perfect "
981
+ "quality. The plugin allows you to quickly and automatically translate your "
982
+ "site (the cost is covered by Lingotek for up to 100,000 characters)."
983
+ msgstr "Traduction automatique est une excellente option si vous êtes sur un budget serré, vous cherchez des résultats quasi instantané et sont d'accord avec qualité de moins-que-parfait. Le plugin vous permet de rapidement et automatiquement traduire votre site (le coût est couvert par Lingotek pour jusqu'à 100 000 caractères)."
984
+
985
+ #: admin/tutorial/features.php:15
986
+ msgid "Fully compatible with Polylang"
987
+ msgstr "Entièrement compatible avec Polylang"
988
+
989
+ #: admin/tutorial/features.php:16
990
+ msgid ""
991
+ "Polylang and Lingotek work together seamlessly. Continue to use Polylang "
992
+ "for content that you want to translate inside WordPress, while sending other "
993
+ "content to be translated by Lingotek. The <b style=\"color: %s\">orange</b> "
994
+ "icons indicate Lingotek statuses/actions while the <b style=\"color: %s"
995
+ "\">blue</b> icons continue to represent Polylang actions."
996
+ msgstr "Polylang et Lingotek travaillent ensemble de façon transparente. Continuer à utiliser Polylang pour le contenu que vous souhaitez traduire à l'intérieur de WordPress, lors de l'envoi de tout autre contenu à traduire par Lingotek. Les icônes <b style=\"color: %s\">orange</b> indiquent Lingotek statuts/actions alors que les icônes <b style=\"color: %s\">bleues</b> continuent de représenter les actions de Polylang."
997
+
998
+ #: admin/tutorial/features.php:25
999
+ msgid "Use translation profiles"
1000
+ msgstr "Utilisation des profils de traduction"
1001
+
1002
+ #: admin/tutorial/features.php:26
1003
+ msgid ""
1004
+ "One of the most time-consuming activities of any multilingual web-site "
1005
+ "project is managing the ongoing flow of changes and additions to site "
1006
+ "content and configurations. Translation profiles were created to allow you "
1007
+ "to create and save and re-use your translation settings."
1008
+ msgstr "Une des activités plus fastidieuses de tout projet de site web multilingue gère le flux continu des changements et des ajouts au contenu du site et des configurations. Les profils de traduction ont été créés pour vous permettre de créer et d'enregistrer et de réutiliser vos paramètres de translation."
1009
+
1010
+ #: admin/tutorial/features.php:30
1011
+ msgid "Content type profiles"
1012
+ msgstr "Profils de type de contenu"
1013
+
1014
+ #: admin/tutorial/features.php:31
1015
+ msgid ""
1016
+ "Content type profiles. Manually choosing which content to upload and "
1017
+ "download is rarely what a content administrator wants to do, and automating "
1018
+ "the upload of every change is not workable because there are various types "
1019
+ "of content. Each type of translatable content can be assigned to a "
1020
+ "customizable profile. For example, by default, we like to have Posts use an "
1021
+ "<i>Automatic</i> profile so that content will automatically be uploaded for "
1022
+ "translation and the resulting translations automatically be downloaded back "
1023
+ "into WordPress."
1024
+ msgstr "Profils de type de contenu. Choisir manuellement quel contenu à télécharger est rarement ce qu'un administrateur de contenu veut faire, et automatiser le téléchargement de chaque changement n'est pas réalisable car il existe différents types de contenus. Chaque type de contenu traduisible peut être assignée à un profil personnalisable. Par exemple, par défaut, nous aimons avoir de messages utilisation profil un <i>automatique</i> afin que le contenu sera automatiquement téléchargé pour la traduction et les traductions qui en résulte automatiquement être téléchargé dans WordPress."
1025
+
1026
+ #: admin/tutorial/features.php:36
1027
+ msgid "Request professional translation"
1028
+ msgstr "Demande de traduction professionnelle"
1029
+
1030
+ #: admin/utilities.php:78
1031
+ msgid "The language update utility ran successfully."
1032
+ msgstr "L'utilitaire de mise à jour de langue a été correctement exécuté."
1033
+
1034
+ #: admin/view-network.php:78
1035
+ msgid "The language utility ran successfully."
1036
+ msgstr "L'utilité de la langue a été correctement exécuté."
1037
+
1038
+ #: admin/view-network.php:80
1039
+ msgid "Your chosen settings have updated successfully for all selected sites."
1040
+ msgstr "Vos paramètres choisis ont mis à jour avec succès pour tous les sites sélectionnés."
1041
+
1042
+ #: admin/view-network.php:83
1043
+ msgid "Please choose at least one destination site."
1044
+ msgstr "Veuillez choisir au moins un site de destination."
1045
+
1046
+ #: admin/view-network.php:95
1047
+ msgid "Your new projects were successfully created."
1048
+ msgstr "Vos nouveaux projets ont été créés avec succès."
1049
+
1050
+ #: admin/view-network.php:112
1051
+ msgid "Lingotek Network Settings"
1052
+ msgstr "Paramètres réseau de Lingotek"
1053
+
1054
+ #: admin/view-network.php:113
1055
+ msgid "Copy Lingotek settings from the source site to multiple sites"
1056
+ msgstr "Paramètres de Lingotek copie sur le site de la source à plusieurs sites"
1057
+
1058
+ #: admin/view-network.php:119
1059
+ msgid "Source Site"
1060
+ msgstr "Source Site"
1061
+
1062
+ #: admin/view-network.php:128
1063
+ msgid "Destination Site"
1064
+ msgstr "Site de destination"
1065
+
1066
+ #: admin/view-network.php:137
1067
+ msgid "Settings to copy"
1068
+ msgstr "Pour copier les paramètres"
1069
+
1070
+ #: admin/view-network.php:142
1071
+ msgid "Resources"
1072
+ msgstr "Ressources"
1073
+
1074
+ #: admin/view-network.php:143
1075
+ msgid "Profiles"
1076
+ msgstr "Profils"
1077
+
1078
+ #: admin/view-network.php:144
1079
+ msgid "Content Types"
1080
+ msgstr "Types de contenu"
1081
+
1082
+ #: admin/view-network.php:149
1083
+ msgid "New Project"
1084
+ msgstr "Nouveau projet"
1085
+
1086
+ #: admin/view-network.php:151
1087
+ msgid ""
1088
+ "Create a new project using the name of the selected site (Recommended for a "
1089
+ "newly created site)"
1090
+ msgstr "Créez un nouveau projet en utilisant le nom du site sélectionné (recommandé pour un site nouvellement créé)"
1091
+
1092
+ #: admin/view-network.php:157
1093
+ msgid ""
1094
+ "Set <i>default language</i> as the language for all existing content that "
1095
+ "has not been assigned a language."
1096
+ msgstr "Définir la <i>langue par défaut</i> comme langue pour tout le contenu existant qui n'a pas reçu une langue."
1097
+
1098
+ #: admin/view-network.php:163
1099
+ msgid "Update Options"
1100
+ msgstr "Options de mise à jour"
1101
+
1102
+ #: admin/view-tutorial.php:4
1103
+ msgid "Features"
1104
+ msgstr "Caractéristiques"
1105
+
1106
+ #: admin/view-tutorial.php:6
1107
+ msgid "FAQ"
1108
+ msgstr "FAQ"
1109
+
1110
+ #: admin/view-tutorial.php:7
1111
+ msgid "Credits"
1112
+ msgstr "Crédits"
1113
+
1114
+ #: admin/view-tutorial.php:15
1115
+ msgid "Welcome to Lingotek"
1116
+ msgstr "Bienvenue chez Lingotek"
1117
+
1118
+ #: admin/view-tutorial.php:17
1119
+ msgid ""
1120
+ "Thank you for updating! Lingotek offers convenient cloud-based localization "
1121
+ "and translation."
1122
+ msgstr "Je vous remercie pour la mise à jour ! Lingotek offre de traduction et localisation commode de nuage."
1123
+
1124
+ #: admin/view-tutorial.php:20
1125
+ msgid "Version %s"
1126
+ msgstr "Version %s"
1127
+
1128
+ #: include/dashboard.php:79
1129
+ msgid "You must keep at least one language."
1130
+ msgstr "Vous devez conserver au moins une langue."
1131
+
1132
+ #: include/dashboard.php:103
1133
+ msgid ""
1134
+ "The language can only be removed when no existing content is using it. If "
1135
+ "you would like to remove this language from the site, then first remove any "
1136
+ "content assigned to this language."
1137
+ msgstr "La langue peut être supprimée uniquement lorsque aucun contenu existant n'est de l'utiliser. Si vous souhaitez supprimer cette langue du site, puis désinstallez n'importe quel contenu assigné à cette langue."
1138
+
1139
+ #: include/group-post.php:46 include/group-post.php:52
1140
+ msgid "Title"
1141
+ msgstr "Titre"
1142
+
1143
+ #: include/group-post.php:47
1144
+ msgid "Caption"
1145
+ msgstr "Caption"
1146
+
1147
+ #: include/group-post.php:48
1148
+ msgid "Alternative Text"
1149
+ msgstr "Texte de remplacement"
1150
+
1151
+ #: include/group-post.php:49 include/group-term.php:47
1152
+ msgid "Description"
1153
+ msgstr "Description"
1154
+
1155
+ #: include/group-post.php:53 include/group-term.php:46
1156
+ msgid "Slug"
1157
+ msgstr "Limace"
1158
+
1159
+ #: include/group-post.php:54
1160
+ msgid "Content"
1161
+ msgstr "Contenu"
1162
+
1163
+ #: include/group-post.php:55
1164
+ msgid "Excerpt"
1165
+ msgstr "Extrait"
1166
+
1167
+ #: include/group-term.php:44
1168
+ msgid "Name"
1169
+ msgstr "Nom"
1170
+
1171
+ #: lingotek.php:356
1172
+ msgid ""
1173
+ "Lingotek Translation requires Polylang to work. Please install Polylang."
1174
+ msgstr "Lingotek Traduction exige Polylang de travailler. S'il vous plaît installer Polylang."
1175
+
1176
+ #: lingotek.php:369
1177
+ msgid ""
1178
+ "Lingotek Translation requires Polylang %s to work. Please upgrade Polylang."
1179
+ msgstr "Lingotek Traduction exige Polylang %s de travailler. Veuillez mettre à jour Polylang."
1180
+
1181
+ #. Plugin Name of the plugin/theme
1182
+ msgid "Lingotek Translation"
1183
+ msgstr "Lingotek Traduction"
1184
+
1185
+ #. Plugin URI of the plugin/theme
1186
+ msgid ""
1187
+ "http://lingotek.com/"
1188
+ "wordpress#utm_source=wpadmin&utm_medium=plugin&utm_campaign=wplingotektranslationplugin"
1189
+ msgstr "http://Lingotek.com/WordPress#utm_source=wpadmin&utm_medium=plugin&utm_campaign=wplingotektranslationplugin"
1190
+
1191
+ #. Description of the plugin/theme
1192
+ msgid ""
1193
+ "Provides a seamless integration to the Lingotek Translation Management "
1194
+ "System (TMS)."
1195
+ msgstr "Fournit une intégration transparente à la Lingotek Translation Management System (TMS)."
1196
+
1197
+ #. Author of the plugin/theme
1198
+ msgid "Lingotek and Frédéric Demarle"
1199
+ msgstr "Lingotek et Frédéric Demarle"
1200
+
1201
+ #. Author URI of the plugin/theme
1202
+ msgid "http://lingotek.com"
1203
+ msgstr "http://Lingotek.com"
lingotek.php ADDED
@@ -0,0 +1,524 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
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.0.0
6
+ Author: Lingotek and Frédéric Demarle
7
+ Author uri: http://lingotek.com
8
+ Description: Lingotek offers convenient cloud-based localization and translation.
9
+ Text Domain: wp-lingotek
10
+ Domain Path: /languages
11
+ GitHub Plugin URI: https://github.com/lingotek/wp-lingotek
12
+ */
13
+
14
+ // don't access directly
15
+ if (!function_exists('add_action'))
16
+ exit();
17
+
18
+ define('LINGOTEK_VERSION', '1.0.0'); // plugin version (should match above meta)
19
+ define('LINGOTEK_MIN_PLL_VERSION', '1.7.4.2');
20
+ define('LINGOTEK_BASENAME', plugin_basename(__FILE__)); // plugin name as known by WP
21
+ define('LINGOTEK_PLUGIN_SLUG', 'wp-lingotek');// plugin slug (should match above meta: Text Domain)
22
+ define('LINGOTEK_DIR', dirname(__FILE__)); // our directory
23
+ define('LINGOTEK_INC', LINGOTEK_DIR . '/include');
24
+ define('LINGOTEK_ADMIN_INC', LINGOTEK_DIR . '/admin');
25
+ define('LINGOTEK_URL', plugins_url('', __FILE__));
26
+
27
+ class Lingotek {
28
+ public $model; // Lingotek model
29
+ public $callback;
30
+
31
+ // array to map Lingotek locales to WP locales
32
+ // map as 'WP locale' => 'Lingotek locale'
33
+ public static $lingotek_locales = array(
34
+ 'af' => 'af-ZA', 'ak' => 'ak-GH', 'am' => 'am-ET', 'ar' => 'ar', 'as' => 'as-IN', 'az' => 'az-AZ',
35
+ 'ba' => 'ba-RU', 'bel' => 'be-BY', 'bg_BG' => 'bg-BG', 'bn_BD' => 'bn-BD', 'bo' => 'bo-CN', 'bs_BA' => 'bs-BA',
36
+ 'ca' => 'ca-ES', 'co' => 'co-FR', 'cs_CZ' => 'cs-CZ', 'cy' => 'cy-GB', 'da_DK' => 'da-DK', 'de_CH' => 'de-CH',
37
+ 'de_DE' => 'de-DE', 'dv' => 'dv-MV', 'el' => 'el-GR', 'en_AU' => 'en-AU', 'en_CA' => 'en-CA', 'en_GB' => 'en-GB',
38
+ 'en_US' => 'en-US', 'eo' => 'eo-FR', 'es_AR' => 'es-AR', 'es_CL' => 'es-CL', 'es_CO' => 'es-CO', 'es_ES' => 'es-ES',
39
+ 'es_MX' => 'es-MX', 'es_PE' => 'es-PE', 'es_PR' => 'es-PR', 'es_VE' => 'es-VE', 'et' => 'et-EE', 'eu' => 'eu-ES',
40
+ 'fa_IR' => 'fa-IR', 'fi' => 'fi-FI', 'fr_FR' => 'fr-FR', 'ga' => 'ga-IE', 'gd' => 'gd-GB', 'gl_ES' => 'gl-ES',
41
+ 'gn' => 'gn-BO', 'haw_US' => 'haw-US', 'he_IL' => 'he-IL', 'hi_IN' => 'hi-IN', 'hr' => 'hr-HR', 'hu_HU' => 'hu-HU',
42
+ 'hy' => 'hy-AM', 'id_ID' => 'id-ID', 'is_IS' => 'is-IS', 'it_IT' => 'it-IT', 'ja' => 'ja-JP', 'jv_ID' => 'jv-ID',
43
+ 'ka_GE' => 'ka-GE', 'kin' => 'kin-RW', 'kk' => 'kk-KZ', 'kn' => 'kn-IN', 'ko_KR' => 'ko-KR', 'ky_KY' => 'ky-KG',
44
+ 'lb_LU' => 'lb-LU', 'lo' => 'lo-LA', 'lt_LT' => 'lt-LT', 'lv' => 'lv-LV', 'mg_MG' => 'mg-MG', 'mk_MK' => 'mk-MK',
45
+ 'ml_IN' => 'ml-IN', 'mn' => 'mn-MN', 'mr' => 'mr-IN', 'ms_MY' => 'ms-MY', 'my_MM' => 'my-MM', 'ne_NP' => 'ne-NP',
46
+ 'nl_BE' => 'nl-BE', 'nl_NL' => 'nl-NL', 'nn_NO' => 'nn-NO', 'pa_IN' => 'pa-IN', 'pl_PL' => 'pl-PL', 'ps' => 'ps-AF',
47
+ 'pt_BR' => 'pt-BR', 'pt_PT' => 'pt-PT', 'ro_RO' => 'ro-RO', 'ru_RU' => 'ru-RU', 'sa_IN' => 'sa-IN', 'sd_PK' => 'sd-PK',
48
+ 'si_LK' => 'si-LK', 'sk_SK' => 'sk-SK', 'sl_SI' => 'sl-SI', 'so_SO' => 'so-SO', 'sq' => 'sq-SQ', 'sr_RS' => 'sr-CS',
49
+ 'su_ID' => 'su-ID', 'sv_SE' => 'sv-SE', 'sw' => 'sw-TZ', 'ta_IN' => 'ta-IN', 'te' => 'te-IN', 'tg' => 'tg-TJ',
50
+ 'th' => 'th-TH', 'tir' => 'ti-ER', 'tl' => 'tl-PH', 'tr_TR' => 'tr-TR', 'ug_CN' => 'ug-CN', 'uk' => 'uk-UA',
51
+ 'ur' => 'ur-PK', 'uz_UZ' => 'uz-UZ', 'vi' => 'vi-VN', 'zh_CN' => 'zh-CN', 'zh_HK' => 'zh-HK', 'zh_TW' => 'zh-TW',
52
+ );
53
+
54
+ /**
55
+ *
56
+ * Unique identifier for your plugin.
57
+ *
58
+ *
59
+ * The variable name is used as the text domain when internationalizing strings
60
+ * of text. Its value should match the Text Domain file header in the main
61
+ * plugin file.
62
+ *
63
+ * @since 1.0.0
64
+ *
65
+ * @var string
66
+ */
67
+ protected $plugin_slug = LINGOTEK_PLUGIN_SLUG;
68
+
69
+ /**
70
+ * Instance of this class.
71
+ *
72
+ * @since 1.0.0
73
+ *
74
+ * @var object
75
+ */
76
+ protected static $instance = null;
77
+
78
+ protected static $logging = FALSE;
79
+
80
+ /*
81
+ * constructor
82
+ *
83
+ * @since 0.1
84
+ */
85
+ public function __construct() {
86
+ // manages plugin activation and deactivation
87
+ register_activation_hook( __FILE__, array(&$this, 'activate'));
88
+ register_deactivation_hook( __FILE__, array(&$this, 'deactivate'));
89
+
90
+ // stopping here if we are going to deactivate the plugin (avoids breaking rewrite rules)
91
+ if (isset($_GET['action'], $_GET['plugin']) && 'deactivate' == $_GET['action'] && plugin_basename(__FILE__) == $_GET['plugin'])
92
+ return;
93
+
94
+ // loads the admin side of Polylang for the dashboard
95
+ if (defined('DOING_AJAX') && DOING_AJAX && isset($_REQUEST['action']) && 'lingotek_language' == $_REQUEST['action']) {
96
+ define('PLL_AJAX_ON_FRONT', false);
97
+ add_filter('pll_model', create_function('$c', 'return "PLL_Admin_Model";'));
98
+ }
99
+
100
+ spl_autoload_register(array(&$this, 'autoload')); // autoload classes
101
+
102
+ // init
103
+ add_filter('pll_model', array(&$this, 'pll_model'));
104
+ add_action('init', array(&$this, 'init'));
105
+ add_action('admin_init', array(&$this, 'admin_init'));
106
+
107
+ // add Lingotek locale to languages
108
+ add_filter('pll_languages_list', array(&$this, 'pre_set_languages_list'));
109
+
110
+ // flag title
111
+ add_filter('pll_flag_title', array(&$this, 'pll_flag_title'), 10, 3);
112
+ }
113
+
114
+ /**
115
+ * Return the plugin slug.
116
+ *
117
+ * @since 0.1
118
+ *
119
+ * @return Plugin slug variable.
120
+ */
121
+ public function get_plugin_slug() {
122
+ return $this->plugin_slug;
123
+ }
124
+
125
+ /**
126
+ * Return an instance of this class.
127
+ *
128
+ * @since 1.0.0
129
+ *
130
+ * @return object A single instance of this class.
131
+ */
132
+ public static function get_instance() {
133
+
134
+ // If the single instance hasn't been set, set it now.
135
+ if ( null == self::$instance ) {
136
+ self::$instance = new self;
137
+ }
138
+
139
+ return self::$instance;
140
+ }
141
+
142
+ /*
143
+ * activation or deactivation for all blogs
144
+ * method taken from Polylang
145
+ *
146
+ * @since 0.1
147
+ *
148
+ * @param string $what either 'activate' or 'deactivate'
149
+ */
150
+ protected function do_for_all_blogs($what) {
151
+ // network
152
+ if (is_multisite() && isset($_GET['networkwide']) && ($_GET['networkwide'] == 1)) {
153
+ global $wpdb;
154
+
155
+ foreach ($wpdb->get_col("SELECT blog_id FROM $wpdb->blogs") as $blog_id) {
156
+ switch_to_blog($blog_id);
157
+ $what == 'activate' ? $this->_activate() : $this->_deactivate();
158
+ }
159
+ restore_current_blog();
160
+ }
161
+
162
+ // single blog
163
+ else
164
+ $what == 'activate' ? $this->_activate() : $this->_deactivate();
165
+ }
166
+
167
+ /*
168
+ * plugin activation for multisite
169
+ *
170
+ * @since 0.1
171
+ */
172
+ public function activate() {
173
+ $this->do_for_all_blogs('activate');
174
+ }
175
+
176
+ /*
177
+ * plugin activation
178
+ *
179
+ * @since 0.1
180
+ */
181
+ protected function _activate() {
182
+ global $polylang;
183
+
184
+ if (isset($polylang)) {
185
+ $polylang->model->clean_languages_cache(); // to add lingotek_locale property
186
+ }
187
+
188
+ // default profiles
189
+ if (false === get_option('lingotek_profiles')) {
190
+ $profiles = array(
191
+ 'automatic' => array(
192
+ 'profile' => 'automatic',
193
+ 'name' => __('Automatic', 'wp-lingotek'),
194
+ 'upload' => 'automatic',
195
+ 'download' => 'automatic'
196
+ ),
197
+ 'manual' => array(
198
+ 'profile' => 'manual',
199
+ 'name' => __('Manual', 'wp-lingotek'),
200
+ 'upload' => 'manual',
201
+ 'download' => 'manual'
202
+ ),
203
+ 'disabled' => array(
204
+ 'profile' => 'disabled',
205
+ 'name' => __('Disabled', 'wp-lingotek'),
206
+ ),
207
+ );
208
+ update_option('lingotek_profiles', $profiles);
209
+ }
210
+
211
+ // for the end point for the Lingoteck callback in rewrite rules
212
+ // don't use flush_rewrite_rules at network activation. See #32471
213
+ delete_option('rewrite_rules');
214
+ }
215
+
216
+ /*
217
+ * provides localized version of the canned translation profiles
218
+ *
219
+ * @since 1.0
220
+ *
221
+ * @return array
222
+ */
223
+ public static function get_profiles() {
224
+ $profiles = get_option('lingotek_profiles');
225
+
226
+ //localize canned profile names
227
+ foreach($profiles as $k=>$v){
228
+ if(in_array($k,array('automatic','manual','disabled'))){
229
+ $profile_name = $profiles[$k]['name'];
230
+ $profiles[$k]['name'] = __($profile_name,'wp-lingotek');// localize canned profile names
231
+ }
232
+ }
233
+
234
+ return $profiles;
235
+ }
236
+
237
+ /*
238
+ * plugin deactivation for multisite
239
+ *
240
+ * @since 0.1
241
+ */
242
+ public function deactivate() {
243
+ $this->do_for_all_blogs('deactivate');
244
+ }
245
+
246
+ /*
247
+ * plugin deactivation
248
+ *
249
+ * @since 0.5
250
+ */
251
+ protected function _deactivate() {
252
+ delete_option('rewrite_rules');
253
+ }
254
+
255
+ /*
256
+ * blog creation on multisite (to set default options)
257
+ *
258
+ * @since 0.1
259
+ *
260
+ * @param int $blog_id
261
+ */
262
+ public function wpmu_new_blog($blog_id) {
263
+ switch_to_blog($blog_id);
264
+ $this->_activate();
265
+ restore_current_blog();
266
+ }
267
+
268
+ /*
269
+ * autoload classes
270
+ *
271
+ * @since 0.1
272
+ *
273
+ * @param string $class
274
+ */
275
+ public function autoload($class) {
276
+ // not a Lingotek class
277
+ if (0 !== strncmp('Lingotek_', $class, 9))
278
+ return;
279
+
280
+ $class = str_replace('_', '-', strtolower(substr($class, 9)));
281
+ foreach (array(LINGOTEK_INC, LINGOTEK_ADMIN_INC) as $path) {
282
+ if (file_exists($file = "$path/$class.php")) {
283
+ require_once($file);
284
+ break;
285
+ }
286
+ }
287
+ }
288
+
289
+ /*
290
+ * set the Polylang model class to PLL_Admin_Model on Lingotek admin pages
291
+ *
292
+ * @since 0.2
293
+ *
294
+ * @param string $class
295
+ * @return string modified class 'PLL_Model' | 'PLL_Admin_Model'
296
+ */
297
+ public function pll_model($class) {
298
+ if (PLL_ADMIN && isset($_GET['page']) && in_array($_GET['page'], array('wp-lingotek', 'wp-lingotek_manage', 'wp-lingotek_settings', 'wp-lingotek_network')))
299
+ return 'PLL_Admin_Model';
300
+ return $class;
301
+ }
302
+
303
+ /*
304
+ * setups Lingotek model and callback
305
+ * sets filters to call Lingotek child classes instead of Polylang classes
306
+ *
307
+ * @since 0.1
308
+ *
309
+ */
310
+ public function init() {
311
+ if (!defined('POLYLANG_VERSION'))
312
+ return;
313
+
314
+ add_rewrite_rule( 'lingotek/?$', 'index.php?lingotek=1&$matches[1]', 'top' );
315
+
316
+ if (is_admin())
317
+ new Lingotek_Admin();
318
+
319
+ // admin side
320
+ if (PLL_ADMIN && !PLL_SETTINGS) {
321
+ $this->model = new Lingotek_Model();
322
+
323
+ // overrides Polylang classes
324
+ $classes = array('Filters_Post', 'Filters_Term', 'Filters_Media', 'Filters_Columns');
325
+ foreach ($classes as $class)
326
+ add_filter('pll_' . strtolower($class) , create_function('$v', "return 'Lingotek_$class';"));
327
+
328
+ // add actions to posts, media and terms list
329
+ // no need to load this if there is no language yet
330
+ if ($GLOBALS['polylang']->model->get_languages_list()) {
331
+ $this->post_actions = new Lingotek_Post_Actions();
332
+ $this->term_actions = new Lingotek_Term_Actions();
333
+ $this->string_actions = new Lingotek_String_actions();
334
+ }
335
+
336
+ $this->utilities = new Lingotek_Utilities();
337
+ }
338
+
339
+ // callback
340
+ elseif (!PLL_ADMIN && !PLL_AJAX_ON_FRONT) {
341
+ $GLOBALS['wp']->add_query_var('lingotek');
342
+
343
+ $this->model = new Lingotek_Model();
344
+ $this->callback = new Lingotek_Callback($this->model);
345
+ }
346
+ }
347
+
348
+ /*
349
+ * some init
350
+ *
351
+ * @since 0.1
352
+ */
353
+ public function admin_init() {
354
+ // plugin i18n, only needed for backend
355
+ load_plugin_textdomain('wp-lingotek', false, basename(LINGOTEK_DIR).'/languages');
356
+
357
+ if (!defined('POLYLANG_VERSION'))
358
+ add_action('all_admin_notices', array(&$this, 'pll_inactive_notice'));
359
+
360
+ elseif (version_compare(POLYLANG_VERSION, LINGOTEK_MIN_PLL_VERSION, '<'))
361
+ add_action('all_admin_notices', array(&$this, 'pll_old_notice'));
362
+
363
+ elseif (isset($GLOBALS['polylang']) && !count($GLOBALS['polylang']->model->get_languages_list()))
364
+ self::create_first_language();
365
+
366
+ wp_enqueue_style('lingotek_admin', LINGOTEK_URL .'/css/admin.css', array(), LINGOTEK_VERSION);
367
+ }
368
+
369
+ /*
370
+ * displays a notice if Polylang is inactive
371
+ *
372
+ * @since 0.1
373
+ */
374
+ public function pll_inactive_notice() {
375
+ printf(
376
+ '<div class="error"><p>%s</p></div>',
377
+ __('Lingotek Translation requires Polylang to work. Please install Polylang.', 'wp-lingotek')
378
+ );
379
+ }
380
+
381
+ /*
382
+ * displays a notice if Polylang is obsolete
383
+ *
384
+ * @since 0.1
385
+ */
386
+ public function pll_old_notice() {
387
+ printf(
388
+ '<div class="error"><p>%s</p></div>',
389
+ sprintf(
390
+ __('Lingotek Translation requires Polylang %s to work. Please upgrade Polylang.', 'wp-lingotek'),
391
+ '<strong>' . LINGOTEK_MIN_PLL_VERSION . '</strong>'
392
+ )
393
+ );
394
+ }
395
+
396
+ /*
397
+ * creates at least on language to avoid breaking the Lingotek Dashboard
398
+ *
399
+ * @since 0.2
400
+ */
401
+ static protected function create_first_language() {
402
+ global $polylang;
403
+
404
+ include(PLL_ADMIN_INC.'/languages.php');
405
+ $locale = get_locale();
406
+
407
+ // attempts to set the default language from the current locale
408
+ foreach ($languages as $lang) {
409
+ if (get_locale() == $lang[1])
410
+ $language = $lang;
411
+ }
412
+
413
+ // defaults to en_US
414
+ if (empty($language))
415
+ $language = array('en', 'en_US', 'English');
416
+
417
+ $pll_model = new PLL_Admin_Model($polylang->options); // need admin model
418
+ $pll_model->add_language(array(
419
+ 'slug' => $language[0],
420
+ 'locale' => $language[1],
421
+ 'name' => $language[2],
422
+ 'rtl' => isset($language[3]) ? 1 : 0,
423
+ 'term_group' => 0
424
+ ));
425
+ }
426
+
427
+ /*
428
+ * adds Lingotek locale to the PLL_Language objects
429
+ * uses the map otherwise uses a stupid fallback
430
+ *
431
+ * @since 0.1
432
+ *
433
+ * @param array $languages list of language objects
434
+ * @return array
435
+ */
436
+ public function pre_set_languages_list($languages) {
437
+ foreach ($languages as $key => $language) {
438
+ if (is_object($language))
439
+ $languages[$key]->lingotek_locale = self::map_to_lingotek_locale($language->locale); // backward compatibility with Polylang < 1.7.3
440
+ else
441
+ $languages[$key]['lingotek_locale'] = self::map_to_lingotek_locale($language['locale']);
442
+ }
443
+
444
+ return $languages;
445
+ }
446
+
447
+ /*
448
+ * maps a Lingotek locale to a WordPress locale
449
+ *
450
+ * @since 0.3
451
+ *
452
+ * @param string $lingotek_locale Lingotek locale
453
+ * @return string WordPress locale
454
+ */
455
+ public static function map_to_wp_locale($lingotek_locale) {
456
+ // look for the locale in the map (take care that Lingotek sends locales with either '_' or '-'
457
+ // if not found just replace '-' by '_'
458
+ $wp_locale = array_search(str_replace('_', '-', $lingotek_locale), self::$lingotek_locales);
459
+ return $wp_locale ? $wp_locale : str_replace('-', '_', $lingotek_locale);
460
+ }
461
+
462
+ /*
463
+ * maps a WordPres locale to a Lingotek locale
464
+ *
465
+ * @since 0.3
466
+ *
467
+ * @param string $wp_locale WordPress locale
468
+ * @return string Lingotek locale
469
+ */
470
+ public static function map_to_lingotek_locale($wp_locale) {
471
+ // look for the locale in the map
472
+ // if not found just replace '_ 'by '-'
473
+ return isset(self::$lingotek_locales[$wp_locale]) ? self::$lingotek_locales[$wp_locale] : str_replace('_', '-', $wp_locale);
474
+ }
475
+
476
+ /*
477
+ * modifies the flag title to add the locale
478
+ *
479
+ * @since 0.3
480
+ *
481
+ * @param string $name language name
482
+ * @param string $slug language code
483
+ * @param string $locale language locale
484
+ * @return string
485
+ */
486
+ public function pll_flag_title($name, $slug, $locale) {
487
+ return "$name ($locale)";
488
+ }
489
+
490
+ public static function log($data, $label = NULL) {
491
+ if (self::$logging) {
492
+ $log_string = "";
493
+ if (is_string($label))
494
+ $log_string .= $label . "\n";
495
+ if (is_string($data)) {
496
+ $log_string .= $data;
497
+ }
498
+ else {
499
+ $log_string .= print_r($data, TRUE);
500
+ }
501
+ error_log($log_string);
502
+ }
503
+ }
504
+ }
505
+
506
+ // Helpers - development functions
507
+ if (!function_exists('dc')) {
508
+ function dc($data, $label = NULL, $die = FALSE) {
509
+ echo '<pre style="background: #f3f3f3; color: #000">';
510
+ if (is_string($label))
511
+ echo '<h1>' . $label . '</h1>';
512
+ print_r($data);
513
+ echo '</pre>';
514
+ if ($die || (is_bool($label) && $label))
515
+ die();
516
+ }
517
+ }
518
+ if (!function_exists('dd')) {
519
+ function dd($data, $label = NULL, $die = FALSE) {
520
+ return dc($data, $label, TRUE);
521
+ }
522
+ }
523
+
524
+ $GLOBALS['wp_lingotek'] = Lingotek::get_instance();
readme.txt ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Lingotek Translation ===
2
+ Contributors: Chouby, msmith, erichie
3
+ Donate link: http://lingotek.com/
4
+ Tags: Lingotek, lingotek, multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
+ Requires at least: 3.8
6
+ Tested up to: 4.2
7
+ Stable tag: 1.0
8
+ License: GPLv2 or later
9
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
+
11
+ Lingotek Translation offers convenient cloud-based localization and translation solutions for your WordPress content.
12
+
13
+ == Description ==
14
+
15
+ Lingotek offers convenient cloud-based localization and translation.
16
+
17
+ Lingotek Translation simplifies the process of creating and maintaining your WordPress multilingual website. Features include machine, professional, and community translation solutions that ease the manual burden of launching in new markets while improving language consistency, cutting costs, and enabling you to deliver your brand to consumers abroad in the quickest and most effective way possible.
18
+
19
+ Make your WordPress website content truly multilingual. The power to translate is now inside WordPress!
20
+
21
+ = Customizable Translation Workflows =
22
+
23
+ WordPress administrators use Translation Profiles to categorize content by its relative value. You associate each content type with a Translation Profile, and the plugin carries out the appropriate workflow:
24
+
25
+ = Professional =
26
+
27
+ The plugin enables you to use your own translation agency or tap into Lingotek's marketplace of more than 5,000+ in-country translators. Don't have time to manage your own translation project? Let Lingotek take care of it for you. The plugin fully automates file transfer between Wordpress and Lingotek. You'll have full visibility into the translation process every step of the way. And once the translations are completed, they'll automatically download and publish to your website according to the preferences you've set.
28
+
29
+ = Community =
30
+
31
+ If you're looking to save money by avoiding professional translation, you can take the do-it-yourself approach and have your bilingual employees, partners and/or users perform translations right within Wordpress. The plugin integrates with and provides use of the Lingotek Workbench, a professional-grade text editor used for translating, reviewing, and post-editing multilingual content.
32
+
33
+ = Free Automatic =
34
+
35
+ Machine translation is an excellent option if you're on a tight budget, looking for near-instant results, and are okay with less-than-perfect quality. The plugin allows you to quickly and automatically translate your site by providing use of the commercial API for Microsoft Translator (the cost is covered by Lingotek for up to 100,000 words). Machine translations can be post-edited at any time using the Lingotek Workbench.
36
+
37
+ = Cloud-Based Translation Management System =
38
+
39
+ Need access to a enterprise level Translation Management System designed to work directly with Wordpress and other content management systems allowing you can get complete transparent access with all your translation projects? The cloud-based Lingotek Translation Management Systems helps your business access new markets and customers. Contact sales@lingotek.com for more information and pricing of these features.
40
+
41
+ = Benefits =
42
+
43
+ * Cost Savings
44
+ * Faster Translation Times
45
+ * Higher Quality
46
+ * Real-time Monitoring
47
+ * Eliminate the Copy/Paste Method of Translating
48
+ * Increased Involvement and Loyalty
49
+ * In-context Review
50
+
51
+ = Features =
52
+
53
+ Lingotek works in conjuction with the [Polylang](https://wordpress.org/plugins/polylang/) plugin (the plumbing to make WordPress multilingual ready) allowing you to create a bilingual or multilingual WordPress site. You write posts, pages, and create categories and post tags as usual, and then define the language for each of them.
54
+
55
+ * You can use as many languages as you want. RTL language scripts are supported. WordPress languages packs are automatically downloaded and updated.
56
+ * You can translate posts, pages, media, categories, post tags, menus, widgets...
57
+ * Custom post types, custom taxonomies, sticky posts and post formats, RSS feeds and all default WordPress widgets are supported.
58
+ * The language is either set by the content or by the language code in the url, or you can use one different subdomain or domain per language.
59
+ * Categories, post tags as well as some other metas are automatically copied when adding a new post or page translation.
60
+ * A customizable language switcher is provided as a widget or in the nav menu.
61
+ * The admin interface is of course multilingual too and each user can set the WordPress admin language in its profile.
62
+
63
+ = Credits =
64
+
65
+ Wherever third party code has been used, credit has been given in the code’s comments.
66
+
67
+ = Do you like Lingotek? =
68
+
69
+ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-reviews/lingotek#postform). It will help make the plugin better. Other [contributions](http://lingotek.wordpress.com/documentation/contribute/) (helping other users on the support forum) are welcome!
70
+
71
+ == Installation ==
72
+
73
+ 1. Make sure you have [Polylang](https://wordpress.org/plugins/polylang/) installed as it provides the framework for the Lingotek plugin.
74
+ 1. Upload the `wp-lingotek` folder to the `/wp-content/plugins/` directory.
75
+ 1. Activate the Lingotek plugin through the 'Plugins' menu in Wordpress.
76
+ 1. Create a Lingotek account by going to the `Translation` menu that appears in your admin menu.
77
+
78
+ == Frequently Asked Questions ==
79
+
80
+ Visit the [Lingotek documentation site](https://lingotek.atlassian.net/wiki/display/PDOC/WordPress)
81
+
82
+ == Screenshots ==
83
+
84
+ 1. The translation dashboard allows you to view your site languages, add and remove new languages, check the overall translation progress of your site, and see site analytics.
85
+ 2. Polylang and Lingotek work together seamlessly. Continue to use Polylang for content that you want to translate inside WordPress, while sending other content to be translated by Lingotek. The orange icons indicate Lingotek statuses/actions while the blue icons continue to represent Polylang actions.
86
+ 3. Use translation profiles. One of the most time-consuming activities of any multilingual web-site project is managing the ongoing flow of changes and additions to site content and configurations. Translation profiles were created to allow you to create and save and re-use your translation settings.
87
+ 4. Content type profiles. Manually choosing which content to upload and download is rarely what a content administrator wants to do, and automating the upload of every change is not workable because there are various types of content. Each type of translatable content can be assigned to a customizable profile. For example, by default, we like to have Posts use an Automatic profile so that content will automatically be uploaded for translation and the resulting translations automatically be downloaded back into WordPress.
88
+
89
+ == Changelog ==
90
+
91
+ = 1.0 (2015-06-22) =
92
+
93
+ * Released on June 22, 2015
94
+
95
+ == Upgrade Notice ==
96
+
97
+ = 1.0 =
98
+ The fully featured version is now released publicly. Upgrade!
uninstall.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ //if uninstall not called from WordPress exit
4
+ if (!defined('WP_UNINSTALL_PLUGIN'))
5
+ exit();
6
+
7
+ class Lingotek_Uninstall {
8
+
9
+ /*
10
+ * constructor: manages uninstall for multisite
11
+ *
12
+ * @since 0.1
13
+ */
14
+ function __construct() {
15
+ global $wpdb;
16
+
17
+ // check if it is a multisite uninstall - if so, run the uninstall function for each blog id
18
+ if (is_multisite()) {
19
+ foreach ($wpdb->get_col("SELECT blog_id FROM $wpdb->blogs") as $blog_id) {
20
+ switch_to_blog($blog_id);
21
+ $this->uninstall();
22
+ }
23
+ restore_current_blog();
24
+ }
25
+ else
26
+ $this->uninstall();
27
+ }
28
+
29
+ /*
30
+ * removes ALL plugin options
31
+ * do not remove the data merged with Polylang data in translations groups
32
+ * these data are removed when uninstalling Polylang
33
+ *
34
+ * @since 0.1
35
+ */
36
+ function uninstall() {
37
+ delete_option('lingotek_prefs');
38
+ delete_option('lingotek_content_type');
39
+ delete_option('lingotek_community');
40
+ delete_option('lingotek_defaults');
41
+ delete_option('lingotek_profiles');
42
+ delete_option('lingotek_token');
43
+ delete_option('lingotek_community_resources');
44
+ }
45
+ }
46
+
47
+ new Lingotek_Uninstall();