Commit c77cee1c authored by webchick's avatar webchick

Issue #1810386 by plach, YesCT, Bojhan, bforchhammer, Gábor Hojtsy, falcon03:...

Issue #1810386 by plach, YesCT, Bojhan, bforchhammer, Gábor Hojtsy, falcon03: Create workflow to setup multilingual for entity types, bundles and fields.
parent 22883148
......@@ -44,6 +44,8 @@
* multiple entity forms when the forms are similar. Defaults to
* Drupal\Core\Entity\EntityFormController.
* - label: The human-readable name of the type.
* - bundle_label: The human-readable name of the entity bundles, e.g.
* Vocabulary.
* - label_callback: (optional) A function taking an entity and optional
* langcode argument, and returning the label of the entity. If langcode is
* omitted, the entity's default language is used.
......
......@@ -18,6 +18,7 @@
* @Plugin(
* id = "comment",
* label = @Translation("Comment"),
* bundle_label = @Translation("Content type"),
* module = "comment",
* controller_class = "Drupal\comment\CommentStorageController",
* render_controller_class = "Drupal\comment\CommentRenderController",
......
/**
* @file
* Styles for the content language administration page.
*/
#language-content-settings-form table .bundle {
width: 25%;
}
#language-content-settings-form table td.bundle {
font-weight: bold;
}
#language-content-settings-form table .operations {
width: 75%;
}
......@@ -1027,3 +1027,171 @@ function language_negotiation_configure_browser_delete_form_submit($form, &$form
}
$form_state['redirect'] = 'admin/config/regional/language/detection/browser';
}
/**
* Returns the content language settings form.
*/
function language_content_settings_page() {
return drupal_get_form('language_content_settings_form', language_entity_supported());
}
/**
* Form constructor for the content language settings form.
*
* @param array $supported
* Entity types with language support.
*
* @see language_content_settings_form_submit()
*
* @ingroup forms
*/
function language_content_settings_form(array $form, array $form_state, array $supported) {
$entity_info = entity_get_info();
$labels = array();
$default = array();
foreach ($supported as $entity_type) {
$labels[$entity_type] = isset($entity_info[$entity_type]['label']) ? $entity_info[$entity_type]['label'] : $entity_type;
$default[$entity_type] = FALSE;
// Check whether we have any custom setting.
foreach (entity_get_bundles($entity_type) as $bundle) {
$conf = language_get_default_configuration($entity_type, $bundle);
if (empty($conf['language_hidden']) || $conf['langcode'] != 'site_default') {
$default[$entity_type] = $entity_type;
}
$language_configuration[$entity_type][$bundle] = $conf;
}
}
$path = drupal_get_path('module', 'language');
$form = array(
'#labels' => $labels,
'#attached' => array(
'css' => array($path . '/language.admin.css'),
),
);
$form['entity_types'] = array(
'#title' => t('Custom language settings'),
'#type' => 'checkboxes',
'#options' => $labels,
'#default_value' => $default,
);
$form['settings'] = array('#tree' => TRUE);
foreach ($supported as $entity_type) {
$info = $entity_info[$entity_type];
$form['settings'][$entity_type] = array(
'#title' => $labels[$entity_type],
'#type' => 'container',
'#theme' => 'language_content_settings_table',
'#bundle_label' => isset($info['bundle_label']) ? $info['bundle_label'] : $labels[$entity_type],
'#states' => array(
'visible' => array(
':input[name="entity_types[' . $entity_type . ']"]' => array('checked' => TRUE),
),
),
);
foreach (entity_get_bundles($entity_type) as $bundle) {
$form['settings'][$entity_type][$bundle]['settings'] = array(
'#type' => 'item',
'#label' => isset($info['bundles'][$bundle]) ? $info['bundles'][$bundle]['label'] : $labels[$entity_type],
'language' => array(
'#type' => 'language_configuration',
'#entity_information' => array(
'entity_type' => $entity_type,
'bundle' => $bundle,
),
'#default_value' => $language_configuration[$entity_type][$bundle],
),
);
}
}
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
/**
* Implements hook_preprocess_HOOK() for theme_language_content_settings_table().
*/
function template_preprocess_language_content_settings_table(&$variables) {
// Add a render element representing the bundle language settings table.
$element = $variables['element'];
$header = array(
array(
'data' => $element['#bundle_label'],
'class' => array('bundle'),
),
array(
'data' => t('Configuration'),
'class' => array('operations'),
),
);
$rows = array();
foreach (element_children($element) as $bundle) {
$rows[$bundle] = array(
'data' => array(
array(
'data' => array(
'#prefix' => '<label>',
'#suffix' => '</label>',
'#markup' => check_plain($element[$bundle]['settings']['#label']),
),
'class' => array('bundle'),
),
array(
'data' => $element[$bundle]['settings'],
'class' => array('operations'),
),
),
'class' => array('bundle-settings'),
);
}
$variables['build'] = array(
'#title' => $element['#title'],
'#header' => $header,
'#rows' => $rows,
'#theme' => 'table',
);
}
/**
* Returns HTML for an administration settings table.
*
* @param array $variables
* An associative array containing:
* - build: A render element representing a table of bundle content language
* settings for a particular entity type.
*
* @ingroup themable
*/
function theme_language_content_settings_table($variables) {
return '<h4>' . $variables['build']['#title'] . '</h4>' . drupal_render($variables['build']);
}
/**
* Form submission handler for language_content_settings_form().
*/
function language_content_settings_form_submit(array $form, array &$form_state) {
$entity_types = $form_state['values']['entity_types'];
$settings = &$form_state['values']['settings'];
foreach ($settings as $entity_type => $entity_settings) {
foreach ($entity_settings as $bundle => $bundle_settings) {
language_save_default_configuration($entity_type, $bundle, $bundle_settings['settings']['language']);
}
}
drupal_set_message(t('Settings successfully updated.'));
}
......@@ -52,6 +52,9 @@ function language_help($path, $arg) {
return '<p>' . t('With multiple languages enabled, registered users can select their preferred language and authors can assign a specific language to content.') . '</p>';
}
break;
case 'admin/config/regional/content-language':
return t('Change language settings for <em>content types</em>, <em>taxonomy vocabularies</em>, <em>user profiles</em>, or any other supported element on your site. By default, language settings hide the language selector and the language is the site\'s default language.');
}
}
......@@ -150,6 +153,15 @@ function language_menu() {
'type' => MENU_VISIBLE_IN_BREADCRUMB,
);
// Content language settings.
$items['admin/config/regional/content-language'] = array(
'title' => 'Content language settings',
'description' => 'Configure content language support for any multilingual element.',
'page callback' => 'language_content_settings_page',
'access arguments' => array('administer languages'),
'file' => 'language.admin.inc',
);
return $items;
}
......@@ -187,9 +199,33 @@ function language_theme() {
'render element' => 'form',
'file' => 'language.admin.inc',
),
'language_content_settings_table' => array(
'render element' => 'element',
'file' => 'language.admin.inc',
),
);
}
/**
* Returns a list of supported entity types.
*
* @return array
* An array of entity type names.
*/
function language_entity_supported() {
$supported = array();
foreach (entity_get_info() as $entity_type => $info) {
// @todo Revisit this once all core entities are migrated to the Entity
// Field API and language support for configuration entities has been
// sorted out.
$entity_class = new ReflectionClass($info['class']);
if ($info['fieldable'] && !$entity_class->implementsInterface('Drupal\Core\Config\Entity\ConfigEntityInterface')) {
$supported[$entity_type] = $entity_type;
}
}
return $supported;
}
/**
* Implements hook_element_info_alter().
*/
......
......@@ -18,6 +18,7 @@
* @Plugin(
* id = "node",
* label = @Translation("Content"),
* bundle_label = @Translation("Content type"),
* module = "node",
* controller_class = "Drupal\node\NodeStorageController",
* render_controller_class = "Drupal\node\NodeRenderController",
......
......@@ -18,6 +18,7 @@
* @Plugin(
* id = "taxonomy_term",
* label = @Translation("Taxonomy term"),
* bundle_label = @Translation("Vocabulary"),
* module = "taxonomy",
* controller_class = "Drupal\taxonomy\TermStorageController",
* render_controller_class = "Drupal\taxonomy\TermRenderController",
......
<?php
/**
* @file
* Contains Drupal\translation_entity\Tests\EntityTranslationSettingsTest.
*/
namespace Drupal\translation_entity\Tests;
use Drupal\simpletest\WebTestBase;
/**
* Tests the Entity Test Translation UI.
*/
class EntityTranslationSettingsTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('language', 'translation_entity', 'comment');
public static function getInfo() {
return array(
'name' => 'Entity Translation settings',
'description' => 'Tests the entity translation settings UI.',
'group' => 'Entity Translation UI',
);
}
function setUp() {
parent::setUp();
// Set up two content types to test field instances shared between different
// bundles.
$this->drupalCreateContentType(array('type' => 'article'));
$this->drupalCreateContentType(array('type' => 'page'));
$admin_user = $this->drupalCreateUser(array('administer languages', 'administer entity translation'));
$this->drupalLogin($admin_user);
}
/**
* Tests that the settings UI works as expected.
*/
function testSettingsUI() {
// Test that the translation settings are ignored if the bundle is marked
// translatable but the entity type is not.
$edit = array('settings[comment][comment_node_article][translatable]' => TRUE);
$this->assertSettings('comment', NULL, FALSE, $edit);
// Test that the translation settings are ignored if only a field is marked
// as translatable and not the related entity type and bundle.
$edit = array('settings[comment][comment_node_article][fields][comment_body]' => TRUE);
$this->assertSettings('comment', NULL, FALSE, $edit);
// Test that the translation settings are not stored if an entity type and
// bundle are marked as translatable but no field is.
$edit = array(
'entity_types[comment]' => TRUE,
'settings[comment][comment_node_article][translatable]' => TRUE,
);
$this->assertSettings('comment', 'comment_node_article', FALSE, $edit);
$xpath_err = '//div[@id="messages"]//div[contains(@class, "error")]';
$this->assertTrue($this->xpath($xpath_err), 'Enabling translation only for entity bundles generates a form error.');
// Test that the translation settings are not stored if a non-configurable
// language is set as default and the language selector is hidden.
$edit = array(
'entity_types[comment]' => TRUE,
'settings[comment][comment_node_article][settings][language][langcode]' => LANGUAGE_NOT_SPECIFIED,
'settings[comment][comment_node_article][settings][language][language_hidden]' => TRUE,
'settings[comment][comment_node_article][translatable]' => TRUE,
'settings[comment][comment_node_article][fields][comment_body]' => TRUE,
);
$this->assertSettings('comment', 'comment_node_article', FALSE, $edit);
$this->assertTrue($this->xpath($xpath_err), 'Enabling translation with a fixed non-configurable language generates a form error.');
// Test that a field shared among different bundles can be enabled without
// needing to make all the related bundles translatable.
$edit = array(
'entity_types[comment]' => TRUE,
'settings[comment][comment_node_article][settings][language][langcode]' => 'current_interface',
'settings[comment][comment_node_article][settings][language][language_hidden]' => FALSE,
'settings[comment][comment_node_article][translatable]' => TRUE,
'settings[comment][comment_node_article][fields][comment_body]' => TRUE,
);
$this->assertSettings('comment', 'comment_node_article', TRUE, $edit);
$field = field_info_field('comment_body');
$this->assertTrue($field['translatable'], 'Comment body is translatable.');
// Test that language settings are correctly stored.
$language_configuration = language_get_default_configuration('comment', 'comment_node_article');
$this->assertEqual($language_configuration['langcode'], 'current_interface', 'The default language for article comments is set to the current interface language.');
$this->assertFalse($language_configuration['language_hidden'], 'The language selector for article comments is shown.');
}
/**
* Asserts that translatability has the expected value for the given bundle.
*
* @param string $entity_type
* The entity type for which to check translatibility.
* @param string $bundle
* The bundle for which to check translatibility.
* @param boolean $enabled
* TRUE if translatibility should be enabled, FALSE otherwise.
* @param array $edit
* An array of values to submit to the entity translation settings page.
*
* @return boolean
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertSettings($entity_type, $bundle, $enabled, $edit) {
$this->drupalPost('admin/config/regional/content-language', $edit, t('Save'));
$args = array('@entity_type' => $entity_type, '@bundle' => $bundle, '@enabled' => $enabled ? 'enabled' : 'disabled');
$message = format_string('Translation for entity @entity_type (@bundle) is @enabled.', $args);
drupal_static_reset();
return $this->assertEqual(translation_entity_enabled($entity_type, $bundle), $enabled, $message);
}
}
/**
* @file
* Styles for the content language administration page.
*/
#language-content-settings-form table .bundle {
width: 24%;
}
#language-content-settings-form table td.bundle {
font-weight: bold;
}
#language-content-settings-form table .field {
width: 24%;
padding-left: 3em;
}
#language-content-settings-form table .field label {
font-weight: normal;
}
#language-content-settings-form table .translatable {
width: 1%;
}
#language-content-settings-form table .operations {
width: 75%;
}
(function ($) {
"use strict";
/**
* Makes field translatability inherit bundle translatability.
*/
Drupal.behaviors.translationEntity = {
attach: function (context) {
// Initially hide all field rows for non translatable bundles.
var $input = $('table .bundle-settings .translatable :input', context);
$input.filter(':not(:checked)').once('translation-entity-admin-hide', function() {
$(this).closest('.bundle-settings').nextUntil('.bundle-settings').hide();
});
// When a bundle is made translatable all of its field instances should
// inherit this setting. Instead when it is made non translatable its field
// instances are hidden, since their translatability no longer matters.
$input.once('translation-entity-admin-bind', function() {
var $bundleTranslatable = $(this).click(function() {
var $bundleSettings = $bundleTranslatable.closest('.bundle-settings');
var $fieldSettings = $bundleSettings.nextUntil('.bundle-settings');
if ($bundleTranslatable.is(':checked')) {
$bundleSettings.find('.operations :input[name$="[language_hidden]"]').attr('checked', false);
$fieldSettings.find('.translatable :input').attr('checked', true);
$fieldSettings.show();
}
else {
$fieldSettings.hide();
}
});
});
}
};
})(jQuery);
......@@ -4,3 +4,4 @@ dependencies[] = language
package = Multilingual
version = VERSION
core = 8.x
configure = admin/config/regional/content-language
......@@ -66,7 +66,8 @@ function translation_entity_install() {
function translation_entity_enable() {
$t_args = array(
'!language_url' => url('admin/config/regional/language'),
'!settings_url' => url('admin/config/regional/content-language'),
);
$message = t('You just added content translation capabilities to your site. To exploit them be sure to <a href="!language_url">enable at least two languages</a> and enable translation for <em>content types</em>, <em>taxonomy vocabularies</em>, <em>accounts</em> and any other element whose content you wish to translate.', $t_args);
$message = t('Content translation has been enabled. To use content translation, <a href="!language_url">enable at least two languages</a> and <a href="!settings_url">enable translation</a> for <em>content types</em>, <em>taxonomy vocabularies</em>, <em>accounts</em>, or any other element you wish to translate.', $t_args);
drupal_set_message($message, 'warning');
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment