diff --git a/entity_translation.module b/entity_translation.module index 4728df1..d03411a 100644 --- a/entity_translation.module +++ b/entity_translation.module @@ -683,6 +683,144 @@ function theme_entity_translation_unavailable($variables) { } /** + * Implements hook_field_info_alter(). + */ +function entity_translation_field_info_alter(&$info) { + $columns = array('fid'); + $supported_types = array('file' => $columns, 'image' => $columns); + + foreach ($info as $field_type => &$field_type_info) { + // Store columns to be synchronized. + $field_type_info['settings'] += array( + 'entity_translation_sync' => isset($supported_types[$field_type]) ? $supported_types[$field_type] : FALSE, + ); + // Synchronization can be enabled per instance. + $field_type_info['instance_settings'] += array( + 'entity_translation_sync' => FALSE, + ); + } +} + +/** + * Implements hook_field_attach_presave(). + */ +function entity_translation_field_attach_presave($entity_type, $entity) { + if (entity_translation_enabled($entity_type)) { + entity_translation_sync($entity_type, $entity); + } +} + +/** + * Performs field column synchronization. + */ +function entity_translation_sync($entity_type, $entity) { + // If we are creataing a new entitiy or if we have no translations for the + // current entity, there is nothing to synchronize. + $handler = entity_translation_get_handler($entity_type, $entity, TRUE); + $translations = $handler->getTranslations(); + $original_langcode = $handler->getSourceLanguage(); + if ($handler->isNewEntity() || (count($translations->data) < 2 && !$original_langcode)) { + return; + } + + list($id, $rid, $bundle) = entity_extract_ids($entity_type, $entity); + $instances = field_info_instances($entity_type, $bundle); + $entity_unchanged = isset($entity->original) ? $entity->original : entity_load_unchanged($entity_type, $id); + + // If the entity language is being changed there is nothing to synchronize. + $langcode = $handler->getLanguage(); + $handler->setEntity($entity_unchanged); + if ($langcode != $handler->getLanguage()) { + return; + } + + foreach ($instances as $field_name => $instance) { + $field = field_info_field($field_name); + + // If the field is empty or has no translations there is nothing to + // synchronize. Synchronization makes sense only for translatable fields. + if (!empty($entity->{$field_name}) && count($entity->{$field_name}) > 1 && !empty($instance['settings']['entity_translation_sync']) && field_is_translatable($entity_type, $field)) { + $columns = $field['settings']['entity_translation_sync']; + $change_map = array(); + $source_langcode = entity_language($entity_type, $entity); + $source_items = $entity->{$field_name}[$source_langcode]; + + // If a translation is being created, the original values should be used + // as the unchanged items. In fact there are no unchanged items to check + // against. + $langcode = $original_langcode ? $original_langcode : $source_langcode; + $unchanged_items = !empty($entity_unchanged->{$field_name}[$langcode]) ? $entity_unchanged->{$field_name}[$langcode] : array(); + + // By picking the maximum size between updated and unchanged items, we + // make sure to process also removed items. + $total = max(array(count($source_items), count($unchanged_items))); + + // Make sure we can detect any change in the source items. + for ($delta = 0; $delta < $total; $delta++) { + foreach ($columns as $column) { + // Store the delta for the unchanged column value. + if (isset($unchanged_items[$delta][$column])) { + $value = $unchanged_items[$delta][$column]; + $change_map[$column][$value]['old'] = $delta; + } + // Store the delta for the new column value. + if (isset($source_items[$delta][$column])) { + $value = $source_items[$delta][$column]; + $change_map[$column][$value]['new'] = $delta; + } + } + } + + // Backup field values. + $field_values = $entity->{$field_name}; + + // Reset field values so that no spurious value is stored. Source values + // must be preserved in any case. + $entity->{$field_name} = array($source_langcode => $source_items); + + // Update translations. + foreach ($field_values as $langcode => $items) { + // We need to synchronize only values different from the source ones. + if ($langcode != $source_langcode) { + // Process even removed items. + for ($delta = 0; $delta < $total; $delta++) { + $created = TRUE; + $removed = TRUE; + + foreach ($columns as $column) { + if (isset($source_items[$delta][$column])) { + $value = $source_items[$delta][$column]; + $created = $created && !isset($change_map[$column][$value]['old']); + $removed = $removed && !isset($change_map[$column][$value]['new']); + } + } + + // If an item has been removed we do not store its translations. + if ($removed) { + continue; + } + // If a synchronized column has changed we need to override the full + // items array for all languages. + elseif ($created) { + $entity->{$field_name}[$langcode][$delta] = $source_items[$delta]; + } + // The current item might have been reordered. + elseif (!empty($change_map[$column][$value])) { + $old_delta = $change_map[$column][$value]['old']; + $new_delta = $change_map[$column][$value]['new']; + // If for nay reason the old value is not defined for the current + // we language we fall back to the new source value. + $items = isset($field_values[$langcode][$old_delta]) ? $field_values[$langcode][$old_delta] : $source_items[$new_delta]; + $entity->{$field_name}[$langcode][$new_delta] = $items; + } + } + } + } + } + } +} + +/** * Implements hook_field_attach_insert(). */ function entity_translation_field_attach_insert($entity_type, $entity) { @@ -1056,11 +1194,25 @@ function theme_entity_translation_language_tabs($variables) { /** * Implements hook_form_FORM_ID_alter(). * + * Adds an option to enable field synchronization. * Enable a selector to choose whether a field is translatable. */ function entity_translation_form_field_ui_field_edit_form_alter(&$form, $form_state) { - $field_name = $form['#field']['field_name']; + $instance = $form['#instance']; + $entity_type = $instance['entity_type']; + $field_name = $instance['field_name']; $field = field_info_field($field_name); + + if (!empty($field['settings']['entity_translation_sync']) && field_is_translatable($entity_type, $field)) { + $form['instance']['settings']['entity_translation_sync'] = array( + '#prefix' => '', + '#type' => 'checkbox', + '#title' => t('Enable field synchronization'), + '#description' => t('Check this option if you wish to synchronize the value of this field accross its translations.'), + '#default_value' => !empty($instance['settings']['entity_translation_sync']), + ); + } + $translatable = $field['translatable']; $label = t('Field translation'); $title = t('Users may translate this field.');