Projet

Général

Profil

Paste
Télécharger (20,3 ko) Statistiques
| Branche: | Révision:

root / htmltest / sites / all / modules / i18n / i18n_node / i18n_node.module @ 4543c6c7

1
<?php
2

    
3
/**
4
 * @file
5
 * Internationalization (i18n) module - Node type handling
6
 *
7
 * The i18n strings created by this module are:
8
 * - node:type:[type]:[name,title,body,help]
9
 */
10

    
11

    
12
/**
13
 * Implements hook_field_extra_fields().
14
 */
15
function i18n_node_field_extra_fields() {
16
  $return = array();
17
  $info = entity_get_info('node');
18
  foreach (array_keys($info['bundles']) as $bundle) {
19
    if (i18n_node_type_enabled($bundle)) {
20
      $return['node'][$bundle] = i18n_language_field_extra();
21
    }
22
  }
23
  return $return;
24
}
25

    
26
/**
27
 * Implements hook_menu().
28
 */
29
function i18n_node_menu() {
30
  $items['admin/config/regional/i18n/node'] = array(
31
    'title' => 'Node options',
32
    'description' => 'Configure extended options for multilingual content and translations.',
33
    'page callback' => 'drupal_get_form',
34
    'page arguments' => array('variable_group_form', 'i18n_node'),
35
    'access arguments' => array('administer site configuration'),
36
    'type' => MENU_LOCAL_TASK,
37
    'weight' => 10,
38
  );
39
  $items['i18n/node/autocomplete'] = array(
40
    'page callback' => 'i18n_node_autocomplete',
41
    'file' => 'i18n_node.pages.inc',
42
    'access arguments' => array('administer content translations'),
43
    'type' => MENU_CALLBACK,
44
  );
45
  return $items;
46
}
47

    
48
/**
49
 * Implements hook_block_view_MODULE_DELTA_alter().
50
 *
51
 * Set translated help text for node/add pages, replace node_help() text.
52
 */
53
function i18n_node_block_view_system_help_alter(&$block) {
54
  $arg = drupal_help_arg(arg(NULL));
55
  if ($arg[0] == 'node' && $arg[1] == 'add' && $arg[2]) {
56
    if (($type = node_type_get_type(str_replace('-', '_', $arg[2]))) && !empty($type->help)) {
57
      $help = i18n_node_translate_type($type, 'help');
58
      if ($help !== $type->help) {
59
        $block['content'] = str_replace(filter_xss_admin($type->help), filter_xss_admin($help), $block['content']);
60
      }
61
    }
62
  }
63
  elseif ($arg[0] == 'node' && $arg[2] == 'edit') {
64
    $node = menu_get_object();
65
    if ($node && isset($node->type)) {
66
      $type = node_type_get_type($node->type);
67
      $help = i18n_node_translate_type($type, 'help');
68
      if ($help !== $type->help) {
69
        $block['content'] = str_replace(filter_xss_admin($type->help), filter_xss_admin($help), $block['content']);
70
      }
71
    }
72
  }
73
}
74

    
75
/**
76
 * Implements hook_help().
77
 */
78
function i18n_node_help($path, $arg) {
79
  switch ($path) {
80
    case 'admin/help#i18n_node':
81
      $output = '<p>' . t('Provides some extended multilingual options for nodes.') . '</p>';
82
      $output .= '<p>' . t('Additionally, if <em>String translation</em> enabled, this module will localize all content type configuration texts.') . '</p>';
83
      $output .= '<ul>';
84
      $output .= '<li>' . t('Content type names') . '</li>';
85
      $output .= '<li>' . t('Submission guidelines') . '</li>';
86
      $output .= '<li>' . t("Content type descriptions were previously localized so they won't be affected.") . '</li>';
87
      $output .= '</ul>';
88
      $output .= '<p>' . t('To search and translate strings, use the <a href="@translate-interface">translation interface</a> pages.', array('@translate-interface' => url('admin/config/regional/translate'))) . '</p>';
89
      return $output;
90
    case 'admin/config/regional/i18n':
91
    case 'admin/config/regional/i18n/node':
92
      $output = '<p>' . t('You can find some more per content type options on the <a href="@configure_content">Content types administration page</a>.', array('@configure_content' => url('admin/structure/types'))) . '</p>';
93
      return $output;
94
  }
95
}
96

    
97
/**
98
 * Implements hook_i18n_context_language().
99
 */
