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 @@ ...@@ -2,6 +2,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter; namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemInterface; use Drupal\Core\Field\FieldItemInterface;
...@@ -116,8 +117,8 @@ public function viewElements(FieldItemListInterface $items, $langcode) { ...@@ -116,8 +117,8 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = []; $elements = [];
$url = NULL; $url = NULL;
if ($this->getSetting('link_to_entity')) { if ($this->getSetting('link_to_entity')) {
// For the default revision this falls back to 'canonical' // For the default revision this falls back to 'canonical'.
$url = $items->getEntity()->urlInfo('revision'); $url = $this->getEntityUrl($items->getEntity());
} }
foreach ($items as $delta => $item) { foreach ($items as $delta => $item) {
...@@ -155,4 +156,18 @@ protected function viewValue(FieldItemInterface $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: ...@@ -2,7 +2,7 @@ comment.admin:
path: '/admin/content/comment' path: '/admin/content/comment'
defaults: defaults:
_title: 'Comments' _title: 'Comments'
_controller: '\Drupal\comment\Controller\AdminController::adminPage' _form: '\Drupal\comment\Form\CommentAdminOverview'
type: 'new' type: 'new'
requirements: requirements:
_permission: 'administer comments' _permission: 'administer comments'
...@@ -11,7 +11,7 @@ comment.admin_approval: ...@@ -11,7 +11,7 @@ comment.admin_approval:
path: '/admin/content/comment/approval' path: '/admin/content/comment/approval'
defaults: defaults:
_title: 'Unapproved comments' _title: 'Unapproved comments'
_controller: '\Drupal\comment\Controller\AdminController::adminPage' _form: '\Drupal\comment\Form\CommentAdminOverview'
type: 'approval' type: 'approval'
requirements: requirements:
_permission: 'administer comments' _permission: 'administer comments'
...@@ -54,6 +54,14 @@ entity.comment.delete_form: ...@@ -54,6 +54,14 @@ entity.comment.delete_form:
_entity_access: 'comment.delete' _entity_access: 'comment.delete'
comment: \d+ 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: comment.reply:
path: '/comment/reply/{entity_type}/{entity}/{field_name}/{pid}' path: '/comment/reply/{entity_type}/{entity}/{field_name}/{pid}'
defaults: 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: ...@@ -38,6 +38,10 @@ action.configuration.comment_unpublish_action:
type: action_configuration_default type: action_configuration_default
label: 'Unpublish comment configuration' label: 'Unpublish comment configuration'
action.configuration.comment_delete_action:
type: action_configuration_default
label: 'Delete comment configuration'
comment.type.*: comment.type.*:
type: config_entity type: config_entity
label: 'Comment type settings' label: 'Comment type settings'
...@@ -105,3 +109,6 @@ field.field_settings.comment: ...@@ -105,3 +109,6 @@ field.field_settings.comment:
preview: preview:
type: integer type: integer
label: 'Preview comment' label: 'Preview comment'
field.formatter.settings.comment_permalink:
type: field.formatter.settings.string
...@@ -16,6 +16,14 @@ views.field.comment_entity_link: ...@@ -16,6 +16,14 @@ views.field.comment_entity_link:
type: boolean type: boolean
label: 'Show teaser-style link' 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: views.field.comment_last_timestamp:
type: views.field.date type: views.field.date
label: 'Last comment date' label: 'Last comment date'
......
...@@ -23,6 +23,7 @@ public function getViewsData() { ...@@ -23,6 +23,7 @@ public function getViewsData() {
$data['comment_field_data']['subject']['title'] = $this->t('Title'); $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']['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']['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."); $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() { ...@@ -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'] = [ $data['comment_field_data']['thread']['field'] = [
'title' => $this->t('Depth'), 'title' => $this->t('Depth'),
'help' => $this->t('Display the depth of the comment if it is threaded.'), '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 @@ ...@@ -3,13 +3,13 @@
namespace Drupal\comment\Form; namespace Drupal\comment\Form;
use Drupal\comment\CommentInterface; use Drupal\comment\CommentInterface;
use Drupal\comment\CommentStorageInterface;
use Drupal\Component\Utility\Unicode; use Drupal\Component\Utility\Unicode;
use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
...@@ -18,11 +18,11 @@ ...@@ -18,11 +18,11 @@
class CommentAdminOverview extends FormBase { 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. * The comment storage.
...@@ -45,23 +45,31 @@ class CommentAdminOverview extends FormBase { ...@@ -45,23 +45,31 @@ class CommentAdminOverview extends FormBase {
*/ */
protected $moduleHandler; protected $moduleHandler;
/**
* The tempstore factory.
*
* @var \Drupal\user\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/** /**
* Creates a CommentAdminOverview form. * Creates a CommentAdminOverview form.
* *
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity manager service. * The entity manager service.
* @param \Drupal\comment\CommentStorageInterface $comment_storage
* The comment storage.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service. * The date formatter service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The 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) { public function __construct(EntityTypeManagerInterface $entity_type_manager, DateFormatterInterface $date_formatter, ModuleHandlerInterface $module_handler, PrivateTempStoreFactory $temp_store_factory) {
$this->entityManager = $entity_manager; $this->entityTypeManager = $entity_type_manager;
$this->commentStorage = $comment_storage; $this->commentStorage = $entity_type_manager->getStorage('comment');
$this->dateFormatter = $date_formatter; $this->dateFormatter = $date_formatter;
$this->moduleHandler = $module_handler; $this->moduleHandler = $module_handler;
$this->tempStoreFactory = $temp_store_factory;
} }
/** /**
...@@ -69,10 +77,10 @@ public function __construct(EntityManagerInterface $entity_manager, CommentStora ...@@ -69,10 +77,10 @@ public function __construct(EntityManagerInterface $entity_manager, CommentStora
*/ */
public static function create(ContainerInterface $container) { public static function create(ContainerInterface $container) {
return new static( return new static(
$container->get('entity.manager'), $container->get('entity_type.manager'),
$container->get('entity.manager')->getStorage('comment'),
$container->get('date.formatter'), $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 = ' ...@@ -171,7 +179,9 @@ public function buildForm(array $form, FormStateInterface $form_state, $type = '
} }
foreach ($commented_entity_ids as $entity_type => $ids) { 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) { foreach ($comments as $comment) {
...@@ -255,23 +265,33 @@ public function validateForm(array &$form, FormStateInterface $form_state) { ...@@ -255,23 +265,33 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
public function submitForm(array &$form, FormStateInterface $form_state) { public function submitForm(array &$form, FormStateInterface $form_state) {
$operation = $form_state->getValue('operation'); $operation = $form_state->getValue('operation');
$cids = $form_state->getValue('comments'); $cids = $form_state->getValue('comments');
/** @var \Drupal\comment\CommentInterface[] $comments */
foreach ($cids as $cid) { $comments = $this->commentStorage->loadMultiple($cids);
// Delete operation handled in \Drupal\comment\Form\ConfirmDeleteMultiple if ($operation != 'delete') {
// see \Drupal\comment\Controller\AdminController::adminPage(). foreach ($comments as $comment) {
if ($operation == 'unpublish') { if ($operation == 'unpublish') {
$comment = $this->commentStorage->load($cid); $comment->setUnpublished();
$comment->setPublished(FALSE); }
elseif ($operation == 'publish') {
$comment->setPublished();
}
$comment->save(); $comment->save();
} }
elseif ($operation == 'publish') { drupal_set_message($this->t('The update has been performed.'));
$comment = $this->commentStorage->load($cid); $form_state->setRedirect('comment.admin');
$comment->setPublished(TRUE); }
$comment->save(); 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 @@ ...@@ -3,7 +3,7 @@
namespace Drupal\comment\Form; namespace Drupal\comment\Form;
use Drupal\comment\CommentStorageInterface; use Drupal\comment\CommentStorageInterface;
use Drupal\Component\Utility\Html; use Drupal\user\PrivateTempStoreFactory;
use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
...@@ -14,6 +14,13 @@ ...@@ -14,6 +14,13 @@
*/ */
class ConfirmDeleteMultiple extends ConfirmFormBase { class ConfirmDeleteMultiple extends ConfirmFormBase {
/**
* The tempstore factory.
*
* @var \Drupal\user\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/** /**
* The comment storage. * The comment storage.
* *
...@@ -24,18 +31,21 @@ class ConfirmDeleteMultiple extends ConfirmFormBase { ...@@ -24,18 +31,21 @@ class ConfirmDeleteMultiple extends ConfirmFormBase {
/** /**
* An array of comments to be deleted. * An array of comments to be deleted.
* *
* @var \Drupal\comment\CommentInterface[] * @var string[][]
*/ */
protected $comments; protected $commentInfo;
/** /**
* Creates an new ConfirmDeleteMultiple form. * Creates an new ConfirmDeleteMultiple form.
* *
* @param \Drupal\comment\CommentStorageInterface $comment_storage * @param \Drupal\comment\CommentStorageInterface $comment_storage
* The 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->commentStorage = $comment_storage;
$this->tempStoreFactory = $temp_store_factory;
} }
/** /**
...@@ -43,7 +53,8 @@ public function __construct(CommentStorageInterface $comment_storage) { ...@@ -43,7 +53,8 @@ public function __construct(CommentStorageInterface $comment_storage) {
*/ */
public static function create(ContainerInterface $container) { public static function create(ContainerInterface $container) {
return new static( 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() { ...@@ -58,7 +69,7 @@ public function getFormId() {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getQuestion() { 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() { ...@@ -72,39 +83,56 @@ public function getCancelUrl() {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getConfirmText() { public function getConfirmText() {
return $this->t('Delete comments'); return $this->t('Delete');
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function buildForm(array $form, FormStateInterface $form_state) { 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'] = [ $form['comments'] = [
'#prefix' => '<ul>', '#theme' => 'item_list',
'#suffix' => '</ul>', '#items' => $items,
'#tree' => TRUE,
]; ];
// 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); return parent::buildForm($form, $form_state);
} }
...@@ -113,12 +141,56 @@ public function buildForm(array $form, FormStateInterface $form_state) { ...@@ -113,12 +141,56 @@ public function buildForm(array $form, FormStateInterface $form_state) {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function submitForm(array &$form, FormStateInterface $form_state) { public function submitForm(array &$form, FormStateInterface $form_state) {
if ($form_state->getValue('confirm')) { if ($form_state->getValue('confirm') && !empty($this->commentInfo)) {
$this->commentStorage->delete($this->comments); $total_count = 0;
$count = count($form_state->getValue('comments')); $delete_comments = [];
$this->logger('comment')->notice('Deleted @count comments.', ['@count' => $count]); /** @var \Drupal\Core\Entity\ContentEntityInterface[][] $delete_translations */
drupal_set_message($this->formatPlural($count, 'Deleted 1 comment.', 'Deleted @count comments.')); $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());
}