diff --git a/entity_translation.admin.inc b/entity_translation.admin.inc
index 4c2c054..68fe2fb 100644
--- a/entity_translation.admin.inc
+++ b/entity_translation.admin.inc
@@ -489,3 +489,207 @@ function entity_translation_delete_confirm_submit($form, &$form_state) {
$form_state['redirect'] = "{$handler->getBasePath()}/translate";
}
+
+/*
+ * Confirm form for changing field translatability.
+ */
+function entity_translation_warning_form($form, &$form_state, $field_name) {
+
+ $field = field_info_field($field_name);
+
+ $warning = t('By submitting this form you will trigger a batch operation.');
+ if ($field['translatable']) {
+ $title = t('Are you sure you want to make the field @name untranslatable?', array('@name' => $field_name));
+ $warning .= "
" . t("All translations of this field will be deleted.
This action cannot be undone.");
+ }
+ else {
+ $title = t('Are you sure you want to make the field @name translatable?', array('@name' => $field_name));
+ }
+
+ $form = confirm_form($form, $title, '', $warning, t('Convert'), t('Cancel'));
+
+ // We need to keep some information for later processing.
+ $form['#etfield'] = $field['field_name'];
+ $form['translatable'] = array(
+ '#type' => 'hidden',
+ '#default_value' => $field['translatable'],
+ );
+
+ return $form;
+}
+
+/**
+ * Submit handler for the field settings form.
+ *
+ * This submit handler maintains consistency between the translatability of an
+ * entity and the language under which the field data is stored. When a field is
+ * marked as translatable, all the data in $entity->{field_name}[LANGUAGE_NONE]
+ * is moved to $entity->{field_name}[$entity_language]. When a field is marked
+ * as untranslatable the opposite process occurs. Note that marking a field as
+ * untranslatable will cause all of its translations to be permanently removed,
+ * with the exception of the one corresponding to the entity language.
+ */
+function entity_translation_warning_form_submit($form, $form_state) {
+ // This is the current state that we want to reverse.
+ $translatable = $form_state['values']['translatable'];
+ $field_name = $form['#etfield'];
+ $field = field_info_field($field_name);
+
+ if ($field['translatable'] !== $translatable) {
+ // Field translatability has changed since form creation, abort.
+ return;
+ }
+
+ // If a field is untranslatable, it can have no data except under
+ // LANGUAGE_NONE. Thus we need a field to be translatable before we convert
+ // data to the entity language. Conversely we need to switch data back to
+ // LANGUAGE_NONE before making a field untranslatable lest we lose
+ // information.
+ if ($translatable) {
+ $opts = array(
+ array('entity_translation_translatable_batch', array(!$translatable, $field_name)),
+ array('entity_translation_set_field_translatable', array(!$translatable, $field_name)),
+ );
+ }
+ else {
+ $opts = array(
+ array('entity_translation_set_field_translatable', array(!$translatable, $field_name)),
+ array('entity_translation_translatable_batch', array(!$translatable, $field_name)),
+ );
+ }
+
+ $batch = array(
+ 'title' => t('Converting @field to be @translatable.', array(
+ '@field' => $field_name,
+ '@translatable' => $translatable ? 'untranslatable' : 'translatable',
+ )),
+ 'operations' => $opts,
+ 'finished' => 'entity_translation_translatable_batch_done',
+ 'file' => drupal_get_path('module', 'entity_translation') . '/entity_translation.admin.inc',
+ );
+
+ batch_set($batch);
+}
+
+/*
+ * Toggle translatability of given field.
+ *
+ * This is called from a batch operation, but should only run once per field.
+ */
+function entity_translation_set_field_translatable($translatable, $field_name) {
+ $field = field_info_field($field_name);
+
+ if ($field['translatable'] === $translatable) {
+ return;
+ }
+
+ $field['translatable'] = $translatable;
+ field_update_field($field);
+
+ // This is important for versions 7.10 and lower (and maybe some others.).
+ // See http://drupal.org/node/1380660 for details.
+ drupal_static_reset('field_available_languages');
+}
+
+/**
+ * Batch operation. Convert field data to or from LANGUAGE_NONE.
+ */
+function entity_translation_translatable_batch($translatable, $field_name, &$context) {
+ if (empty($context['sandbox'])) {
+ $context['sandbox']['progress'] = 0;
+
+ // How many entities will need processing?
+ $query = new EntityFieldQuery();
+ $count = $query
+ ->fieldCondition($field_name)
+ ->count()
+ ->execute();
+
+ if (intval($count) === 0) {
+ // Nothing to do.
+ $context['finished'] = 1;
+ return;
+ }
+ $context['sandbox']['max'] = $count;
+ }
+
+ // Number of entities to be processed for each step.
+ $limit = variable_get('entity_translation_translatable_batch_limit', 10);
+
+ $query = new EntityFieldQuery();
+ $result = $query
+ ->fieldCondition($field_name)
+ ->range($context['sandbox']['progress'], $limit)
+ ->execute();
+
+ foreach ($result as $entity_type => $entities) {
+ foreach (entity_load($entity_type, array_keys($entities)) as $id => $entity) {
+ $context['sandbox']['progress']++;
+
+ $handler = entity_translation_get_handler($entity_type, $entity);
+ $langcode = $handler->getLanguage();
+
+ if ($translatable && isset($entity->{$field_name}[LANGUAGE_NONE])) {
+ // If the field is being switched to translatable and has data for
+ // LANGUAGE_NONE then we need to move the data to the right language.
+ $entity->{$field_name}[$langcode] = $entity->{$field_name}[LANGUAGE_NONE];
+ $entity->{$field_name}[LANGUAGE_NONE] = array();
+ }
+ elseif (!$translatable && isset($entity->{$field_name}[$langcode])) {
+ // The field has been marked untranslatable and has data in the entity
+ // language: we need to move it to LANGUAGE_NONE and drop the other
+ // translations.
+ $data = $entity->{$field_name}[$langcode];
+
+ // Check if the entity has any more translatable fields. If not, then
+ // we must delete the translation handler.
+ $has_translatables = FALSE;
+ $fields = field_info_instances($entity_type, $entity->type);
+ foreach ($fields as $field => $value) {
+ if ($field === $field_name) {
+ // We're setting this to untranslatable later.
+ continue;
+ }
+ $field = field_info_field($field);
+ if ($field['translatable']) {
+ $has_translatables = TRUE;
+ break;
+ }
+ }
+ dsm($has_translatables, "as");
+
+ foreach ($entity->{$field_name} as $lang => $items) {
+ if (!$has_translatables) {
+ // This also deletes the field data in $lang.
+ $handler->removeTranslation($lang);
+ }
+ else {
+ $entity->{$field_name}[$lang] = array();
+ }
+
+ }
+ $entity->{$field_name}[LANGUAGE_NONE] = $data;
+ }
+ else {
+ // No need to save unchanged entities.
+ continue;
+ }
+ field_attach_presave($entity_type, $entity);
+ field_attach_update($entity_type, $entity);
+ }
+ }
+ $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+}
+
+/**
+ * Check the exit status of the batch operation.
+ */
+function entity_translation_translatable_batch_done($success, $results, $operations) {
+ if ($success) {
+ drupal_set_message(t("Data successfully converted."));
+ }
+ else {
+ // @todo: Do something about this case.
+ drupal_set_message(t("Something went wrong when converting the data. Some nodes may appear to have lost fields."));
+ }
+}
diff --git a/entity_translation.module b/entity_translation.module
index 56f42d0..bbc39ad 100644
--- a/entity_translation.module
+++ b/entity_translation.module
@@ -120,6 +120,23 @@ function entity_translation_enabled($entity_type, $skip_handler = FALSE) {
}
/**
+ * Implments hook_menu().
+ */
+function entity_translation_menu() {
+ $items = array();
+
+ $items['admin/config/regional/entity_translation/translate/%'] = array(
+ 'title' => 'Confirm Change in translatability.',
+ 'description' => 'Confirm page for changing field translatability.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('entity_translation_warning_form', 5),
+ 'access arguments' => array('change field translatability'),
+ 'file' => 'entity_translation.admin.inc',
+ );
+ return $items;
+}
+
+/**
* Implements hook_menu_alter().
*/
function entity_translation_menu_alter(&$items) {
@@ -211,6 +228,7 @@ function entity_translation_menu_alter(&$items) {
return $items;
}
+
/**
* Implements hook_admin_paths().
*/
@@ -276,6 +294,10 @@ function entity_translation_permission() {
'title' => t('Administer entity translation'),
'description' => t('Select which entities can be translated.'),
),
+ 'change field translatability' => array(
+ 'title' => t('Change field translatability'),
+ 'description' => t('Change translatability of fields and perform bulk updates.'),
+ ),
);
foreach (entity_get_info() as $entity_type => $info) {
if ($info['fieldable']) {
@@ -481,12 +503,32 @@ function entity_translation_edit_form_submit($form, &$form_state) {
* Enable a selector to choose whether a field is translatable.
*/
function entity_translation_form_field_ui_field_settings_form_alter(&$form, $form_state) {
- $instance = $form_state['build_info']['args'][0];
- $field = field_info_field($instance['field_name']);
+ $field_name = $form['field']['field_name']['#value'];
+ $field = field_info_field($field_name);
+ $translatable = $field['translatable'];
+
+ if ($translatable) {
+ $status = t('This field is currently translatable.');
+ }
+ else {
+ $status = t('This field is currently untranslatable.');
+ }
+
+ $path = "admin/config/regional/entity_translation/translate/$field_name";
+
$form['field']['translatable'] = array(
- '#type' => 'checkbox',
- '#title' => t('Users may translate this field.'),
- '#default_value' => $field['translatable'],
+ '#prefix' => '