annotate core/modules/comment/src/Controller/CommentController.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\comment\Controller;
Chris@0 4
Chris@0 5 use Drupal\comment\CommentInterface;
Chris@0 6 use Drupal\comment\CommentManagerInterface;
Chris@0 7 use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
Chris@0 8 use Drupal\Core\Access\AccessResult;
Chris@0 9 use Drupal\Core\Cache\CacheableResponseInterface;
Chris@0 10 use Drupal\Core\Controller\ControllerBase;
Chris@18 11 use Drupal\Core\Entity\EntityFieldManagerInterface;
Chris@0 12 use Drupal\Core\Entity\EntityInterface;
Chris@18 13 use Drupal\Core\Entity\EntityRepositoryInterface;
Chris@18 14 use Drupal\Core\Entity\EntityTypeManagerInterface;
Chris@0 15 use Symfony\Component\DependencyInjection\ContainerInterface;
Chris@0 16 use Symfony\Component\HttpFoundation\JsonResponse;
Chris@0 17 use Symfony\Component\HttpFoundation\RedirectResponse;
Chris@0 18 use Symfony\Component\HttpFoundation\Request;
Chris@0 19 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
Chris@0 20 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
Chris@0 21 use Symfony\Component\HttpKernel\HttpKernelInterface;
Chris@0 22
Chris@0 23 /**
Chris@0 24 * Controller for the comment entity.
Chris@0 25 *
Chris@0 26 * @see \Drupal\comment\Entity\Comment.
Chris@0 27 */
Chris@0 28 class CommentController extends ControllerBase {
Chris@0 29
Chris@0 30 /**
Chris@0 31 * The HTTP kernel.
Chris@0 32 *
Chris@0 33 * @var \Symfony\Component\HttpKernel\HttpKernelInterface
Chris@0 34 */
Chris@0 35 protected $httpKernel;
Chris@0 36
Chris@0 37 /**
Chris@0 38 * The comment manager service.
Chris@0 39 *
Chris@0 40 * @var \Drupal\comment\CommentManagerInterface
Chris@0 41 */
Chris@0 42 protected $commentManager;
Chris@0 43
Chris@0 44 /**
Chris@18 45 * The entity field manager.
Chris@0 46 *
Chris@18 47 * @var \Drupal\Core\Entity\EntityFieldManagerInterface
Chris@0 48 */
Chris@18 49 protected $entityFieldManager;
Chris@18 50
Chris@18 51 /**
Chris@18 52 * The entity repository.
Chris@18 53 *
Chris@18 54 * @var Drupal\Core\Entity\EntityRepositoryInterface
Chris@18 55 */
Chris@18 56 protected $entityRepository;
Chris@0 57
Chris@0 58 /**
Chris@0 59 * Constructs a CommentController object.
Chris@0 60 *
Chris@0 61 * @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
Chris@0 62 * HTTP kernel to handle requests.
Chris@0 63 * @param \Drupal\comment\CommentManagerInterface $comment_manager
Chris@0 64 * The comment manager service.
Chris@18 65 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
Chris@18 66 * The entity type manager service.
Chris@18 67 * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
Chris@18 68 * The entity field manager service.
Chris@18 69 * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
Chris@18 70 * The entity repository service.
Chris@0 71 */
Chris@18 72 public function __construct(HttpKernelInterface $http_kernel, CommentManagerInterface $comment_manager, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager = NULL, EntityRepositoryInterface $entity_repository = NULL) {
Chris@0 73 $this->httpKernel = $http_kernel;
Chris@0 74 $this->commentManager = $comment_manager;
Chris@18 75 $this->entityTypeManager = $entity_type_manager;
Chris@18 76 if (!$entity_field_manager) {
Chris@18 77 @trigger_error('The entity_field.manager service must be passed to CommentController::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
Chris@18 78 $entity_field_manager = \Drupal::service('entity_field.manager');
Chris@18 79 }
Chris@18 80 $this->entityFieldManager = $entity_field_manager;
Chris@18 81 if (!$entity_repository) {
Chris@18 82 @trigger_error('The entity.repository service must be passed to CommentController::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
Chris@18 83 $entity_repository = \Drupal::service('entity.repository');
Chris@18 84 }
Chris@18 85 $this->entityRepository = $entity_repository;
Chris@0 86 }
Chris@0 87
Chris@0 88 /**
Chris@0 89 * {@inheritdoc}
Chris@0 90 */
Chris@0 91 public static function create(ContainerInterface $container) {
Chris@0 92 return new static(
Chris@0 93 $container->get('http_kernel'),
Chris@0 94 $container->get('comment.manager'),
Chris@18 95 $container->get('entity_type.manager'),
Chris@18 96 $container->get('entity_field.manager'),
Chris@18 97 $container->get('entity.repository')
Chris@0 98 );
Chris@0 99 }
Chris@0 100
Chris@0 101 /**
Chris@0 102 * Publishes the specified comment.
Chris@0 103 *
Chris@0 104 * @param \Drupal\comment\CommentInterface $comment
Chris@0 105 * A comment entity.
Chris@0 106 *
Chris@0 107 * @return \Symfony\Component\HttpFoundation\RedirectResponse
Chris@0 108 */
Chris@0 109 public function commentApprove(CommentInterface $comment) {
Chris@17 110 $comment->setPublished();
Chris@0 111 $comment->save();
Chris@0 112
Chris@17 113 $this->messenger()->addStatus($this->t('Comment approved.'));
Chris@0 114 $permalink_uri = $comment->permalink();
Chris@0 115 $permalink_uri->setAbsolute();
Chris@0 116 return new RedirectResponse($permalink_uri->toString());
Chris@0 117 }
Chris@0 118
Chris@0 119 /**
Chris@0 120 * Redirects comment links to the correct page depending on comment settings.
Chris@0 121 *
Chris@0 122 * Since comments are paged there is no way to guarantee which page a comment
Chris@0 123 * appears on. Comment paging and threading settings may be changed at any
Chris@0 124 * time. With threaded comments, an individual comment may move between pages
Chris@0 125 * as comments can be added either before or after it in the overall
Chris@0 126 * discussion. Therefore we use a central routing function for comment links,
Chris@0 127 * which calculates the page number based on current comment settings and
Chris@0 128 * returns the full comment view with the pager set dynamically.
Chris@0 129 *
Chris@0 130 * @param \Symfony\Component\HttpFoundation\Request $request
Chris@0 131 * The request of the page.
Chris@0 132 * @param \Drupal\comment\CommentInterface $comment
Chris@0 133 * A comment entity.
Chris@0 134 *
Chris@0 135 * @return \Symfony\Component\HttpFoundation\Response
Chris@0 136 * The comment listing set to the page on which the comment appears.
Chris@0 137 *
Chris@0 138 * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
Chris@0 139 * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
Chris@0 140 */
Chris@0 141 public function commentPermalink(Request $request, CommentInterface $comment) {
Chris@0 142 if ($entity = $comment->getCommentedEntity()) {
Chris@0 143 // Check access permissions for the entity.
Chris@0 144 if (!$entity->access('view')) {
Chris@0 145 throw new AccessDeniedHttpException();
Chris@0 146 }
Chris@18 147 $field_definition = $this->entityFieldManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle())[$comment->getFieldName()];
Chris@0 148
Chris@0 149 // Find the current display page for this comment.
Chris@18 150 $page = $this->entityTypeManager()->getStorage('comment')->getDisplayOrdinal($comment, $field_definition->getSetting('default_mode'), $field_definition->getSetting('per_page'));
Chris@0 151 // @todo: Cleaner sub request handling.
Chris@18 152 $subrequest_url = $entity->toUrl()->setOption('query', ['page' => $page])->toString(TRUE);
Chris@0 153 $redirect_request = Request::create($subrequest_url->getGeneratedUrl(), 'GET', $request->query->all(), $request->cookies->all(), [], $request->server->all());
Chris@0 154 // Carry over the session to the subrequest.
Chris@18 155 if ($request->hasSession()) {
Chris@18 156 $redirect_request->setSession($request->getSession());
Chris@0 157 }
Chris@0 158 $request->query->set('page', $page);
Chris@0 159 $response = $this->httpKernel->handle($redirect_request, HttpKernelInterface::SUB_REQUEST);
Chris@0 160 if ($response instanceof CacheableResponseInterface) {
Chris@0 161 // @todo Once path aliases have cache tags (see
Chris@0 162 // https://www.drupal.org/node/2480077), add test coverage that
Chris@0 163 // the cache tag for a commented entity's path alias is added to the
Chris@0 164 // comment's permalink response, because there can be blocks or
Chris@0 165 // other content whose renderings depend on the subrequest's URL.
Chris@0 166 $response->addCacheableDependency($subrequest_url);
Chris@0 167 }
Chris@0 168 return $response;
Chris@0 169 }
Chris@0 170 throw new NotFoundHttpException();
Chris@0 171 }
Chris@0 172
Chris@0 173 /**
Chris@0 174 * The _title_callback for the page that renders the comment permalink.
Chris@0 175 *
Chris@0 176 * @param \Drupal\comment\CommentInterface $comment
Chris@0 177 * The current comment.
Chris@0 178 *
Chris@0 179 * @return string
Chris@0 180 * The translated comment subject.
Chris@0 181 */
Chris@0 182 public function commentPermalinkTitle(CommentInterface $comment) {
Chris@18 183 return $this->entityRepository->getTranslationFromContext($comment)->label();
Chris@0 184 }
Chris@0 185
Chris@0 186 /**
Chris@0 187 * Redirects legacy node links to the new path.
Chris@0 188 *
Chris@0 189 * @param \Drupal\Core\Entity\EntityInterface $node
Chris@0 190 * The node object identified by the legacy URL.
Chris@0 191 *
Chris@0 192 * @return \Symfony\Component\HttpFoundation\RedirectResponse
Chris@0 193 * Redirects user to new url.
Chris@0 194 *
Chris@0 195 * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
Chris@0 196 */
Chris@0 197 public function redirectNode(EntityInterface $node) {
Chris@0 198 $fields = $this->commentManager->getFields('node');
Chris@0 199 // Legacy nodes only had a single comment field, so use the first comment
Chris@0 200 // field on the entity.
Chris@0 201 if (!empty($fields) && ($field_names = array_keys($fields)) && ($field_name = reset($field_names))) {
Chris@0 202 return $this->redirect('comment.reply', [
Chris@0 203 'entity_type' => 'node',
Chris@0 204 'entity' => $node->id(),
Chris@0 205 'field_name' => $field_name,
Chris@0 206 ]);
Chris@0 207 }
Chris@0 208 throw new NotFoundHttpException();
Chris@0 209 }
Chris@0 210
Chris@0 211 /**
Chris@0 212 * Form constructor for the comment reply form.
Chris@0 213 *
Chris@0 214 * There are several cases that have to be handled, including:
Chris@0 215 * - replies to comments
Chris@0 216 * - replies to entities
Chris@0 217 *
Chris@0 218 * @param \Symfony\Component\HttpFoundation\Request $request
Chris@0 219 * The current request object.
Chris@0 220 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 221 * The entity this comment belongs to.
Chris@0 222 * @param string $field_name
Chris@0 223 * The field_name to which the comment belongs.
Chris@0 224 * @param int $pid
Chris@0 225 * (optional) Some comments are replies to other comments. In those cases,
Chris@0 226 * $pid is the parent comment's comment ID. Defaults to NULL.
Chris@0 227 *
Chris@0 228 * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
Chris@0 229 * An associative array containing:
Chris@0 230 * - An array for rendering the entity or parent comment.
Chris@0 231 * - comment_entity: If the comment is a reply to the entity.
Chris@0 232 * - comment_parent: If the comment is a reply to another comment.
Chris@0 233 * - comment_form: The comment form as a renderable array.
Chris@0 234 *
Chris@0 235 * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
Chris@0 236 */
Chris@0 237 public function getReplyForm(Request $request, EntityInterface $entity, $field_name, $pid = NULL) {
Chris@0 238 $account = $this->currentUser();
Chris@0 239 $build = [];
Chris@0 240
Chris@0 241 // The user is not just previewing a comment.
Chris@0 242 if ($request->request->get('op') != $this->t('Preview')) {
Chris@0 243
Chris@0 244 // $pid indicates that this is a reply to a comment.
Chris@0 245 if ($pid) {
Chris@0 246 // Load the parent comment.
Chris@18 247 $comment = $this->entityTypeManager()->getStorage('comment')->load($pid);
Chris@0 248 // Display the parent comment.
Chris@18 249 $build['comment_parent'] = $this->entityTypeManager()->getViewBuilder('comment')->view($comment);
Chris@0 250 }
Chris@0 251
Chris@0 252 // The comment is in response to a entity.
Chris@0 253 elseif ($entity->access('view', $account)) {
Chris@0 254 // We make sure the field value isn't set so we don't end up with a
Chris@0 255 // redirect loop.
Chris@0 256 $entity = clone $entity;
Chris@0 257 $entity->{$field_name}->status = CommentItemInterface::HIDDEN;
Chris@0 258 // Render array of the entity full view mode.
Chris@18 259 $build['commented_entity'] = $this->entityTypeManager()->getViewBuilder($entity->getEntityTypeId())->view($entity, 'full');
Chris@0 260 unset($build['commented_entity']['#cache']);
Chris@0 261 }
Chris@0 262 }
Chris@0 263 else {
Chris@0 264 $build['#title'] = $this->t('Preview comment');
Chris@0 265 }
Chris@0 266
Chris@0 267 // Show the actual reply box.
Chris@18 268 $comment = $this->entityTypeManager()->getStorage('comment')->create([
Chris@0 269 'entity_id' => $entity->id(),
Chris@0 270 'pid' => $pid,
Chris@0 271 'entity_type' => $entity->getEntityTypeId(),
Chris@0 272 'field_name' => $field_name,
Chris@0 273 ]);
Chris@0 274 $build['comment_form'] = $this->entityFormBuilder()->getForm($comment);
Chris@0 275
Chris@0 276 return $build;
Chris@0 277 }
Chris@0 278
Chris@0 279 /**
Chris@0 280 * Access check for the reply form.
Chris@0 281 *
Chris@0 282 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 283 * The entity this comment belongs to.
Chris@0 284 * @param string $field_name
Chris@0 285 * The field_name to which the comment belongs.
Chris@0 286 * @param int $pid
Chris@0 287 * (optional) Some comments are replies to other comments. In those cases,
Chris@0 288 * $pid is the parent comment's comment ID. Defaults to NULL.
Chris@0 289 *
Chris@0 290 * @return \Drupal\Core\Access\AccessResultInterface
Chris@0 291 * An access result
Chris@0 292 *
Chris@0 293 * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
Chris@0 294 */
Chris@0 295 public function replyFormAccess(EntityInterface $entity, $field_name, $pid = NULL) {
Chris@0 296 // Check if entity and field exists.
Chris@0 297 $fields = $this->commentManager->getFields($entity->getEntityTypeId());
Chris@0 298 if (empty($fields[$field_name])) {
Chris@0 299 throw new NotFoundHttpException();
Chris@0 300 }
Chris@0 301
Chris@0 302 $account = $this->currentUser();
Chris@0 303
Chris@0 304 // Check if the user has the proper permissions.
Chris@0 305 $access = AccessResult::allowedIfHasPermission($account, 'post comments');
Chris@0 306
Chris@12 307 // If commenting is open on the entity.
Chris@0 308 $status = $entity->{$field_name}->status;
Chris@0 309 $access = $access->andIf(AccessResult::allowedIf($status == CommentItemInterface::OPEN)
Chris@12 310 ->addCacheableDependency($entity))
Chris@12 311 // And if user has access to the host entity.
Chris@12 312 ->andIf(AccessResult::allowedIf($entity->access('view')));
Chris@0 313
Chris@0 314 // $pid indicates that this is a reply to a comment.
Chris@0 315 if ($pid) {
Chris@0 316 // Check if the user has the proper permissions.
Chris@0 317 $access = $access->andIf(AccessResult::allowedIfHasPermission($account, 'access comments'));
Chris@0 318
Chris@0 319 // Load the parent comment.
Chris@18 320 $comment = $this->entityTypeManager()->getStorage('comment')->load($pid);
Chris@0 321 // Check if the parent comment is published and belongs to the entity.
Chris@0 322 $access = $access->andIf(AccessResult::allowedIf($comment && $comment->isPublished() && $comment->getCommentedEntityId() == $entity->id()));
Chris@0 323 if ($comment) {
Chris@0 324 $access->addCacheableDependency($comment);
Chris@0 325 }
Chris@0 326 }
Chris@0 327 return $access;
Chris@0 328 }
Chris@0 329
Chris@0 330 /**
Chris@0 331 * Returns a set of nodes' last read timestamps.
Chris@0 332 *
Chris@0 333 * @param \Symfony\Component\HttpFoundation\Request $request
Chris@0 334 * The request of the page.
Chris@0 335 *
Chris@0 336 * @return \Symfony\Component\HttpFoundation\JsonResponse
Chris@0 337 * The JSON response.
Chris@0 338 *
Chris@0 339 * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
Chris@0 340 * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
Chris@0 341 */
Chris@0 342 public function renderNewCommentsNodeLinks(Request $request) {
Chris@0 343 if ($this->currentUser()->isAnonymous()) {
Chris@0 344 throw new AccessDeniedHttpException();
Chris@0 345 }
Chris@0 346
Chris@0 347 $nids = $request->request->get('node_ids');
Chris@0 348 $field_name = $request->request->get('field_name');
Chris@0 349 if (!isset($nids)) {
Chris@0 350 throw new NotFoundHttpException();
Chris@0 351 }
Chris@0 352 // Only handle up to 100 nodes.
Chris@0 353 $nids = array_slice($nids, 0, 100);
Chris@0 354
Chris@0 355 $links = [];
Chris@0 356 foreach ($nids as $nid) {
Chris@18 357 $node = $this->entityTypeManager()->getStorage('node')->load($nid);
Chris@0 358 $new = $this->commentManager->getCountNewComments($node);
Chris@18 359 $page_number = $this->entityTypeManager()->getStorage('comment')
Chris@0 360 ->getNewCommentPageNumber($node->{$field_name}->comment_count, $new, $node, $field_name);
Chris@0 361 $query = $page_number ? ['page' => $page_number] : NULL;
Chris@0 362 $links[$nid] = [
Chris@0 363 'new_comment_count' => (int) $new,
Chris@0 364 'first_new_comment_link' => $this->getUrlGenerator()->generateFromRoute('entity.node.canonical', ['node' => $node->id()], ['query' => $query, 'fragment' => 'new']),
Chris@0 365 ];
Chris@0 366 }
Chris@0 367
Chris@0 368 return new JsonResponse($links);
Chris@0 369 }
Chris@0 370
Chris@0 371 }