/** * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state) { $options = array(); $types = NodeType::loadMultiple(); $comment_fields = $this->commentManager ? $this->commentManager->getFields('node') : array(); $map = array($this->t('Hidden'), $this->t('Closed'), $this->t('Open')); foreach ($types as $type) { $options[$type->id()] = array('type' => array('#markup' => $this->t($type->label()))); if ($this->commentManager) { $fields = array(); foreach ($comment_fields as $field_name => $info) { // Find all comment fields for the bundle. if (in_array($type->id(), $info['bundles'])) { $instance = FieldConfig::loadByName('node', $type->id(), $field_name); $default_mode = reset($instance->default_value); $fields[] = SafeMarkup::format('@field: !state', array('@field' => $instance->label(), '!state' => $map[$default_mode['status']])); } } // @todo Refactor display of comment fields. if (!empty($fields)) { $options[$type->id()]['comments'] = array('data' => array('#theme' => 'item_list', '#items' => $fields)); } else { $options[$type->id()]['comments'] = $this->t('No comment fields'); } } } if (empty($options)) { $create_url = $this->urlGenerator->generateFromRoute('node.type_add'); $this->setMessage($this->t('You do not have any content types that can be generated. <a href="@create-type">Go create a new content type</a> already!</a>', array('@create-type' => $create_url)), 'error', FALSE); return; } $header = array('type' => $this->t('Content type')); if ($this->commentManager) { $header['comments'] = array('data' => $this->t('Comments'), 'class' => array(RESPONSIVE_PRIORITY_MEDIUM)); } $form['node_types'] = array('#type' => 'tableselect', '#header' => $header, '#options' => $options); $form['kill'] = array('#type' => 'checkbox', '#title' => $this->t('<strong>Delete all content</strong> in these content types before generating new content.'), '#default_value' => $this->getSetting('kill')); $form['num'] = array('#type' => 'textfield', '#title' => $this->t('How many nodes would you like to generate?'), '#default_value' => $this->getSetting('num'), '#size' => 10); $options = array(1 => $this->t('Now')); foreach (array(3600, 86400, 604800, 2592000, 31536000) as $interval) { $options[$interval] = \Drupal::service('date.formatter')->formatInterval($interval, 1) . ' ' . $this->t('ago'); } $form['time_range'] = array('#type' => 'select', '#title' => $this->t('How far back in time should the nodes be dated?'), '#description' => $this->t('Node creation dates will be distributed randomly from the current time, back to the selected time.'), '#options' => $options, '#default_value' => 604800); $form['max_comments'] = array('#type' => $this->moduleHandler->moduleExists('comment') ? 'textfield' : 'value', '#title' => $this->t('Maximum number of comments per node.'), '#description' => $this->t('You must also enable comments for the content types you are generating. Note that some nodes will randomly receive zero comments. Some will receive the max.'), '#default_value' => $this->getSetting('max_comments'), '#size' => 3, '#access' => $this->moduleHandler->moduleExists('comment')); $form['title_length'] = array('#type' => 'textfield', '#title' => $this->t('Maximum number of words in titles'), '#default_value' => $this->getSetting('title_length'), '#size' => 10); $form['add_alias'] = array('#type' => 'checkbox', '#disabled' => !$this->moduleHandler->moduleExists('path'), '#description' => $this->t('Requires path.module'), '#title' => $this->t('Add an url alias for each node.'), '#default_value' => FALSE); $form['add_statistics'] = array('#type' => 'checkbox', '#title' => $this->t('Add statistics for each node (node_counter table).'), '#default_value' => TRUE, '#access' => $this->moduleHandler->moduleExists('statistics')); $options = array(); // We always need a language $languages = \Drupal::languageManager()->getLanguages(LanguageInterface::STATE_ALL); foreach ($languages as $langcode => $language) { $options[$langcode] = $language->getName(); } $default_language = \Drupal::service('language.default')->get(); $default_langcode = $default_language->getId(); $form['add_language'] = array('#type' => 'select', '#title' => $this->t('Set language on nodes'), '#multiple' => TRUE, '#description' => $this->t('Requires locale.module'), '#options' => $options, '#default_value' => array($default_langcode)); $form['submit'] = array('#type' => 'submit', '#value' => $this->t('Generate'), '#tableselect' => TRUE); $form['#redirect'] = FALSE; return $form; }
/** * Prepares mocks for the test. */ protected function setUp() { $this->commentManager = $this->getMock('\\Drupal\\comment\\CommentManagerInterface'); $this->stringTranslation = $this->getStringTranslationStub(); $this->moduleHandler = $this->getMock('\\Drupal\\Core\\Extension\\ModuleHandlerInterface'); $this->currentUser = $this->getMock('\\Drupal\\Core\\Session\\AccountProxyInterface'); $this->commentLinkBuilder = new CommentLinkBuilder($this->currentUser, $this->commentManager, $this->moduleHandler, $this->stringTranslation); $this->commentManager->expects($this->any())->method('getFields')->with('node')->willReturn(array('comment' => array())); $this->commentManager->expects($this->any())->method('forbiddenMessage')->willReturn("Can't let you do that Dave."); $this->stringTranslation->expects($this->any())->method('formatPlural')->willReturnArgument(1); }
/** * Build the default links (reply, edit, delete …) for a comment. * * @param \Drupal\comment\CommentInterface $entity * The comment object. * @param \Drupal\Core\Entity\EntityInterface $commented_entity * The entity to which the comment is attached. * * @return array * An array that can be processed by drupal_pre_render_links(). */ protected function buildLinks(CommentInterface $entity, EntityInterface $commented_entity) { $links = array(); $status = $commented_entity->get($entity->getFieldName())->status; if ($status == CommentItemInterface::OPEN) { if ($entity->access('delete')) { $links['comment-delete'] = array('title' => t('Delete'), 'url' => $entity->urlInfo('delete-form')); } if ($entity->access('update')) { $links['comment-edit'] = array('title' => t('Edit'), 'url' => $entity->urlInfo('edit-form')); } if ($entity->access('create')) { $links['comment-reply'] = array('title' => t('Reply'), 'url' => Url::fromRoute('comment.reply', ['entity_type' => $entity->getCommentedEntityTypeId(), 'entity' => $entity->getCommentedEntityId(), 'field_name' => $entity->getFieldName(), 'pid' => $entity->id()])); } if (!$entity->isPublished() && $entity->access('approve')) { $links['comment-approve'] = array('title' => t('Approve'), 'url' => Url::fromRoute('comment.approve', ['comment' => $entity->id()])); } if (empty($links) && $this->currentUser->isAnonymous()) { $links['comment-forbidden']['title'] = $this->commentManager->forbiddenMessage($commented_entity, $entity->getFieldName()); } } // Add translations link for translation-enabled comment bundles. if ($this->moduleHandler->moduleExists('content_translation') && $this->access($entity)->isAllowed()) { $links['comment-translations'] = array('title' => t('Translate'), 'url' => $entity->urlInfo('drupal:content-translation-overview')); } return array('#theme' => 'links__comment__comment', '#links' => $links, '#attributes' => array('class' => array('links', 'inline'))); }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { $comments = $this->queryFactory->get('comment')->condition('comment_type', $this->entity->id())->execute(); $entity_type = $this->entity->getTargetEntityTypeId(); $caption = ''; foreach (array_keys($this->commentManager->getFields($entity_type)) as $field_name) { /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */ if (($field_storage = FieldStorageConfig::loadByName($entity_type, $field_name)) && $field_storage->getSetting('comment_type') == $this->entity->id() && !$field_storage->isDeleted()) { $caption .= '<p>' . $this->t('%label is used by the %field field on your site. You can not remove this comment type until you have removed the field.', array('%label' => $this->entity->label(), '%field' => $field_storage->label())) . '</p>'; } } if (!empty($comments)) { $caption .= '<p>' . $this->formatPlural(count($comments), '%label is used by 1 comment on your site. You can not remove this comment type until you have removed all of the %label comments.', '%label is used by @count comments on your site. You may not remove %label until you have removed all of the %label comments.', array('%label' => $this->entity->label())) . '</p>'; } if ($caption) { $form['description'] = array('#markup' => $caption); return $form; } else { return parent::buildForm($form, $form_state); } }
/** * {@inheritdoc} */ public function save(array $form, FormStateInterface $form_state) { $comment_type = $this->entity; $status = $comment_type->save(); $edit_link = $this->entity->link($this->t('Edit')); if ($status == SAVED_UPDATED) { drupal_set_message(t('Comment type %label has been updated.', array('%label' => $comment_type->label()))); $this->logger->notice('Comment type %label has been updated.', array('%label' => $comment_type->label(), 'link' => $edit_link)); } else { $this->commentManager->addBodyField($comment_type->id()); drupal_set_message(t('Comment type %label has been added.', array('%label' => $comment_type->label()))); $this->logger->notice('Comment type %label has been added.', array('%label' => $comment_type->label(), 'link' => $edit_link)); } $form_state->setRedirectUrl($comment_type->urlInfo('collection')); }
/** * #post_render_cache callback; attaches "X new comments" link metadata. * * @param array $element * A render array with the following keys: * - #markup * - #attached * @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 * The updated $element. */ public function attachNewCommentsLinkMetadata(array $element, array $context) { $entity = $this->entityManager->getStorage($context['entity_type'])->load($context['entity_id']); // Build "X new comments" link metadata. $new = $this->commentManager->getCountNewComments($entity); // Early-return if there are zero new comments for the current user. if ($new === 0) { return $element; } $field_name = $context['field_name']; $page_number = $this->entityManager->getStorage('comment')->getNewCommentPageNumber($entity->{$field_name}->comment_count, $new, $entity); $query = $page_number ? array('page' => $page_number) : NULL; // Attach metadata. $element['#attached']['js'][] = array('type' => 'setting', 'data' => array('comment' => array('newCommentsLinks' => array($context['entity_type'] => array($context['field_name'] => array($context['entity_id'] => array('new_comment_count' => (int) $new, 'first_new_comment_link' => $entity->url('canonical', ['query' => $query, 'fragment' => 'new'])))))))); return $element; }
/** * Returns a set of nodes' last read timestamps. * * @param \Symfony\Component\HttpFoundation\Request $request * The request of the page. * * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException * @return \Symfony\Component\HttpFoundation\JsonResponse * The JSON response. */ public function renderNewCommentsNodeLinks(Request $request) { if ($this->currentUser()->isAnonymous()) { throw new AccessDeniedHttpException(); } $nids = $request->request->get('node_ids'); $field_name = $request->request->get('field_name'); if (!isset($nids)) { throw new NotFoundHttpException(); } // Only handle up to 100 nodes. $nids = array_slice($nids, 0, 100); $links = array(); foreach ($nids as $nid) { $node = $this->entityManager->getStorage('node')->load($nid); $new = $this->commentManager->getCountNewComments($node); $page_number = $this->entityManager()->getStorage('comment')->getNewCommentPageNumber($node->{$field_name}->comment_count, $new, $node); $query = $page_number ? array('page' => $page_number) : NULL; $links[$nid] = array('new_comment_count' => (int) $new, 'first_new_comment_link' => $this->getUrlGenerator()->generateFromRoute('entity.node.canonical', array('node' => $node->id()), array('query' => $query, 'fragment' => 'new'))); } return new JsonResponse($links); }
/** * {@inheritdoc} */ public function buildCommentedEntityLinks(FieldableEntityInterface $entity, array &$context) { $entity_links = array(); $view_mode = $context['view_mode']; if ($view_mode == 'search_index' || $view_mode == 'search_result' || $view_mode == 'print' || $view_mode == 'rss') { // Do not add any links if the entity is displayed for: // - search indexing. // - constructing a search result excerpt. // - print. // - rss. return array(); } $fields = $this->commentManager->getFields($entity->getEntityTypeId()); foreach ($fields as $field_name => $detail) { // Skip fields that the entity does not have. if (!$entity->hasField($field_name)) { continue; } $links = array(); $commenting_status = $entity->get($field_name)->status; if ($commenting_status != CommentItemInterface::HIDDEN) { // Entity has commenting status open or closed. $field_definition = $entity->getFieldDefinition($field_name); if ($view_mode == 'teaser') { // Teaser view: display the number of comments that have been posted, // or a link to add new comments if the user has permission, the // entity is open to new comments, and there currently are none. if ($this->currentUser->hasPermission('access comments')) { if (!empty($entity->get($field_name)->comment_count)) { $links['comment-comments'] = array('title' => $this->formatPlural($entity->get($field_name)->comment_count, '1 comment', '@count comments'), 'attributes' => array('title' => $this->t('Jump to the first comment.')), 'fragment' => 'comments', 'url' => $entity->urlInfo()); if ($this->moduleHandler->moduleExists('history')) { $links['comment-new-comments'] = array('title' => '', 'url' => Url::fromRoute('<current>'), 'attributes' => array('class' => 'hidden', 'title' => $this->t('Jump to the first new comment.'), 'data-history-node-last-comment-timestamp' => $entity->get($field_name)->last_comment_timestamp, 'data-history-node-field-name' => $field_name)); } } } // Provide a link to new comment form. if ($commenting_status == CommentItemInterface::OPEN) { $comment_form_location = $field_definition->getSetting('form_location'); if ($this->currentUser->hasPermission('post comments')) { $links['comment-add'] = array('title' => $this->t('Add new comments'), 'language' => $entity->language(), 'attributes' => array('title' => $this->t('Share your thoughts and opinions.')), 'fragment' => 'comment-form'); if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE) { $links['comment-add']['url'] = Url::fromRoute('comment.reply', ['entity_type' => $entity->getEntityTypeId(), 'entity' => $entity->id(), 'field_name' => $field_name]); } else { $links['comment-add'] += ['url' => $entity->urlInfo()]; } } elseif ($this->currentUser->isAnonymous()) { $links['comment-forbidden'] = array('title' => $this->commentManager->forbiddenMessage($entity, $field_name)); } } } else { // Entity in other view modes: add a "post comment" link if the user // is allowed to post comments and if this entity is allowing new // comments. if ($commenting_status == CommentItemInterface::OPEN) { $comment_form_location = $field_definition->getSetting('form_location'); if ($this->currentUser->hasPermission('post comments')) { // Show the "post comment" link if the form is on another page, or // if there are existing comments that the link will skip past. if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE || !empty($entity->get($field_name)->comment_count) && $this->currentUser->hasPermission('access comments')) { $links['comment-add'] = array('title' => $this->t('Add new comment'), 'attributes' => array('title' => $this->t('Share your thoughts and opinions.')), 'fragment' => 'comment-form'); if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE) { $links['comment-add']['url'] = Url::fromRoute('comment.reply', ['entity_type' => $entity->getEntityTypeId(), 'entity' => $entity->id(), 'field_name' => $field_name]); } else { $links['comment-add']['url'] = $entity->urlInfo(); } } } elseif ($this->currentUser->isAnonymous()) { $links['comment-forbidden'] = array('title' => $this->commentManager->forbiddenMessage($entity, $field_name)); } } } } if (!empty($links)) { $entity_links['comment__' . $field_name] = array('#theme' => 'links__entity__comment__' . $field_name, '#links' => $links, '#attributes' => array('class' => array('links', 'inline'))); if ($view_mode == 'teaser' && $this->moduleHandler->moduleExists('history') && $this->currentUser->isAuthenticated()) { $entity_links['comment__' . $field_name]['#cache']['contexts'][] = 'user'; $entity_links['comment__' . $field_name]['#attached']['library'][] = 'comment/drupal.node-new-comments-link'; // Embed the metadata for the "X new comments" link (if any) on this // entity. $entity_links['comment__' . $field_name]['#attached']['drupalSettings']['history']['lastReadTimestamps'][$entity->id()] = (int) history_read($entity->id()); $new_comments = $this->commentManager->getCountNewComments($entity); if ($new_comments > 0) { $page_number = $this->entityManager->getStorage('comment')->getNewCommentPageNumber($entity->{$field_name}->comment_count, $new_comments, $entity, $field_name); $query = $page_number ? ['page' => $page_number] : NULL; $value = ['new_comment_count' => (int) $new_comments, 'first_new_comment_link' => $entity->url('canonical', ['query' => $query, 'fragment' => 'new'])]; $parents = ['comment', 'newCommentsLinks', $entity->getEntityTypeId(), $field_name, $entity->id()]; NestedArray::setValue($entity_links['comment__' . $field_name]['#attached']['drupalSettings'], $parents, $value); } } } } return $entity_links; }
/** * {@inheritdoc} */ public function getTopics($tid, AccountInterface $account) { $config = $this->configFactory->get('forum.settings'); $forum_per_page = $config->get('topics.page_limit'); $sortby = $config->get('topics.order'); $header = array(array('data' => $this->t('Topic'), 'field' => 'f.title'), array('data' => $this->t('Replies'), 'field' => 'f.comment_count'), array('data' => $this->t('Last reply'), 'field' => 'f.last_comment_timestamp')); $order = $this->getTopicOrder($sortby); for ($i = 0; $i < count($header); $i++) { if ($header[$i]['field'] == $order['field']) { $header[$i]['sort'] = $order['sort']; } } $query = $this->connection->select('forum_index', 'f')->extend('Drupal\\Core\\Database\\Query\\PagerSelectExtender')->extend('Drupal\\Core\\Database\\Query\\TableSortExtender'); $query->fields('f'); $query->condition('f.tid', $tid)->addTag('node_access')->addMetaData('base_table', 'forum_index')->orderBy('f.sticky', 'DESC')->orderByHeader($header)->limit($forum_per_page); $count_query = $this->connection->select('forum_index', 'f'); $count_query->condition('f.tid', $tid); $count_query->addExpression('COUNT(*)'); $count_query->addTag('node_access'); $count_query->addMetaData('base_table', 'forum_index'); $query->setCountQuery($count_query); $result = $query->execute(); $nids = array(); foreach ($result as $record) { $nids[] = $record->nid; } if ($nids) { $nodes = $this->entityManager->getStorage('node')->loadMultiple($nids); $query = $this->connection->select('node_field_data', 'n')->extend('Drupal\\Core\\Database\\Query\\TableSortExtender'); $query->fields('n', array('nid')); $query->join('comment_entity_statistics', 'ces', "n.nid = ces.entity_id AND ces.field_name = 'comment_forum' AND ces.entity_type = 'node'"); $query->fields('ces', array('cid', 'last_comment_uid', 'last_comment_timestamp', 'comment_count')); $query->join('forum_index', 'f', 'f.nid = n.nid'); $query->addField('f', 'tid', 'forum_tid'); $query->join('users_field_data', 'u', 'n.uid = u.uid AND u.default_langcode = 1'); $query->addField('u', 'name'); $query->join('users_field_data', 'u2', 'ces.last_comment_uid = u2.uid AND u.default_langcode = 1'); $query->addExpression('CASE ces.last_comment_uid WHEN 0 THEN ces.last_comment_name ELSE u2.name END', 'last_comment_name'); $query->orderBy('f.sticky', 'DESC')->orderByHeader($header)->condition('n.nid', $nids, 'IN')->condition('n.default_langcode', 1); $result = array(); foreach ($query->execute() as $row) { $topic = $nodes[$row->nid]; $topic->comment_mode = $topic->comment_forum->status; foreach ($row as $key => $value) { $topic->{$key} = $value; } $result[] = $topic; } } else { $result = array(); } $topics = array(); $first_new_found = FALSE; foreach ($result as $topic) { if ($account->isAuthenticated()) { // A forum is new if the topic is new, or if there are new comments since // the user's last visit. if ($topic->forum_tid != $tid) { $topic->new = 0; } else { $history = $this->lastVisit($topic->id(), $account); $topic->new_replies = $this->commentManager->getCountNewComments($topic, 'comment_forum', $history); $topic->new = $topic->new_replies || $topic->last_comment_timestamp > $history; } } else { // Do not track "new replies" status for topics if the user is anonymous. $topic->new_replies = 0; $topic->new = 0; } // Make sure only one topic is indicated as the first new topic. $topic->first_new = FALSE; if ($topic->new != 0 && !$first_new_found) { $topic->first_new = TRUE; $first_new_found = TRUE; } if ($topic->comment_count > 0) { $last_reply = new \stdClass(); $last_reply->created = $topic->last_comment_timestamp; $last_reply->name = $topic->last_comment_name; $last_reply->uid = $topic->last_comment_uid; $topic->last_reply = $last_reply; } $topics[$topic->id()] = $topic; } return array('topics' => $topics, 'header' => $header); }