100
function i18n_node_i18n_context_language() {
101
  // Node language when loading specific nodes or creating translations.
102
  if (arg(0) == 'node' ) {
103
    if (($node = menu_get_object('node')) && !empty($node->language) && i18n_node_type_enabled($node)) {
104
      return i18n_language_object($node->language);
105
    }
106
    elseif (arg(1) == 'add' && !empty($_GET['translation']) && !empty($_GET['target']) && ($source = node_load($_GET['translation'])) && i18n_node_type_enabled($source)) {
107
      return i18n_language_object($_GET['target']);
108
    }
109
  }
110
}
111

    
112
/**
113
 * Implements hook_i18n_translate_path()
114
 */
115
function i18n_node_i18n_translate_path($path) {
116
  if (preg_match("!^node/(\d+)(/.+|)!", $path, $matches) && ($node = node_load((int) $matches[1])) && i18n_object_langcode($node) && !empty($node->tnid)) {
117
    if ($translations = translation_node_get_translations($node->tnid)) {
118
      $result = array();
119
      foreach ($translations as $langcode => $node_translation) {
120
        $result[$langcode] = array(
121
          'href' => 'node/' . $node_translation->nid . $matches[2],
122
          'title' => $node_translation->title,
123
          'object' => $node_translation,
124
        );
125
      }
126
      return $result;
127
    }
128
  }
129
}
130

    
131
/**
132
 * Implements hook_menu_alter().
133
 *
134
 * Take over the node translation page.
135
 */
136
function i18n_node_menu_alter(&$items) {
137
  if (isset($items['node/%node/translate'])) {
138
    $items['node/%node/translate']['page callback'] = 'i18n_node_translation_overview';
139
    $items['node/%node/translate']['file'] = 'i18n_node.pages.inc';
140
    $items['node/%node/translate']['module'] = 'i18n_node';
141
  }
142
  // Take over node/add pages for string translation
143
  $items['node/add']['page callback'] =  'i18n_node_add_page';
144
  $items['node/add']['file'] = 'i18n_node.pages.inc';
145
  $items['node/add']['file path'] = drupal_get_path('module', 'i18n_node');
146
  // @TODO avoid iterating over every router path.
147
  foreach (node_type_get_types() as $type) {
148
    $path = 'node/add/' . str_replace('_', '-', $type->type);
149
    if (isset($items[$path])) {
150
      $items[$path]['title callback'] = 'i18n_node_type_name';
151
      $items[$path]['title arguments'] = array($type->type, $type->name);
152
    }
153
  }
154
}
155

    
156
/**
157
 * Get node language.
158
 */
