forum.module 26.1 KB
Newer Older
Dries's avatar
Dries committed
1 2
<?php

3 4
/**
 * @file
5
 * Provides discussion forums.
6 7
 */

8
use Drupal\comment\CommentInterface;
9
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
10
use Drupal\Core\Entity\EntityInterface;
11
use Drupal\Core\Entity\EntityTypeInterface;
12
use Drupal\Core\Url;
13
use Drupal\Core\Form\FormStateInterface;
14
use Drupal\Core\Routing\RouteMatchInterface;
15
use Drupal\taxonomy\VocabularyInterface;
16
use Drupal\user\Entity\User;
17

18
/**
19
 * Implements hook_help().
20
 */
21
function forum_help($route_name, RouteMatchInterface $route_match) {
22 23
  switch ($route_name) {
    case 'help.page.forum':
24 25
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
26
      $output .= '<p>' . 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.') . '</p>';
27
      $output .= '<p>' . t('The Forum module adds and uses a content type called <em>Forum topic</em>. For background information on content types, see the <a href=":node_help">Node module help page</a>.', [':node_help' => \Drupal::url('help.page', ['name' => 'node'])]) . '</p>';
28
      $output .= '<p>' . t('A forum is represented by a hierarchical structure, consisting of:');
29
      $output .= '<ul>';
30 31 32
      $output .= '<li>' . t('<em>Forums</em> (for example, <em>Recipes for cooking vegetables</em>)') . '</li>';
      $output .= '<li>' . t('<em>Forum topics</em> submitted by users (for example, <em>How to cook potatoes</em>), which start discussions.') . '</li>';
      $output .= '<li>' . t('Threaded <em>comments</em> submitted by users (for example, <em>You wash the potatoes first and then...</em>).') . '</li>';
33
      $output .= '<li>' . t('Optional <em>containers</em>, used to group similar forums. Forums can be placed inside containers, and vice versa.') . '</li>';
34
      $output .= '</ul>';
35
      $output .= '</p>';
36
      $output .= '<p>' . t('For more information, see the <a href=":forum">online documentation for the Forum module</a>.', [':forum' => 'https://www.drupal.org/documentation/modules/forum']) . '</p>';
37 38
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
39
      $output .= '<dt>' . t('Setting up the forum structure') . '</dt>';
40
      $output .= '<dd>' . t('Visit the <a href=":forums">Forums page</a> to set up containers and forums to hold your discussion topics.', [':forums' => \Drupal::url('forum.overview')]) . '</dd>';
41
      $output .= '<dt>' . t('Starting a discussion') . '</dt>';
42
      $output .= '<dd>' . t('The <a href=":create-topic">Forum topic</a> link on the <a href=":content-add">Add content</a> page creates the first post of a new threaded discussion, or thread.', [':create-topic' => \Drupal::url('node.add', ['node_type' => 'forum']), ':content-add' => \Drupal::url('node.add_page')]) . '</dd>';
43
      $output .= '<dt>' . t('Navigating in the forum') . '</dt>';
44
      $output .= '<dd>' . t('Enabling the Forum module provides a default <em>Forums</em> menu item in the Tools menu that links to the <a href=":forums">Forums page</a>.', [':forums' => \Drupal::url('forum.index')]) . '</dd>';
45 46 47
      $output .= '<dt>' . t('Moving forum topics') . '</dt>';
      $output .= '<dd>' . t('A forum topic (and all of its comments) may be moved between forums by selecting a different forum while editing a forum topic. When moving a forum topic between forums, the <em>Leave shadow copy</em> option creates a link in the original forum pointing to the new location.') . '</dd>';
      $output .= '<dt>' . t('Locking and disabling comments') . '</dt>';
48
      $output .= '<dd>' . t('Selecting <em>Closed</em> under <em>Comment settings</em> while editing a forum topic will lock (prevent new comments on) the thread. Selecting <em>Hidden</em> under <em>Comment settings</em> while editing a forum topic will hide all existing comments on the thread, and prevent new ones.') . '</dd>';
49
      $output .= '</dl>';
50
      return $output;
51 52

    case 'forum.overview':
53
      $output = '<p>' . t('Forums contain forum topics. Use containers to group related forums.') . '</p>';
54
      $more_help_link = [
55
        '#type' => 'link',
56
        '#url' => Url::fromRoute('help.page', ['name' => 'forum']),
57
        '#title' => t('More help'),
58 59 60 61 62
        '#attributes' => [
          'class' => ['icon-help'],
        ],
      ];
      $container = [
63
        '#theme' => 'container',
64
        '#children' => $more_help_link,
65 66 67 68
        '#attributes' => [
          'class' => ['more-link'],
        ],
      ];
69
      $output .= \Drupal::service('renderer')->renderPlain($container);
70
      return $output;
71 72

    case 'forum.add_container':
73
      return '<p>' . t('Use containers to group related forums.') . '</p>';
74 75

    case 'forum.add_forum':
76
      return '<p>' . t('A forum holds related forum topics.') . '</p>';
77 78

    case 'forum.settings':
79
      return '<p>' . t('Adjust the display of your forum topics. Organize the forums on the <a href=":forum-structure">forum structure page</a>.', [':forum-structure' => \Drupal::url('forum.overview')]) . '</p>';
80 81 82
  }
}

