diff --git a/entity_translation.module b/entity_translation.module
index 053d39d..6194635 100644
--- a/entity_translation.module
+++ b/entity_translation.module
@@ -189,10 +189,42 @@ function entity_translation_menu() {
'access arguments' => array('administer entity translation'),
'file' => 'entity_translation.admin.inc',
);
+ $items['admin/structure/types/manage/%/fields/%/translatable'] = array(
+ 'title' => t('Confirm Change in translatability.'),
+ 'description' => 'Confirm page for changing field translatability.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('entity_translation_warning_form', 4, 6),
+ 'access arguments' => array('administer entity translation'),
+ );
return $items;
}
+/*
+ * Confirm form for changing field translatability.
+ */
+function entity_translation_warning_form($form, &$form_state, $bundle, $field_name) {
+
+ $field = field_info_field($field_name);
+
+ $warning = t('By submitting this form you will trigger a batch operation.');
+ if ($field['translatable']) {
+ $warning .= "
" . t("All translations of this field will be deleted.
Please backup your data before proceeding.");
+ }
+
+ $redirect = "admin/structure/types/manage/$bundle/fields/$field_name/field-settings";
+
+ $form = confirm_form($form, t('Warning.'), $redirect, $warning, t('Convert'), t('Cancel'));
+
+ $form['#submit'] = array('entity_translation_form_translatable_submit');
+
+ // We need to attach the field name for future processing.
+ $form['#etfield'] = $field;
+
+ return $form;
+}
+
+
/**
* Implements hook_admin_paths().
*/
@@ -463,16 +495,155 @@ 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_state['build_info']['args'][0]['field_name'];
+ $bundle = $form['#bundle'];
+
+ $path = "admin/structure/types/manage/$bundle/fields/$field_name/translatable";
$form['field']['translatable'] = array(
- '#type' => 'checkbox',
- '#title' => t('Users may translate this field.'),
- '#default_value' => $field['translatable'],
+ '#markup' => "
" . l(t('Change Translatability'), $path, array('query' => drupal_get_destination())) . "
",
);
}
/**
+ * 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_form_translatable_submit($form, $form_state) {
+ // This is the current state that we want to reverse.
+ $translatable = $form['#etfield']['translatable'];
+
+ $field_name = $form['#etfield']['field_name'];
+
+ // 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 convert 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_toggle_field_translatable', array($field_name)),
+ );
+ }
+ else {
+ $opts = array(
+ array('entity_translation_toggle_field_translatable', array($field_name)),
+ array('entity_translation_translatable_batch', array(!$translatable, $field_name)),
+ );
+ }
+
+ $batch = array(
+ 'title' => t('Converting'),
+ 'operations' => $opts,
+ 'finished' => 'entity_translation_translatable_batch_done',
+ );
+
+ 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_toggle_field_translatable($field_name) {
+ $field = field_read_fields(array('field_name' => $field_name));
+
+ $field = $field[$field_name];
+ $field['translatable'] = !$field['translatable'];
+
+ field_update_field($field);
+}
+
+/**
+ * Batch operation. Convert field data to or from LANGUAGE_NONE.
+ */
+function entity_translation_translatable_batch($make_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 (!$count) {
+ // Nothing to do.
+ $context['finished'] = 1;
+ return;
+ }
+
+ $context['sandbox']['max'] = $count;
+
+ }
+ // Number of entities to be processed for each step.
+ $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 ($make_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 (!$make_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];
+ foreach ($entity->{$field_name} as $lang => $items) {
+ // It's not enough to simply delete the field, we need to tell
+ // the translation handler to wipe it out.
+ $handler->removeTranslation($lang);
+ }
+ // Move the original field data to LANGUAGE_NONE
+ $entity->{$field_name}[LANGUAGE_NONE] = $data;
+ }
+ else {
+ // No need to save unchanged entities.
+ continue;
+ }
+ // Save field data.
+ field_attach_update($entity_type, $entity);
+ }
+ }
+ $context['finished'] = $context['sandbox']['progress']/$context['sandbox']['max'];
+}
+
+/**
+ * Check the exit status of the batch operation, and possibly resubmit the changed translatable status.
+ */
+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."));
+ }
+}
+
+/**
* Translation handler factory.
*
* @param $entity_type