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