83
/**
84
 * Implements hook_theme().
85 86
 */
function forum_theme() {
87 88 89 90 91 92 93 94 95 96 97 98 99 100
  return [
    'forums' => [
      'variables' => ['forums' => [], 'topics' => [], 'topics_pager' => [], 'parents' => NULL, 'term' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL, 'header' => []],
    ],
    'forum_list' => [
      'variables' => ['forums' => NULL, 'parents' => NULL, 'tid' => NULL],
    ],
    'forum_icon' => [
      'variables' => ['new_posts' => NULL, 'num_posts' => 0, 'comment_mode' => 0, 'sticky' => 0, 'first_new' => FALSE],
    ],
    'forum_submitted' => [
      'variables' => ['topic' => NULL],
    ],
  ];
101 102
}

103
/**
104
 * Implements hook_entity_type_build().
105
 */
106 107
function forum_entity_type_build(array &$entity_types) {
  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
108
  // Register forum specific forms.
109
  $entity_types['taxonomy_term']
110 111
    ->setFormClass('forum', 'Drupal\forum\Form\ForumForm')
    ->setFormClass('container', 'Drupal\forum\Form\ContainerForm')
112 113 114
    ->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}');
115 116
}

117
/**
118
 * Implements hook_entity_bundle_info_alter().
119
 */
120
function forum_entity_bundle_info_alter(&$bundles) {
121
  // Take over URI construction for taxonomy terms that are forums.
122
  if ($vid = \Drupal::config('forum.settings')->get('vocabulary')) {
123 124
    if (isset($bundles['taxonomy_term'][$vid])) {
      $bundles['taxonomy_term'][$vid]['uri_callback'] = 'forum_uri';
125 126 127 128 129
    }
  }
}

/**
130
 * Entity URI callback used in forum_entity_bundle_info_alter().
131 132
 */
function forum_uri($forum) {
133
  return Url::fromRoute('forum.page', ['taxonomy_term' => $forum->id()]);
134 135
}

136
/**
137
 * Implements hook_entity_bundle_field_info_alter().
138
 */
139
function forum_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) {
140 141
  if ($entity_type->id() == 'node'  && !empty($fields['taxonomy_forums'])) {
    $fields['taxonomy_forums']->addConstraint('ForumLeaf', []);
142 143
  }
}
144

145
/**
146
 * Implements hook_ENTITY_TYPE_presave() for node entities.
147
 *
148
 * Assigns the forum taxonomy when adding a topic from within a forum.
149
 */
150
function forum_node_presave(EntityInterface $node) {
151
  if (\Drupal::service('forum_manager')->checkNodeType($node)) {
152 153
    // Make sure all fields are set properly:
    $node->icon = !empty($node->icon) ? $node->icon : '';
154 155
    if (!$node->taxonomy_forums->isEmpty()) {
      $node->forum_tid = $node->taxonomy_forums->target_id;
156 157
      // Only do a shadow copy check if this is not a new node.
      if (!$node->isNew()) {
158
        $old_tid = \Drupal::service('forum.index_storage')->getOriginalTermId($node);
159 160
        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.
161
          $node->taxonomy_forums[count($node->taxonomy_forums)] = ['target_id' => $old_tid];
162
        }
163 164 165 166
      }
    }
  }
}
167

168
/**
169
 * Implements hook_ENTITY_TYPE_update() for node entities.
170
 */
