Issue #1446382 by plach: Need a reliable way to determine if a specific bundle for an entity type is translatable.

8.0.x
webchick 2013-03-18 05:40:12 -07:00
parent dd927b0008
commit 1bec76308f
21 changed files with 121 additions and 95 deletions

View File

@ -107,6 +107,8 @@ function hook_entity_view_mode_info_alter(&$view_modes) {
* - access callback: As in hook_menu(). 'user_access' will be assumed if
* no value is provided.
* - access arguments: As in hook_menu().
* - translatable: (optional) A boolean value specifying whether this bundle
* has translation support enabled. Defaults to FALSE.
*
* @see entity_get_bundles()
* @see hook_entity_bundle_info_alter()

View File

@ -440,4 +440,14 @@ class Entity implements IteratorAggregate, EntityInterface {
// As entities are always the root of the tree of typed data, we do not need
// to set any parent or name.
}
/**
* Implements \Drupal\Core\Entity\EntityInterface::isTranslatable().
*/
public function isTranslatable() {
// @todo Inject the entity manager and retrieve bundle info from it.
$bundles = entity_get_bundles($this->entityType);
return !empty($bundles[$this->bundle()]['translatable']);
}
}

View File

@ -460,4 +460,12 @@ class EntityBCDecorator implements IteratorAggregate, EntityInterface {
public function getExportProperties() {
$this->decorated->getExportProperties();
}
/**
* Forwards the call to the decorated entity.
*/
public function isTranslatable() {
return $this->decorated->isTranslatable();
}
}

View File

@ -219,4 +219,13 @@ interface EntityInterface extends ContextAwareInterface, ComplexDataInterface, A
* @see \Drupal\Core\Entity\EntityInterface::getBCEntity()
*/
public function getOriginalEntity();
/**
* Returns the translation support status.
*
* @return bool
* TRUE if the entity bundle has translation support enabled.
*/
public function isTranslatable();
}

View File

@ -75,11 +75,8 @@ use Drupal\Core\Cache\CacheBackendInterface;
* - static_cache: (optional) Boolean indicating whether entities should be
* statically cached during a page request. Used by
* Drupal\Core\Entity\DatabaseStorageController. Defaults to TRUE.
* - translation: (optional) An associative array of modules registered as
* field translation handlers. Array keys are the module names, and array
* values can be any data structure the module uses to provide field
* translation. If the value is empty, the module will not be used as a
* translation handler.
* - translatable: (optional) Boolean indicating whether entities of this type
* have mutlilingual support. Defaults to FALSE.
* - entity_keys: An array describing how the Field API can extract certain
* information from objects of this entity type. Elements:
* - id: The name of the property that contains the primary ID of the

View File

@ -52,4 +52,5 @@ interface TranslatableInterface {
* A typed data object for the translated data.
*/
public function getTranslation($langcode, $strict = TRUE);
}

View File

