Commit 04d69926 authored by larowlan's avatar larowlan

Issue #1986606 by jibran, pcambra, vijaycs85, dawehner, andypost, mbovan,...

Issue #1986606 by jibran, pcambra, vijaycs85, dawehner, andypost, mbovan, Arla, adnen, vprocessor, tkuldeep17, xjm, kim.pepper, dobe, snig, larowlan, miro_dietiker, tim.plunkett, Berdir, Lendude, plach, olli, damiankloip: Convert the comments administration screen to a view
parent bcc7d038
......@@ -2,6 +2,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemInterface;
......@@ -116,8 +117,8 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
$url = NULL;
if ($this->getSetting('link_to_entity')) {
// For the default revision this falls back to 'canonical'
$url = $items->getEntity()->urlInfo('revision');
// For the default revision this falls back to 'canonical'.
$url = $this->getEntityUrl($items->getEntity());
}
foreach ($items as $delta => $item) {
......@@ -155,4 +156,18 @@ protected function viewValue(FieldItemInterface $item) {
];
}
/**
* Gets the URI elements of the entity.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity object.
*
* @return \Drupal\Core\Url
* The URI elements of the entity.
*/
protected function getEntityUrl(EntityInterface $entity) {
// For the default revision this falls back to 'canonical'.
return $entity->toUrl('revision');
}
}
<?php
/**
* @file
* Post update functions for the comment module.
*/
use Drupal\Core\Config\FileStorage;
use Drupal\Core\Config\InstallStorage;
/**
* Enable the comment admin view.
*/
function comment_post_update_enable_comment_admin_view() {
$module_handler = \Drupal::moduleHandler();
$entity_type_manager = \Drupal::entityTypeManager();
// Save the comment delete action to config.
$config_install_path = $module_handler->getModule('comment')->getPath() . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY;
$storage = new FileStorage($config_install_path);
$entity_type_manager
->getStorage('action')
->create($storage->read('system.action.comment_delete_action'))
->save();
// Only create if the views module is enabled.
if (!$module_handler->moduleExists('views')) {
return;
}
// Save the comment admin view to config.
$optional_install_path = $module_handler->getModule('comment')->getPath() . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
$storage = new FileStorage($optional_install_path);
$entity_type_manager
->getStorage('view')
->create($storage->read('views.view.comment'))
->save();
}
......@@ -2,7 +2,7 @@ comment.admin:
path: '/admin/content/comment'
defaults:
_title: 'Comments'
_controller: '\Drupal\comment\Controller\AdminController::adminPage'
_form: '\Drupal\comment\Form\CommentAdminOverview'
type: 'new'
requirements:
_permission: 'administer comments'
......@@ -11,7 +11,7 @@ comment.admin_approval:
path: '/admin/content/comment/approval'
defaults:
_title: 'Unapproved comments'
_controller: '\Drupal\comment\Controller\AdminController::adminPage'
_form: '\Drupal\comment\Form\CommentAdminOverview'
type: 'approval'
requirements:
_permission: 'administer comments'
......@@ -54,6 +54,14 @@ entity.comment.delete_form:
_entity_access: 'comment.delete'
comment: \d+
comment.multiple_delete_confirm:
path: '/admin/content/comment/delete'
defaults:
_title: 'Delete'
_form: '\Drupal\comment\Form\ConfirmDeleteMultiple'
requirements:
_permission: 'administer comments'
comment.reply:
path: '/comment/reply/{entity_type}/{entity}/{field_name}/{pid}'
defaults:
......
langcode: en
status: true
dependencies:
module:
- comment
id: comment_delete_action
label: 'Delete comment'
type: comment
plugin: comment_delete_action
configuration: { }
This diff is collapsed.
......@@ -38,6 +38,10 @@ action.configuration.comment_unpublish_action:
type: action_configuration_default
label: 'Unpublish comment configuration'
action.configuration.comment_delete_action:
type: action_configuration_default
label: 'Delete comment configuration'
comment.type.*:
type: config_entity
label: 'Comment type settings'
......@@ -105,3 +109,6 @@ field.field_settings.comment:
preview:
type: integer
label: 'Preview comment'
field.formatter.settings.comment_permalink:
type: field.formatter.settings.string
......@@ -16,6 +16,14 @@ views.field.comment_entity_link:
type: boolean
label: 'Show teaser-style link'
views.field.comment_bulk_form:
type: views_field_bulk_form
label: 'Comment bulk form'
views.field.commented_entity:
type: views.field.field
label: 'Commented entity'
views.field.comment_last_timestamp:
type: views.field.date
label: 'Last comment date'
......
......@@ -23,6 +23,7 @@ public function getViewsData() {
$data['comment_field_data']['subject']['title'] = $this->t('Title');
$data['comment_field_data']['subject']['help'] = $this->t('The title of the comment.');
$data['comment_field_data']['subject']['field']['default_formatter'] = 'comment_permalink';
$data['comment_field_data']['name']['title'] = $this->t('Author');
$data['comment_field_data']['name']['help'] = $this->t("The name of the comment's author. Can be rendered as a link to the author's homepage.");
......@@ -168,6 +169,17 @@ public function getViewsData() {
],
];
$data['comment_field_data']['entity_id']['field']['id'] = 'commented_entity';
unset($data['comment_field_data']['entity_id']['relationship']);
$data['comment']['comment_bulk_form'] = [
'title' => $this->t('Comment operations bulk form'),
'help' => $this->t('Add a form element that lets you run operations on multiple comments.'),
'field' => [
'id' => 'comment_bulk_form',
],
];
$data['comment_field_data']['thread']['field'] = [
'title' => $this->t('Depth'),
'help' => $this->t('Display the depth of the comment if it is threaded.'),
......
<?php
namespace Drupal\comment\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Returns responses for comment module administrative routes.
*/
class AdminController extends ControllerBase {
/**
* The form builder.
*
* @var \Drupal\Core\Form\FormBuilderInterface
*/
protected $formBuilder;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('form_builder')
);
}
/**
* Constructs an AdminController object.
*
* @param \Drupal\Core\Form\FormBuilderInterface $form_builder
* The form builder.
*/
public function __construct(FormBuilderInterface $form_builder) {
$this->formBuilder = $form_builder;
}
/**
* Presents an administrative comment listing.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request of the page.
* @param string $type
* The type of the overview form ('approval' or 'new') default to 'new'.
*
* @return array
* Then comment multiple delete confirmation form or the comments overview
* administration form.
*/
public function adminPage(Request $request, $type = 'new') {
if ($request->request->get('operation') == 'delete' && $request->request->get('comments')) {
return $this->formBuilder->getForm('\Drupal\comment\Form\ConfirmDeleteMultiple', $request);
}
else {
return $this->formBuilder->getForm('\Drupal\comment\Form\CommentAdminOverview', $type);
}
}
}
......@@ -3,13 +3,13 @@
namespace Drupal\comment\Form;
use Drupal\comment\CommentInterface;
use Drupal\comment\CommentStorageInterface;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -18,11 +18,11 @@
class CommentAdminOverview extends FormBase {
/**
* The entity storage.
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityManager;
protected $entityTypeManager;
/**
* The comment storage.
......@@ -45,23 +45,31 @@ class CommentAdminOverview extends FormBase {
*/
protected $moduleHandler;
/**
* The tempstore factory.
*
* @var \Drupal\user\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/**
* Creates a CommentAdminOverview form.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity manager service.
* @param \Drupal\comment\CommentStorageInterface $comment_storage
* The comment storage.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
*/
public function __construct(EntityManagerInterface $entity_manager, CommentStorageInterface $comment_storage, DateFormatterInterface $date_formatter, ModuleHandlerInterface $module_handler) {
$this->entityManager = $entity_manager;
$this->commentStorage = $comment_storage;
public function __construct(EntityTypeManagerInterface $entity_type_manager, DateFormatterInterface $date_formatter, ModuleHandlerInterface $module_handler, PrivateTempStoreFactory $temp_store_factory) {
$this->entityTypeManager = $entity_type_manager;
$this->commentStorage = $entity_type_manager->getStorage('comment');
$this->dateFormatter = $date_formatter;
$this->moduleHandler = $module_handler;
$this->tempStoreFactory = $temp_store_factory;
}
/**
......@@ -69,10 +77,10 @@ public function __construct(EntityManagerInterface $entity_manager, CommentStora
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager'),
$container->get('entity.manager')->getStorage('comment'),
$container->get('entity_type.manager'),
$container->get('date.formatter'),
$container->get('module_handler')
$container->get('module_handler'),
$container->get('user.private_tempstore')
);
}
......@@ -171,7 +179,9 @@ public function buildForm(array $form, FormStateInterface $form_state, $type = '
}
foreach ($commented_entity_ids as $entity_type => $ids) {
$commented_entities[$entity_type] = $this->entityManager->getStorage($entity_type)->loadMultiple($ids);
$commented_entities[$entity_type] = $this->entityTypeManager
->getStorage($entity_type)
->loadMultiple($ids);
}
foreach ($comments as $comment) {
......@@ -255,23 +265,33 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
public function submitForm(array &$form, FormStateInterface $form_state) {
$operation = $form_state->getValue('operation');
$cids = $form_state->getValue('comments');
foreach ($cids as $cid) {
// Delete operation handled in \Drupal\comment\Form\ConfirmDeleteMultiple
// see \Drupal\comment\Controller\AdminController::adminPage().
if ($operation == 'unpublish') {
$comment = $this->commentStorage->load($cid);
$comment->setPublished(FALSE);
/** @var \Drupal\comment\CommentInterface[] $comments */
$comments = $this->commentStorage->loadMultiple($cids);
if ($operation != 'delete') {
foreach ($comments as $comment) {
if ($operation == 'unpublish') {
$comment->setUnpublished();
}
elseif ($operation == 'publish') {
$comment->setPublished();
}
$comment->save();
}
elseif ($operation == 'publish') {
$comment = $this->commentStorage->load($cid);
$comment->setPublished(TRUE);
$comment->save();
drupal_set_message($this->t('The update has been performed.'));
$form_state->setRedirect('comment.admin');
}
else {
$info = [];
/** @var \Drupal\comment\CommentInterface $comment */
foreach ($comments as $comment) {
$langcode = $comment->language()->getId();
$info[$comment->id()][$langcode] = $langcode;
}
$this->tempStoreFactory
->get('comment_multiple_delete_confirm')
->set($this->currentUser()->id(), $info);
$form_state->setRedirect('comment.multiple_delete_confirm');
}
drupal_set_message($this->t('The update has been performed.'));
$form_state->setRedirect('comment.admin');
}
}
......@@ -3,7 +3,7 @@
namespace Drupal\comment\Form;
use Drupal\comment\CommentStorageInterface;
use Drupal\Component\Utility\Html;
use Drupal\user\PrivateTempStoreFactory;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
......@@ -14,6 +14,13 @@
*/
class ConfirmDeleteMultiple extends ConfirmFormBase {
/**
* The tempstore factory.
*
* @var \Drupal\user\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/**
* The comment storage.
*
......@@ -24,18 +31,21 @@ class ConfirmDeleteMultiple extends ConfirmFormBase {
/**
* An array of comments to be deleted.
*
* @var \Drupal\comment\CommentInterface[]
* @var string[][]
*/
protected $comments;
protected $commentInfo;
/**
* Creates an new ConfirmDeleteMultiple form.
*
* @param \Drupal\comment\CommentStorageInterface $comment_storage
* The comment storage.
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
*/
public function __construct(CommentStorageInterface $comment_storage) {
public function __construct(CommentStorageInterface $comment_storage, PrivateTempStoreFactory $temp_store_factory) {
$this->commentStorage = $comment_storage;
$this->tempStoreFactory = $temp_store_factory;
}
/**
......@@ -43,7 +53,8 @@ public function __construct(CommentStorageInterface $comment_storage) {
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager')->getStorage('comment')
$container->get('entity.manager')->getStorage('comment'),
$container->get('user.private_tempstore')
);
}
......@@ -58,7 +69,7 @@ public function getFormId() {
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to delete these comments and all their children?');
return $this->formatPlural(count($this->commentInfo), 'Are you sure you want to delete this comment and all its children?', 'Are you sure you want to delete these comments and all their children?');
}
/**
......@@ -72,39 +83,56 @@ public function getCancelUrl() {
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Delete comments');
return $this->t('Delete');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$edit = $form_state->getUserInput();
$this->commentInfo = $this->tempStoreFactory->get('comment_multiple_delete_confirm')->get($this->currentUser()->id());
if (empty($this->commentInfo)) {
return $this->redirect('comment.admin');
}
/** @var \Drupal\comment\CommentInterface[] $comments */
$comments = $this->commentStorage->loadMultiple(array_keys($this->commentInfo));
$items = [];
foreach ($this->commentInfo as $id => $langcodes) {
foreach ($langcodes as $langcode) {
$comment = $comments[$id]->getTranslation($langcode);
$key = $id . ':' . $langcode;
$default_key = $id . ':' . $comment->getUntranslated()->language()->getId();
// If we have a translated entity we build a nested list of translations
// that will be deleted.
$languages = $comment->getTranslationLanguages();
if (count($languages) > 1 && $comment->isDefaultTranslation()) {
$names = [];
foreach ($languages as $translation_langcode => $language) {
$names[] = $language->getName();
unset($items[$id . ':' . $translation_langcode]);
}
$items[$default_key] = [
'label' => [
'#markup' => $this->t('@label (Original translation) - <em>The following comment translations will be deleted:</em>', ['@label' => $comment->label()]),
],
'deleted_translations' => [
'#theme' => 'item_list',
'#items' => $names,
],
];
}
elseif (!isset($items[$default_key])) {
$items[$key] = $comment->label();
}
}
}
$form['comments'] = [
'#prefix' => '<ul>',
'#suffix' => '</ul>',
'#tree' => TRUE,
'#theme' => 'item_list',
'#items' => $items,
];
// array_filter() returns only elements with actual values.
$comment_counter = 0;
$this->comments = $this->commentStorage->loadMultiple(array_keys(array_filter($edit['comments'])));
foreach ($this->comments as $comment) {
$cid = $comment->id();
$form['comments'][$cid] = [
'#type' => 'hidden',
'#value' => $cid,
'#prefix' => '<li>',
'#suffix' => Html::escape($comment->label()) . '</li>'
];
$comment_counter++;
}
$form['operation'] = ['#type' => 'hidden', '#value' => 'delete'];
if (!$comment_counter) {
drupal_set_message($this->t('There do not appear to be any comments to delete, or your selected comment was deleted by another administrator.'));
$form_state->setRedirect('comment.admin');
}
return parent::buildForm($form, $form_state);
}
......@@ -113,12 +141,56 @@ public function buildForm(array $form, FormStateInterface $form_state) {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
if ($form_state->getValue('confirm')) {
$this->commentStorage->delete($this->comments);
$count = count($form_state->getValue('comments'));
$this->logger('comment')->notice('Deleted @count comments.', ['@count' => $count]);
drupal_set_message($this->formatPlural($count, 'Deleted 1 comment.', 'Deleted @count comments.'));
if ($form_state->getValue('confirm') && !empty($this->commentInfo)) {
$total_count = 0;
$delete_comments = [];
/** @var \Drupal\Core\Entity\ContentEntityInterface[][] $delete_translations */
$delete_translations = [];
/** @var \Drupal\comment\CommentInterface[] $comments */
$comments = $this->commentStorage->loadMultiple(array_keys($this->commentInfo));
foreach ($this->commentInfo as $id => $langcodes) {
foreach ($langcodes as $langcode) {
$comment = $comments[$id]->getTranslation($langcode);
if ($comment->isDefaultTranslation()) {
$delete_comments[$id] = $comment;
unset($delete_translations[$id]);
$total_count += count($comment->getTranslationLanguages());
}
elseif (!isset($delete_comments[$id])) {
$delete_translations[$id][] = $comment;
}
}
}
if ($delete_comments) {
$this->commentStorage->delete($delete_comments);
$this->logger('content')->notice('Deleted @count comments.', ['@count' => count($delete_comments)]);
}
if ($delete_translations) {
$count = 0;
foreach ($delete_translations as $id => $translations) {
$comment = $comments[$id]->getUntranslated();
foreach ($translations as $translation) {
$comment->removeTranslation($translation->language()->getId());
}
$comment->save();
$count += count($translations);
}
if ($count) {
$total_count += $count;
$this->logger('content')->notice('Deleted @count comment translations.', ['@count' => $count]);
}
}
if ($total_count) {
drupal_set_message($this->formatPlural($total_count, 'Deleted 1 comment.', 'Deleted @count comments.'));
}
$this->tempStoreFactory->get('comment_multiple_delete_confirm')->delete($this->currentUser()->id());
}
$form_state->setRedirectUrl($this->getCancelUrl());
}
......
<?php
namespace Drupal\comment\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Deletes a comment.
*
* @Action(
* id = "comment_delete_action",
* label = @Translation("Delete comment"),
* type = "comment",
* confirm_form_route_name = "comment.multiple_delete_confirm"
* )
*/
class DeleteComment extends ActionBase implements ContainerFactoryPluginInterface {
/**
* The tempstore object.
*
* @var \Drupal\user\PrivateTempStore
*/
protected $tempStore;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Constructs a new DeleteComment object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param array $plugin_definition
* The plugin implementation definition.
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
$this->currentUser = $current_user;
$this->tempStore = $temp_store_factory->get('comment_multiple_delete_confirm');
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('user.private_tempstore'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}
*/
public function executeMultiple(array $entities) {
$info = [];
/** @var \Drupal\comment\CommentInterface $comment */
foreach ($entities as $comment) {
$langcode = $comment->language()->getId();
$info[$comment->id()][$langcode] = $langcode;
}
$this->tempStore->set($this->currentUser->id(), $info);
}
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$this->executeMultiple([$entity]);
}
/**
* {@inheritdoc}
*/
public function access($comment, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\comment\CommentInterface $comment */
return $comment->access('delete', $account, $return_as_object);
}
}
<?php
namespace Drupal\comment\Plugin\Field\FieldFormatter;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\Plugin\Field\FieldFormatter\StringFormatter;
/**
* Plugin implementation of the 'comment_permalink' formatter.