159
function i18n_node_get_lang($nid, $default = '') {
160
  $lang = db_query('SELECT language FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchField();
161
  return $lang ? $lang : $default ;
162
}
163

    
164
/**
165
 * Get allowed languages for node.
166
 *
167
 * This allows node types to define its own language list implementing hook 'language_list'.
168
 *
169
 * @param $node
170
 *   Node to retrieve language list for.
171
 * @param $translate
172
 *   Only languages available for translation.
173
 * @param $select
174
 *   Only languages that can be selected for this node
175
 */
176
function i18n_node_language_list($node, $translate = FALSE, $select = FALSE) {
177
  // Check if the node module manages its own language list.
178
  $languages = node_invoke($node, 'language_list', $translate);
179

    
180
  if (!$languages) {
181
    $languages = i18n_language_list('name', i18n_node_language_mode($node));
182
    if ($translate && isset($node->tnid) && $node->tnid && ($translations = translation_node_get_translations($node->tnid))) {
183
      unset($translations[$node->language]);
184
      foreach (array_keys($translations) as $langcode) {
185
        unset($languages[$langcode]);
186
      }
187
    }
188
    // Language may be locked for this node type, restrict options to current one
189
    if ($select && i18n_node_language_options($node, 'lock') && !empty($node->language) && !empty($languages[$node->language])) {
190
      $languages = array($node->language => $languages[$node->language]);
191
    }
192
    // Check language required for this type (no language neutral)
193
    elseif (!i18n_node_language_options($node, 'required')) {
194
      $languages = array(LANGUAGE_NONE => t('Language neutral')) + $languages;
195
    }
196
  }
197

    
198
  return $languages;
199
}
200

    
201
/**
202
 * Check options for node language
203
 */
204
function i18n_node_language_options($node, $option) {
205
  $options = variable_get('i18n_node_options_' . $node->type, array());
206
  return in_array($option, $options, TRUE);
207
}
208

    
209
/**
210
 * Get language mode for node or node type
211
 */
212
function i18n_node_language_mode($type) {
213
  $type = is_object($type) ? $type->type : $type;
214
  return variable_get('i18n_node_extended_' . $type, I18N_LANGUAGE_ENABLED);
215
}
216

    
217
/**
218
 * Implements hook_node_prepare().
219
 */
220
function i18n_node_node_prepare($node) {
221
  $options = variable_get('i18n_node_options_' . $node->type, array());
222
  if (i18n_node_type_enabled($node) && empty($node->nid) && !i18n_object_langcode($node) && in_array('current', $options)) {
223
    // Set current language for new nodes if option enabled
224
    $node->language = i18n_language_content()->language;
225
  }
226
}
227

    
228
/**
229
 * Implements hook_permission().
230
 *
231
 * Permissions defined
232
 * - administer all languages
233
 *   Disables language conditions for administration pages, so the user can view objects for all languages at the same time.
234
 *   This applies for: menu items, taxonomy
235
 * - administer translations
236
 *   Will allow to add/remove existing nodes to/from translation sets.
237
 */
238
function i18n_node_permission() {
239
  return array(
240
    'administer content translations' => array(
241
      'title' => t('Administer content translations'),
242
      'description' => t('Add or remove existing content to translation sets.'),
243
    ),
244
  );
245
}
246

    
247
/**
248
 * Implements hook_node_view()
249
 */
250
function i18n_node_node_view($node) {
251
  if (i18n_node_type_enabled($node)) {
252
    $node->content['language'] = array(
253
      '#type' => 'item',
254
      '#title' => t('Language'),
255
      '#markup' => i18n_language_name($node->language),
256
    );
257
  }
258
}
259

    
260
/**
261
 * Implements hook_node_view_alter().
262
 *
263
 * Handles links for extended languages. Sets current interface language.
264
 */
265
function i18n_node_node_view_alter(&$build) {
266
  $node = $build['#node'];
267
  // Hide node translation links.
268
  if (variable_get('i18n_hide_translation_links', 0)) {
269
    if (isset($build['links']['translation'])) {
270
      unset($build['links']['translation']);
271
    }
272
  }
273
  elseif (!empty($node->tnid) && !empty($build['links']['translation']) && i18n_node_language_mode($node) == I18N_LANGUAGE_EXTENDED) {
274
    // We only get links for translations for enabled languages
275
    // Set the right languages if language support is extended but not visible.
276
    $links = &$build['links']['translation']['#links'];
277
    $translations = translation_node_get_translations($node->tnid);
278
    foreach (i18n_node_language_list($node) as $langcode => $langname) {
279
      $index = 'translation_' . $langcode;
280
      if ($langcode != $node->language && isset($translations[$langcode]) && $translations[$langcode]->status && !isset($links[$index])) {
281
        // This a published translation to a disabled language. As the node is language extended, display the linkso
282
        // These links shouldn't switch the interface, though they have a language so the right language icon will be added
283
        $language = i18n_language_object($langcode);
284
        $links[$index] = array(
285
          'href' => 'node/' . $translations[$langcode]->nid,
286
          'title' => $language->native,
287
          'language' => $language,
288
          'attributes' => array(
289
            'title' => $translations[$langcode]->title,
290
            'class' => array('translation-link'),
291
          ),
292
        );
293
      }
294
    }
295
  }
296
}
297

    
298
/**
299
 * Implements hook_node_type_insert()
300
 */
301
function i18n_node_node_type_insert($info) {
302
  i18n_node_node_type_update($info);
303
}
304

    
305
/**
306
 * Implements hook_node_type_update()
307
 */
308
function i18n_node_node_type_update($info) {
309
  if (!empty($info->old_type) && $info->old_type != $info->type) {
310
    i18n_string_update_context("node:type:$info->old_type:*", "node:type:$info->type:*");
311
  }
312
  i18n_string_object_update('node_type', $info);
313
}
314

    
315
/**
316
 * Implements hook_node_type_delete()
317
 */
318
function i18n_node_node_type_delete($info) {
319
  i18n_string_object_remove('node_type', $info);
320
}
321

    
322
/**
323
 * Implements hook_elements().
324
 *
325
 * Add a process callback for textfields.
326
 *
327
 *  * @todo Update D7
328
 */
329
/*
330
function i18n_node_elements() {
331
  $type = array();
332
  $type['textfield'] = array('#process' => array('i18n_node_textfield_process'));
333
  return $type;
334
}
335
*/
336

    
337
/**
338
 * Process callback for textfield elements.
339
 *
340
 * When editing or translating a node, set Javascript to rewrite autocomplete
341
 * paths to use the node language prefix rather than the current content one.
342
 *
343
 * @todo Update D7
344
 */
345
function i18n_node_textfield_process($element) {
346
  global $language;
347
  static $sent = FALSE;
348

    
349
  // Ensure we send the Javascript only once.
350
  if (!$sent && isset($element['#autocomplete_path']) && !empty($element['#autocomplete_path']) && variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) != LANGUAGE_NEGOTIATION_NONE) {
351
    // Add a JS file for node forms.
352
    // Determine if we are either editing or translating an existing node.
353
    // We can't act on regular node creation because we don't have a specified
354
    // node language.
355
    $node_edit = $node = menu_get_object() && arg(2) == 'edit' && isset($node->language) && !empty($node->language);
356
    $node_translate = arg(0) == 'node' && arg(1) == 'add' && !empty($_GET['translation']) && !empty($_GET['language']);
357
    if ($node_edit || $node_translate) {
358
      $node_language = $node_edit ? $node->language : $_GET['language'];
359
      // Only needed if the node language is different from the interface one.
360
      if ($node_language != $language->language) {
361
        $languages = language_list();
362
        if (isset($languages[$node_language])) {
363
          drupal_add_js(drupal_get_path('module', 'i18n_node') . '/i18n_node.js');
364
          // Pass the interface and content language base paths.
365
          // Remove any trailing forward slash. Doing so prevents a mismatch
366
          // that occurs when a language has no prefix and hence gets a path
367
          // with a trailing forward slash.
368
          $interface = rtrim(url('', array('absolute' => TRUE)), '/');
369
          $content = rtrim(url('', array('absolute' => TRUE, 'language' => $languages[$node_language])), '/');
370
          $data = array('interface_path' => $interface, 'content_path' => $content);
371
          drupal_add_js(array('i18n' => $data), 'setting');
372
        }
373
      }
374
    }
375
    $sent = TRUE;
376
  }
377
  return $element;
378
}
379

    
380
/**
381
 * Implements hook_form_FORM_ID_alter().
382
 */