@ -31,6 +31,7 @@ use Drupal\Core\Annotation\Translation;
* revision_table = "custom_block_revision",
* menu_base_path = "block/%custom_block",
* fieldable = TRUE,
* translatable = TRUE,
* entity_keys = {
* "id" = "id",
* "revision" = "revision_id",

View File

@ -29,6 +29,7 @@ use Drupal\Core\Annotation\Translation;
* base_table = "comment",
* uri_callback = "comment_uri",
* fieldable = TRUE,
* translatable = TRUE,
* static_cache = FALSE,
* entity_keys = {
* "id" = "cid",

View File

@ -184,3 +184,13 @@ function config_test_entity_disable(ConfigTest $config_test) {
$config_test->disable()->save();
return new RedirectResponse(url('admin/structure/config_test', array('absolute' => TRUE)));
}
/**
* Implements hook_entity_info_alter().
*/
function config_test_entity_info_alter(&$entity_info) {
// The 'translatable' entity key is not supposed to change over time. In this
// case we can safely do it because we set it once and we do not change it for
// all the duration of the test session.
$entity_info['config_test']['translatable'] = Drupal::service('state')->get('config_test.translatable');
}

View File

@ -29,17 +29,9 @@ use Drupal\Core\Entity\EntityInterface;
* The available language codes for a particular field are returned by
* field_available_languages(). Whether a field is translatable is determined by
* calling field_is_translatable(), which checks the $field['translatable']
* property returned by field_info_field(), and whether there is at least one
* translation handler available for the field. A translation handler is a
* module registering itself via hook_entity_info_alter() to handle field
* translations.
* property returned by field_info_field() and whether the entity type the field
* is attached to supports translation.
*
* By default, _field_invoke() and _field_invoke_multiple() are processing a
* field in all available languages, unless they are given a language code
* suggestion. Based on that suggestion, _field_language_suggestion() determines
* the languages to act on.
* By default, _field_invoke() and _field_invoke_multiple() process a field in
* all available languages, unless they are given a language code suggestion.
* Based on that suggestion, _field_language_suggestion() determines the
@ -226,23 +218,12 @@ function field_is_translatable($entity_type, $field) {
* TRUE, if the given handler is allowed to manage field translations. If no
* handler is passed, TRUE means there is at least one registered translation
* handler.
*
* @todo Remove this once the migration to the Entity Field API is complete.
*/
function field_has_translation_handler($entity_type, $handler = NULL) {
$entity_info = entity_get_info($entity_type);
if (isset($handler)) {
return !empty($entity_info['translation'][$handler]);
}
elseif (isset($entity_info['translation'])) {
foreach ($entity_info['translation'] as $handler_info) {
// The translation handler must use a non-empty data structure.
if (!empty($handler_info)) {
return TRUE;
}
}
}
return FALSE;
$info = entity_get_info($entity_type);
return !empty($info['translatable']);
}
/**

View File

@ -12,13 +12,8 @@ use Drupal\field_test\Plugin\Core\Entity\TestEntity;
* Implements hook_entity_info_alter().
*/
function field_test_entity_info_alter(&$entity_info) {
// Enable/disable field_test as a translation handler.
foreach (field_test_entity_info_translatable() as $entity_type => $translatable) {
$entity_info[$entity_type]['translation']['field_test'] = $translatable;
}
// Disable the entity type translation handler.
foreach ($entity_info as $entity_type => $info) {
$entity_info[$entity_type]['translation'][$entity_type] = FALSE;
$entity_info[$entity_type]['translatable'] = $translatable;
}
}

View File

@ -210,11 +210,7 @@ function language_theme() {
function language_entity_supported() {
$supported = array();
foreach (entity_get_info() as $entity_type => $info) {
// @todo Revisit this once all core entities are migrated to the Entity
// Field API and language support for configuration entities has been
// sorted out.
$entity_class = new ReflectionClass($info['class']);
if ($info['fieldable'] && !$entity_class->implementsInterface('Drupal\Core\Config\Entity\ConfigEntityInterface')) {
if (!empty($info['fieldable']) && !empty($info['translatable'])) {
$supported[$entity_type] = $entity_type;
}
}

View File

@ -30,6 +30,7 @@ use Drupal\Core\Annotation\Translation;
* revision_table = "node_revision",
* uri_callback = "node_uri",
* fieldable = TRUE,
* translatable = TRUE,
* entity_keys = {
* "id" = "nid",
* "revision" = "vid",

View File

@ -27,6 +27,7 @@ use Drupal\Core\Annotation\Translation;
* base_table = "entity_test_mul",
* data_table = "entity_test_mul_property_data",
* fieldable = TRUE,
* translatable = TRUE,
* entity_keys = {
* "id" = "id",
* "uuid" = "uuid",

View File

@ -28,6 +28,7 @@ use Drupal\Core\Annotation\Translation;
* data_table = "entity_test_mulrev_property_data",
* revision_table = "entity_test_mulrev_property_revision",
* fieldable = TRUE,
* translatable = TRUE,
* entity_keys = {
* "id" = "id",
* "uuid" = "uuid",

View File

@ -30,6 +30,7 @@ use Drupal\Core\Annotation\Translation;
* base_table = "taxonomy_term_data",
* uri_callback = "taxonomy_term_uri",
* fieldable = TRUE,
* translatable = TRUE,
* entity_keys = {
* "id" = "tid",
* "bundle" = "vid",

View File

@ -50,9 +50,7 @@ use Drupal\Core\Entity\EntityInterface;
* Additionally some more entity info keys can be defined to further customize
* the translation UI. The entity translation info is an associative array that
* has to match the following structure. Two nested arrays keyed respectively
* by the 'translation' key and the 'entity_translation' key: the first one is
* the key defined by the core entity system, while the second one registers
* Entity Tanslation as a field translation handler. Elements:
* by the 'translation' key and the 'translation_entity' key. Elements:
* - access callback: The access callback for the translation pages. Defaults to
* 'entity_translation_translate_access'.
* - access arguments: The access arguments for the translation pages. By

View File

@ -34,6 +34,14 @@ class ConfigTestTranslationUITest extends EntityTranslationUITest {
parent::setUp();
}
/**
* Overrides \Drupal\translation_entity\Tests\EntityTranslationUITest::enableTranslation().
*/
protected function enableTranslation() {
$this->container->get('state')->set('config_test.translatable', TRUE);
parent::enableTranslation();
}
/**
* Overrides \Drupal\translation_entity\Tests\EntityTranslationUITest::getNewEntityValues().
*/

View File

@ -72,11 +72,12 @@ function translation_entity_language_types_info_alter(array &$language_types) {
* Implements hook_entity_info_alter().
*/
function translation_entity_entity_info_alter(array &$entity_info) {
$edit_form_info = array();
$bundles_info = entity_get_bundles();
// Provide defaults for translation info.
foreach ($entity_info as $entity_type => &$info) {
if (empty($info['translatable'])) {
continue;
}
if (!isset($info['translation']['translation_entity'])) {
$info['translation']['translation_entity'] = array();
}
@ -87,39 +88,37 @@ function translation_entity_entity_info_alter(array &$entity_info) {
// shared accross different entities.
$info += array('translation_controller_class' => 'Drupal\translation_entity\EntityTranslationController');
// Check whether translation is enabled at least for one bundle. We cannot
// use translation_entity_enabled() here since it would cause infinite
// recursion, as it relies on entity info.
$enabled = FALSE;
$bundles = isset($bundles_info[$entity_type]) ? array_keys($bundles_info[$entity_type]) : array($entity_type);
foreach ($bundles as $bundle) {
if (translation_entity_get_config($entity_type, $bundle, 'enabled')) {
$enabled = TRUE;
break;
}
// If no menu base path is provided we default to the usual
// "entity_type/%entity_type" pattern.
if (!isset($info['menu_base_path'])) {
$path = "$entity_type/%$entity_type";
$info['menu_base_path'] = $path;
}
if ($enabled) {
// If no menu base path is provided we default to the usual
// "entity_type/%entity_type" pattern.
if (!isset($info['menu_base_path'])) {
$path = "$entity_type/%$entity_type";
$info['menu_base_path'] = $path;
}
$path = $info['menu_base_path'];
$path = $info['menu_base_path'];
$info += array(
'menu_view_path' => $path,
'menu_edit_path' => "$path/edit",
'menu_path_wildcard' => "%$entity_type",
);
$info += array(
'menu_view_path' => $path,
'menu_edit_path' => "$path/edit",
'menu_path_wildcard' => "%$entity_type",
);
$entity_position = count(explode('/', $path)) - 1;
$info['translation']['translation_entity'] += array(
'access_callback' => 'translation_entity_translate_access',
'access_arguments' => array($entity_position),
);
}
}
$entity_position = count(explode('/', $path)) - 1;
$info['translation']['translation_entity'] += array(
'access_callback' => 'translation_entity_translate_access',
'access_arguments' => array($entity_position),
);
/**
* Implements hook_entity_bundle_info_alter().
*/
function translation_entity_entity_bundle_info_alter(&$bundles) {
foreach ($bundles as $entity_type => &$info) {
foreach ($info as $bundle => &$bundle_info) {
$enabled = translation_entity_get_config($entity_type, $bundle, 'enabled');
$bundle_info['translatable'] = !empty($enabled);
}
}
}
@ -279,9 +278,7 @@ function _translation_entity_menu_strip_loaders($path) {
*/
function translation_entity_translate_access(EntityInterface $entity) {
$entity_type = $entity->entityType();
return empty($entity->language()->locked) &&
language_multilingual() &&
translation_entity_enabled($entity_type, $entity->bundle()) &&
return empty($entity->language()->locked) && language_multilingual() && $entity->isTranslatable() &&
(user_access('create entity translations') || user_access('update entity translations') || user_access('delete entity translations'));
}
@ -445,26 +442,26 @@ function translation_entity_set_config($entity_type, $bundle, $setting, $value)
* @param string $bundle
* (optional) The bundle of the entity. If no bundle is provided, all the
* available bundles are checked.
* @param boolean $skip_handler
* (optional) Specifies whether the availablity of a field translation handler
* should affect the returned value. By default the check is performed.
*
* @returns
* TRUE if the specified bundle is translatable. If no bundle is provided
* returns TRUE if at least one of the entity bundles is translatable.
*/
function translation_entity_enabled($entity_type, $bundle = NULL, $skip_handler = FALSE) {
function translation_entity_enabled($entity_type, $bundle = NULL) {
$enabled = FALSE;
$bundles = !empty($bundle) ? array($bundle) : array_keys(entity_get_bundles($entity_type));
$info = entity_get_info($entity_type);
foreach ($bundles as $bundle) {
if (translation_entity_get_config($entity_type, $bundle, 'enabled')) {
$enabled = TRUE;
break;
if (!empty($info['translatable'])) {
$bundles = !empty($bundle) ? array($bundle) : array_keys(entity_get_bundles($entity_type));
foreach ($bundles as $bundle) {
if (translation_entity_get_config($entity_type, $bundle, 'enabled')) {
$enabled = TRUE;
break;
}
}
}
return $enabled && ($skip_handler || field_has_translation_handler($entity_type, 'translation_entity'));
return $enabled;
}
/**
@ -602,7 +599,7 @@ function translation_entity_permission() {
* Implements hook_form_alter().
*/
function translation_entity_form_alter(array &$form, array &$form_state) {
if (($form_controller = translation_entity_form_controller($form_state)) && ($entity = $form_controller->getEntity($form_state)) && !$entity->isNew() && translation_entity_enabled($entity->entityType(), $entity->bundle())) {
if (($form_controller = translation_entity_form_controller($form_state)) && ($entity = $form_controller->getEntity($form_state)) && !$entity->isNew() && $entity->isTranslatable()) {
$controller = translation_entity_controller($entity->entityType());
$controller->entityFormAlter($form, $form_state, $entity);
@ -643,7 +640,7 @@ function translation_entity_field_language_alter(&$display_language, $context) {
$entity = $context['entity'];
$entity_type = $entity->entityType();
if (isset($entity->translation[$context['langcode']]) && translation_entity_enabled($entity_type, $entity->bundle()) && !translation_entity_view_access($entity, $context['langcode'])) {
if (isset($entity->translation[$context['langcode']]) && $entity->isTranslatable() && !translation_entity_view_access($entity, $context['langcode'])) {
$instances = field_info_instances($entity_type, $entity->bundle());
// Avoid altering the real entity.
$entity = clone($entity);
@ -679,7 +676,7 @@ function translation_entity_entity_load(array $entities, $entity_type) {
if (translation_entity_enabled($entity_type)) {
foreach ($entities as $entity) {
if (translation_entity_enabled($entity_type, $entity->bundle())) {
if ($entity->isTranslatable()) {
$enabled_entities[$entity->id()] = $entity;
}
}
@ -718,7 +715,7 @@ function translation_entity_load_translation_metadata(array $entities, $entity_t
*/
function translation_entity_entity_insert(EntityInterface $entity) {
// Only do something if translation support for the given entity is enabled.
if (!translation_entity_enabled($entity->entityType(), $entity->bundle())) {
if (!$entity->isTranslatable()) {
return;
}
@ -758,7 +755,7 @@ function translation_entity_entity_insert(EntityInterface $entity) {
*/
function translation_entity_entity_delete(EntityInterface $entity) {
// Only do something if translation support for the given entity is enabled.
if (!translation_entity_enabled($entity->entityType(), $entity->bundle())) {
if (!$entity->isTranslatable()) {
return;
}
@ -773,7 +770,7 @@ function translation_entity_entity_delete(EntityInterface $entity) {
*/
function translation_entity_entity_update(EntityInterface $entity) {
// Only do something if translation support for the given entity is enabled.
if (!translation_entity_enabled($entity->entityType(), $entity->bundle())) {
if (!$entity->isTranslatable()) {
return;
}
@ -867,7 +864,7 @@ function translation_entity_field_info_alter(&$info) {
* Implements hook_field_attach_presave().
*/
function translation_entity_field_attach_presave(EntityInterface $entity) {
if (translation_entity_enabled($entity->entityType(), $entity->bundle())) {
if ($entity->isTranslatable()) {
$attributes = drupal_container()->get('request')->attributes;
Drupal::service('translation_entity.synchronizer')->synchronizeFields($entity, $attributes->get('working_langcode'), $attributes->get('source_langcode'));
}

View File

@ -31,6 +31,7 @@ use Drupal\Core\Annotation\Translation;
* uri_callback = "user_uri",
* label_callback = "user_label",
* fieldable = TRUE,
* translatable = TRUE,
* entity_keys = {
* "id" = "uid",
* "uuid" = "uuid"

View File

@ -984,6 +984,13 @@ class ViewUI implements ViewStorageInterface {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* Implements \Drupal\Core\Entity\EntityInterface::isTranslatable().
*/
public function isTranslatable() {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* Implements \Drupal\Core\TypedData\ContextAwareInterface::getName().
*/