Projet

Général

Profil

Paste
Télécharger (12,2 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / i18n / i18n_node / i18n_node.pages.inc @ 9faa5de0

1
<?php
2

    
3
/**
4
 * @file
5
 * User page callbacks for the translation module.
6
 */
7

    
8

    
9
/**
10
 * Replacement for node_add_page.
11
 */
12
function i18n_node_add_page() {
13
  $item = menu_get_item();
14
  $content = system_admin_menu_block($item);
15
  // Bypass the node/add listing if only one content type is available.
16
  if (count($content) == 1) {
17
    $item = array_shift($content);
18
    drupal_goto($item['href']);
19
  }
20
  foreach ($content as &$item) {
21
    // Type machine name will be the first page argument
22
    $page_arguments = unserialize($item['page_arguments']);
23
    // Check whether this has a node type, other items may be here too, see #1264662
24
    $type = isset($page_arguments[0]) ? $page_arguments[0] : NULL;
25
    if ($type) {
26
      // We just need to translate the description, the title is translated by the menu system
27
      // The string will be filtered (xss_admin) on the theme layer
28
      $item['description'] = i18n_node_translate_type($type, 'description', $item['description'], array('sanitize' => FALSE));
29
    }
30
  }
31
  return theme('node_add_list', array('content' => $content));
32
}
33

    
34
/**
35
 * Overview page for a node's translations.
36
 *
37
 * @param $node
38
 *   Node object.
39
 */
40
function i18n_node_translation_overview($node) {
41
  include_once DRUPAL_ROOT . '/includes/language.inc';
42

    
43
  if (!empty($node->tnid)) {
44
    // Already part of a set, grab that set.
45
    $tnid = $node->tnid;
46
    $translations = translation_node_get_translations($node->tnid);
47
  }
48
  else {
49
    // We have no translation source nid, this could be a new set, emulate that.
50
    $tnid = $node->nid;
51
    $translations = array($node->language => $node);
52
  }
53

    
54
  $type = variable_get('translation_language_type', LANGUAGE_TYPE_INTERFACE);
55
  $header = array(t('Language'), t('Title'), t('Status'), t('Operations'));
56

    
57
  // Modes have different allowed languages
58
  foreach (i18n_node_language_list($node) as $langcode => $language_name) {
59
    if ($langcode == LANGUAGE_NONE) {
60
      // Never show language neutral on the overview.
61
      continue;
62
    }
63
    $options = array();
64
    if (isset($translations[$langcode])) {
65
      // Existing translation in the translation set: display status.
66
      // We load the full node to check whether the user can edit it.
67
      $translation_node = node_load($translations[$langcode]->nid);
68
      $path = 'node/' . $translation_node->nid;
69
      $title = i18n_node_translation_link($translation_node->title, $path, $langcode);
70
      if (node_access('update', $translation_node)) {
71
        $text = t('edit');
72
        $path = 'node/' . $translation_node->nid . '/edit';
73
        $options[] = i18n_node_translation_link($text, $path, $langcode);
74
      }
75
      $status = $translation_node->status ? t('Published') : t('Not published');
76
      $status .= $translation_node->translate ? ' - <span class="marker">' . t('outdated') . '</span>' : '';
77
      if ($translation_node->nid == $tnid) {
78
        $language_name = t('<strong>@language_name</strong> (source)', array('@language_name' => $language_name));
79
      }
80
    }
81
    else {
82
      // No such translation in the set yet: help user to create it.
83
      $title = t('n/a');
84
      if (node_access('create', $node->type)) {
85
        $text = t('add translation');
86
        $path = 'node/add/' . str_replace('_', '-', $node->type);
87
        $query = array('query' => array('translation' => $node->nid, 'target' => $langcode));
88
        $options[] = i18n_node_translation_link($text, $path, $langcode, $query);
89
      }
90
      $status = t('Not translated');
91
    }
92
    $rows[] = array($language_name, $title, $status, implode(" | ", $options));
93
  }
94

    
95
  drupal_set_title(t('Translations of %title', array('%title' => $node->title)), PASS_THROUGH);
96

    
97
  $build['translation_node_overview'] = array(
98
    '#theme' => 'table',
99
    '#header' => $header,
100
    '#rows' => $rows,
101
  );
102

    
103
  if (user_access('administer content translations')) {
104
    $build['translation_node_select'] = drupal_get_form('i18n_node_select_translation', $node, $translations);
105
  }
106
  return $build;
107
}
108

    
109
/**
110
 * Create link for node translation. This may be a 'edit' or a 'add translation' link.
111
 */
112
function i18n_node_translation_link($text, $path, $langcode, $options = array()) {
113
  $type = variable_get('translation_language_type', LANGUAGE_TYPE_INTERFACE);
114
  $links = language_negotiation_get_switch_links($type, $path);
115
  // When node not published, links don't have href so we use path instead
116
  // Note: this is a bug in Core translation module, see http://drupal.org/node/1137074
117
  if (!empty($links->links[$langcode]) && !empty($links->links[$langcode]['href'])) {
118
    $options += array('attributes' => array(), 'html' => FALSE);
119
    $options['attributes'] += $links->links[$langcode]['attributes'];
120
    $options += $links->links[$langcode];
121
    $path = $links->links[$langcode]['href'];
122
  }
123
  return l($text, $path, $options);
124
}
125

    
126
/**
127
 * Form to select existing nodes as translation
128
 *
129
 * This one uses autocomplete fields for all languages
130
 */
131
function i18n_node_select_translation($form, &$form_state, $node, $translations) {
132
  $form['node'] = array('#type' => 'value', '#value' => $node);
133
  $form['translations'] = array(
134
    '#type' => 'fieldset',
135
    '#title' => t('Select translations for %title', array('%title' => $node->title)),
136
    '#tree' => TRUE,
137
    '#theme' => 'i18n_node_select_translation',
138
    '#description' => t("Alternatively, you can select existing nodes as translations of this one or remove nodes from this translation set. Only nodes that have the right language and don't belong to other translation set will be available here.")
139
  );
140
  foreach (i18n_node_language_list($node) as $langcode => $language_name) {
141
    if ($langcode != $node->language && $langcode != LANGUAGE_NONE) {
142
      $nid = isset($translations[$langcode]) ? $translations[$langcode]->nid : 0;
143
      $form['translations']['nid'][$langcode] = array(
144
        '#type' => 'value',
145
        '#value' => $nid,
146
      );
147
      $form['translations']['language'][$langcode] = array(
148
        '#type' => 'value',
149
        '#value' => $language_name,
150
      );
151
      $form['translations']['node'][$langcode] = array(
152
        '#type' => 'textfield',
153
        '#maxlength' => 255,
154
        '#autocomplete_path' => 'i18n/node/autocomplete/' . $node->type . '/' . $langcode,
155
        '#default_value' => $nid ? i18n_node_nid2autocomplete($nid) : '',
156
      );
157
    }
158
  }
159
  $form['actions'] = array('#type' => 'actions');
160
  $form['actions']['update'] = array(
161
    '#type' => 'submit',
162
    '#value' => t('Update translations'),
163
  );
164
  //$form['buttons']['clean'] = array('#type' => 'submit', '#value' => t('Delete translation set'));
165
  return $form;
166
}
167

    
168
/**
169
 * Form validation
170
 */
171
function i18n_node_select_translation_validate($form, &$form_state) {
172
  foreach ($form_state['values']['translations']['node'] as $lang => $title) {
173
    if (!$title) {
174
      $nid = 0;
175
    }
176
    else {
177
      $nid = i18n_node_autocomplete2nid($title, "translations][node][$lang", array($form_state['values']['node']->type), array($lang));
178
    }
179
    $form_state['values']['translations']['nid'][$lang] = $nid;
180
  }
181
}
182

    
183
/**
184
 * Form submission: update / delete the translation set
185
 */
186
function i18n_node_select_translation_submit($form, &$form_state) {
187
  $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : NULL;
188
  $node = $form_state['values']['node'];
189
  $translations = $node->tnid ? translation_node_get_translations($node->tnid) : array($node->language => $node);
190
  foreach ($translations as $trans) {
191
    $current[$trans->language] = $trans->nid;
192
  }
193
  $update = array($node->language => $node->nid) + array_filter($form_state['values']['translations']['nid']);
194
  // Compute the difference to see which are the new translations and which ones to remove
195
  $new = array_diff_assoc($update, $current);
196
  $remove = array_diff_assoc($current, $update);
197

    
198
  // The tricky part: If the existing source is not in the new set, we need to create a new tnid
199
  if ($node->tnid && in_array($node->tnid, $update)) {
200
    $tnid = $node->tnid;
201
    $add = $new;
202
  }
203
  else {
204
    // Create new tnid, which is the source node
205
    $tnid = $node->nid;
206
    $add = $update;
207
  }
208
  // Now update values for all nodes
209
  if ($add) {
210
    db_update('node')
211
      ->fields(array(
212
        'tnid' => $tnid,
213
      ))
214
      ->condition('nid', $add)
215
      ->execute();
216
    entity_get_controller('node')->resetCache($add);
217
    if (count($new)) {
218
      drupal_set_message(format_plural(count($new), 'Added a node to the translation set.', 'Added @count nodes to the translation set.'));
219
    }
220
  }
221
  if ($remove) {
222
    db_update('node')
223
      ->fields(array(
224
        'tnid' => 0,
225
      ))
226
      ->condition('nid', $remove)
227
      ->execute();
228
    entity_get_controller('node')->resetCache($remove);
229
    drupal_set_message(format_plural(count($remove), 'Removed a node from the translation set.', 'Removed @count nodes from the translation set.'));
230
  }
231
}
232

    
233
/**
234
 * Node title autocomplete callback
235
 */
236
function i18n_node_autocomplete($type, $language, $string = '') {
237
  $params = array('type' => $type, 'language' => $language, 'tnid' => 0);
238
  $matches = array();
239
  foreach (_i18n_node_references($string, 'contains', $params) as $id => $row) {
240
    // Add a class wrapper for a few required CSS overrides.
241
    $matches[$row['title'] . " [nid:$id]"] = '<div class="reference-autocomplete">' . $row['rendered'] . '</div>';
242
  }
243
  drupal_json_output($matches);
244
}
245

    
246
/**
247
 * Generates 'title [nid:$nid]' for the autocomplete field
248
 */
249
function i18n_node_nid2autocomplete($nid) {
250
  if ($node = node_load($nid)) {
251
    return $node->title . ' [nid:' . $nid . ']';
252
  }
253
  else {
254
    return t('Not found');
255
  }
256
}
257

    
258
/**
259
 * Reverse mapping from node title to nid
260
 *
261
 * We also handle autocomplete values (title [nid:x]) and validate the form
262
 */
263
function i18n_node_autocomplete2nid($name, $field, $type, $language) {
264
  if (!empty($name)) {
265
    preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $name, $matches);
266
    if (!empty($matches)) {
267
      // Explicit [nid:n].
268
      list(, $title, $nid) = $matches;
269
      if (!empty($title) && ($node = node_load($nid)) && $title != $node->title) {
270
        if ($field) {
271
          form_set_error($field, t('Node title mismatch. Please check your selection.'));
272
        }
273
        $nid = NULL;
274
      }
275
    }
276
    else {
277
      // No explicit nid.
278
      $reference = _i18n_node_references($name, 'equals', array('type' => $type, 'language' => $language), 1);
279
      if (!empty($reference)) {
280
        $nid = key($reference);
281
      }
282
      elseif ($field) {
283
        form_set_error($field, t('Found no valid post with that title: %title', array('%title' => $name)));
284
      }
285
    }
286
  }
287
  return !empty($nid) ? $nid : NULL;
288
}
289

    
290
/**
291
 * Find node title matches.
292
 *
293
 * @param $string
294
 *   String to match against node title
295
 * @param $match
296
 *   Match mode: 'contains', 'equals', 'starts_with'
297
 * @param $params
298
 *   Other query arguments: type, language or numeric ones
299
 */
