PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/acipriani/madeinapulia.com
PHP | 4216 lines | 3263 code | 643 blank | 310 comment | 770 complexity | d801fb1245a37a064a7a36afd3c95f6a MD5 | raw file
Possible License(s): GPL-3.0, MIT, BSD-3-Clause, LGPL-2.1, GPL-2.0, Apache-2.0
  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. if(!class_exists('WPML_Config')) {
  17. require ICL_PLUGIN_PATH . '/inc/wpml-config/wpml-config.class.php';
  18. }
  19. class TranslationManagement{
  20. private $selected_translator = array('ID'=>0);
  21. private $current_translator = array('ID'=>0);
  22. public $messages = array();
  23. public $dashboard_select = array();
  24. public $settings;
  25. public $admin_texts_to_translate = array();
  26. function __construct(){
  27. add_action('init', array($this, 'init'), 1500);
  28. if(isset($_GET['icl_tm_message'])){
  29. $this->messages[] = array(
  30. 'type' => isset($_GET['icl_tm_message_type']) ? $_GET['icl_tm_message_type'] : 'updated',
  31. 'text' => $_GET['icl_tm_message']
  32. );
  33. }
  34. add_action('save_post', array($this, 'save_post_actions'), 110, 2); // calling *after* the Sitepress actions
  35. add_action('delete_post', array($this, 'delete_post_actions'), 1, 1); // calling *before* the Sitepress actions
  36. //add_action('edit_term', array($this, 'edit_term'),11, 2); // calling *after* the Sitepress actions
  37. add_action('icl_ajx_custom_call', array($this, 'ajax_calls'), 10, 2);
  38. add_action('wp_ajax_show_post_content', array($this, '_show_post_content'));
  39. if(isset($_GET['sm']) && ($_GET['sm'] == 'dashboard' || $_GET['sm'] == 'jobs')){@session_start();}
  40. elseif(isset($_GET['page']) && preg_match('@/menu/translations-queue\.php$@', $_GET['page'])){@session_start();}
  41. add_filter('icl_additional_translators', array($this, 'icl_additional_translators'), 99, 3);
  42. add_filter('icl_translators_list', array(__CLASS__, 'icanlocalize_translators_list'));
  43. add_action('user_register', array($this, 'clear_cache'));
  44. add_action('profile_update', array($this, 'clear_cache'));
  45. add_action('delete_user', array($this, 'clear_cache'));
  46. add_action('added_existing_user', array($this, 'clear_cache'));
  47. add_action('remove_user_from_blog', array($this, 'clear_cache'));
  48. add_action('admin_print_scripts', array($this, '_inline_js_scripts'));
  49. add_action('wp_ajax_icl_tm_user_search', array($this, '_user_search'));
  50. add_action('wp_ajax_icl_tm_abort_translation', array($this, 'abort_translation'));
  51. }
  52. function save_settings()
  53. {
  54. global $sitepress, $sitepress_settings;
  55. $iclsettings[ 'translation-management' ] = $this->settings;
  56. $custom_posts_sync_option = isset( $sitepress_settings[ 'custom_posts_sync_option' ] ) ? $sitepress_settings[ 'custom_posts_sync_option' ] : false;
  57. if ( is_array( $custom_posts_sync_option ) ) {
  58. foreach ( $custom_posts_sync_option as $k => $v ) {
  59. $iclsettings[ 'custom_posts_sync_option' ][ $k ] = $v;
  60. }
  61. }
  62. $sitepress->save_settings( $iclsettings );
  63. }
  64. function init(){
  65. global $wpdb, $current_user, $sitepress_settings, $sitepress;
  66. $this->settings =& $sitepress_settings['translation-management'];
  67. //logic for syncing comments
  68. if($sitepress->get_option('sync_comments_on_duplicates')){
  69. add_action('delete_comment', array($this, 'duplication_delete_comment'));
  70. add_action('edit_comment', array($this, 'duplication_edit_comment'));
  71. add_action('wp_set_comment_status', array($this, 'duplication_status_comment'), 10, 2);
  72. add_action('wp_insert_comment', array($this, 'duplication_insert_comment'), 100);
  73. }
  74. $this->initial_custom_field_translate_states();
  75. // defaults
  76. if(!isset($this->settings['notification']['new-job'])) $this->settings['notification']['new-job'] = ICL_TM_NOTIFICATION_IMMEDIATELY;
  77. if(!isset($this->settings['notification']['completed'])) $this->settings['notification']['completed'] = ICL_TM_NOTIFICATION_IMMEDIATELY;
  78. if(!isset($this->settings['notification']['resigned'])) $this->settings['notification']['resigned'] = ICL_TM_NOTIFICATION_IMMEDIATELY;
  79. if(!isset($this->settings['notification']['dashboard'])) $this->settings['notification']['dashboard'] = true;
  80. if(!isset($this->settings['notification']['purge-old'])) $this->settings['notification']['purge-old'] = 7;
  81. if(!isset($this->settings['custom_fields_translation'])) $this->settings['custom_fields_translation'] = array();
  82. if(!isset($this->settings['doc_translation_method'])) $this->settings['doc_translation_method'] = ICL_TM_TMETHOD_MANUAL;
  83. get_currentuserinfo();
  84. $user = false;
  85. if(isset($current_user->ID)){
  86. $user = new WP_User($current_user->ID);
  87. }
  88. if(!$user || empty($user->data)) return;
  89. $ct['translator_id'] = $current_user->ID;
  90. $ct['display_name'] = isset($user->data->display_name) ? $user->data->display_name : $user->data->user_login;
  91. $ct['user_login'] = $user->data->user_login;
  92. $ct['language_pairs'] = get_user_meta($current_user->ID, $wpdb->prefix.'language_pairs', true);
  93. if(empty($ct['language_pairs'])) $ct['language_pairs'] = array();
  94. $this->current_translator = (object)$ct;
  95. WPML_Config::load_config();
  96. if(isset($_POST['icl_tm_action'])){
  97. $this->process_request($_POST['icl_tm_action'], $_POST);
  98. }elseif(isset($_GET['icl_tm_action'])){
  99. $this->process_request($_GET['icl_tm_action'], $_GET);
  100. }
  101. if($GLOBALS['pagenow']=='edit.php'){ // use standard WP admin notices
  102. add_action('admin_notices', array($this, 'show_messages'));
  103. }else{ // use custom WP admin notices
  104. add_action('icl_tm_messages', array($this, 'show_messages'));
  105. }
  106. if(isset($_GET['page']) && basename($_GET['page']) == 'translations-queue.php' && isset($_GET['job_id'])){
  107. add_filter('admin_head',array($this, '_show_tinyMCE'));
  108. }
  109. //if(!isset($this->settings['doc_translation_method'])){
  110. if(isset($this->settings['doc_translation_method']) && $this->settings['doc_translation_method'] < 0 ){
  111. if(isset($_GET['sm']) && $_GET['sm']=='mcsetup' && isset($_GET['src']) && $_GET['src']=='notice'){
  112. $this->settings['doc_translation_method'] = ICL_TM_TMETHOD_MANUAL;
  113. $this->save_settings();
  114. }else{
  115. add_action('admin_notices', array($this, '_translation_method_notice'));
  116. }
  117. }
  118. if(defined('WPML_TM_VERSION') && isset($_GET['page']) && $_GET['page'] == WPML_TM_FOLDER. '/menu/main.php' && isset($_GET['sm']) && $_GET['sm'] == 'translators'){
  119. $iclsettings =& $sitepress_settings;
  120. $sitepress->get_icl_translator_status($iclsettings);
  121. $sitepress->save_settings($iclsettings);
  122. }
  123. // default settings
  124. if(empty($this->settings['doc_translation_method']) || !defined('WPML_TM_VERSION')){
  125. $this->settings['doc_translation_method'] = ICL_TM_TMETHOD_MANUAL;
  126. }
  127. }
  128. function initial_custom_field_translate_states() {
  129. global $wpdb;
  130. $cf_keys_limit = 1000; // jic
  131. $custom_keys = $wpdb->get_col( "
  132. SELECT meta_key
  133. FROM $wpdb->postmeta
  134. GROUP BY meta_key
  135. ORDER BY meta_key
  136. LIMIT $cf_keys_limit" );
  137. $changed = false;
  138. foreach($custom_keys as $cfield) {
  139. if(empty($this->settings['custom_fields_translation'][$cfield]) || $this->settings['custom_fields_translation'][$cfield] == 0) {
  140. // see if a plugin handles this field
  141. $override = apply_filters('icl_cf_translate_state', 'nothing', $cfield);
  142. switch($override) {
  143. case 'nothing':
  144. break;
  145. case 'ignore':
  146. $changed = true;
  147. $this->settings['custom_fields_translation'][$cfield] = 3;
  148. break;
  149. case 'translate':
  150. $changed = true;
  151. $this->settings['custom_fields_translation'][$cfield] = 2;
  152. break;
  153. case 'copy':
  154. $changed = true;
  155. $this->settings['custom_fields_translation'][$cfield] = 1;
  156. break;
  157. }
  158. }
  159. }
  160. if ($changed) {
  161. $this->save_settings();
  162. }
  163. }
  164. function _translation_method_notice(){
  165. 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'),
  166. admin_url('admin.php?page='.WPML_TM_FOLDER.'/menu/main.php&sm=mcsetup&src=notice')) . '</p></div>';
  167. }
  168. function _show_tinyMCE() {
  169. wp_print_scripts('editor');
  170. //add_filter('the_editor', array($this, 'editor_directionality'), 9999);
  171. add_filter('tiny_mce_before_init', array($this, '_mce_set_direction'), 9999);
  172. add_filter('mce_buttons', array($this, '_mce_remove_fullscreen'), 9999);
  173. if (version_compare($GLOBALS['wp_version'], '3.1.4', '<=') && function_exists('wp_tiny_mce'))
  174. try{
  175. /** @noinspection PhpDeprecationInspection */
  176. @wp_tiny_mce();
  177. } catch(Exception $e) { /*don't do anything with this */ }
  178. }
  179. function _mce_remove_fullscreen($options){
  180. foreach($options as $k=>$v) if($v == 'fullscreen') unset($options[$k]);
  181. return $options;
  182. }
  183. function _inline_js_scripts(){
  184. // remove fullscreen mode
  185. if(defined('WPML_TM_FOLDER') && isset($_GET['page']) && $_GET['page'] == WPML_TM_FOLDER . '/menu/translations-queue.php' && isset($_GET['job_id'])){
  186. ?>
  187. <script type="text/javascript">addLoadEvent(function(){jQuery('#ed_fullscreen').remove();});</script>
  188. <?php
  189. }
  190. }
  191. function _mce_set_direction($settings) {
  192. $job = $this->get_translation_job((int)$_GET['job_id'], false, true);
  193. if (!empty($job)) {
  194. $rtl_translation = in_array($job->language_code, array('ar','he','fa'));
  195. if ($rtl_translation) {
  196. $settings['directionality'] = 'rtl';
  197. } else {
  198. $settings['directionality'] = 'ltr';
  199. }
  200. }
  201. return $settings;
  202. }
  203. /*
  204. function editor_directionality($tag) {
  205. $job = $this->get_translation_job((int)$_GET['job_id'], false, true);
  206. $rtl_translation = in_array($job->language_code, array('ar','he','fa'));
  207. if ($rtl_translation) {
  208. $dir = 'dir="rtl"';
  209. } else {
  210. $dir = 'dir="ltr"';
  211. }
  212. return str_replace('<textarea', '<textarea ' . $dir, $tag);
  213. }
  214. */
  215. function process_request($action, $data){
  216. $data = stripslashes_deep($data);
  217. switch($action){
  218. case 'add_translator':
  219. if(wp_create_nonce('add_translator') == $data['add_translator_nonce']){
  220. // Initial adding
  221. if (isset($data['from_lang']) && isset($data['to_lang'])) {
  222. $data['lang_pairs'] = array();
  223. $data['lang_pairs'][$data['from_lang']] = array($data['to_lang'] => 1);
  224. }
  225. $this->add_translator($data['user_id'], $data['lang_pairs']);
  226. $_user = new WP_User($data['user_id']);
  227. 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');
  228. }
  229. break;
  230. case 'edit_translator':
  231. if(wp_create_nonce('edit_translator') == $data['edit_translator_nonce']){
  232. $this->edit_translator($data['user_id'], isset($data['lang_pairs']) ? $data['lang_pairs'] : array());
  233. $_user = new WP_User($data['user_id']);
  234. 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');
  235. }
  236. break;
  237. case 'remove_translator':
  238. if(wp_create_nonce('remove_translator') == $data['remove_translator_nonce']){
  239. $this->remove_translator($data['user_id']);
  240. $_user = new WP_User($data['user_id']);
  241. 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');
  242. }
  243. break;
  244. case 'edit':
  245. $this->selected_translator['ID'] = intval($data['user_id']);
  246. break;
  247. case 'dashboard_filter':
  248. $_SESSION['translation_dashboard_filter'] = $data['filter'];
  249. wp_redirect('admin.php?page='.WPML_TM_FOLDER . '/menu/main.php&sm=dashboard');
  250. break;
  251. case 'sort':
  252. if(isset($data['sort_by'])) $_SESSION['translation_dashboard_filter']['sort_by'] = $data['sort_by'];
  253. if(isset($data['sort_order'])) $_SESSION['translation_dashboard_filter']['sort_order'] = $data['sort_order'];
  254. break;
  255. case 'reset_filters':
  256. unset($_SESSION['translation_dashboard_filter']);
  257. break;
  258. case 'send_jobs':
  259. if(isset($data['iclnonce']) && wp_verify_nonce($data['iclnonce'], 'pro-translation-icl')){
  260. $this->send_jobs($data);
  261. }
  262. break;
  263. case 'jobs_filter':
  264. $_SESSION['translation_jobs_filter'] = $data['filter'];
  265. wp_redirect('admin.php?page='.WPML_TM_FOLDER . '/menu/main.php&sm=jobs');
  266. break;
  267. case 'ujobs_filter':
  268. $_SESSION['translation_ujobs_filter'] = $data['filter'];
  269. wp_redirect('admin.php?page='.WPML_TM_FOLDER . '/menu/translations-queue.php');
  270. break;
  271. case 'save_translation':
  272. if(!empty($data['resign'])){
  273. $this->resign_translator($data['job_id']);
  274. wp_redirect(admin_url('admin.php?page='.WPML_TM_FOLDER.'/menu/translations-queue.php&resigned='.$data['job_id']));
  275. exit;
  276. }else{
  277. $this->save_translation($data);
  278. }
  279. break;
  280. case 'save_notification_settings':
  281. $this->settings['notification'] = $data['notification'];
  282. $this->save_settings();
  283. $this->messages[] = array(
  284. 'type'=>'updated',
  285. 'text' => __('Preferences saved.', 'sitepress')
  286. );
  287. break;
  288. case 'create_job':
  289. global $current_user;
  290. if(!isset($this->current_translator->ID) && isset($current_user->ID)){
  291. $this->current_translator->ID = $current_user->ID;
  292. }
  293. $data['translator'] = $this->current_translator->ID;
  294. $job_ids = $this->send_jobs($data);
  295. wp_redirect('admin.php?page='.WPML_TM_FOLDER . '/menu/translations-queue.php&job_id=' . array_pop($job_ids));
  296. break;
  297. case 'cancel_jobs':
  298. $ret = $this->cancel_translation_request($data['icl_translation_id']);
  299. $this->messages[] = array(
  300. 'type'=>'updated',
  301. 'text' => __('Translation requests cancelled.', 'sitepress')
  302. );
  303. break;
  304. }
  305. }
  306. function ajax_calls( $call, $data ) {
  307. global $wpdb, $sitepress, $sitepress_settings;
  308. switch ( $call ) {
  309. /*
  310. case 'save_dashboard_setting':
  311. $iclsettings['dashboard'] = $sitepress_settings['dashboard'];
  312. if(isset($data['setting']) && isset($data['value'])){
  313. $iclsettings['dashboard'][$data['setting']] = $data['value'];
  314. $sitepress->save_settings($iclsettings);
  315. }
  316. break;
  317. */
  318. case 'assign_translator':
  319. $_exp = explode( '-', $data[ 'translator_id' ] );
  320. $service = isset( $_exp[ 1 ] ) ? $_exp[ 1 ] : 'local';
  321. $translator_id = $_exp[ 0 ];
  322. if ( $this->assign_translation_job( $data[ 'job_id' ], $translator_id, $service ) ) {
  323. if ( $service == 'icanlocalize' ) {
  324. $job = $this->get_translation_job( $data[ 'job_id' ] );
  325. global $ICL_Pro_Translation;
  326. $ICL_Pro_Translation->send_post( $job->original_doc_id, array( $job->language_code ), $translator_id );
  327. $lang_tr_id = false;
  328. $contract_id = false;
  329. foreach ( $sitepress_settings[ 'icl_lang_status' ] as $lp ) {
  330. if ( $lp[ 'from' ] == $job->source_language_code && $lp[ 'to' ] == $job->language_code ) {
  331. $contract_id = $lp[ 'contract_id' ];
  332. $lang_tr_id = $lp[ 'id' ];
  333. break;
  334. }
  335. }
  336. $popup_link = ICL_API_ENDPOINT . '/websites/' . $sitepress_settings[ 'site_id' ] . '/website_translation_offers/' . $lang_tr_id . '/website_translation_contracts/' . $contract_id;
  337. $popup_args = array(
  338. 'title' => __( 'Chat with translator', 'sitepress' ),
  339. 'unload_cb' => 'icl_thickbox_refresh'
  340. );
  341. $translator_edit_link = $sitepress->create_icl_popup_link( $popup_link, $popup_args );
  342. $translator_edit_link .= esc_html( ICL_Pro_Translation::get_translator_name( $translator_id ) );
  343. $translator_edit_link .= '</a> (ICanLocalize)';
  344. } else {
  345. $translator_edit_link =
  346. '<a href="' . $this->get_translator_edit_url( $data[ 'translator_id' ] ) . '">' . esc_html( $wpdb->get_var( $wpdb->prepare( "SELECT display_name FROM {$wpdb->users} WHERE ID=%d", $data[ 'translator_id' ] ) ) ) . '</a>';
  347. }
  348. echo json_encode( array( 'error' => 0, 'message' => $translator_edit_link, 'status' => $this->status2text( ICL_TM_WAITING_FOR_TRANSLATOR ), 'service' => $service ) );
  349. } else {
  350. echo json_encode( array( 'error' => 1 ) );
  351. }
  352. break;
  353. case 'icl_cf_translation':
  354. if ( !empty( $data[ 'cf' ] ) ) {
  355. foreach ( $data[ 'cf' ] as $k => $v ) {
  356. $cft[ base64_decode( $k ) ] = $v;
  357. }
  358. if ( isset( $cft ) ) {
  359. $this->settings['custom_fields_translation'] = $cft;
  360. $this->save_settings();
  361. }
  362. }
  363. echo '1|';
  364. break;
  365. case 'icl_doc_translation_method':
  366. $this->settings['doc_translation_method'] = intval($data['t_method']);
  367. $sitepress->set_setting( 'hide_how_to_translate', empty( $data[ 'how_to_translate' ] ) );
  368. if (isset($data[ 'tm_block_retranslating_terms' ])) {
  369. $sitepress->set_setting( 'tm_block_retranslating_terms', $data[ 'tm_block_retranslating_terms' ] );
  370. } else {
  371. $sitepress->set_setting( 'tm_block_retranslating_terms', '' );
  372. }
  373. $this->save_settings();
  374. echo '1|';
  375. break;
  376. case 'reset_duplication':
  377. $this->reset_duplicate_flag( $_POST[ 'post_id' ] );
  378. break;
  379. case 'set_duplication':
  380. $this->set_duplicate( $_POST[ 'post_id' ] );
  381. break;
  382. case 'make_duplicates':
  383. $mdata[ 'iclpost' ] = array( $data[ 'post_id' ] );
  384. $langs = explode( ',', $data[ 'langs' ] );
  385. foreach ( $langs as $lang ) {
  386. $mdata[ 'duplicate_to' ][ $lang ] = 1;
  387. }
  388. $this->make_duplicates( $mdata );
  389. break;
  390. }
  391. }
  392. function show_messages(){
  393. if(!empty($this->messages)){
  394. foreach($this->messages as $m){
  395. echo '<div class="'.$m['type'].' below-h2"><p>' . $m['text'] . '</p></div>';
  396. }
  397. }
  398. }
  399. /* TRANSLATORS */
  400. /* ******************************************************************************************** */
  401. function add_translator($user_id, $language_pairs){
  402. global $wpdb;
  403. $user = new WP_User($user_id);
  404. $user->add_cap('translate');
  405. $um = get_user_meta($user_id, $wpdb->prefix . 'language_pairs', true);
  406. if(!empty($um)){
  407. foreach($um as $fr=>$to){
  408. if(isset($language_pairs[$fr])){
  409. $language_pairs[$fr] = array_merge($language_pairs[$fr], $to);
  410. }
  411. }
  412. }
  413. update_user_meta($user_id, $wpdb->prefix . 'language_pairs', $language_pairs);
  414. $this->clear_cache();
  415. }
  416. function edit_translator($user_id, $language_pairs){
  417. global $wpdb;
  418. $_user = new WP_User($user_id);
  419. if(empty($language_pairs)){
  420. $this->remove_translator($user_id);
  421. wp_redirect('admin.php?page='.WPML_TM_FOLDER.'/menu/main.php&sm=translators&icl_tm_message='.
  422. urlencode(sprintf(__('%s has been removed as a translator for this site.','sitepress'),$_user->data->display_name)).'&icl_tm_message_type=updated'); exit;
  423. }
  424. else{
  425. if(!$_user->has_cap('translate')) $_user->add_cap('translate');
  426. update_user_meta($user_id, $wpdb->prefix . 'language_pairs', $language_pairs);
  427. }
  428. }
  429. function remove_translator($user_id){
  430. global $wpdb;
  431. $user = new WP_User($user_id);
  432. $user->remove_cap('translate');
  433. delete_user_meta($user_id, $wpdb->prefix . 'language_pairs');
  434. $this->clear_cache();
  435. }
  436. function is_translator( $user_id, $args = array() )
  437. {
  438. extract( $args, EXTR_OVERWRITE );
  439. global $wpdb;
  440. $user = new WP_User( $user_id );
  441. $is_translator = $user->has_cap( 'translate' );
  442. // check if user is administrator and return true if he is
  443. $user_caps = $user->caps;
  444. if ( isset( $user_caps[ 'activate_plugins' ] ) && $user_caps[ 'activate_plugins' ] == true ) {
  445. $is_translator = true;
  446. } else {
  447. if ( isset( $lang_from ) && isset( $lang_to ) ) {
  448. $um = get_user_meta( $user_id, $wpdb->prefix . 'language_pairs', true );
  449. $is_translator = $is_translator && isset( $um[ $lang_from ] ) && isset( $um[ $lang_from ][ $lang_to ] ) && $um[ $lang_from ][ $lang_to ];
  450. }
  451. if ( isset( $job_id ) ) {
  452. $translator_id = $wpdb->get_var( $wpdb->prepare( "
  453. SELECT j.translator_id
  454. FROM {$wpdb->prefix}icl_translate_job j
  455. JOIN {$wpdb->prefix}icl_translation_status s ON j.rid = s.rid
  456. WHERE job_id = %d AND s.translation_service='local'
  457. ", $job_id ) );
  458. $is_translator = $is_translator && ( ( $translator_id == $user_id ) || empty( $translator_id ) );
  459. }
  460. }
  461. return $is_translator;
  462. }
  463. function translator_exists($id, $from, $to, $type = 'local'){
  464. global $sitepress_settings;
  465. $exists = false;
  466. if($type == 'icanlocalize' && !empty($sitepress_settings['icl_lang_status'])){
  467. foreach($sitepress_settings['icl_lang_status'] as $lpair){
  468. if($lpair['from'] == $from && $lpair['to'] == $to){
  469. if(!empty($lpair['translators'])){
  470. foreach($lpair['translators'] as $t){
  471. if($t['id'] == $id){
  472. $exists = true;
  473. break(2);
  474. }
  475. }
  476. }
  477. }
  478. }
  479. }elseif($type == 'local'){
  480. $exists = $this->is_translator($id, array('lang_from'=>$from, 'lang_to'=>$to));
  481. }
  482. return $exists;
  483. }
  484. function set_default_translator($id, $from, $to, $type = 'local'){
  485. global $sitepress, $sitepress_settings;
  486. $iclsettings['default_translators'] = isset($sitepress_settings['default_translators']) ? $sitepress_settings['default_translators'] : array();
  487. $iclsettings['default_translators'][$from][$to] = array('id'=>$id, 'type'=>$type);
  488. $sitepress->save_settings($iclsettings);
  489. }
  490. function get_default_translator($from, $to){
  491. global $sitepress_settings;
  492. if(isset($sitepress_settings['default_translators'][$from][$to])){
  493. $dt = $sitepress_settings['default_translators'][$from][$to];
  494. }else{
  495. $dt = array();
  496. }
  497. return $dt;
  498. }
  499. public static function get_blog_not_translators(){
  500. global $wpdb;
  501. $cached_translators = get_option($wpdb->prefix . 'icl_non_translators_cached', array());
  502. if (!empty($cached_translators)) {
  503. return $cached_translators;
  504. }
  505. $sql = "SELECT u.ID, u.user_login, u.display_name, m.meta_value AS caps
  506. 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";
  507. $res = $wpdb->get_results($sql);
  508. $users = array();
  509. foreach($res as $row){
  510. $caps = @unserialize($row->caps);
  511. if(!isset($caps['translate'])){
  512. $users[] = $row;
  513. }
  514. }
  515. update_option($wpdb->prefix . 'icl_non_translators_cached', $users);
  516. return $users;
  517. }
  518. /**
  519. * Implementation of 'icl_translators_list' hook
  520. *
  521. * @global object $sitepress
  522. * @param array $array
  523. * @return array
  524. */
  525. public static function icanlocalize_translators_list() {
  526. global $sitepress_settings, $sitepress;
  527. $lang_status = isset($sitepress_settings['icl_lang_status']) ? $sitepress_settings['icl_lang_status'] : array();
  528. if (0 != key($lang_status)){
  529. $buf[] = $lang_status;
  530. $lang_status = $buf;
  531. }
  532. $translators = array();
  533. foreach($lang_status as $lpair){
  534. foreach((array)$lpair['translators'] as $translator){
  535. $translators[$translator['id']]['name'] = $translator['nickname'];
  536. $translators[$translator['id']]['langs'][$lpair['from']][] = $lpair['to'];
  537. $translators[$translator['id']]['type'] = 'ICanLocalize';
  538. $translators[$translator['id']]['action'] = $sitepress->create_icl_popup_link(ICL_API_ENDPOINT . '/websites/' . $sitepress_settings['site_id']
  539. . '/website_translation_offers/' . $lpair['id'] . '/website_translation_contracts/'
  540. . $translator['contract_id'], array('title' => __('Chat with translator', 'sitepress'), 'unload_cb' => 'icl_thickbox_refresh', 'ar'=>1)) . __('Chat with translator', 'sitepress') . '</a>';
  541. }
  542. }
  543. return $translators;
  544. }
  545. public static function get_blog_translators($args = array()){
  546. global $wpdb;
  547. $args_default = array('from'=>false, 'to'=>false);
  548. extract($args_default);
  549. extract($args, EXTR_OVERWRITE);
  550. // $sql = "SELECT u.ID, u.user_login, u.display_name, u.user_email, m.meta_value AS caps
  551. // 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";
  552. // $res = $wpdb->get_results($sql);
  553. $cached_translators = get_option($wpdb->prefix . 'icl_translators_cached', array());
  554. if (empty($cached_translators)) {
  555. $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";
  556. $res = $wpdb->get_results($sql);
  557. update_option($wpdb->prefix . 'icl_translators_cached', $res);
  558. } else {
  559. $res = $cached_translators;
  560. }
  561. $users = array();
  562. foreach($res as $row){
  563. $user = new WP_User($row->ID);
  564. // $caps = @unserialize($row->caps);
  565. // $row->language_pairs = (array)get_user_meta($row->ID, $wpdb->prefix.'language_pairs', true);
  566. $user->language_pairs = (array)get_user_meta($row->ID, $wpdb->prefix.'language_pairs', true);
  567. // if(!empty($from) && !empty($to) && (!isset($row->language_pairs[$from][$to]) || !$row->language_pairs[$from][$to])){
  568. // continue;
  569. // }
  570. if(!empty($from) && !empty($to) && (!isset($user->language_pairs[$from][$to]) || !$user->language_pairs[$from][$to])){
  571. continue;
  572. }
  573. // if(isset($caps['translate'])){
  574. // $users[] = $user;
  575. // }
  576. if($user->has_cap('translate')){
  577. $users[] = $user;
  578. }
  579. }
  580. return $users;
  581. }
  582. function get_selected_translator(){
  583. global $wpdb;
  584. if($this->selected_translator['ID']){
  585. $user = new WP_User($this->selected_translator['ID']);
  586. $this->selected_translator['display_name'] = $user->data->display_name;
  587. $this->selected_translator['user_login'] = $user->data->user_login;
  588. $this->selected_translator['language_pairs'] = get_user_meta($this->selected_translator['ID'], $wpdb->prefix.'language_pairs', true);
  589. }else{
  590. $this->selected_translator['ID'] = 0;
  591. }
  592. return (object)$this->selected_translator;
  593. }
  594. function get_current_translator(){
  595. return $this->current_translator;
  596. }
  597. public function get_translator_edit_url($translator_id){
  598. $url = '';
  599. if(!empty($translator_id)){
  600. $url = 'admin.php?page='. WPML_TM_FOLDER .'/menu/main.php&amp;sm=translators&icl_tm_action=edit&amp;user_id='. $translator_id;
  601. }
  602. return $url;
  603. }
  604. public function translators_dropdown( $args = array() ) {
  605. global $sitepress_settings;
  606. $args_default = array(
  607. 'from' => false,
  608. 'to' => false,
  609. 'name' => 'translator_id',
  610. 'selected' => 0,
  611. 'echo' => true,
  612. 'services' => array( 'local' ),
  613. 'show_service' => true,
  614. 'disabled' => false
  615. );
  616. extract( $args_default );
  617. extract( $args, EXTR_OVERWRITE );
  618. $translators = array();
  619. /** @var $from string|false */
  620. /** @var $to string|false */
  621. /** @var $name string|false */
  622. /** @var $selected bool */
  623. /** @var $echo bool */
  624. /** @var $services array */
  625. /** @var $show_service bool */
  626. /** @var $disabled bool */
  627. if ( in_array( 'icanlocalize', $services ) ) {
  628. if ( empty( $sitepress_settings[ 'icl_lang_status' ] ) ) {
  629. $sitepress_settings[ 'icl_lang_status' ] = array();
  630. }
  631. foreach ( (array) $sitepress_settings[ 'icl_lang_status' ] as $language_pair ) {
  632. if ( $from && $from != $language_pair[ 'from' ] ) {
  633. continue;
  634. }
  635. if ( $to && $to != $language_pair[ 'to' ] ) {
  636. continue;
  637. }
  638. if ( !empty( $language_pair[ 'translators' ] ) ) {
  639. if ( 1 < count( $language_pair[ 'translators' ] ) ) {
  640. $translators[ ] = (object) array(
  641. 'ID' => '0-icanlocalize',
  642. 'display_name' => __( 'First available', 'sitepress' ),
  643. 'service' => 'ICanLocalize'
  644. );
  645. }
  646. foreach ( $language_pair[ 'translators' ] as $tr ) {
  647. if ( !isset( $_icl_translators[ $tr[ 'id' ] ] ) ) {
  648. $translators[ ] = $_icl_translators[ $tr[ 'id' ] ] = (object) array(
  649. 'ID' => $tr[ 'id' ] . '-icanlocalize',
  650. 'display_name' => $tr[ 'nickname' ],
  651. 'service' => 'ICanLocalize'
  652. );
  653. }
  654. }
  655. }
  656. }
  657. }
  658. if ( in_array( 'local', $services ) ) {
  659. $translators[ ] = (object) array(
  660. 'ID' => 0,
  661. 'display_name' => __( 'First available', 'sitepress' ),
  662. );
  663. $translators = array_merge( $translators, self::get_blog_translators( array( 'from' => $from, 'to' => $to ) ) );
  664. }
  665. $translators = apply_filters( 'wpml_tm_translators_list', $translators );
  666. ?>
  667. <select name="<?php echo $name ?>" <?php if ($disabled): ?>disabled="disabled"<?php endif; ?>>
  668. <?php foreach ( $translators as $t ): ?>
  669. <option value="<?php echo $t->ID ?>" <?php if ($selected == $t->ID): ?>selected="selected"<?php endif; ?>><?php echo esc_html( $t->display_name );
  670. ?> <?php if ( $show_service ) {
  671. echo '(';
  672. echo isset( $t->service ) ? $t->service : _e( 'Local', 'sitepress' );
  673. echo ')';
  674. } ?></option>
  675. <?php endforeach; ?>
  676. </select>
  677. <?php
  678. }
  679. public function get_number_of_docs_sent($service = 'icanlocalize'){
  680. global $wpdb;
  681. $n = $wpdb->get_var($wpdb->prepare("
  682. SELECT COUNT(rid) FROM {$wpdb->prefix}icl_translation_status WHERE translation_service=%s
  683. ", $service));
  684. return $n;
  685. }
  686. public function get_number_of_docs_pending($service = 'icanlocalize'){
  687. global $wpdb;
  688. $n = $wpdb->get_var($wpdb->prepare("
  689. SELECT COUNT(rid) FROM {$wpdb->prefix}icl_translation_status WHERE translation_service=%s AND status < " . ICL_TM_COMPLETE . "
  690. ", $service));
  691. return $n;
  692. }
  693. /* HOOKS */
  694. /* ******************************************************************************************** */
  695. function save_post_actions( $post_id, $post, $force_set_status = false )
  696. {
  697. global $wpdb, $sitepress, $current_user;
  698. // skip revisions
  699. if ( $post->post_type == 'revision' ) {
  700. return;
  701. }
  702. // skip auto-drafts
  703. if ( $post->post_status == 'auto-draft' ) {
  704. return;
  705. }
  706. // skip autosave
  707. if ( isset( $_POST[ 'autosave' ] ) ) {
  708. return;
  709. }
  710. if ( isset( $_POST[ 'icl_trid' ] ) && is_numeric($_POST['icl_trid']) ) {
  711. $trid = $_POST['icl_trid'];
  712. } else {
  713. $trid = $sitepress->get_element_trid( $post_id, 'post_' . $post->post_type );
  714. }
  715. // set trid and lang code if front-end translation creating
  716. $trid = apply_filters( 'wpml_tm_save_post_trid_value', isset( $trid ) ? $trid : '', $post_id );
  717. $lang = apply_filters( 'wpml_tm_save_post_lang_value', isset( $lang ) ? $lang : '', $post_id );
  718. // is this the original document?
  719. $is_original = false;
  720. if ( !empty( $trid ) ) {
  721. $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, $trid ) );
  722. }
  723. // when a manual translation is added/edited make sure to update translation tables
  724. if ( !empty( $trid ) && !$is_original ) {
  725. if ( ( !isset( $lang ) || !$lang ) && isset( $_POST[ 'icl_post_language' ] ) && !empty( $_POST[ 'icl_post_language' ] ) ) {
  726. $lang = $_POST[ 'icl_post_language' ];
  727. }
  728. $res = $wpdb->get_row( $wpdb->prepare( "
  729. SELECT element_id, language_code FROM {$wpdb->prefix}icl_translations WHERE trid=%d AND source_language_code IS NULL
  730. ", $trid ) );
  731. if ( $res ) {
  732. $original_post_id = $res->element_id;
  733. $from_lang = $res->language_code;
  734. $original_post = get_post( $original_post_id );
  735. $md5 = $this->post_md5( $original_post );
  736. $translation_id_prepared = $wpdb->prepare( "SELECT translation_id FROM {$wpdb->prefix}icl_translations WHERE trid=%d AND language_code=%s", $trid, $lang );
  737. $translation_id = $wpdb->get_var( $translation_id_prepared );
  738. get_currentuserinfo();
  739. $user_id = $current_user->ID;
  740. if ( !$this->is_translator( $user_id, array( 'lang_from' => $from_lang, 'lang_to' => $lang ) ) ) {
  741. $this->add_translator( $user_id, array( $from_lang => array( $lang => 1 ) ) );
  742. }
  743. if ( $translation_id ) {
  744. $translation_package = $this->create_translation_package( $original_post_id );
  745. list( $rid, $update ) = $this->update_translation_status( array(
  746. 'translation_id' => $translation_id,
  747. 'status' => isset( $force_set_status ) && $force_set_status > 0 ? $force_set_status : ICL_TM_COMPLETE,
  748. 'translator_id' => $user_id,
  749. 'needs_update' => 0,
  750. 'md5' => $md5,
  751. 'translation_service' => 'local',
  752. 'translation_package' => serialize( $translation_package )
  753. ) );
  754. if ( !$update ) {
  755. $job_id = $this->add_translation_job( $rid, $user_id, $translation_package );
  756. } else {
  757. $job_id = $wpdb->get_var( $wpdb->prepare( "SELECT MAX(job_id) FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d GROUP BY rid", $rid ) );
  758. }
  759. // saving the translation
  760. $this->save_job_fields_from_post( $job_id, $post );
  761. }
  762. }
  763. }
  764. // if this is an original post - compute md5 hash and mark for update if neded
  765. if ( !empty( $trid ) && empty( $_POST[ 'icl_minor_edit' ] ) ) {
  766. $is_original = false;
  767. $translations = $sitepress->get_element_translations( $trid, 'post_' . $post->post_type );
  768. foreach ( $translations as $lang => $translation ) {
  769. if ( $translation->original == 1 && $translation->element_id == $post_id ) {
  770. $is_original = true;
  771. break;
  772. }
  773. }
  774. if ( $is_original ) {
  775. $md5 = $this->post_md5( $post_id );
  776. foreach ( $translations as $lang => $translation ) {
  777. if ( !$translation->original ) {
  778. $emd5_prepared = $wpdb->prepare( "SELECT md5 FROM {$wpdb->prefix}icl_translation_status WHERE translation_id = %d", $translation->translation_id );
  779. $emd5 = $wpdb->get_var( $emd5_prepared );
  780. if ( $md5 != $emd5 ) {
  781. $translation_package = $this->create_translation_package( $post_id );
  782. list( $rid, $update ) = $this->update_translation_status( array(
  783. 'translation_id' => $translation->translation_id,
  784. 'needs_update' => 1,
  785. 'md5' => $md5,
  786. 'translation_package' => serialize( $translation_package )
  787. ) );
  788. // update
  789. $translator_id_prepared = $wpdb->prepare( "SELECT translator_id FROM {$wpdb->prefix}icl_translation_status WHERE translation_id = %d", $translation->translation_id );
  790. $translator_id = $wpdb->get_var( $translator_id_prepared );
  791. $job_id = $this->add_translation_job( $rid, $translator_id, $translation_package );
  792. // updating a post that's being translated - update fields in icl_translate
  793. if ( false === $job_id ) {
  794. $job_id_prepared = $wpdb->prepare( "SELECT MAX(job_id) FROM {$wpdb->prefix}icl_translate_job WHERE rid = %d", $rid );
  795. $job_id = $wpdb->get_var( $job_id_prepared );
  796. if ( $job_id ) {
  797. $job = $this->get_translation_job( $job_id );
  798. if ( $job ) {
  799. foreach ( $job->elements as $element ) {
  800. unset( $field_data );
  801. $_taxs_ids = false;
  802. switch ( $element->field_type ) {
  803. case 'title':
  804. $field_data = $this->encode_field_data( $post->post_title, $element->field_format );
  805. break;
  806. case 'body':
  807. $field_data = $this->encode_field_data( $post->post_content, $element->field_format );
  808. break;
  809. case 'excerpt':
  810. $field_data = $this->encode_field_data( $post->post_excerpt, $element->field_format );
  811. break;
  812. case 'tags':
  813. $terms = (array)get_the_terms( $post->ID, 'post_tag' );
  814. $_taxs = array();
  815. foreach ( $terms as $term ) {
  816. $_taxs[ ] = $term->name;
  817. $_taxs_ids[ ] = $term->term_taxonomy_id;
  818. }
  819. $field_data = $this->encode_field_data( $_taxs, $element->field_format );
  820. break;
  821. case 'categories':
  822. $terms = get_the_terms( $post->ID, 'category' );
  823. $_taxs = array();
  824. foreach ( $terms as $term ) {
  825. $_taxs[ ] = $term->name;
  826. $_taxs_ids[ ] = $term->term_taxonomy_id;
  827. }
  828. $field_data = $this->encode_field_data( $_taxs, $element->field_format );
  829. break;
  830. default:
  831. if ( false !== strpos( $element->field_type, 'field-' ) && !empty( $this->settings[ 'custom_fields_translation' ] ) ) {
  832. $cf_name = preg_replace( '#^field-#', '', $element->field_type );
  833. if ( isset( $this->settings[ 'custom_fields_translation' ][ $cf_name ] ) ) {
  834. $field_data = get_post_meta( $post->ID, $cf_name, 1 );
  835. $field_data = $this->encode_field_data( $field_data, $element->field_format );
  836. }
  837. } else {
  838. // taxonomies
  839. if ( taxonomy_exists( $element->field_type ) ) {
  840. $terms = get_the_terms( $post->ID, $element->field_type );
  841. $_taxs = array();
  842. foreach ( $terms as $term ) {
  843. $_taxs[ ] = $term->name;
  844. $_taxs_ids[ ] = $term->term_taxonomy_id;
  845. }
  846. $field_data = $this->encode_field_data( $_taxs, $element->field_format );
  847. }
  848. }
  849. }
  850. if ( isset( $field_data ) && $field_data != $element->field_data ) {
  851. $wpdb->update( $wpdb->prefix . 'icl_translate', array( 'field_data' => $field_data ), array( 'tid' => $element->tid ) );
  852. if ( $_taxs_ids && $element->field_type == 'categories' ) {
  853. $wpdb->update( $wpdb->prefix . 'icl_translate', array( 'field_data' => join( ',', $_taxs_ids ) ), array( 'job_id' => $job_id, 'field_type' => 'category_ids' ) );
  854. }
  855. }
  856. }
  857. }
  858. }
  859. }
  860. }
  861. }
  862. }
  863. }
  864. }
  865. // sync copies/duplicates
  866. $duplicates = $this->get_duplicates( $post_id );
  867. static $duplicated_post_ids;
  868. if ( !isset( $duplicated_post_ids ) ) {
  869. $duplicated_post_ids = array();
  870. }
  871. foreach ( $duplicates as $lang => $_pid ) {
  872. // Avoid infinite recursions
  873. if ( !in_array( $post_id . '|' . $lang, $duplicated_post_ids ) ) {
  874. $duplicated_post_ids[ ] = $post_id . '|' . $lang;
  875. $this->make_duplicate( $post_id, $lang );
  876. }
  877. }
  878. }
  879. function make_duplicates( $data )
  880. {
  881. foreach ( $data[ 'iclpost' ] as $master_post_id ) {
  882. foreach ( $data[ 'duplicate_to' ] as $lang => $one ) {
  883. $this->make_duplicate( $master_post_id, $lang );
  884. }
  885. }
  886. }
  887. function make_duplicate( $master_post_id, $lang )
  888. {
  889. static $duplicated_post_ids;
  890. if(!isset($duplicated_post_ids)) {
  891. $duplicated_post_ids = array();
  892. }
  893. //It is already done? (avoid infinite recursions)
  894. if(in_array($master_post_id . '|' . $lang, $duplicated_post_ids)) {
  895. return true;
  896. }
  897. $duplicated_post_ids[] = $master_post_id . '|' . $lang;
  898. global $sitepress, $sitepress_settings, $wpdb;
  899. do_action( 'icl_before_make_duplicate', $master_post_id, $lang );
  900. $master_post = get_post( $master_post_id );
  901. $is_duplicated = false;
  902. $trid = $sitepress->get_element_trid( $master_post_id, 'post_' . $master_post->post_type );
  903. if ( $trid ) {
  904. $translations = $sitepress->get_element_translations( $trid, 'post_' . $master_post->post_type );
  905. if ( isset( $translations[ $lang ] ) ) {
  906. $post_array[ 'ID' ] = $translations[ $lang ]->element_id;
  907. $is_duplicated = get_post_meta( $translations[ $lang ]->element_id, '_icl_lang_duplicate_of', true );
  908. }
  909. }
  910. // covers the case when deleting in bulk from all languages
  911. // setting post_status to trash before wp_trash_post runs issues an WP error
  912. $posts_to_delete_or_restore_in_bulk = array();
  913. if ( isset( $_GET[ 'action' ] ) && ( $_GET[ 'action' ] == 'trash' || $_GET[ 'action' ] == 'untrash' ) && isset( $_GET[ 'lang' ] ) && $_GET[ 'lang' ] == 'all' ) {
  914. static $posts_to_delete_or_restore_in_bulk;
  915. if ( is_null( $posts_to_delete_or_restore_in_bulk ) ) {
  916. $posts_to_delete_or_restore_in_bulk = isset( $_GET[ 'post' ] ) && is_array( $_GET[ 'post' ] ) ? $_GET[ 'post' ] : array();
  917. }
  918. }
  919. $post_array[ 'post_author' ] = $master_post->post_author;
  920. $post_array[ 'post_date' ] = $master_post->post_date;
  921. $post_array[ 'post_date_gmt' ] = $master_post->post_date_gmt;
  922. $post_array[ 'post_content' ] = addslashes_gpc(apply_filters( 'icl_duplicate_generic_string', $master_post->post_content, $lang, array( 'context' => 'post', 'attribute' => 'content', 'key' => $master_post->ID ) ));
  923. $post_array[ 'post_title' ] = addslashes_gpc(apply_filters( 'icl_duplicate_generic_string', $master_post->post_title, $lang, array( 'context' => 'post', 'attribute' => 'title', 'key' => $master_post->ID ) ));
  924. $post_array[ 'post_excerpt' ] = addslashes_gpc(apply_filters( 'icl_duplicate_generic_string', $master_post->post_excerpt, $lang, array( 'context' => 'post', 'attribute' => 'excerpt', 'key' => $master_post->ID ) ));
  925. if ( isset( $sitepress_settings[ 'sync_post_status' ] ) && $sitepress_settings[ 'sync_post_status' ] ) {
  926. $sync_post_status = true;
  927. } else {
  928. $sync_post_status = ( !isset( $post_array[ 'ID' ] ) || ( $sitepress_settings[ 'sync_delete' ] && $master_post->post_status == 'trash' ) || $is_duplicated );
  929. $sync_post_status &= ( !$posts_to_delete_or_restore_in_bulk || ( !isset( $post_array[ 'ID' ] ) || !in_array( $post_array[ 'ID' ], $posts_to_delete_or_restore_in_bulk ) ) );
  930. }
  931. if ( $sync_post_status ) {
  932. $post_array[ 'post_status' ] = $master_post->post_status;
  933. }
  934. $post_array[ 'comment_status' ] = $master_post->comment_status;
  935. $post_array[ 'ping_status' ] = $master_post->ping_status;
  936. $post_array[ 'post_name' ] = $master_post->post_name;
  937. if ( $master_post->post_parent ) {
  938. $parent = icl_object_id( $master_post->post_parent, $master_post->post_type, false, $lang );
  939. $post_array[ 'post_parent' ] = $parent;
  940. }
  941. $post_array[ 'menu_order' ] = $master_post->menu_order;
  942. $post_array[ 'post_type' ] = $master_post->post_type;
  943. $post_array[ 'post_mime_type' ] = $master_post->post_mime_type;
  944. $trid = $sitepress->get_element_trid( $master_post->ID, 'post_' . $master_post->post_type );
  945. $_POST[ 'icl_trid' ] = $trid;
  946. $_POST[ 'icl_post_language' ] = $lang;
  947. $_POST[ 'skip_sitepress_actions' ] = true;
  948. $_POST[ 'post_type' ] = $master_post->post_type;
  949. if ( isset( $post_array[ 'ID' ] ) ) {
  950. $id = wp_update_post( $post_array );
  951. } else {
  952. $id = $this->icl_insert_post( $post_array, $lang );
  953. }
  954. require_once ICL_PLUGIN_PATH . '/inc/cache.php';
  955. icl_cache_clear( $post_array[ 'post_type' ] . 's_per_language' );
  956. global $ICL_Pro_Translation;
  957. $ICL_Pro_Translation->_content_fix_links_to_translated_content( $id, $lang );
  958. if ( !is_wp_error( $id ) ) {
  959. $sitepress->set_element_language_details( $id, 'post_' . $master_post->post_type, $trid, $lang );
  960. $this->save_post_actions( $id, get_post( $id ), ICL_TM_DUPLICATE );
  961. $this->duplicate_fix_children( $master_post_id, $lang );
  962. // dup comments
  963. if ( $sitepress->get_option( 'sync_comments_on_duplicates' ) ) {
  964. $this->duplicate_comments( $id, $master_post_id );
  965. }
  966. // make sure post name is copied
  967. $wpdb->update( $wpdb->posts, array( 'post_name' => $master_post->post_name ), array( 'ID' => $id ) );
  968. update_post_meta( $id, '_icl_lang_duplicate_of', $master_post->ID );
  969. if ( $sitepress->get_option( 'sync_post_taxonomies' ) ) {
  970. $this->duplicate_taxonomies( $master_post_id, $lang );
  971. }
  972. $this->duplicate_custom_fields( $master_post_id, $lang );
  973. $ret = $id;
  974. do_action( 'icl_make_duplicate', $master_post_id, $lang, $post_array, $id );
  975. } else {
  976. $ret = false;
  977. }
  978. return $ret;
  979. }
  980. function duplicate_taxonomies( $master_post_id, $lang )
  981. {
  982. global $wpdb, $sitepress;
  983. $post_type = get_post_field( 'post_type', $master_post_id );
  984. $taxonomies = get_object_taxonomies( $post_type );
  985. $trid = $sitepress->get_element_trid( $master_post_id, 'post_' . $post_type );
  986. $duplicate_post_id = false;
  987. if ( $trid ) {
  988. $translations = $sitepress->get_element_translations( $trid, 'post_' . $post_type, false, false, true );
  989. if ( isset( $translations[ $lang ] ) ) {
  990. $duplicate_post_id = $translations[ $lang ]->element_id;
  991. /* If we have an existing post, we first of all remove all terms currently attached to it.
  992. * The main reason behind is the removal of the potentially present default category on the post.
  993. */
  994. wp_delete_object_term_relationships( $duplicate_post_id, $taxonomies );
  995. } else {
  996. return false; // translation not found!
  997. }
  998. }
  999. foreach ( $taxonomies as $taxonomy ) {
  1000. if ( $sitepress->is_translated_taxonomy ( $taxonomy ) ) {
  1001. WPML_Terms_Translations::sync_post_and_taxonomy_terms_language( $master_post_id, $taxonomy, true );
  1002. }
  1003. }
  1004. return true;
  1005. }
  1006. function duplicate_custom_fields( $master_post_id, $lang )
  1007. {
  1008. global $wpdb, $sitepress;
  1009. $duplicate_post_id = false;
  1010. $post_type = get_post_field( 'post_type', $master_post_id );
  1011. $trid = $sitepress->get_element_trid( $master_post_id, 'post_' . $post_type );
  1012. if ( $trid ) {
  1013. $translations = $sitepress->get_element_translations( $trid, 'post_' . $post_type );
  1014. if ( isset( $translations[ $lang ] ) ) {
  1015. $duplicate_post_id = $translations[ $lang ]->element_id;
  1016. } else {
  1017. return false; // translation not found!
  1018. }
  1019. }
  1020. $default_exceptions = array(
  1021. '_wp_old_slug',
  1022. '_edit_last',
  1023. '_edit_lock',
  1024. '_icl_translator_note',
  1025. '_icl_lang_duplicate_of',
  1026. '_wpml_media_duplicate',
  1027. '_wpml_media_featured'
  1028. );
  1029. $exceptions = $default_exceptions;
  1030. //Todo: make sure the following filter won't remove the default exceptions
  1031. $exceptions = apply_filters('wpml_duplicate_custom_fields_exceptions', $exceptions);
  1032. // low level copy
  1033. $custom_fields_master = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM {$wpdb->postmeta} WHERE post_id=%d group by meta_key", $master_post_id ) );
  1034. $custom_fields_duplicate = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM {$wpdb->postmeta} WHERE post_id=%d group by meta_key", $duplicate_post_id ) );
  1035. $custom_fields_master = array_diff( $custom_fields_master, $exceptions );
  1036. $custom_fields_duplicate = array_diff( $custom_fields_duplicate, $exceptions );
  1037. $remove = array_diff( $custom_fields_duplicate, $custom_fields_master );
  1038. foreach ( $remove as $key ) {
  1039. delete_post_meta( $duplicate_post_id, $key );
  1040. }
  1041. foreach ( $custom_fields_master as $key ) {
  1042. $master_custom_field_values_array = get_post_meta( $master_post_id, $key );
  1043. $master_custom_field_values_single = get_post_meta( $master_post_id, $key, true );
  1044. $is_repeated = false;
  1045. if($master_custom_field_values_array != $master_custom_field_values_single) {
  1046. //Repeated fields
  1047. $master_custom_field_values = $master_custom_field_values_array;
  1048. $is_repeated = true;
  1049. } else {
  1050. //Field stored as serialized array
  1051. $master_custom_field_values[] = $master_custom_field_values_single;
  1052. }
  1053. if($is_repeated) {
  1054. $duplicate_custom_field_values = get_post_meta( $duplicate_post_id, $key );
  1055. } else {
  1056. $duplicate_custom_field_values[] = get_post_meta( $duplicate_post_id, $key, true );
  1057. }
  1058. if ( !$duplicate_custom_field_values || $master_custom_field_values != $duplicate_custom_field_values ) {
  1059. if($is_repeated) {
  1060. //Delete the old one
  1061. delete_post_meta($duplicate_post_id, $key);
  1062. //And add new ones from the original
  1063. foreach($master_custom_field_values as $master_custom_field_value) {
  1064. add_post_meta( $duplicate_post_id, $key, addslashes_gpc(apply_filters( 'icl_duplicate_generic_string', $master_custom_field_value, $lang, array( 'context' => 'custom_field', 'attribute' => 'value', 'key' => $key ) ) ) );
  1065. }
  1066. } else {
  1067. update_post_meta( $duplicate_post_id, $key, addslashes_gpc(apply_filters( 'icl_duplicate_generic_string', $master_custom_field_value, $lang, array( 'context' => 'custom_field', 'attribute' => 'value', 'key' => $key ) ) ) );
  1068. }
  1069. }
  1070. }
  1071. return true;
  1072. }
  1073. function duplicate_fix_children( $master_post_id, $lang )
  1074. {
  1075. global $wpdb;
  1076. $post_type = $wpdb->get_var( $wpdb->prepare( "SELECT post_type FROM {$wpdb->posts} WHERE ID=%d", $master_post_id ) );
  1077. $master_children = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_parent=%d AND post_type != 'revision'", $master_post_id ) );
  1078. $dup_parent = icl_object_id( $master_post_id, $post_type, false, $lang );
  1079. if ( $master_children ) {
  1080. foreach ( $master_children as $master_child ) {
  1081. $dup_child = icl_object_id( $master_child, $post_type, false, $lang );
  1082. if ( $dup_child ) {
  1083. $wpdb->update( $wpdb->posts, array( 'post_parent' => $dup_parent ), array( 'ID' => $dup_child ) );
  1084. }
  1085. $this->duplicate_fix_children( $master_child, $lang );
  1086. }
  1087. }
  1088. }
  1089. function make_duplicates_all( $master_post_id )
  1090. {
  1091. global $sitepress;
  1092. $master_post = get_post( $master_post_id );
  1093. if($master_post->post_status == 'auto-draft' || $master_post->post_type == 'revision') {
  1094. return;
  1095. }
  1096. $language_details_original = $sitepress->get_element_language_details( $master_post_id, 'post_' . $master_post->post_type );
  1097. if(!$language_details_original) return;
  1098. $data[ 'iclpost' ] = array( $master_post_id );
  1099. foreach ( $sitepress->get_active_languages() as $lang => $details ) {
  1100. if ( $lang != $language_details_original->language_code ) {
  1101. $data[ 'duplicate_to' ][ $lang ] = 1;
  1102. }
  1103. }
  1104. $this->make_duplicates( $data );
  1105. }
  1106. function reset_duplicate_flag( $post_id )
  1107. {
  1108. global $sitepress;
  1109. $post = get_post( $post_id );
  1110. $trid = $sitepress->get_element_trid( $post_id, 'post_' . $post->post_type );
  1111. $translations = $sitepress->get_element_translations( $trid, 'post_' . $post->post_type );
  1112. foreach ( $translations as $tr ) {
  1113. if ( $tr->element_id == $post_id ) {
  1114. $this->update_translation_status( array(
  1115. 'translation_id' => $tr->translation_id,
  1116. 'status' => ICL_TM_COMPLETE
  1117. ) );
  1118. }
  1119. }
  1120. delete_post_meta( $post_id, '_icl_lang_duplicate_of' );
  1121. }
  1122. function set_duplicate( $post_id )
  1123. {
  1124. global $sitepress;
  1125. // find original (source) and copy
  1126. $post = get_post( $post_id );
  1127. $trid = $sitepress->get_element_trid( $post_id, 'post_' . $post->post_type );
  1128. $translations = $sitepress->get_element_translations( $trid, 'post_' . $post->post_type );
  1129. foreach ( $translations as $lang => $tr ) {
  1130. if ( $tr->original ) {
  1131. $master_post_id = $tr->element_id;
  1132. } elseif ( $tr->element_id == $post_id ) {
  1133. $this_language = $lang;
  1134. }
  1135. }
  1136. $this->make_duplicate( $master_post_id, $this_language );
  1137. }
  1138. function get_duplicates( $master_post_id )
  1139. {
  1140. global $wpdb, $sitepress;
  1141. $duplicates = array();
  1142. $res = $wpdb->get_col( $wpdb->prepare( "SELECT post_id FROM {$wpdb->postmeta}
  1143. WHERE meta_key='_icl_lang_duplicate_of' AND meta_value=%d", $master_post_id ) );
  1144. foreach ( $res as $post_id ) {
  1145. $post = get_post( $post_id );
  1146. $language_details = $sitepress->get_element_language_details( $post_id, 'post_' . $post->post_type );
  1147. $duplicates[ $language_details->language_code ] = $post_id;
  1148. }
  1149. return $duplicates;
  1150. }
  1151. function duplicate_comments( $post_id, $master_post_id ) {
  1152. global $wpdb, $sitepress;
  1153. // delete existing comments
  1154. $current_comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_ID FROM {$wpdb->comments} WHERE comment_post_ID = %d", $post_id ) );
  1155. foreach ( $current_comments as $current_comment ) {
  1156. if ( isset( $current_comment->comment_ID ) && is_numeric( $current_comment->comment_ID ) ) {
  1157. wp_delete_comment( $current_comment->comment_ID );
  1158. }
  1159. }
  1160. $original_comments = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_post_id = %d", $master_post_id ), ARRAY_A );
  1161. $post_type = $wpdb->get_var( $wpdb->prepare( "SELECT post_type FROM {$wpdb->posts} WHERE ID=%d", $post_id ) );
  1162. $language = $wpdb->get_var( $wpdb->prepare( "SELECT language_code FROM {$wpdb->prefix}icl_translations WHERE element_id=%d AND element_type=%s", $post_id, 'post_' . $post_type ) );
  1163. $wpdb->update( $wpdb->posts, array( 'comment_count' => count( $original_comments ) ), array( 'ID' => $post_id ) );
  1164. foreach ( $original_comments as $comment ) {
  1165. $original_comment_id = $comment[ 'comment_ID' ];
  1166. unset( $comment[ 'comment_ID' ] );
  1167. $comment[ 'comment_post_ID' ] = $post_id;
  1168. $wpdb->insert( $wpdb->comments, $comment );
  1169. $comment_id = $wpdb->insert_id;
  1170. update_comment_meta( $comment_id, '_icl_duplicate_of', $original_comment_id );
  1171. // comment meta
  1172. $meta = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value FROM {$wpdb->commentmeta} WHERE comment_id=%d", $original_comment_id ) );
  1173. foreach ( $meta as $meta_data ) {
  1174. if ( is_object( $meta_data ) && isset( $meta_data->meta_key ) && isset( $meta_data->meta_value ) ) {
  1175. $wpdb->insert( $wpdb->commentmeta, array(
  1176. 'comment_id' => $comment_id,
  1177. 'meta_key' => $meta_data->meta_key,
  1178. 'meta_value' => $meta_data->meta_value
  1179. ) );
  1180. }
  1181. }
  1182. $original_comment_tr = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}icl_translations WHERE element_id=%d AND element_type=%s", $original_comment_id, 'comment' ) );
  1183. if ( $original_comment_tr && isset( $original_comment_tr->trid ) ) {
  1184. $comment_translation = array(
  1185. 'element_type' => 'comment',
  1186. 'element_id' => $comment_id,
  1187. 'trid' => $original_comment_tr->trid,
  1188. 'language_code' => $language,
  1189. /*'source_language_code' => $original_comment_tr->language_code */
  1190. );
  1191. $comments_map[ $original_comment_id ] = array( 'trid' => $original_comment_tr->trid, 'comment' => $comment_id );
  1192. $existing_translation_tr = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}icl_translations WHERE trid=%d AND element_type=%s AND language_code=%s", $original_comment_tr->trid, 'comment', $language ) );
  1193. if ( $existing_translation_tr ) {
  1194. $wpdb->update( $wpdb->prefix . 'icl_translations', $comment_translation, array( 'trid' => $comment_id, 'element_type' => 'comment', 'language_code' => $language ) );
  1195. } else {
  1196. $wpdb->insert( $wpdb->prefix . 'icl_translations', $comment_translation );
  1197. }
  1198. }
  1199. }
  1200. // sync parents
  1201. foreach ( $original_comments as $comment ) {
  1202. if ( $comment[ 'comment_parent' ] ) {
  1203. $tr_comment_id = $comments_map[ $comment[ 'comment_ID' ] ][ 'comment' ];
  1204. $tr_parent = icl_object_id( $comment[ 'comment_parent' ], 'comment', false, $language );
  1205. if ( $tr_parent ) {
  1206. $wpdb->update( $wpdb->comments, array( 'comment_parent' => $tr_parent ), array( 'comment_ID' => $tr_comment_id ) );
  1207. }
  1208. }
  1209. }
  1210. }
  1211. function duplication_delete_comment( $comment_id )
  1212. {
  1213. global $wpdb;
  1214. static $_avoid_8_loop;
  1215. if ( isset( $_avoid_8_loop ) ) {
  1216. return;
  1217. }
  1218. $_avoid_8_loop = true;
  1219. $original_comment = get_comment_meta( $comment_id, '_icl_duplicate_of', true );
  1220. if ( $original_comment ) {
  1221. $duplicates = $wpdb->get_col( $wpdb->prepare( "SELECT comment_id FROM {$wpdb->commentmeta} WHERE meta_key='_icl_duplicate_of' AND meta_value=%d", $original_comment ) );
  1222. $duplicates = array( $original_comment ) + array_diff( $duplicates, array( $comment_id ) );
  1223. foreach ( $duplicates as $dup ) {
  1224. wp_delete_comment( $dup );
  1225. }
  1226. } else {
  1227. $duplicates = $wpdb->get_col( $wpdb->prepare( "SELECT comment_id FROM {$wpdb->commentmeta} WHERE meta_key='_icl_duplicate_of' AND meta_value=%d", $comment_id ) );
  1228. if ( $duplicates ) {
  1229. foreach ( $duplicates as $dup ) {
  1230. wp_delete_comment( $dup );
  1231. }
  1232. }
  1233. }
  1234. unset( $_avoid_8_loop );
  1235. }
  1236. function duplication_edit_comment( $comment_id )
  1237. {
  1238. global $wpdb;
  1239. $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID=%d", $comment_id ), ARRAY_A );
  1240. unset( $comment[ 'comment_ID' ], $comment[ 'comment_post_ID' ] );
  1241. $comment_meta = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value FROM {$wpdb->commentmeta} WHERE comment_id=%d AND meta_key <> '_icl_duplicate_of'", $comment_id ) );
  1242. $original_comment = get_comment_meta( $comment_id, '_icl_duplicate_of', true );
  1243. if ( $original_comment ) {
  1244. $duplicates = $wpdb->get_col( $wpdb->prepare( "SELECT comment_id FROM {$wpdb->commentmeta} WHERE meta_key='_icl_duplicate_of' AND meta_value=%d", $original_comment ) );
  1245. $duplicates = array( $original_comment ) + array_diff( $duplicates, array( $comment_id ) );
  1246. } else {
  1247. $duplicates = $wpdb->get_col( $wpdb->prepare( "SELECT comment_id FROM {$wpdb->commentmeta} WHERE meta_key='_icl_duplicate_of' AND meta_value=%d", $comment_id ) );
  1248. }
  1249. if ( !empty( $duplicates ) ) {
  1250. foreach ( $duplicates as $dup ) {
  1251. $wpdb->update( $wpdb->comments, $comment, array( 'comment_ID' => $dup ) );
  1252. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->commentmeta} WHERE comment_id=%d AND meta_key <> '_icl_duplicate_of'", $dup ) );
  1253. if ( $comment_meta ) {
  1254. foreach ( $comment_meta as $key => $value ) {
  1255. update_comment_meta( $dup, $key, $value );
  1256. }
  1257. }
  1258. }
  1259. }
  1260. }
  1261. function duplication_status_comment( $comment_id, $comment_status )
  1262. {
  1263. global $wpdb;
  1264. static $_avoid_8_loop;
  1265. if ( isset( $_avoid_8_loop ) ) {
  1266. return;
  1267. }
  1268. $_avoid_8_loop = true;
  1269. $original_comment = get_comment_meta( $comment_id, '_icl_duplicate_of', true );
  1270. if ( $original_comment ) {
  1271. $duplicates = $wpdb->get_col( $wpdb->prepare( "SELECT comment_id FROM {$wpdb->commentmeta} WHERE meta_key='_icl_duplicate_of' AND meta_value=%d", $original_comment ) );
  1272. $duplicates = array( $original_comment ) + array_diff( $duplicates, array( $comment_id ) );
  1273. } else {
  1274. $duplicates = $wpdb->get_col( $wpdb->prepare( "SELECT comment_id FROM {$wpdb->commentmeta} WHERE meta_key='_icl_duplicate_of' AND meta_value=%d", $comment_id ) );
  1275. }
  1276. if ( !empty( $duplicates ) ) {
  1277. foreach ( $duplicates as $duplicate ) {
  1278. wp_set_comment_status( $duplicate, $comment_status );
  1279. }
  1280. }
  1281. unset( $_avoid_8_loop );
  1282. }
  1283. function duplication_insert_comment( $comment_id )
  1284. {
  1285. global $wpdb, $sitepress;
  1286. $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID=%d", $comment_id ), ARRAY_A );
  1287. // loop duplicate posts, add new comment
  1288. $post_id = $comment[ 'comment_post_ID' ];
  1289. // if this is a duplicate post
  1290. $duplicate_of = get_post_meta( $post_id, '_icl_lang_duplicate_of', true );
  1291. if ( $duplicate_of ) {
  1292. $post_duplicates = $this->get_duplicates( $duplicate_of );
  1293. $_lang = $wpdb->get_var( $wpdb->prepare( "SELECT language_code FROM {$wpdb->prefix}icl_translations WHERE element_type='comment' AND element_id=%d", $comment_id ) );
  1294. unset( $post_duplicates[ $_lang ] );
  1295. $_post = get_post( $duplicate_of );
  1296. $_orig_lang = $sitepress->get_language_for_element( $duplicate_of, 'post_' . $_post->post_type );
  1297. $post_duplicates[ $_orig_lang ] = $duplicate_of;
  1298. } else {
  1299. $post_duplicates = $this->get_duplicates( $post_id );
  1300. }
  1301. unset( $comment[ 'comment_ID' ], $comment[ 'comment_post_ID' ] );
  1302. foreach ( $post_duplicates as $lang => $dup_id ) {
  1303. $comment[ 'comment_post_ID' ] = $dup_id;
  1304. if ( $comment[ 'comment_parent' ] ) {
  1305. $comment[ 'comment_parent' ] = icl_object_id( $comment[ 'comment_parent' ], 'comment', false, $lang );
  1306. }
  1307. $wpdb->insert( $wpdb->comments, $comment );
  1308. $dup_comment_id = $wpdb->insert_id;
  1309. update_comment_meta( $dup_comment_id, '_icl_duplicate_of', $comment_id );
  1310. // comment meta
  1311. $meta = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value FROM {$wpdb->commentmeta} WHERE comment_id=%d", $comment_id ) );
  1312. foreach ( $meta as $key => $val ) {
  1313. $wpdb->insert( $wpdb->commentmeta, array(
  1314. 'comment_id' => $dup_comment_id,
  1315. 'meta_key' => $key,
  1316. 'meta_value' => $val
  1317. ) );
  1318. }
  1319. $original_comment_tr = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}icl_translations WHERE element_id=%d AND element_type=%s", $comment_id, 'comment' ) );
  1320. $comment_translation = array(
  1321. 'element_type' => 'comment',
  1322. 'element_id' => $dup_comment_id,
  1323. 'trid' => $original_comment_tr->trid,
  1324. 'language_code' => $lang,
  1325. /*'source_language_code' => $original_comment_tr->language_code */
  1326. );
  1327. $wpdb->insert( $wpdb->prefix . 'icl_translations', $comment_translation );
  1328. }
  1329. }
  1330. function delete_post_actions($post_id){
  1331. global $wpdb;
  1332. $post_type = $wpdb->get_var("SELECT post_type FROM {$wpdb->posts} WHERE ID={$post_id}");
  1333. if(!empty($post_type)){
  1334. $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));
  1335. if($translation_id){
  1336. $rid = $wpdb->get_var($wpdb->prepare("SELECT rid FROM {$wpdb->prefix}icl_translation_status WHERE translation_id=%d", $translation_id));
  1337. $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}icl_translation_status WHERE translation_id=%d", $translation_id));
  1338. if($rid){
  1339. $jobs = $wpdb->get_col($wpdb->prepare("SELECT job_id FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d", $rid));
  1340. $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d", $rid));
  1341. if(!empty($jobs)){
  1342. $wpdb->query("DELETE FROM {$wpdb->prefix}icl_translate WHERE job_id IN (".join(',', $jobs).")");
  1343. }
  1344. }
  1345. }
  1346. }
  1347. }
  1348. function edit_term($cat_id, $tt_id){
  1349. global $wpdb, $sitepress;
  1350. $el_type = $wpdb->get_var("SELECT taxonomy FROM {$wpdb->term_taxonomy} WHERE term_taxonomy_id={$tt_id}");
  1351. if(!$sitepress->is_translated_taxonomy($el_type)){
  1352. return;
  1353. };
  1354. $icl_el_type = 'tax_' . $el_type;
  1355. // has trid only when it's a translation of another tag
  1356. $trid = isset($_POST['icl_trid']) && (isset($_POST['icl_'.$icl_el_type.'_language'])) ? $_POST['icl_trid']:null;
  1357. // see if we have a "translation of" setting.
  1358. $src_language = false;
  1359. if (isset($_POST['icl_translation_of']) && $_POST['icl_translation_of']) {
  1360. $src_term_id = $_POST['icl_translation_of'];
  1361. if ($src_term_id != 'none') {
  1362. $res = $wpdb->get_row("SELECT trid, language_code
  1363. FROM {$wpdb->prefix}icl_translations WHERE element_id={$src_term_id} AND element_type='{$icl_el_type}'");
  1364. $trid = $res->trid;
  1365. $src_language = $res->language_code;
  1366. } else {
  1367. $trid = null;
  1368. }
  1369. }
  1370. if(isset($_POST['action']) && $_POST['action'] == 'inline-save-tax'){
  1371. $trid = $sitepress->get_element_trid($tt_id, $icl_el_type);
  1372. }
  1373. // update icl_translate if necessary
  1374. // get the terms translations
  1375. $element_translations = $sitepress->get_element_translations($trid, $icl_el_type);
  1376. $tr_ids = array();
  1377. foreach($element_translations as $el_lang => $el_info){
  1378. if($tt_id != $el_info->term_id){
  1379. $tr_ids[] = $el_info->term_id;
  1380. }
  1381. }
  1382. // does the term taxonomy we are currently editing have a record in the icl_translate?
  1383. $results = $wpdb->get_results( $wpdb->prepare(
  1384. "SELECT field_data, job_id FROM {$wpdb->prefix}icl_translate WHERE field_type = %s", $el_type.'_ids'
  1385. ));
  1386. if(!empty($results)){
  1387. $job_ids = array(); // this will hold the job ids that contain our term under field_data
  1388. $job_ids2 = array(); // this will hold the job ids that contain our term under field_data_translated
  1389. foreach($results as $result){
  1390. $r_ids = explode (',', $result->field_data);
  1391. // is the term found?
  1392. if(in_array ($tt_id, $r_ids)){
  1393. $job_ids[] = $result->job_id;
  1394. // this is used further down to identify the correct term name to update
  1395. foreach($r_ids as $k => $v){
  1396. if($v == $tt_id){
  1397. $t_k[] = $k;
  1398. }
  1399. }
  1400. }
  1401. // the name of the current category being edited could also be under field_data_translated
  1402. if(!empty($tr_ids)){
  1403. foreach($tr_ids as $tr_id){
  1404. // is the term found?
  1405. if(in_array ($tr_id, $r_ids)){
  1406. $job_ids2[] = $result->job_id;
  1407. // this is used further down to identify the correct term name to update
  1408. foreach($r_ids as $k => $v){
  1409. if($v == $tr_id){
  1410. $t_k2[] = $k;
  1411. }
  1412. }
  1413. }
  1414. }
  1415. }
  1416. }
  1417. // if we have job_ids to be updated proceed
  1418. if(!empty($job_ids)){
  1419. $in = implode (',', $job_ids);
  1420. $field_type = ($el_type == 'category' ? 'categories' : $el_type);
  1421. //grab the term names
  1422. $results = $wpdb->get_results( $wpdb->prepare(
  1423. "SELECT tid, field_data, field_format FROM {$wpdb->prefix}icl_translate WHERE job_id IN ({$in}) AND field_type = %s", $field_type
  1424. ));
  1425. $count = 0;
  1426. foreach($results as $result){
  1427. // decode
  1428. $decoded_data = self::decode_field_data($result->field_data, $result->field_format);
  1429. // we may have multiple comma separated term names - pass the new term name to the correct one!
  1430. $decoded_data[$t_k[$count]] = $_POST['name'];
  1431. // encode
  1432. $encoded_data =$this->encode_field_data($decoded_data, $result->field_format);
  1433. // update
  1434. $wpdb->update($wpdb->prefix.'icl_translate',
  1435. array('field_data'=>$encoded_data),
  1436. array('tid'=>$result->tid)
  1437. );
  1438. $count++;
  1439. }
  1440. // update the translation status as "needs_update"
  1441. foreach($job_ids as $job_id){
  1442. list($translator_id, $rid) = $wpdb->get_row($wpdb->prepare("SELECT translator_id, rid FROM {$wpdb->prefix}icl_translate_job WHERE job_id=%d", $job_id), ARRAY_N);
  1443. // update
  1444. $wpdb->update($wpdb->prefix.'icl_translation_status',
  1445. array('needs_update'=>1),
  1446. array('rid'=>$rid, 'translator_id'=>$translator_id)
  1447. );
  1448. }
  1449. }
  1450. // if we have job_ids2 to be updated proceed
  1451. if(!empty($job_ids2)){
  1452. $in = implode (',', $job_ids2);
  1453. $field_type = ($el_type == 'category' ? 'categories' : $el_type);
  1454. //grab the term names
  1455. $results = $wpdb->get_results( $wpdb->prepare(
  1456. "SELECT tid, field_data_translated, field_format FROM {$wpdb->prefix}icl_translate WHERE job_id IN ({$in}) AND field_type = %s", $field_type
  1457. ));
  1458. $count = 0;
  1459. foreach($results as $result){
  1460. if(!empty($result->field_data_translated)){
  1461. // decode
  1462. $decoded_data = self::decode_field_data($result->field_data_translated, $result->field_format);
  1463. // we may have multiple comma separated term names - pass the new term name to the correct one!
  1464. $decoded_data[$t_k2[$count]] = $_POST['name'];
  1465. // encode
  1466. $encoded_data =$this->encode_field_data($decoded_data, $result->field_format);
  1467. // update
  1468. $wpdb->update($wpdb->prefix.'icl_translate',
  1469. array('field_data_translated'=>$encoded_data),
  1470. array('tid'=>$result->tid)
  1471. );
  1472. }
  1473. $count++;
  1474. }
  1475. }
  1476. }
  1477. }
  1478. /* TRANSLATIONS */
  1479. /* ******************************************************************************************** */
  1480. /**
  1481. * calculate post md5
  1482. *
  1483. * @param object|int $post
  1484. * @return string
  1485. *
  1486. * @todo full support for custom posts and custom taxonomies
  1487. */
  1488. function post_md5($post){
  1489. if (isset($post->external_type) && $post->external_type) {
  1490. $md5str = '';
  1491. foreach ($post->string_data as $key => $value) {
  1492. $md5str .= $key . $value;
  1493. }
  1494. } else {
  1495. $post_tags = $post_categories = $custom_fields_values = array();
  1496. if(is_numeric($post)){
  1497. $post = get_post($post);
  1498. }
  1499. foreach(wp_get_object_terms($post->ID, 'post_tag') as $tag){
  1500. $post_tags[] = $tag->name;
  1501. }
  1502. if(is_array($post_tags)){
  1503. sort($post_tags, SORT_STRING);
  1504. }
  1505. foreach(wp_get_object_terms($post->ID, 'category') as $cat){
  1506. $post_categories[] = $cat->name;
  1507. }
  1508. if(is_array($post_categories)){
  1509. sort($post_categories, SORT_STRING);
  1510. }
  1511. global $wpdb, $sitepress_settings;
  1512. // get custom taxonomies
  1513. $taxonomies = $wpdb->get_col("
  1514. SELECT DISTINCT tx.taxonomy
  1515. FROM {$wpdb->term_taxonomy} tx JOIN {$wpdb->term_relationships} tr ON tx.term_taxonomy_id = tr.term_taxonomy_id
  1516. WHERE tr.object_id = {$post->ID}
  1517. ");
  1518. sort($taxonomies, SORT_STRING);
  1519. foreach($taxonomies as $t){
  1520. if(taxonomy_exists($t)){
  1521. if(@intval($sitepress_settings['taxonomies_sync_option'][$t]) == 1){
  1522. $taxs = array();
  1523. foreach(wp_get_object_terms($post->ID, $t) as $trm){
  1524. $taxs[] = $trm->name;
  1525. }
  1526. if($taxs){
  1527. sort($taxs,SORT_STRING);
  1528. $all_taxs[] = '['.$t.']:'.join(',',$taxs);
  1529. }
  1530. }
  1531. }
  1532. }
  1533. $custom_fields_values = array();
  1534. if ( is_array( $this->settings['custom_fields_translation'] ) ) {
  1535. foreach ( $this->settings['custom_fields_translation'] as $cf => $op ) {
  1536. if ( $op == 2 || $op == 1 ) {
  1537. $value = get_post_meta( $post->ID, $cf, true );
  1538. if ( !is_array( $value ) && !is_object( $value ) ) {
  1539. $custom_fields_values[] = $value;
  1540. }
  1541. }
  1542. }
  1543. }
  1544. $md5str =
  1545. $post->post_title . ';' .
  1546. $post->post_content . ';' .
  1547. join(',',$post_tags).';' .
  1548. join(',',$post_categories) . ';' .
  1549. join(',', $custom_fields_values);
  1550. if(!empty($all_taxs)){
  1551. $md5str .= ';' . join(';', $all_taxs);
  1552. }
  1553. if($sitepress_settings['translated_document_page_url'] == 'translate'){
  1554. $md5str .= $post->post_name . ';';
  1555. }
  1556. }
  1557. $md5 = md5($md5str);
  1558. return $md5;
  1559. }
  1560. /**
  1561. * get documents
  1562. *
  1563. * @param array $args
  1564. *
  1565. * @return mixed
  1566. */
  1567. function get_documents($args){
  1568. $parent_id = false;
  1569. $parent_all = false;
  1570. $to_lang = false;
  1571. $from_lang = false;
  1572. $tstatus = false;
  1573. $sort_by = false;
  1574. $sort_order = false;
  1575. $limit_no = 0;
  1576. extract($args);
  1577. global $wpdb, $wp_query, $sitepress;
  1578. $t_el_types = array_keys($sitepress->get_translatable_documents());
  1579. // SELECT
  1580. $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";
  1581. $active_languages = $sitepress->get_active_languages();
  1582. if($to_lang){
  1583. $select .= ", iclts.status, iclts.needs_update";
  1584. }else{
  1585. foreach( $active_languages as $lang){
  1586. if($lang['code'] == $from_lang) continue;
  1587. $tbl_alias_suffix = str_replace('-','_',$lang['code']);
  1588. $select .= ", iclts_{$tbl_alias_suffix}.status AS status_{$tbl_alias_suffix}, iclts_{$tbl_alias_suffix}.needs_update AS needs_update_{$tbl_alias_suffix}";
  1589. }
  1590. }
  1591. // FROM
  1592. $from = " {$wpdb->posts} p";
  1593. // JOIN
  1594. $join = "";
  1595. $join .= " LEFT JOIN {$wpdb->prefix}icl_translations t ON t.element_id=p.ID\n";
  1596. if($to_lang){
  1597. $tbl_alias_suffix = str_replace('-','_',$to_lang);
  1598. $join .= " LEFT JOIN {$wpdb->prefix}icl_translations iclt_{$tbl_alias_suffix}
  1599. ON iclt_{$tbl_alias_suffix}.trid=t.trid AND iclt_{$tbl_alias_suffix}.language_code='{$to_lang}'\n";
  1600. $join .= " LEFT JOIN {$wpdb->prefix}icl_translation_status iclts ON iclts.translation_id=iclt_{$tbl_alias_suffix}.translation_id\n";
  1601. }else{
  1602. foreach( $active_languages as $lang){
  1603. if($lang['code'] == $from_lang) continue;
  1604. $tbl_alias_suffix = str_replace('-','_',$lang['code']);
  1605. $join .= " LEFT JOIN {$wpdb->prefix}icl_translations iclt_{$tbl_alias_suffix}
  1606. ON iclt_{$tbl_alias_suffix}.trid=t.trid AND iclt_{$tbl_alias_suffix}.language_code='{$lang['code']}'\n";
  1607. $join .= " LEFT JOIN {$wpdb->prefix}icl_translation_status iclts_{$tbl_alias_suffix}
  1608. ON iclts_{$tbl_alias_suffix}.translation_id=iclt_{$tbl_alias_suffix}.translation_id\n";
  1609. }
  1610. }
  1611. // WHERE
  1612. $where = " t.language_code = '{$from_lang}' AND p.post_status NOT IN ('trash', 'auto-draft') \n";
  1613. if(!empty($type)){
  1614. $where .= " AND p.post_type = '{$type}'";
  1615. $where .= " AND t.element_type = 'post_{$type}'\n";
  1616. }else{
  1617. $where .= " AND p.post_type IN ('".join("','",$t_el_types)."')\n";
  1618. foreach($t_el_types as $k=>$v){
  1619. $t_el_types[$k] = 'post_' . $v;
  1620. }
  1621. $where .= " AND t.element_type IN ('".join("','",$t_el_types)."')\n";
  1622. }
  1623. if(!empty($title)){
  1624. $where .= " AND p.post_title LIKE '%".esc_sql($title)."%'\n";
  1625. }
  1626. if(!empty($status)){
  1627. $where .= " AND p.post_status = '{$status}'\n";
  1628. }
  1629. if(isset($from_date)){
  1630. $where .= " AND p.post_date > '{$from_date}'\n";
  1631. }
  1632. if(isset($to_date)){
  1633. $where .= " AND p.post_date > '{$to_date}'\n";
  1634. }
  1635. if($tstatus){
  1636. if($to_lang){
  1637. if($tstatus == 'not'){
  1638. $where .= " AND (iclts.status IS NULL OR iclts.status = ".ICL_TM_WAITING_FOR_TRANSLATOR." OR iclts.needs_update = 1)\n";
  1639. }elseif($tstatus == 'need-update'){
  1640. $where .= " AND iclts.needs_update = 1\n";
  1641. }elseif($tstatus == 'in_progress'){
  1642. $where .= " AND iclts.status = ".ICL_TM_IN_PROGRESS." AND iclts.needs_update = 0\n";
  1643. }elseif($tstatus == 'complete'){
  1644. $where .= " AND (iclts.status = ".ICL_TM_COMPLETE." OR iclts.status = ".ICL_TM_DUPLICATE.") AND iclts.needs_update = 0\n";
  1645. }
  1646. }elseif( $active_languages && count($active_languages)>1 ){
  1647. if($tstatus == 'not'){
  1648. $where .= " AND (";
  1649. $wheres = array();
  1650. foreach( $active_languages as $lang){
  1651. if($lang['code'] == $from_lang) continue;
  1652. $tbl_alias_suffix = str_replace('-','_',$lang['code']);
  1653. $wheres[] = "iclts_{$tbl_alias_suffix}.status IS NULL OR iclts_{$tbl_alias_suffix}.status = ".ICL_TM_WAITING_FOR_TRANSLATOR." OR iclts_{$tbl_alias_suffix}.needs_update = 1\n";
  1654. }
  1655. $where .= join(' OR ', $wheres) . ")";
  1656. }elseif($tstatus == 'need-update'){
  1657. $where .= " AND (";
  1658. $wheres = array();
  1659. foreach( $active_languages as $lang){
  1660. if($lang['code'] == $from_lang) continue;
  1661. $tbl_alias_suffix = str_replace('-','_',$lang['code']);
  1662. $wheres[] = "iclts_{$tbl_alias_suffix}.needs_update = 1\n";
  1663. }
  1664. $where .= join(' OR ', $wheres) . ")";
  1665. }elseif($tstatus == 'in_progress'){
  1666. $where .= " AND (";
  1667. $wheres = array();
  1668. foreach( $active_languages as $lang){
  1669. if($lang['code'] == $from_lang) continue;
  1670. $tbl_alias_suffix = str_replace('-','_',$lang['code']);
  1671. $wheres[] = "iclts_{$tbl_alias_suffix}.status = ".ICL_TM_IN_PROGRESS."\n";
  1672. }
  1673. $where .= join(' OR ', $wheres) . ")";
  1674. }elseif($tstatus == 'complete'){
  1675. foreach( $active_languages as $lang){
  1676. if($lang['code'] == $from_lang) continue;
  1677. $tbl_alias_suffix = str_replace('-','_',$lang['code']);
  1678. $where .= " AND (iclts_{$tbl_alias_suffix}.status = ".ICL_TM_COMPLETE." OR iclts_{$tbl_alias_suffix}.status = ".ICL_TM_DUPLICATE.") AND iclts_{$tbl_alias_suffix}.needs_update = 0\n";
  1679. }
  1680. }
  1681. }
  1682. }
  1683. if(isset($parent_type) && $parent_type == 'page' && $parent_id > 0){
  1684. if($parent_all){
  1685. $children = icl_get_post_children_recursive($parent_id);
  1686. if(!$children) $children[] = -1;
  1687. $where .= ' AND p.ID IN (' . join(',', $children) . ')';
  1688. }else{
  1689. $where .= ' AND p.post_parent=' . intval($parent_id);
  1690. }
  1691. }
  1692. if(isset($parent_type) && $parent_type == 'category' && $parent_id > 0){
  1693. if($parent_all){
  1694. $children = icl_get_tax_children_recursive($parent_id);
  1695. $children[] = $parent_id;
  1696. $join .= " JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
  1697. JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id AND taxonomy = 'category'
  1698. JOIN {$wpdb->terms} tm ON tt.term_id = tm.term_id AND tm.term_id IN(" . join(',', $children) . ")";
  1699. }else{
  1700. $join .= " JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
  1701. JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id AND taxonomy = 'category'
  1702. JOIN {$wpdb->terms} tm ON tt.term_id = tm.term_id AND tm.term_id = " . intval($parent_id);
  1703. }
  1704. }
  1705. // ORDER
  1706. if($sort_by){
  1707. $order = " $sort_by ";
  1708. }else{
  1709. $order = " p.post_date DESC";
  1710. }
  1711. if($sort_order){
  1712. $order .= $sort_order;
  1713. }else{
  1714. $order .= 'DESC';
  1715. }
  1716. // LIMIT
  1717. if(!isset($_GET['paged'])) $_GET['paged'] = 1;
  1718. $offset = ($_GET['paged']-1)*$limit_no;
  1719. $limit = " " . $offset . ',' . $limit_no;
  1720. $sql = "
  1721. SELECT SQL_CALC_FOUND_ROWS {$select}
  1722. FROM {$from}
  1723. {$join}
  1724. WHERE {$where}
  1725. ORDER BY {$order}
  1726. LIMIT {$limit}
  1727. ";
  1728. $results = $wpdb->get_results($sql);
  1729. $count = $wpdb->get_var("SELECT FOUND_ROWS()");
  1730. $wp_query->found_posts = $count;
  1731. $wp_query->query_vars['posts_per_page'] = $limit_no;
  1732. $wp_query->max_num_pages = ceil($wp_query->found_posts/$limit_no);
  1733. // post process
  1734. foreach($results as $k=>$v){
  1735. if($v->is_translation){
  1736. $source_language = $wpdb->get_var($wpdb->prepare("SELECT language_code FROM {$wpdb->prefix}icl_translations WHERE trid=%d AND source_language_code IS NULL", $v->trid));
  1737. $_tmp = 'status_' . $source_language;
  1738. $v->$_tmp = ICL_TM_COMPLETE;
  1739. }
  1740. }
  1741. return $results;
  1742. }
  1743. function get_element_translation($element_id, $language, $element_type='post_post'){
  1744. global $wpdb, $sitepress;
  1745. $trid = $sitepress->get_element_trid($element_id, $element_type);
  1746. $translation = array();
  1747. if($trid){
  1748. $translation = $wpdb->get_row($wpdb->prepare("
  1749. SELECT *
  1750. FROM {$wpdb->prefix}icl_translations tr
  1751. JOIN {$wpdb->prefix}icl_translation_status ts ON tr.translation_id = ts.translation_id
  1752. WHERE tr.trid=%s AND tr.language_code='%s'
  1753. ", $trid, $language));
  1754. }
  1755. return $translation;
  1756. }
  1757. function get_element_translations($element_id, $element_type='post_post', $service = false){
  1758. global $wpdb, $sitepress;
  1759. $trid = $sitepress->get_element_trid($element_id, $element_type);
  1760. $translations = array();
  1761. if($trid){
  1762. $service = $service ? " AND translation_service = '$service'" : '';
  1763. $translations = $wpdb->get_results($wpdb->prepare("
  1764. SELECT *
  1765. FROM {$wpdb->prefix}icl_translations tr
  1766. JOIN {$wpdb->prefix}icl_translation_status ts ON tr.translation_id = ts.translation_id
  1767. WHERE tr.trid=%s {$service}
  1768. ", $trid));
  1769. foreach($translations as $k=>$v){
  1770. $translations[$v->language_code] = $v;
  1771. unset($translations[$k]);
  1772. }
  1773. }
  1774. return $translations;
  1775. }
  1776. /**
  1777. * returns icon file name according to status code
  1778. *
  1779. * @param int $status
  1780. * @param int $needs_update
  1781. *
  1782. * @return string
  1783. */
  1784. public function status2img_filename($status, $needs_update = 0){
  1785. if($needs_update){
  1786. $img_file = 'needs-update.png';
  1787. }else{
  1788. switch($status){
  1789. case ICL_TM_NOT_TRANSLATED: $img_file = 'not-translated.png'; break;
  1790. case ICL_TM_WAITING_FOR_TRANSLATOR: $img_file = 'in-progress.png'; break;
  1791. case ICL_TM_IN_PROGRESS: $img_file = 'in-progress.png'; break;
  1792. case ICL_TM_NEEDS_UPDATE: $img_file = 'needs-update.png'; break;
  1793. case ICL_TM_DUPLICATE: $img_file = 'copy.png'; break;
  1794. case ICL_TM_COMPLETE: $img_file = 'complete.png'; break;
  1795. default: $img_file = '';
  1796. }
  1797. }
  1798. return $img_file;
  1799. }
  1800. public function status2text($status){
  1801. switch($status){
  1802. case ICL_TM_NOT_TRANSLATED: $text = __('Not translated', 'sitepress'); break;
  1803. case ICL_TM_WAITING_FOR_TRANSLATOR: $text = __('Waiting for translator', 'sitepress'); break;
  1804. case ICL_TM_IN_PROGRESS: $text = __('In progress', 'sitepress'); break;
  1805. case ICL_TM_NEEDS_UPDATE: $text = __('Needs update', 'sitepress'); break;
  1806. case ICL_TM_DUPLICATE: $text = __('Duplicate', 'sitepress'); break;
  1807. case ICL_TM_COMPLETE: $text = __('Complete', 'sitepress'); break;
  1808. default: $text = '';
  1809. }
  1810. return $text;
  1811. }
  1812. public static function estimate_word_count($data, $lang_code){
  1813. global $asian_languages;
  1814. $words = 0;
  1815. if(isset($data->post_title)){
  1816. if(in_array($lang_code, $asian_languages)){
  1817. $words += strlen(strip_tags($data->post_title)) / 6;
  1818. } else {
  1819. $words += count(preg_split(
  1820. '/[\s\/]+/', $data->post_title, 0, PREG_SPLIT_NO_EMPTY));
  1821. }
  1822. }
  1823. if(isset($data->post_content)){
  1824. if(in_array($lang_code, $asian_languages)){
  1825. $words += strlen(strip_tags($data->post_content)) / 6;
  1826. } else {
  1827. $words += count(preg_split(
  1828. '/[\s\/]+/', strip_tags($data->post_content), 0, PREG_SPLIT_NO_EMPTY));
  1829. }
  1830. }
  1831. return (int)$words;
  1832. }
  1833. public static function estimate_custom_field_word_count( $post_id, $lang_code ) {
  1834. global $asian_languages, $sitepress_settings;
  1835. $words = 0;
  1836. if ( !empty( $sitepress_settings[ 'translation-management' ][ 'custom_fields_translation' ] ) && is_array( $sitepress_settings[ 'translation-management' ][ 'custom_fields_translation' ] ) ) {
  1837. $custom_fields = array();
  1838. foreach ( $sitepress_settings[ 'translation-management' ][ 'custom_fields_translation' ] as $cf => $op ) {
  1839. if ( $op == 2 ) {
  1840. $custom_fields[ ] = $cf;
  1841. }
  1842. }
  1843. foreach ( $custom_fields as $cf ) {
  1844. $custom_fields_value = get_post_meta( $post_id, $cf );
  1845. if ( $custom_fields_value ) {
  1846. if ( is_scalar( $custom_fields_value ) ) { // only support scalar values fo rnow
  1847. if ( in_array( $lang_code, $asian_languages ) ) {
  1848. $words += strlen( strip_tags( $custom_fields_value ) ) / 6;
  1849. } else {
  1850. $words += count( preg_split( '/[\s\/]+/', strip_tags( $custom_fields_value ), 0, PREG_SPLIT_NO_EMPTY ) );
  1851. }
  1852. } else {
  1853. foreach ( $custom_fields_value as $custom_fields_value_item ) {
  1854. if ( $custom_fields_value_item && is_scalar( $custom_fields_value_item ) ) { // only support scalar values fo rnow
  1855. if ( in_array( $lang_code, $asian_languages ) ) {
  1856. $words += strlen( strip_tags( $custom_fields_value_item ) ) / 6;
  1857. } else {
  1858. $words += count( preg_split( '/[\s\/]+/', strip_tags( $custom_fields_value_item ), 0, PREG_SPLIT_NO_EMPTY ) );
  1859. }
  1860. }
  1861. }
  1862. }
  1863. }
  1864. }
  1865. }
  1866. return (int)$words;
  1867. }
  1868. public static function decode_field_data($data, $format){
  1869. if($format == 'base64'){
  1870. $data = base64_decode($data);
  1871. }elseif($format == 'csv_base64'){
  1872. $exp = explode(',', $data);
  1873. foreach($exp as $k=>$e){
  1874. $exp[$k] = base64_decode(trim($e,'"'));
  1875. }
  1876. $data = $exp;
  1877. }
  1878. return $data;
  1879. }
  1880. public function encode_field_data($data, $format){
  1881. if($format == 'base64'){
  1882. $data = base64_encode($data);
  1883. }elseif($format == 'csv_base64'){
  1884. $exp = $data;
  1885. foreach($exp as $k=>$e){
  1886. $exp[$k] = '"' . base64_encode(trim($e)) . '"';
  1887. }
  1888. $data = join(',', $exp);
  1889. }
  1890. return $data;
  1891. }
  1892. /**
  1893. * create translation package
  1894. *
  1895. * @param object|int $post
  1896. *
  1897. * @return array
  1898. */
  1899. function create_translation_package($post){
  1900. global $sitepress, $sitepress_settings;
  1901. $package = array();
  1902. if(is_numeric($post)){
  1903. $post = get_post($post);
  1904. }
  1905. if (isset($post->external_type) && $post->external_type) {
  1906. foreach ($post->string_data as $key => $value) {
  1907. $package['contents'][$key] = array(
  1908. 'translate' => 1,
  1909. 'data' => $this->encode_field_data($value, 'base64'),
  1910. 'format' => 'base64'
  1911. );
  1912. }
  1913. $package['contents']['original_id'] = array(
  1914. 'translate' => 0,
  1915. 'data' => $post->post_id,
  1916. );
  1917. } else {
  1918. $home_url = get_home_url();
  1919. if($post->post_type=='page'){
  1920. $package['url'] = htmlentities( $home_url . '?page_id=' . ($post->ID));
  1921. }else{
  1922. $package['url'] = htmlentities( $home_url . '?p=' . ($post->ID));
  1923. }
  1924. $package['contents']['title'] = array(
  1925. 'translate' => 1,
  1926. 'data' => $this->encode_field_data($post->post_title, 'base64'),
  1927. 'format' => 'base64'
  1928. );
  1929. if($sitepress_settings['translated_document_page_url'] == 'translate'){
  1930. $package['contents']['URL'] = array(
  1931. 'translate' => 1,
  1932. 'data' => $this->encode_field_data($post->post_name, 'base64'),
  1933. 'format' => 'base64'
  1934. );
  1935. }
  1936. $package['contents']['body'] = array(
  1937. 'translate' => 1,
  1938. 'data' => $this->encode_field_data($post->post_content, 'base64'),
  1939. 'format' => 'base64'
  1940. );
  1941. if(!empty($post->post_excerpt)){
  1942. $package['contents']['excerpt'] = array(
  1943. 'translate' => 1,
  1944. 'data' => base64_encode($post->post_excerpt),
  1945. 'format' => 'base64'
  1946. );
  1947. }
  1948. $package['contents']['original_id'] = array(
  1949. 'translate' => 0,
  1950. 'data' => $post->ID
  1951. );
  1952. if(!empty($this->settings['custom_fields_translation']))
  1953. foreach($this->settings['custom_fields_translation'] as $cf => $op){
  1954. if ($op == 2) { // translate
  1955. /* */
  1956. $custom_fields_value = get_post_meta($post->ID, $cf, true);
  1957. if ($custom_fields_value != '' && is_scalar($custom_fields_value)) {
  1958. $package['contents']['field-'.$cf] = array(
  1959. 'translate' => 1,
  1960. 'data' => $this->encode_field_data($custom_fields_value, 'base64'),
  1961. 'format' => 'base64'
  1962. );
  1963. $package['contents']['field-'.$cf.'-name'] = array(
  1964. 'translate' => 0,
  1965. 'data' => $cf
  1966. );
  1967. $package['contents']['field-'.$cf.'-type'] = array(
  1968. 'translate' => 0,
  1969. 'data' => 'custom_field'
  1970. );
  1971. }
  1972. }
  1973. }
  1974. foreach((array)$sitepress->get_translatable_taxonomies(true, $post->post_type) as $taxonomy){
  1975. $terms = get_the_terms( $post->ID , $taxonomy );
  1976. if(!empty($terms)){
  1977. $_taxs = $_tax_ids = array();
  1978. foreach($terms as $term){
  1979. $_taxs[] = $term->name;
  1980. $_tax_ids[] = $term->term_taxonomy_id;
  1981. }
  1982. if($taxonomy == 'post_tag'){
  1983. $tax_package_key = 'tags';
  1984. $tax_id_package_key = 'tag_ids';
  1985. }
  1986. elseif($taxonomy == 'category'){
  1987. $tax_package_key = 'categories';
  1988. $tax_id_package_key = 'category_ids';
  1989. }
  1990. else{
  1991. $tax_package_key = $taxonomy;
  1992. $tax_id_package_key = $taxonomy . '_ids';
  1993. }
  1994. $package['contents'][$tax_package_key] = array(
  1995. 'translate' => 1,
  1996. 'data' => $this->encode_field_data($_taxs,'csv_base64'),
  1997. 'format'=>'csv_base64'
  1998. );
  1999. $package['contents'][$tax_id_package_key] = array(
  2000. 'translate' => 0,
  2001. 'data' => join(',', $_tax_ids)
  2002. );
  2003. }
  2004. }
  2005. }
  2006. return $package;
  2007. }
  2008. /**
  2009. * add/update icl_translation_status record
  2010. *
  2011. * @param array $data
  2012. *
  2013. * @return array
  2014. */
  2015. function update_translation_status($data){
  2016. global $wpdb;
  2017. if(!isset($data['translation_id'])) return;
  2018. $rid = $wpdb->get_var($wpdb->prepare("SELECT rid FROM {$wpdb->prefix}icl_translation_status WHERE translation_id=%d", $data['translation_id']));
  2019. if($rid){
  2020. $wpdb->update($wpdb->prefix.'icl_translation_status', $data, array('rid'=>$rid));
  2021. $update = true;
  2022. }else{
  2023. $wpdb->insert($wpdb->prefix.'icl_translation_status',$data);
  2024. $rid = $wpdb->insert_id;
  2025. $update = false;
  2026. }
  2027. return array($rid, $update);
  2028. }
  2029. function _get_post($post_id) {
  2030. if (is_string($post_id) && strcmp(substr($post_id, 0, strlen('external_')), 'external_')===0) {
  2031. $item = null;
  2032. return apply_filters('WPML_get_translatable_item', $item, $post_id);
  2033. } else {
  2034. return get_post($post_id);
  2035. }
  2036. }
  2037. /* TRANSLATION JOBS */
  2038. /* ******************************************************************************************** */
  2039. function send_jobs($data){
  2040. global $wpdb, $sitepress;
  2041. if(!isset($data['tr_action']) && isset($data['translate_to'])){ //adapt new format
  2042. $data['tr_action'] = $data['translate_to'];
  2043. unset($data['translate_to']);
  2044. }
  2045. /** @var $translate_from string */
  2046. // tr_action (translate_to)
  2047. // translator
  2048. // post
  2049. // service
  2050. // defaults
  2051. $data_default = array(
  2052. 'translate_from' => $sitepress->get_default_language()
  2053. );
  2054. extract($data_default);
  2055. extract($data, EXTR_OVERWRITE);
  2056. // no language selected ?
  2057. if(!isset($tr_action) || empty($tr_action)){
  2058. $this->messages[] = array(
  2059. 'type'=>'error',
  2060. 'text' => __('Please select at least one language to translate into.', 'sitepress')
  2061. );
  2062. $this->dashboard_select = $data; // prepopulate dashboard
  2063. return false;
  2064. }
  2065. // no post selected ?
  2066. if(!isset($iclpost) || empty($iclpost)){
  2067. $this->messages[] = array(
  2068. 'type'=>'error',
  2069. 'text' => __('Please select at least one document to translate.', 'sitepress')
  2070. );
  2071. $this->dashboard_select = $data; // pre-populate dashboard
  2072. return false;
  2073. }
  2074. $selected_posts = $iclpost;
  2075. $selected_translators = isset($translator) ? $translator : array();
  2076. $selected_languages = $tr_action;
  2077. $job_ids = array();
  2078. foreach($selected_posts as $post_id){
  2079. $post = $this->_get_post($post_id);
  2080. $post_trid = $sitepress->get_element_trid($post->ID, 'post_' . $post->post_type);
  2081. $post_translations = $sitepress->get_element_translations($post_trid, 'post_' . $post->post_type);
  2082. $md5 = $this->post_md5($post);
  2083. $translation_package = $this->create_translation_package($post);
  2084. foreach($selected_languages as $lang => $action){
  2085. // making this a duplicate?
  2086. if($action == 2){
  2087. // dont send documents that are in progress
  2088. $current_translation_status = $this->get_element_translation($post_id, $lang, 'post_' . $post->post_type);
  2089. if($current_translation_status && $current_translation_status->status == ICL_TM_IN_PROGRESS) continue;
  2090. $job_ids[] = $this->make_duplicate($post_id, $lang);
  2091. }elseif($action == 1){
  2092. if(empty($post_translations[$lang])){
  2093. $translation_id = $sitepress->set_element_language_details(null , 'post_' . $post->post_type, $post_trid, $lang, $translate_from);
  2094. }else{
  2095. $translation_id = $post_translations[$lang]->translation_id;
  2096. }
  2097. $current_translation_status = $this->get_element_translation($post_id, $lang, 'post_' . $post->post_type);
  2098. // don't send documents that are in progress
  2099. // don't send documents that are already translated and don't need update
  2100. // don't send documents that are waiting for translator
  2101. if(!empty($current_translation_status)){
  2102. if($current_translation_status->status == ICL_TM_IN_PROGRESS) continue;
  2103. if($current_translation_status->status == ICL_TM_COMPLETE && !$current_translation_status->needs_update) continue;
  2104. if($current_translation_status->status == ICL_TM_WAITING_FOR_TRANSLATOR) continue;
  2105. }
  2106. $_status = ICL_TM_WAITING_FOR_TRANSLATOR;
  2107. $_exp = isset($selected_translators[$lang]) ? explode('-', $selected_translators[$lang]) : false;
  2108. if(!isset($service)){
  2109. $translation_service = isset($_exp[1]) ? $_exp[1] : 'local';
  2110. }else{
  2111. $translation_service = $service;
  2112. }
  2113. $translator_id = $_exp[0];
  2114. // set as default translator
  2115. if($translator_id > 0){
  2116. $this->set_default_translator($translator_id, $translate_from, $lang, $translation_service);
  2117. }
  2118. // add translation_status record
  2119. $data = array(
  2120. 'translation_id' => $translation_id,
  2121. 'status' => $_status,
  2122. 'translator_id' => $translator_id,
  2123. 'needs_update' => 0,
  2124. 'md5' => $md5,
  2125. 'translation_service' => $translation_service,
  2126. 'translation_package' => serialize($translation_package)
  2127. );
  2128. $_prevstate = $wpdb->get_row($wpdb->prepare("
  2129. SELECT status, translator_id, needs_update, md5, translation_service, translation_package, timestamp, links_fixed
  2130. FROM {$wpdb->prefix}icl_translation_status
  2131. WHERE translation_id = %d
  2132. ", $translation_id), ARRAY_A);
  2133. if(!empty($_prevstate)){
  2134. $data['_prevstate'] = serialize($_prevstate);
  2135. }
  2136. list($rid, $update) = $this->update_translation_status($data);
  2137. $job_ids[] = $this->add_translation_job($rid, $translator_id, $translation_package);
  2138. if( $translation_service == 'icanlocalize' ){
  2139. global $ICL_Pro_Translation;
  2140. $sent = $ICL_Pro_Translation->send_post($post, array($lang), $translator_id);
  2141. if(!$sent){
  2142. $job_id = array_pop($job_ids);
  2143. $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}icl_translate_job WHERE job_id=%d", $job_id));
  2144. $wpdb->query($wpdb->prepare("UPDATE {$wpdb->prefix}icl_translate_job SET revision = NULL WHERE rid=%d ORDER BY job_id DESC LIMIT 1", $rid));
  2145. $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}icl_translate WHERE job_id=%d", $job_id));
  2146. }
  2147. }
  2148. } // if / else is making a duplicate
  2149. }
  2150. }
  2151. $job_ids = array_unique($job_ids);
  2152. if(array(false) == $job_ids || empty($job_ids)){
  2153. $this->messages[] = array(
  2154. 'type'=>'error',
  2155. 'text' => __('No documents were sent to translation. Make sure that translations are not currently in progress or already translated for the selected language(s).', 'sitepress')
  2156. );
  2157. }elseif(in_array(false, $job_ids)){
  2158. $this->messages[] = array(
  2159. 'type'=>'updated',
  2160. 'text' => __('Some documents were sent to translation.', 'sitepress')
  2161. );
  2162. $this->messages[] = array(
  2163. 'type'=>'error',
  2164. 'text' => __('Some documents were <i>not</i> sent to translation. Make sure that translations are not currently in progress for the selected language(s).', 'sitepress')
  2165. );
  2166. }else{
  2167. $this->messages[] = array(
  2168. 'type'=>'updated',
  2169. 'text' => __('Selected document(s) sent to translation.', 'sitepress')
  2170. );
  2171. }
  2172. return $job_ids;
  2173. }
  2174. /**
  2175. * Adds a translation job record in icl_translate_job
  2176. *
  2177. * @param mixed $rid
  2178. * @param mixed $translator_id
  2179. * @param $translation_package
  2180. *
  2181. * @return bool|int
  2182. */
  2183. function add_translation_job($rid, $translator_id, $translation_package){
  2184. global $wpdb, $current_user;
  2185. get_currentuserinfo();
  2186. if(!$current_user->ID){
  2187. $manager_id = $wpdb->get_var($wpdb->prepare("SELECT manager_id FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d ORDER BY job_id DESC LIMIT 1", $rid));
  2188. }else{
  2189. $manager_id = $current_user->ID;
  2190. }
  2191. $translation_status = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}icl_translation_status WHERE rid=%d", $rid));
  2192. // if we have a previous job_id for this rid mark it as the top (last) revision
  2193. list($prev_job_id, $prev_job_translated) = $wpdb->get_row($wpdb->prepare("
  2194. SELECT job_id, translated FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d AND revision IS NULL
  2195. ", $rid), ARRAY_N);
  2196. if ( !is_null( $prev_job_id ) ) {
  2197. // if previous job is not complete bail out
  2198. if ( !$prev_job_translated ) {
  2199. //trigger_error(sprintf(__('Translation is in progress for job: %s.', 'sitepress'), $prev_job_id), E_USER_NOTICE);
  2200. return false;
  2201. }
  2202. $last_rev = $wpdb->get_var( $wpdb->prepare( "
  2203. SELECT MAX(revision) AS rev FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d AND revision IS NOT NULL
  2204. ", $rid ) );
  2205. $wpdb->update( $wpdb->prefix . 'icl_translate_job', array( 'revision' => $last_rev + 1 ), array( 'job_id' => $prev_job_id ) );
  2206. $prev_job = $this->get_translation_job( $prev_job_id );
  2207. if ( isset( $prev_job->original_doc_id ) ) {
  2208. $original_post = get_post( $prev_job->original_doc_id );
  2209. foreach ( $prev_job->elements as $element ) {
  2210. $prev_translation[ $element->field_type ] = $element->field_data_translated;
  2211. switch ( $element->field_type ) {
  2212. case 'title':
  2213. if ( self::decode_field_data( $element->field_data, $element->field_format ) == $original_post->post_title ) {
  2214. //$unchanged[$element->field_type] = $element->field_data_translated;
  2215. $unchanged[ $element->field_type ] = true;
  2216. }
  2217. break;
  2218. case 'body':
  2219. if ( self::decode_field_data( $element->field_data, $element->field_format ) == $original_post->post_content ) {
  2220. //$unchanged[$element->field_type] = $element->field_data_translated;
  2221. $unchanged[ $element->field_type ] = true;
  2222. }
  2223. break;
  2224. case 'excerpt':
  2225. if ( self::decode_field_data( $element->field_data, $element->field_format ) == $original_post->post_excerpt ) {
  2226. //$unchanged[$element->field_type] = $element->field_data_translated;
  2227. $unchanged[ $element->field_type ] = true;
  2228. }
  2229. break;
  2230. case 'tags':
  2231. $terms = get_the_terms( $prev_job->original_doc_id, 'post_tag' );
  2232. $_taxs = array();
  2233. if ( $terms ) {
  2234. foreach ( $terms as $term ) {
  2235. $_taxs[ ] = $term->name;
  2236. }
  2237. }
  2238. if ( $element->field_data == $this->encode_field_data( $_taxs, $element->field_format ) ) {
  2239. //$unchanged['tags'] = $element->field_data_translated;
  2240. $unchanged[ 'tags' ] = true;
  2241. }
  2242. break;
  2243. case 'categories':
  2244. $terms = get_the_terms( $prev_job->original_doc_id, 'category' );
  2245. $_taxs = array();
  2246. if ( $terms ) {
  2247. foreach ( $terms as $term ) {
  2248. $_taxs[ ] = $term->name;
  2249. }
  2250. }
  2251. if ( $element->field_data == $this->encode_field_data( $_taxs, $element->field_format ) ) {
  2252. //$unchanged['categories'] = $element->field_data_translated;
  2253. $unchanged[ 'categories' ] = true;
  2254. }
  2255. break;
  2256. default:
  2257. if ( false !== strpos( $element->field_type, 'field-' ) && !empty( $this->settings[ 'custom_fields_translation' ] ) ) {
  2258. $cf_name = preg_replace( '#^field-#', '', $element->field_type );
  2259. if ( self::decode_field_data( $element->field_data, $element->field_format ) == get_post_meta( $prev_job->original_doc_id, $cf_name, 1 ) ) {
  2260. //$unchanged[$element->field_type] = $element->field_data_translated;
  2261. $unchanged[ $element->field_type ] = true;
  2262. }
  2263. } else {
  2264. // taxonomies
  2265. if ( taxonomy_exists( $element->field_type ) ) {
  2266. $terms = get_the_terms( $prev_job->original_doc_id, $element->field_type );
  2267. $_taxs = array();
  2268. if ( $terms ) {
  2269. foreach ( $terms as $term ) {
  2270. $_taxs[ ] = $term->name;
  2271. }
  2272. }
  2273. if ( $element->field_data == $this->encode_field_data( $_taxs, $element->field_format ) ) {
  2274. //$unchanged[$element->field_type] = $field['data_translated'];
  2275. $unchanged[ $element->field_type ] = true;
  2276. }
  2277. }
  2278. }
  2279. }
  2280. }
  2281. }
  2282. }
  2283. $wpdb->insert($wpdb->prefix . 'icl_translate_job', array(
  2284. 'rid' => $rid,
  2285. 'translator_id' => $translator_id,
  2286. 'translated' => 0,
  2287. 'manager_id' => $manager_id
  2288. ));
  2289. $job_id = $wpdb->insert_id;
  2290. foreach($translation_package['contents'] as $field => $value){
  2291. $job_translate = array(
  2292. 'job_id' => $job_id,
  2293. 'content_id' => 0,
  2294. 'field_type' => $field,
  2295. 'field_format' => isset($value['format'])?$value['format']:'',
  2296. 'field_translate' => $value['translate'],
  2297. 'field_data' => $value['data'],
  2298. 'field_data_translated' => isset($prev_translation[$field]) ? $prev_translation[$field] : '',
  2299. 'field_finished' => 0
  2300. );
  2301. if(isset($unchanged[$field])){
  2302. $job_translate['field_finished'] = 1;
  2303. }
  2304. //$job_translate['field_data_translated'] = $unchanged[$field];
  2305. $wpdb->insert($wpdb->prefix . 'icl_translate', $job_translate);
  2306. }
  2307. if($this->settings['doc_translation_method'] == ICL_TM_TMETHOD_EDITOR){ // only send notifications if the translation editor method is on
  2308. if(!defined('ICL_TM_DISABLE_ALL_NOTIFICATIONS') && $translation_status->translation_service=='local'){
  2309. if($this->settings['notification']['new-job'] == ICL_TM_NOTIFICATION_IMMEDIATELY){
  2310. require_once ICL_PLUGIN_PATH . '/inc/translation-management/tm-notification.class.php';
  2311. if($job_id){
  2312. $tn_notification = new TM_Notification();
  2313. if(empty($translator_id)){
  2314. $tn_notification->new_job_any($job_id);
  2315. }else{
  2316. $tn_notification->new_job_translator($job_id, $translator_id);
  2317. }
  2318. }
  2319. }
  2320. }
  2321. }
  2322. return $job_id;
  2323. }
  2324. function assign_translation_job($job_id, $translator_id, $service='local'){
  2325. global $wpdb, $iclTranslationManagement;
  2326. // make sure TM is running
  2327. if(empty($this->settings)){
  2328. $iclTranslationManagement->init();
  2329. }
  2330. list($prev_translator_id, $rid) = $wpdb->get_row($wpdb->prepare("SELECT translator_id, rid FROM {$wpdb->prefix}icl_translate_job WHERE job_id=%d", $job_id), ARRAY_N);
  2331. require_once ICL_PLUGIN_PATH . '/inc/translation-management/tm-notification.class.php';
  2332. $tn_notification = new TM_Notification();
  2333. if($this->settings['notification']['resigned'] == ICL_TM_NOTIFICATION_IMMEDIATELY){
  2334. if(!empty($prev_translator_id) && $prev_translator_id != $translator_id){
  2335. if($job_id){
  2336. $tn_notification->translator_removed($prev_translator_id, $job_id);
  2337. }
  2338. }
  2339. }
  2340. if($this->settings['notification']['new-job'] == ICL_TM_NOTIFICATION_IMMEDIATELY){
  2341. if(empty($translator_id)){
  2342. $tn_notification->new_job_any($job_id);
  2343. }else{
  2344. $tn_notification->new_job_translator($job_id, $translator_id);
  2345. }
  2346. }
  2347. $wpdb->update($wpdb->prefix.'icl_translation_status',
  2348. array('translator_id'=>$translator_id, 'status'=>ICL_TM_WAITING_FOR_TRANSLATOR, 'translation_service' => $service),
  2349. array('rid'=>$rid));
  2350. $wpdb->update($wpdb->prefix.'icl_translate_job', array('translator_id'=>$translator_id), array('job_id'=>$job_id));
  2351. return true;
  2352. }
  2353. function get_translation_jobs($args = array()){
  2354. global $wpdb, $sitepress, $wp_query;
  2355. // defaults
  2356. /** @var $translator_id int */
  2357. /** @var $status int */
  2358. /** @var $include_unassigned bool */
  2359. /** @var $orderby bool|string */
  2360. /** @var $limit_no int */
  2361. $args_default = array(
  2362. 'translator_id' => 0,
  2363. 'status' => false,
  2364. 'include_unassigned' => false
  2365. );
  2366. extract($args_default);
  2367. extract($args, EXTR_OVERWRITE);
  2368. $language_pairs = array();
  2369. $_exp = explode('-', $translator_id);
  2370. $service = isset($_exp[1]) ? $_exp[1] : 'local';
  2371. $translator_id = $_exp[0];
  2372. $where = " s.status > " . ICL_TM_NOT_TRANSLATED;
  2373. if($status != ''){
  2374. $where .= " AND s.status=" . intval($status);
  2375. }
  2376. if($status != ICL_TM_DUPLICATE){
  2377. $where .= " AND s.status <> " . ICL_TM_DUPLICATE;
  2378. }
  2379. if(!empty($translator_id)){
  2380. if($include_unassigned){
  2381. $where .= " AND (j.translator_id=" . intval($translator_id) . " OR j.translator_id=0) ";
  2382. }else{
  2383. $where .= " AND j.translator_id=" . intval($translator_id);
  2384. }
  2385. if(!empty($service)){
  2386. $where .= " AND s.translation_service='{$service}'";
  2387. }
  2388. $language_pairs = get_user_meta($translator_id, $wpdb->prefix.'language_pairs', true);
  2389. }
  2390. // HANDLE FROM
  2391. if(!empty($from)){
  2392. $where .= PHP_EOL . " AND t.source_language_code='".esc_sql($from)."'";
  2393. }else{
  2394. // only if we filter by translator, make sure to use just the 'from' languages that apply
  2395. // in no translator_id, ommit condition and all will be pulled
  2396. if($translator_id){
  2397. if(!empty($to)){
  2398. // get 'from' languages corresdonding to $to (to $translator_id)
  2399. $from_languages = array();
  2400. foreach($language_pairs as $fl => $tls){
  2401. if(isset($tls[$to])) $from_languages[] = $fl;
  2402. }
  2403. if($from_languages){
  2404. $where .= PHP_EOL . sprintf(" AND t.source_language_code IN(%s)", "'" . join("','", $from_languages) . "'");
  2405. }
  2406. }else{
  2407. // all to all case
  2408. // get all possible combinations for $translator_id
  2409. $from_languages = array_keys($language_pairs);
  2410. $where_conditions = array();
  2411. foreach($from_languages as $fl){
  2412. $to_languages = "'" . join("','", array_keys($language_pairs[$fl])) . "'";
  2413. $where_conditions[] = sprintf(" (t.source_language_code='%s' AND t.language_code IN (%s)) ", $fl, $to_languages);
  2414. }
  2415. if(!empty($where_conditions)){
  2416. $where .= PHP_EOL . ' AND ( ' . join (' OR ', $where_conditions) . ')';
  2417. }
  2418. }
  2419. }
  2420. }
  2421. // HANDLE TO
  2422. if(!empty($to)){
  2423. $where .= PHP_EOL . " AND t.language_code='".esc_sql($to)."'";
  2424. }else{
  2425. // only if we filter by translator, make sure to use just the 'from' languages that apply
  2426. // in no translator_id, omit condition and all will be pulled
  2427. if($translator_id){
  2428. if(!empty($from)){
  2429. // get languages the user can translate into from $from
  2430. $tos = isset($language_pairs[$from]) ? array_keys($language_pairs[$from]) : array();
  2431. if($tos){
  2432. $where .= PHP_EOL . sprintf(" AND t.language_code IN(%s)", "'" . join("','", $tos) . "'");
  2433. }
  2434. }else{
  2435. // covered by 'all to all case' above
  2436. }
  2437. }
  2438. }
  2439. // ORDER BY
  2440. if($include_unassigned){
  2441. $orderby[] = 'j.translator_id DESC';
  2442. }
  2443. $orderby[] = ' j.job_id DESC ';
  2444. $orderby = join(', ', $orderby);
  2445. // LIMIT
  2446. if(!isset($_GET['paged'])) $_GET['paged'] = 1;
  2447. $offset = ($_GET['paged']-1)*$limit_no;
  2448. $limit = " " . $offset . ',' . $limit_no;
  2449. $jobs = $wpdb->get_results(
  2450. "SELECT SQL_CALC_FOUND_ROWS
  2451. j.job_id, t.trid, t.language_code, t.source_language_code,
  2452. s.translation_id, s.status, s.needs_update, s.translator_id, u.display_name AS translator_name, s.translation_service
  2453. FROM {$wpdb->prefix}icl_translate_job j
  2454. JOIN {$wpdb->prefix}icl_translation_status s ON j.rid = s.rid
  2455. JOIN {$wpdb->prefix}icl_translations t ON s.translation_id = t.translation_id
  2456. LEFT JOIN {$wpdb->users} u ON s.translator_id = u.ID
  2457. WHERE {$where} AND revision IS NULL
  2458. ORDER BY {$orderby}
  2459. LIMIT {$limit}
  2460. "
  2461. );
  2462. //echo '<pre>';
  2463. //print_r($wpdb->last_query);
  2464. //echo '</pre>';
  2465. $count = $wpdb->get_var("SELECT FOUND_ROWS()");
  2466. $wp_query->found_posts = $count;
  2467. $wp_query->query_vars['posts_per_page'] = $limit_no;
  2468. $wp_query->max_num_pages = ceil($wp_query->found_posts/$limit_no);
  2469. foreach($jobs as $k=>$row){
  2470. //original
  2471. $post_id = $wpdb->get_var($wpdb->prepare("
  2472. SELECT field_data
  2473. FROM {$wpdb->prefix}icl_translate
  2474. WHERE job_id=%d and field_type='original_id'", $row->job_id));
  2475. $parts = explode('_', $post_id);
  2476. if ($parts[0] == 'external') {
  2477. $jobs[$k]->original_doc_id = $post_id;
  2478. $jobs[$k]->post_title = base64_decode($wpdb->get_var($wpdb->prepare("
  2479. SELECT field_data
  2480. FROM {$wpdb->prefix}icl_translate
  2481. WHERE job_id=%d and field_type='name'", $row->job_id)));
  2482. if ($jobs[$k]->post_title == "") {
  2483. // try the title field.
  2484. $jobs[$k]->post_title = base64_decode($wpdb->get_var($wpdb->prepare("
  2485. SELECT field_data
  2486. FROM {$wpdb->prefix}icl_translate
  2487. WHERE job_id=%d and field_type='title'", $row->job_id)));
  2488. }
  2489. $jobs[$k]->post_title = apply_filters('WPML_translation_job_title', $jobs[$k]->post_title, $post_id);
  2490. $jobs[$k]->edit_link = self::tm_post_link($post_id);
  2491. $ldf = $sitepress->get_language_details($row->source_language_code);
  2492. } else {
  2493. $doc = $wpdb->get_row($wpdb->prepare("SELECT ID, post_title, post_type FROM {$wpdb->posts} WHERE ID=%d", $post_id));
  2494. if ($doc) {
  2495. $jobs[$k]->post_title = $doc->post_title;
  2496. $jobs[$k]->original_doc_id = $doc->ID;
  2497. $jobs[$k]->edit_link = get_edit_post_link($doc->ID);
  2498. $ldf = $sitepress->get_language_details($sitepress->get_element_language_details($post_id, 'post_' . $doc->post_type)->language_code);
  2499. } else {
  2500. $jobs[$k]->post_title = __("The original has been deleted!", "sitepress");
  2501. $jobs[$k]->original_doc_id = 0;
  2502. $jobs[$k]->edit_link = "";
  2503. $ldf['display_name'] = __("Deleted", "sitepress");
  2504. }
  2505. }
  2506. $ldt = $sitepress->get_language_details($row->language_code);
  2507. $jobs[$k]->lang_text = $ldf['display_name'] . ' &raquo; ' . $ldt['display_name'];
  2508. if($row->translation_service=='icanlocalize'){
  2509. $row->translator_name = ICL_Pro_Translation::get_translator_name($row->translator_id);
  2510. }
  2511. }
  2512. return $jobs;
  2513. }
  2514. function get_translation_job($job_id, $include_non_translatable_elements = false, $auto_assign = false, $revisions = 0){
  2515. global $wpdb, $sitepress, $current_user;
  2516. get_currentuserinfo();
  2517. $job = $wpdb->get_row($wpdb->prepare("
  2518. SELECT
  2519. j.rid, j.translator_id, j.translated, j.manager_id,
  2520. s.status, s.needs_update, s.translation_service,
  2521. t.trid, t.language_code, t.source_language_code
  2522. FROM {$wpdb->prefix}icl_translate_job j
  2523. JOIN {$wpdb->prefix}icl_translation_status s ON j.rid = s.rid
  2524. JOIN {$wpdb->prefix}icl_translations t ON s.translation_id = t.translation_id
  2525. WHERE j.job_id = %d", $job_id));
  2526. if (!$job) {
  2527. return false;
  2528. }
  2529. $post_id = $wpdb->get_var($wpdb->prepare("
  2530. SELECT field_data
  2531. FROM {$wpdb->prefix}icl_translate
  2532. WHERE job_id=%d and field_type='original_id'", $job_id));
  2533. $parts = explode('_', $post_id);
  2534. if ($parts[0] == 'external') {
  2535. $job->original_doc_id = $post_id;
  2536. $job->original_doc_title = base64_decode($wpdb->get_var($wpdb->prepare("
  2537. SELECT field_data
  2538. FROM {$wpdb->prefix}icl_translate
  2539. WHERE job_id=%d and field_type='name'", $job_id)));
  2540. if ($job->original_doc_title == "") {
  2541. // try the title field.
  2542. $job->original_doc_title = base64_decode($wpdb->get_var($wpdb->prepare("
  2543. SELECT field_data
  2544. FROM {$wpdb->prefix}icl_translate
  2545. WHERE job_id=%d and field_type='title'", $job_id)));
  2546. }
  2547. $job->original_post_type = $wpdb->get_var($wpdb->prepare("
  2548. SELECT element_type
  2549. FROM {$wpdb->prefix}icl_translations
  2550. WHERE trid=%d AND language_code=%s",
  2551. $job->trid, $job->source_language_code));
  2552. } else {
  2553. $original = $wpdb->get_row($wpdb->prepare("
  2554. SELECT t.element_id, p.post_title, p.post_type
  2555. FROM {$wpdb->prefix}icl_translations t
  2556. JOIN {$wpdb->posts} p ON t.element_id = p.ID AND t.trid = %d
  2557. WHERE t.source_language_code IS NULL", $job->trid));
  2558. if($original){
  2559. $job->original_doc_title = $original->post_title;
  2560. $job->original_doc_id = $original->element_id;
  2561. $job->original_post_type = $original->post_type;
  2562. }
  2563. }
  2564. $_ld = $sitepress->get_language_details($job->source_language_code);
  2565. $job->from_language = $_ld['display_name'];
  2566. $_ld = $sitepress->get_language_details($job->language_code);
  2567. $job->to_language = $_ld['display_name'];
  2568. if(!$include_non_translatable_elements){
  2569. $jelq = ' AND field_translate = 1';
  2570. }else{
  2571. $jelq = '';
  2572. }
  2573. $job->elements = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->prefix}icl_translate WHERE job_id = %d {$jelq} ORDER BY tid ASC", $job_id));
  2574. if($job->translator_id == 0 || $job->status == ICL_TM_WAITING_FOR_TRANSLATOR){
  2575. if($auto_assign){
  2576. $wpdb->update($wpdb->prefix . 'icl_translate_job', array('translator_id' => $this->current_translator->translator_id), array('job_id'=>$job_id));
  2577. $wpdb->update($wpdb->prefix . 'icl_translation_status',
  2578. array('translator_id' => $this->current_translator->translator_id, 'status' => ICL_TM_IN_PROGRESS),
  2579. array('rid'=>$job->rid)
  2580. );
  2581. }
  2582. } elseif ( $job->translator_id != @intval( $this->current_translator->translator_id ) && !defined( 'XMLRPC_REQUEST' ) && $job->manager_id != $current_user->ID ) {
  2583. // Returns the job for admin users
  2584. if ( $this->is_translator($current_user->ID) ) {
  2585. return $job;
  2586. } else {
  2587. static $erroronce = array();
  2588. if ( empty( $erroronce[ $job_id ] ) ) {
  2589. $this->messages[ ] = array(
  2590. 'type' => 'error',
  2591. 'text' => sprintf( __( "You can't translate this document. It's assigned to a different translator.<br /> Document: <strong>%s</strong> (ID = %d).", 'sitepress' ), $job->original_doc_title, $job_id )
  2592. );
  2593. $erroronce[ $job_id ] = true;
  2594. }
  2595. return false;
  2596. }
  2597. }
  2598. //do we have a previous version
  2599. if($revisions > 0){
  2600. $prev_version_job_id = $wpdb->get_var($wpdb->prepare("
  2601. SELECT MAX(job_id)
  2602. FROM {$wpdb->prefix}icl_translate_job
  2603. WHERE rid=%d AND job_id < %d", $job->rid, $job_id));
  2604. if($prev_version_job_id){
  2605. $job->prev_version = $this->get_translation_job($prev_version_job_id, false, false, $revisions - 1);
  2606. }
  2607. }
  2608. // allow adding custom elements
  2609. $job->elements = apply_filters('icl_job_elements', $job->elements, $post_id, $job_id);
  2610. return $job;
  2611. }
  2612. function get_translation_job_id($trid, $language_code){
  2613. global $wpdb, $sitepress;
  2614. $job_id = $wpdb->get_var($wpdb->prepare("
  2615. SELECT tj.job_id FROM {$wpdb->prefix}icl_translate_job tj
  2616. JOIN {$wpdb->prefix}icl_translation_status ts ON tj.rid = ts.rid
  2617. JOIN {$wpdb->prefix}icl_translations t ON ts.translation_id = t.translation_id
  2618. WHERE t.trid = %d AND t.language_code='%s'
  2619. ORDER BY tj.job_id DESC LIMIT 1
  2620. ", $trid, $language_code));
  2621. return $job_id;
  2622. }
  2623. function _save_translation_field($tid, $field){
  2624. global $wpdb;
  2625. $update['field_data_translated'] = $this->encode_field_data($field['data'], $field['format']);
  2626. if(isset($field['finished']) && $field['finished']){
  2627. $update['field_finished'] = 1;
  2628. }
  2629. $wpdb->update($wpdb->prefix . 'icl_translate', $update, array('tid'=>$tid));
  2630. }
  2631. function save_translation($data){
  2632. global $wpdb, $sitepress, $sitepress_settings, $ICL_Pro_Translation;
  2633. $new_post_id = false;
  2634. $is_incomplete = false;
  2635. foreach($data['fields'] as $field){
  2636. $this->_save_translation_field($field['tid'], $field);
  2637. if(!isset($field['finished']) || !$field['finished']){
  2638. $is_incomplete = true;
  2639. }
  2640. }
  2641. //check if translation job still exists
  2642. $job_count = $wpdb->get_var( $wpdb->prepare( "SELECT count(1) FROM {$wpdb->prefix}icl_translate_job WHERE job_id=%d", $data[ 'job_id' ] ) );
  2643. if ( $job_count == 0 ) {
  2644. wp_redirect( admin_url( sprintf( 'admin.php?page=%s', WPML_TM_FOLDER . '/menu/translations-queue.php', 'job-cancelled' ) ) );
  2645. exit;
  2646. }
  2647. if(!empty($data['complete']) && !$is_incomplete){
  2648. $wpdb->update($wpdb->prefix . 'icl_translate_job', array('translated'=>1), array('job_id'=>$data['job_id']));
  2649. $rid = $wpdb->get_var($wpdb->prepare("SELECT rid FROM {$wpdb->prefix}icl_translate_job WHERE job_id=%d", $data['job_id']));
  2650. $translation_id = $wpdb->get_var($wpdb->prepare("SELECT translation_id FROM {$wpdb->prefix}icl_translation_status WHERE rid=%d", $rid));
  2651. $wpdb->update($wpdb->prefix . 'icl_translation_status', array('status'=>ICL_TM_COMPLETE, 'needs_update'=>0), array('rid'=>$rid));
  2652. list($element_id, $trid) = $wpdb->get_row($wpdb->prepare("SELECT element_id, trid FROM {$wpdb->prefix}icl_translations WHERE translation_id=%d", $translation_id), ARRAY_N);
  2653. $job = $this->get_translation_job($data['job_id'], true);
  2654. $parts = explode('_', $job->original_doc_id);
  2655. if ($parts[0] == 'external') {
  2656. // Translations are saved in the string table for 'external' types
  2657. $id = array_pop($parts);
  2658. unset($parts[0]);
  2659. $type = implode('_', $parts);
  2660. $type = apply_filters('WPML_get_package_type', $type, $job->original_doc_id);
  2661. foreach($job->elements as $field){
  2662. if ($field->field_translate) {
  2663. if (function_exists('icl_st_is_registered_string')) {
  2664. $string_id = icl_st_is_registered_string($type, $id . '_' . $field->field_type);
  2665. if (!$string_id) {
  2666. icl_register_string($type, $id . '_' . $field->field_type, self::decode_field_data($field->field_data, $field->field_format));
  2667. $string_id = icl_st_is_registered_string($type, $id . '_' . $field->field_type);
  2668. }
  2669. if ($string_id) {
  2670. icl_add_string_translation($string_id, $job->language_code, self::decode_field_data($field->field_data_translated, $field->field_format), ICL_STRING_TRANSLATION_COMPLETE);
  2671. }
  2672. }
  2673. }
  2674. }
  2675. } else {
  2676. if(!is_null($element_id)){
  2677. $postarr['ID'] = $_POST['post_ID'] = $element_id;
  2678. }
  2679. foreach($job->elements as $field){
  2680. switch($field->field_type){
  2681. case 'title':
  2682. $postarr['post_title'] = self::decode_field_data($field->field_data_translated, $field->field_format);
  2683. break;
  2684. case 'body':
  2685. $postarr['post_content'] = self::decode_field_data($field->field_data_translated, $field->field_format);
  2686. break;
  2687. case 'excerpt':
  2688. $postarr['post_excerpt'] = self::decode_field_data($field->field_data_translated, $field->field_format);
  2689. break;
  2690. case 'URL':
  2691. $postarr['post_name'] = self::decode_field_data($field->field_data_translated, $field->field_format);
  2692. break;
  2693. default:
  2694. break;
  2695. }
  2696. }
  2697. $original_post = get_post($job->original_doc_id);
  2698. $postarr['post_author'] = $original_post->post_author;
  2699. $postarr['post_type'] = $original_post->post_type;
  2700. if($sitepress_settings['sync_comment_status']){
  2701. $postarr['comment_status'] = $original_post->comment_status;
  2702. }
  2703. if($sitepress_settings['sync_ping_status']){
  2704. $postarr['ping_status'] = $original_post->ping_status;
  2705. }
  2706. if($sitepress_settings['sync_page_ordering']){
  2707. $postarr['menu_order'] = $original_post->menu_order;
  2708. }
  2709. if($sitepress_settings['sync_private_flag'] && $original_post->post_status=='private'){
  2710. $postarr['post_status'] = 'private';
  2711. }
  2712. if($sitepress_settings['sync_post_date']){
  2713. $postarr['post_date'] = $original_post->post_date;
  2714. }
  2715. //set as draft or the same status as original post
  2716. $postarr['post_status'] = !$sitepress_settings['translated_document_status'] ? 'draft' : $original_post->post_status;
  2717. if($original_post->post_parent){
  2718. $post_parent_trid = $wpdb->get_var("SELECT trid FROM {$wpdb->prefix}icl_translations
  2719. WHERE element_type='post_{$original_post->post_type}' AND element_id='{$original_post->post_parent}'");
  2720. if($post_parent_trid){
  2721. $parent_id = $wpdb->get_var("SELECT element_id FROM {$wpdb->prefix}icl_translations
  2722. WHERE element_type='post_{$original_post->post_type}' AND trid='{$post_parent_trid}' AND language_code='{$job->language_code}'");
  2723. }
  2724. }
  2725. if(isset($parent_id) && $sitepress_settings['sync_page_parent']){
  2726. $_POST['post_parent'] = $postarr['post_parent'] = $parent_id;
  2727. $_POST['parent_id'] = $postarr['parent_id'] = $parent_id;
  2728. }
  2729. $_POST['trid'] = $trid;
  2730. $_POST['lang'] = $job->language_code;
  2731. $_POST['skip_sitepress_actions'] = true;
  2732. $postarr = apply_filters('icl_pre_save_pro_translation', $postarr);
  2733. if(isset($element_id)){ // it's an update so dont change the url
  2734. $postarr['post_name'] = $wpdb->get_var($wpdb->prepare("SELECT post_name FROM {$wpdb->posts} WHERE ID=%d", $element_id));
  2735. }
  2736. if(isset($element_id)){ // it's an update so dont change post date
  2737. $existing_post = get_post($element_id);
  2738. $postarr['post_date'] = $existing_post->post_date;
  2739. $postarr['post_date_gmt'] = $existing_post->post_date_gmt;
  2740. }
  2741. $new_post_id = $this->icl_insert_post( $postarr, $job->language_code );
  2742. icl_cache_clear( $postarr['post_type'] . 's_per_language' ); // clear post counter per language in cache
  2743. do_action('icl_pro_translation_saved', $new_post_id, $data['fields']);
  2744. // Allow identical slugs
  2745. $post_name = sanitize_title($postarr['post_title']);
  2746. // for Translated documents options:Page URL = Translate
  2747. if(isset($data['fields']['URL']['data']) && $data['fields']['URL']['data']){
  2748. $post_name = $data['fields']['URL']['data'];
  2749. }
  2750. $post_name_rewritten = $wpdb->get_var($wpdb->prepare("SELECT post_name FROM {$wpdb->posts} WHERE ID=%d", $new_post_id));
  2751. $post_name_base = $post_name;
  2752. if ( $post_name != $post_name_rewritten || $postarr[ 'post_type' ] == 'post' || $postarr[ 'post_type' ] == 'page' ) {
  2753. $incr = 1;
  2754. do{
  2755. $exists = $wpdb->get_var($wpdb->prepare("
  2756. SELECT p.ID FROM {$wpdb->posts} p
  2757. JOIN {$wpdb->prefix}icl_translations t ON t.element_id = p.ID
  2758. WHERE p.ID <> %d AND t.language_code = %s AND p.post_name=%s
  2759. ", $new_post_id, $job->language_code, $post_name));
  2760. if($exists){
  2761. $incr++;
  2762. }else{
  2763. break;
  2764. }
  2765. $post_name = $post_name_base . '-' . $incr;
  2766. }while($exists);
  2767. $wpdb->update($wpdb->posts, array('post_name' => $post_name), array('ID' => $new_post_id));
  2768. }
  2769. $ICL_Pro_Translation->_content_fix_links_to_translated_content($new_post_id, $job->language_code);
  2770. // update body translation with the links fixed
  2771. $new_post_content = $wpdb->get_var($wpdb->prepare("SELECT post_content FROM {$wpdb->posts} WHERE ID=%d", $new_post_id));
  2772. foreach($job->elements as $jel){
  2773. if($jel->field_type=='body'){
  2774. $fields_data_translated = $this->encode_field_data($new_post_content, $jel->field_format);
  2775. break;
  2776. }
  2777. }
  2778. $wpdb->update($wpdb->prefix.'icl_translate', array('field_data_translated'=>$fields_data_translated), array('job_id'=>$data['job_id'], 'field_type'=>'body'));
  2779. // set stickiness
  2780. //is the original post a sticky post?
  2781. remove_filter('option_sticky_posts', array($sitepress,'option_sticky_posts')); // remove filter used to get language relevant stickies. get them all
  2782. $sticky_posts = get_option('sticky_posts');
  2783. $is_original_sticky = $original_post->post_type=='post' && in_array($original_post->ID, $sticky_posts);
  2784. if($is_original_sticky && $sitepress_settings['sync_sticky_flag']){
  2785. stick_post($new_post_id);
  2786. }else{
  2787. if($original_post->post_type=='post' && !is_null($element_id)){
  2788. unstick_post($new_post_id); //just in case - if this is an update and the original post stckiness has changed since the post was sent to translation
  2789. }
  2790. }
  2791. //sync plugins texts
  2792. foreach((array)$this->settings['custom_fields_translation'] as $cf => $op){
  2793. if ($op == 1) {
  2794. update_post_meta($new_post_id, $cf, get_post_meta($original_post->ID,$cf,true));
  2795. }
  2796. }
  2797. // set specific custom fields
  2798. $copied_custom_fields = array('_top_nav_excluded', '_cms_nav_minihome');
  2799. foreach($copied_custom_fields as $ccf){
  2800. $val = get_post_meta($original_post->ID, $ccf, true);
  2801. update_post_meta($new_post_id, $ccf, $val);
  2802. }
  2803. // sync _wp_page_template
  2804. if($sitepress_settings['sync_page_template']){
  2805. $_wp_page_template = get_post_meta($original_post->ID, '_wp_page_template', true);
  2806. if(!empty($_wp_page_template)){
  2807. update_post_meta($new_post_id, '_wp_page_template', $_wp_page_template);
  2808. }
  2809. }
  2810. // sync post format
  2811. if ( $sitepress_settings[ 'sync_post_format' ] ) {
  2812. $_wp_post_format = get_post_format( $original_post->ID );
  2813. set_post_format( $new_post_id, $_wp_post_format );
  2814. }
  2815. // set the translated custom fields if we have any.
  2816. foreach((array)$this->settings['custom_fields_translation'] as $field_name => $val){
  2817. if ($val == 2) { // should be translated
  2818. // find it in the translation
  2819. foreach($job->elements as $name => $eldata) {
  2820. if ($eldata->field_data == $field_name) {
  2821. if (preg_match("/field-(.*?)-name/", $eldata->field_type, $match)) {
  2822. $field_id = $match[1];
  2823. foreach($job->elements as $k => $v){
  2824. if($v->field_type=='field-'.$field_id){
  2825. $field_translation = self::decode_field_data($v->field_data_translated, $v->field_format) ;
  2826. }
  2827. if($v->field_type=='field-'.$field_id.'-type'){
  2828. $field_type = $v->field_data;
  2829. }
  2830. }
  2831. if (isset($field_type) && $field_type == 'custom_field') {
  2832. $field_translation = str_replace ( '&#0A;', "\n", $field_translation );
  2833. // always decode html entities eg decode &amp; to &
  2834. $field_translation = html_entity_decode($field_translation);
  2835. update_post_meta($new_post_id, $field_name, $field_translation);
  2836. }
  2837. }
  2838. }
  2839. }
  2840. }
  2841. }
  2842. $link = get_edit_post_link($new_post_id);
  2843. if ($link == '') {
  2844. // the current user can't edit so just include permalink
  2845. $link = get_permalink($new_post_id);
  2846. }
  2847. if(is_null($element_id)){
  2848. $wpdb->delete ( $wpdb->prefix . 'icl_translations', array( 'element_id' => $new_post_id, 'element_type' => 'post_' . $postarr[ 'post_type' ] ) );
  2849. $wpdb->update ( $wpdb->prefix . 'icl_translations', array( 'element_id' => $new_post_id), array('translation_id' => $translation_id) );
  2850. $user_message = __('Translation added: ', 'sitepress') . '<a href="'.$link.'">' . $postarr['post_title'] . '</a>.';
  2851. }else{
  2852. $user_message = __('Translation updated: ', 'sitepress') . '<a href="'.$link.'">' . $postarr['post_title'] . '</a>.';
  2853. }
  2854. // synchronize the page parent for translations
  2855. if ($trid && $sitepress_settings['sync_page_parent']) {
  2856. $translations = $sitepress->get_element_translations($trid, 'post_' . $postarr['post_type']);
  2857. foreach ($translations as $target_lang => $target_details) {
  2858. if ($target_lang != $job->language_code) {
  2859. if ($target_details->element_id) {
  2860. $sitepress->fix_translated_parent($new_post_id, $target_details->element_id, $target_lang);
  2861. }
  2862. }
  2863. }
  2864. }
  2865. }
  2866. if(isset($user_message)) {
  2867. $this->messages[] = array(
  2868. 'type'=>'updated',
  2869. 'text' => $user_message
  2870. );
  2871. }
  2872. if($this->settings['notification']['completed'] != ICL_TM_NOTIFICATION_NONE){
  2873. require_once ICL_PLUGIN_PATH . '/inc/translation-management/tm-notification.class.php';
  2874. if($data['job_id']){
  2875. $tn_notification = new TM_Notification();
  2876. $tn_notification->work_complete($data['job_id'], !is_null($element_id));
  2877. }
  2878. }
  2879. self::set_page_url($new_post_id);
  2880. // redirect to jobs list
  2881. wp_redirect(admin_url(sprintf('admin.php?page=%s&%s=%d',
  2882. WPML_TM_FOLDER . '/menu/translations-queue.php', is_null($element_id) ? 'added' : 'updated', is_null($element_id) ? $new_post_id : $element_id )));
  2883. }else{
  2884. $this->messages[] = array('type'=>'updated', 'text' => __('Translation (incomplete) saved.', 'sitepress'));
  2885. }
  2886. /*
  2887. * After all previous functionality the terms form the job are assigned to the new post just created or updated.
  2888. * $overwrite is true by default for now.
  2889. */
  2890. $overwrite = true;
  2891. WPML_Terms_Translations::save_all_terms_from_job( $data[ 'job_id' ], $new_post_id, $overwrite );
  2892. // Set the posts mime type correctly.
  2893. if ( isset( $original_post ) && isset( $original_post->ID ) && $original_post->post_type == 'attachment' ) {
  2894. $attached_file = get_post_meta ( $original_post->ID, '_wp_attached_file', false );
  2895. update_post_meta ( $new_post_id, '_wp_attached_file', array_pop ( $attached_file ) );
  2896. $mime_type = get_post_mime_type ( $original_post->ID );
  2897. if ( $mime_type ) {
  2898. $wpdb->update ( $wpdb->posts, array( 'post_mime_type' => $mime_type ), array( 'ID' => $new_post_id ) );
  2899. }
  2900. }
  2901. do_action('icl_pro_translation_completed', $new_post_id);
  2902. }
  2903. // returns a front end link to a post according to the user access
  2904. // hide_empty - if current user doesn't have access to the link don't show at all
  2905. public static function tm_post_link($post_id, $anchor = false, $hide_empty = false){
  2906. global $current_user;
  2907. get_currentuserinfo();
  2908. $parts = explode('_', $post_id);
  2909. if ($parts[0] == 'external') {
  2910. $link = '';
  2911. return apply_filters('WPML_get_link', $link, $post_id, $anchor, $hide_empty);
  2912. }
  2913. if(false === $anchor){
  2914. $anchor = get_the_title($post_id);
  2915. }
  2916. $opost = get_post($post_id);
  2917. if(!$opost || ($opost->post_status == 'draft' || $opost->post_status == 'private' || $opost->post_status == 'trash') && $opost->post_author != $current_user->data->ID){
  2918. if($hide_empty){
  2919. $elink = '';
  2920. }else{
  2921. $elink = sprintf('<i>%s</i>', $anchor);
  2922. }
  2923. }else{
  2924. $elink = sprintf('<a href="%s">%s</a>', get_permalink($post_id), $anchor);
  2925. }
  2926. return $elink;
  2927. }
  2928. public function tm_post_permalink($post_id){
  2929. global $current_user;
  2930. get_currentuserinfo();
  2931. $parts = explode('_', $post_id);
  2932. if ($parts[0] == 'external') {
  2933. return '';
  2934. }
  2935. $opost = get_post($post_id);
  2936. if(!$opost || ($opost->post_status == 'draft' || $opost->post_status == 'private' || $opost->post_status == 'trash') && $opost->post_author != $current_user->data->ID){
  2937. $elink = '';
  2938. }else{
  2939. $elink = get_permalink($post_id);
  2940. }
  2941. return $elink;
  2942. }
  2943. // when the translated post was created, we have the job_id and need to update the job
  2944. function save_job_fields_from_post($job_id, $post){
  2945. global $wpdb, $sitepress;
  2946. $data['complete'] = 1;
  2947. $data['job_id'] = $job_id;
  2948. $job = $this->get_translation_job($job_id,1);
  2949. if(is_array($job->elements))
  2950. foreach($job->elements as $element){
  2951. $field_data = '';
  2952. switch($element->field_type){
  2953. case 'title':
  2954. $field_data = $this->encode_field_data($post->post_title, $element->field_format);
  2955. break;
  2956. case 'body':
  2957. $field_data = $this->encode_field_data($post->post_content, $element->field_format);
  2958. break;
  2959. case 'excerpt':
  2960. $field_data = $this->encode_field_data($post->post_excerpt, $element->field_format);
  2961. break;
  2962. default:
  2963. if(false !== strpos($element->field_type, 'field-') && !empty($this->settings['custom_fields_translation'])){
  2964. $cf_name = preg_replace('#^field-#', '', $element->field_type);
  2965. if(isset($this->settings['custom_fields_translation'][$cf_name])){
  2966. if($this->settings['custom_fields_translation'][$cf_name] == 1){ //copy
  2967. // @todo: Check when this code is run, it seems obsolete
  2968. $field_data = get_post_meta($job->original_doc_id, $cf_name, 1);
  2969. if(is_scalar($field_data))
  2970. $field_data = $this->encode_field_data($field_data, $element->field_format);
  2971. else $field_data = '';
  2972. }elseif($this->settings['custom_fields_translation'][$cf_name] == 2){ // translate
  2973. $field_data = get_post_meta($post->ID, $cf_name, 1);
  2974. if(is_scalar($field_data))
  2975. $field_data = $this->encode_field_data($field_data, $element->field_format);
  2976. else $field_data = '';
  2977. }
  2978. }
  2979. }else{
  2980. if(in_array($element->field_type, $sitepress->get_translatable_taxonomies(true, $post->post_type))){
  2981. $ids = array();
  2982. foreach($job->elements as $je){
  2983. if($je->field_type == $element->field_type .'_ids' ){
  2984. $ids = explode(',', $je->field_data);
  2985. }
  2986. }
  2987. $translated_tax_names = array();
  2988. foreach($ids as $id){
  2989. $translated_tax_id = icl_object_id($id, $element->field_type,false,$job->language_code);
  2990. if($translated_tax_id){
  2991. $translated_tax_names[] = $wpdb->get_var($wpdb->prepare("
  2992. SELECT t.name FROM {$wpdb->terms} t JOIN {$wpdb->term_taxonomy} x ON t.term_id = x.term_id
  2993. WHERE x.term_taxonomy_id = %d
  2994. ", $translated_tax_id));
  2995. }
  2996. }
  2997. $field_data = $this->encode_field_data($translated_tax_names, $element->field_format);
  2998. }
  2999. }
  3000. }
  3001. $wpdb->update($wpdb->prefix.'icl_translate',
  3002. array('field_data_translated'=>$field_data, 'field_finished'=>1),
  3003. array('tid'=>$element->tid)
  3004. );
  3005. }
  3006. $this->mark_job_done($job_id);
  3007. }
  3008. public static function determine_translated_taxonomies($elements, $taxonomy, $translated_language){
  3009. global $sitepress, $wpdb;
  3010. $translated_elements = false;
  3011. foreach($elements as $k=>$element){
  3012. $term = get_term_by('name', $element, $taxonomy);
  3013. if ($term) {
  3014. $trid = $sitepress->get_element_trid($term->term_taxonomy_id, 'tax_' . $taxonomy);
  3015. $translations = $sitepress->get_element_translations($trid, 'tax_' . $taxonomy);
  3016. if(isset($translations[$translated_language])){
  3017. $translated_elements[$k] = $translations[$translated_language]->name;
  3018. }else{
  3019. $translated_elements[$k] = '';
  3020. }
  3021. } else {
  3022. $translated_elements[$k] = '';
  3023. }
  3024. }
  3025. return $translated_elements;
  3026. }
  3027. function mark_job_done($job_id){
  3028. global $wpdb;
  3029. $wpdb->update($wpdb->prefix.'icl_translate_job', array('translated'=>1), array('job_id'=>$job_id));
  3030. $wpdb->update($wpdb->prefix.'icl_translate', array('field_finished'=>1), array('job_id'=>$job_id));
  3031. }
  3032. function resign_translator($job_id){
  3033. global $wpdb;
  3034. list($translator_id, $rid) = $wpdb->get_row($wpdb->prepare("SELECT translator_id, rid FROM {$wpdb->prefix}icl_translate_job WHERE job_id=%d", $job_id), ARRAY_N);
  3035. if(!empty($translator_id)){
  3036. if($this->settings['notification']['resigned'] != ICL_TM_NOTIFICATION_NONE){
  3037. require_once ICL_PLUGIN_PATH . '/inc/translation-management/tm-notification.class.php';
  3038. if($job_id){
  3039. $tn_notification = new TM_Notification();
  3040. $tn_notification->translator_resigned($translator_id, $job_id);
  3041. }
  3042. }
  3043. }
  3044. $wpdb->update($wpdb->prefix.'icl_translate_job', array('translator_id'=>0), array('job_id'=>$job_id));
  3045. $wpdb->update($wpdb->prefix.'icl_translation_status', array('translator_id'=>0, 'status'=>ICL_TM_WAITING_FOR_TRANSLATOR), array('rid'=>$rid));
  3046. }
  3047. function remove_translation_job($job_id, $new_translation_status = ICL_TM_WAITING_FOR_TRANSLATOR, $new_translator_id = 0){
  3048. global $wpdb;
  3049. list($prev_translator_id, $rid) = $wpdb->get_row($wpdb->prepare("SELECT translator_id, rid FROM {$wpdb->prefix}icl_translate_job WHERE job_id=%d", $job_id), ARRAY_N);
  3050. $wpdb->update($wpdb->prefix . 'icl_translate_job', array('translator_id' => $new_translator_id), array('job_id' => $job_id));
  3051. $wpdb->update($wpdb->prefix . 'icl_translate', array('field_data_translated' => '', 'field_finished' => 0), array('job_id' => $job_id));
  3052. $error = false;
  3053. if($rid){
  3054. $wpdb->update($wpdb->prefix . 'icl_translation_status', array('status' => $new_translation_status, 'translator_id' => $new_translator_id), array('rid' => $rid));
  3055. if($this->settings['notification']['resigned'] == ICL_TM_NOTIFICATION_IMMEDIATELY && !empty($prev_translator_id)){
  3056. require_once ICL_PLUGIN_PATH . '/inc/translation-management/tm-notification.class.php';
  3057. $tn_notification = new TM_Notification();
  3058. $tn_notification->translator_removed($prev_translator_id, $job_id);
  3059. $tn_notification->mail_queue();
  3060. }
  3061. }else{
  3062. $error = sprintf(__('Translation entry not found for: %d', 'wpml-translation-management'), $job_id);
  3063. }
  3064. }
  3065. function abort_translation(){
  3066. global $wpdb;
  3067. $job_id = $_POST['job_id'];
  3068. $error = '';
  3069. $message = '';
  3070. $error = $this->remove_translation_job($job_id, ICL_TM_WAITING_FOR_TRANSLATOR, 0);
  3071. if(!$error){
  3072. $message = __('Job removed', 'wpml-translation-management');
  3073. }
  3074. echo json_encode(array('message' => $message, 'error' => $error));
  3075. exit;
  3076. }
  3077. // $translation_id - int or array
  3078. function cancel_translation_request($translation_id){
  3079. global $wpdb;
  3080. if(is_array($translation_id)){
  3081. foreach($translation_id as $id){
  3082. $this->cancel_translation_request($id);
  3083. }
  3084. }else{
  3085. list($rid, $translator_id) = $wpdb->get_row($wpdb->prepare("SELECT rid, translator_id FROM {$wpdb->prefix}icl_translation_status WHERE translation_id=%d", $translation_id), ARRAY_N);
  3086. $job_id = $wpdb->get_var($wpdb->prepare("SELECT job_id FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d AND revision IS NULL ", $rid));
  3087. if($this->settings['notification']['resigned'] == ICL_TM_NOTIFICATION_IMMEDIATELY && !empty($translator_id)){
  3088. require_once ICL_PLUGIN_PATH . '/inc/translation-management/tm-notification.class.php';
  3089. $tn_notification = new TM_Notification();
  3090. $tn_notification->translator_removed($translator_id, $job_id);
  3091. $tn_notification->mail_queue();
  3092. }
  3093. $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}icl_translate_job WHERE job_id=%d", $job_id));
  3094. $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}icl_translate WHERE job_id=%d", $job_id));
  3095. $max_job_id = $wpdb->get_var($wpdb->prepare("SELECT MAX(job_id) FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d", $rid));
  3096. if($max_job_id){
  3097. $wpdb->query($wpdb->prepare("UPDATE {$wpdb->prefix}icl_translate_job SET revision = NULL WHERE job_id=%d", $max_job_id));
  3098. $_prevstate = $wpdb->get_var($wpdb->prepare("SELECT _prevstate FROM {$wpdb->prefix}icl_translation_status WHERE translation_id = %d", $translation_id));
  3099. if(!empty($_prevstate)){
  3100. $_prevstate = unserialize($_prevstate);
  3101. $wpdb->update($wpdb->prefix . 'icl_translation_status',
  3102. array(
  3103. 'status' => $_prevstate['status'],
  3104. 'translator_id' => $_prevstate['translator_id'],
  3105. 'needs_update' => $_prevstate['needs_update'],
  3106. 'md5' => $_prevstate['md5'],
  3107. 'translation_service' => $_prevstate['translation_service'],
  3108. 'translation_package' => $_prevstate['translation_package'],
  3109. 'timestamp' => $_prevstate['timestamp'],
  3110. 'links_fixed' => $_prevstate['links_fixed']
  3111. ),
  3112. array('translation_id'=>$translation_id)
  3113. );
  3114. }
  3115. }else{
  3116. $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}icl_translation_status WHERE translation_id=%d", $translation_id));
  3117. }
  3118. // delete record from icl_translations if trid is null
  3119. $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}icl_translations WHERE translation_id=%d AND element_id IS NULL", $translation_id));
  3120. }
  3121. }
  3122. function _array_keys_recursive( $arr )
  3123. {
  3124. $arr_rec_ret = array();
  3125. foreach ( (array)$arr as $k => $v ) {
  3126. if ( is_array( $v ) ) {
  3127. $arr_rec_ret[ $k ] = $this->_array_keys_recursive( $v );
  3128. } else {
  3129. $arr_rec_ret[$k] = $v;
  3130. }
  3131. }
  3132. return $arr_rec_ret;
  3133. }
  3134. function _read_admin_texts_recursive(&$arr, $keys, $config_contexts){
  3135. if(!is_numeric(key($keys))){
  3136. $_keys = array($keys);
  3137. $keys = $_keys;
  3138. unset($_keys);
  3139. }
  3140. if($keys) {
  3141. foreach($keys as $key){
  3142. if(isset($key['key'])){
  3143. $this->_read_admin_texts_recursive($arr, $key['key'], $config_contexts);
  3144. }else{
  3145. if(isset($config_contexts['wpml-config']['admin-texts']['key'][$key[ 'attr' ][ 'name' ]])) {
  3146. $context = $config_contexts['wpml-config']['admin-texts']['key'][$key[ 'attr' ][ 'name' ]]['slug'];
  3147. $arr[$context][$key['attr']['name']] = 1;
  3148. }
  3149. }
  3150. }
  3151. }
  3152. return $arr;
  3153. }
  3154. function _register_string_recursive($key, $value, $arr, $prefix = '', $suffix){
  3155. if(is_scalar($value)){
  3156. if(!empty($value) && $arr == 1){
  3157. icl_register_string('admin_texts_' . $suffix, $prefix . $key , $value);
  3158. }
  3159. }else{
  3160. if(!is_null($value)){
  3161. foreach($value as $sub_key=>$sub_value){
  3162. $is_wildcard = false;
  3163. if($arr && !isset($arr[$sub_key])){ //wildcard
  3164. if ( is_array( $arr ) ) {
  3165. $array_keys = array_keys( $arr );
  3166. if ( is_array( $array_keys ) ) {
  3167. foreach( $array_keys as $array_key){
  3168. $array_key = str_replace('/', '\/', $array_key);
  3169. $array_key = '/' . str_replace('*', '(.*)', $array_key) . '/';
  3170. if(preg_match($array_key, $sub_key)){
  3171. $is_wildcard = true;
  3172. $arr[$sub_key] = true; //placeholder
  3173. break;
  3174. };
  3175. }
  3176. }
  3177. }
  3178. }
  3179. if(isset($arr[$sub_key]) || $is_wildcard){
  3180. $this->_register_string_recursive($sub_key, $sub_value, $arr[$sub_key], $prefix . '[' . $key .']', $suffix);
  3181. }
  3182. }
  3183. }
  3184. }
  3185. }
  3186. function _read_settings_recursive($config_settings){
  3187. $iclsettings = false;
  3188. foreach($config_settings as $s){
  3189. if(isset($s['key'])){
  3190. if(!is_numeric(key($s['key']))){
  3191. $sub_key[0] = $s['key'];
  3192. }else{
  3193. $sub_key = $s['key'];
  3194. }
  3195. $read_settings_recursive = $this->_read_settings_recursive( $sub_key );
  3196. if($read_settings_recursive) {
  3197. $iclsettings[$s['attr']['name']] = $read_settings_recursive;
  3198. }
  3199. }else{
  3200. $iclsettings[$s['attr']['name']] = $s['value'];
  3201. }
  3202. }
  3203. return $iclsettings;
  3204. }
  3205. function render_option_writes($name, $value, $key=''){
  3206. if(!defined('WPML_ST_FOLDER')) return;
  3207. //Cache the previous option, when called recursively
  3208. static $option = false;
  3209. if(!$key){
  3210. $option = maybe_unserialize(get_option($name));
  3211. if(is_object($option)){
  3212. $option = (array)$option;
  3213. }
  3214. }
  3215. $admin_option_names = get_option('_icl_admin_option_names');
  3216. // determine theme/plugin name (string context)
  3217. $es_context = '';
  3218. foreach($admin_option_names as $context => $element) {
  3219. $found = false;
  3220. foreach ( (array)$element as $slug => $options ) {
  3221. $found = false;
  3222. foreach ( (array)$options as $option_key => $option_value ) {
  3223. $found = false;
  3224. $es_context = '';
  3225. if( $option_key == $name ) {
  3226. if ( is_scalar( $option_value ) ) {
  3227. $es_context = 'admin_texts_' . $context . '_' . $slug;
  3228. $found = true;
  3229. } elseif ( is_array( $option_value ) && is_array( $value ) && ( $option_value == $value ) ) {
  3230. $es_context = 'admin_texts_' . $context . '_' . $slug;
  3231. $found = true;
  3232. }
  3233. }
  3234. if($found) break;
  3235. }
  3236. if($found) break;
  3237. }
  3238. if($found) break;
  3239. }
  3240. echo '<ul class="icl_tm_admin_options">';
  3241. echo '<li>';
  3242. $context_html = '';
  3243. if(!$key){
  3244. $context_html = '[' . $context . ': ' . $slug . '] ';
  3245. }
  3246. if(is_scalar($value)){
  3247. $int = preg_match_all('#\[([^\]]+)\]#', $key, $matches);
  3248. if(count($matches[1]) > 1){
  3249. $o_value = $option;
  3250. for($i = 1; $i < count($matches[1]); $i++){
  3251. $o_value = $o_value[$matches[1][$i]];
  3252. }
  3253. $o_value = $o_value[$name];
  3254. $edit_link = '';
  3255. }else{
  3256. if(is_scalar($option)){
  3257. $o_value = $option;
  3258. }elseif(isset($option[$name])){
  3259. $o_value = $option[$name];
  3260. }else{
  3261. $o_value = '';
  3262. }
  3263. if(!$key){
  3264. if(icl_st_is_registered_string($es_context, $name)) {
  3265. $edit_link = '[<a href="'.admin_url('admin.php?page='.WPML_ST_FOLDER.'/menu/string-translation.php&context='.$es_context) . '">' . __('translate', 'sitepress') . '</a>]';
  3266. } else {
  3267. $edit_link = '<div class="updated below-h2">' . __('string not registered', 'sitepress') . '</div>';
  3268. }
  3269. }else{
  3270. $edit_link = '';
  3271. }
  3272. }
  3273. if(false !== strpos($name, '*')){
  3274. $o_value = '<span style="color:#bbb">{{ ' . __('Multiple options', 'wpml-translation-management') . ' }}</span>';
  3275. }else{
  3276. $o_value = esc_html($o_value);
  3277. if(strlen($o_value) > 200){
  3278. $o_value = substr($o_value, 0, 200) . ' ...';
  3279. }
  3280. }
  3281. echo '<li>' . $context_html . $name . ': <i>' . $o_value . '</i> ' . $edit_link . '</li>';
  3282. }else{
  3283. $edit_link = '[<a href="'.admin_url('admin.php?page='.WPML_ST_FOLDER.'/menu/string-translation.php&context='.$es_context) . '">' . __('translate', 'sitepress') . '</a>]';
  3284. echo '<strong>' . $context_html . $name . '</strong> ' . $edit_link;
  3285. if(!icl_st_is_registered_string($es_context, $name)) {
  3286. $notice = '<div class="updated below-h2">' . __('some strings might be not registered', 'sitepress') . '</div>';
  3287. echo $notice;
  3288. }
  3289. foreach((array)$value as $o_key=>$o_value){
  3290. $this->render_option_writes($o_key, $o_value, $o_key . '[' . $name . ']');
  3291. }
  3292. //Reset cached data
  3293. $option = false;
  3294. }
  3295. echo '</li>';
  3296. echo '</ul>';
  3297. }
  3298. function _override_get_translatable_documents($types){
  3299. global $wp_post_types;
  3300. foreach($types as $k=>$type){
  3301. if(isset($this->settings['custom_types_readonly_config'][$k]) && !$this->settings['custom_types_readonly_config'][$k]){
  3302. unset($types[$k]);
  3303. }
  3304. }
  3305. foreach($this->settings['custom_types_readonly_config'] as $cp=>$translate){
  3306. if($translate && !isset($types[$cp]) && isset($wp_post_types[$cp])){
  3307. $types[$cp] = $wp_post_types[$cp];
  3308. }
  3309. }
  3310. return $types;
  3311. }
  3312. function _override_get_translatable_taxonomies($taxs_obj_type){
  3313. global $wp_taxonomies, $sitepress;
  3314. $taxs = $taxs_obj_type['taxs'];
  3315. $object_type = $taxs_obj_type['object_type'];
  3316. foreach($taxs as $k=>$tax){
  3317. if(!$sitepress->is_translated_taxonomy($tax)){
  3318. unset($taxs[$k]);
  3319. }
  3320. }
  3321. foreach($this->settings['taxonomies_readonly_config'] as $tx=>$translate){
  3322. if($translate && !in_array($tx, $taxs) && isset($wp_taxonomies[$tx]) && in_array($object_type, $wp_taxonomies[$tx]->object_type)){
  3323. $taxs[] = $tx;
  3324. }
  3325. }
  3326. $ret = array('taxs'=>$taxs, 'object_type'=>$taxs_obj_type['object_type']);
  3327. return $ret;
  3328. }
  3329. public static function icanlocalize_service_info($info = array()) {
  3330. global $sitepress;
  3331. $return = array();
  3332. $return['name'] = 'ICanLocalize';
  3333. $return['logo'] = ICL_PLUGIN_URL . '/res/img/web_logo_small.png';
  3334. $return['setup_url'] = $sitepress->create_icl_popup_link('@select-translators;from_replace;to_replace@', array('ar' => 1), true);
  3335. $return['header'] = __('Looking for a quality translation service?', 'sitepress');
  3336. $return['description'] = __('ICanLocalize, the makers of WPML, offers excellent<br /> human service done by expert translators, for only $0.09 per word.', 'sitepress');
  3337. $info['icanlocalize'] = $return;
  3338. return $info;
  3339. }
  3340. public function clear_cache() {
  3341. global $wpdb;
  3342. delete_option($wpdb->prefix . 'icl_translators_cached');
  3343. delete_option($wpdb->prefix . 'icl_non_translators_cached');
  3344. }
  3345. // shows post content for visual mode (iframe) in translation editor
  3346. function _show_post_content(){
  3347. global $tinymce_version;
  3348. $data = '';
  3349. $post = $this->_get_post($_GET['post_id']);
  3350. if($post){
  3351. if(0 === strpos($_GET['field_type'], 'field-')){
  3352. // A Types field
  3353. $data = get_post_meta($_GET['post_id'], preg_replace('#^field-#', '', $_GET['field_type']), true);
  3354. }else{
  3355. if (isset($post->string_data[$_GET['field_type']])) {
  3356. // A string from an external
  3357. $data = $post->string_data[$_GET['field_type']];
  3358. } else {
  3359. // The post body.
  3360. remove_filter('the_content', 'do_shortcode', 11);
  3361. $data = apply_filters('the_content', $post->post_content);
  3362. }
  3363. }
  3364. if(@intval($_GET['rtl'])){
  3365. $rtl = ' dir="rtl"';
  3366. }else{
  3367. $rtl = '';
  3368. }
  3369. echo '<html'.$rtl.'>';
  3370. echo '<head>';
  3371. $csss = array(
  3372. '/' . WPINC . '/js/tinymce/themes/advanced/skins/wp_theme/content.css?ver='.$tinymce_version,
  3373. '/' . WPINC . '/js/tinymce/plugins/spellchecker/css/content.css?ver='.$tinymce_version,
  3374. '/' . WPINC . '/js/tinymce/plugins/wordpress/css/content.css?ver='.$tinymce_version
  3375. );
  3376. foreach($csss as $css){
  3377. echo '<link rel="stylesheet" href="'.site_url() . $css . '">' . "\n";
  3378. }
  3379. echo '</head>';
  3380. echo '<body>';
  3381. echo $data;
  3382. echo '</body>';
  3383. echo '</html>';
  3384. exit;
  3385. }else{
  3386. wp_die(__('Post not found!', 'sitepress'));
  3387. }
  3388. exit;
  3389. }
  3390. function _user_search(){
  3391. $q = $_POST['q'];
  3392. $non_translators = self::get_blog_not_translators();
  3393. $matched_users = array();
  3394. foreach($non_translators as $t){
  3395. if(false !== stripos($t->user_login, $q) || false !== stripos($t->display_name, $q)){
  3396. $matched_users[] = $t;
  3397. }
  3398. if(count($matched_users) == 100) break;
  3399. }
  3400. if(!empty($matched_users)){
  3401. $cssheight = count($matched_users) > 10 ? '200' : 20*count($matched_users) + 5;
  3402. echo '<select size="10" class="icl_tm_auto_suggest_dd" style="height:'.$cssheight.'px">';
  3403. foreach($matched_users as $u){
  3404. echo '<option value="' . $u->ID . '|' . esc_attr($u->display_name).'">'.$u->display_name . ' ('.$u->user_login.')'.'</option>';
  3405. }
  3406. echo '</select>';
  3407. }else{
  3408. echo '&nbsp;<span id="icl_user_src_nf">';
  3409. _e('No matches', 'sitepress');
  3410. echo '</span>';
  3411. }
  3412. exit;
  3413. }
  3414. // set slug according to user preference
  3415. static function set_page_url($post_id){
  3416. global $sitepress, $sitepress_settings, $wpdb;
  3417. if($sitepress_settings['translated_document_page_url'] == 'copy-encoded'){
  3418. $post = $wpdb->get_row($wpdb->prepare("SELECT post_type FROM {$wpdb->posts} WHERE ID=%d", $post_id));
  3419. $translation_row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}icl_translations WHERE element_id=%d AND element_type=%s", $post_id, 'post_' . $post->post_type));
  3420. $encode_url = $wpdb->get_var($wpdb->prepare("SELECT encode_url FROM {$wpdb->prefix}icl_languages WHERE code=%s", $translation_row->language_code));
  3421. if($encode_url){
  3422. $trid = $sitepress->get_element_trid($post_id, 'post_' . $post->post_type);
  3423. $original_post_id = $wpdb->get_var($wpdb->prepare("SELECT element_id FROM {$wpdb->prefix}icl_translations WHERE trid=%d AND source_language_code IS NULL", $trid));
  3424. $post_name_original = $wpdb->get_var($wpdb->prepare("SELECT post_name FROM {$wpdb->posts} WHERE ID = %d", $original_post_id));
  3425. $post_name_to_be = $post_name_original;
  3426. $taken = true;
  3427. $incr = 1;
  3428. do{
  3429. $taken = $wpdb->get_var($wpdb->prepare("
  3430. SELECT ID FROM {$wpdb->posts} p
  3431. JOIN {$wpdb->prefix}icl_translations t ON p.ID = t.element_id
  3432. WHERE ID <> %d AND t.element_type = %s AND t.language_code = %s AND p.post_name = %s
  3433. ", $post_id, 'post_' . $post->post_type, $translation_row->language_code, $post_name_to_be ));
  3434. if($taken){
  3435. $incr++;
  3436. $post_name_to_be = $post_name_original . '-' . $incr;
  3437. }else{
  3438. $taken = false;
  3439. }
  3440. }while($taken == true);
  3441. $wpdb->update($wpdb->posts, array('post_name' => $post_name_to_be), array('ID' => $post_id));
  3442. }
  3443. }
  3444. }
  3445. /**
  3446. * @param $postarr
  3447. *
  3448. * @param $lang
  3449. *
  3450. * @return int|WP_Error
  3451. */
  3452. public function icl_insert_post( $postarr, $lang )
  3453. {
  3454. global $sitepress;
  3455. $current_language = $sitepress->get_current_language();
  3456. $sitepress->switch_lang( $lang, false );
  3457. $new_post_id = wp_insert_post( $postarr );
  3458. $sitepress->switch_lang( $current_language, false );
  3459. return $new_post_id;
  3460. }
  3461. /**
  3462. * Add missing language to posts
  3463. *
  3464. * @param array $post_types
  3465. */
  3466. protected function add_missing_language_to_posts( $post_types )
  3467. {
  3468. global $wpdb;
  3469. //This will be improved when it will be possible to pass an array to the IN clause
  3470. $posts_prepared = "SELECT ID, post_type, post_status FROM {$wpdb->posts} WHERE post_type IN ('" . implode("', '", esc_sql($post_types)) . "')";
  3471. $posts = $wpdb->get_results( $posts_prepared );
  3472. if ( $posts ) {
  3473. foreach ( $posts as $post ) {
  3474. $this->add_missing_language_to_post( $post );
  3475. }
  3476. }
  3477. }
  3478. /**
  3479. * Add missing language to a given post
  3480. *
  3481. * @param WP_Post $post
  3482. */
  3483. protected function add_missing_language_to_post( $post ) {
  3484. global $sitepress, $wpdb;
  3485. $query_prepared = $wpdb->prepare( "SELECT translation_id, language_code FROM {$wpdb->prefix}icl_translations WHERE element_type=%s AND element_id=%d", array( 'post_' . $post->post_type, $post->ID ) );
  3486. $query_results = $wpdb->get_row( $query_prepared );
  3487. //if translation exists
  3488. if (!is_null($query_results)){
  3489. $translation_id = $query_results->translation_id;
  3490. $language_code = $query_results->language_code;
  3491. }else{
  3492. $translation_id = null;
  3493. $language_code = null;
  3494. }
  3495. $urls = $sitepress->get_setting( 'urls' );
  3496. $is_root_page = $urls && isset( $urls[ 'root_page' ] ) && $urls[ 'root_page' ] == $post->ID;
  3497. $default_language = $sitepress->get_default_language();
  3498. if ( !$translation_id && !$is_root_page && !in_array( $post->post_status, array( 'auto-draft' ) ) ) {
  3499. $sitepress->set_element_language_details( $post->ID, 'post_' . $post->post_type, null, $default_language );
  3500. } elseif ( $translation_id && $is_root_page ) {
  3501. $trid = $sitepress->get_element_trid( $post->ID, 'post_' . $post->post_type );
  3502. if ( $trid ) {
  3503. $sitepress->delete_element_translation( $trid, 'post_' . $post->post_type );
  3504. }
  3505. } elseif ( $translation_id && !$language_code && $default_language ) {
  3506. $where = array( 'translation_id' => $translation_id );
  3507. $data = array( 'language_code' => $default_language );
  3508. $wpdb->update( $wpdb->prefix . 'icl_translations', $data, $where );
  3509. }
  3510. }
  3511. /**
  3512. * Add missing language to taxonomies
  3513. *
  3514. * @param array $post_types
  3515. */
  3516. protected function add_missing_language_to_taxonomies( $post_types )
  3517. {
  3518. global $sitepress, $wpdb;
  3519. $taxonomy_types = array();
  3520. foreach ( $post_types as $post_type ) {
  3521. $taxonomy_types = array_merge( $sitepress->get_translatable_taxonomies( true, $post_type ), $taxonomy_types );
  3522. }
  3523. $taxonomy_types = array_unique( $taxonomy_types );
  3524. $taxonomies = $wpdb->get_results( "SELECT taxonomy, term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE taxonomy IN ('" . join( "','", $taxonomy_types ) . "')" );
  3525. if ( $taxonomies ) {
  3526. foreach ( $taxonomies as $taxonomy ) {
  3527. $this->add_missing_language_to_taxonomy( $taxonomy );
  3528. }
  3529. }
  3530. }
  3531. /**
  3532. * Add missing language to a given taxonomy
  3533. *
  3534. * @param OBJECT $taxonomy
  3535. */
  3536. protected function add_missing_language_to_taxonomy( $taxonomy )
  3537. {
  3538. global $sitepress, $wpdb;
  3539. $tid_prepared = $wpdb->prepare( "SELECT translation_id FROM {$wpdb->prefix}icl_translations WHERE element_type=%s AND element_id=%d", 'tax_' . $taxonomy->taxonomy, $taxonomy->term_taxonomy_id );
  3540. $tid = $wpdb->get_var( $tid_prepared );
  3541. if ( !$tid ) {
  3542. $sitepress->set_element_language_details( $taxonomy->term_taxonomy_id, 'tax_' . $taxonomy->taxonomy, null, $sitepress->get_default_language() );
  3543. }
  3544. }
  3545. /**
  3546. * Add missing language to comments
  3547. */
  3548. protected function add_missing_language_to_comments()
  3549. {
  3550. global $sitepress, $wpdb;
  3551. $comment_ids_prepared = $wpdb->prepare( "SELECT c.comment_ID FROM {$wpdb->comments} c LEFT JOIN {$wpdb->prefix}icl_translations t ON t.element_id = c.comment_id AND t.element_type=%s WHERE t.element_id IS NULL", 'comment' );
  3552. $comment_ids = $wpdb->get_col( $comment_ids_prepared );
  3553. if ( $comment_ids ) {
  3554. foreach ( $comment_ids as $comment_id ) {
  3555. $sitepress->set_element_language_details( $comment_id, 'comment', null, $sitepress->get_default_language() );
  3556. }
  3557. }
  3558. }
  3559. /**
  3560. * Add missing language information to entities that don't have this
  3561. * information configured.
  3562. */
  3563. public function add_missing_language_information()
  3564. {
  3565. global $sitepress;
  3566. $translatable_documents = array_keys( $sitepress->get_translatable_documents(true) );
  3567. if( $translatable_documents ) {
  3568. $this->add_missing_language_to_posts( $translatable_documents );
  3569. $this->add_missing_language_to_taxonomies( $translatable_documents );
  3570. }
  3571. $this->add_missing_language_to_comments();
  3572. }
  3573. }