diff --git a/entity_translation.install b/entity_translation.install index 9108aca..d0b1442 100644 --- a/entity_translation.install +++ b/entity_translation.install @@ -96,14 +96,14 @@ function entity_translation_install() { // Grant the 'edit original values' permission, so we don't break editing on // existing sites. - $msg = _entity_translation_grant_original_values_permissions(); + $msg = _entity_translation_grant_edit_permissions(); drupal_set_message($msg); } /** * Grant 'edit $type original values' permission to existing roles. */ -function _entity_translation_grant_original_values_permissions() { +function _entity_translation_grant_edit_permissions() { $permissions = array(); // Nodes. @@ -129,8 +129,12 @@ function _entity_translation_grant_original_values_permissions() { $assignments = array(); foreach ($permissions as $entity_type => $permissions_filter) { - $permission = "edit $entity_type original values"; - $assignments[] = _entity_translation_grant_permission($permission, $permissions_filter); + if (entity_translation_enabled($entity_type)) { + $permission = "edit $entity_type original values"; + $assignments[] = _entity_translation_grant_permission($permission, $permissions_filter); + $permission = "edit $entity_type translation shared fields"; + $assignments[] = _entity_translation_grant_permission($permission, $permissions_filter); + } } $assignments = ''; @@ -230,10 +234,10 @@ function entity_translation_update_7001() { } /** - * Grant 'edit original values' permission to roles which have entity editing permissions. + * Grant 'edit original values' and 'edit shared field' permissions to roles which have entity editing permissions. */ function entity_translation_update_7002() { - return _entity_translation_grant_original_values_permissions(); + return _entity_translation_grant_edit_permissions(); } /** diff --git a/entity_translation.module b/entity_translation.module index 31d1ec8..7ce69bf 100644 --- a/entity_translation.module +++ b/entity_translation.module @@ -117,6 +117,7 @@ function entity_translation_entity_info() { 'entity_translation' => array( 'class' => 'EntityTranslationUserHandler', 'skip original values access' => TRUE, + 'skip shared fields access' => TRUE, ), ), ); @@ -727,7 +728,7 @@ function entity_translation_permission() { ); foreach (entity_get_info() as $entity_type => $info) { - if ($info['fieldable']) { + if ($info['fieldable'] && entity_translation_enabled($entity_type)) { $label = !empty($info['label']) ? t($info['label']) : $entity_type; $permission["translate $entity_type entities"] = array( 'title' => t('Translate entities of type @type', array('@type' => $label)), @@ -735,19 +736,20 @@ function entity_translation_permission() { ); // Avoid access control for original values on the current entity. - if (!empty($info['translation']['entity_translation']['skip original values access'])) { - continue; + if (empty($info['translation']['entity_translation']['skip original values access'])) { + $permission["edit $entity_type original values"] = array( + 'title' => t('Edit original values on entities of type @type', array('@type' => $label)), + 'description' => t('Access the edit form in the original language for entities of type @type.', array('@type' => $label)), + ); } - $permission["edit $entity_type original values"] = array( - 'title' => t('Edit original values on entities of type @type', array('@type' => $label)), - 'description' => t('Access the edit form in the original language for entities of type @type.', array('@type' => $label)), - ); - - $permission["edit $entity_type translation shared fields"] = array( - 'title' => t('Edit @type shared fields.', array('@type' => $label)), - 'description' => t('Edit fields shared between translations on the @type translation form.', array('@type' => $label)), - ); + // Avoid access control for shared fields on the current entity. + if (empty($info['translation']['entity_translation']['skip shared fields access'])) { + $permission["edit $entity_type translation shared fields"] = array( + 'title' => t('Edit @type shared fields.', array('@type' => $label)), + 'description' => t('Edit fields shared between translations on the @type translation form.', array('@type' => $label)), + ); + } } } @@ -1127,29 +1129,13 @@ function entity_translation_field_attach_form($entity_type, $entity, &$form, &$f // Handle fields shared between translations when there is at least one // translation available or a new one is being created. if (!$handler->isNewEntity() && ($new_translation || count($translations->data) > 1)) { + $access = user_access('edit translation shared fields') || user_access("edit $entity_type translation shared fields"); list(, , $bundle) = entity_extract_ids($entity_type, $entity); - - // Shared fields are visible if we are either editing the source node, or - // if the user has the respective permission. - $is_source = $langcode == $handler->getLanguage(); - $shared_fields_visible = $is_source || user_access('edit translation shared fields'); - foreach (field_info_instances($entity_type, $bundle) as $instance) { $field_name = $instance['field_name']; $field = field_info_field($field_name); - - if ($shared_fields_visible) { - // Add visual clues about translatability. - if (!isset($form[$field_name]['#process'])) { - $form[$field_name]['#process'] = array(); - } - $form[$field_name]['#field_name'] = $field_name; - array_unshift($form[$field_name]['#process'], 'entity_translation_element_translatability_clue'); - } - // Hide shared fields. - elseif (!$field['translatable']) { - $form[$field_name]['#access'] = FALSE; - } + $form[$field_name]['#access'] = !empty($form[$field_name]['#access']) && ($field['translatable'] || $access) ; + $form[$field_name]['#multilingual'] = (boolean) $field['translatable']; } } @@ -1236,14 +1222,10 @@ function entity_translation_form_element_language_replace(&$element, $source, $l * shared between different translations. Moreover fields receive a CSS class to * distinguish between translatable and shared fields. */ -function entity_translation_element_translatability_clue($element, &$form_state, $form) { - $field_name = $element['#field_name']; - $field = field_info_field($field_name); - +function entity_translation_element_translatability_clue($element) { // Append language to element title. - if (variable_get('entity_translation_shared_labels', TRUE)) { - $suffix = $field['translatable'] ? '' : ' (' . t('shared field') . ')'; - _entity_translation_element_title_append($element, $suffix); + if (empty($element['#multilingual'])) { + _entity_translation_element_title_append($element, ' (' . t('all languages') . ')'); } // Add CSS class names. @@ -1253,12 +1235,43 @@ function entity_translation_element_translatability_clue($element, &$form_state, if (!isset($element['#attributes']['class'])) { $element['#attributes']['class'] = array(); } - $element['#attributes']['class'][] = 'entity-translation-' . ($field['translatable'] ? 'field-translatable' : 'field-shared'); + $element['#attributes']['class'][] = 'entity-translation-' . ($element['#multilingual'] ? 'field-translatable' : 'field-shared'); return $element; } /** + * Adds a callback function to the given FAPI element. + * + * Drupal core only adds default element callbacks if the respective handler + * type is not defined yet. This function ensures that our callback is only + * prepended/appended to the default set of callbacks instead of replacing it. + * + * @param $element + * The FAPI element. + * @param $type + * The callback type, e.g. '#pre_render' or '#process'. + * @param $function + * The name of the callback to add. + * @param $prepend + * Set to TRUE to add the new callback to the beginning of the existing set of + * callbacks, and set it to FALSE to append it at the end. + */ +function _entity_translation_element_add_callback(&$element, $type, $function, $prepend = TRUE) { + // If handler type has not been set, add defaults from element_info(). + if (!isset($element[$type])) { + $element_info = element_info($element['#type']); + $element[$type] = isset($element_info[$type]) ? $element_info[$type] : array(); + } + if ($prepend) { + array_unshift($element[$type], $function); + } + else { + $element[$type][] = $function; + } +} + +/** * Appends the given $suffix string to the title of the given form element. * * If the given element does not have a #title attribute, the function is @@ -1299,6 +1312,8 @@ function entity_translation_form_alter(&$form, &$form_state) { if ($handler = entity_translation_entity_form_get_handler($form, $form_state)) { if (!$handler->isNewEntity()) { $handler->entityForm($form, $form_state); + // Hide shared form elements if the user is not allowed to edit them. + $handler->entityFormSharedElements($form); } else { $handler->entityFormLanguageWidget($form, $form_state); diff --git a/entity_translation_i18n_menu/entity_translation_i18n_menu.module b/entity_translation_i18n_menu/entity_translation_i18n_menu.module index 88a9315..196f98f 100644 --- a/entity_translation_i18n_menu/entity_translation_i18n_menu.module +++ b/entity_translation_i18n_menu/entity_translation_i18n_menu.module @@ -165,6 +165,8 @@ function entity_translation_i18n_menu_form(&$form, &$form_state) { $langcode = $handler->getFormLanguage(); $language_name = isset($languages[$langcode]) ? t($languages[$langcode]->name) : t('current'); + $form['menu']['#multilingual'] = TRUE; + $form['menu']['link']['tset'] = array( '#type' => 'checkbox', '#title' => t('Menu link enabled only for the %language language', array('%language' => $language_name)), diff --git a/includes/translation.handler.inc b/includes/translation.handler.inc index b8b4325..d75d578 100644 --- a/includes/translation.handler.inc +++ b/includes/translation.handler.inc @@ -256,6 +256,11 @@ interface EntityTranslationHandlerInterface { public function entityFormLanguageWidgetSubmit($form, &$form_state); /** + * Handle shared form elements. + */ + public function entityFormSharedElements(&$element); + + /** * Performs validation tasks on the submitted entity forms. */ public function entityFormValidate($form, &$form_state); @@ -942,6 +947,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa '#tree' => TRUE, '#weight' => -100, '#access' => $access, + '#multilingual' => TRUE, 'language' => array( '#type' => 'select', '#default_value' => $this->getSourceLanguage(), @@ -996,6 +1002,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa '#tree' => TRUE, '#weight' => 10, '#access' => $access, + '#multilingual' => TRUE, ); $status = $new_translation || $translations->data[$form_langcode]['status']; @@ -1074,20 +1081,43 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa if (!empty($form['actions']['delete']['#submit'])) { $form['actions']['delete']['#submit'][] = 'entity_translation_entity_form_submit'; } - - // Hide shared fields if the user is not allowed to edit them. - $this->entityFormAccess($form, user_access('edit translation shared fields') || user_access("edit $this->entityType translation shared fields")); } /** - * Assign the given access to the form elements not defining one yet. + * @see EntityTranslationHandlerInterface::entityFormSharedElements() + * + * Either remove access or add a translatability clue depending on the current + * user's "edit translation shared fields" permissions. */ - protected function entityFormAccess(&$form, $access) { - if (!$access) { - $allowed_types = array_flip(array('actions', 'container', 'value', 'hidden', 'vertical_tabs')); - foreach (element_children($form) as $key) { - if (!isset($form[$key]['#access']) && !isset($allowed_types[$form[$key]['#type']])) { - $form[$key]['#access'] = $access; + public function entityFormSharedElements(&$element) { + static $ignored_types; + if (!isset($ignored_types)) { + $ignored_types = array_flip(array('actions', 'value', 'hidden', 'vertical_tabs', 'token')); + } + + $shared_labels = variable_get('entity_translation_shared_labels', TRUE); + $access = user_access('edit translation shared fields') || user_access("edit $this->entityType translation shared fields"); + + foreach (element_children($element) as $key) { + if (!isset($element[$key]['#type'])) { + $this->entityFormSharedElements($element[$key]); + } + else { + // Ignore non-widget form elements. + if (isset($ignored_types[$element[$key]['#type']])) { + continue; + } + // Elements are considered to be non multilingual by default. + if (!isset($element[$key]['#multilingual'])) { + $element[$key]['#multilingual'] = FALSE; + } + // Update #access only if it has not been set already. + if (!isset($element[$key]['#access'])) { + $element[$key]['#access'] = $access || $element[$key]['#multilingual']; + } + // Add translatability clue for visible elements. + if ($access && $shared_labels) { + _entity_translation_element_add_callback($element[$key], '#process', 'entity_translation_element_translatability_clue'); } } } @@ -1123,6 +1153,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa '#options' => $options, '#access' => empty($settings['hide_language_selector']), '#disabled' => $is_translation || (!$is_new && !empty($settings['lock_language'])), + '#multilingual' => TRUE, ); if (!empty($form['actions']['submit']['#submit'])) { diff --git a/includes/translation.handler.node.inc b/includes/translation.handler.node.inc index 6d82175..b91bf09 100644 --- a/includes/translation.handler.node.inc +++ b/includes/translation.handler.node.inc @@ -52,6 +52,11 @@ class EntityTranslationNodeHandler extends EntityTranslationDefaultHandler { $form['translation']['created']['#access'] = FALSE; } } + + // Path aliases natively support multilingual values. + if (isset($form['path'])) { + $form['path']['#multilingual'] = TRUE; + } } /**