i18n_sync.node.inc 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <?php
  2. /**
  3. * @file
  4. * Internationalization (i18n) package. Synchronization of translations
  5. *
  6. * Node synchronization.
  7. */
  8. /**
  9. * Synchronizes fields for node translation.
  10. *
  11. * There's some specific handling for known fields like:
  12. * - files, for file attachments.
  13. * - iid (CCK node attachments, translations for them will be handled too).
  14. *
  15. * All the rest of the fields will be just copied over.
  16. * The 'revision' field will have the special effect of creating a revision too for the translation.
  17. *
  18. * @param $node
  19. * Source node being edited.
  20. * @param $translations
  21. * Node translations to synchronize, just needs nid property.
  22. * @param $fields
  23. * List of fields to synchronize.
  24. * @param $op
  25. * Node operation (insert|update).
  26. */
  27. function i18n_sync_node_translation($node, $translations, $field_names, $op) {
  28. $total = count($translations);
  29. $count = 0;
  30. // Disable language selection and synchronization temporarily, enable it again later
  31. $i18n_select = i18n_select(FALSE);
  32. i18n_sync(FALSE);
  33. foreach ($translations as $translation) {
  34. // If translation is the same node, we cannot synchronize with itself
  35. if ($node->nid == $translation->nid) {
  36. $total--;
  37. continue;
  38. }
  39. // Load full node, we need all data here.
  40. entity_get_controller('node')->resetCache(array($translation->nid));
  41. $translation = node_load($translation->nid);
  42. $i18n_options = i18n_sync_node_options($node->type);
  43. // Invoke callback for each field, the default is just copy over
  44. foreach ($field_names as $field) {
  45. if (!empty($i18n_options[$field]['field_name'])) {
  46. i18n_sync_field_translation_sync('node', $node->type, $translation, $translation->language, $node, $node->language, $i18n_options[$field]['field_name']);
  47. }
  48. elseif (isset($node->$field)) {
  49. // Standard node field, just copy over.
  50. $translation->$field = $node->$field;
  51. }
  52. }
  53. // Give a chance to other modules for additional sync
  54. module_invoke_all('i18n_sync_translation', 'node', $translation, $translation->language, $node, $node->language, $field_names);
  55. node_save($translation);
  56. $count++;
  57. // Flush each entity from the load cache after processing, to
  58. // avoid exceeding PHP memory limits. It should be safe to keep
  59. // at least one, however; so we retain the final translation in
  60. // the cache after saving it.
  61. if ($count < $total) {
  62. entity_get_controller('node')->resetCache(array($translation->nid));
  63. }
  64. }
  65. i18n_sync(TRUE);
  66. i18n_select($i18n_select);
  67. drupal_set_message(format_plural($count, 'One node translation has been synchronized.', 'All @count node translations have been synchronized.'));
  68. }
  69. /**
  70. * Node attachments (CCK) that may have translation.
  71. */
  72. function i18n_sync_node_translation_attached_node(&$node, &$translation, $field) {
  73. if ($attached = node_load($node->$field)) {
  74. $translation->$field = i18n_sync_node_translation_reference_field($attached, $node->$field, $translation->language);
  75. }
  76. }
  77. /**
  78. * Translating a nodereference field (cck).
  79. */
  80. function i18n_sync_node_translation_nodereference_field(&$node, &$translation, $field) {
  81. $translated_references = array();
  82. foreach ($node->$field as $reference) {
  83. if ($reference_node = node_load($reference['nid'])) {
  84. $translated_references[] = array(
  85. 'nid' => i18n_sync_node_translation_reference_field($reference_node, $reference['nid'], $translation->language)
  86. );
  87. }
  88. }
  89. $translation->$field = $translated_references;
  90. }
  91. /**
  92. * Helper function to which translates reference field. We try to use translations for reference, otherwise fallback.
  93. * Example:
  94. * English A references English B and English C.
  95. * English A and B are translated to German A and B, but English C is not.
  96. * The syncronization from English A to German A would it German B and English C.
  97. */
  98. function i18n_sync_node_translation_reference_field(&$reference_node, $default_value, $langcode) {
  99. if (isset($reference_node->tnid) && translation_supported_type($reference_node->type)) {
  100. // This content type has translations, find the one.
  101. if (($reference_trans = translation_node_get_translations($reference_node->tnid)) && isset($reference_trans[$langcode])) {
  102. return $reference_trans[$langcode]->nid;
  103. }
  104. else {
  105. // No requested language found, just copy the field.
  106. return $default_value;
  107. }
  108. }
  109. else {
  110. // Content type without language, just copy the field.
  111. return $default_value;
  112. }
  113. }
  114. /**
  115. * Synchronize configurable field
  116. *
  117. * @param $field_info
  118. * Field API field information.
  119. */
  120. function i18n_sync_node_translation_default_field($node, $translation, $field, $field_info) {
  121. switch ($field_info['field']['type']) {
  122. case 'file':
  123. case 'image':
  124. i18n_sync_node_translation_file_field($node, $translation, $field);
  125. break;
  126. default:
  127. // For fields that don't need special handling, just copy it over if defined.
  128. // Field languages are completely unconsistent, for not to say broken
  129. // both in Drupal core and entity translation. Let's hope this works.
  130. $source_lang = field_language('node', $node, $field);
  131. $translation_lang = field_language('node', $translation, $field);
  132. if (isset($node->{$field}[$source_lang])) {
  133. $translation->{$field}[$translation_lang] = $node->{$field}[$source_lang];
  134. }
  135. break;
  136. }
  137. }
  138. /**
  139. * Sync a file or image field:
  140. * - file-id's (fid) are synced
  141. * - order of fid's is synced
  142. * - description, alt, title is kept if already existing, copied otherwise
  143. *
  144. * @param object $node the node whose changes are to be synced
  145. * @param object $translation a node to which the changes need to be synced
  146. * @param string $field field name
  147. */
  148. function i18n_sync_node_translation_file_field($node, $translation, $field) {
  149. if (isset($node->{$field}[$node->language])) {
  150. // Build a copy of the existing files in the translation node
  151. // indexed by fid for easy retrieval in the copy loop below
  152. $existing_files = array();
  153. if (isset($translation->{$field}[$translation->language])) {
  154. foreach ($translation->{$field}[$translation->language] as $delta => $file) {
  155. $existing_files[$file['fid']] = $file;
  156. }
  157. }
  158. // Start creating the translated copy
  159. $translated_files = $node->{$field}[$node->language];
  160. foreach ($translated_files as $delta => &$file) {
  161. // keep alt, title, description if they already exist
  162. if (array_key_exists($file['fid'], $existing_files)) {
  163. foreach (array('title', 'description', 'alt') as $property) {
  164. if (!empty($existing_files[$file['fid']][$property])) {
  165. $file[$property] = $existing_files[$file['fid']][$property];
  166. }
  167. }
  168. }
  169. }
  170. $translation->{$field}[$translation->language] = $translated_files;
  171. }
  172. }