Removed some debug code #118456
[project/i18n.git] / translation / translation.module
1 <?php
2 // $Id$
3
4 /**
5 * Internationalization (i18n) package.
6 *
7 * Translation module: translation
8 *
9 * @author Jose A. Reyero, 2004, http://www.reyero.net
10 *
11 */
12
13 // Status for translations
14 define('TRANSLATION_STATUS_NONE', 0);
15 define('TRANSLATION_STATUS_SOURCE', 1);
16 define('TRANSLATION_STATUS_WORKING', 2);
17 define('TRANSLATION_STATUS_TRANSLATED', 3);
18 define('TRANSLATION_STATUS_UPDATED', 4);
19
20 /**
21 * Implementation of hook_help().
22 */
23 function translation_help($section = 'admin/help#translation' ) {
24 switch ($section) {
25 case 'admin/help#translation' :
26 $output = '<p>'.t('This module is part of i18n package and provides support for translation relationships.').'</p>';
27 $output .= '<p>'.t('The objects you can define translation relationships for are:').'</p>';
28 $output .= '<ul>';
29 $output .= '<li>'.t('Nodes.').'</li>';
30 $output .= '<li>'.t('Taxonomy Terms').'</li>';
31 $output .= '</ul>';
32 $output .= '<p>'.t('Additional features:').'</p>';
33 $output .= '<ul>';
34 $output .= '<li>'.t('<i>Translations</i> block that looks like the language switcher provided by i18n module but also links to translations when available.').'</li>';
35 $output .= '<li>'.t('Basic translation workflow and administration page for content translation.').'</li>';
36 $output .= '<li>'.t('Links for node translations that can be displayed below each node, depending on module settings.').'</li>';
37 $output .= '</ul>';
38 $output .= '<p>'.t('For more information please read the <a href="@i18n">on-line help pages</a>.', array('@i18n' =>'http://drupal.org/node/31631')) .'</p>';
39 return $output;
40 break;
41 case 'admin/access#translation':
42 $output = t('<h2>Translations</h2>');
43 $output = t('<strong>translate nodes</strong> <p>This one, combined with create content permissions, will allow to create node translation</p>');
44 }
45 return $output;
46 }
47
48 /**
49 * Implementation of hook_menu().
50 */
51 function translation_menu($may_cache) {
52 $items = array();
53
54 if ($may_cache) {
55 $items[] = array(
56 'path' => 'admin/settings/i18n/translation',
57 'title' => t('Translation'),
58 'callback' => 'drupal_get_form',
59 'callback arguments' => array('translation_admin_settings'),
60 'access' => user_access('administer site configuration'),
61 'type' => MENU_LOCAL_TASK,
62 );
63 $items[] = array(
64 'path' => 'admin/content/translation',
65 'title' => t('Translations'),
66 'description' => t("Manage content translations."),
67 'callback' => 'translation_admin_content',
68 'position' => 'left',
69 'access' => user_access('translate nodes'));
70 $items[] = array('path' => 'admin/content/translation/overview', 'title' => t('List'),
71 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
72 }
73 else {
74 if (arg(0) == 'node' && is_numeric(arg(1)) && variable_get('i18n_node_'.translation_get_node_type(arg(1)), 0)) {
75 $access = user_access('translate nodes');
76 $type = MENU_LOCAL_TASK;
77 $items[] = array(
78 'path' => 'node/'. arg(1) .'/translation',
79 'title' => t('Translation'),
80 'callback' => 'translation_node_page',
81 'access' => $access,
82 'type' => $type,
83 'weight' => 3);
84 }
85 if(arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'taxonomy' && is_numeric(arg(3)) ) {
86 $items[] = array(
87 'path' => 'admin/content/taxonomy/'.arg(3).'/translation',
88 'title' => t('Translation'),
89 'callback' => 'translation_taxonomy_admin',
90 'access' => user_access('administer taxonomy'),
91 'type' => MENU_LOCAL_TASK);
92
93 }
94 // Special redirections and rewrite conditions
95 if( arg(0) == 'node' && arg(1) == 'add' && isset($_GET['translation']) && ($source_nid = $_GET['translation']) && isset($_GET['language']) && ($lang = $_GET['language']) && array_key_exists($lang, i18n_supported_languages()) ) {
96 // Special redirection for product types
97 if (arg(2) == 'product' && !arg(3) && is_numeric($source_nid) && $ptype = db_result(db_query("SELECT ptype FROM {ec_product} WHERE nid = %d", $source_nid))) {
98 drupal_goto(url("node/add/product/$ptype", "translation=$source_nid&language=$lang", NULL, TRUE));
99 }
100 // Change rewrite conditions when translating node
101 i18n_selection_mode('translation', db_escape_string($lang));
102 }
103 }
104
105 return $items;
106 }
107
108 /**
109 * Implementation of hook_perm
110 */
111 function translation_perm(){
112 return array('translate nodes');
113 }
114
115 /**
116 * Form builder function: settings form
117 */
118 function translation_admin_settings(){
119 $form['i18n_translation_links'] = array(
120 '#type' => 'radios',
121 '#title' => t('Language Management'),
122 '#default_value' => variable_get('i18n_translation_links', 0),
123 '#options' => array(t('Interface language depends on content.'), t('Interface language is independent')),
124 '#description' => t("Whether the whole page should switch language when clicking on a node translation or not."),
125 );
126 $form['i18n_translation_node_links'] = array(
127 '#type' => 'radios',
128 '#title' => t('Links to node translations'),
129 '#default_value' => variable_get('i18n_translation_node_links', 0),
130 '#options' => array(t('None.'), t('Main page only'), t('Teaser and Main page')),
131 '#description' => t("Links from nodes to translated versions."),
132 );
133 $form['i18n_translation_workflow'] = array(
134 '#type' => 'radios',
135 '#title' => t('Translation workflow'),
136 '#default_value' => variable_get('i18n_translation_workflow', 1),
137 '#options' => array(t('Disabled'), t('Enabled')),
138 '#description' => t("If enabled some worklow will be provided for content translation."),
139 );
140 return system_settings_form($form);
141 }
142
143 /**
144 * Implementation of hook_block().
145 *
146 * This is a simple language switcher which knows nothing about translations
147 */
148 function translation_block($op = 'list', $delta = 0) {
149 if ($op == 'list') {
150 $blocks[0]['info'] = t('Translations');
151 }
152 elseif($op == 'view') {
153 $blocks['subject'] = t('Languages');
154 $query = drupal_query_string_encode($_GET, array('q'));
155 $blocks['content'] = theme('item_list', translation_get_links($_GET['q'], empty($query) ? NULL : $query));
156 }
157
158 return $blocks;
159 }
160
161 /**
162 * Implementation of hook_form_alter().
163 */
164 function translation_form_alter($form_id, &$form) {
165 // Node add/edit form
166 if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id && variable_get('i18n_node_'.$form['type']['#value'], 0)) {
167 $node = $form['#node'];
168 $languages = i18n_supported_languages();
169 // Translation workflow, default
170 if ($node->nid && $node->translation) {
171 $translations = $node->translation;
172 }
173 elseif (isset($_GET['translation']) && user_access('translate nodes')) {
174 // We are translating a node: node/add/type/translation/nid/lang
175 $translation_nid = $_GET['translation'];
176 $language = $_GET['language'];
177
178 // Load the node to be translated and populate fields
179 $trans = node_load($translation_nid);
180 $form['i18n']['translation_nid'] = array('#type' => 'hidden', '#value' => $translation_nid);
181 $form['i18n']['language']['#default_value'] = $language;
182
183 // Do not allow language change
184 $form['i18n']['language']['#disabled'] = TRUE;
185 $form['i18n']['language']['#description'] = t('Language cannot be changed while creating a translation.');
186 //translation_node_populate_fields($trans, $form, $node);
187
188 if($trans->trid){
189 $form['i18n']['trid'] = array('#type' => 'hidden', '#value' => $trans->trid);
190 }
191 // Translations are taken from source node
192 $translations = $trans->translation;
193 $translations[$trans->language] = $trans;
194 }
195 // Display translations and restrict languages
196 if ($translations) {
197 // Unset invalid languages
198 foreach(array_keys($translations) as $lang) {
199 unset($form['i18n']['language']['#options'][$lang]);
200 }
201 // Add translation list
202 $form['i18n']['#title'] = t('Language and translations');
203 $form['i18n']['translations'] = array(
204 '#type' => 'markup',
205 '#value' => theme('translation_node_list', $translations, FALSE)
206 );
207 }
208 // Translation workflow
209 if (variable_get('i18n_translation_workflow', 1) && (user_access('translate nodes') || user_access('administer nodes'))) {
210 $form['i18n']['i18n_status'] = array(
211 '#type' => 'select',
212 '#title' => t('Translation workflow'),
213 '#options' => _translation_status(),
214 '#description' => t('Use the translation workflow to keep track of content that needs translation.'));
215 if($node->nid) {
216 $form['i18n']['i18n_status']['#default_value'] = isset($node->i18n_status) ? $node->i18n_status : TRANSLATION_STATUS_NONE;
217 } elseif(isset($trans)) {
218 $form['i18n']['i18n_status']['#default_value'] = TRANSLATION_STATUS_WORKING;
219 }
220 } else {
221 $form['i18n']['i18n_status'] = array('#type' => 'value', '#value' => isset($node->i18n_status) ? $node->i18n_status : TRANSLATION_STATUS_NONE);
222 }
223 // Clone files for original node ?
224 if (isset($trans) && is_array($trans->files) && count($trans->files)) {
225 $form['i18n']['translation_files'] = array(
226 '#type' => 'fieldset',
227 '#title' => t('Files from translated content'),
228 '#tree' => TRUE,
229 '#prefix' => '<div class="attachments">',
230 '#suffix' => '</div>',
231 '#theme' => 'upload_form_current',
232 '#description' => t('You can remove the files for this translation or keep the original files and translate the description.')
233 );
234 foreach($trans->files as $key => $file) {
235 $description = file_create_url((strpos($file->fid, 'upload') === false ? $file->filepath : file_create_filename($file->filename, file_create_path())));
236 $description = "<small>". check_plain($description) ."</small>";
237 $form['i18n']['translation_files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => (strlen($file->description)) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description );
238 $form['i18n']['translation_files'][$key]['size'] = array('#type' => 'markup', '#value' => format_size($file->filesize));
239 $form['i18n']['translation_files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => 0);
240 $form['i18n']['translation_files'][$key]['list'] = array('#type' => 'checkbox', '#default_value' => $file->list);
241 $form['i18n']['translation_files'][$key]['fid'] = array('#type' => 'value', '#value' => $file->fid);
242 }
243 }
244 }
245 }
246
247 /**
248 * Implementation of hook_nodeapi().
249 *
250 * Delete case is now handled in i18n_nodeapi
251 */
252 function translation_nodeapi(&$node, $op, $arg = 0) {
253 if (variable_get("i18n_node_$node->type", 0)) {
254 switch ($op) {
255 case 'load':
256 $node->translation = translation_node_get_translations(array('nid' =>$node->nid), FALSE);
257 break;
258 case 'insert':
259 if($node->translation_nid) {
260 // If not existing translation set, update both nodes. Otherwise trid is saved by i18n module
261 if(!$node->trid){
262 $node->trid = db_next_id('{i18n_node}_trid');
263 db_query("UPDATE {i18n_node} SET trid = %d WHERE nid=%d OR nid=%d", $node->trid, $node->nid, $node->translation_nid);
264 }
265 // Clone files for node attachments
266 if(isset($node->translation_files)) {
267 foreach($node->translation_files as $fid => $file) {
268 if(!$file['remove']) {
269 // We are using revisions to have a file linked to different nodes, different descriptions
270 db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $file['fid'], $node->vid, $file['list'], $file['description']);
271 }
272 }
273 }
274 }
275 break;
276 case 'prepare': // When creating a translation
277 if (!$node->nid && isset($_GET['translation']) && ($nid = $_GET['translation']) && ($language = $_GET['language']) && user_access('translate nodes')) {
278 // We are translating a node from a source node
279 // Load the node to be translated and populate fields
280 $node->translation_nid = $nid;
281 $node->language = $language;
282 $source = $node->translation_source = node_load($node->translation_nid);
283
284 // Taxonomy translation
285 if(is_array($source->taxonomy)) {
286 // Set translated taxonomy terms
287 $node->taxonomy = array();
288 foreach ($source->taxonomy as $tid => $term) {
289 if ($term->language) {
290 $translated_terms = translation_term_get_translations(array('tid' =>$tid));
291 if($translated_terms && $newterm = $translated_terms[$node->language]) {
292 $node->taxonomy[$newterm->tid] = $newterm;
293 //drupal_set_message("DEBUG: Translated term $tid to ". $newterm->tid);
294 }
295 } else {
296 // Term has no language. Should be ok
297 $node->taxonomy[$tid] = $term;
298 }
299 }
300 }
301
302 // Translations are taken from source node
303 $node->translation = $source->translation;
304 $node->translation[$source->language] = $source;
305
306 // Rest of fields. Unset some known ones not to be copied over
307 unset($source->nid, $source->vid, $source->path, $source->language, $source->files);
308 foreach($source as $field => $value) {
309 if (!isset($node->$field)) {
310 $node->$field = $value;
311 }
312 }
313 break;
314 }
315 }
316 }
317 }
318
319 /**
320 * Fills up some fields from source node
321 */
322 function translation_node_populate_fields($source, &$form, &$node, $level = 0){
323 $fields = array();
324 $language = $form['i18n']['language']['#default_value'];
325 foreach(element_children($form) as $key) {
326 if($key == 'nid' || $key == 'vid' || $key == 'i18n'){
327 continue;
328 } elseif(isset($source->$key) && !isset($node->$key)) {
329 if($level == 0 && $key == 'parent' && is_numeric($source->parent)) {
330 // Translate book outline
331 $trans = translation_node_get_translations(array('nid' => $source->parent));
332 if(isset($trans[$language])) {
333 $form['parent']['#default_value'] = $trans[$language]->nid;
334 }
335 } else {
336 $node->$key = $form[$key]['#default_value'] = $source->$key;
337 $fields[] = $key;
338 }
339 } elseif(!isset($form[$key]['#tree'])) {
340 translation_node_populate_fields($source, $form[$key], $node, $level +1);
341 }
342 }
343 // For debugging
344 //if($fields) drupal_set_message("Populated fields ($level): ". implode(', ', $fields));
345 }
346
347 /**
348 * Multilingual Nodes support
349 */
350
351 /**
352 * This is the callback for the tab 'translation' for nodes
353 */
354 function translation_node_page() {
355 $args = func_get_args();
356 $op = $args[0];
357 $nid = arg(1);
358 $node = node_load($nid);
359 // If node has no language, just warning message. Function returns here
360 if(!$node->language) {
361 form_set_error('language', t("You need to set a language before creating a translation."));
362 drupal_goto("node/$nid/edit");
363 }
364 drupal_set_title($node->title);
365 $output = '';
366 switch($op){
367 case 'select':
368 $output .= translation_node_overview($node);
369 $output .= translation_node_select($node, $args[1]);
370 break;
371 case 'remove':
372 case t('Remove'):
373 db_query("UPDATE {i18n_node} SET trid = NULL WHERE nid=%d", $node->nid);
374 drupal_set_message("The node has been removed from the translation set");
375 drupal_goto("node/$node->nid/translation");
376 default:
377 $output .= translation_node_overview($node);
378 }
379 return $output;
380 }
381
382 /**
383 * Form builder function
384 */
385 function translation_node_form($node, $lang, $list){
386 $form['node'] = array('#type' => 'value', '#value' =>$node);
387 $form['language'] = array('#type' => 'hidden', '#value' => $lang);
388 $form['source_nid'] = array('#type' => 'hidden', '#value' => $node->nid);
389 $form['trid'] = array('#type' => 'hidden', '#value' => $node->trid);
390 $languages = i18n_supported_languages();
391
392 $form['nodes']['nid'] = array(
393 '#type' => 'radios', '#title' => t('Select translation for %language', array('%language' => $languages[$lang])),
394 '#default_value' => isset($node->translation[$lang]) ? $node->translation[$lang]->nid : '',
395 '#options' => $list);
396 $form['pager'] = array('#value' => theme('pager'));
397 $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
398 return $form;
399 }
400
401 /**
402 * Menu callback: administration page for node translations
403 */
404 function translation_admin_content() {
405 $output = '';
406 $defaults = array('translation_language' => i18n_get_lang(), 'source_status' => TRANSLATION_STATUS_SOURCE);
407 // Filter forms
408 $output .= drupal_get_form('node_filter_form');
409 $output .= drupal_get_form('translation_filter_form', $defaults);
410 // Node listing depending on previous forms
411 $output .= drupal_get_form('translation_admin_nodes');
412
413 return $output;
414 }
415
416 /**
417 * Form builder. Administrative form for node translations
418 */
419 function translation_admin_nodes() {
420 // First, translation filter because it may need parameters for join conditions
421 $filter = translation_build_filter_query();
422 $where = $filter['where'];
423 $params = $filter['args'];
424 $join = $filter['join'];
425
426 $filter = node_build_filter_query();
427 // Remove WHERE
428 if($filter['where']) {
429 $where += array(str_replace('WHERE', '', $filter['where']));
430 }
431
432 $params = array_merge($params, $filter['args']);
433
434 $join .= $filter['join'];
435
436 $sql = "SELECT n.nid, n.type, n.title, n.status, u.name, u.uid, i.language, i2.nid AS translation_nid, i2.status AS translation_status ".
437 "FROM {node} n INNER JOIN {users} u ON n.uid = u.uid INNER JOIN {i18n_node} i ON n.nid = i.nid ".
438 "LEFT JOIN {i18n_node} i2 ON i.trid = i2.trid AND 0 != i2.trid AND i.language != i2.language $join".
439 (count($where) ? ' WHERE '.implode(' AND ', $where) : '');
440
441 // Fetch data
442 //drupal_set_message("DEBUG:query: $sql");
443 //drupal_set_message("DEBUG:params: ".implode(', ', $params));
444 i18n_selection_mode('off');
445 $result = pager_query(db_rewrite_sql($sql), 50, 0, NULL, $params);
446 i18n_selection_mode('reset');
447
448 $languages = i18n_supported_languages();
449 $translation_status = _translation_status();
450
451 // Fetch language for translations
452 $language = isset($_SESSION['translation_filter']['translation_language']) ? $_SESSION['translation_filter']['translation_language'] : i18n_get_lang();
453 $destination = drupal_get_destination();
454 while ($node = db_fetch_object($result)) {
455 $nodes[$node->nid] = '';
456 $form['language'][$node->nid] = array('#value' => $languages[$node->language]);
457 $form['title'][$node->nid] = array('#value' => l($node->title, 'node/'. $node->nid) .' '. theme('mark', node_mark($node->nid, $node->changed)));
458 $form['name'][$node->nid] = array('#value' => node_get_types('name', $node));
459 $form['username'][$node->nid] = array('#value' => theme('username', $node));
460 $form['status'][$node->nid] = array('#value' => ($node->status ? t('published') : t('not published')));
461 if ($node->translation_nid) {
462 $form['translation_status'][$node->nid] = array('#value' => $translation_status[$node->translation_status]);
463 $form['operations'][$node->nid] = array('#value' => l(t('edit translation'), 'node/'. $node->translation_nid .'/edit', array(), $destination));
464 } else {
465 $form['translation_status'][$node->nid] = array('#value' => '--');
466 if($language == $node->language) {
467 $form['operations'][$node->nid] = array('#value' => '--');
468 } else {
469 $form['operations'][$node->nid] = array('#value' => l(t('create translation'), 'node/add/'.$node->type, array(), "translation=$node->nid&language=$language").
470 ' | '.l(t('select node'), "node/$node->nid/translation/select/$language", array(), $destination));
471 }
472 }
473 }
474 /*
475 $form['nodes'] = array('#type' => 'checkboxes', '#options' => $nodes);
476 */
477 $form['pager'] = array('#value' => theme('pager', NULL, 50, 0));
478 return $form;
479 }
480
481
482 /**
483 * Build query for node administration filters based on session.
484 */
485 function translation_build_filter_query() {
486
487 // Build query
488 $where = $args = array();
489 $join = '';
490 // This will produce an empty join
491 if (!is_array($_SESSION['translation_filter'])) {
492 $_SESSION['translation_filter'] = array();
493 }
494 foreach ($_SESSION['translation_filter'] as $type => $value) {
495 switch($type) {
496 case 'source_language':
497 $where[] = "i.language = '%s'";
498 $args[] = $value;
499 break;
500 case 'translation_language':
501 $join .= " AND i2.language ='".db_escape_string($value)."' ";
502 break;
503 case 'source_status':
504 $where[] = "i.status = %d";
505 $args[] = $value;
506 break;
507 case 'translation_status':
508 $join .= " AND i2.status = ".db_escape_string($value);
509 break;
510 }
511 }
512
513 return array('where' => $where, 'join' => $join, 'args' => $args);
514 }
515
516 /**
517 * Returns form for translation administration filters.
518 */
519 function translation_filter_form($defaults = array()) {
520 $session = &$_SESSION['translation_filter'];
521 $session = is_array($session) ? $session : $defaults;
522
523 // Save defaults for form reset
524 $form['_defaults'] = array('#type' => 'value', '#value' => $defaults);
525 $form['filters'] = array('#type' => 'fieldset',
526 '#title' => t('And translation conditions are'),
527 );
528 $languages = i18n_supported_languages();
529 // Translation and language conditions
530
531 $form['filters']['source_language'] = array('#type' => 'select', '#title' => t('source language'),
532 '#options' => array('' => '') + $languages, '#default_value' => $session['source_language']);
533 $form['filters']['source_status'] = array('#type' => 'select', '#title' => t('source status'),
534 '#options' => array('' => '') + _translation_status(), '#default_value' => $session['source_status']);
535 $form['filters']['translation_language'] = array('#type' => 'select', '#title' => t('translation language'),
536 '#options' => array('' => '') + $languages, '#default_value' => $session['translation_language']);
537 $form['filters']['translation_status'] = array('#type' => 'select', '#title' => t('translation status'),
538 '#options' => array('' => '') + _translation_status(), '#default_value' => $session['translation_status']);
539 $form['filters']['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Filter'));
540 if (count($session)) {
541 $form['filters']['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset'));
542 }
543 return $form;
544 }
545
546 function theme_translation_filter_form($form) {
547 $form['filters']['source_language']['#prefix'] = '<table><tr><td>';
548 $form['filters']['source_status']['#suffix'] = '</td><td>';
549 $form['filters']['buttons']['#prefix'] = '</td><td>';
550 $form['filters']['buttons']['#suffix'] = '</td></tr></table>';
551 return drupal_render($form);
552 }
553 /**
554 * Builds translation admin filters
555 */
556 function translation_filter_form_submit($form_id, $form_values) {
557 $op = $_POST['op'];
558 $session = &$_SESSION['translation_filter'];
559
560 switch ($op) {
561 case t('Filter'):
562 $session = array();
563 foreach($form_values as $name => $value) {
564 if($value) $session[$name] = $value;
565 }
566 break;
567 case t('Reset'):
568 $session = $form_values['_defaults'];
569 break;
570 }
571
572 }
573
574 /**
575 * Theme node administration overview.
576 */
577 function theme_translation_admin_nodes($form) {
578 // Overview table:
579 $header = array(t('Language'), t('Title'), t('Type'), t('Author'), t('Status'), t('Translation status'), t('Operations'));
580
581 $output .= drupal_render($form['options']);
582 if (isset($form['title']) && is_array($form['title'])) {
583 foreach (element_children($form['title']) as $key) {
584 $row = array();
585 $row[] = drupal_render($form['language'][$key]);
586 $row[] = drupal_render($form['title'][$key]);
587 $row[] = drupal_render($form['name'][$key]);
588 $row[] = drupal_render($form['username'][$key]);
589 $row[] = drupal_render($form['status'][$key]);
590 $row[] = drupal_render($form['translation_status'][$key]);
591 $row[] = drupal_render($form['operations'][$key]);
592 $rows[] = $row;
593 }
594
595 }
596 else {
597 $rows[] = array(array('data' => t('No posts available.'), 'colspan' => '6'));
598 }
599
600 $output .= theme('table', $header, $rows);
601 if ($form['pager']['#value']) {
602 $output .= drupal_render($form['pager']);
603 }
604
605 $output .= drupal_render($form);
606
607 return $output;
608 }
609
610 /**
611 * Shows overview of current translations plus remove button
612 */
613 function translation_node_overview($node) {
614 $languages = i18n_supported_languages();
615 unset($languages[$node->language]);
616 // $output = t('<h2>Current translations</h2>');
617 $header = array(t('Language'), t('Title'), t('Status'), t('Options'));
618 // Special links for product nodes
619 $createlink = $node->type == 'product' ? "node/add/$node->type/$node->ptype" : "node/add/$node->type";
620 foreach($languages as $lang => $langname){
621 $options = array();
622 if(isset($node->translation[$lang])){
623 $trnode = $node->translation[$lang];
624 $title = l($trnode->title, 'node/'.$trnode->nid);
625 $status = $trnode->status ? t('Published') : t('Not published');
626 } else {
627 $title = t('Not translated');
628 $options[] = l(t('create translation'), $createlink, array(), "translation=$node->nid&language=$lang");
629 $status = '--';
630 }
631 $options[] = l(t('select node'), "node/$node->nid/translation/select/$lang");
632 $rows[] = array($langname, $title, $status, implode(" | ", $options));
633 }
634 $output .= theme('table', $header, $rows);
635 if($node->trid){
636 $output .= drupal_get_form('translation_node_remove', $node);
637 }
638 return theme('box', t('Current translations'), $output);
639 }
640
641 /**
642 * Form to remove node from translation set
643 */
644 function translation_node_remove($node) {
645 $form['submit'] = array('#type' => 'submit', '#value' => t('Remove'), '#suffix' => t('Remove node from this translation set'));
646 $form['#action'] = url('node/'.$node->nid.'/translation/remove');
647 return $form;
648 }
649
650 /**
651 * Form to select a translation from existing nodes
652 */
653 function translation_node_select($node, $lang) {
654 $languages = i18n_supported_languages();
655 // Disable i18n rewrite. Order by trid to show first nodes with no translation
656 i18n_selection_mode('off');
657 $result = pager_query(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n INNER JOIN {i18n_node} i ON n.nid = i.nid WHERE i.language = '%s' ORDER BY i.trid"), 40, 0, NULL, $lang);
658 i18n_selection_mode('reset');
659 while($trnode = db_fetch_object($result)){
660 $list[$trnode->nid] = l($trnode->title, "node/$trnode->nid") ;
661 }
662 if($list){
663 return drupal_get_form('translation_node_form', $node, $lang, $list);
664 } else {
665 return t("<p>No nodes available in %language</p>", array('%language' => $languages[$lang]) );
666 }
667 }
668
669 /**
670 * Process translation node form
671 */
672 function translation_node_form_submit($form_id, $form_values){
673 $op = $_POST['op'];
674 $source_nid = $form_values['source_nid'];
675 $language = $form_values['language'];
676 $nid = $form_values['nid'];
677 if( $source_nid && $language && $nid ) {
678 if($trid = $form_values['trid']){
679 // Delete old translations
680 db_query("UPDATE {i18n_node} SET trid = 0 WHERE trid = %d AND language = '%s'", $trid, $language);
681 } else {
682 $trid = db_next_id('{i18n_node}_trid');
683 }
684 db_query("UPDATE {i18n_node} SET trid = %d WHERE nid=%d OR nid=%d", $trid, $source_nid, $nid);
685 drupal_set_message(t('The translation has been saved'));
686
687 }
688 }
689
690 /**
691 * Implementation of hook taxonomy.
692 */
693 function translation_taxonomy($op, $type = NULL, $edit = NULL) {
694 switch ("$type/$op") {
695 case 'term/insert':
696 case 'term/update':
697 if (!$edit['language'] && $edit['trid']) {
698 // Removed language, remove trid
699 db_query('UPDATE {term_data} SET trid=0 WHERE tid=%d', $edit['tid']);
700 if(db_affected_rows()) drupal_set_message(t('Removed translation information from term'));
701 }
702 break;
703 }
704 }
705
706 /**
707 * Implementation of hook_link().
708 */
709 function translation_link($type, $node = NULL, $teaser = FALSE) {
710 $languages = i18n_supported_languages();
711 $links = array();
712 if ($type == 'node' && variable_get('i18n_translation_node_links', 0) > ($teaser ? 1 : 0) && $node->translation) {
713 foreach ($node->translation as $lang => $trnode) {
714 // Add node link if published
715 if($trnode->status) {
716 $baselang = variable_get('i18n_translation_links', 0) ? i18n_get_lang() : $lang;
717 $links['translation-'.$lang]= theme('translation_node_link', $trnode , $lang, $baselang);
718 }
719 }
720 }
721 return $links;
722 }
723
724 /**
725 * Returns a list for terms for vocabulary, language
726 */
727 function translation_vocabulary_get_terms($vid, $lang, $status = 'all') {
728 switch($status){
729 case 'translated':
730 $andsql = ' AND trid > 0';
731 break;
732 case 'untranslated':
733 $andsql = ' AND trid = 0';
734 break;
735 default:
736 $andsql = '';
737 }
738 $result = db_query("SELECT * FROM {term_data} WHERE vid=%d AND language='%s' $andsql", $vid, $lang);
739 $list = array();
740 while ($term = db_fetch_object($result)) {
741 $list[$term->tid] = $term->name;
742 }
743 return $list;
744 }
745
746 /**
747 * Get translations
748 *
749 * @param $params
750 * array of parameters
751 * @param $getall
752 * TRUE to get the also node itself
753 */
754 function translation_node_get_translations($params, $getall = TRUE) {
755 foreach($params as $field => $value) {
756 $conds[] = "b.$field = '%s'";
757 $values[] = $value;
758 }
759 if(!$getall){ // If not all, a parameter must be nid
760 $conds[] = "n.nid != %d";
761 $values[] = $params['nid'];
762 }
763 $conds[] = "b.trid != 0";
764 $sql = 'SELECT n.nid, n.title, n.status, a.language FROM {node} n INNER JOIN {i18n_node} a ON n.nid = a.nid INNER JOIN {i18n_node} b ON a.trid = b.trid WHERE '. implode(' AND ', $conds);
765
766 i18n_selection_mode('off');
767 $result = db_query(db_rewrite_sql($sql), $values);
768 i18n_selection_mode('reset');
769
770 $items = array();
771 while ($node = db_fetch_object($result)) {
772 $items[$node->language] = $node;
773 }
774 return $items;
775 }
776
777 /**
778 * Returns node type for nid
779 */
780 function translation_get_node_type($nid) {
781 return db_result(db_query('SELECT type FROM {node} WHERE nid=%d', $nid));
782 }
783
784 /**
785 * Multilingual Taxonomy
786 *
787 */
788
789 /**
790 * This is the callback for taxonomy translations
791 *
792 * Gets the urls:
793 * admin/content/taxonomy/i18n/term/xx
794 * admin/content/taxonomy/i18n/term/new/xx
795 * admin/content/taxonomy/vid/translation/op/trid
796 */
797
798 function translation_taxonomy_admin() {
799 $vid = arg(3);
800 $op = $_POST['op'] ? $_POST['op'] : arg(5);
801 $edit = $_POST['edit'];
802
803 switch ($op) {
804 case t('Save'):
805 case 'edit':
806 drupal_set_title(t('Edit term translations'));
807 $output = drupal_get_form('translation_taxonomy_term_form', $vid, arg(6), $edit);
808 break;
809 case t('Submit'):
810 drupal_set_title(t('Submit'));
811 translation_taxonomy_term_save($edit);
812 $output = translation_taxonomy_overview($vid);
813 break;
814 case 'delete':
815 //print theme('page', node_delete($edit), t('Delete'));
816 break;
817 default:
818 $output = translation_taxonomy_overview($vid);
819 }
820 return $output;
821 }
822
823 /**
824 * Generate a tabular listing of translations for vocabularies.
825 */
826 function translation_taxonomy_overview($vid) {
827 $vocabulary = taxonomy_get_vocabulary($vid);
828 drupal_set_title(check_plain($vocabulary->name));
829
830 $languages = i18n_supported_languages();
831 $header = array_merge($languages, array(t('Operations')));
832 $links = array();
833 $types = array();
834 // Get terms/translations for this vocab
835 $result = db_query('SELECT * FROM {term_data} t WHERE vid=%d',$vocabulary->vid);
836 $terms = array();
837 while ($term = db_fetch_object($result)) {
838 if($term->trid && $term->language) {
839 $terms[$term->trid][$term->language] = $term;
840 }
841 }
842 // Reorder data for rows and languages
843 foreach ($terms as $trid => $terms) {
844 $thisrow = array();
845 foreach ($languages as $lang => $name) {
846 if (array_key_exists($lang, $terms)) {
847 $thisrow[] = $terms[$lang]->name;
848 }
849 else {
850 $thisrow[] = '--';
851 }
852 }
853 $thisrow[] = l(t('edit'), "admin/content/taxonomy/$vid/translation/edit/$trid");
854 $rows[] = $thisrow;
855 }
856 $output .= theme('table', $header, $rows);
857 $output .= l(t('new translation'), "admin/content/taxonomy/$vid/translation/edit/new");
858 return $output;
859 }
860
861 /**
862 * Produces a vocabulary translation form
863 */
864 function translation_taxonomy_term_form($vid, $trid = NULL, $edit = array()) {
865 $languages = i18n_supported_languages();
866 if ($trid == 'new') {
867 $translations = array();
868 } else {
869 $form['trid'] = array('#type' => 'hidden', '#value' => $trid);
870 $translations = translation_term_get_translations(array('trid' =>$trid));
871 }
872 //var_dump($translations);
873 $vocabulary = taxonomy_get_vocabulary($vid);
874
875 // List of terms for languages
876 foreach ($languages as $lang => $langname) {
877 $current = isset($translations[$lang]) ? $translations[$lang]->tid : '';
878 $list = translation_vocabulary_get_terms($vid, $lang, 'all');
879 $list[''] = '--';
880 $form[$lang] = array('#type' => 'fieldset', '#tree' => TRUE);
881 $form[$lang]['tid'] = array(
882 '#type' => 'select',
883 '#title' => $langname,
884 '#default_value' => $current,
885 '#options' => $list
886 );
887 $form[$lang]['old'] = array('#type' => 'hidden', '#value' =>$current);
888 }
889 $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
890 $form['destination'] = array('#type' => 'hidden', '#value' => 'admin/content/taxonomy/'.arg(3).'/translation');
891 return $form;
892 }
893
894 /**
895 * Form callback: Process vocabulary translation form
896 */
897 function translation_taxonomy_term_form_submit($form_id, $form_values) {
898 $trid = $form_values['trid'];
899 $languages = i18n_supported_languages();
900 $translations = array();
901 // Delete old translations
902 if($trid){
903 db_query("UPDATE {term_data} SET trid = 0 WHERE trid= %d", $trid);
904 }
905 foreach ($languages as $lang => $name) {
906 if (is_numeric($form_values[$lang]['tid'])) {
907 $translations[$lang] = $form_values[$lang]['tid'];
908 }
909 }
910 if(count($translations)) {
911 $trid = is_numeric($trid) ? $trid : db_next_id('{term_data}_trid');
912 db_query('UPDATE {term_data} SET trid=%d WHERE tid IN(%s)', $trid, implode(',',$translations));
913 }
914 drupal_set_message(t('Term translations have been updated'));
915 }
916
917 /**
918 * Converts a list of arrays to an array of the form keyfield => namefield
919 */
920 function translation_array2list($data, $keyfield, $namefield = 'name') {
921 foreach ($data as $key => $value) {
922 if (is_array($data)) {
923 $list[$value[$keyfield]] = $value[$namefield];
924 }
925 else {
926 $list[$value->$keyfield] = $value->$namefield;
927 }
928 }
929 return $list;
930 }
931
932 /**
933 * Get term translations
934 *
935 * @return
936 * An array of the from lang => Term
937 */
938 function translation_term_get_translations($params, $getall = TRUE) {
939 foreach($params as $field => $value) {
940 $conds[] = "i.$field = '%s'";
941 $values[] = $value;
942 }
943 if(!$getall){ // If not all, a parameter must be tid
944 $conds[] = "t.tid != %d";
945 $values[] = $params['tid'];
946 }
947 $conds[] = "t.trid != 0";
948 $sql = 'SELECT t.* FROM {term_data} t INNER JOIN {term_data} i ON t.trid = i.trid WHERE '. implode(' AND ', $conds);;
949 $result = db_query($sql, $values);
950 $items = array();
951 while ($data = db_fetch_object($result)) {
952 $items[$data->language] = $data;
953 }
954 return $items;
955 }
956
957 /**
958 * Produces url of translated page
959 */
960 function translation_url($url, $lang) {
961 global $i18n_langpath;
962 // If !url get from original request
963 if (!$url) {
964 $url = _i18n_get_original_path();
965 }
966 // If url has lang_prefix, remove it
967 i18n_get_lang_prefix($url, true);
968 // are we looking at a node?
969 if (preg_match("/^(node\/)([0-9]*)$/",$url,$matches)) {
970 if ($nid = translation_node_nid($matches[2], $lang)) {
971 $url = "node/$nid";
972 }
973 }
974 // or a taxonomy term
975 elseif (preg_match("/^(taxonomy\/term\/)([^\/]*)$/",$url,$matches)) {//or at a taxonomy-listing?
976 if ($str_tids = translation_taxonomy_tids($matches[2], $lang)) {
977 $url = "taxonomy/term/$str_tids";
978 }
979 }
980
981 return $url;
982 }
983
984 /**
985 * Get translated node's nid
986 *
987 * @param $nid
988 * Node nid to search for translation
989 * @param $language
990 * Language to search for the translation, defaults to current language
991 * @param $default
992 * Value that will be returned if no translation is found
993 * @return
994 * Translated node nid if exists, or $default
995 */
996 function translation_node_nid($nid, $language = NULL, $default = NULL) {
997 $translation = db_result(db_query("SELECT n.nid FROM {i18n_node} n INNER JOIN {i18n_node} a ON n.trid = a.trid AND n.nid != a.nid WHERE a.nid = %d AND n.language = '%s' AND n.trid", $nid, $language ? $language : i18n_get_lang()));
998 return $translation ? $translation : $default;
999 }
1000
1001 /**
1002 * Get translated term's tid
1003 *
1004 * @param $tid
1005 * Node nid to search for translation
1006 * @param $language
1007 * Language to search for the translation, defaults to current language
1008 * @param $default
1009 * Value that will be returned if no translation is found
1010 * @return
1011 * Translated term tid if exists, or $default
1012 */
1013 function translation_term_tid($tid, $language = NULL, $default = NULL) {
1014 $translation = db_result(db_query("SELECT t.tid FROM {term_data} t INNER JOIN {term_data} a ON t.trid = a.trid AND t.tid != a.tid WHERE a.tid = %d AND t.language = '%s' AND t.trid", $tid, $language ? $language : i18n_get_lang()));
1015 return $translation ? $translation : $default;
1016 }
1017
1018 /**
1019 * Returns an url for the translated taxonomy-page, if exists
1020 */
1021 function translation_taxonomy_tids($str_tids, $lang) {
1022 if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str_tids)) {
1023 $separator = '+';
1024 // The '+' character in a query string may be parsed as ' '.
1025 $tids = preg_split('/[+ ]/', $str_tids);
1026 }
1027 else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str_tids)) {
1028 $separator = ',';
1029 $tids = explode(',', $str_tids);
1030 }
1031 else {
1032 return;
1033 }
1034 $translated_tids = array();
1035 foreach ($tids as $tid) {
1036 if ($translated_tid = translation_term_tid($tid, $lang)) {
1037 $translated_tids[] = $translated_tid;
1038 }
1039 }
1040 return implode($separator, $translated_tids);
1041 }
1042
1043 /**
1044 * function translation_get_links
1045 *
1046 * Returns an array of links for all languages, with or without names
1047 */
1048 function translation_get_links($path = '', $query = NULL) {
1049 $current = i18n_get_lang();
1050 foreach(i18n_supported_languages() as $lang => $name){
1051 $url = translation_url($path, $lang);
1052 $links[]= theme('i18n_link', $name, i18n_path($url, $lang) , $lang, $query);
1053 }
1054 return $links;
1055 }
1056
1057 /**
1058 * Return status for translations workflow
1059 */
1060 function _translation_status(){
1061 return array(
1062 TRANSLATION_STATUS_NONE => t('None'),
1063 TRANSLATION_STATUS_SOURCE => t('Source content (to be translated)'),
1064 TRANSLATION_STATUS_WORKING => t('Translation in progress'),
1065 TRANSLATION_STATUS_TRANSLATED => t('Translated content'),
1066 TRANSLATION_STATUS_UPDATED => t('Source updated (to update translation)'));
1067 }
1068
1069 /**
1070 * Theme a link to node translation
1071 *
1072 * Since Drupal 5, returns array instead of html
1073 */
1074 function theme_translation_node_link($node, $lang, $baselang = NULL, $title = FALSE){
1075 $baselang = $baselang ? $baselang : $lang;
1076
1077 if($title){
1078 $name = $node->title;
1079 } else {
1080 $languages = i18n_supported_languages();
1081 $name = $languages[$lang];
1082 }
1083 return array(
1084 'title' => $name,
1085 'href' => i18n_path('node/'.$node->nid, $baselang)
1086 );
1087 }
1088
1089 /**
1090 * Theme a link for a translation
1091 */
1092 function theme_translation_link($text, $target, $lang, $separator='&nbsp;') {
1093 return theme('i18n_link', $text, $target, $lang, $separator);
1094 }
1095
1096 /**
1097 * Theme list of node translations
1098 */
1099 function theme_translation_node_list($list){
1100 $header = array(t('Language'), t('Title'));
1101 $languages = i18n_supported_languages();
1102 foreach($list as $lang => $node){
1103 $rows[] = array($languages[$lang], l($node->title, 'node/'.$node->nid) );
1104 }
1105 return theme('table', $header, $rows);
1106 }