383
function i18n_node_form_search_form_alter(&$form, &$form_state) {
384
  // Advanced search form. Add language and localize content type names
385
  if ($form['module']['#value'] == 'node' && !empty($form['advanced'])) {
386
    // @todo Handle language search conditions
387
    //$form['advanced']['language'] = _i18n_language_select();
388
    if (!empty($form['advanced']['type'])) {
389
      foreach ($form['advanced']['type']['#options'] as $type => $name) {
390
        $form['advanced']['type']['#options'][$type] = i18n_node_translate_type($type, 'name', $name);
391
      }
392
    }
393
  }
394
}
395

    
396
/**
397
 * Implements hook_form_FORM_ID_alter().
398
 */
399
function i18n_node_form_node_type_form_alter(&$form, &$form_state) {
400
  if (isset($form['type'])) {
401
    $disabled = !i18n_node_type_enabled($form['#node_type']);
402
    $form['i18n'] = array(
403
      '#type' => 'fieldset',
404
      '#title' => t('Multilingual settings'),
405
      '#collapsible' => TRUE,
406
      '#collapsed' => TRUE,
407
      '#group' => 'additional_settings',
408
      '#attributes' => array(
409
        'class' => array('i18n-node-type-settings-form'),
410
      ),
411
      '#description' => t('Extended multilingual options provided by Internationalization module.'),
412
      '#disabled' => $disabled,
413
    );
414

    
415
    // Some settings about node languages. Add variables for node type from variable definition
416
    if ($form['#node_type']->type) {
417
      variable_type_include('node_type');
418
      $form['i18n'] += node_variable_type_subform($form['#node_type']->type, array('i18n_node_options', 'i18n_node_extended'));
419
    }
420
    // Add disabled message
421
    if ($disabled) {
422
      $form['i18n']['#description'] .= ' <em>' . t('These will be available only when you enable Multilingual support in Publishing options above.') . '</em>';
423
      foreach (element_children($form['i18n']) as $key) {
424
        $form['i18n'][$key]['#disabled'] = TRUE;
425
      }
426
    }
427
  }
428
}
429

    
430
/**
431
 * Implements hook_form_BASE_FORM_ID_alter().
432
 */