300
function _i18n_node_references($string, $match = 'contains', $params = array(), $limit = 10) {
301
  $query = db_select('node', 'n')
302
    ->fields('n', array('nid', 'title' , 'type'))
303
    ->orderBy('n.title')
304
    ->orderBy('n.type')
305
    ->range(0, $limit);
306

    
307
  foreach ($params as $key => $value) {
308
    $query->condition($key, $value);
309
  }
310

    
311
  switch ($match) {
312
    case 'equals':
313
      $query->condition('n.title', $string);
314
      break;
315

    
316
    case 'starts_with':
317
      $query->condition('n.title', $string . '%', 'LIKE');
318
      break;
319

    
320
    case 'contains':
321
    default:
322
      $query->condition('n.title', '%' . $string . '%', 'LIKE');
323
      break;
324
  }
325

    
326
  // Disable and reenable i18n selection mode so no language conditions are inserted
327
  i18n_select(false);
328
  $references = array();
329
  foreach ($query->execute() as $node) {
330
    $references[$node->nid] = array(
331
      'title' => $node->title,
332
      'rendered' => check_plain($node->title),
333
    );
334
  }
335
  i18n_select(true);
336
  return $references;
337
}
338

    
339
/**
340
 * Theme select translation form
341
 * @ingroup themeable
342
 */
343
function theme_i18n_node_select_translation($variables) {
344
  $elements = $variables['element'];
345
  $output = '';
346
  if (isset($elements['nid'])) {
347
    $rows = array();
348
    foreach (element_children($elements['nid']) as $lang) {
349
      $rows[] = array(
350
        $elements['language'][$lang]['#value'],
351
        drupal_render($elements['node'][$lang]),
352
      );
353
    }
354
    $output .= theme('table', array('rows' => $rows));
355
    $output .= drupal_render_children($elements);
356
  }
357
  return $output;
358
}