diff --git a/includes/common.inc b/includes/common.inc index ff25493..407470a 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -7760,6 +7760,37 @@ function entity_label($entity_type, $entity) { } /** + * Returns the language of an entity. + * + * @param $entity_type + * The entity type; e.g., 'node' or 'user'. + * @param $entity + * The entity for which to get the language. + * + * @return + * A valid language code or NULL if the entity has no language support. + */ +function entity_language($entity_type, $entity) { + $info = entity_get_info($entity_type); + + // Invoke the callback to get the language. If there is no callback, try to + // get it from a property of the entity, otherwise NULL. + if (isset($info['language callback']) && function_exists($info['language callback'])) { + $langcode = $info['language callback']($entity_type, $entity); + } + elseif (!empty($info['entity keys']['language']) && isset($entity->{$info['entity keys']['language']})) { + $langcode = $entity->{$info['entity keys']['language']}; + } + else { + // @todo The right value for D8 should be LANGUAGE_NONE, we cannot use it + // here to preserve backward compatibility. + $langcode = NULL; + } + + return $langcode; +} + +/** * Helper function for attaching field API validation to entity forms. */ function entity_form_field_validate($entity_type, $form, &$form_state) { diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 6938833..429c3b0 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -103,6 +103,7 @@ function comment_entity_info() { 'id' => 'cid', 'bundle' => 'node_type', 'label' => 'subject', + 'language' => 'language', ), 'bundles' => array(), 'view modes' => array( diff --git a/modules/locale/locale.module b/modules/locale/locale.module index e0981b2..c3133bc 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -396,7 +396,7 @@ function locale_form_node_form_alter(&$form, &$form_state) { function locale_field_node_form_submit($form, &$form_state) { if (field_has_translation_handler('node', 'locale')) { $node = (object) $form_state['values']; - $available_languages = field_content_languages(); + $current_language = entity_language('node', $node); list(, , $bundle) = entity_extract_ids('node', $node); foreach (field_info_instances('node', $bundle) as $instance) { @@ -406,8 +406,8 @@ function locale_field_node_form_submit($form, &$form_state) { // Handle a possible language change: new language values are inserted, // previous ones are deleted. - if ($field['translatable'] && $previous_language != $node->language) { - $form_state['values'][$field_name][$node->language] = $node->{$field_name}[$previous_language]; + if ($field['translatable'] && $previous_language != $current_language) { + $form_state['values'][$field_name][$current_language] = $node->{$field_name}[$previous_language]; $form_state['values'][$field_name][$previous_language] = array(); } } diff --git a/modules/locale/locale.test b/modules/locale/locale.test index ffda6f5..0c6da13 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -2788,8 +2788,9 @@ class LocaleCommentLanguageFunctionalTest extends DrupalWebTestCase { ->orderBy('cid', 'DESC') ->execute() ->fetchObject(); - $args = array('%node_language' => $node_langcode, '%comment_language' => $comment->language, '%langcode' => $langcode); - $this->assertEqual($comment->language, $langcode, t('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args)); + $comment_langcode = entity_language('comment', $comment); + $args = array('%node_language' => $node_langcode, '%comment_language' => $comment_langcode, '%langcode' => $langcode); + $this->assertEqual($comment_langcode, $langcode, t('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args)); } } } diff --git a/modules/node/node.admin.inc b/modules/node/node.admin.inc index 9eeee0d..272373a 100644 --- a/modules/node/node.admin.inc +++ b/modules/node/node.admin.inc @@ -431,7 +431,8 @@ function node_admin_nodes() { $destination = drupal_get_destination(); $options = array(); foreach ($nodes as $node) { - $l_options = $node->language != LANGUAGE_NONE && isset($languages[$node->language]) ? array('language' => $languages[$node->language]) : array(); + $langcode = entity_language('node', $node); + $l_options = $langcode != LANGUAGE_NONE && isset($languages[$langcode]) ? array('language' => $languages[$langcode]) : array(); $options[$node->nid] = array( 'title' => array( 'data' => array( @@ -448,11 +449,11 @@ function node_admin_nodes() { 'changed' => format_date($node->changed, 'short'), ); if ($multilanguage) { - if ($node->language == LANGUAGE_NONE || isset($languages[$node->language])) { - $options[$node->nid]['language'] = $node->language == LANGUAGE_NONE ? t('Language neutral') : t($languages[$node->language]->name); + if ($langcode == LANGUAGE_NONE || isset($languages[$langcode])) { + $options[$node->nid]['language'] = $langcode == LANGUAGE_NONE ? t('Language neutral') : t($languages[$langcode]->name); } else { - $options[$node->nid]['language'] = t('Undefined language (@langcode)', array('@langcode' => $node->language)); + $options[$node->nid]['language'] = t('Undefined language (@langcode)', array('@langcode' => $langcode)); } } // Build a list of all the accessible operations for the current node. diff --git a/modules/node/node.install b/modules/node/node.install index c54ebfb..c8fa1bd 100644 --- a/modules/node/node.install +++ b/modules/node/node.install @@ -890,7 +890,7 @@ function node_update_7012() { ->execute(); // Switch field languages to LANGUAGE_NONE, since initially they were - // assigned $node->language. + // assigned the node language. foreach (array('field_data_body', 'field_revision_body') as $table) { db_update($table) ->fields(array('language' => LANGUAGE_NONE)) diff --git a/modules/node/node.module b/modules/node/node.module index 2614c41..6142c0d 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -177,6 +177,7 @@ function node_entity_info() { 'revision' => 'vid', 'bundle' => 'type', 'label' => 'title', + 'language' => 'language', ), 'bundle keys' => array( 'bundle' => 'type', @@ -1684,7 +1685,7 @@ function node_search_execute($keys = NULL, $conditions = NULL) { 'extra' => $extra, 'score' => $item->calculated_score, 'snippet' => search_excerpt($keys, $node->rendered), - 'language' => $node->language, + 'language' => entity_language('node', $node), ); } return $results; diff --git a/modules/node/node.pages.inc b/modules/node/node.pages.inc index 89a1593..31d396a 100644 --- a/modules/node/node.pages.inc +++ b/modules/node/node.pages.inc @@ -299,7 +299,7 @@ function node_form($form, &$form_state, $node) { } $form += array('#submit' => array()); - field_attach_form('node', $node, $form, $form_state, $node->language); + field_attach_form('node', $node, $form, $form_state, entity_language('node', $node)); return $form; } diff --git a/modules/node/node.test b/modules/node/node.test index f46d2a1..54773d0 100644 --- a/modules/node/node.test +++ b/modules/node/node.test @@ -2268,15 +2268,16 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase { // Generate and test sanitized tokens. $tests = array(); + $langcode = entity_language('node', $node); $tests['[node:nid]'] = $node->nid; $tests['[node:vid]'] = $node->vid; $tests['[node:tnid]'] = $node->tnid; $tests['[node:type]'] = 'article'; $tests['[node:type-name]'] = 'Article'; $tests['[node:title]'] = check_plain($node->title); - $tests['[node:body]'] = _text_sanitize($instance, $node->language, $node->body[$node->language][0], 'value'); - $tests['[node:summary]'] = _text_sanitize($instance, $node->language, $node->body[$node->language][0], 'summary'); - $tests['[node:language]'] = check_plain($node->language); + $tests['[node:body]'] = _text_sanitize($instance, $langcode, $node->body[$langcode][0], 'value'); + $tests['[node:summary]'] = _text_sanitize($instance, $langcode, $node->body[$langcode][0], 'summary'); + $tests['[node:language]'] = check_plain($langcode); $tests['[node:url]'] = url('node/' . $node->nid, $url_options); $tests['[node:edit-url]'] = url('node/' . $node->nid . '/edit', $url_options); $tests['[node:author]'] = check_plain(format_username($account)); @@ -2295,9 +2296,9 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase { // Generate and test unsanitized tokens. $tests['[node:title]'] = $node->title; - $tests['[node:body]'] = $node->body[$node->language][0]['value']; - $tests['[node:summary]'] = $node->body[$node->language][0]['summary']; - $tests['[node:language]'] = $node->language; + $tests['[node:body]'] = $node->body[$langcode][0]['value']; + $tests['[node:summary]'] = $node->body[$langcode][0]['summary']; + $tests['[node:language]'] = $langcode; $tests['[node:author:name]'] = format_username($account); foreach ($tests as $input => $expected) { diff --git a/modules/node/node.tokens.inc b/modules/node/node.tokens.inc index 491ec81..e43db5e 100644 --- a/modules/node/node.tokens.inc +++ b/modules/node/node.tokens.inc @@ -144,7 +144,8 @@ function node_tokens($type, $tokens, array $data = array(), array $options = arr break; case 'language': - $replacements[$original] = $sanitize ? check_plain($node->language) : $node->language; + $langcode = entity_language('node', $node); + $replacements[$original] = $sanitize ? check_plain($langcode) : $langcode; break; case 'url': diff --git a/modules/path/path.module b/modules/path/path.module index 9df4988..1bb06f4 100644 --- a/modules/path/path.module +++ b/modules/path/path.module @@ -99,8 +99,9 @@ function path_form_node_form_alter(&$form, $form_state) { $path = array(); if (!empty($form['#node']->nid)) { $conditions = array('source' => 'node/' . $form['#node']->nid); - if ($form['#node']->language != LANGUAGE_NONE) { - $conditions['language'] = $form['#node']->language; + $langcode = entity_language('node', $form['#node']); + if ($langcode != LANGUAGE_NONE) { + $conditions['language'] = $langcode; } $path = path_load($conditions); if ($path === FALSE) { @@ -111,7 +112,7 @@ function path_form_node_form_alter(&$form, $form_state) { 'pid' => NULL, 'source' => isset($form['#node']->nid) ? 'node/' . $form['#node']->nid : NULL, 'alias' => '', - 'language' => isset($form['#node']->language) ? $form['#node']->language : LANGUAGE_NONE, + 'language' => isset($langcode) ? $langcode : LANGUAGE_NONE, ); $form['path'] = array( @@ -192,8 +193,9 @@ function path_node_insert($node) { // Only save a non-empty alias. if (!empty($path['alias'])) { // Ensure fields for programmatic executions. + $langcode = entity_language('node', $node); $path['source'] = 'node/' . $node->nid; - $path['language'] = isset($node->language) ? $node->language : LANGUAGE_NONE; + $path['language'] = isset($langcode) ? $langcode : LANGUAGE_NONE; path_save($path); } } @@ -210,13 +212,7 @@ function path_node_update($node) { if (!empty($path['pid']) && empty($path['alias'])) { path_delete($path['pid']); } - // Only save a non-empty alias. - if (!empty($path['alias'])) { - // Ensure fields for programmatic executions. - $path['source'] = 'node/' . $node->nid; - $path['language'] = isset($node->language) ? $node->language : LANGUAGE_NONE; - path_save($path); - } + path_node_insert($node); } } @@ -234,7 +230,10 @@ function path_node_delete($node) { function path_form_taxonomy_form_term_alter(&$form, $form_state) { // Make sure this does not show up on the delete confirmation form. if (empty($form_state['confirm_delete'])) { - $path = (isset($form['#term']['tid']) ? path_load('taxonomy/term/' . $form['#term']['tid']) : array()); + $langcode = entity_language('taxonomy_term', (object) $form['#term']); + $langcode = !empty($langcode) ? $langcode : LANGUAGE_NONE; + $conditions = array('source' => 'taxonomy/term/' . $form['#term']['tid'], 'language' => $langcode); + $path = (isset($form['#term']['tid']) ? path_load($conditions) : array()); if ($path === FALSE) { $path = array(); } @@ -242,7 +241,7 @@ function path_form_taxonomy_form_term_alter(&$form, $form_state) { 'pid' => NULL, 'source' => isset($form['#term']['tid']) ? 'taxonomy/term/' . $form['#term']['tid'] : NULL, 'alias' => '', - 'language' => LANGUAGE_NONE, + 'language' => $langcode, ); $form['path'] = array( '#access' => user_access('create url aliases') || user_access('administer url aliases'), @@ -274,7 +273,10 @@ function path_taxonomy_term_insert($term) { if (!empty($path['alias'])) { // Ensure fields for programmatic executions. $path['source'] = 'taxonomy/term/' . $term->tid; - $path['language'] = LANGUAGE_NONE; + // Core does not provide a way to store the term language but contrib + // modules can do it so we need to take this into account. + $langcode = entity_language('taxonomy_term', $term); + $path['language'] = !empty($langcode) ? $langcode : LANGUAGE_NONE; path_save($path); } } @@ -295,7 +297,10 @@ function path_taxonomy_term_update($term) { if (!empty($path['alias'])) { // Ensure fields for programmatic executions. $path['source'] = 'taxonomy/term/' . $term->tid; - $path['language'] = LANGUAGE_NONE; + // Core does not provide a way to store the term language but contrib + // modules can do it so we need to take this into account. + $langcode = entity_language('taxonomy_term', $term); + $path['language'] = !empty($langcode) ? $langcode : LANGUAGE_NONE; path_save($path); } } diff --git a/modules/taxonomy/taxonomy.admin.inc b/modules/taxonomy/taxonomy.admin.inc index a236cfe..828fde0 100644 --- a/modules/taxonomy/taxonomy.admin.inc +++ b/modules/taxonomy/taxonomy.admin.inc @@ -696,7 +696,8 @@ function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary = '#value' => isset($term->vocabulary_machine_name) ? $term->vocabulary_machine_name : $vocabulary->name, ); - field_attach_form('taxonomy_term', $term, $form, $form_state); + $langcode = entity_language('taxonomy_term', $term); + field_attach_form('taxonomy_term', $term, $form, $form_state, $langcode); $form['relations'] = array( '#type' => 'fieldset', diff --git a/modules/translation/translation.module b/modules/translation/translation.module index e61be1d..3312357 100644 --- a/modules/translation/translation.module +++ b/modules/translation/translation.module @@ -84,7 +84,7 @@ function translation_menu() { * @see translation_menu() */ function _translation_tab_access($node) { - if ($node->language != LANGUAGE_NONE && translation_supported_type($node->type) && node_access('view', $node)) { + if (entity_language('node', $node) != LANGUAGE_NONE && translation_supported_type($node->type) && node_access('view', $node)) { return user_access('translate content'); } return FALSE; @@ -233,7 +233,7 @@ function translation_node_view($node, $view_mode) { foreach ($translations as $langcode => $translation) { // Do not show links to the same node, to unpublished translations or to // translations in disabled languages. - if ($translation->status && isset($languages[$langcode]) && $langcode != $node->language) { + if ($translation->status && isset($languages[$langcode]) && $langcode != entity_language('node', $node)) { $language = $languages[$langcode]; $key = "translation_$langcode"; @@ -313,7 +313,7 @@ function translation_node_prepare($node) { // Add field translations and let other modules module add custom translated // fields. - field_attach_prepare_translation('node', $node, $node->language, $source_node, $source_node->language); + field_attach_prepare_translation('node', $node, $langcode, $source_node, $source_node->language); } } @@ -358,7 +358,8 @@ function translation_node_insert($node) { function translation_node_update($node) { // Only act if we are dealing with a content type supporting translations. if (translation_supported_type($node->type)) { - if (isset($node->translation) && $node->translation && !empty($node->language) && $node->tnid) { + $langcode = entity_language('node', $node); + if (isset($node->translation) && $node->translation && !empty($langcode) && $node->tnid) { // Update translation information. db_update('node') ->fields(array( @@ -389,7 +390,8 @@ function translation_node_validate($node, $form) { if (translation_supported_type($node->type) && (!empty($node->tnid) || !empty($form['#node']->translation_source->nid))) { $tnid = !empty($node->tnid) ? $node->tnid : $form['#node']->translation_source->nid; $translations = translation_node_get_translations($tnid); - if (isset($translations[$node->language]) && $translations[$node->language]->nid != $node->nid ) { + $langcode = entity_language('node', $node); + if (isset($translations[$langcode]) && $translations[$langcode]->nid != $node->nid ) { form_set_error('language', t('There is already a translation in this language.')); } } @@ -469,7 +471,8 @@ function translation_node_get_translations($tnid) { ->execute(); foreach ($result as $node) { - $translations[$tnid][$node->language] = $node; + $langcode = entity_language('node', $node); + $translations[$tnid][$langcode] = $node; } } return $translations[$tnid]; @@ -523,10 +526,11 @@ function translation_language_switch_links_alter(array &$links, $type, $path) { // have translations it might be a language neutral node, in which case we // must leave the language switch links unaltered. This is true also for // nodes not having translation support enabled. - if (empty($node) || $node->language == LANGUAGE_NONE || !translation_supported_type($node->type)) { + if (empty($node) || entity_language('node', $node) == LANGUAGE_NONE || !translation_supported_type($node->type)) { return; } - $translations = array($node->language => $node); + $langcode = entity_language('node', $node); + $translations = array($langcode => $node); } else { $translations = translation_node_get_translations($node->tnid); diff --git a/modules/translation/translation.pages.inc b/modules/translation/translation.pages.inc index fa4070b..110fea6 100644 --- a/modules/translation/translation.pages.inc +++ b/modules/translation/translation.pages.inc @@ -27,7 +27,7 @@ function translation_node_overview($node) { else { // We have no translation source nid, this could be a new set, emulate that. $tnid = $node->nid; - $translations = array($node->language => $node); + $translations = array(entity_language('node', $node) => $node); } $type = variable_get('translation_language_type', LANGUAGE_TYPE_INTERFACE); diff --git a/modules/translation/translation.test b/modules/translation/translation.test index 09bc9e3..e64f9cb 100644 --- a/modules/translation/translation.test +++ b/modules/translation/translation.test @@ -429,7 +429,7 @@ class TranslationTestCase extends DrupalWebTestCase { $result = TRUE; $languages = language_list(); - $page_language = $languages[$node->language]; + $page_language = $languages[entity_language('node', $node)]; $translation_language = $languages[$translation->language]; $url = url("node/$translation->nid", array('language' => $translation_language)); diff --git a/modules/user/user.module b/modules/user/user.module index fb12d70..77420f5 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -161,6 +161,9 @@ function user_entity_info() { 'fieldable' => TRUE, 'entity keys' => array( 'id' => 'uid', + // $user->language is only the preferred user language for the user + // interface textual elements. As it is not necessarily related to the + // language fields have we do not define it as the entity langauge key. ), 'bundles' => array( 'user' => array( @@ -3707,7 +3710,8 @@ function user_register_form($form, &$form_state) { // Attach field widgets, and hide the ones where the 'user_register_form' // setting is not on. - field_attach_form('user', $form['#user'], $form, $form_state); + $langcode = entity_language('user', $form['#user']); + field_attach_form('user', $form['#user'], $form, $form_state, $langcode); foreach (field_info_instances('user', 'user') as $field_name => $instance) { if (empty($instance['settings']['user_register_form'])) { $form[$field_name]['#access'] = FALSE; diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc index 25f4528..dc696d7 100644 --- a/modules/user/user.pages.inc +++ b/modules/user/user.pages.inc @@ -262,7 +262,8 @@ function user_profile_form($form, &$form_state, $account, $category = 'account') if ($category == 'account') { user_account_form($form, $form_state); // Attach field widgets. - field_attach_form('user', $account, $form, $form_state); + $langcode = entity_language('user', $account); + field_attach_form('user', $account, $form, $form_state, $langcode); } $form['actions'] = array('#type' => 'actions');