433
function i18n_node_form_node_form_alter(&$form, $form_state) {
434
  $node = $form['#node'];
435
  /**
436
   * i18n has to override locale.module
437
   * drupal_alter() fails to order modules correctly in some cases
438
   * for example specific hooks like hook_form_BASE_FORM_ID_alter
439
   *
440
   * its not possbile to reorder hook_form_BASE_FORM_ID_alter with
441
   * hook_module_implements_alter
442
   *
443
   * @see http://drupal.org/node/765860
444
  */
445
  
446
  // Replace core's node submit callback with our own,
447
  // in order to translate the node type name.
448
  $key = array_search('node_form_submit', $form['actions']['submit']['#submit']);
449
  if ($key !== FALSE) {
450
    $form['actions']['submit']['#submit'][$key] = 'i18n_node_form_submit';
451
  }
452

    
453
  // call a 'private' implemenation of i18n_node_form_node_form_alter()
454
  $form['#after_build'][] = '_i18n_node_form_node_form_alter';
455
}
456

    
457

    
458
/**
459
 * Replacement for core's node_form_submit(), taking care of
460
 * translating node type names.
461
 */
462
function i18n_node_form_submit($form, &$form_state) {
463
  $node = node_form_submit_build_node($form, $form_state);
464
  $insert = empty($node->nid);
465
  node_save($node);
466
  $node_link = l(t('view'), 'node/' . $node->nid);
467
  $type = node_type_get_type($node->type);
468
  $type_name = i18n_node_translate_type($type);
469

    
470
  $watchdog_args = array('@type' => $node->type, '%title' => $node->title);
471
  $t_args = array('@type' => $type_name, '%title' => $node->title);
472

    
473
  if ($insert) {
474
    watchdog('content', '@type: added %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
475
    drupal_set_message(t('@type %title has been created.', $t_args));
476
  }
477
  else {
478
    watchdog('content', '@type: updated %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
479
    drupal_set_message(t('@type %title has been updated.', $t_args));
480
  }
481
  if ($node->nid) {
482
    $form_state['values']['nid'] = $node->nid;
483
    $form_state['nid'] = $node->nid;
484
    $form_state['redirect'] = 'node/' . $node->nid;
485
  }
486
  else {
487
    // In the unlikely case something went wrong on save, the node will be
488
    // rebuilt and node form redisplayed the same way as in preview.
489
    drupal_set_message(t('The post could not be saved.'), 'error');
490
    $form_state['rebuild'] = TRUE;
491
  }
492
  // Clear the page and block caches.
493
  cache_clear_all();
494
}
495

    
496
/**
497
 * Implements hook_form_BASE_FORM_ID_alter().
498
 * Called by i18n_node_form_node_form_alter
499
 */
500
function _i18n_node_form_node_form_alter($form, &$form_state) {
501
  $node = $form['#node'];
502
  if (i18n_node_type_enabled($node)) {
503
    if (!empty($form['language']['#options'])) {
504
      $form['language']['#options'] = i18n_node_language_list($node, TRUE, TRUE);
505
    }
506
  }
507
  elseif (variable_get('i18n_node_default_language_none', 0) && !isset($form['#node']->nid)) {
508
    // Override locale module setting default language to nodes. It is already in form_state.
509
    $form['language']['#value'] = $form_state['values']['language'] = LANGUAGE_NONE;
510
  }
511
  // Translate field names for title and body for the node edit form.
512
  if (!empty($form['title']['#title'])) {
513
    $form['title']['#title'] = i18n_node_translate_type($node->type, 'title_label', $form['title']['#title']);
514
  }
515
  if (!empty($form['body_field']['body']['#title'])) {
516
    $form['body_field']['body']['#title'] = i18n_node_translate_type($node->type, 'body', $form['body_field']['body']['#title']);
517
  }
518
  // Translate page title for node/add/% and node/%/edit pages.
519
  $types = node_type_get_types();
520
  if (empty($node->nid) && strpos($_GET['q'], 'node/add/' . str_replace('_', '-', $node->type)) === 0) {
521
    drupal_set_title(t('Create @name', array('@name' => i18n_node_translate_type($types[$node->type], 'name'))), PASS_THROUGH);
522
  }
523
  elseif (!empty($node->nid) && $_GET['q'] == 'node/' . $node->nid . '/edit') {
524
    drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => i18n_node_translate_type($types[$node->type], 'name'), '@title' => $node->title)), PASS_THROUGH);
525
  }
