Projet

Général

Profil

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

root / drupal7 / sites / all / modules / i18n / i18n_node / i18n_node.module @ 76df55b7

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', NULL, array('sanitize' => FALSE));
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', NULL, array('sanitize' => FALSE));
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
          // Performance: for node view add access information right away.
125
          'access' => !$matches[2] ? $node_translation->status : NULL,
126
        );
127
      }
128
      return $result;
129
    }
130
  }
131
}
132

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

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

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

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

    
200
  return $languages;
201
}
202

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
432
/**
433
 * Implements hook_form_BASE_FORM_ID_alter().
434
 */
435
function i18n_node_form_node_form_alter(&$form, $form_state) {
436
  $node = $form['#node'];
437
  /**
438
   * i18n has to override locale.module
439
   * drupal_alter() fails to order modules correctly in some cases
440
   * for example specific hooks like hook_form_BASE_FORM_ID_alter
441
   *
442
   * its not possbile to reorder hook_form_BASE_FORM_ID_alter with
443
   * hook_module_implements_alter
444
   *
445
   * @see http://drupal.org/node/765860
446
  */
447

    
448
  // Replace core's node submit callback with our own,
449
  // in order to translate the node type name.
450
  $key = array_search('node_form_submit', $form['actions']['submit']['#submit']);
451
  if ($key !== FALSE) {
452
    $form['actions']['submit']['#submit'][$key] = 'i18n_node_form_submit';
453
  }
454

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

    
459

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

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

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

    
497
/**
498
 * Implements hook_form_BASE_FORM_ID_alter().
499
 * Called by i18n_node_form_node_form_alter
500
 */
501
function _i18n_node_form_node_form_alter($form, &$form_state) {
502
  $node = $form['#node'];
503
  if (i18n_node_type_enabled($node)) {
504
    if (!empty($form['language']['#options'])) {
505
      $form['language']['#options'] = i18n_node_language_list($node, TRUE, TRUE);
506
    }
507
  }
508
  elseif (variable_get('i18n_node_default_language_none', 0) && !isset($form['#node']->nid)) {
509
    // Override locale module setting default language to nodes. It is already in form_state.
510
    $form['language']['#value'] = $form_state['values']['language'] = LANGUAGE_NONE;
511
  }
512
  // Translate field names for title and body for the node edit form.
513
  if (!empty($form['title']['#title'])) {
514
    $form['title']['#title'] = i18n_node_translate_type($node->type, 'title_label', $form['title']['#title']);
515
  }
516
  if (!empty($form['body_field']['body']['#title'])) {
517
    $form['body_field']['body']['#title'] = i18n_node_translate_type($node->type, 'body', $form['body_field']['body']['#title']);
518
  }
519
  // Translate page title for node/add/% and node/%/edit pages.
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_type_name($node->type))), 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_type_name($node->type), '@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
 * Get translated node type name (unfiltered)
557
 *
558
 * @param string $type
559
 *   Node type.
560
 * @param string $name
561
 *   Optional node type name.
562
 */
563
function i18n_node_type_name($type, $name = NULL) {
564
  $name = isset($name) ? $name : node_type_get_name($type);
565
  return i18n_string_translate(array('node', 'type', $type, 'name'), $name, array('sanitize' => FALSE));
566
}
567

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