Projet

Général

Profil

Paste
Télécharger (21,5 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / i18n / i18n_node / i18n_node.module @ e0d35157

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
    $default = variable_get('i18n_node_default_language_for_' . $node->type, '-- current --');
226

    
227
    // Set current language for new nodes if option enabled
228
    if ($default === '-- current --') {
229
      $node->language = i18n_language_content()->language;
230
    }
231
    // If a custom language was specified, apply it.
232
    else {
233
      $node->language = $default;
234
    }
235
  }
236
}
237

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

    
257
/**
258
 * Implements hook_node_view()
259
 */
260
function i18n_node_node_view($node) {
261
  if (i18n_node_type_enabled($node)) {
262
    $node->content['language'] = array(
263
      '#type' => 'item',
264
      '#title' => t('Language'),
265
      '#markup' => i18n_language_name($node->language),
266
    );
267
  }
268
}
269

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

    
310
/**
311
 * Implements hook_node_type_insert()
312
 */
313
function i18n_node_node_type_insert($info) {
314
  i18n_node_node_type_update($info);
315
}
316

    
317
/**
318
 * Implements hook_node_type_update()
319
 */
320
function i18n_node_node_type_update($info) {
321
  if (!empty($info->old_type) && $info->old_type != $info->type) {
322
    i18n_string_update_context("node:type:$info->old_type:*", "node:type:$info->type:*");
323
  }
324
  i18n_string_object_update('node_type', $info);
325
}
326

    
327
/**
328
 * Implements hook_node_type_delete()
329
 */
330
function i18n_node_node_type_delete($info) {
331
  i18n_string_object_remove('node_type', $info);
332
}
333

    
334
/**
335
 * Implements hook_elements().
336
 *
337
 * Add a process callback for textfields.
338
 *
339
 *  * @todo Update D7
340
 */
341
/*
342
function i18n_node_elements() {
343
  $type = array();
344
  $type['textfield'] = array('#process' => array('i18n_node_textfield_process'));
345
  return $type;
346
}
347
*/
348

    
349
/**
350
 * Process callback for textfield elements.
351
 *
352
 * When editing or translating a node, set Javascript to rewrite autocomplete
353
 * paths to use the node language prefix rather than the current content one.
354
 *
355
 * @todo Update D7
356
 */
