PageRenderTime 134ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 3ms

/htdocs/wp-content/plugins/sitepress-multilingual-cms/inc/translation-management/translation-management.class.php

https://bitbucket.org/dkrzos/phc
PHP | 3758 lines | 2883 code | 573 blank | 302 comment | 611 complexity | 6562e9287eb76105f6468d7ba157806a MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. define ( 'ICL_TM_NOT_TRANSLATED', 0);
  3. define ( 'ICL_TM_WAITING_FOR_TRANSLATOR', 1);
  4. define ( 'ICL_TM_IN_PROGRESS', 2);
  5. define ( 'ICL_TM_NEEDS_UPDATE', 3); //virt. status code (based on needs_update)
  6. define ( 'ICL_TM_DUPLICATE', 9);
  7. define ( 'ICL_TM_COMPLETE', 10);
  8. define('ICL_TM_NOTIFICATION_NONE', 0);
  9. define('ICL_TM_NOTIFICATION_IMMEDIATELY', 1);
  10. define('ICL_TM_NOTIFICATION_DAILY', 2);
  11. define('ICL_TM_TMETHOD_MANUAL', 0);
  12. define('ICL_TM_TMETHOD_EDITOR', 1);
  13. define('ICL_TM_TMETHOD_PRO', 2);
  14. define('ICL_TM_DOCS_PER_PAGE', 20);
  15. $asian_languages = array('ja', 'ko', 'zh-hans', 'zh-hant', 'mn', 'ne', 'hi', 'pa', 'ta', 'th');
  16. class TranslationManagement{
  17. private $selected_translator = array('ID'=>0);
  18. private $current_translator = array('ID'=>0);
  19. public $messages = array();
  20. public $dashboard_select = array();
  21. public $settings;
  22. public $admin_texts_to_translate = array();
  23. function __construct(){
  24. add_action('init', array($this, 'init'), 15);
  25. if(isset($_GET['icl_tm_message'])){
  26. $this->messages[] = array(
  27. 'type' => isset($_GET['icl_tm_message_type']) ? $_GET['icl_tm_message_type'] : 'updated',
  28. 'text' => $_GET['icl_tm_message']
  29. );
  30. }
  31. add_action('save_post', array($this, 'save_post_actions'), 11, 2); // calling *after* the Sitepress actions
  32. add_action('delete_post', array($this, 'delete_post_actions'), 1, 1); // calling *before* the Sitepress actions
  33. add_action('icl_ajx_custom_call', array($this, 'ajax_calls'), 10, 2);
  34. add_action('wp_ajax_show_post_content', array($this, '_show_post_content'));
  35. if(isset($_GET['sm']) && ($_GET['sm'] == 'dashboard' || $_GET['sm'] == 'jobs')){@session_start();}
  36. elseif(isset($_GET['page']) && preg_match('@/menu/translations-queue\.php$@', $_GET['page'])){@session_start();}
  37. add_filter('icl_additional_translators', array($this, 'icl_additional_translators'), 99, 3);
  38. add_filter('icl_translators_list', array($this, 'icanlocalize_translators_list'));
  39. add_action('user_register', array($this, 'clear_cache'));
  40. add_action('profile_update', array($this, 'clear_cache'));
  41. add_action('delete_user', array($this, 'clear_cache'));
  42. add_action('added_existing_user', array($this, 'clear_cache'));
  43. add_action('remove_user_from_blog', array($this, 'clear_cache'));
  44. add_action('admin_print_scripts', array($this, '_inline_js_scripts'));
  45. add_action('wp_ajax_icl_tm_user_search', array($this, '_user_search'));
  46. }
  47. function save_settings(){
  48. global $sitepress;
  49. $iclsettings['translation-management'] = $this->settings;
  50. $sitepress->save_settings($iclsettings);
  51. }
  52. function init(){
  53. global $wpdb, $current_user, $sitepress_settings, $sitepress, $pagenow;
  54. $this->settings =& $sitepress_settings['translation-management'];
  55. //logic for syncing comments
  56. if($sitepress->get_option('sync_comments_on_duplicates')){
  57. add_action('delete_comment', array($this, 'duplication_delete_comment'));
  58. add_action('edit_comment', array($this, 'duplication_edit_comment'));
  59. add_action('wp_set_comment_status', array($this, 'duplication_status_comment'), 10, 2);
  60. add_action('wp_insert_comment', array($this, 'duplication_insert_comment'), 100);
  61. }
  62. $this->initial_custom_field_translate_states();
  63. // defaults
  64. if(!isset($this->settings['notification']['new-job'])) $this->settings['notification']['new-job'] = ICL_TM_NOTIFICATION_IMMEDIATELY;
  65. if(!isset($this->settings['notification']['completed'])) $this->settings['notification']['completed'] = ICL_TM_NOTIFICATION_IMMEDIATELY;
  66. if(!isset($this->settings['notification']['resigned'])) $this->settings['notification']['resigned'] = ICL_TM_NOTIFICATION_IMMEDIATELY;
  67. if(!isset($this->settings['notification']['dashboard'])) $this->settings['notification']['dashboard'] = true;
  68. if(!isset($this->settings['notification']['purge-old'])) $this->settings['notification']['purge-old'] = 7;
  69. if(!isset($this->settings['custom_fields_translation'])) $this->settings['custom_fields_translation'] = array();
  70. if(!isset($this->settings['doc_translation_method'])) $this->settings['doc_translation_method'] = ICL_TM_TMETHOD_MANUAL;
  71. get_currentuserinfo();
  72. if(isset($current_user->ID)){
  73. $user = new WP_User($current_user->ID);
  74. }
  75. if(empty($user->data)) return;
  76. $ct['translator_id'] = $current_user->ID;
  77. $ct['display_name'] = isset($user->data->display_name) ? $user->data->display_name : $user->data->user_login;
  78. $ct['user_login'] = $user->data->user_login;
  79. $ct['language_pairs'] = get_user_meta($current_user->ID, $wpdb->prefix.'language_pairs', true);
  80. if(empty($ct['language_pairs'])) $ct['language_pairs'] = array();
  81. $this->current_translator = (object)$ct;
  82. $this->load_config_pre_process();
  83. $this->load_plugins_wpml_config();
  84. $this->load_theme_wpml_config();
  85. $this->load_config_post_process();
  86. if(isset($_POST['icl_tm_action'])){
  87. $this->process_request($_POST['icl_tm_action'], $_POST);
  88. }elseif(isset($_GET['icl_tm_action'])){
  89. $this->process_request($_GET['icl_tm_action'], $_GET);
  90. }
  91. //$this->load_plugins_wpml_config();
  92. //$this->load_theme_wpml_config();
  93. if($GLOBALS['pagenow']=='edit.php'){ // use standard WP admin notices
  94. add_action('admin_notices', array($this, 'show_messages'));
  95. }else{ // use custom WP admin notices
  96. add_action('icl_tm_messages', array($this, 'show_messages'));
  97. }
  98. if(isset($_GET['page']) && basename($_GET['page']) == 'translations-queue.php' && isset($_GET['job_id'])){
  99. add_filter('admin_head',array($this, '_show_tinyMCE'));
  100. }
  101. //if(!isset($this->settings['doc_translation_method'])){
  102. if(isset($this->settings['doc_translation_method']) && $this->settings['doc_translation_method'] < 0 ){
  103. if(isset($_GET['sm']) && $_GET['sm']=='mcsetup' && isset($_GET['src']) && $_GET['src']=='notice'){
  104. $this->settings['doc_translation_method'] = ICL_TM_TMETHOD_MANUAL;
  105. $this->save_settings();
  106. }else{
  107. add_action('admin_notices', array($this, '_translation_method_notice'));
  108. }
  109. }
  110. if(defined('WPML_TM_VERSION') && isset($_GET['page']) && $_GET['page'] == WPML_TM_FOLDER. '/menu/main.php' && isset($_GET['sm']) && $_GET['sm'] == 'translators'){
  111. $iclsettings =& $sitepress_settings;
  112. $sitepress->get_icl_translator_status($iclsettings);
  113. $sitepress->save_settings($iclsettings);
  114. }
  115. // default settings
  116. if(empty($this->settings['doc_translation_method']) || !defined('WPML_TM_VERSION')){
  117. $this->settings['doc_translation_method'] = ICL_TM_TMETHOD_MANUAL;
  118. }
  119. }
  120. function initial_custom_field_translate_states() {
  121. global $wpdb;
  122. $cf_keys_limit = 1000; // jic
  123. $custom_keys = $wpdb->get_col( "
  124. SELECT meta_key
  125. FROM $wpdb->postmeta
  126. GROUP BY meta_key
  127. ORDER BY meta_key
  128. LIMIT $cf_keys_limit" );
  129. $changed = false;
  130. foreach($custom_keys as $cfield) {
  131. if(empty($this->settings['custom_fields_translation'][$cfield]) || $this->settings['custom_fields_translation'][$cfield] == 0) {
  132. // see if a plugin handles this field
  133. $override = apply_filters('icl_cf_translate_state', 'nothing', $cfield);
  134. switch($override) {
  135. case 'nothing':
  136. break;
  137. case 'ignore':
  138. $changed = true;
  139. $this->settings['custom_fields_translation'][$cfield] = 3;
  140. break;
  141. case 'translate':
  142. $changed = true;
  143. $this->settings['custom_fields_translation'][$cfield] = 2;
  144. break;
  145. case 'copy':
  146. $changed = true;
  147. $this->settings['custom_fields_translation'][$cfield] = 1;
  148. break;
  149. }
  150. }
  151. }
  152. if ($changed) {
  153. $this->save_settings();
  154. }
  155. }
  156. function _translation_method_notice(){
  157. echo '<div class="error fade"><p id="icl_side_by_site">'.sprintf(__('New - side-by-site translation editor: <a href="%s">try it</a> | <a href="#cancel">no thanks</a>.', 'sitepress'),
  158. admin_url('admin.php?page='.WPML_TM_FOLDER.'/menu/main.php&sm=mcsetup&src=notice')) . '</p></div>';
  159. }
  160. function _show_tinyMCE() {
  161. wp_print_scripts('editor');
  162. //add_filter('the_editor', array($this, 'editor_directionality'), 9999);
  163. add_filter('tiny_mce_before_init', array($this, '_mce_set_direction'), 9999);
  164. add_filter('mce_buttons', array($this, '_mce_remove_fullscreen'), 9999);
  165. if (version_compare($GLOBALS['wp_version'], '3.1.4', '<=') && function_exists('wp_tiny_mce'))
  166. try{
  167. @wp_tiny_mce();
  168. } catch(Exception $e) { /*don't do anything with this */ }
  169. }
  170. function _mce_remove_fullscreen($options){
  171. foreach($options as $k=>$v) if($v == 'fullscreen') unset($options[$k]);
  172. return $options;
  173. }
  174. function _inline_js_scripts(){
  175. // remove fullscreen mode
  176. if(defined('WPML_TM_FOLDER') && isset($_GET['page']) && $_GET['page'] == WPML_TM_FOLDER . '/menu/translations-queue.php' && isset($_GET['job_id'])){
  177. ?>
  178. <script type="text/javascript">addLoadEvent(function(){jQuery('#ed_fullscreen').remove();});</script>
  179. <?php
  180. }
  181. }
  182. function _mce_set_direction($settings) {
  183. $job = $this->get_translation_job((int)$_GET['job_id'], false, true);
  184. if (!empty($job)) {
  185. $rtl_translation = in_array($job->language_code, array('ar','he','fa'));
  186. if ($rtl_translation) {
  187. $settings['directionality'] = 'rtl';
  188. } else {
  189. $settings['directionality'] = 'ltr';
  190. }
  191. }
  192. return $settings;
  193. }
  194. /*
  195. function editor_directionality($tag) {
  196. $job = $this->get_translation_job((int)$_GET['job_id'], false, true);
  197. $rtl_translation = in_array($job->language_code, array('ar','he','fa'));
  198. if ($rtl_translation) {
  199. $dir = 'dir="rtl"';
  200. } else {
  201. $dir = 'dir="ltr"';
  202. }
  203. return str_replace('<textarea', '<textarea ' . $dir, $tag);
  204. }
  205. */
  206. function process_request($action, $data){
  207. $data = stripslashes_deep($data);
  208. switch($action){
  209. case 'add_translator':
  210. if(wp_create_nonce('add_translator') == $data['add_translator_nonce']){
  211. // Initial adding
  212. if (isset($data['from_lang']) && isset($data['to_lang'])) {
  213. $data['lang_pairs'] = array();
  214. $data['lang_pairs'][$data['from_lang']] = array($data['to_lang'] => 1);
  215. }
  216. $this->add_translator($data['user_id'], $data['lang_pairs']);
  217. $_user = new WP_User($data['user_id']);
  218. wp_redirect('admin.php?page='.WPML_TM_FOLDER.'/menu/main.php&sm=translators&icl_tm_message='.urlencode(sprintf(__('%s has been added as a translator for this site.','sitepress'),$_user->data->display_name)).'&icl_tm_message_type=updated');
  219. }
  220. break;
  221. case 'edit_translator':
  222. if(wp_create_nonce('edit_translator') == $data['edit_translator_nonce']){
  223. $this->edit_translator($data['user_id'], $data['lang_pairs']);
  224. $_user = new WP_User($data['user_id']);
  225. wp_redirect('admin.php?page='.WPML_TM_FOLDER.'/menu/main.php&sm=translators&icl_tm_message='.urlencode(sprintf(__('Language pairs for %s have been edited.','sitepress'),$_user->data->display_name)).'&icl_tm_message_type=updated');
  226. }
  227. break;
  228. case 'remove_translator':
  229. if(wp_create_nonce('remove_translator') == $data['remove_translator_nonce']){
  230. $this->remove_translator($data['user_id']);
  231. $_user = new WP_User($data['user_id']);
  232. wp_redirect('admin.php?page='.WPML_TM_FOLDER.'/menu/main.php&sm=translators&icl_tm_message='.urlencode(sprintf(__('%s has been removed as a translator for this site.','sitepress'),$_user->data->display_name)).'&icl_tm_message_type=updated');
  233. }
  234. break;
  235. case 'edit':
  236. $this->selected_translator['ID'] = intval($data['user_id']);
  237. break;
  238. case 'dashboard_filter':
  239. $_SESSION['translation_dashboard_filter'] = $data['filter'];
  240. wp_redirect('admin.php?page='.WPML_TM_FOLDER . '/menu/main.php&sm=dashboard');
  241. break;
  242. case 'sort':
  243. if(isset($data['sort_by'])) $_SESSION['translation_dashboard_filter']['sort_by'] = $data['sort_by'];
  244. if(isset($data['sort_order'])) $_SESSION['translation_dashboard_filter']['sort_order'] = $data['sort_order'];
  245. break;
  246. case 'reset_filters':
  247. unset($_SESSION['translation_dashboard_filter']);
  248. break;
  249. case 'send_jobs':
  250. if(isset($data['iclnonce']) && wp_verify_nonce($data['iclnonce'], 'pro-translation-icl')){
  251. $this->send_jobs($data);
  252. }
  253. break;
  254. case 'jobs_filter':
  255. $_SESSION['translation_jobs_filter'] = $data['filter'];
  256. wp_redirect('admin.php?page='.WPML_TM_FOLDER . '/menu/main.php&sm=jobs');
  257. break;
  258. case 'ujobs_filter':
  259. $_SESSION['translation_ujobs_filter'] = $data['filter'];
  260. wp_redirect('admin.php?page='.WPML_TM_FOLDER . '/menu/translations-queue.php');
  261. break;
  262. case 'save_translation':
  263. if(!empty($data['resign'])){
  264. $this->resign_translator($data['job_id']);
  265. wp_redirect(admin_url('admin.php?page='.WPML_TM_FOLDER.'/menu/translations-queue.php&resigned='.$data['job_id']));
  266. exit;
  267. }else{
  268. $this->save_translation($data);
  269. }
  270. break;
  271. case 'save_notification_settings':
  272. $this->settings['notification'] = $data['notification'];
  273. $this->save_settings();
  274. $this->messages[] = array(
  275. 'type'=>'updated',
  276. 'text' => __('Preferences saved.', 'sitepress')
  277. );
  278. break;
  279. case 'create_job':
  280. global $current_user;
  281. if(!isset($this->current_translator->ID) && isset($current_user->ID)){
  282. $this->current_translator->ID = $current_user->ID;
  283. }
  284. $data['translator'] = $this->current_translator->ID;
  285. $job_ids = $this->send_jobs($data);
  286. wp_redirect('admin.php?page='.WPML_TM_FOLDER . '/menu/translations-queue.php&job_id=' . array_pop($job_ids));
  287. break;
  288. case 'cancel_jobs':
  289. $ret = $this->cancel_translation_request($data['icl_translation_id']);
  290. $this->messages[] = array(
  291. 'type'=>'updated',
  292. 'text' => __('Translation requests cancelled.', 'sitepress')
  293. );
  294. break;
  295. }
  296. }
  297. function ajax_calls($call, $data){
  298. global $wpdb, $sitepress, $sitepress_settings;
  299. switch($call){
  300. /*
  301. case 'save_dashboard_setting':
  302. $iclsettings['dashboard'] = $sitepress_settings['dashboard'];
  303. if(isset($data['setting']) && isset($data['value'])){
  304. $iclsettings['dashboard'][$data['setting']] = $data['value'];
  305. $sitepress->save_settings($iclsettings);
  306. }
  307. break;
  308. */
  309. case 'assign_translator':
  310. $_exp = explode('-', $data['translator_id']);
  311. $service = isset($_exp[1]) ? $_exp[1] : 'local';
  312. $translator_id = $_exp[0];
  313. if($this->assign_translation_job($data['job_id'], $translator_id, $service)){
  314. if($service == 'icanlocalize'){
  315. $job = $this->get_translation_job($data['job_id']);
  316. global $ICL_Pro_Translation;
  317. $ICL_Pro_Translation->send_post($job->original_doc_id, array($job->language_code), $translator_id);
  318. foreach($sitepress_settings['icl_lang_status'] as $lp){
  319. if($lp['from'] == $job->source_language_code && $lp['to'] == $job->language_code){
  320. $contract_id = $lp['contract_id'];
  321. $lang_tr_id = $lp['id'];
  322. break;
  323. }
  324. }
  325. $translator_edit_link = $sitepress->create_icl_popup_link(ICL_API_ENDPOINT . '/websites/' . $sitepress_settings['site_id']
  326. . '/website_translation_offers/' . $lang_tr_id . '/website_translation_contracts/'
  327. . $contract_id, array('title' => __('Chat with translator', 'sitepress'), 'unload_cb' => 'icl_thickbox_refresh'))
  328. . esc_html(ICL_Pro_Translation::get_translator_name($translator_id)) . '</a> (ICanLocalize)';
  329. }else{
  330. $translator_edit_link = '<a href="'.$this->get_translator_edit_url($data['translator_id']).'">' .
  331. esc_html($wpdb->get_var($wpdb->prepare("SELECT display_name FROM {$wpdb->users} WHERE ID=%d",$data['translator_id']))) . '</a>';
  332. }
  333. echo json_encode(array('error'=>0, 'message'=>$translator_edit_link, 'status'=>$this->status2text(ICL_TM_WAITING_FOR_TRANSLATOR), 'service'=>$service));
  334. }else{
  335. echo json_encode(array('error'=>1));
  336. }
  337. break;
  338. case 'icl_cf_translation':
  339. if(!empty($data['cf'])){
  340. foreach($data['cf'] as $k=>$v){
  341. $cft[base64_decode($k)] = $v;
  342. }
  343. $this->settings['custom_fields_translation'] = $cft;
  344. $this->save_settings();
  345. }
  346. echo '1|';
  347. break;
  348. case 'icl_doc_translation_method':
  349. $this->settings['doc_translation_method'] = intval($data['t_method']);
  350. $this->save_settings();
  351. echo '1|';
  352. break;
  353. case 'reset_duplication':
  354. $this->reset_duplicate_flag($_POST['post_id']);
  355. break;
  356. case 'set_duplication':
  357. $this->set_duplicate($_POST['post_id']);
  358. break;
  359. case 'make_duplicates':
  360. $mdata['iclpost'] = array($data['post_id']);
  361. $langs = explode(',', $data['langs']);
  362. foreach($langs as $lang){
  363. $mdata['duplicate_to'][$lang] = 1;
  364. }
  365. $this->make_duplicates($mdata);
  366. break;
  367. }
  368. }
  369. function show_messages(){
  370. if(!empty($this->messages)){
  371. foreach($this->messages as $m){
  372. echo '<div class="'.$m['type'].' below-h2"><p>' . $m['text'] . '</p></div>';
  373. }
  374. }
  375. }
  376. /* TRANSLATORS */
  377. /* ******************************************************************************************** */
  378. function add_translator($user_id, $language_pairs){
  379. global $wpdb;
  380. $user = new WP_User($user_id);
  381. $user->add_cap('translate');
  382. $um = get_user_meta($user_id, $wpdb->prefix . 'language_pairs', true);
  383. if(!empty($um)){
  384. foreach($um as $fr=>$to){
  385. if(isset($language_pairs[$fr])){
  386. $language_pairs[$fr] = array_merge($language_pairs[$fr], $to);
  387. }
  388. }
  389. }
  390. update_user_meta($user_id, $wpdb->prefix . 'language_pairs', $language_pairs);
  391. $this->clear_cache();
  392. }
  393. function edit_translator($user_id, $language_pairs){
  394. global $wpdb;
  395. $_user = new WP_User($user_id);
  396. if(empty($language_pairs)){
  397. $this->remove_translator($user_id);
  398. wp_redirect('admin.php?page='.WPML_TM_FOLDER.'/menu/main.php&sm=translators&icl_tm_message='.
  399. urlencode(sprintf(__('%s has been removed as a translator for this site.','sitepress'),$_user->data->display_name)).'&icl_tm_message_type=updated'); exit;
  400. }
  401. else{
  402. if(!$_user->has_cap('translate')) $_user->add_cap('translate');
  403. update_user_meta($user_id, $wpdb->prefix . 'language_pairs', $language_pairs);
  404. }
  405. }
  406. function remove_translator($user_id){
  407. global $wpdb;
  408. $user = new WP_User($user_id);
  409. $user->remove_cap('translate');
  410. delete_user_meta($user_id, $wpdb->prefix . 'language_pairs');
  411. $this->clear_cache();
  412. }
  413. function is_translator($user_id, $args = array()){
  414. // $lang_from
  415. // $lang_to
  416. // $job_id
  417. //$default_args = array();
  418. //extract($default_args);
  419. extract($args, EXTR_OVERWRITE);
  420. global $wpdb;
  421. $user = new WP_User($user_id);
  422. $is_translator = $user->has_cap('translate');
  423. if(isset($lang_from) && isset($lang_to)){
  424. $um = get_user_meta($user_id, $wpdb->prefix . 'language_pairs', true);
  425. $is_translator = $is_translator && isset($um[$lang_from][$lang_to]) && $um[$lang_from][$lang_to];
  426. }
  427. if(isset($job_id)){
  428. $translator_id = $wpdb->get_var($wpdb->prepare("
  429. SELECT j.translator_id
  430. FROM {$wpdb->prefix}icl_translate_job j
  431. JOIN {$wpdb->prefix}icl_translation_status s ON j.rid = s.rid
  432. WHERE job_id = %d AND s.translation_service='local'
  433. ", $job_id));
  434. //$is_translator = $is_translator && ($translator_id == $user_id || ($translator_id === '0'));
  435. $is_translator = $is_translator && (($translator_id == $user_id) || empty($translator_id));
  436. }
  437. return $is_translator;
  438. }
  439. function translator_exists($id, $from, $to, $type = 'local'){
  440. global $sitepress_settings;
  441. $exists = false;
  442. if($type == 'icanlocalize' && !empty($sitepress_settings['icl_lang_status'])){
  443. foreach($sitepress_settings['icl_lang_status'] as $lpair){
  444. if($lpair['from'] == $from && $lpair['to'] == $to){
  445. if(!empty($lpair['translators'])){
  446. foreach($lpair['translators'] as $t){
  447. if($t['id'] == $id){
  448. $exists = true;
  449. break(2);
  450. }
  451. }
  452. }
  453. }
  454. }
  455. }elseif($type == 'local'){
  456. $exists = $this->is_translator($id, array('lang_from'=>$from, 'lang_to'=>$to));
  457. }
  458. return $exists;
  459. }
  460. function set_default_translator($id, $from, $to, $type = 'local'){
  461. global $sitepress, $sitepress_settings;
  462. $iclsettings['default_translators'] = isset($sitepress_settings['default_translators']) ? $sitepress_settings['default_translators'] : array();
  463. $iclsettings['default_translators'][$from][$to] = array('id'=>$id, 'type'=>$type);
  464. $sitepress->save_settings($iclsettings);
  465. }
  466. function get_default_translator($from, $to){
  467. global $sitepress_settings;
  468. if(isset($sitepress_settings['default_translators'][$from][$to])){
  469. $dt = $sitepress_settings['default_translators'][$from][$to];
  470. }else{
  471. $dt = array();
  472. }
  473. return $dt;
  474. }
  475. public function get_blog_not_translators(){
  476. global $wpdb;
  477. $cached_translators = get_option($wpdb->prefix . 'icl_non_translators_cached', array());
  478. if (!empty($cached_translators)) {
  479. return $cached_translators;
  480. }
  481. $sql = "SELECT u.ID, u.user_login, u.display_name, m.meta_value AS caps
  482. FROM {$wpdb->users} u JOIN {$wpdb->usermeta} m ON u.id=m.user_id AND m.meta_key = '{$wpdb->prefix}capabilities' ORDER BY u.display_name";
  483. $res = $wpdb->get_results($sql);
  484. $users = array();
  485. foreach($res as $row){
  486. $caps = @unserialize($row->caps);
  487. if(!isset($caps['translate'])){
  488. $users[] = $row;
  489. }
  490. }
  491. update_option($wpdb->prefix . 'icl_non_translators_cached', $users);
  492. return $users;
  493. }
  494. /**
  495. * Implementation of 'icl_translators_list' hook
  496. *
  497. * @global object $sitepress
  498. * @param array $array
  499. * @return array
  500. */
  501. public function icanlocalize_translators_list() {
  502. global $sitepress_settings, $sitepress;
  503. $lang_status = isset($sitepress_settings['icl_lang_status']) ? $sitepress_settings['icl_lang_status'] : array();
  504. if (0 != key($lang_status)){
  505. $buf[] = $lang_status;
  506. $lang_status = $buf;
  507. }
  508. $translators = array();
  509. foreach($lang_status as $lpair){
  510. foreach((array)$lpair['translators'] as $translator){
  511. $translators[$translator['id']]['name'] = $translator['nickname'];
  512. $translators[$translator['id']]['langs'][$lpair['from']][] = $lpair['to'];
  513. $translators[$translator['id']]['type'] = 'ICanLocalize';
  514. $translators[$translator['id']]['action'] = $sitepress->create_icl_popup_link(ICL_API_ENDPOINT . '/websites/' . $sitepress_settings['site_id']
  515. . '/website_translation_offers/' . $lpair['id'] . '/website_translation_contracts/'
  516. . $translator['contract_id'], array('title' => __('Chat with translator', 'sitepress'), 'unload_cb' => 'icl_thickbox_refresh', 'ar'=>1)) . __('Chat with translator', 'sitepress') . '</a>';
  517. }
  518. }
  519. return $translators;
  520. }
  521. public function get_blog_translators($args = array()){
  522. global $wpdb;
  523. $args_default = array('from'=>false, 'to'=>false);
  524. extract($args_default);
  525. extract($args, EXTR_OVERWRITE);
  526. // $sql = "SELECT u.ID, u.user_login, u.display_name, u.user_email, m.meta_value AS caps
  527. // FROM {$wpdb->users} u JOIN {$wpdb->usermeta} m ON u.id=m.user_id AND m.meta_key LIKE '{$wpdb->prefix}capabilities' ORDER BY u.display_name";
  528. // $res = $wpdb->get_results($sql);
  529. $cached_translators = get_option($wpdb->prefix . 'icl_translators_cached', array());
  530. if (empty($cached_translators)) {
  531. $sql = "SELECT u.ID FROM {$wpdb->users} u JOIN {$wpdb->usermeta} m ON u.id=m.user_id AND m.meta_key = '{$wpdb->prefix}language_pairs' ORDER BY u.display_name";
  532. $res = $wpdb->get_results($sql);
  533. update_option($wpdb->prefix . 'icl_translators_cached', $res);
  534. } else {
  535. $res = $cached_translators;
  536. }
  537. $users = array();
  538. foreach($res as $row){
  539. $user = new WP_User($row->ID);
  540. // $caps = @unserialize($row->caps);
  541. // $row->language_pairs = (array)get_user_meta($row->ID, $wpdb->prefix.'language_pairs', true);
  542. $user->language_pairs = (array)get_user_meta($row->ID, $wpdb->prefix.'language_pairs', true);
  543. // if(!empty($from) && !empty($to) && (!isset($row->language_pairs[$from][$to]) || !$row->language_pairs[$from][$to])){
  544. // continue;
  545. // }
  546. if(!empty($from) && !empty($to) && (!isset($user->language_pairs[$from][$to]) || !$user->language_pairs[$from][$to])){
  547. continue;
  548. }
  549. // if(isset($caps['translate'])){
  550. // $users[] = $user;
  551. // }
  552. if($user->has_cap('translate')){
  553. $users[] = $user;
  554. }
  555. }
  556. return $users;
  557. }
  558. function get_selected_translator(){
  559. global $wpdb;
  560. if($this->selected_translator['ID']){
  561. $user = new WP_User($this->selected_translator['ID']);
  562. $this->selected_translator['display_name'] = $user->data->display_name;
  563. $this->selected_translator['user_login'] = $user->data->user_login;
  564. $this->selected_translator['language_pairs'] = get_user_meta($this->selected_translator['ID'], $wpdb->prefix.'language_pairs', true);
  565. }else{
  566. $this->selected_translator['ID'] = 0;
  567. }
  568. return (object)$this->selected_translator;
  569. }
  570. function get_current_translator(){
  571. return $this->current_translator;
  572. }
  573. public function get_translator_edit_url($translator_id){
  574. $url = '';
  575. if(!empty($translator_id)){
  576. $url = 'admin.php?page='. WPML_TM_FOLDER .'/menu/main.php&amp;sm=translators&icl_tm_action=edit&amp;user_id='. $translator_id;
  577. }
  578. return $url;
  579. }
  580. public function translators_dropdown($args = array()){
  581. global $sitepress_settings;
  582. $args_default = array(
  583. 'from'=>false, 'to'=>false,
  584. 'name' => 'translator_id',
  585. 'selected' => 0,
  586. 'echo' => true,
  587. 'services' => array('local')
  588. );
  589. extract($args_default);
  590. extract($args, EXTR_OVERWRITE);
  591. $translators = array();
  592. if(in_array('icanlocalize', $services)){
  593. if(empty($sitepress_settings['icl_lang_status'])) $sitepress_settings['icl_lang_status'] = array();
  594. foreach((array)$sitepress_settings['icl_lang_status'] as $langpair){
  595. if($from && $from != $langpair['from']) continue;
  596. if($to && $to != $langpair['to']) continue;
  597. if(!empty($langpair['translators'])){
  598. if (1 < count($langpair['translators'])) {
  599. $translators[] = (object) array(
  600. 'ID' => '0-icanlocalize',
  601. 'display_name' => __('First available', 'sitepress'),
  602. 'service' => 'ICanLocalize'
  603. );
  604. }
  605. foreach($langpair['translators'] as $tr){
  606. if(!isset($_icl_translators[$tr['id']])){
  607. $translators[] = $_icl_translators[$tr['id']] = (object) array(
  608. 'ID'=>$tr['id'].'-icanlocalize',
  609. 'display_name'=>$tr['nickname'],
  610. 'service' => 'ICanLocalize'
  611. );
  612. }
  613. }
  614. }
  615. }
  616. }
  617. if(in_array('local', $services)){
  618. $translators[] = (object) array(
  619. 'ID' => 0,
  620. 'display_name' => __('First available', 'sitepress'),
  621. );
  622. $translators = array_merge($translators, $this->get_blog_translators(array('from'=>$from,'to'=>$to)));
  623. }
  624. ?>
  625. <select name="<?php echo $name ?>">
  626. <?php foreach($translators as $t):?>
  627. <option value="<?php echo $t->ID ?>" <?php if($selected==$t->ID):?>selected="selected"<?php endif;?>><?php echo esc_html($t->display_name);
  628. ?> (<?php if(isset($t->service)) echo $t->service; else _e('Local', 'sitepress'); ?>)</option>
  629. <?php endforeach; ?>
  630. </select>
  631. <?php
  632. }
  633. public function get_number_of_docs_sent($service = 'icanlocalize'){
  634. global $wpdb;
  635. $n = $wpdb->get_var($wpdb->prepare("
  636. SELECT COUNT(rid) FROM {$wpdb->prefix}icl_translation_status WHERE translation_service=%s
  637. ", $service));
  638. return $n;
  639. }
  640. public function get_number_of_docs_pending($service = 'icanlocalize'){
  641. global $wpdb;
  642. $n = $wpdb->get_var($wpdb->prepare("
  643. SELECT COUNT(rid) FROM {$wpdb->prefix}icl_translation_status WHERE translation_service=%s AND status < " . ICL_TM_COMPLETE . "
  644. ", $service));
  645. return $n;
  646. }
  647. /* HOOKS */
  648. /* ******************************************************************************************** */
  649. function save_post_actions($post_id, $post, $force_set_status = false){
  650. global $wpdb, $sitepress, $sitepress_settings, $current_user;
  651. // skip revisions
  652. if($post->post_type == 'revision'){
  653. return;
  654. }
  655. // skip auto-drafts
  656. if($post->post_status == 'auto-draft'){
  657. return;
  658. }
  659. // skip autosave
  660. if(isset($_POST['autosave'])){
  661. return;
  662. }
  663. // is this the original document?
  664. if(!empty($_POST['icl_trid'])){
  665. $is_original = $wpdb->get_var($wpdb->prepare("SELECT source_language_code IS NULL FROM {$wpdb->prefix}icl_translations WHERE element_id=%d AND trid=%d", $post_id, $_POST['icl_trid']));
  666. }
  667. // when a manual translation is added/edited make sure to update translation tables
  668. if(!empty($_POST['icl_trid']) && !$is_original){
  669. $trid = $_POST['icl_trid'];
  670. $lang = $_POST['icl_post_language'];
  671. $res = $wpdb->get_row($wpdb->prepare("
  672. SELECT element_id, language_code FROM {$wpdb->prefix}icl_translations WHERE trid=%d AND source_language_code IS NULL
  673. ", $trid));
  674. $original_post_id = $res->element_id;
  675. $from_lang = $res->language_code;
  676. $original_post = get_post($original_post_id);
  677. $translation_id = $wpdb->get_var($wpdb->prepare("
  678. SELECT translation_id FROM {$wpdb->prefix}icl_translations WHERE trid=%d AND language_code='%s'
  679. ", $trid, $lang));
  680. $md5 = $this->post_md5($original_post);
  681. get_currentuserinfo();
  682. $user_id = $current_user->ID;
  683. if(!$this->is_translator($user_id, array('lang_from'=>$from_lang, 'lang_to'=>$lang))){
  684. $this->add_translator($user_id, array($from_lang=>array($lang=>1)));
  685. }
  686. if($translation_id){
  687. $translation_package = $this->create_translation_package($original_post_id);
  688. list($rid, $update) = $this->update_translation_status(array(
  689. 'translation_id' => $translation_id,
  690. 'status' => isset($force_set_status) && $force_set_status > 0 ? $force_set_status : ICL_TM_COMPLETE,
  691. 'translator_id' => $user_id,
  692. 'needs_update' => 0,
  693. 'md5' => $md5,
  694. 'translation_service' => 'local',
  695. 'translation_package' => serialize($translation_package)
  696. ));
  697. if(!$update){
  698. $job_id = $this->add_translation_job($rid, $user_id , $translation_package);
  699. }else{
  700. $job_id = $wpdb->get_var($wpdb->prepare("SELECT MAX(job_id) FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d GROUP BY rid", $rid));
  701. }
  702. // saving the translation
  703. $this->save_job_fields_from_post($job_id, $post);
  704. /*
  705. $data['complete'] = 1;
  706. $data['job_id'] = $job_id;
  707. $job = $this->get_translation_job($job_id,1);
  708. foreach($job->elements as $element){
  709. $field_data = '';
  710. switch($element->field_type){
  711. case 'title':
  712. $field_data = $this->encode_field_data($post->post_title, $element->field_format);
  713. break;
  714. case 'body':
  715. $field_data = $this->encode_field_data($post->post_content, $element->field_format);
  716. break;
  717. case 'excerpt':
  718. $field_data = $this->encode_field_data($post->post_excerpt, $element->field_format);
  719. break;
  720. default:
  721. if(false !== strpos($element->field_type, 'field-') && !empty($this->settings['custom_fields_translation'])){
  722. $cf_name = preg_replace('#^field-#', '', $element->field_type);
  723. if(isset($this->settings['custom_fields_translation'][$cf_name])){
  724. if($this->settings['custom_fields_translation'][$cf_name] == 1){ //copy
  725. $field_data = get_post_meta($original_post->ID, $cf_name, 1);
  726. $field_data = $this->encode_field_data($field_data, $element->field_format);
  727. }elseif($this->settings['custom_fields_translation'][$cf_name] == 2){ // translate
  728. $field_data = get_post_meta($post->ID, $cf_name, 1);
  729. $field_data = $this->encode_field_data($field_data, $element->field_format);
  730. }
  731. }
  732. }else{
  733. // taxonomies
  734. // TBD
  735. }
  736. }
  737. $wpdb->update($wpdb->prefix.'icl_translate',
  738. array('field_data_translated'=>$field_data, 'field_finished'=>1),
  739. array('tid'=>$element->tid)
  740. );
  741. }
  742. $wpdb->update($wpdb->prefix . 'icl_translate_job', array('translated'=>1), array('job_id'=>$job_id));
  743. */
  744. }
  745. }
  746. // if this is an original post - compute md5 hash and mark for update if neded
  747. if(!empty($_POST['icl_trid']) && empty($_POST['icl_minor_edit'])){
  748. $needs_update = false;
  749. $is_original = false;
  750. $translations = $sitepress->get_element_translations($_POST['icl_trid'], 'post_' . $post->post_type);
  751. foreach($translations as $lang=>$translation){
  752. if($translation->original == 1 && $translation->element_id == $post_id){
  753. $is_original = true;
  754. break;
  755. }
  756. }
  757. if($is_original){
  758. $md5 = $this->post_md5($post_id);
  759. foreach($translations as $lang=>$translation){
  760. if(!$translation->original){
  761. $emd5 = $wpdb->get_var($wpdb->prepare("SELECT md5 FROM {$wpdb->prefix}icl_translation_status WHERE translation_id = %d", $translation->translation_id));
  762. if($md5 != $emd5){
  763. $wpdb->update($wpdb->prefix.'icl_translation_status', array('needs_update'=>1, 'md5'=>$md5), array('translation_id'=>$translation->translation_id));
  764. }
  765. }
  766. }
  767. }
  768. }
  769. // sync copies/duplicates
  770. $duplicates = $this->get_duplicates($post_id);
  771. foreach($duplicates as $lang => $_pid){
  772. $this->make_duplicate($post_id, $lang);
  773. }
  774. // review?
  775. /*
  776. if($_POST['icl_trid']){
  777. //
  778. // get original document
  779. $translations = $sitepress->get_element_translations($_POST['icl_trid'], 'post_' . $post->post_type);
  780. foreach($translations as $t){
  781. if($t->original){
  782. $origin = $t->language_code;
  783. }
  784. }
  785. // remove ?
  786. $rid = $wpdb->get_var($wpdb->prepare("SELECT rid FROM {$wpdb->prefix}icl_content_status WHERE nid = %d"), $post_id);
  787. if(!$rid){
  788. $wpdb->insert($wpdb->prefix.'icl_content_status', array('nid' => $post_id, 'md5'=>$this->post_md5($post), 'timestamp'=>date('Y-m-d H:i:s')));
  789. $rid = $wpdb->insert_id;
  790. }else{
  791. $wpdb->update($wpdb->prefix.'icl_content_status', array('md5'=>$this->post_md5($post)), array('rid'=>$rid));
  792. }
  793. // add update icl_core_status entry
  794. $id = $wpdb->get_var($wpdb->prepare("SELECT rid FROM {$wpdb->prefix}icl_core_status WHERE rid = %d AND target= = %s"), $rid, $_POST['icl_post_language']);
  795. if(!$id){
  796. $wpdb->insert($wpdb->prefix.'icl_core_status', array('rid' => $rid, 'origin' => $origin, 'target' => $_POST['icl_post_language'], 'status' => 1 )); //!!!!!!!
  797. }else{
  798. $wpdb->update($wpdb->prefix.'icl_content_status', array('md5'=>$this->post_md5($post)), array('rid'=>$rid));
  799. }
  800. }
  801. */
  802. }
  803. function delete_post_actions($post_id){
  804. global $wpdb;
  805. $post_type = $wpdb->get_var("SELECT post_type FROM {$wpdb->posts} WHERE ID={$post_id}");
  806. if(!empty($post_type)){
  807. $translation_id = $wpdb->get_var($wpdb->prepare("SELECT translation_id FROM {$wpdb->prefix}icl_translations WHERE element_id=%d AND element_type=%s", $post_id, 'post_' . $post_type));
  808. if($translation_id){
  809. $rid = $wpdb->get_var($wpdb->prepare("SELECT rid FROM {$wpdb->prefix}icl_translation_status WHERE translation_id=%d", $translation_id));
  810. $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}icl_translation_status WHERE translation_id=%d", $translation_id));
  811. if($rid){
  812. $jobs = $wpdb->get_col($wpdb->prepare("SELECT job_id FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d", $rid));
  813. $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d", $rid));
  814. if(!empty($jobs)){
  815. $wpdb->query("DELETE FROM {$wpdb->prefix}icl_translate WHERE job_id IN (".join(',', $jobs).")");
  816. }
  817. }
  818. }
  819. }
  820. }
  821. /* TRANSLATIONS */
  822. /* ******************************************************************************************** */
  823. /**
  824. * calculate post md5
  825. *
  826. * @param object|int $post
  827. * @return string
  828. *
  829. * @todo full support for custom posts and custom taxonomies
  830. */
  831. function post_md5($post){
  832. if (isset($post->external_type) && $post->external_type) {
  833. $md5str = '';
  834. foreach ($post->string_data as $key => $value) {
  835. $md5str .= $key . $value;
  836. }
  837. } else {
  838. $post_tags = $post_categories = $custom_fields_values = array();
  839. if(is_numeric($post)){
  840. $post = get_post($post);
  841. }
  842. $post_type = $post->post_type;
  843. if($post_type=='post'){
  844. foreach(wp_get_object_terms($post->ID, 'post_tag') as $tag){
  845. $post_tags[] = $tag->name;
  846. }
  847. if(is_array($post_tags)){
  848. sort($post_tags, SORT_STRING);
  849. }
  850. foreach(wp_get_object_terms($post->ID, 'category') as $cat){
  851. $post_categories[] = $cat->name;
  852. }
  853. if(is_array($post_categories)){
  854. sort($post_categories, SORT_STRING);
  855. }
  856. global $wpdb, $sitepress_settings;
  857. // get custom taxonomies
  858. $taxonomies = $wpdb->get_col("
  859. SELECT DISTINCT tx.taxonomy
  860. FROM {$wpdb->term_taxonomy} tx JOIN {$wpdb->term_relationships} tr ON tx.term_taxonomy_id = tr.term_taxonomy_id
  861. WHERE tr.object_id = {$post->ID}
  862. ");
  863. sort($taxonomies, SORT_STRING);
  864. foreach($taxonomies as $t){
  865. if(@intval($sitepress_settings['taxonomies_sync_option'][$t]) == 1){
  866. $taxs = array();
  867. foreach(wp_get_object_terms($post->ID, $t) as $trm){
  868. $taxs[] = $trm->name;
  869. }
  870. if($taxs){
  871. sort($taxs,SORT_STRING);
  872. $all_taxs[] = '['.$t.']:'.join(',',$taxs);
  873. }
  874. }
  875. }
  876. }
  877. $custom_fields_values = array();
  878. if(is_array($this->settings['custom_fields_translation'])){
  879. foreach($this->settings['custom_fields_translation'] as $cf => $op){
  880. if ($op == 2) {
  881. $custom_fields_values[] = get_post_meta($post->ID, $cf, true);
  882. }
  883. }
  884. }
  885. $md5str =
  886. $post->post_title . ';' .
  887. $post->post_content . ';' .
  888. join(',',$post_tags).';' .
  889. join(',',$post_categories) . ';' .
  890. join(',', $custom_fields_values);
  891. if(!empty($all_taxs)){
  892. $md5str .= ';' . join(';', $all_taxs);
  893. }
  894. }
  895. $md5 = md5($md5str);
  896. return $md5;
  897. }
  898. /**
  899. * get documents
  900. *
  901. * @param array $args
  902. */
  903. function get_documents($args){
  904. extract($args);
  905. global $wpdb, $wp_query, $sitepress;
  906. $t_el_types = array_keys($sitepress->get_translatable_documents());
  907. // SELECT
  908. $select = " p.ID AS post_id, p.post_title, p.post_content, p.post_type, p.post_status, p.post_date, t.trid, t.source_language_code <> '' AS is_translation";
  909. if($to_lang){
  910. $select .= ", iclts.status, iclts.needs_update";
  911. }else{
  912. foreach($sitepress->get_active_languages() as $lang){
  913. if($lang['code'] == $from_lang) continue;
  914. $tbl_alias_suffix = str_replace('-','_',$lang['code']);
  915. $select .= ", iclts_{$tbl_alias_suffix}.status AS status_{$tbl_alias_suffix}, iclts_{$tbl_alias_suffix}.needs_update AS needs_update_{$tbl_alias_suffix}";
  916. }
  917. }
  918. // FROM
  919. $from = " {$wpdb->posts} p";
  920. // JOIN
  921. $join = "";
  922. $join .= " LEFT JOIN {$wpdb->prefix}icl_translations t ON t.element_id=p.ID\n";
  923. if($to_lang){
  924. $tbl_alias_suffix = str_replace('-','_',$to_lang);
  925. $join .= " LEFT JOIN {$wpdb->prefix}icl_translations iclt_{$tbl_alias_suffix}
  926. ON iclt_{$tbl_alias_suffix}.trid=t.trid AND iclt_{$tbl_alias_suffix}.language_code='{$to_lang}'\n";
  927. $join .= " LEFT JOIN {$wpdb->prefix}icl_translation_status iclts ON iclts.translation_id=iclt_{$tbl_alias_suffix}.translati…

Large files files are truncated, but you can click here to view the full file