Project

General

Profile

Paste
Download (21.7 KB) Statistics
| Branch: | Revision:

root / drupal7 / sites / all / modules / i18n / i18n_node / i18n_node.module @ 01f36513

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, $view_mode, $langcode) {
261
  if (i18n_node_type_enabled($node)) {
262
    $extra_fields_display_settings = field_extra_fields_get_display('node', $node->type, $view_mode);
263
    if ($extra_fields_display_settings['language']['visible']) {
264
      $node->content['language'] = array(
265
        '#type' => 'item',
266
        '#title' => t('Language'),
267
        '#markup' => i18n_language_name($node->language),
268
      );
269
    }
270
  }
271
}
272

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

    
313
/**
314
 * Implements hook_node_type_insert()
315
 */
316
function i18n_node_node_type_insert($info) {
317
  i18n_node_node_type_update($info);
318
}
319

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

    
330
/**
331
 * Implements hook_node_type_delete()
332
 */
333
function i18n_node_node_type_delete($info) {
334
  i18n_string_object_remove('node_type', $info);
335
}
336

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

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

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

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

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

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

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

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

    
477
  // call a 'private' implemenation of i18n_node_form_node_form_alter()
478
  $form['#after_build'][] = '_i18n_node_form_node_form_alter';
479
}
480

    
481

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

    
493
  $watchdog_args = array('@type' => $node->type, '%title' => $node->title);
494
  $t_args = array('@type' => $type_name, '%title' => $node->title);
495

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

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

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

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

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

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