Commit 6f9fc447 authored by webchick's avatar webchick

Issue #2037979 by Gábor Hojtsy, Sweetchuck, jhodgdon, dawehner: Fixed 'Current...

Issue #2037979 by Gábor Hojtsy, Sweetchuck, jhodgdon, dawehner: Fixed 'Current user's language' views filter label is named very misleading.
parent 84dfd06d
......@@ -78,6 +78,31 @@ public function getLanguageTypes() {
return array(LanguageInterface::TYPE_INTERFACE, LanguageInterface::TYPE_CONTENT, LanguageInterface::TYPE_URL);
}
/**
* {@inheritdoc}
*/
public function getDefinedLanguageTypesInfo() {
// This needs to have the same return value as
// language_language_type_info(), so that even if the Language module is
// not defined, users of this information, such as the Views module, can
// access names and descriptions of the default language types.
return array(
LanguageInterface::TYPE_INTERFACE => array(
'name' => $this->t('User interface text'),
'description' => $this->t('Order of language detection methods for user interface text. If a translation of user interface text is available in the detected language, it will be displayed.'),
'locked' => TRUE,
),
LanguageInterface::TYPE_CONTENT => array(
'name' => $this->t('Content'),
'description' => $this->t('Order of language detection methods for content. If a version of content is available in the detected language, it will be displayed.'),
'locked' => TRUE,
),
LanguageInterface::TYPE_URL => array(
'locked' => TRUE,
),
);
}
/**
* {@inheritdoc}
*/
......@@ -120,7 +145,9 @@ public function getLanguages($flags = LanguageInterface::STATE_CONFIGURABLE) {
// Add the site's default language if flagged as allowed value.
if ($flags & LanguageInterface::STATE_SITE_DEFAULT) {
$default = isset($default) ? $default : $this->getDefaultLanguage();
// Rename the default language.
// Rename the default language. But we do not want to do this globally,
// if we're acting on a global object, so clone the object first.
$default = clone $default;
$default->name = $this->t("Site's default language (@lang_name)", array('@lang_name' => $default->name));
$filtered_languages['site_default'] = $default;
}
......
......@@ -34,19 +34,32 @@ public function isMultilingual();
* Returns an array of the available language types.
*
* @return array
* An array of language type names.
* An array of language type machine names.
*/
public function getLanguageTypes();
/**
* Returns information about all defined language types.
*
* @return array
* An associative array of language type information arrays keyed by
* language type machine name, in the format of
* hook_language_types_info(). In some implementing classes, this is based
* on information from hook_language_types_info() and
* hook_language_types_info_alter().
*/
public function getDefinedLanguageTypesInfo();
/**
* Returns the current language for the given type.
*
* @param string $type
* (optional) The language type, e.g. the interface or the content language.
* Defaults to \Drupal\Core\Language\LanguageInterface::TYPE_INTERFACE.
* (optional) The language type; e.g., the interface or the content
* language. Defaults to
* \Drupal\Core\Language\LanguageInterface::TYPE_INTERFACE.
*
* @return \Drupal\Core\Language\LanguageInterface
* A language object for the given type.
* The current language object for the given type of language.
*/
public function getCurrentLanguage($type = LanguageInterface::TYPE_INTERFACE);
......
......@@ -228,7 +228,7 @@ display:
content: 'No comments available.'
tokenize: false
plugin_id: text_custom
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
block_1:
provider: views
......@@ -239,7 +239,7 @@ display:
display_options:
block_description: 'Recent comments'
block_category: 'Lists (Views)'
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
allow:
items_per_page: true
......@@ -160,7 +160,7 @@ display:
footer: { }
empty: { }
arguments: { }
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
page_tc:
display_plugin: page
......@@ -169,7 +169,7 @@ display:
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
path: test-title-filter
display_description: ''
......@@ -180,7 +180,7 @@ display:
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
path: test-body-paris
display_description: ''
......@@ -241,7 +241,7 @@ display:
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
path: test-title-paris
display_description: ''
......@@ -301,7 +301,7 @@ display:
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
path: test-body-filter
display_description: ''
......
......@@ -289,20 +289,18 @@ public function query($use_groupby = FALSE) {
$this->ensureMyTable();
$this->addAdditionalFields($fields);
// Filter by langcode, if field translation is enabled.
// If we are grouping by something on this field, we want to group by
// the displayed value, which is translated. So, we need to figure out
// which language should be used to translate the value. See also
// $this->field_langcode().
$field = $field_definition;
if ($field->isTranslatable() && !empty($this->view->display_handler->options['field_langcode_add_to_query'])) {
$column = $this->tableAlias . '.langcode';
// By the same reason as field_language the field might be
// LanguageInterface::LANGCODE_NOT_SPECIFIED in reality so allow it as
// well.
// @see this::field_langcode()
$default_langcode = language_default()->id;
$langcode = str_replace(
array('***CURRENT_LANGUAGE***', '***DEFAULT_LANGUAGE***'),
array($this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT), $default_langcode),
$this->view->display_handler->options['field_langcode']
);
$langcode = $this->view->display_handler->options['field_langcode'];
$substitutions = static::queryLanguageSubstitutions();
if (isset($substitutions[$langcode])) {
$langcode = $substitutions[$langcode];
}
$placeholder = $this->placeholder();
$langcode_fallback_candidates = $this->languageManager->getFallbackCandidates(array('langcode' => $langcode, 'operation' => 'views_query', 'data' => $this));
$this->query->addWhereExpression(0, "$column IN($placeholder) OR $column IS NULL", array($placeholder => $langcode_fallback_candidates));
......@@ -917,12 +915,11 @@ protected function addSelfTokens(&$tokens, $item) {
*/
function field_langcode(EntityInterface $entity) {
if ($this->getFieldDefinition()->isTranslatable()) {
$default_langcode = language_default()->id;
$langcode = str_replace(
array('***CURRENT_LANGUAGE***', '***DEFAULT_LANGUAGE***'),
array($this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->id, $default_langcode),
$this->view->display_handler->options['field_langcode']
);
$langcode = $this->view->display_handler->options['field_langcode'];
$substitutions = static::queryLanguageSubstitutions();
if (isset($substitutions[$langcode])) {
$langcode = $substitutions[$langcode];
}
// Give the Entity Field API a chance to fallback to a different language
// (or LanguageInterface::LANGCODE_NOT_SPECIFIED), in case the field has
......
......@@ -44,21 +44,10 @@ public function setNegotiator(LanguageNegotiatorInterface $negotiator);
* through the user interface.
*
* @return array
* An array of language type names.
* An array of language type machine names.
*/
public function getDefinedLanguageTypes();
/**
* Returns information about all defined language types.
*
* @return array
* An associative array of language type information arrays keyed by type
* names. Based on information from hook_language_types_info().
*
* @see hook_language_types_info()
*/
public function getDefinedLanguageTypesInfo();
/**
* Stores language types configuration.
*
......
......@@ -536,7 +536,7 @@ display:
operator: AND
groups:
1: AND
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
display_plugin: default
display_title: Master
......@@ -558,7 +558,7 @@ display:
description: 'Find and manage content'
menu_name: admin
weight: -10
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
display_plugin: page
display_title: Page
......
......@@ -130,7 +130,7 @@ display:
- views
operator: in
value:
'***CURRENT_LANGUAGE***': '***CURRENT_LANGUAGE***'
'***LANGUAGE_language_content***': '***LANGUAGE_language_content***'
group: 1
exposed: false
expose:
......@@ -240,7 +240,7 @@ display:
relationships: { }
fields: { }
arguments: { }
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
display_plugin: default
display_title: Master
......@@ -249,7 +249,7 @@ display:
page_1:
display_options:
path: node
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
display_plugin: page
display_title: Page
......@@ -286,5 +286,5 @@ display:
view_mode: rss
links: false
provider: views
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
......@@ -184,5 +184,52 @@ public function testLanguages() {
}
}
}
// Override the config for the front page view, so that the language
// filter is set to the site default language instead. This should just
// show the English nodes, no matter what the content language is.
$config = \Drupal::config('views.view.frontpage');
$config->set('display.default.display_options.filters.langcode.value', array('***LANGUAGE_site_default***' => '***LANGUAGE_site_default***'));
$config->save();
foreach ($this->node_titles as $langcode => $titles) {
$this->drupalGet(($langcode == 'en' ? '' : "$langcode/") . 'node');
foreach ($this->node_titles as $control_langcode => $control_titles) {
foreach ($control_titles as $title) {
if ($control_langcode == 'en') {
$this->assertText($title, 'English title is shown when filtering is site default');
}
else {
$this->assertNoText($title, 'Non-English title is not shown when filtering is site default');
}
}
}
}
// Override the config so that the language filter is set to the UI
// language, and make that have a fixed value of 'es'.
//
// IMPORTANT: Make sure this part of the test is last -- it is changing
// language configuration!
$config->set('display.default.display_options.filters.langcode.value', array('***LANGUAGE_language_interface***' => '***LANGUAGE_language_interface***'));
$config->save();
$language_config = \Drupal::config('language.types');
$language_config->set('negotiation.language_interface.enabled', array('language-selected' => 1));
$language_config->save();
$language_config = \Drupal::config('language.negotiation');
$language_config->set('selected_langcode', 'es');
$language_config->save();
// With a fixed language selected, there is no language-based URL.
$this->drupalGet('node');
foreach ($this->node_titles as $control_langcode => $control_titles) {
foreach ($control_titles as $title) {
if ($control_langcode == 'es') {
$this->assertText($title, 'Spanish title is shown when filtering is fixed UI language');
}
else {
$this->assertNoText($title, 'Non-Spanish title is not shown when filtering is fixed UI language');
}
}
}
}
}
......@@ -173,7 +173,7 @@ display:
empty: { }
relationships: { }
arguments: { }
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
page_tf:
display_plugin: page
......@@ -182,7 +182,7 @@ display:
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
path: test-title-filter
display_description: ''
......@@ -255,7 +255,7 @@ display:
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
path: test-body-filter
display_description: ''
......@@ -326,7 +326,7 @@ display:
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
path: test-body-paris
display_description: ''
......@@ -398,7 +398,7 @@ display:
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
path: test-title-paris
display_description: ''
......
......@@ -300,7 +300,7 @@ display:
validate_options: { }
plugin_id: language
provider: views
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
page_1:
display_plugin: page
......@@ -309,6 +309,6 @@ display:
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
path: test-language
......@@ -145,7 +145,7 @@ display:
empty: { }
relationships: { }
arguments: { }
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
page_dc:
display_plugin: page
......@@ -154,7 +154,7 @@ display:
position: 3
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
display_description: ''
path: test-desc-filter
......@@ -215,7 +215,7 @@ display:
position: 3
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
display_description: ''
path: test-desc-paris
......@@ -277,7 +277,7 @@ display:
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
path: test-name-filter
display_description: ''
......@@ -288,7 +288,7 @@ display:
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
path: test-name-paris
display_description: ''
......@@ -348,7 +348,7 @@ display:
position: 3
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
display_description: ''
path: test-field-paris
......@@ -410,7 +410,7 @@ display:
position: 3
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
display_description: ''
path: test-field-filter
......
......@@ -172,5 +172,5 @@ display:
empty: { }
relationships: { }
arguments: { }
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode: '***LANGUAGE_language_content***'
field_langcode_add_to_query: null
......@@ -2,13 +2,14 @@
/**
* @file
* Definition of Drupal\views\Plugin\views\PluginBase.
* Contains \Drupal\views\Plugin\views\PluginBase.
*/
namespace Drupal\views\Plugin\views;
use Drupal\Component\Utility\String;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase as ComponentPluginBase;
use Drupal\Core\Render\Element;
......@@ -43,6 +44,13 @@
*/
abstract class PluginBase extends ComponentPluginBase implements ContainerFactoryPluginInterface {
/**
* Include negotiated languages when listing languages.
*
* @see \Drupal\views\Plugin\views\PluginBase::listLanguages()
*/
const INCLUDE_NEGOTIATED = 16;
/**
* Options for this plugin will be held here.
*
......@@ -439,4 +447,86 @@ public function getDependencies() {
return array();
}
/**
* Makes an array of languages, optionally including special languages.
*
* @param int $flags
* (optional) Flags for which languages to return (additive). Options:
* - \Drupal\Core\Language::STATE_ALL (default): All languages
* (configurable and default).
* - \Drupal\Core\Language::STATE_CONFIGURABLE: Configurable languages.
* - \Drupal\Core\Language::STATE_LOCKED: Locked languages.
* - \Drupal\Core\Language::STATE_SITE_DEFAULT: Add site default language;
* note that this is not included in STATE_ALL.
* - \Drupal\views\Plugin\views\PluginBase::INCLUDE_NEGOTIATED: Add
* negotiated language types.
*
* @return array
* An array of language names, keyed by the language code. Negotiated and
* special languages have special codes that are substituted in queries by
* static::queryLanguageSubstitutions().
*/
protected function listLanguages($flags = LanguageInterface::STATE_ALL) {
$manager = \Drupal::languageManager();
$list = array();
// The Language Manager class takes care of the STATE_SITE_DEFAULT case.
// It comes in with ID set to 'site_default'. Since this is not a real
// language, surround it by '***LANGUAGE_...***', like the negotiated
// languages below.
$languages = $manager->getLanguages($flags);
foreach ($languages as $id => $language) {
if ($id == 'site_default') {
$id = '***LANGUAGE_' . $id . '***';
}
$list[$id] = t($language->name);
}
// Add in negotiated languages, if requested.
if ($flags & PluginBase::INCLUDE_NEGOTIATED) {
$types = $manager->getDefinedLanguageTypesInfo();
foreach ($types as $id => $type) {
// Omit unnamed types. These are things like language_url, which are
// not configurable and do not need to be in this list. And surround
// IDs by '***LANGUAGE_...***', to avoid query collisions.
if (isset($type['name'])) {
$id = '***LANGUAGE_' . $id . '***';
$list[$id] = t('Language selected for !type', array('!type' => $type['name']));
}
}
}
return $list;
}
/**
* Returns substitutions for Views queries for languages.
*
* This is needed so that the language options returned by
* $this->listLanguages() are able to be used in queries. It is called
* by the Views module implementation of hook_views_query_substitutions()
* to get the language-related substitutions.
*
* @return array
* An array in the format of hook_views_query_substitutions() that gives
* the query substitutions needed for the special language types.
*/
public static function queryLanguageSubstitutions() {
$changes = array();
$manager = \Drupal::languageManager();
// Handle default language.
$default = $manager->getDefaultLanguage()->id;
$changes['***LANGUAGE_site_default***'] = $default;
// Handle negotiated languages.
$types = $manager->getDefinedLanguageTypesInfo();
foreach ($types as $id => $type) {
if (isset($type['name'])) {
$changes['***LANGUAGE_' . $id . '***'] = $manager->getCurrentLanguage($id)->id;
}
}
return $changes;
}
}
......@@ -46,7 +46,7 @@ function title() {
* language was not found.
*/
function language($langcode) {
$languages = views_language_list();
$languages = $this->listLanguages();
return isset($languages[$langcode]) ? $languages[$langcode] : t('Unknown language');
}
......
......@@ -579,7 +579,7 @@ protected function defineOptions() {
'bool' => TRUE,
),
'field_langcode' => array(
'default' => '***CURRENT_LANGUAGE***',
'default' => '***LANGUAGE_language_content***',
),
'field_langcode_add_to_query' => array(
'default' => TRUE,
......@@ -1245,19 +1245,13 @@ public function optionsSummary(&$categories, &$options) {
'desc' => t('Allow to set some advanced settings for the query plugin'),
);
$languages = array(
'***CURRENT_LANGUAGE***' => t("Current user's language"),
'***DEFAULT_LANGUAGE***' => t("Default site language"),
LanguageInterface::LANGCODE_NOT_SPECIFIED => t('Language neutral'),
);
if (\Drupal::moduleHandler()->moduleExists('language')) {
$languages = array_merge($languages, language_list());
}
$language_options = $this->listLanguages(LanguageInterface::STATE_ALL | LanguageInterface::STATE_SITE_DEFAULT | PluginBase::INCLUDE_NEGOTIATED);
$options['field_langcode'] = array(
'category' => 'other',
'title' => t('Field Language'),
'value' => $languages[$this->getOption('field_langcode')],
'desc' => t('All fields which support translations will be displayed in the selected language.'),
'value' => $language_options[$this->getOption('field_langcode')],
'desc' => t('All fields that support translations will be displayed in the selected language.'),
);
$access_plugin = $this->getPlugin('access');
......@@ -1619,12 +1613,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
// an entity base table. Also, we make sure that there's at least one
// entity type with a translation handler attached.
if (in_array($this->view->storage->get('base_table'), $translatable_entity_tables)) {
$languages = array(
'***CURRENT_LANGUAGE***' => t("Current user's language"),
'***DEFAULT_LANGUAGE***' => t("Default site language"),
LanguageInterface::LANGCODE_NOT_SPECIFIED => t('Language neutral'),
);
$languages = array_merge($languages, views_language_list());
$languages = $this->listLanguages(LanguageInterface::STATE_ALL | LanguageInterface::STATE_SITE_DEFAULT | PluginBase::INCLUDE_NEGOTIATED);
$form['field_langcode'] = array(
'#type' => 'select',
......
......@@ -7,6 +7,9 @@
namespace Drupal\views\Plugin\views\filter;
use Drupal\Core\Language\LanguageInterface;
use Drupal\views\Plugin\views\PluginBase;
/**
* Provides filtering by language.
*
......@@ -16,16 +19,13 @@
*/
class LanguageFilter extends InOperator {
/**
* {@inheritdoc}
*/
public function getValueOptions() {
if (!isset($this->value_options)) {
$this->value_title = t('Language');
$languages = array(
'***CURRENT_LANGUAGE***' => t("Current user's language"),
'***DEFAULT_LANGUAGE***' => t("Default site language"),
);
$languages = array_merge($languages, views_language_list());
$this->value_options = $languages;
$this->value_options = $this->listLanguages(LanguageInterface::STATE_ALL |LanguageInterface::STATE_SITE_DEFAULT | PluginBase::INCLUDE_NEGOTIATED);
}
}
}
......@@ -559,20 +559,26 @@ function hook_field_views_data_views_data_alter(array &$data, \Drupal\field\Fiel
/**
* Replace special strings in the query before it is executed.
*
* The idea is that certain dynamic values can be placed in a query when it is
* built, and substituted at run-time, allowing the query to be cached and
* still work correctly when executed.
*
* @param \Drupal\views\ViewExecutable $view
* The View being executed.
*
* @return array
* An associative array where each key is a string to be replaced, and the
* corresponding value is its replacement. The strings to replace are often
* surrounded with '***', as illustrated in the example implementation.
* surrounded with '***', as illustrated in the example implementation, to
* avoid collisions with other values in the query.
*/
function hook_views_query_substitutions(ViewExecutable $view) {
// Example from views_views_query_substitutions().
return array(
'***CURRENT_VERSION***' => \Drupal::VERSION,
'***CURRENT_TIME***' => REQUEST_TIME,
'***CURRENT_LANGUAGE***' => \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->id,
'***DEFAULT_LANGUAGE***' => \Drupal::languageManager()->getDefaultLanguage()->id,
'***LANGUAGE_language_content***' => \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->id,
'***LANGUAGE_site_default***' => \Drupal::languageManager()->getDefaultLanguage()->id,
);
}
......
......@@ -13,7 +13,6 @@
use Drupal\Core\Cache\Cache;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\views\Plugin\Derivative\ViewsLocalTask;
......@@ -467,34 +466,6 @@ function views_add_contextual_links(&$render_element, $location, ViewExecutable
}
}
/**
* Prepares a list of language names.
*
* This is a wrapper around \Drupal::languageManager()->getLanguages() to return
* a plain key value array.
*
* @param string $field
* The field of the language object which should be used as the value of the