171
function forum_node_update(EntityInterface $node) {
172
  if (\Drupal::service('forum_manager')->checkNodeType($node)) {
173 174
    // If this is not a new revision and does exist, update the forum record,
    // otherwise insert a new one.
175 176 177
    /** @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)) {
178
      if (!empty($node->forum_tid)) {
179
        $forum_index_storage->update($node);
180 181 182
      }
      // The node is removed from the forum.
      else {
183
        $forum_index_storage->delete($node);
184 185 186
      }
    }
    else {
187
      if (!empty($node->forum_tid)) {
188
        $forum_index_storage->create($node);
189
      }
190
    }
191 192
    // If the node has a shadow forum topic, update the record for this
    // revision.
193
    if (!empty($node->shadow)) {
194 195
      $forum_index_storage->deleteRevision($node);
      $forum_index_storage->create($node);
196 197 198 199
    }

    // If the node is published, update the forum index.
    if ($node->isPublished()) {
200 201
      $forum_index_storage->deleteIndex($node);
      $forum_index_storage->createIndex($node);
202 203 204
    }
    // When a forum node is unpublished, remove it from the forum_index table.
    else {
205
      $forum_index_storage->deleteIndex($node);
206
    }
207 208
  }
}
209

210
/**
211
 * Implements hook_ENTITY_TYPE_insert() for node entities.
212
 */
213
function forum_node_insert(EntityInterface $node) {
214
  if (\Drupal::service('forum_manager')->checkNodeType($node)) {
215 216
    /** @var \Drupal\forum\ForumIndexStorageInterface $forum_index_storage */
    $forum_index_storage = \Drupal::service('forum.index_storage');
217
    if (!empty($node->forum_tid)) {
218
      $forum_index_storage->create($node);
219
    }
220 221 222

    // If the node is published, update the forum index.
    if ($node->isPublished()) {
223
      $forum_index_storage->createIndex($node);
224
    }
225 226
  }
}
227

228
/**
229
 * Implements hook_ENTITY_TYPE_predelete() for node entities.
230
 */
231
function forum_node_predelete(EntityInterface $node) {
232
  if (\Drupal::service('forum_manager')->checkNodeType($node)) {
233 234 235 236
    /** @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);
237
  }
238
}
239

240
/**
241
 * Implements hook_ENTITY_TYPE_storage_load() for node entities.
242
 */
243
function forum_node_storage_load($nodes) {
244
  $node_vids = [];
245
  foreach ($nodes as $node) {
246
    if (\Drupal::service('forum_manager')->checkNodeType($node)) {
247
      $node_vids[] = $node->getRevisionId();
248 249 250
    }
  }
  if (!empty($node_vids)) {
251
    $result = \Drupal::service('forum.index_storage')->read($node_vids);
252 253 254
    foreach ($result as $record) {
      $nodes[$record->nid]->forum_tid = $record->tid;
    }
255
  }
256 257
}

258
/**
259
 * Implements hook_ENTITY_TYPE_update() for comment entities.
260
 */
261
function forum_comment_update(CommentInterface $comment) {
262
  if ($comment->getCommentedEntityTypeId() == 'node') {
263
    \Drupal::service('forum.index_storage')->updateIndex($comment->getCommentedEntity());
264 265 266 267
  }
}

/**
268
 * Implements hook_ENTITY_TYPE_insert() for comment entities.
269
 */
270
function forum_comment_insert(CommentInterface $comment) {
271
  if ($comment->getCommentedEntityTypeId() == 'node') {
272
    \Drupal::service('forum.index_storage')->updateIndex($comment->getCommentedEntity());
273
  }
274 275 276
}

/**
277
 * Implements hook_ENTITY_TYPE_delete() for comment entities.
278
 */
279
function forum_comment_delete(CommentInterface $comment) {
280
  if ($comment->getCommentedEntityTypeId() == 'node') {
281
    \Drupal::service('forum.index_storage')->updateIndex($comment->getCommentedEntity());
282
  }
283 284
}

285
/**
286
 * Implements hook_form_BASE_FORM_ID_alter() for \Drupal\taxonomy\VocabularyForm.
287
 */
288
function forum_form_taxonomy_vocabulary_form_alter(&$form, FormStateInterface $form_state, $form_id) {
289
  $vid = \Drupal::config('forum.settings')->get('vocabulary');
290
  $vocabulary = $form_state->getFormObject()->getEntity();
291
  if ($vid == $vocabulary->id()) {
292
    $form['help_forum_vocab'] = [
293 294
      '#markup' => t('This is the designated forum vocabulary. Some of the normal vocabulary options have been removed.'),
      '#weight' => -1,
295
    ];
296 297 298
    // 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.
299
    $form['hierarchy']['#value'] = VocabularyInterface::HIERARCHY_SINGLE;
300 301
    // Do not allow to delete forum's vocabulary.
    $form['actions']['delete']['#access'] = FALSE;
302 303
    // Do not allow to change a vid of forum's vocabulary.
    $form['vid']['#disabled'] = TRUE;
304 305 306 307
  }
}

/**
308
 * Implements hook_form_FORM_ID_alter() for \Drupal\taxonomy\TermForm.
309
 */
310
function forum_form_taxonomy_term_form_alter(&$form, FormStateInterface $form_state, $form_id) {
311
  $vid = \Drupal::config('forum.settings')->get('vocabulary');
312
  if (isset($form['vid']['#value']) && $form['vid']['#value'] == $vid) {
313
    // Hide multiple parents select from forum terms.
314
    $form['relations']['parent']['#access'] = FALSE;
315
  }
316 317 318
}

/**
319
 * Implements hook_form_BASE_FORM_ID_alter() for \Drupal\node\NodeForm.
320
 */
321
function forum_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
322
  $node = $form_state->getFormObject()->getEntity();
323 324 325 326
  if (isset($node->taxonomy_forums) && !$node->isNew()) {
    $forum_terms = $node->taxonomy_forums;
    // If editing, give option to leave shadows.
    $shadow = (count($forum_terms) > 1);
327
    $form['shadow'] = [
328 329 330 331
      '#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.'),
332 333
    ];
    $form['forum_tid'] = ['#type' => 'value', '#value' => $node->forum_tid];
334 335
  }

336
  if (isset($form['taxonomy_forums'])) {
337
    $widget =& $form['taxonomy_forums']['widget'];
338
    // Make the vocabulary required for 'real' forum-nodes.
339 340 341
    $widget['#required'] = TRUE;
    $widget['#multiple'] = FALSE;
    if (empty($widget['#default_value'])) {
342
      // If there is no default forum already selected, try to get the forum
343 344
      // 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).
345
      $requested_forum_id = \Drupal::request()->query->get('forum_id');
346
      $widget['#default_value'] = is_numeric($requested_forum_id) ? $requested_forum_id : '';
347
    }
348
  }
349 350
}

351
/**
352
 * Implements hook_preprocess_HOOK() for block templates.
353 354
 */
function forum_preprocess_block(&$variables) {
355
  if ($variables['configuration']['provider'] == 'forum') {
356
    $variables['attributes']['role'] = 'navigation';
357 358 359
  }
}

360 361 362 363
/**
 * Implements hook_theme_suggestions_HOOK().
 */
function forum_theme_suggestions_forums(array $variables) {
364
  $suggestions = [];
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
  $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;
}

387
/**
388
 * Prepares variables for forums templates.
389
 *
390 391 392
 * Default template: forums.html.twig.
 *
 * @param array $variables
393 394 395 396 397 398
 *   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.
399
 *   - term: Taxonomy term of the current forum.
400 401 402 403 404 405
 *   - 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.
406
 */
407
function template_preprocess_forums(&$variables) {
408
  $variables['tid'] = $variables['term']->id();
409
  if ($variables['forums_defined'] = count($variables['forums']) || count($variables['parents'])) {
410
    if (!empty($variables['forums'])) {
411
      $variables['forums'] = [
412 413 414 415
        '#theme' => 'forum_list',
        '#forums' => $variables['forums'],
        '#parents' => $variables['parents'],
        '#tid' => $variables['tid'],
416
      ];
417
    }
418

419
    if ($variables['term'] && empty($variables['term']->forum_container->value) && !empty($variables['topics'])) {
420
      $forum_topic_list_header = $variables['header'];
421

422
      $table = [
423 424
        '#theme' => 'table__forum_topic_list',
        '#responsive' => FALSE,
425 426 427 428
        '#attributes' => ['id' => 'forum-topic-' . $variables['tid']],
        '#header' => [],
        '#rows' => [],
      ];
429 430 431 432 433 434 435

      if (!empty($forum_topic_list_header)) {
        $table['#header'] = $forum_topic_list_header;
      }

      /** @var \Drupal\node\NodeInterface $topic */
      foreach ($variables['topics'] as $id => $topic) {
436
        $variables['topics'][$id]->icon = [
437 438 439 440 441 442
          '#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,
443
        ];
444 445 446 447 448 449

        // 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;
450
          $variables['topics'][$id]->title = $topic->getTitle();
451
          $variables['topics'][$id]->message = \Drupal::l(t('This topic has been moved'), new Url('forum.page', ['taxonomy_term' => $topic->forum_tid]));
452 453 454
        }
        else {
          $variables['topics'][$id]->moved = FALSE;
455
          $variables['topics'][$id]->title_link = \Drupal::l($topic->getTitle(), $topic->urlInfo());
456 457
          $variables['topics'][$id]->message = '';
        }
458 459 460 461 462 463 464 465
        $forum_submitted = [
          '#theme' => 'forum_submitted',
          '#topic' => (object) [
            'uid' => $topic->getOwnerId(),
            'name' => $topic->getOwner()->getDisplayName(),
            'created' => $topic->getCreatedTime(),
          ],
        ];
466
        $variables['topics'][$id]->submitted = \Drupal::service('renderer')->render($forum_submitted);
467
        $forum_submitted = [
468 469
          '#theme' => 'forum_submitted',
          '#topic' => isset($topic->last_reply) ? $topic->last_reply : NULL,
470
        ];
471
        $variables['topics'][$id]->last_reply = \Drupal::service('renderer')->render($forum_submitted);
472 473 474 475 476

        $variables['topics'][$id]->new_text = '';
        $variables['topics'][$id]->new_url = '';

        if ($topic->new_replies) {
477 478
          $page_number = \Drupal::entityManager()->getStorage('comment')
            ->getNewCommentPageNumber($topic->comment_count, $topic->new_replies, $topic, 'comment_forum');
479 480
          $query = $page_number ? ['page' => $page_number] : NULL;
          $variables['topics'][$id]->new_text = \Drupal::translation()->formatPlural($topic->new_replies, '1 new post<span class="visually-hidden"> in topic %title</span>', '@count new posts<span class="visually-hidden"> in topic %title</span>', ['%title' => $variables['topics'][$id]->label()]);
481
          $variables['topics'][$id]->new_url = \Drupal::url('entity.node.canonical', ['node' => $topic->id()], ['query' => $query, 'fragment' => 'new']);
482 483 484
        }

        // Build table rows from topics.
485 486 487
        $row = [];
        $row[] = [
          'data' => [
488
            $topic->icon,
489
            [
490
              '#markup' => '<div class="forum__title"><div>' . $topic->title_link . '</div><div>' . $topic->submitted . '</div></div>',
491 492 493 494
            ],
          ],
          'class' => ['forum__topic'],
        ];
495 496

        if ($topic->moved) {
497
          $row[] = [
498 499
            'data' => $topic->message,
            'colspan' => '2',
500
          ];
501 502 503 504 505 506 507
        }
        else {
          $new_replies = '';
          if ($topic->new_replies) {
            $new_replies = '<br /><a href="' . $topic->new_url . '">' . $topic->new_text . '</a>';
          }

508
          $row[] = [
509 510 511 512 513 514
            'data' => [
              [
                '#prefix' => $topic->comment_count,
                '#markup' => $new_replies,
              ],
            ],
515 516 517
            'class' => ['forum__replies'],
          ];
          $row[] = [
518
            'data' => $topic->last_reply,
519 520
            'class' => ['forum__last-reply'],
          ];
521 522 523 524
        }
        $table['#rows'][] = $row;
      }

525
      $variables['topics_original'] = $variables['topics'];
526
      $variables['topics'] = $table;
527
      $variables['topics_pager'] = [
528
        '#type' => 'pager',
529
      ];
530
    }
531 532 533
  }
}

534
/**
535
 * Prepares variables for forum list templates.
536
 *
537 538 539
 * Default template: forum-list.html.twig.
 *
 * @param array $variables
540 541 542 543 544 545
 *   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.
 *   - parents: An array of taxonomy term objects that are ancestors of the
 *     current term ID.
 *   - tid: Taxonomy term ID of the current forum.
546
 */
547
function template_preprocess_forum_list(&$variables) {
548
  $user = \Drupal::currentUser();
549
  $row = 0;
550 551
  // Sanitize each forum so that the template can safely print the data.
  foreach ($variables['forums'] as $id => $forum) {
552
    $variables['forums'][$id]->description = ['#markup' => $forum->description->value];
553
    $variables['forums'][$id]->link = forum_uri($forum);
554
    $variables['forums'][$id]->name = $forum->label();
555
    $variables['forums'][$id]->is_container = !empty($forum->forum_container->value);
556 557 558 559 560 561 562
    $variables['forums'][$id]->zebra = $row % 2 == 0 ? 'odd' : 'even';
    $row++;

    $variables['forums'][$id]->new_text = '';
    $variables['forums'][$id]->new_url = '';
    $variables['forums'][$id]->new_topics = 0;
    $variables['forums'][$id]->old_topics = $forum->num_topics;
563 564
    $variables['forums'][$id]->icon_class = 'default';
    $variables['forums'][$id]->icon_title = t('No new posts');
565
    if ($user->isAuthenticated()) {
566
      $variables['forums'][$id]->new_topics = \Drupal::service('forum_manager')->unreadTopics($forum->id(), $user->id());
567
      if ($variables['forums'][$id]->new_topics) {
568
        $variables['forums'][$id]->new_text = \Drupal::translation()->formatPlural($variables['forums'][$id]->new_topics, '1 new post<span class="visually-hidden"> in forum %title</span>', '@count new posts<span class="visually-hidden"> in forum %title</span>', ['%title' => $variables['forums'][$id]->label()]);
569
        $variables['forums'][$id]->new_url = \Drupal::url('forum.page', ['taxonomy_term' => $forum->id()], ['fragment' => 'new']);
570 571
        $variables['forums'][$id]->icon_class = 'new';
        $variables['forums'][$id]->icon_title = t('New posts');
Dries's avatar
Dries committed
572
      }
573
      $variables['forums'][$id]->old_topics = $forum->num_topics - $variables['forums'][$id]->new_topics;
574
    }
575
    $forum_submitted = ['#theme' => 'forum_submitted', '#topic' => $forum->last_post];
576
    $variables['forums'][$id]->last_reply = \Drupal::service('renderer')->render($forum_submitted);
577
  }
578

579
  $variables['pager'] = [
580
   '#type' => 'pager',
581
  ];
582 583

  // Give meaning to $tid for themers. $tid actually stands for term ID.
584 585
  $variables['forum_id'] = $variables['tid'];
  unset($variables['tid']);
586 587
}

588
/**
589
 * Prepares variables for forum icon templates.
590
 *
591 592 593
 * Default template: forum-icon.html.twig.
 *
 * @param array $variables
594 595 596 597 598 599 600
 *   An array containing the following elements:
 *   - new_posts: Indicates whether or not the topic contains new posts.
 *   - num_posts: The total number of posts in all topics.
 *   - comment_mode: An integer indicating whether comments are open, closed,
 *     or hidden.
 *   - sticky: Indicates whether the topic is sticky.
 *   - first_new: Indicates whether this is the first topic with new posts.
601
 */
602
function template_preprocess_forum_icon(&$variables) {
603
  $variables['hot_threshold'] = \Drupal::config('forum.settings')->get('topics.hot_threshold');
604

605
  if ($variables['num_posts'] > $variables['hot_threshold']) {
606
    $variables['icon_status'] = $variables['new_posts'] ? 'hot-new' : 'hot';
607
    $variables['icon_title'] = $variables['new_posts'] ? t('Hot topic, new comments') : t('Hot topic');
608 609
  }
  else {
610
    $variables['icon_status'] = $variables['new_posts'] ? 'new' : 'default';
611
    $variables['icon_title'] = $variables['new_posts'] ? t('New comments') : t('Normal topic');
612
  }
613

614
  if ($variables['comment_mode'] == CommentItemInterface::CLOSED || $variables['comment_mode'] == CommentItemInterface::HIDDEN) {
615
    $variables['icon_status'] = 'closed';
616
    $variables['icon_title'] = t('Closed topic');
617 618
  }

619
  if ($variables['sticky'] == 1) {
620
    $variables['icon_status'] = 'sticky';
621
    $variables['icon_title'] = t('Sticky topic');
622
  }
623 624

  $variables['attributes']['title'] = $variables['icon_title'];
625 626
}

627
/**
628
 * Prepares variables for forum submission information templates.
629 630 631
 *
 * The submission information will be displayed in the forum list and topic
 * list.
632
 *
633 634 635
 * Default template: forum-submitted.html.twig.
 *
 * @param array $variables
636 637
 *   An array containing the following elements:
 *   - topic: The topic object.
638
 */
639
function template_preprocess_forum_submitted(&$variables) {
640 641
  $variables['author'] = '';
  if (isset($variables['topic']->uid)) {
642
    $username = ['#theme' => 'username', '#account' => User::load($variables['topic']->uid)];
643
    $variables['author'] = \Drupal::service('renderer')->render($username);
644
  }
645
  $variables['time'] = isset($variables['topic']->created) ? \Drupal::service('date.formatter')->formatTimeDiffSince($variables['topic']->created) : '';
646
}