526
  return $form;
527
}
528

    
529
/**
530
 * Implements hook_theme().
531
 */
532
function i18n_node_theme() {
533
  return array(
534
    'i18n_node_select_translation' => array(
535
      'render element' => 'element',
536
      'file' => 'i18n_node.pages.inc',
537
    ),
538
  );
539
}
540

    
541
/**
542
 * Shorthand for translating node type strings
543
 *
544
 * @param $type
545
 *   Node type name or full object
546
 */
547
function i18n_node_translate_type($type, $property = 'name', $source = NULL, $options = array()) {
548
  if (is_object($type)) {
549
    $source = $type->$property;
550
    $type = $type->type;
551
  }
552
  return i18n_string_translate(array('node', 'type', $type, $property), $source, $options);
553
}
554

    
555
/**
556
 * Translate node type name (unfiltered)
557
 */
558
function i18n_node_type_name($type, $name) {
559
  return i18n_string_translate(array('node', 'type', $type, 'name'), $name);
560
}
561

    
562
/**
563
 * Check whether this is a language enabled node type
564
 *
565
 * @param $type
566
 *   Node, node type object, or node type name
567
 */
568
function i18n_node_type_enabled($type) {
569
  $type = is_object($type) ? $type->type : $type;
570
  $mode = variable_get('language_content_type_' . $type, 0);
571
  return $mode == 1 || $mode == 2; // 2 == TRANSLATION_ENABLED
572
}