Skip to content
mobile: '(min-width: 0px)'
narrow: '(min-width: 560px)'
wide: '(min-width: 851px)'
tv: 'only screen and (min-width: 3456px)'
......@@ -155,13 +155,15 @@ function comment_admin_overview($form, &$form_state, $arg) {
$links = array();
$links['edit'] = array(
'title' => t('edit'),
'href' => 'comment/' . $comment->id() . '/edit',
'route_name' => 'comment.edit_page',
'route_parameters' => array('comment' => $comment->id()),
'query' => $destination,
);
if (module_invoke('content_translation', 'translate_access', $comment)) {
$links['translate'] = array(
'title' => t('translate'),
'href' => 'comment/' . $comment->id() . '/translations',
'route_name' => 'content_translation.translation_overview_comment',
'route_parameters' => array('comment' => $comment->id()),
'query' => $destination,
);
}
......
......@@ -1175,9 +1175,7 @@ function comment_load($cid, $reset = FALSE) {
* The number of new comments or FALSE if the user is not logged in.
*/
function comment_num_new($entity_id, $entity_type, $field_name = NULL, $timestamp = 0) {
global $user;
if ($user->isAuthenticated() && \Drupal::moduleHandler()->moduleExists('history')) {
if (\Drupal::currentUser()->isAuthenticated() && \Drupal::moduleHandler()->moduleExists('history')) {
// Retrieve the timestamp at which the current user last viewed this entity.
if (!$timestamp) {
if ($entity_type == 'node') {
......@@ -1733,3 +1731,20 @@ function comment_library_info() {
);
return $libraries;
}
/**
* #post_render_cache callback; replaces the placeholder with the comment form.
*
* @param array $context
* An array with the following keys:
* - entity_type: an entity type
* - entity_id: an entity ID
* - field_name: a comment field name
*
* @return array $element
* The updated $element.
*/
function comment_replace_form_placeholder(array $context) {
$entity = entity_load($context['entity_type'], $context['entity_id']);
return comment_add($entity, $context['field_name']);
}
......@@ -2,9 +2,7 @@
/**
* @file
* Provide views data and handlers for comment.module.
*
* @ingroup views_module_handlers
* Provide views data for comment.module.
*/
/**
......
......@@ -36,7 +36,6 @@
* fieldable = TRUE,
* translatable = TRUE,
* render_cache = FALSE,
* route_base_path = "admin/structure/comments/manage/{bundle}",
* entity_keys = {
* "id" = "cid",
* "bundle" = "field_id",
......@@ -47,8 +46,9 @@
* "bundle" = "field_id"
* },
* links = {
* "canonical" = "/comment/{comment}",
* "edit-form" = "/comment/{comment}/edit"
* "canonical" = "comment.permalink",
* "edit-form" = "comment.edit_page",
* "admin-form" = "comment.bundle"
* }
* )
*/
......@@ -216,10 +216,10 @@ public function id() {
public function preSave(EntityStorageControllerInterface $storage_controller) {
parent::preSave($storage_controller);
global $user;
$user = \Drupal::currentUser();
if (!isset($this->status->value)) {
$this->status->value = user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED;
$this->status->value = $user->hasPermission('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED;
}
if ($this->isNew()) {
// Add the comment to database. This next section builds the thread field.
......
......@@ -138,7 +138,24 @@ public function viewElements(FieldItemListInterface $items) {
if ($status == COMMENT_OPEN && $comment_settings['form_location'] == COMMENT_FORM_BELOW) {
// Only show the add comment form if the user has permission.
if ($this->currentUser->hasPermission('post comments')) {
$output['comment_form'] = comment_add($entity, $field_name);
// All users in the "anonymous" role can use the same form: it is fine
// for this form to be stored in the render cache.
if ($this->currentUser->isAnonymous()) {
$output['comment_form'] = comment_add($entity, $field_name);
}
// All other users need a user-specific form, which would break the
// render cache: hence use a #post_render_cache callback.
else {
$output['comment_form'] = array(
'#type' => 'render_cache_placeholder',
'#callback' => 'comment_replace_form_placeholder',
'#context' => array(
'entity_type' => $entity->entityType(),
'entity_id' => $entity->id(),
'field_name' => $field_name
),
);
}
}
}
......
......@@ -46,6 +46,9 @@ function setUp() {
'node test view',
'skip comment approval',
));
// Set the author of the created node to the web_user uid.
$this->node->setAuthorId($this->web_user->id())->save();
}
/**
......
......@@ -229,11 +229,11 @@ function testCommentFunctionality() {
));
$this->drupalLogin($limited_user);
// Test that default field exists.
$this->drupalGet('admin/structure/entity-test/manage/entity_test/fields');
$this->drupalGet('entity_test/structure/entity_test/fields');
$this->assertText(t('Comment settings'));
$this->assertLinkByHref('admin/structure/entity-test/manage/entity_test/fields/entity_test.entity_test.comment');
$this->assertLinkByHref('entity_test/structure/entity_test/fields/entity_test.entity_test.comment');
// Test widget hidden option is not visible when there's no comments.
$this->drupalGet('admin/structure/entity-test/manage/entity_test/entity-test/fields/entity_test.entity_test.comment');
$this->drupalGet('entity_test/structure/entity_test/entity-test/fields/entity_test.entity_test.comment');
$this->assertNoField('edit-default-value-input-comment-und-0-status-0');
$this->drupalLogin($this->admin_user);
......@@ -343,20 +343,20 @@ function testCommentFunctionality() {
'administer entity_test content',
));
$this->drupalLogin($limited_user);
$this->drupalGet('admin/structure/entity-test/manage/entity_test/fields/entity_test.entity_test.comment');
$this->drupalGet('entity_test/structure/entity_test/fields/entity_test.entity_test.comment');
$this->assertNoFieldChecked('edit-default-value-input-comment-0-status-0');
$this->assertNoFieldChecked('edit-default-value-input-comment-0-status-1');
$this->assertFieldChecked('edit-default-value-input-comment-0-status-2');
// Test comment option change in field settings.
$edit = array('default_value_input[comment][0][status]' => COMMENT_CLOSED);
$this->drupalPostForm(NULL, $edit, t('Save settings'));
$this->drupalGet('admin/structure/entity-test/manage/entity_test/fields/entity_test.entity_test.comment');
$this->drupalGet('entity_test/structure/entity_test/fields/entity_test.entity_test.comment');
$this->assertNoFieldChecked('edit-default-value-input-comment-0-status-0');
$this->assertFieldChecked('edit-default-value-input-comment-0-status-1');
$this->assertNoFieldChecked('edit-default-value-input-comment-0-status-2');
// Add a new comment field.
$this->drupalGet('admin/structure/entity-test/manage/entity_test/fields');
$this->drupalGet('entity_test/structure/entity_test/fields');
$edit = array(
'fields[_add_new_field][label]' => 'Foobar',
'fields[_add_new_field][field_name]' => 'foobar',
......
......@@ -155,7 +155,7 @@ protected function assertPublishedStatus() {
* Tests translate link on comment content admin page.
*/
function testTranslateLinkCommentAdminPage() {
$this->admin_user = $this->drupalCreateUser(array_merge(parent::getTranslatorPermissions(), array('access administration pages', 'administer comments')));
$this->admin_user = $this->drupalCreateUser(array_merge(parent::getTranslatorPermissions(), array('access administration pages', 'administer comments', 'skip comment approval')));
$this->drupalLogin($this->admin_user);
$cid_translatable = $this->createEntity(array(), $this->langcodes[0]);
......
......@@ -36,7 +36,7 @@ function setUp() {
// Add two users, create a node with the user1 as author and another node
// with user2 as author. For the second node add a comment from user1.
$this->account = $this->drupalCreateUser();
$this->account = $this->drupalCreateUser(array('skip comment approval'));
$this->account2 = $this->drupalCreateUser();
$this->drupalLogin($this->account);
......
......@@ -76,7 +76,7 @@ function testList() {
$actual_operations = $controller->getOperations($entity);
// Sort the operations to normalize link order.
uasort($actual_operations, 'drupal_sort_weight');
$this->assertIdentical($expected_operations, $actual_operations);
$this->assertIdentical($expected_operations, $actual_operations, 'The operations are identical.');
// Test buildHeader() method.
$expected_items = array(
......@@ -149,7 +149,7 @@ function testList() {
$actual_operations = $controller->getOperations($entity);
// Sort the operations to normalize link order.
uasort($actual_operations, 'drupal_sort_weight');
$this->assertIdentical($expected_operations, $actual_operations);
$this->assertIdentical($expected_operations, $actual_operations, 'The operations are identical.');
}
/**
......
......@@ -28,9 +28,6 @@
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid"
* },
* links = {
* "edit-form" = "admin/structure/config_test/manage/{config_query_test}"
* }
* )
*
......
......@@ -35,7 +35,7 @@
* "status" = "status"
* },
* links = {
* "edit-form" = "admin/structure/config_test/manage/{config_test}"
* "edit-form" = "config_test.entity"
* }
* )
*/
......
<?php
/**
* @file
* Hooks provided by the Configuration Translation module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Introduce dynamic translation tabs for translation of configuration.
*
* This hook augments MODULE.config_translation.yml as well as
* THEME.config_translation.yml files to collect dynamic translation mapper
* information. If your information is static, just provide such a YAML file
* with your module containing the mapping.
*
* Note that while themes can provide THEME.config_translation.yml files this
* hook is not invoked for themes.
*
* @param array $info
* An associative array of configuration mapper information. Use an entity
* name for the key (for entity mapping) or a unique string for configuration
* name list mapping. The values of the associative array are arrays
* themselves in the same structure as the *.configuration_translation.yml
* files.
*
* @see hook_config_translation_info_alter()
* @see \Drupal\config_translation\ConfigMapperManagerInterface
* @see \Drupal\config_translation\Routing\RouteSubscriber::routes()
*/
function hook_config_translation_info(&$info) {
$entity_manager = \Drupal::entityManager();
$route_provider = \Drupal::service('router.route_provider');
// If field UI is not enabled, the base routes of the type
// "field_ui.instance_edit_$entity_type" are not defined.
if (\Drupal::moduleHandler()->moduleExists('field_ui')) {
// Add fields entity mappers to all fieldable entity types defined.
foreach ($entity_manager->getDefinitions() as $entity_type => $entity_info) {
$base_route = NULL;
try {
$base_route = $route_provider->getRouteByName('field_ui.instance_edit_' . $entity_type);
}
catch (RouteNotFoundException $e) {
// Ignore non-existent routes.
}
// Make sure entity type is fieldable and has a base route.
if ($entity_info['fieldable'] && !empty($base_route)) {
$info[$entity_type . '_fields'] = array(
'base_route_name' => 'field_ui.instance_edit_' . $entity_type,
'entity_type' => 'field_instance',
'title' => t('!label field'),
'class' => '\Drupal\config_translation\ConfigFieldInstanceMapper',
'base_entity_type' => $entity_type,
'list_controller' => '\Drupal\config_translation\Controller\ConfigTranslationFieldInstanceListController',
'weight' => 10,
);
}
}
}
}
/**
* Alter existing translation tabs for translation of configuration.
*
* This hook is useful to extend existing configuration mappers with new
* configuration names, for example when altering existing forms with new
* settings stored elsewhere. This allows the translation experience to also
* reflect the compound form element in one screen.
*
* @param array $info
* An associative array of discovered configuration mappers. Use an entity
* name for the key (for entity mapping) or a unique string for configuration
* name list mapping. The values of the associative array are arrays
* themselves in the same structure as the *.configuration_translation.yml
* files.
*
* @see hook_translation_info()
* @see \Drupal\config_translation\ConfigMapperManagerInterface
*/
function hook_config_translation_info_alter(&$info) {
// Add additional site settings to the site information screen, so it shows
// up on the translation screen. (Form alter in the elements whose values are
// stored in this config file using regular form altering on the original
// configuration form.)
$info['system.site_information_settings']['names'][] = 'example.site.setting';
}
/**
* Alter config typed data definitions.
*
* Used to automatically generate translation forms, you can alter the typed
* data types representing each configuration schema type to change default
* labels or form element renderers.
*
* @param $definitions
* Associative array of configuration type definitions keyed by schema type
* names. The elements are themselves array with information about the type.
*/
function hook_config_translation_type_info_alter(&$definitions) {
// Enhance the text and date type definitions with classes to generate proper
// form elements in ConfigTranslationFormBase. Other translatable types will
// appear as a one line textfield.
$definitions['text']['form_element_class'] = '\Drupal\config_translation\FormElement\Textarea';
$definitions['date_format']['form_element_class'] = '\Drupal\config_translation\FormElement\DateFormat';
}
/**
* @} End of "addtogroup hooks".
*/
config_translation.contextual_links:
title: 'Translate @type_name'
derivative: 'Drupal\config_translation\Plugin\Derivative\ConfigTranslationContextualLinks'
weight: 100
name: 'Configuration Translation'
type: module
description: 'Provides a translation interface for configuration.'
package: Multilingual
version: VERSION
core: 8.x
dependencies:
- locale
config_translation.local_tasks:
title: 'Translate @type_name'
derivative: 'Drupal\config_translation\Plugin\Derivative\ConfigTranslationLocalTasks'
weight: 100
<?php
/**
* @file
* Configuration Translation module.
*/
use Drupal\config_translation\Plugin\Derivative\ConfigTranslationLocalTasks;
use Drupal\Core\Entity\EntityInterface;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
/**
* Implements hook_help().
*/
function config_translation_help($path) {
switch ($path) {
case 'admin/help#config_translation':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Configuration Translation module allows configurations to be translated into different languages. Views, your site name, contact module categories, vocabularies, menus, blocks, and so on are all stored within the unified configuration system and can be translated with this module. Content, such as nodes, taxonomy terms, custom blocks, and so on are translatable with the Content Translation module in Drupal core, while the built-in user interface (such as registration forms, content submission and administration interfaces) are translated with the Interface Translation module. Use these three modules effectively together to translate your whole site to different languages.') . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Translating') . '</dt>';
$output .= '<dd>' . t('To translate configuration items, select the translate tab when viewing the configuration, select the language for which you wish to provide translations and then enter the content.') . '</dd>';
$output .= '</dl>';
return $output;
case 'admin/config/regional/config-translation':
$output = '<p>' . t('This page lists all configuration items on your site which have translatable text, like your site name, role names, etc.') . '</p>';
return $output;
}
}
/**
* Implements hook_menu().
*/
function config_translation_menu() {
$items = array();
$items['admin/config/regional/config-translation'] = array(
'title' => 'Configuration translation',
'description' => 'Translate the configuration.',
'route_name' => 'config_translation.mapper_list',
'weight' => 30,
);
return $items;
}
/**
* Implements hook_permission().
*/
function config_translation_permission() {
return array(
'translate configuration' => array(
'title' => t('Translate user edited configuration'),
'description' => t('Translate any configuration not shipped with modules and themes.'),
),
);
}
/**
* Implements hook_theme().
*/
function config_translation_theme() {
return array(
'config_translation_manage_form_element' => array(
'render element' => 'element',
'template' => 'config_translation_manage_form_element',
),
);
}
/**
* Implements hook_config_translation_info().
*/
function config_translation_config_translation_info(&$info) {
$entity_manager = \Drupal::entityManager();
$route_provider = \Drupal::service('router.route_provider');
// If field UI is not enabled, the base routes of the type
// "field_ui.instance_edit_$entity_type" are not defined.
if (\Drupal::moduleHandler()->moduleExists('field_ui')) {
// Add fields entity mappers to all fieldable entity types defined.
foreach ($entity_manager->getDefinitions() as $entity_type => $entity_info) {
$base_route = NULL;
try {
$base_route = $route_provider->getRouteByName('field_ui.instance_edit_' . $entity_type);
}
catch (RouteNotFoundException $e) {
// Ignore non-existent routes.
}
// Make sure entity type is fieldable and has a base route.
if ($entity_info['fieldable'] && !empty($base_route)) {
$info[$entity_type . '_fields'] = array(
'base_route_name' => 'field_ui.instance_edit_' . $entity_type,
'entity_type' => 'field_instance',
'title' => '!label field',
'class' => '\Drupal\config_translation\ConfigFieldInstanceMapper',
'base_entity_type' => $entity_type,
'list_controller' => '\Drupal\config_translation\Controller\ConfigTranslationFieldInstanceListController',
'weight' => 10,
);
}
}
}
// Discover configuration entities automatically.
foreach ($entity_manager->getDefinitions() as $entity_type => $entity_info) {
// Determine base path for entities automatically if provided via the
// configuration entity.
if (
!in_array('Drupal\Core\Config\Entity\ConfigEntityInterface', class_implements($entity_info['class'])) ||
!isset($entity_info['links']['edit-form'])
) {
// Do not record this entity mapper if the entity type does not
// provide a base route. We'll surely not be able to do anything with
// it anyway. Configuration entities with a dynamic base path, such as
// field instances, need special treatment. See above.
continue;
}
// Use the entity type as the plugin ID.
$info[$entity_type] = array(
'class' => '\Drupal\config_translation\ConfigEntityMapper',
'base_route_name' => $entity_info['links']['edit-form'],
'title' => '!label !entity_type',
'names' => array(),
'entity_type' => $entity_type,
'weight' => 10,
);
if ($entity_type == 'block') {
// Blocks placements need a specific list controller.
$info['block']['list_controller'] = '\Drupal\config_translation\Controller\ConfigTranslationBlockListController';
}
}
}
/**
* Implements hook_entity_operation_alter().
*/
function config_translation_entity_operation_alter(array &$operations, EntityInterface $entity) {
if (\Drupal::currentUser()->hasPermission('translate configuration')) {
$uri = $entity->uri();
$operations['translate'] = array(
'title' => t('Translate'),
'href' => $uri['path'] . '/translate',
'options' => $uri['options'],
'weight' => 50,
);
}
}
/**
* Implements hook_config_translation_type_info_alter().
*/
function config_translation_config_translation_type_info_alter(&$definitions) {
// Enhance the text and date type definitions with classes to generate proper
// form elements in ConfigTranslationFormBase. Other translatable types will
// appear as a one line textfield.
$definitions['text']['form_element_class'] = '\Drupal\config_translation\FormElement\Textarea';
$definitions['date_format']['form_element_class'] = '\Drupal\config_translation\FormElement\DateFormat';
}
/**
* Implements hook_library_info().
*/
function config_translation_library_info() {
$libraries['drupal.config_translation.admin'] = array(
'title' => 'Configuration translation admin',
'version' => \Drupal::VERSION,
'css' => array(
drupal_get_path('module', 'config_translation') . '/css/config_translation.admin.css' => array(),
),
);
return $libraries;
}
/**
* Implements hook_local_tasks_alter().
*/
function config_translation_local_tasks_alter(&$local_tasks) {
// Alters in tab_root_ids onto the config translation local tasks.
$derivative = ConfigTranslationLocalTasks::create(\Drupal::getContainer(), 'config_translation.local_tasks');
$derivative->alterLocalTasks($local_tasks);
}
config_translation.mapper_list:
path: '/admin/config/regional/config-translation'
defaults:
_title: 'Configuration translation'
_content: '\Drupal\config_translation\Controller\ConfigTranslationMapperList::render'
requirements:
_permission: 'translate configuration'
config_translation.entity_list:
path: '/admin/config/regional/config-translation/{config_translation_mapper}'
defaults:
_content: '\Drupal\config_translation\Controller\ConfigTranslationListController::listing'
requirements:
_permission: 'translate configuration'
services:
config_translation.route_subscriber:
class: Drupal\config_translation\Routing\RouteSubscriber
arguments: ['@plugin.manager.config_translation.mapper']
tags:
- { name: event_subscriber }
config_translation.access.overview:
class: Drupal\config_translation\Access\ConfigTranslationOverviewAccess
arguments: ['@plugin.manager.config_translation.mapper']
tags:
- { name: access_check }
config_translation.access.form:
class: Drupal\config_translation\Access\ConfigTranslationFormAccess
arguments: ['@plugin.manager.config_translation.mapper']
tags:
- { name: access_check }
plugin.manager.config_translation.mapper:
class: Drupal\config_translation\ConfigMapperManager
arguments:
- '@cache.cache'
- '@language_manager'
- '@module_handler'
- '@config.typed'