non security modules update

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-20 16:32:07 +02:00
parent 6a8d30db08
commit 37fbabab56
466 changed files with 32690 additions and 9652 deletions

View File

@@ -3,6 +3,51 @@ Entity Translation 7.x-1.x, xxxx-xx-xx
--------------------------------------
Entity Translation 7.x-1.0-beta4, 2015-01-23
--------------------------------------------
#2396103 by plach: Fixed [DATA LOSS] Translations deleted.
#2218087 by plach: Fixed PHP Fatal error: Unsupported operand types in
modules/field_ui/field_ui.admin.inc on line 477.
#2396653 by SchnWalter, mongolito404: Fix validation handler for the entity
language widget.
#2395327 by damiankloip: FATAL error when requesting an edit path for a
non-existent entity.
#2342787 by jcisio: Language widget submit handler runs on disabled entity
types.
#2389945 by rafal.enden: Tests don't pass when using different source than
original node language.
#2185523 by leon.nk: Document that entity_translation_enabled_bundle() does not
check whether the entity type is translatable.
#2378195 by Johnny vd Laar: Node edit notice when author of a translation is
deleted.
#2382713 by seanr: entity_translation_update_7006 fails because of SQL error
before variable_set('entity_translation_revision_enabled', FALSE); can run.
#1046282 by plach, good_man, miro_dietiker, Gábor Hojtsy: Make the module work
with revisions.
#2179183 by dkingofpa, Jānis Bebrītis | SocialNicheGuru: Fixed Fatal error:
Unsupported operand types in /entity_translation.module on line 188.
#1605406 by joelrosen, Jose Reyero, paulihuhtiniemi, guillaumev | mindaugasd:
Exposed translated field content to Views so that multiple language
translations can be shown simultaneously.
#2160559 by w3wfr: Warning because of empty value line 675.
#2151503 by SebCorbin: Check for translation access for Add links.
#2130091 by peximo: Media edit dialog integration bug.
#2065221 by catch, plach: Fixed Static cache for handlers should key by $vid.
#1516202 by fabsor, spotzero: Added a CTools access plugin for checking if a
translation is available.
#2027513 by GaëlG, jcisio: Added support for nested values for 'edit form' in
hook_entity_info().
#2073231 by plach: Rename entity_translation_form_language() to
entity_translation_get_existing_language().
#2072865 by douggreen: Fixed Only replace 'delete' button with
'delete translation', don't add one that doesn't exist.
#1865244 by plach | bojanz, agoradesign, ciss, interdruper, joel_osc: Added
support for multiple translation handlers on the same form.
#2055491 by plach: Fixed Synced image fields cannot be emptied.
#1866076 by grndlvl: Added support for non-entity load tokens when declaring
translations.
Entity Translation 7.x-1.0-beta3, 2013-07-23
--------------------------------------------
#2037789 by Jelle_S: Fixed Default to user_access() access callback like drupal
@@ -33,7 +78,7 @@ Entity Translation 7.x-1.0-beta3, 2013-07-23
#1947764 by plach: Improve bundle translatability checks.
#1370900 by mojzis, plach | mgladding: Fixed Fatal Error: Cannot access empty
property in translation.handler.inc.
Issue #1924088 by plach | drzraf: Fixed Wrong form 'language' value in case of
#1924088 by plach | drzraf: Fixed Wrong form 'language' value in case of
content-translation enabled bundles.
#1933742 by das-peter: Fixed Delete translation doesn't flush the entity cache.
#1903024 by fabsor | dimitrileonidas: Fixed SQL error in Views when adding an

View File

@@ -204,6 +204,8 @@ function entity_translation_overview($entity_type, $entity, $callback = NULL) {
}
$handler = entity_translation_get_handler($entity_type, $entity);
// Ensure $entity holds an entity object and not an id.
$entity = $handler->getEntity();
$handler->initPathScheme();
// Initialize translations if they are empty.
@@ -284,7 +286,7 @@ function entity_translation_overview($entity_type, $entity, $callback = NULL) {
// No such translation in the set yet: help user to create it.
$row_title = $source_name = t('n/a');
if ($source != $langcode && $handler->getAccess('update')) {
if ($source != $langcode && $handler->getAccess('update') && $handler->getTranslationAccess($langcode)) {
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
$translatable = FALSE;
@@ -382,12 +384,14 @@ function theme_entity_translation_overview_outdated($variables){
*/
function entity_translation_delete_confirm($form, $form_state, $entity_type, $entity, $langcode) {
$handler = entity_translation_get_handler($entity_type, $entity);
$handler->setFormLanguage($langcode);
$languages = language_list();
$form = array(
'#handler' => $handler,
'#entity_type' => $entity_type,
'#entity' => $entity,
// Ensure $entity holds an entity object and not an id.
'#entity' => $handler->getEntity(),
'#language' => $langcode,
);

View File

@@ -64,7 +64,7 @@
* - skip original values access: A flag specifying whether skipping access
* control when editing original values for this entity. Defaults to FALSE.
* - bundle callback: A callback to check whether the passed bundle has entity
* translation enabled. If empty all bundles are supposed to be enabled.
* translation enabled. Defaults to TRUE for all bundles.
* - default settings: The defaults to be applied to settings when an explicit
* choice is missing.
*/
@@ -151,3 +151,16 @@ function hook_entity_translation_update($entity_type, $entity, $translation, $va
*/
function hook_entity_translation_delete($entity_type, $entity, $langcode) {
}
/**
* Allows modules to act when a revision translation is deleted.
*
* @param $entity_type
* The entity type.
* @param $entity
* The entity.
* @param $langcode
* The langcode of the revision translation which was deleted.
*/
function hook_entity_translation_delete_revision($entity_type, $entity, $langcode) {
}

View File

@@ -5,6 +5,7 @@ core = 7.x
configure = admin/config/regional/entity_translation
dependencies[] = locale (>7.14)
files[] = includes/translation.handler_factory.inc
files[] = includes/translation.handler.inc
files[] = includes/translation.handler.comment.inc
files[] = includes/translation.handler.node.inc
@@ -19,10 +20,11 @@ files[] = views/entity_translation_handler_field_label.inc
files[] = views/entity_translation_handler_filter_entity_type.inc
files[] = views/entity_translation_handler_filter_language.inc
files[] = views/entity_translation_handler_filter_translation_exists.inc
files[] = views/entity_translation_handler_field_field.inc
; Information added by drupal.org packaging script on 2013-07-23
version = "7.x-1.0-beta3"
; Information added by Drupal.org packaging script on 2015-01-22
version = "7.x-1.0-beta4"
core = "7.x"
project = "entity_translation"
datestamp = "1374601567"
datestamp = "1421971088"

View File

@@ -9,6 +9,8 @@
* Implements hook_schema().
*/
function entity_translation_schema() {
$schema = array();
$schema['entity_translation'] = array(
'description' => 'Table to track entity translations',
'fields' => array(
@@ -25,7 +27,12 @@ function entity_translation_schema() {
'not null' => TRUE,
'description' => 'The entity id this translation relates to',
),
// @todo: Consider an integer field for 'language'.
'revision_id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'The entity revision id this translation relates to',
),
'language' => array(
'type' => 'varchar',
'length' => 32,
@@ -73,6 +80,78 @@ function entity_translation_schema() {
),
'primary key' => array('entity_type', 'entity_id', 'language'),
);
$schema['entity_translation_revision'] = array(
'description' => 'Table to track entity translation revisions',
'fields' => array(
'entity_type' => array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => '',
'description' => 'The entity type this translation revision relates to',
),
'entity_id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'The entity id this translation revision relates to',
),
'revision_id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'The entity revision id this translation relates to',
),
'language' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
'description' => 'The target language for this translation revision.',
),
'source' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
'description' => 'The source language from which this translation revision was created.',
),
'uid' => array(
'description' => 'The author of this translation revision.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'status' => array(
'description' => 'Boolean indicating whether the translation revision is published (visible to non-administrators).',
'type' => 'int',
'not null' => TRUE,
'default' => 1,
),
'translate' => array(
'description' => 'A boolean indicating whether this translation revision needs to be updated.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'created' => array(
'description' => 'The Unix timestamp when the translation revision was created.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'changed' => array(
'description' => 'The Unix timestamp when the translation revision was most recently saved.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
),
'primary key' => array('entity_type', 'revision_id', 'language'),
'indexes'=> array('revision_id' => array('revision_id')),
);
return $schema;
}
@@ -93,6 +172,9 @@ function entity_translation_install() {
// Make translation use the content language type.
variable_set('translation_language_type', LANGUAGE_TYPE_CONTENT);
// Enable revision support for entity translation.
variable_set('entity_translation_revision_enabled', TRUE);
}
/**
@@ -254,3 +336,113 @@ function entity_translation_update_7003() {
function entity_translation_update_7004() {
entity_info_cache_clear();
}
/**
* Rebuild the class registry to pick up the translation handler factory class.
*/
function entity_translation_update_7005() {
registry_rebuild();
}
/**
* Add revision schema for entity translation metadata.
*/
function entity_translation_update_7006() {
// Create revision id column.
$spec = array(
'type' => 'int',
'unsigned' => TRUE,
// If we have existing data we cannot enforce this to be NOT NULL.
'not null' => FALSE,
'description' => 'The entity revision id this translation relates to',
);
db_add_field('entity_translation', 'revision_id', $spec);
// Create the entity translation revision schema.
$table = array(
'description' => 'Table to track entity translation revisions',
'fields' => array(
'entity_type' => array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => '',
'description' => 'The entity type this translation revision relates to',
),
'entity_id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'The entity id this translation revision relates to',
),
'revision_id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'The entity revision id this translation relates to',
),
'language' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
'description' => 'The target language for this translation revision.',
),
'source' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
'description' => 'The source language from which this translation revision was created.',
),
'uid' => array(
'description' => 'The author of this translation revision.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'status' => array(
'description' => 'Boolean indicating whether the translation revision is published (visible to non-administrators).',
'type' => 'int',
'not null' => TRUE,
'default' => 1,
),
'translate' => array(
'description' => 'A boolean indicating whether this translation revision needs to be updated.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'created' => array(
'description' => 'The Unix timestamp when the translation revision was created.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'changed' => array(
'description' => 'The Unix timestamp when the translation revision was most recently saved.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
),
'primary key' => array('entity_type', 'revision_id', 'language'),
'indexes'=> array('revision_id' => array('revision_id')),
);
db_create_table('entity_translation_revision', $table);
}
/**
* Disable revision support on existing installations.
*/
function entity_translation_update_7007() {
// Revision support is not enabled by default on existing installations as
// making it work implies copying translation metadata to the
// {entity_translation_revision} table for all the existing translations.
// Since this process cannot be reliably implemented in an update function,
// we leave the choice of manually performing the upgrade to people with the
// required skills to do so. Be aware that enabling revision support on sites
// where data has not been manually migrated may cause translation metadata to
// be permanently lost or corrupted. See https://www.drupal.org/node/2396103.
variable_set('entity_translation_revision_enabled', FALSE);
}

View File

@@ -129,7 +129,7 @@ function entity_translation_entity_info() {
* Processes the given path schemes and fill-in default values.
*/
function _entity_translation_process_path_schemes($entity_type, &$et_info) {
$path_scheme_keys = array_flip(array('base path', 'view path', 'edit path', 'translate path', 'path wildcard', 'admin theme'));
$path_scheme_keys = array_flip(array('base path', 'view path', 'edit path', 'translate path', 'path wildcard', 'admin theme', 'edit tabs'));
// Insert the default path scheme into the 'path schemes' array and remove
// respective elements from the entity_translation info array.
@@ -164,6 +164,7 @@ function _entity_translation_process_path_schemes($entity_type, &$et_info) {
$et_info['path schemes'][$delta] += array(
'admin theme' => TRUE,
'path wildcard' => "%$entity_type",
'edit tabs' => TRUE,
);
}
}
@@ -184,6 +185,9 @@ function entity_translation_entity_info_alter(&$entity_info) {
// not. As a matter of fact we might need them to correctly switch field
// translatability when a field is shared across different entity types.
$et_info += array('class' => 'EntityTranslationDefaultHandler');
if (!isset($entity_info[$entity_type]['entity keys'])) {
$entity_info[$entity_type]['entity keys'] = array();
}
$entity_info[$entity_type]['entity keys'] += array('translations' => 'translations');
if (entity_translation_enabled($entity_type, NULL, TRUE)) {
@@ -452,21 +456,23 @@ function entity_translation_menu_alter(&$items) {
$edit_item['access arguments'] = array_merge($args, $original_item['access arguments']);
// Edit translation callback.
$translation_position = count($edit_path_parts);
$args = array($entity_type, $entity_position, $translation_position, $original_item);
$items["$edit_path/%entity_translation_language"] = array(
'type' => MENU_DEFAULT_LOCAL_TASK,
'title callback' => 'entity_translation_edit_title',
'title arguments' => array($translation_position),
'page callback' => 'entity_translation_edit_page',
'page arguments' => array_merge($args, $original_item['page arguments']),
'access callback' => 'entity_translation_edit_access',
'access arguments' => array_merge($args, $original_item['access arguments']),
)
// We need to inherit the remaining menu item keys, mostly 'module'
// and 'file' to keep ajax callbacks working (see form_get_cache() and
// drupal_retrieve_form()).
+ $original_item;
if ($scheme['edit tabs'] !== FALSE) {
$translation_position = count($edit_path_parts);
$args = array($entity_type, $entity_position, $translation_position, $original_item);
$items["$edit_path/%entity_translation_language"] = array(
'type' => MENU_DEFAULT_LOCAL_TASK,
'title callback' => 'entity_translation_edit_title',
'title arguments' => array($translation_position),
'page callback' => 'entity_translation_edit_page',
'page arguments' => array_merge($args, $original_item['page arguments']),
'access callback' => 'entity_translation_edit_access',
'access arguments' => array_merge($args, $original_item['access arguments']),
)
// We need to inherit the remaining menu item keys, mostly 'module'
// and 'file' to keep ajax callbacks working (see form_get_cache() and
// drupal_retrieve_form()).
+ $original_item;
}
// Add translation callback.
$add_path = "$edit_path/add/%entity_translation_language/%entity_translation_language";
@@ -533,7 +539,7 @@ function entity_translation_edit_page() {
// Set the current form language.
$handler = entity_translation_get_handler($entity_type, $entity);
$handler->initPathScheme();
$langcode = entity_translation_form_language($langcode, $handler);
$langcode = entity_translation_get_existing_language($entity_type, $entity, $langcode);
$handler->setFormLanguage($langcode);
// Display the entity edit form.
@@ -548,10 +554,20 @@ function entity_translation_edit_access() {
$entity_type = array_shift($args);
$entity = array_shift($args);
$langcode = array_shift($args);
$edit_form_item = array_shift($args);
$access_callback = isset($edit_form_item['access callback']) ? $edit_form_item['access callback'] : 'user_access';
$handler = entity_translation_get_handler($entity_type, $entity);
// First, check a handler has been loaded. This could be empty if a
// non-existent entity edit path has been requested, for example. Delegate
// directly to the edit form item access callback in this case.
if (empty($handler)) {
return _entity_translation_callback($access_callback, $args, $edit_form_item);
}
$translations = $handler->getTranslations();
$langcode = entity_translation_form_language($langcode, $handler);
$langcode = $langcode = entity_translation_get_existing_language($entity_type, $entity, $langcode);
// The user must be explicitly allowed to access the original values if
// workflow permissions are enabled.
@@ -567,8 +583,6 @@ function entity_translation_edit_access() {
// is language neutral we need to let editors access it.
$enabled_languages = entity_translation_languages($entity_type, $entity);
if (isset($enabled_languages[$langcode]) || $langcode == LANGUAGE_NONE) {
$edit_form_item = array_shift($args);
$access_callback = isset($edit_form_item['access callback']) ? $edit_form_item['access callback'] : 'user_access';
return _entity_translation_callback($access_callback, $args, $edit_form_item);
}
}
@@ -579,11 +593,6 @@ function entity_translation_edit_access() {
/**
* Determines the current form language.
*
* Based on the requested language and the translations available for the entity
* being edited, determines the active form language. This takes into account
* language fallback rules so that the translation being edited matches the one
* being viewed.
*
* @param $langcode
* The requested language code.
* @param EntityTranslationHandlerInterface $handler
@@ -591,8 +600,35 @@ function entity_translation_edit_access() {
*
* @return
* A valid language code.
*
* @deprecated This is no longer used and will be removed in the first stable
* release.
*/
function entity_translation_form_language($langcode, $handler) {
return entity_translation_get_existing_language($handler->getEntity(), $handler->getEntityType(), $langcode);
}
/**
* Determines an existing translation language.
*
* Based on the requested language and the translations available for the given
* entity, determines an existing translation language. This takes into account
* language fallback rules.
*
* @param $entity_type
* The type of the entity.
* @param $entity
* The entity whose existing translation language has to be returned.
* @param $langcode
* (optional) The requested language code. Defaults to the current content
* language.
*
* @return
* A valid language code.
*/
function entity_translation_get_existing_language($entity_type, $entity, $langcode = NULL) {
$handler = entity_translation_get_handler($entity_type, $entity);
if (empty($langcode)) {
$langcode = $GLOBALS['language_content']->language;
}
@@ -671,7 +707,7 @@ function _entity_translation_callback($callback, $args, $info = array()) {
function entity_translation_admin_paths() {
$paths = array();
foreach (entity_get_info() as $entity_type => $info) {
if (entity_translation_enabled($entity_type, NULL, TRUE)) {
if (isset($info['translation']['entity_translation']['path schemes']) && entity_translation_enabled($entity_type, NULL, TRUE)) {
foreach ($info['translation']['entity_translation']['path schemes'] as $scheme) {
if (!empty($scheme['admin theme'])) {
if (isset($scheme['translate path'])) {
@@ -695,8 +731,12 @@ function entity_translation_admin_paths() {
*/
function entity_translation_tab_access($entity_type, $entity) {
if (drupal_multilingual() && (user_access('translate any entity') || user_access("translate $entity_type entities"))) {
$handler = entity_translation_get_handler($entity_type, $entity);
// Ensure $entity holds an entity object and not an id.
$entity = $handler->getEntity();
$enabled = entity_translation_enabled($entity_type, $entity);
return $enabled && entity_translation_get_handler($entity_type, $entity)->getLanguage() != LANGUAGE_NONE;
return $enabled && $handler->getLanguage() != LANGUAGE_NONE;
}
return FALSE;
}
@@ -831,6 +871,13 @@ function entity_translation_field_extra_fields() {
if (entity_translation_enabled($entity_type)) {
$bundles = !empty($info[$entity_type]['bundles']) ? array_keys($info[$entity_type]['bundles']) : array($entity_type);
foreach ($bundles as $bundle) {
// @todo Clean this up in https://www.drupal.org/node/1661348.
if ($entity_type == 'taxonomy_term') {
$vocabulary = taxonomy_vocabulary_machine_name_load($bundle);
if ($vocabulary && module_invoke('i18n_taxonomy', 'vocabulary_mode', $vocabulary, 4)) {
continue;
}
}
$settings = entity_translation_settings($entity_type, $bundle);
if (empty($settings['hide_language_selector']) && entity_translation_enabled_bundle($entity_type, $bundle) && ($handler = entity_translation_get_handler($entity_type, $bundle))) {
$language_key = $handler->getLanguageKey();
@@ -1060,6 +1107,10 @@ function entity_translation_sync($entity_type, $entity) {
// If an item has been removed we do not store its translations.
if ($removed) {
// Ensure items are actually removed.
if (!isset($entity->{$field_name}[$langcode])) {
$entity->{$field_name}[$langcode] = array();
}
continue;
}
// If a synchronized column has changed we need to override the full
@@ -1120,12 +1171,25 @@ function entity_translation_field_attach_delete($entity_type, $entity) {
}
}
/**
* Implements hook_field_attach_delete_revision().
*/
function entity_translation_field_attach_delete_revision($entity_type, $entity) {
if (entity_translation_enabled($entity_type, $entity)) {
$handler = entity_translation_get_handler($entity_type, $entity);
$handler->removeRevisionTranslations();
$handler->saveTranslations();
}
}
/**
* Implementation of hook_field_attach_form().
*/
function entity_translation_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
// Skip recursing into the source form.
if (empty($form['#entity_translation_source_form']) && ($handler = entity_translation_entity_form_get_handler($form, $form_state))) {
// Avoid recursing into the source form.
list($id, , $bundle) = entity_extract_ids($entity_type, $entity);
if (empty($form['#entity_translation_source_form']) && entity_translation_enabled($entity_type, $bundle)) {
$handler = entity_translation_get_handler($entity_type, $entity);
$langcode = !empty($langcode) ? $langcode : $handler->getLanguage();
$form_langcode = $handler->getFormLanguage();
$translations = $handler->getTranslations();
@@ -1142,8 +1206,6 @@ function entity_translation_field_attach_form($entity_type, $entity, &$form, &$f
// with the correct form language and replace the field elements with the
// correct ones.
if ($update_langcode || ($source && !isset($translations->data[$form_langcode]) && isset($translations->data[$source]) && empty($form_state['rebuild']))) {
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
foreach (field_info_instances($entity_type, $bundle) as $instance) {
$field_name = $instance['field_name'];
$field = field_info_field($field_name);
@@ -1153,25 +1215,30 @@ function entity_translation_field_attach_form($entity_type, $entity, &$form, &$f
// user can find the form items already populated with the source values
// while the field form element holds the correct language information.
if ($field['translatable']) {
$form[$field_name]['#field_name'] = $field_name;
$form[$field_name]['#source'] = $update_langcode ? $form_langcode : $source;
$form[$field_name]['#previous'] = NULL;
$element = &$form[$field_name];
$element['#entity_type'] = $entity_type;
$element['#entity'] = $entity;
$element['#entity_id'] = $id;
$element['#field_name'] = $field_name;
$element['#source'] = $update_langcode ? $form_langcode : $source;
$element['#previous'] = NULL;
$element['#form_parents'] = $form['#parents'];
// If we are updating the form language we need to make sure that the
// wrong language is unset and the right one is stored in the field
// element (see entity_translation_prepare_element()).
if ($update_langcode) {
$form[$field_name]['#previous'] = $form[$field_name]['#language'];
$form[$field_name]['#language'] = $form_langcode;
$element['#previous'] = $element['#language'];
$element['#language'] = $form_langcode;
}
// Swap default values during form processing to avoid recursion. We
// try to act before any other callback so that the correct values are
// already in place for them.
if (!isset($form[$field_name]['#process'])) {
$form[$field_name]['#process'] = array();
if (!isset($element['#process'])) {
$element['#process'] = array();
}
array_unshift($form[$field_name]['#process'], 'entity_translation_prepare_element');
array_unshift($element['#process'], 'entity_translation_prepare_element');
}
}
}
@@ -1205,18 +1272,31 @@ function entity_translation_field_attach_form($entity_type, $entity, &$form, &$f
* Form element process callback.
*/
function entity_translation_prepare_element($element, &$form_state) {
$source_form = &drupal_static(__FUNCTION__, array());
static $drupal_static_fast;
if (!isset($drupal_static_fast)) {
$drupal_static_fast['source_forms'] = &drupal_static(__FUNCTION__, array());
}
$source_forms = &$drupal_static_fast['source_forms'];
$form = $form_state['complete form'];
$build_id = $form['#build_id'];
$source = $element['#source'];
$entity_type = $element['#entity_type'];
$id = $element['#entity_id'];
if (!isset($source_form[$build_id][$source])) {
$source_form[$build_id][$source] = array('#entity_translation_source_form' => TRUE);
// Key the source form cache per entity type and entity id to allow for
// multiple entities on the same entity form.
if (!isset($source_forms[$build_id][$source][$entity_type][$id])) {
$source_form = array(
'#entity_translation_source_form' => TRUE,
'#parents' => $element['#form_parents'],
);
$source_form_state = $form_state;
$info = entity_translation_edit_form_info($form, $form_state);
field_attach_form($info['entity type'], $info['entity'], $source_form[$build_id][$source], $source_form_state, $source);
field_attach_form($entity_type, $element['#entity'], $source_form, $source_form_state, $source);
$source_forms[$build_id][$source][$entity_type][$id] = &$source_form;
}
$source_form = &$source_forms[$build_id][$source][$entity_type][$id];
$langcode = $element['#language'];
$field_name = $element['#field_name'];
@@ -1224,8 +1304,8 @@ function entity_translation_prepare_element($element, &$form_state) {
// language information from source to target language, this way the user can
// find the form items already populated with the source values while the
// field form element holds the correct language information.
if (isset($source_form[$build_id][$source][$field_name][$source])) {
$element[$langcode] = $source_form[$build_id][$source][$field_name][$source];
if (isset($source_form[$field_name][$source])) {
$element[$langcode] = $source_form[$field_name][$source];
entity_translation_form_element_language_replace($element, $source, $langcode);
unset($element[$element['#previous']]);
}
@@ -1245,7 +1325,7 @@ function entity_translation_form_element_language_replace(&$element, $source, $l
// Replace specific occurrences of the source language with the target
// language.
foreach (element_properties($element) as $key) {
if ($key === '#language') {
if ($key === '#language' && $element[$key] != LANGUAGE_NONE) {
$element[$key] = $langcode;
}
elseif ($key === '#parents' || $key === '#field_parents') {
@@ -1362,30 +1442,32 @@ function _entity_translation_element_title_append(&$element, $suffix) {
* Implements hook_form_alter().
*/
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);
$translations = $handler->getTranslations();
$form_langcode = $handler->getFormLanguage();
if (!isset($translations->data[$form_langcode]) || count($translations->data) > 1) {
// Hide shared form elements if the user is not allowed to edit them.
$handler->entityFormSharedElements($form);
if ($info = entity_translation_edit_form_info($form, $form_state)) {
$handler = entity_translation_get_handler($info['entity type'], $info['entity']);
if (entity_translation_enabled($info['entity type'], $info['entity'])) {
if (!$handler->isNewEntity()) {
$handler->entityForm($form, $form_state);
$translations = $handler->getTranslations();
$form_langcode = $handler->getFormLanguage();
if (!isset($translations->data[$form_langcode]) || count($translations->data) > 1) {
// Hide shared form elements if the user is not allowed to edit them.
$handler->entityFormSharedElements($form);
}
}
else {
$handler->entityFormLanguageWidget($form, $form_state);
}
// We need to process the posted form as early as possible to update the
// form language value.
array_unshift($form['#validate'], 'entity_translation_entity_form_validate');
}
// We might have an entity form for an entity or a bundle not enabled for
// translation. In this case we might need to deal with entity and field
// languages anyway, since fields may be shared among different bundles and
// entity types.
else {
$handler->entityFormLanguageWidget($form, $form_state);
}
// We need to process the posted form as early as possible to update the
// form language value.
array_unshift($form['#validate'], 'entity_translation_entity_form_validate');
}
// We might have an entity form for an entity or a bundle not enabled for
// translation. In this case we might need to deal with entity and field
// languages anyway, since fields may be shared among different bundles and
// entity types.
elseif ($info = entity_translation_edit_form_info($form, $form_state)) {
$handler = entity_translation_get_handler($info['entity type'], $info['entity']);
$handler->entityFormLanguageWidget($form, $form_state);
}
}
@@ -1431,16 +1513,22 @@ function entity_translation_entity_form_validate($form, &$form_state) {
}
/**
* Submit handler for the entity language widget.
* Validation handler for the entity language widget.
*/
function entity_translation_language_widget_submit($form, &$form_state) {
function entity_translation_entity_form_language_update($element, &$form_state, $form) {
$handler = entity_translation_entity_form_get_handler($form, $form_state);
// On non-translatable entities, we need to handle just the entity and field
// language.
if (empty($handler) && ($info = entity_translation_edit_form_info($form, $form_state))) {
$handler = entity_translation_get_handler($info['entity type'], $info['entity']);
// Ensure the handler form language match the actual one. This is mainly
// needed when responding to an AJAX request where the languages cannot be set
// from the usual page callback.
if (!empty($form_state['entity_translation']['form_langcode'])) {
$handler->setFormLanguage($form_state['entity_translation']['form_langcode']);
}
// When responding to an AJAX request we should ignore any change in the
// language widget as it may alter the field language expected by the AJAX
// callback.
if (empty($form_state['triggering_element']['#ajax'])) {
$handler->entityFormLanguageWidgetSubmit($form, $form_state);
}
$handler->entityFormLanguageWidgetSubmit($form, $form_state);
}
/**
@@ -1462,7 +1550,7 @@ function entity_translation_entity_form_submit($form, &$form_state) {
* Mark translations as outdated if the submitted value is true.
*/
function entity_translation_field_attach_submit($entity_type, $entity, $form, &$form_state) {
if ($handler = entity_translation_entity_form_get_handler($form, $form_state)) {
if (($handler = entity_translation_entity_form_get_handler($form, $form_state)) && entity_translation_enabled($entity_type, $entity)) {
// Update the wrapped entity with the submitted values.
$handler->setEntity($entity);
$handler->entityFormSubmit($form, $form_state);
@@ -1648,6 +1736,9 @@ function entity_translation_enabled($entity_type, $entity = NULL, $skip_handler
/**
* Determines whether the given entity bundle is translatable.
*
* NOTE: Does not check for whether the entity type is translatable.
* Consider using entity_translation_enabled() instead.
*
* @param $entity_type
* The entity type the bundle to be checked belongs to.
* @param $bundle
@@ -1707,32 +1798,7 @@ function entity_translation_settings($entity_type, $bundle) {
* A valid language code.
*/
function entity_translation_language($entity_type, $entity) {
// If a form has been post, we need to check its state to verify if any form
// translation handler is stored there. This is mainly needed when responding
// to an AJAX request where the form language cannot be set from the page
// callback.
$handler = entity_translation_current_form_get_handler();
// Make sure we always have a translation handler instance available.
if (empty($handler)) {
$handler = entity_translation_get_handler($entity_type, $entity);
}
// If a translation handler associated to the current form is found, we need
// to update the wrapped entity. This way submitted values will be picked up.
// Other entities may be created or saved while submitting the current one,
// hence we need to check we are dealing with it.
elseif ($handler->isWrappedEntity($entity_type, $entity)) {
$langcode = $handler->getLanguage();
$handler->setEntity($entity);
$submitted_langcode = $handler->getLanguage();
// If the entity language has changed we are editing the original values. In
// this case we need to update the current form language with the submitted
// one.
if ($submitted_langcode != $langcode) {
$handler->setFormLanguage($submitted_langcode);
}
}
$handler = entity_translation_get_handler($entity_type, $entity);
$langcode = $handler->getFormLanguage();
return !empty($langcode) ? $langcode : $handler->getLanguage();
}
@@ -1745,61 +1811,20 @@ function entity_translation_language($entity_type, $entity) {
* @param $entity
* (optional) The entity to be translated. A bundle name may be passed to
* instantiate an empty entity.
* @param $update
* (optional) Instances are statically cached: if this is TRUE the wrapped
* entity will be replaced by the passed one.
*
* @return EntityTranslationHandlerInterface
* A class implementing EntityTranslationHandlerInterface.
*/
function entity_translation_get_handler($entity_type = NULL, $entity = NULL, $update = FALSE) {
static $drupal_static_fast;
if (!isset($drupal_static_fast['handlers'])) {
$drupal_static_fast['handlers'] = &drupal_static(__FUNCTION__, array());
function entity_translation_get_handler($entity_type = NULL, $entity = NULL) {
if (class_exists('EntityTranslationHandlerFactory')) {
$factory = EntityTranslationHandlerFactory::getInstance();
return empty($entity) ? $factory->getLastHandler($entity_type) : $factory->getHandler($entity_type, $entity);
}
$handlers = &$drupal_static_fast['handlers'];
// Workaround the lack of a context object.
if (empty($entity)) {
if (isset($handlers[$entity_type]['#current'])) {
return $handlers[$entity_type]['#current'];
}
elseif (empty($entity_type) && isset($handlers['#current']['#current'])) {
return $handlers['#current']['#current'];
}
else {
return NULL;
}
}
elseif (is_string($entity)) {
$entity = entity_create_stub_entity($entity_type, array(NULL, NULL, $entity));
}
list($entity_id) = entity_extract_ids($entity_type, $entity);
if (!isset($handlers[$entity_type][$entity_id])) {
// @todo BC layer. Remove before the first stable release.
elseif (!empty($entity_type) && is_object($entity)) {
$entity_info = entity_get_info($entity_type);
$class = $entity_info['translation']['entity_translation']['class'];
// @todo remove fourth parameter once 3rd-party translation handlers have
// been fixed and no longer require the deprecated entity_id parameter.
$handler = new $class($entity_type, $entity_info, $entity, NULL);
// If the entity id is empty we cannot cache the translation handler
// instance.
if (empty($entity_id)) {
return $handler;
}
else {
$handlers[$entity_type][$entity_id] = $handler;
}
return new EntityTranslationDefaultHandler($entity_type, $entity_info, $entity);
}
elseif ($update) {
$handlers[$entity_type][$entity_id]->setEntity($entity);
}
$handlers[$entity_type]['#current'] = $handlers[$entity_type][$entity_id];
$handlers['#current']['#current'] = $handlers[$entity_type][$entity_id];
return $handlers[$entity_type][$entity_id];
}
/**
@@ -1813,24 +1838,11 @@ function entity_translation_get_handler($entity_type = NULL, $entity = NULL, $up
* @return EntityTranslationHandlerInterface
* A class implementing EntityTranslationHandlerInterface.
*/
function entity_translation_entity_form_get_handler($form, &$form_state) {
function entity_translation_entity_form_get_handler($form, $form_state) {
$handler = FALSE;
$entity_type = isset($form['#entity_type']) && is_string($form['#entity_type']) ? $form['#entity_type'] : FALSE;
if ($entity_type) {
if (empty($form_state['storage']['entity_translation']['handler'][$entity_type])) {
if ($info = entity_translation_edit_form_info($form, $form_state)) {
if (entity_translation_enabled($info['entity type'], $info['entity'])) {
$handler = entity_translation_get_handler($info['entity type'], $info['entity']);
$form_state['storage']['entity_translation']['handler'][$info['entity type']] = $handler;
}
}
}
else {
$handler = $form_state['storage']['entity_translation']['handler'][$entity_type];
}
if ($info = entity_translation_edit_form_info($form, $form_state)) {
$handler = entity_translation_get_handler($info['entity type'], $info['entity']);
}
return $handler;
}
@@ -1839,14 +1851,18 @@ function entity_translation_entity_form_get_handler($form, &$form_state) {
*
* @return EntityTranslationHandlerInterface
* A translation handler instance if available, FALSE oterwise.
*
* @deprecated This is no longer used and will be removed in the first stable
* release.
*/
function entity_translation_current_form_get_handler() {
$handler = FALSE;
if (!empty($_POST['form_build_id'])) {
$form_state = form_state_defaults();
$form = form_get_cache($_POST['form_build_id'], $form_state);
$handler = entity_translation_entity_form_get_handler($form, $form_state);
if ($form = form_get_cache($_POST['form_build_id'], $form_state)) {
$handler = entity_translation_entity_form_get_handler($form, $form_state);
}
}
return $handler;
@@ -1871,11 +1887,13 @@ function entity_translation_edit_form_info($form, $form_state) {
if ($entity_type) {
$entity_info = entity_get_info($form['#entity_type']);
if (!empty($entity_info['translation']['entity_translation']['edit form'])) {
$entity_key = $entity_info['translation']['entity_translation']['edit form'];
if (isset($form_state[$entity_key])) {
$entity_keys = explode('][', $entity_info['translation']['entity_translation']['edit form']);
$key_exists = FALSE;
$entity = drupal_array_get_nested_value($form_state, $entity_keys, $key_exists);
if ($key_exists) {
$info = array(
'entity type' => $form['#entity_type'],
'entity' => (object) $form_state[$entity_key],
'entity' => (object) $entity,
);
}
}

View File

@@ -7,9 +7,9 @@ dependencies[] = i18n
dependencies[] = i18n_menu
files[] = entity_translation_i18n_menu.test
; Information added by drupal.org packaging script on 2013-07-23
version = "7.x-1.0-beta3"
; Information added by Drupal.org packaging script on 2015-01-22
version = "7.x-1.0-beta4"
core = "7.x"
project = "entity_translation"
datestamp = "1374601567"
datestamp = "1421971088"

View File

@@ -4,9 +4,9 @@ package = Multilingual - Entity Translation
core = 7.x
dependencies[] = entity_translation
; Information added by drupal.org packaging script on 2013-07-23
version = "7.x-1.0-beta3"
; Information added by Drupal.org packaging script on 2015-01-22
version = "7.x-1.0-beta4"
core = "7.x"
project = "entity_translation"
datestamp = "1374601567"
datestamp = "1421971088"

View File

@@ -14,6 +14,21 @@
*/
interface EntityTranslationHandlerInterface {
/**
* Injects the translation handler factory.
*/
public function setFactory(EntityTranslationHandlerFactory $factory);
/**
* Registers a child translation handler for the given entity.
*/
public function addChild($entity_type, $entity);
/**
* Removes a previously registered child translation handler.
*/
public function removeChild($entity_type, $entity);
/**
* Loads the translation data into the wrapped entity.
*/
@@ -62,6 +77,11 @@ interface EntityTranslationHandlerInterface {
*/
public function removeTranslations();
/**
* Removes all translations from the current revision.
*/
public function removeRevisionTranslations();
/**
* Initialize the language of the original field values.
*
@@ -98,6 +118,9 @@ interface EntityTranslationHandlerInterface {
/**
* Returns TRUE if the entity is currently being translated.
*
* @deprecated This is no longer used and will be removed before the first
* stable release.
*/
public function isTranslating();
@@ -106,6 +129,9 @@ interface EntityTranslationHandlerInterface {
*
* @param $translating
* A boolean value.
*
* @deprecated This is no longer used and will be removed before the first
* stable release.
*/
public function setTranslating($translating);
@@ -114,6 +140,11 @@ interface EntityTranslationHandlerInterface {
*/
public function isRevision();
/**
* Return TRUE if the entity type supports revisions.
*/
public function isRevisionable();
/**
* Replaces the wrapped entity.
*
@@ -122,6 +153,22 @@ interface EntityTranslationHandlerInterface {
*/
public function setEntity($entity);
/**
* Returns the wrapped entity.
*
* @param return
* The wrapped entity.
*/
public function getEntity();
/**
* Returns the wrapped entity type.
*
* @param return
* The wrapped entity type.
*/
public function getEntityType();
/**
* Checks that the wrapped entity matches the give entity
*
@@ -301,6 +348,21 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
protected $entityInfo;
protected $entityId;
protected $bundle;
protected $revisionable;
/**
* The translation handler factory.
*
* @var EntityTranslationHandlerFactory
*/
protected $factory;
/**
* The translation handler hierarchy storage.
*
* @var array
*/
protected $children = array();
private $entityForm;
private $translating;
@@ -357,14 +419,29 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
return;
}
$revisionable = self::isEntityTypeRevisionable($entity_type);
$revisions_ids = array();
foreach ($entities as $id => $entity) {
$entities[$id]->{$translations_key} = self::emptyTranslations();
if ($revisionable) {
list(, $revisions_id,) = entity_extract_ids($entity_type, $entity);
$revisions_ids[$id] = $revisions_id;
}
}
$results = db_select('entity_translation', 'et')
$table = $revisionable ? 'entity_translation_revision' : 'entity_translation';
$query = db_select($table, 'et')
->fields('et')
->condition('entity_type', $entity_type)
->condition('entity_id', array_keys($entities), 'IN')
->condition('entity_type', $entity_type);
if (!$revisionable) {
$query->condition('entity_id', array_keys($entities), 'IN');
}
else {
$query->condition('revision_id', $revisions_ids, 'IN');
}
$results = $query
->orderBy('entity_id')
->orderBy('created')
->execute();
@@ -394,6 +471,46 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
return $links;
}
/**
* @see EntityTranslationHandlerInterface::setFactory()
*/
public function setFactory(EntityTranslationHandlerFactory $factory) {
$this->factory = $factory;
}
/**
* @see EntityTranslationHandlerInterface::addChild()
*/
public function addChild($entity_type, $entity) {
if (!empty($this->factory)) {
$handler = $this->factory->getHandler($entity_type, $entity);
$handler->setFormLanguage($this->getFormLanguage());
$handler->setSourceLanguage($this->getSourceLanguage());
// Avoid registering more than one child handler for each entity.
$hid = $this->factory->getHandlerId($entity_type, $entity);
$this->children[$hid] = $handler;
}
}
/**
* @see EntityTranslationHandlerInterface::removeChild()
*/
public function removeChild($entity_type, $entity) {
if (!empty($this->factory)) {
$hid = $this->factory->getHandlerId($entity_type, $entity);
unset($this->children[$hid]);
}
}
/**
* Proxies the specified method invocation to a child translation handler.
*/
protected function notifyChildren($method, $args) {
foreach ($this->children as $handler) {
call_user_func_array(array($handler, $method), $args);
}
}
/**
* @see EntityTranslationHandlerInterface::loadTranslations()
*/
@@ -410,43 +527,14 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
* @see EntityTranslationHandlerInterface::saveTranslations()
*/
public function saveTranslations() {
// Delete and insert, rather than update, in case a value was added.
db_delete('entity_translation')
->condition('entity_type', $this->entityType)
->condition('entity_id', $this->entityId)
->execute();
$translations = $this->getTranslations();
if (count($translations->data)) {
global $user;
// Save current values.
$this->doSaveTranslations($translations, 'entity_translation');
$columns = array('entity_type', 'entity_id', 'language', 'source', 'uid', 'status', 'translate', 'created', 'changed');
$query = db_insert('entity_translation')->fields($columns);
// These values should overridde the translation ones as they are not
// supposed to change.
$overrides = array(
'entity_id' => $this->entityId,
'entity_type' => $this->entityType,
);
// These instead are just defaults.
$defaults = array(
'source' => '',
'uid' => $user->uid,
'translate' => 0,
'status' => 0,
'created' => REQUEST_TIME,
'changed' => REQUEST_TIME,
);
foreach ($translations->data as $langcode => $translation) {
$translation = $overrides + $translation + $defaults;
$query->values($translation);
}
$query->execute();
// Save revision values.
if ($this->isRevisionable()) {
$this->doSaveTranslations($translations, 'entity_translation_revision', TRUE);
}
// The translation handler interface decouples operations on translations at
@@ -476,6 +564,54 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
}
}
/**
* Saves entity translation records to the storage.
*/
protected function doSaveTranslations($translations, $table, $revision = FALSE) {
// Delete and insert, rather than update, in case a value was added.
$query = db_delete($table)
->condition('entity_type', $this->entityType)
->condition('entity_id', $this->entityId);
// If we are storing translations for the current revision or we are
// deleting the entity we should remove all translation data.
$langcode = $translations->original;
$hook = isset($translations->hook) ? $translations->hook : array();
if ($revision && $this->isRevisionable() && (empty($hook[$langcode]['hook']) || $hook[$langcode]['hook'] != 'delete')) {
$query->condition('revision_id', $this->revisionId);
}
$query->execute();
if (count($translations->data)) {
$columns = array('entity_type', 'entity_id', 'revision_id', 'language', 'source', 'uid', 'status', 'translate', 'created', 'changed');
$query = db_insert($table)->fields($columns);
// These values should override the translation ones as they are not
// supposed to change.
$overrides = array(
'entity_type' => $this->entityType,
'entity_id' => $this->entityId,
'revision_id' => $this->isRevisionable() ? $this->revisionId : $this->entityId,
);
// These instead are just defaults.
$defaults = array(
'source' => '',
'uid' => $GLOBALS['user']->uid,
'translate' => 0,
'status' => 0,
'created' => REQUEST_TIME,
'changed' => REQUEST_TIME,
);
foreach ($translations->data as $translation) {
$translation = $overrides + $translation + $defaults;
$query->values($translation);
}
$query->execute();
}
}
/**
* @see EntityTranslationHandlerInterface::getTranslations()
*/
@@ -530,6 +666,9 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
}
}
}
$args = func_get_args();
$this->notifyChildren(__FUNCTION__, $args);
}
/**
@@ -600,7 +739,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
if (empty($this->getTranslations()->data)) {
$this->initTranslations();
}
elseif (!empty($langcode) && !$this->isTranslating()) {
elseif (!empty($langcode)) {
$this->setOriginalLanguage($langcode);
}
}
@@ -612,6 +751,17 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
$this->removeTranslation(NULL);
}
/**
* @see EntityTranslationHandlerInterface::removeRevisionTranslations()
*/
public function removeRevisionTranslations() {
$translations_key = $this->getTranslationsKey();
$keys = array_keys($this->entity->{$translations_key}->data);
$values = array_fill(0, count($keys), array('hook' => 'delete_revision'));
$this->removeTranslation(NULL);
$this->entity->{$translations_key}->hook = array_combine($keys, $values);
}
/**
* @see EntityTranslationHandlerInterface::initOriginalTranslation()
*/
@@ -702,13 +852,17 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
public function setOriginalLanguage($langcode) {
$translations = $this->getTranslations();
if (isset($translations->original) && $translations->original != $langcode) {
$translations->data[$langcode] = $translations->data[$translations->original];
$translations->data[$langcode]['language'] = $langcode;
unset($translations->data[$translations->original]);
}
if (!isset($translations->original) || $translations->original != $langcode) {
if (isset($translations->original)) {
$translations->data[$langcode] = $translations->data[$translations->original];
$translations->data[$langcode]['language'] = $langcode;
unset($translations->data[$translations->original]);
}
$translations->original = $langcode;
$translations->original = $langcode;
$args = func_get_args();
$this->notifyChildren(__FUNCTION__, $args);
}
}
/**
@@ -732,6 +886,25 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
return FALSE;
}
/**
* @see EntityTranslationHandlerInterface::isRevisionable()
*/
public function isRevisionable() {
$result = FALSE;
if (!isset($this->revisionable)) {
$result = self::isEntityTypeRevisionable($this->entityType);
}
return $result;
}
/**
* Returns whether the entity type is revisionable.
*/
public static function isEntityTypeRevisionable($entity_type) {
$entity_info = entity_get_info($entity_type);
return variable_get('entity_translation_revision_enabled', FALSE) && !empty($entity_info['entity keys']['revision']);
}
/**
* @see EntityTranslationHandlerInterface::setEntity()
*/
@@ -744,8 +917,27 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
$this->entity->{$translations_key} = self::emptyTranslations();
}
// Update bundle and entity id properties.
list($this->entityId, , $this->bundle) = entity_extract_ids($this->entityType, $this->entity);
// Update entity properties.
list($this->entityId, $this->revisionId, $this->bundle) = entity_extract_ids($this->entityType, $this->entity);
// Initialize the handler id if needed.
if (!empty($this->factory)) {
$this->factory->getHandlerId($this->entityType, $entity);
}
}
/**
* @see EntityTranslationHandlerInterface::getEntity()
*/
public function getEntity() {
return $this->entity;
}
/**
* @see EntityTranslationHandlerInterface::getEntityType()
*/
public function getEntityType() {
return $this->entityType;
}
/**
@@ -767,6 +959,8 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
$translation['translate'] = 1;
}
}
$args = func_get_args();
$this->notifyChildren(__FUNCTION__, $args);
}
}
@@ -922,6 +1116,8 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
*/
public function setFormLanguage($langcode) {
$this->formLanguage = $langcode;
$args = func_get_args();
$this->notifyChildren(__FUNCTION__, $args);
}
/**
@@ -936,6 +1132,8 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
*/
public function setSourceLanguage($langcode) {
$this->sourceLanguage = $langcode;
$args = func_get_args();
$this->notifyChildren(__FUNCTION__, $args);
}
/**
@@ -974,12 +1172,16 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
$languages = language_list();
$access = user_access('translate any entity') || user_access("translate $this->entityType entities");
// Store contextual information in the form state.
$form_state['entity_translation']['form_langcode'] = $form_langcode;
$form_state['entity_translation']['source_langcode'] = $this->getSourceLanguage();
// The only way to determine whether we are editing the original values is
// comparing form language and entity language. Since a language change
// might render impossible to make this check after form submission, we
// store the related information here.
$form_state['entity_translation']['is_translation'] = $is_translation;
// Adjust page title to specify the current language being edited, if we
// have at least one translation.
if ($form_langcode != LANGUAGE_NONE && (!$no_translations || $new_translation)) {
@@ -1019,7 +1221,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
// Add the entity language switcher.
$this->entityFormLanguageWidget($form, $form_state);
if ($is_translation) {
if ($is_translation && isset($form['actions']['delete'])) {
// Replace the delete button with the delete translation one.
if (!$new_translation) {
$weight = 100;
@@ -1101,7 +1303,8 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
);
}
$name = $new_translation ? $GLOBALS['user']->name : user_load($translations->data[$form_langcode]['uid'])->name;
$translation_author = $new_translation ? $GLOBALS['user'] : user_load($translations->data[$form_langcode]['uid']);
$name = isset($translation_author->name) ? $translation_author->name : '';
$form['translation']['name'] = array(
'#type' => 'textfield',
'#title' => t('Authored by'),
@@ -1142,8 +1345,9 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
* Either remove access or add a translatability clue depending on the current
* user's "edit translation shared fields" permissions.
*/
public function entityFormSharedElements(&$element) {
static $ignored_types, $shared_labels, $access;
public function entityFormSharedElements(&$element, $access = NULL) {
static $ignored_types, $shared_labels;
if (!isset($ignored_types)) {
$ignored_types = array_flip(array('actions', 'value', 'hidden', 'vertical_tabs', 'token'));
}
@@ -1156,7 +1360,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
foreach (element_children($element) as $key) {
if (!isset($element[$key]['#type'])) {
$this->entityFormSharedElements($element[$key]);
$this->entityFormSharedElements($element[$key], $access);
}
else {
// Ignore non-widget form elements.
@@ -1183,7 +1387,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
public function entityFormLanguageWidget(&$form, &$form_state) {
if (entity_translation_enabled($this->entityType, $this->bundle)) {
$is_new = $this->isNewEntity();
$is_translation = !$is_new && !empty($form_state['entity_translation']['is_translation']);
$is_translation = !empty($form_state['entity_translation']['is_translation']);
$translations = $this->getTranslations();
$settings = entity_translation_settings($this->entityType, $this->bundle);
$languages = entity_translation_languages($this->entityType, $this->entity);
@@ -1191,8 +1395,8 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
foreach ($languages as $langcode => $language) {
// Disable languages for existing translations, so it is not possible to
// switch this entity to some language which is already in the translation
// set.
// switch this entity to some language which is already in the
// translation set.
if (!isset($translations->data[$langcode]) || empty($translations->data[$langcode]['source'])) {
$options[$langcode] = t($language->name);
}
@@ -1217,17 +1421,14 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
}
}
if (!empty($form['actions']['submit']['#submit'])) {
$submit = &$form['actions']['submit']['#submit'];
}
else {
if (!isset($form['#submit'])) {
$form['#submit'] = array();
}
$submit = &$form['#submit'];
}
array_unshift($submit, 'entity_translation_language_widget_submit');
// Prepend an empty form element to the form array so that we can update the
// form language before any other form handler has been called.
$form = array(
'entity_translation_entity_form_language_update' => array(
'#element_validate' => array('entity_translation_entity_form_language_update'),
'#entity_type' => $this->entityType,
),
) + $form;
}
/**
@@ -1260,28 +1461,36 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
protected function updateFormLanguage($form_state) {
// Update the form language as it might have changed. We exploit the
// validation phase to be sure to act as early as possible.
if (isset($form_state['values']['language']) && !$this->isTranslationForm()) {
$this->setFormLanguage($form_state['values'][$this->getLanguageKey()]);
$language_key = $this->getLanguageKey();
if (isset($form_state['values'][$language_key]) && !$this->isTranslationForm()) {
$langcode = $form_state['values'][$language_key];
$this->setFormLanguage($langcode);
}
}
/**
* @see EntityTranslationHandlerInterface::entityFormLanguageWidgetSubmit()
*/
function entityFormLanguageWidgetSubmit($form, &$form_state) {
public function entityFormLanguageWidgetSubmit($form, &$form_state) {
if (!entity_translation_enabled($this->entityType, $this->bundle)) {
return;
}
$this->updateFormLanguage($form_state);
$form_langcode = $this->getFormLanguage();
foreach (field_info_instances($this->entityType, $this->bundle) as $instance) {
$field_name = $instance['field_name'];
$field = field_info_field($field_name);
$previous_langcode = $form[$field_name]['#language'];
if (isset($form[$field_name]['#language'])) {
$field = field_info_field($field_name);
$previous_langcode = $form[$field_name]['#language'];
// Handle a possible language change: new language values are inserted,
// previous ones are deleted.
if ($field['translatable'] && $previous_langcode != $form_langcode) {
$form_state['values'][$field_name][$form_langcode] = $form_state['values'][$field_name][$previous_langcode];
$form_state['values'][$field_name][$previous_langcode] = array();
// Handle a possible language change: new language values are inserted,
// previous ones are deleted.
if ($field['translatable'] && $previous_langcode != $form_langcode && isset($form_state['values'][$field_name][$previous_langcode])) {
$form_state['values'][$field_name][$form_langcode] = $form_state['values'][$field_name][$previous_langcode];
$form_state['values'][$field_name][$previous_langcode] = array();
}
}
}
}

View File

@@ -0,0 +1,139 @@
<?php
/**
* @file
* Translation handler factory for the Entity Translation module.
*/
/**
* Class implementing the entity translation handler factory.
*/
class EntityTranslationHandlerFactory {
/**
* A singleton instance of the factory.
*
* @var EntityTranslationHandlerFactory
*/
protected static $instance;
/**
* Counter used to generate handler ids for new entities.
*
* @var int
*/
protected static $newId = 1;
/**
* Handlers cache.
*
* @var array
*/
protected $handlers = array();
/**
* The last translation handler retrieved.
*
* @var EntityTranslationHandlerInterface
*/
protected $last;
/**
* An array of translation handler retrieved by type.
*
* @var array
*/
protected $lastByType = array();
/**
* Returns the singleton instance of the translation handler factory.
*
* @return EntityTranslationHandlerFactory
*/
public static function getInstance() {
if (!isset(self::$instance)) {
self::$instance = new EntityTranslationHandlerFactory();
}
return self::$instance;
}
/**
* Prevents the factory from being publicly instantiated.
*/
protected function __construct() {}
/**
* Translation handler factory.
*
* @param $entity_type
* The type of $entity; e.g. 'node' or 'user'.
* @param $entity
* The entity to be translated. A bundle name may be passed to instantiate
* an empty entity.
*
* @return EntityTranslationHandlerInterface
* A class implementing EntityTranslationHandlerInterface.
*/
public function getHandler($entity_type, $entity) {
if (is_numeric($entity)) {
$entities = entity_load($entity_type, array($entity));
$entity = reset($entities);
}
elseif (is_string($entity)) {
$entity = entity_create_stub_entity($entity_type, array(NULL, NULL, $entity));
}
$id = $this->getHandlerId($entity_type, $entity);
if (!isset($this->handlers[$entity_type][$id])) {
$entity_info = entity_get_info($entity_type);
$class = $entity_info['translation']['entity_translation']['class'];
// @todo Remove the fourth parameter once 3rd-party translation handlers
// have been fixed and no longer require the deprecated entity_id
// parameter.
$handler = new $class($entity_type, $entity_info, $entity, NULL);
$handler->setFactory($this);
$this->handlers[$entity_type][$id] = $handler;
}
$this->last = $this->handlers[$entity_type][$id];
$this->lastByType[$entity_type] = $this->last;
$this->last->setEntity($entity);
return $this->last;
}
/**
* Retrieves the translation handler identifier for the given entity.
*
* @param $entity_type
* The type of the entity the translation handler will wrap.
* @param $entity
* The entity the translation handler will wrap.
*/
public function getHandlerId($entity_type, $entity) {
if (!isset($entity->entity_translation_handler_id)) {
list($id, $revision_id) = entity_extract_ids($entity_type, $entity);
$revision_id = isset($revision_id) ? $revision_id : 0;
$entity->entity_translation_handler_id = $entity_type . '-' . (!empty($id) ? 'eid-' . $id . '-' . $revision_id : 'new-' . self::$newId++);
}
return $entity->entity_translation_handler_id;
}
/**
* Returns the last translation handler retrieved.
*
* @param $entity_type
* (optional) The entity type of the translation handler. Defaults to the
* last one.
*
* @return EntityTranslationHandlerInterface
* A class implementing EntityTranslationHandlerInterface.
*/
public function getLastHandler($entity_type = NULL) {
if (isset($entity_type)) {
return isset($this->lastByType[$entity_type]) ? $this->lastByType[$entity_type] : NULL;
}
return $this->last;
}
}

View File

@@ -0,0 +1,142 @@
<?php
/**
* @file
* Plugin to provide access control on entities based on translations.
*/
/**
* CTools Plugin definition.
*/
$plugin = array(
'title' => t("Entity translation: translation exists"),
'description' => t('Control access by checking if a translation exists.'),
'callback' => 'entity_translation_translation_exists_ctools_access_check',
'default' => array('language' => array()),
'settings form' => 'entity_translation_translation_exists_ctools_access_settings',
'settings form submit' => 'entity_translation_translation_exists_ctools_access_settings_submit',
'summary' => 'entity_translation_translation_exists_ctools_access_summary',
'get child' => 'entity_translation_translation_exists_ctools_access_get_child',
'get children' => 'entity_translation_translation_exists_ctools_access_get_children',
);
/**
* Get a particular instance of this plugin.
*/
function entity_translation_translation_exists_ctools_access_get_child($plugin, $parent, $child) {
$plugins = &drupal_static(__FUNCTION__, array());
if (empty($plugins[$parent . ':' . $child])) {
$plugins[$parent . ':' . $child] = _entity_translation_translation_exists_ctools_access_definition($plugin, $parent, $child);
}
return $plugins[$parent . ':' . $child];
}
/**
* Get all children of this plugin.
*/
function entity_translation_translation_exists_ctools_access_get_children($plugin, $parent) {
$plugins = &drupal_static(__FUNCTION__, array());
if (!empty($plugins)) {
return $plugins;
}
$entities = entity_get_info();
foreach ($entities as $entity_type => $entity) {
$plugin = _entity_translation_translation_exists_ctools_access_definition($plugin, $parent, $entity_type);
$plugins[$parent . ':' . $entity_type] = $plugin;
}
return $plugins;
}
/**
* Plugin definition for one particular plugin child.
*/
function _entity_translation_translation_exists_ctools_access_definition($plugin, $parent, $entity_type) {
$entity = entity_get_info($entity_type);
$plugin['title'] = t('@entity: entity translation exists', array('@entity' => $entity['label']));
$plugin['keyword'] = $entity_type;
$plugin['description'] = t('Control access by @entity language', array('@entity' => $entity['label']));
$plugin['name'] = $parent . ':' . $entity_type;
$plugin['required context'] = new ctools_context_required(t(ucfirst($entity_type)), $entity_type);
return $plugin;
}
/**
* Settings form for the 'by node_language' access plugin
*/
function entity_translation_translation_exists_ctools_access_settings($form, &$form_state, $conf) {
$options = array(
ENTITY_TRANSLATION_LANGUAGE_CURRENT => t('Current content language'),
ENTITY_TRANSLATION_LANGUAGE_DEFAULT => t('Default site language'),
LANGUAGE_NONE => t('Language neutral'),
);
$options = array_merge($options, locale_language_list());
$form['settings']['language'] = array(
'#title' => t('Language'),
'#type' => 'checkboxes',
'#options' => $options,
'#description' => t('This rule will pass if any of these languages are present.'),
'#default_value' => $conf['language'],
);
return $form;
}
/**
* Grant access based on the which translations are available.
*/
function entity_translation_translation_exists_ctools_access_check($conf, $context) {
// Check that the context exists.
if (empty($context) || empty($context->data)) {
return FALSE;
}
$entity = $context->data;
$handler = entity_translation_get_handler($context->keyword, $entity);
if (!empty($handler)) {
$translations = $handler->getTranslations();
global $language_content;
foreach ($conf['language'] as $lang) {
if ($lang) {
switch ($lang) {
case ENTITY_TRANSLATION_LANGUAGE_CURRENT:
$lang = $language_content->language;
break;
case ENTITY_TRANSLATION_LANGUAGE_DEFAULT:
$lang = language_default('language');
break;
}
if (isset($translations->data[$lang]) && $translations->data[$lang]['status']) {
return TRUE;
}
}
}
}
return FALSE;
}
/**
* Provide a summary description based upon the checked node_languages.
*/
function entity_translation_translation_exists_ctools_access_summary($conf, $context) {
$languages = array(
ENTITY_TRANSLATION_LANGUAGE_CURRENT => t('Current site content language'),
ENTITY_TRANSLATION_LANGUAGE_DEFAULT => t('Default site language'),
LANGUAGE_NONE => t('Language neutral'),
);
$languages = array_merge($languages, locale_language_list());
if (!isset($conf['language'])) {
$conf['language'] = array();
}
$names = array();
foreach (array_filter($conf['language']) as $language) {
$names[] = $languages[$language];
}
if (empty($names)) {
return t('@identifier is in any language', array('@identifier' => $context->identifier));
}
return format_plural(count($names), '@languages translation exists for @identifier', '@languages translations exists for identifier', array('@languages' => implode(', ', $names), '@identifier' => $context->identifier));
}

View File

@@ -234,7 +234,7 @@ class EntityTranslationTestCase extends DrupalWebTestCase {
$this->drupalGet('node/' . $node->nid . '/edit/add/' . $source_langcode . '/' . $langcode);
$body_key = "body[$langcode][0][value]";
$this->assertFieldByXPath("//textarea[@name='$body_key']", $node->body[$node->language][0]['value'], 'Original body value correctly populated.');
$this->assertFieldByXPath("//textarea[@name='$body_key']", $node->body[$source_langcode][0]['value'], 'Original body value correctly populated.');
$this->assertFieldById('edit-body-' . $langcode . '-add-more', t('Add another item'), t('Add another item button found.'));
$edit = array();
@@ -477,7 +477,7 @@ class EntityTranslationHookTestCase extends EntityTranslationTestCase {
$handler->setTranslation($translation);
$handler->saveTranslations();
$node = node_load($node->nid, NULL, TRUE);
$handler = entity_translation_get_handler('node', $node, TRUE);
$handler = entity_translation_get_handler('node', $node);
$translations = $handler->getTranslations();
$this->assertTrue(!empty($translations->data['it']), t('An Italian translation has been created'));
$info = $this->getHookInfo();
@@ -497,7 +497,7 @@ class EntityTranslationHookTestCase extends EntityTranslationTestCase {
$handler->setTranslation($translation);
$handler->saveTranslations();
$node = node_load($node->nid, NULL, TRUE);
$handler = entity_translation_get_handler('node', $node, TRUE);
$handler = entity_translation_get_handler('node', $node);
$translations = $handler->getTranslations();
$this->assertTrue(!empty($translations->data['es']), t('A Spanish translation has been created'));
$info = $this->getHookInfo();

View File

@@ -6,9 +6,9 @@ hidden = TRUE
dependencies[] = entity_translation
files[] = entity_translation_test.module
; Information added by drupal.org packaging script on 2013-07-23
version = "7.x-1.0-beta3"
; Information added by Drupal.org packaging script on 2015-01-22
version = "7.x-1.0-beta4"
core = "7.x"
project = "entity_translation"
datestamp = "1374601567"
datestamp = "1421971088"

View File

@@ -224,4 +224,24 @@ function entity_translation_views_data_alter(&$data) {
);
}
}
// Expose all translatable fields, using a handler based off Views' default
// field handler, that allows users to select the language to display the
// field in.
foreach (field_info_fields() as $field) {
if ($field['translatable'] && $field['storage']['type'] == 'field_sql_storage') {
// Set defaults and just change the handler, title, group, and help.
$defaults = field_views_field_default_views_data($field);
foreach ($defaults as $table_name => $table_data) {
if (isset($data[$table_name][$field['field_name']])) {
$field_title = $table_data[$field['field_name']]['title'];
$table_data[$field['field_name']]['title'] = t('!title: translated', array('!title' => $field_title));
$table_data[$field['field_name']]['group'] = t('Entity translation');
$table_data[$field['field_name']]['help'] .= ' ' . t('Show the field !title translated into a specified language', array('!title' => $field_title));
$table_data[$field['field_name']]['field']['handler'] = 'entity_translation_handler_field_field';
$data[$table_name][$field['field_name'] . '_et'] = $table_data[$field['field_name']];
}
}
}
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* @file
* This file contains a field handler for entity translation that shows field
* content translated into a specified language.
*/
class entity_translation_handler_field_field extends views_handler_field_field {
function option_definition() {
$options = parent::option_definition();
$options['language'] = array(
'default' => '***CURRENT_LANGUAGE***',
);
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$languages = array(
'***CURRENT_LANGUAGE***' => t("Current user's language"),
'***DEFAULT_LANGUAGE***' => t("Default site language"),
LANGUAGE_NONE => t('Language neutral'),
);
$languages = array_merge($languages, locale_language_list());
$form['language'] = array(
'#type' => 'select',
'#title' => t('Language'),
'#options' => $languages,
'#default_value' => $this->options['language'],
'#description' => t('Select the language to display this field in')
);
}
/**
* Overrides parent::field_language, retrieving the language from the handler
* options.
*/
function field_language($entity_type, $entity) {
global $language_content;
if (field_is_translatable($entity_type, $this->field_info)) {
$default_language = language_default('language');
$language = str_replace(array('***CURRENT_LANGUAGE***', '***DEFAULT_LANGUAGE***'),
array($language_content->language, $default_language),
$this->options['language']);
// Give the Field Language API a chance to fallback to a different language
// (or LANGUAGE_NONE), in case the field has no data for the selected language.
// field_view_field() does this as well, but since the returned language code
// is used before calling it, the fallback needs to happen explicitly.
$language = field_language($entity_type, $entity, $this->field_info['field_name'], $language);
return $language;
}
else {
return LANGUAGE_NONE;
}
}
}