357
function i18n_node_textfield_process($element) {
358
  global $language;
359
  static $sent = FALSE;
360

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

    
392
/**
393
 * Implements hook_form_FORM_ID_alter().
394
 */
395
function i18n_node_form_search_form_alter(&$form, &$form_state) {
396
  // Advanced search form. Add language and localize content type names
397
  if ($form['module']['#value'] == 'node' && !empty($form['advanced'])) {
398
    // @todo Handle language search conditions
399
    //$form['advanced']['language'] = _i18n_language_select();
400
    if (!empty($form['advanced']['type'])) {
401
      foreach ($form['advanced']['type']['#options'] as $type => $name) {
402
        $form['advanced']['type']['#options'][$type] = i18n_node_translate_type($type, 'name', $name);
403
      }
404
    }
405
  }
406
}
407

    
408
/**
409
 * Implements hook_form_FORM_ID_alter().
410
 */
411
function i18n_node_form_node_type_form_alter(&$form, &$form_state) {
412
  if (isset($form['type'])) {
413
    $disabled = !i18n_node_type_enabled($form['#node_type']);
414
    $form['i18n'] = array(
415
      '#type' => 'fieldset',
416
      '#title' => t('Multilingual settings'),
417
      '#collapsible' => TRUE,
418
      '#collapsed' => TRUE,
419
      '#group' => 'additional_settings',
420
      '#attributes' => array(
421
        'class' => array('i18n-node-type-settings-form'),
422
      ),
423
      '#description' => t('Extended multilingual options provided by Internationalization module.'),
424
      '#disabled' => $disabled,
425
    );
426

    
427
    // Some settings about node languages. Add variables for node type from variable definition
428
    if ($form['#node_type']->type) {
429
      variable_type_include('node_type');
430
      $form['i18n'] += node_variable_type_subform($form['#node_type']->type, array('i18n_node_options', 'i18n_node_default_language_for', 'i18n_node_extended'));
431
      // Only show custom default language field if "current" is checked.
432
      $form['i18n']['i18n_node_default_language_for']['#states'] = array(
433
        'visible' => array(
434
          ':input[name="i18n_node_options[current]"]' => array('checked' => TRUE),
435
        ),
436
        'required' => array(
437
          ':input[name="i18n_node_options[current]"]' => array('checked' => TRUE),
438
        ),
439
      );
440
    }
441
    // Add disabled message
442
    if ($disabled) {
443
      $form['i18n']['#description'] .= ' <em>' . t('These will be available only when you enable Multilingual support in Publishing options above.') . '</em>';
444
      foreach (element_children($form['i18n']) as $key) {
445
        $form['i18n'][$key]['#disabled'] = TRUE;
446
      }
447
    }
448
  }
449
}
450

    
451
/**
452
 * Implements hook_form_BASE_FORM_ID_alter().
453
 */
454
function i18n_node_form_node_form_alter(&$form, $form_state) {
455
  $node = $form['#node'];
456
  /**
457
   * i18n has to override locale.module
458
   * drupal_alter() fails to order modules correctly in some cases
459
   * for example specific hooks like hook_form_BASE_FORM_ID_alter
460
   *
461
   * its not possbile to reorder hook_form_BASE_FORM_ID_alter with
462
   * hook_module_implements_alter
463
   *
464
   * @see http://drupal.org/node/765860
465
  */
466

    
467
  // Replace core's node submit callback with our own,
468
  // in order to translate the node type name.
469
  $key = array_search('node_form_submit', $form['actions']['submit']['#submit']);
470
  if ($key !== FALSE) {
471
    $form['actions']['submit']['#submit'][$key] = 'i18n_node_form_submit';
472
  }
473

    
474
  // call a 'private' implemenation of i18n_node_form_node_form_alter()
475
  $form['#after_build'][] = '_i18n_node_form_node_form_alter';
476
}
477

    
478

    
479
/**
480
 * Replacement for core's node_form_submit(), taking care of
481
 * translating node type names.
482
 */
483
function i18n_node_form_submit($form, &$form_state) {
484
  $node = node_form_submit_build_node($form, $form_state);
485
  $insert = empty($node->nid);
486
  node_save($node);
487
  $node_link = l(t('view'), 'node/' . $node->nid);
488
  $type_name = i18n_node_type_name($node->type);
489

    
490
  $watchdog_args = array('@type' => $node->type, '%title' => $node->title);
491
  $t_args = array('@type' => $type_name, '%title' => $node->title);
492

    
493
  if ($insert) {
494
    watchdog('content', '@type: added %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
495
    drupal_set_message(t('@type %title has been created.', $t_args));
496
  }
497
  else {
498
    watchdog('content', '@type: updated %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
499
    drupal_set_message(t('@type %title has been updated.', $t_args));
500
  }
501
  if ($node->nid) {
502
    $form_state['values']['nid'] = $node->nid;
503
    $form_state['nid'] = $node->nid;
504
    $form_state['redirect'] = node_access('view', $node) ? 'node/' . $node->nid : '<front>';
505
  }
506
  else {
507
    // In the unlikely case something went wrong on save, the node will be
508
    // rebuilt and node form redisplayed the same way as in preview.
509
    drupal_set_message(t('The post could not be saved.'), 'error');
510
    $form_state['rebuild'] = TRUE;
511
  }
512
  // Clear the page and block caches.
513
  cache_clear_all();
514
}
515

    
516
/**
517
 * Implements hook_form_BASE_FORM_ID_alter().
518
 * Called by i18n_node_form_node_form_alter
519
 */
520
function _i18n_node_form_node_form_alter($form, &$form_state) {
521
  $node = $form['#node'];
522
  if (i18n_node_type_enabled($node)) {
523
    if (!empty($form['language']['#options'])) {
524
      $form['language']['#options'] = i18n_node_language_list($node, TRUE, TRUE);
525
    }
526
  }
527
  elseif (variable_get('i18n_node_default_language_none', 0) && !isset($form['#node']->nid)) {
528
    // Only do this if the language is really disabled
529
    if (variable_get('language_content_type_' . $node->type, 0) == 0) {
530
      // Override locale module setting default language to nodes. It is already in form_state.
531
      $form['language']['#value'] = $form_state['values']['language'] = LANGUAGE_NONE;
532
    }
533
  }
534
  // Translate field names for title and body for the node edit form.
535
  if (!empty($form['title']['#title'])) {
536
    $form['title']['#title'] = i18n_node_translate_type($node->type, 'title_label', $form['title']['#title']);
537
  }
538
  if (!empty($form['body_field']['body']['#title'])) {
539
    $form['body_field']['body']['#title'] = i18n_node_translate_type($node->type, 'body', $form['body_field']['body']['#title']);
540
  }
541
  // Translate page title for node/add/% and node/%/edit pages.
542
  if (empty($node->nid) && strpos($_GET['q'], 'node/add/' . str_replace('_', '-', $node->type)) === 0) {
543
    drupal_set_title(t('Create @name', array('@name' => i18n_node_type_name($node->type))), PASS_THROUGH);
544
  }
545
  elseif (!empty($node->nid) && $_GET['q'] == 'node/' . $node->nid . '/edit') {
546
    drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => i18n_node_type_name($node->type), '@title' => $node->title)), PASS_THROUGH);
547
  }
548
  return $form;
549
}
550

    
551
/**
552
 * Implements hook_theme().
553
 */
554
function i18n_node_theme() {
555
  return array(
556
    'i18n_node_select_translation' => array(
557
      'render element' => 'element',
558
      'file' => 'i18n_node.pages.inc',
559
    ),
560
  );
561
}
562

    
563
/**
564
 * Shorthand for translating node type strings
565
 *
566
 * @param $type
567
 *   Node type name or full object
568
 */
569
function i18n_node_translate_type($type, $property = 'name', $source = NULL, $options = array()) {
570
  if (is_object($type)) {
571
    $source = $type->$property;
572
    $type = $type->type;
573
  }
574
  return i18n_string_translate(array('node', 'type', $type, $property), $source, $options);
575
}
576

    
577
/**
578
 * Get translated node type name (unfiltered)
579
 *
580
 * @param string $type
581
 *   Node type.
582
 * @param string $name
583
 *   Optional node type name.
584
 */
585
function i18n_node_type_name($type, $name = NULL) {
586
  $name = isset($name) ? $name : node_type_get_name($type);
587
  return i18n_string_translate(array('node', 'type', $type, 'name'), $name, array('sanitize' => FALSE));
588
}
589

    
590
/**
591
 * Check whether this is a language enabled node type
592
 *
593
 * @param $type
594
 *   Node, node type object, or node type name
595
 */
596
function i18n_node_type_enabled($type) {
597
  $type = is_object($type) ? $type->type : $type;
598
  $mode = variable_get('language_content_type_' . $type, 0);
599
  return $mode == 1 || $mode == 2; // 2 == TRANSLATION_ENABLED
600
}