' . t('About') . ''; $output .= '
' . t('The Forum module lets you create threaded discussion forums with functionality similar to other message board systems. In a forum, users post topics and threads in nested hierarchies, allowing discussions to be categorized and grouped.') . '
'; $output .= '' . t('The Forum module adds and uses a content type called Forum topic. For background information on content types, see the Node module help page.', array(':node_help'=>\Drupal::url('help.page', array('name' => 'node')))) . '
'; $output .= '' . t('A forum is represented by a hierarchical structure, consisting of:'); $output .= '
' . t('For more information, see the online documentation for the Forum module.', array(':forum' => 'https://www.drupal.org/documentation/modules/forum')) . '
'; $output .= '' . t('Forums contain forum topics. Use containers to group related forums.') . '
'; $more_help_link = array( '#type' => 'link', '#url' => Url::fromRoute('help.page', ['name' => 'forum']), '#title' => t('More help'), '#attributes' => array( 'class' => array('icon-help'), ), ); $container = array( '#theme' => 'container', '#children' => $more_help_link, '#attributes' => array( 'class' => array('more-link'), ), ); $output .= \Drupal::service('renderer')->renderPlain($container); return $output; case 'forum.add_container': return '' . t('Use containers to group related forums.') . '
'; case 'forum.add_forum': return '' . t('A forum holds related forum topics.') . '
'; case 'forum.settings': return '' . t('Adjust the display of your forum topics. Organize the forums on the forum structure page.', array(':forum-structure' => \Drupal::url('forum.overview'))) . '
'; } } /** * Implements hook_theme(). */ function forum_theme() { return array( 'forums' => array( 'variables' => array('forums' => array(), 'topics' => array(), 'topics_pager' => array(), 'parents' => NULL, 'term' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL, 'header' => array()), ), 'forum_list' => array( 'variables' => array('forums' => NULL, 'parents' => NULL, 'tid' => NULL), ), 'forum_icon' => array( 'variables' => array('new_posts' => NULL, 'num_posts' => 0, 'comment_mode' => 0, 'sticky' => 0, 'first_new' => FALSE), ), 'forum_submitted' => array( 'variables' => array('topic' => NULL), ), ); } /** * Implements hook_entity_type_build(). */ function forum_entity_type_build(array &$entity_types) { /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */ // Register forum specific forms. $entity_types['taxonomy_term'] ->setFormClass('forum', 'Drupal\forum\Form\ForumForm') ->setFormClass('container', 'Drupal\forum\Form\ContainerForm') ->setLinkTemplate('forum-edit-container-form', '/admin/structure/forum/edit/container/{taxonomy_term}') ->setLinkTemplate('forum-delete-form', '/admin/structure/forum/delete/forum/{taxonomy_term}') ->setLinkTemplate('forum-edit-form', '/admin/structure/forum/edit/forum/{taxonomy_term}'); } /** * Implements hook_entity_bundle_info_alter(). */ function forum_entity_bundle_info_alter(&$bundles) { // Take over URI construction for taxonomy terms that are forums. if ($vid = \Drupal::config('forum.settings')->get('vocabulary')) { if (isset($bundles['taxonomy_term'][$vid])) { $bundles['taxonomy_term'][$vid]['uri_callback'] = 'forum_uri'; } } } /** * Entity URI callback used in forum_entity_bundle_info_alter(). */ function forum_uri($forum) { return Url::fromRoute('forum.page', ['taxonomy_term' => $forum->id()]); } /** * Implements hook_entity_bundle_field_info_alter(). */ function forum_entity_bundle_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle) { if ($entity_type->id() == 'node' && !empty($fields['taxonomy_forums'])) { $fields['taxonomy_forums']->addConstraint('ForumLeaf', []); } } /** * Implements hook_ENTITY_TYPE_presave() for node entities. * * Assigns the forum taxonomy when adding a topic from within a forum. */ function forum_node_presave(EntityInterface $node) { if (\Drupal::service('forum_manager')->checkNodeType($node)) { // Make sure all fields are set properly: $node->icon = !empty($node->icon) ? $node->icon : ''; if (!$node->taxonomy_forums->isEmpty()) { $node->forum_tid = $node->taxonomy_forums->target_id; // Only do a shadow copy check if this is not a new node. if (!$node->isNew()) { $old_tid = \Drupal::service('forum.index_storage')->getOriginalTermId($node); if ($old_tid && isset($node->forum_tid) && ($node->forum_tid != $old_tid) && !empty($node->shadow)) { // A shadow copy needs to be created. Retain new term and add old term. $node->taxonomy_forums[count($node->taxonomy_forums)] = array('target_id' => $old_tid); } } } } } /** * Implements hook_ENTITY_TYPE_update() for node entities. */ function forum_node_update(EntityInterface $node) { if (\Drupal::service('forum_manager')->checkNodeType($node)) { // If this is not a new revision and does exist, update the forum record, // otherwise insert a new one. /** @var \Drupal\forum\ForumIndexStorageInterface $forum_index_storage */ $forum_index_storage = \Drupal::service('forum.index_storage'); if ($node->getRevisionId() == $node->original->getRevisionId() && $forum_index_storage->getOriginalTermId($node)) { if (!empty($node->forum_tid)) { $forum_index_storage->update($node); } // The node is removed from the forum. else { $forum_index_storage->delete($node); } } else { if (!empty($node->forum_tid)) { $forum_index_storage->create($node); } } // If the node has a shadow forum topic, update the record for this // revision. if (!empty($node->shadow)) { $forum_index_storage->deleteRevision($node); $forum_index_storage->create($node); } // If the node is published, update the forum index. if ($node->isPublished()) { $forum_index_storage->deleteIndex($node); $forum_index_storage->createIndex($node); } // When a forum node is unpublished, remove it from the forum_index table. else { $forum_index_storage->deleteIndex($node); } } } /** * Implements hook_ENTITY_TYPE_insert() for node entities. */ function forum_node_insert(EntityInterface $node) { if (\Drupal::service('forum_manager')->checkNodeType($node)) { /** @var \Drupal\forum\ForumIndexStorageInterface $forum_index_storage */ $forum_index_storage = \Drupal::service('forum.index_storage'); if (!empty($node->forum_tid)) { $forum_index_storage->create($node); } // If the node is published, update the forum index. if ($node->isPublished()) { $forum_index_storage->createIndex($node); } } } /** * Implements hook_ENTITY_TYPE_predelete() for node entities. */ function forum_node_predelete(EntityInterface $node) { if (\Drupal::service('forum_manager')->checkNodeType($node)) { /** @var \Drupal\forum\ForumIndexStorageInterface $forum_index_storage */ $forum_index_storage = \Drupal::service('forum.index_storage'); $forum_index_storage->delete($node); $forum_index_storage->deleteIndex($node); } } /** * Implements hook_ENTITY_TYPE_storage_load() for node entities. */ function forum_node_storage_load($nodes) { $node_vids = array(); foreach ($nodes as $node) { if (\Drupal::service('forum_manager')->checkNodeType($node)) { $node_vids[] = $node->getRevisionId(); } } if (!empty($node_vids)) { $result = \Drupal::service('forum.index_storage')->read($node_vids); foreach ($result as $record) { $nodes[$record->nid]->forum_tid = $record->tid; } } } /** * Implements hook_ENTITY_TYPE_update() for comment entities. */ function forum_comment_update(CommentInterface $comment) { if ($comment->getCommentedEntityTypeId() == 'node') { \Drupal::service('forum.index_storage')->updateIndex($comment->getCommentedEntity()); } } /** * Implements hook_ENTITY_TYPE_insert() for comment entities. */ function forum_comment_insert(CommentInterface $comment) { if ($comment->getCommentedEntityTypeId() == 'node') { \Drupal::service('forum.index_storage')->updateIndex($comment->getCommentedEntity()); } } /** * Implements hook_ENTITY_TYPE_delete() for comment entities. */ function forum_comment_delete(CommentInterface $comment) { if ($comment->getCommentedEntityTypeId() == 'node') { \Drupal::service('forum.index_storage')->updateIndex($comment->getCommentedEntity()); } } /** * Implements hook_form_BASE_FORM_ID_alter(). */ function forum_form_taxonomy_vocabulary_form_alter(&$form, FormStateInterface $form_state, $form_id) { $vid = \Drupal::config('forum.settings')->get('vocabulary'); $vocabulary = $form_state->getFormObject()->getEntity(); if ($vid == $vocabulary->id()) { $form['help_forum_vocab'] = array( '#markup' => t('This is the designated forum vocabulary. Some of the normal vocabulary options have been removed.'), '#weight' => -1, ); // Forum's vocabulary always has single hierarchy. Forums and containers // have only one parent or no parent for root items. By default this value // is 0. $form['hierarchy']['#value'] = TAXONOMY_HIERARCHY_SINGLE; // Do not allow to delete forum's vocabulary. $form['actions']['delete']['#access'] = FALSE; // Do not allow to change a vid of forum's vocabulary. $form['vid']['#disabled'] = TRUE; } } /** * Implements hook_form_FORM_ID_alter() for taxonomy_term_form(). */ function forum_form_taxonomy_term_form_alter(&$form, FormStateInterface $form_state, $form_id) { $vid = \Drupal::config('forum.settings')->get('vocabulary'); if (isset($form['vid']['#value']) && $form['vid']['#value'] == $vid) { // Hide multiple parents select from forum terms. $form['relations']['parent']['#access'] = FALSE; } } /** * Implements hook_form_BASE_FORM_ID_alter() for node_form(). */ function forum_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) { $node = $form_state->getFormObject()->getEntity(); if (isset($node->taxonomy_forums) && !$node->isNew()) { $forum_terms = $node->taxonomy_forums; // If editing, give option to leave shadows. $shadow = (count($forum_terms) > 1); $form['shadow'] = array( '#type' => 'checkbox', '#title' => t('Leave shadow copy'), '#default_value' => $shadow, '#description' => t('If you move this topic, you can leave a link in the old forum to the new forum.'), ); $form['forum_tid'] = array('#type' => 'value', '#value' => $node->forum_tid); } if (isset($form['taxonomy_forums'])) { $widget =& $form['taxonomy_forums']['widget']; // Make the vocabulary required for 'real' forum-nodes. $widget['#required'] = TRUE; $widget['#multiple'] = FALSE; if (empty($widget['#default_value'])) { // If there is no default forum already selected, try to get the forum // ID from the URL (e.g., if we are on a page like node/add/forum/2, we // expect "2" to be the ID of the forum that was requested). $requested_forum_id = \Drupal::request()->query->get('forum_id'); $widget['#default_value'] = is_numeric($requested_forum_id) ? $requested_forum_id : ''; } } } /** * Implements hook_preprocess_HOOK() for block templates. */ function forum_preprocess_block(&$variables) { if ($variables['configuration']['provider'] == 'forum') { $variables['attributes']['role'] = 'navigation'; } } /** * Implements hook_theme_suggestions_HOOK(). */ function forum_theme_suggestions_forums(array $variables) { $suggestions = array(); $tid = $variables['term']->id(); // Provide separate template suggestions based on what's being output. Topic // ID is also accounted for. Check both variables to be safe then the inverse. // Forums with topic IDs take precedence. if ($variables['forums'] && !$variables['topics']) { $suggestions[] = 'forums__containers'; $suggestions[] = 'forums__' . $tid; $suggestions[] = 'forums__containers__' . $tid; } elseif (!$variables['forums'] && $variables['topics']) { $suggestions[] = 'forums__topics'; $suggestions[] = 'forums__' . $tid; $suggestions[] = 'forums__topics__' . $tid; } else { $suggestions[] = 'forums__' . $tid; } return $suggestions; } /** * Prepares variables for forums templates. * * Default template: forums.html.twig. * * @param array $variables * An array containing the following elements: * - forums: An array of all forum objects to display for the given taxonomy * term ID. If tid = 0 then all the top-level forums are displayed. * - topics: An array of all the topics in the current forum. * - parents: An array of taxonomy term objects that are ancestors of the * current term ID. * - term: Taxonomy term of the current forum. * - sortby: One of the following integers indicating the sort criteria: * - 1: Date - newest first. * - 2: Date - oldest first. * - 3: Posts with the most comments first. * - 4: Posts with the least comments first. * - forum_per_page: The maximum number of topics to display per page. */ function template_preprocess_forums(&$variables) { $variables['tid'] = $variables['term']->id(); if ($variables['forums_defined'] = count($variables['forums']) || count($variables['parents'])) { if (!empty($variables['forums'])) { $variables['forums'] = array( '#theme' => 'forum_list', '#forums' => $variables['forums'], '#parents' => $variables['parents'], '#tid' => $variables['tid'], ); } if ($variables['term'] && empty($variables['term']->forum_container->value) && !empty($variables['topics'])) { $forum_topic_list_header = $variables['header']; $table = array( '#theme' => 'table__forum_topic_list', '#responsive' => FALSE, '#attributes' => array('id' => 'forum-topic-' . $variables['tid']), '#header' => array(), '#rows' => array(), ); if (!empty($forum_topic_list_header)) { $table['#header'] = $forum_topic_list_header; } /** @var \Drupal\node\NodeInterface $topic */ foreach ($variables['topics'] as $id => $topic) { $variables['topics'][$id]->icon = array( '#theme' => 'forum_icon', '#new_posts' => $topic->new, '#num_posts' => $topic->comment_count, '#comment_mode' => $topic->comment_mode, '#sticky' => $topic->isSticky(), '#first_new' => $topic->first_new, ); // We keep the actual tid in forum table, if it's different from the // current tid then it means the topic appears in two forums, one of // them is a shadow copy. if ($variables['tid'] != $topic->forum_tid) { $variables['topics'][$id]->moved = TRUE; $variables['topics'][$id]->title = $topic->getTitle(); $variables['topics'][$id]->message = \Drupal::l(t('This topic has been moved'), new Url('forum.page', ['taxonomy_term' => $topic->forum_tid])); } else { $variables['topics'][$id]->moved = FALSE; $variables['topics'][$id]->title_link = \Drupal::l($topic->getTitle(), $topic->urlInfo()); $variables['topics'][$id]->message = ''; } $forum_submitted = array('#theme' => 'forum_submitted', '#topic' => (object) array( 'uid' => $topic->getOwnerId(), 'name' => $topic->getOwner()->getDisplayName(), 'created' => $topic->getCreatedTime(), )); $variables['topics'][$id]->submitted = drupal_render($forum_submitted); $forum_submitted = array( '#theme' => 'forum_submitted', '#topic' => isset($topic->last_reply) ? $topic->last_reply : NULL, ); $variables['topics'][$id]->last_reply = drupal_render($forum_submitted); $variables['topics'][$id]->new_text = ''; $variables['topics'][$id]->new_url = ''; if ($topic->new_replies) { $page_number = \Drupal::entityManager()->getStorage('comment') ->getNewCommentPageNumber($topic->comment_count, $topic->new_replies, $topic, 'comment_forum'); $query = $page_number ? array('page' => $page_number) : NULL; $variables['topics'][$id]->new_text = \Drupal::translation()->formatPlural($topic->new_replies, '1 new post in topic %title', '@count new posts in topic %title', array('%title' => $variables['topics'][$id]->label())); $variables['topics'][$id]->new_url = \Drupal::url('entity.node.canonical', ['node' => $topic->id()], ['query' => $query, 'fragment' => 'new']); } // Build table rows from topics. $row = array(); $row[] = array( 'data' => array( $topic->icon, array( '#markup' => '