annotate core/modules/forum/src/ForumManager.php @ 2:92f882872392

Trusted hosts, + remove migration modules
author Chris Cannam
date Tue, 05 Dec 2017 09:26:43 +0000
parents 4c8ae668cc8c
children 129ea1e6d783
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\forum;
Chris@0 4
Chris@0 5 use Drupal\Core\Config\ConfigFactoryInterface;
Chris@0 6 use Drupal\Core\Database\Connection;
Chris@0 7 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
Chris@0 8 use Drupal\Core\Entity\EntityManagerInterface;
Chris@0 9 use Drupal\Core\Session\AccountInterface;
Chris@0 10 use Drupal\Core\StringTranslation\TranslationInterface;
Chris@0 11 use Drupal\Core\StringTranslation\StringTranslationTrait;
Chris@0 12 use Drupal\comment\CommentManagerInterface;
Chris@0 13 use Drupal\node\NodeInterface;
Chris@0 14
Chris@0 15 /**
Chris@0 16 * Provides forum manager service.
Chris@0 17 */
Chris@0 18 class ForumManager implements ForumManagerInterface {
Chris@0 19 use StringTranslationTrait;
Chris@0 20 use DependencySerializationTrait {
Chris@0 21 __wakeup as defaultWakeup;
Chris@0 22 __sleep as defaultSleep;
Chris@0 23 }
Chris@0 24
Chris@0 25 /**
Chris@0 26 * Forum sort order, newest first.
Chris@0 27 */
Chris@0 28 const NEWEST_FIRST = 1;
Chris@0 29
Chris@0 30 /**
Chris@0 31 * Forum sort order, oldest first.
Chris@0 32 */
Chris@0 33 const OLDEST_FIRST = 2;
Chris@0 34
Chris@0 35 /**
Chris@0 36 * Forum sort order, posts with most comments first.
Chris@0 37 */
Chris@0 38 const MOST_POPULAR_FIRST = 3;
Chris@0 39
Chris@0 40 /**
Chris@0 41 * Forum sort order, posts with the least comments first.
Chris@0 42 */
Chris@0 43 const LEAST_POPULAR_FIRST = 4;
Chris@0 44
Chris@0 45 /**
Chris@0 46 * Forum settings config object.
Chris@0 47 *
Chris@0 48 * @var \Drupal\Core\Config\ConfigFactoryInterface
Chris@0 49 */
Chris@0 50 protected $configFactory;
Chris@0 51
Chris@0 52 /**
Chris@0 53 * Entity manager service
Chris@0 54 *
Chris@0 55 * @var \Drupal\Core\Entity\EntityManagerInterface
Chris@0 56 */
Chris@0 57 protected $entityManager;
Chris@0 58
Chris@0 59 /**
Chris@0 60 * Database connection
Chris@0 61 *
Chris@0 62 * @var \Drupal\Core\Database\Connection
Chris@0 63 */
Chris@0 64 protected $connection;
Chris@0 65
Chris@0 66 /**
Chris@0 67 * The comment manager service.
Chris@0 68 *
Chris@0 69 * @var \Drupal\comment\CommentManagerInterface
Chris@0 70 */
Chris@0 71 protected $commentManager;
Chris@0 72
Chris@0 73 /**
Chris@0 74 * Array of last post information keyed by forum (term) id.
Chris@0 75 *
Chris@0 76 * @var array
Chris@0 77 */
Chris@0 78 protected $lastPostData = [];
Chris@0 79
Chris@0 80 /**
Chris@0 81 * Array of forum statistics keyed by forum (term) id.
Chris@0 82 *
Chris@0 83 * @var array
Chris@0 84 */
Chris@0 85 protected $forumStatistics = [];
Chris@0 86
Chris@0 87 /**
Chris@0 88 * Array of forum children keyed by parent forum (term) id.
Chris@0 89 *
Chris@0 90 * @var array
Chris@0 91 */
Chris@0 92 protected $forumChildren = [];
Chris@0 93
Chris@0 94 /**
Chris@0 95 * Array of history keyed by nid.
Chris@0 96 *
Chris@0 97 * @var array
Chris@0 98 */
Chris@0 99 protected $history = [];
Chris@0 100
Chris@0 101 /**
Chris@0 102 * Cached forum index.
Chris@0 103 *
Chris@0 104 * @var \Drupal\taxonomy\TermInterface
Chris@0 105 */
Chris@0 106 protected $index;
Chris@0 107
Chris@0 108 /**
Chris@0 109 * Constructs the forum manager service.
Chris@0 110 *
Chris@0 111 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
Chris@0 112 * The config factory service.
Chris@0 113 * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
Chris@0 114 * The entity manager service.
Chris@0 115 * @param \Drupal\Core\Database\Connection $connection
Chris@0 116 * The current database connection.
Chris@0 117 * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
Chris@0 118 * The translation manager service.
Chris@0 119 * @param \Drupal\comment\CommentManagerInterface $comment_manager
Chris@0 120 * The comment manager service.
Chris@0 121 */
Chris@0 122 public function __construct(ConfigFactoryInterface $config_factory, EntityManagerInterface $entity_manager, Connection $connection, TranslationInterface $string_translation, CommentManagerInterface $comment_manager) {
Chris@0 123 $this->configFactory = $config_factory;
Chris@0 124 $this->entityManager = $entity_manager;
Chris@0 125 $this->connection = $connection;
Chris@0 126 $this->stringTranslation = $string_translation;
Chris@0 127 $this->commentManager = $comment_manager;
Chris@0 128 }
Chris@0 129
Chris@0 130 /**
Chris@0 131 * {@inheritdoc}
Chris@0 132 */
Chris@0 133 public function getTopics($tid, AccountInterface $account) {
Chris@0 134 $config = $this->configFactory->get('forum.settings');
Chris@0 135 $forum_per_page = $config->get('topics.page_limit');
Chris@0 136 $sortby = $config->get('topics.order');
Chris@0 137
Chris@0 138 $header = [
Chris@0 139 ['data' => $this->t('Topic'), 'field' => 'f.title'],
Chris@0 140 ['data' => $this->t('Replies'), 'field' => 'f.comment_count'],
Chris@0 141 ['data' => $this->t('Last reply'), 'field' => 'f.last_comment_timestamp'],
Chris@0 142 ];
Chris@0 143
Chris@0 144 $order = $this->getTopicOrder($sortby);
Chris@0 145 for ($i = 0; $i < count($header); $i++) {
Chris@0 146 if ($header[$i]['field'] == $order['field']) {
Chris@0 147 $header[$i]['sort'] = $order['sort'];
Chris@0 148 }
Chris@0 149 }
Chris@0 150
Chris@0 151 $query = $this->connection->select('forum_index', 'f')
Chris@0 152 ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
Chris@0 153 ->extend('Drupal\Core\Database\Query\TableSortExtender');
Chris@0 154 $query->fields('f');
Chris@0 155 $query
Chris@0 156 ->condition('f.tid', $tid)
Chris@0 157 ->addTag('node_access')
Chris@0 158 ->addMetaData('base_table', 'forum_index')
Chris@0 159 ->orderBy('f.sticky', 'DESC')
Chris@0 160 ->orderByHeader($header)
Chris@0 161 ->limit($forum_per_page);
Chris@0 162
Chris@0 163 $count_query = $this->connection->select('forum_index', 'f');
Chris@0 164 $count_query->condition('f.tid', $tid);
Chris@0 165 $count_query->addExpression('COUNT(*)');
Chris@0 166 $count_query->addTag('node_access');
Chris@0 167 $count_query->addMetaData('base_table', 'forum_index');
Chris@0 168
Chris@0 169 $query->setCountQuery($count_query);
Chris@0 170 $result = $query->execute();
Chris@0 171 $nids = [];
Chris@0 172 foreach ($result as $record) {
Chris@0 173 $nids[] = $record->nid;
Chris@0 174 }
Chris@0 175 if ($nids) {
Chris@0 176 $nodes = $this->entityManager->getStorage('node')->loadMultiple($nids);
Chris@0 177
Chris@0 178 $query = $this->connection->select('node_field_data', 'n')
Chris@0 179 ->extend('Drupal\Core\Database\Query\TableSortExtender');
Chris@0 180 $query->fields('n', ['nid']);
Chris@0 181
Chris@0 182 $query->join('comment_entity_statistics', 'ces', "n.nid = ces.entity_id AND ces.field_name = 'comment_forum' AND ces.entity_type = 'node'");
Chris@0 183 $query->fields('ces', [
Chris@0 184 'cid',
Chris@0 185 'last_comment_uid',
Chris@0 186 'last_comment_timestamp',
Chris@0 187 'comment_count'
Chris@0 188 ]);
Chris@0 189
Chris@0 190 $query->join('forum_index', 'f', 'f.nid = n.nid');
Chris@0 191 $query->addField('f', 'tid', 'forum_tid');
Chris@0 192
Chris@0 193 $query->join('users_field_data', 'u', 'n.uid = u.uid AND u.default_langcode = 1');
Chris@0 194 $query->addField('u', 'name');
Chris@0 195
Chris@0 196 $query->join('users_field_data', 'u2', 'ces.last_comment_uid = u2.uid AND u.default_langcode = 1');
Chris@0 197
Chris@0 198 $query->addExpression('CASE ces.last_comment_uid WHEN 0 THEN ces.last_comment_name ELSE u2.name END', 'last_comment_name');
Chris@0 199
Chris@0 200 $query
Chris@0 201 ->orderBy('f.sticky', 'DESC')
Chris@0 202 ->orderByHeader($header)
Chris@0 203 ->condition('n.nid', $nids, 'IN')
Chris@0 204 // @todo This should be actually filtering on the desired node language
Chris@0 205 // and just fall back to the default language.
Chris@0 206 ->condition('n.default_langcode', 1);
Chris@0 207
Chris@0 208 $result = [];
Chris@0 209 foreach ($query->execute() as $row) {
Chris@0 210 $topic = $nodes[$row->nid];
Chris@0 211 $topic->comment_mode = $topic->comment_forum->status;
Chris@0 212
Chris@0 213 foreach ($row as $key => $value) {
Chris@0 214 $topic->{$key} = $value;
Chris@0 215 }
Chris@0 216 $result[] = $topic;
Chris@0 217 }
Chris@0 218 }
Chris@0 219 else {
Chris@0 220 $result = [];
Chris@0 221 }
Chris@0 222
Chris@0 223 $topics = [];
Chris@0 224 $first_new_found = FALSE;
Chris@0 225 foreach ($result as $topic) {
Chris@0 226 if ($account->isAuthenticated()) {
Chris@0 227 // A forum is new if the topic is new, or if there are new comments since
Chris@0 228 // the user's last visit.
Chris@0 229 if ($topic->forum_tid != $tid) {
Chris@0 230 $topic->new = 0;
Chris@0 231 }
Chris@0 232 else {
Chris@0 233 $history = $this->lastVisit($topic->id(), $account);
Chris@0 234 $topic->new_replies = $this->commentManager->getCountNewComments($topic, 'comment_forum', $history);
Chris@0 235 $topic->new = $topic->new_replies || ($topic->last_comment_timestamp > $history);
Chris@0 236 }
Chris@0 237 }
Chris@0 238 else {
Chris@0 239 // Do not track "new replies" status for topics if the user is anonymous.
Chris@0 240 $topic->new_replies = 0;
Chris@0 241 $topic->new = 0;
Chris@0 242 }
Chris@0 243
Chris@0 244 // Make sure only one topic is indicated as the first new topic.
Chris@0 245 $topic->first_new = FALSE;
Chris@0 246 if ($topic->new != 0 && !$first_new_found) {
Chris@0 247 $topic->first_new = TRUE;
Chris@0 248 $first_new_found = TRUE;
Chris@0 249 }
Chris@0 250
Chris@0 251 if ($topic->comment_count > 0) {
Chris@0 252 $last_reply = new \stdClass();
Chris@0 253 $last_reply->created = $topic->last_comment_timestamp;
Chris@0 254 $last_reply->name = $topic->last_comment_name;
Chris@0 255 $last_reply->uid = $topic->last_comment_uid;
Chris@0 256 $topic->last_reply = $last_reply;
Chris@0 257 }
Chris@0 258 $topics[$topic->id()] = $topic;
Chris@0 259 }
Chris@0 260
Chris@0 261 return ['topics' => $topics, 'header' => $header];
Chris@0 262
Chris@0 263 }
Chris@0 264
Chris@0 265 /**
Chris@0 266 * Gets topic sorting information based on an integer code.
Chris@0 267 *
Chris@0 268 * @param int $sortby
Chris@0 269 * One of the following integers indicating the sort criteria:
Chris@0 270 * - ForumManager::NEWEST_FIRST: Date - newest first.
Chris@0 271 * - ForumManager::OLDEST_FIRST: Date - oldest first.
Chris@0 272 * - ForumManager::MOST_POPULAR_FIRST: Posts with the most comments first.
Chris@0 273 * - ForumManager::LEAST_POPULAR_FIRST: Posts with the least comments first.
Chris@0 274 *
Chris@0 275 * @return array
Chris@0 276 * An array with the following values:
Chris@0 277 * - field: A field for an SQL query.
Chris@0 278 * - sort: 'asc' or 'desc'.
Chris@0 279 */
Chris@0 280 protected function getTopicOrder($sortby) {
Chris@0 281 switch ($sortby) {
Chris@0 282 case static::NEWEST_FIRST:
Chris@0 283 return ['field' => 'f.last_comment_timestamp', 'sort' => 'desc'];
Chris@0 284
Chris@0 285 case static::OLDEST_FIRST:
Chris@0 286 return ['field' => 'f.last_comment_timestamp', 'sort' => 'asc'];
Chris@0 287
Chris@0 288 case static::MOST_POPULAR_FIRST:
Chris@0 289 return ['field' => 'f.comment_count', 'sort' => 'desc'];
Chris@0 290
Chris@0 291 case static::LEAST_POPULAR_FIRST:
Chris@0 292 return ['field' => 'f.comment_count', 'sort' => 'asc'];
Chris@0 293
Chris@0 294 }
Chris@0 295 }
Chris@0 296
Chris@0 297 /**
Chris@0 298 * Gets the last time the user viewed a node.
Chris@0 299 *
Chris@0 300 * @param int $nid
Chris@0 301 * The node ID.
Chris@0 302 * @param \Drupal\Core\Session\AccountInterface $account
Chris@0 303 * Account to fetch last time for.
Chris@0 304 *
Chris@0 305 * @return int
Chris@0 306 * The timestamp when the user last viewed this node, if the user has
Chris@0 307 * previously viewed the node; otherwise HISTORY_READ_LIMIT.
Chris@0 308 */
Chris@0 309 protected function lastVisit($nid, AccountInterface $account) {
Chris@0 310 if (empty($this->history[$nid])) {
Chris@0 311 $result = $this->connection->select('history', 'h')
Chris@0 312 ->fields('h', ['nid', 'timestamp'])
Chris@0 313 ->condition('uid', $account->id())
Chris@0 314 ->execute();
Chris@0 315 foreach ($result as $t) {
Chris@0 316 $this->history[$t->nid] = $t->timestamp > HISTORY_READ_LIMIT ? $t->timestamp : HISTORY_READ_LIMIT;
Chris@0 317 }
Chris@0 318 }
Chris@0 319 return isset($this->history[$nid]) ? $this->history[$nid] : HISTORY_READ_LIMIT;
Chris@0 320 }
Chris@0 321
Chris@0 322 /**
Chris@0 323 * Provides the last post information for the given forum tid.
Chris@0 324 *
Chris@0 325 * @param int $tid
Chris@0 326 * The forum tid.
Chris@0 327 *
Chris@0 328 * @return \stdClass
Chris@0 329 * The last post for the given forum.
Chris@0 330 */
Chris@0 331 protected function getLastPost($tid) {
Chris@0 332 if (!empty($this->lastPostData[$tid])) {
Chris@0 333 return $this->lastPostData[$tid];
Chris@0 334 }
Chris@0 335 // Query "Last Post" information for this forum.
Chris@0 336 $query = $this->connection->select('node_field_data', 'n');
Chris@0 337 $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', [':tid' => $tid]);
Chris@0 338 $query->join('comment_entity_statistics', 'ces', "n.nid = ces.entity_id AND ces.field_name = 'comment_forum' AND ces.entity_type = 'node'");
Chris@0 339 $query->join('users_field_data', 'u', 'ces.last_comment_uid = u.uid AND u.default_langcode = 1');
Chris@0 340 $query->addExpression('CASE ces.last_comment_uid WHEN 0 THEN ces.last_comment_name ELSE u.name END', 'last_comment_name');
Chris@0 341
Chris@0 342 $topic = $query
Chris@0 343 ->fields('ces', ['last_comment_timestamp', 'last_comment_uid'])
Chris@0 344 ->condition('n.status', 1)
Chris@0 345 ->orderBy('last_comment_timestamp', 'DESC')
Chris@0 346 ->range(0, 1)
Chris@0 347 ->addTag('node_access')
Chris@0 348 ->execute()
Chris@0 349 ->fetchObject();
Chris@0 350
Chris@0 351 // Build the last post information.
Chris@0 352 $last_post = new \stdClass();
Chris@0 353 if (!empty($topic->last_comment_timestamp)) {
Chris@0 354 $last_post->created = $topic->last_comment_timestamp;
Chris@0 355 $last_post->name = $topic->last_comment_name;
Chris@0 356 $last_post->uid = $topic->last_comment_uid;
Chris@0 357 }
Chris@0 358
Chris@0 359 $this->lastPostData[$tid] = $last_post;
Chris@0 360 return $last_post;
Chris@0 361 }
Chris@0 362
Chris@0 363 /**
Chris@0 364 * Provides statistics for a forum.
Chris@0 365 *
Chris@0 366 * @param int $tid
Chris@0 367 * The forum tid.
Chris@0 368 *
Chris@0 369 * @return \stdClass|null
Chris@0 370 * Statistics for the given forum if statistics exist, else NULL.
Chris@0 371 */
Chris@0 372 protected function getForumStatistics($tid) {
Chris@0 373 if (empty($this->forumStatistics)) {
Chris@0 374 // Prime the statistics.
Chris@0 375 $query = $this->connection->select('node_field_data', 'n');
Chris@0 376 $query->join('comment_entity_statistics', 'ces', "n.nid = ces.entity_id AND ces.field_name = 'comment_forum' AND ces.entity_type = 'node'");
Chris@0 377 $query->join('forum', 'f', 'n.vid = f.vid');
Chris@0 378 $query->addExpression('COUNT(n.nid)', 'topic_count');
Chris@0 379 $query->addExpression('SUM(ces.comment_count)', 'comment_count');
Chris@0 380 $this->forumStatistics = $query
Chris@0 381 ->fields('f', ['tid'])
Chris@0 382 ->condition('n.status', 1)
Chris@0 383 ->condition('n.default_langcode', 1)
Chris@0 384 ->groupBy('tid')
Chris@0 385 ->addTag('node_access')
Chris@0 386 ->execute()
Chris@0 387 ->fetchAllAssoc('tid');
Chris@0 388 }
Chris@0 389
Chris@0 390 if (!empty($this->forumStatistics[$tid])) {
Chris@0 391 return $this->forumStatistics[$tid];
Chris@0 392 }
Chris@0 393 }
Chris@0 394
Chris@0 395 /**
Chris@0 396 * {@inheritdoc}
Chris@0 397 */
Chris@0 398 public function getChildren($vid, $tid) {
Chris@0 399 if (!empty($this->forumChildren[$tid])) {
Chris@0 400 return $this->forumChildren[$tid];
Chris@0 401 }
Chris@0 402 $forums = [];
Chris@0 403 $_forums = $this->entityManager->getStorage('taxonomy_term')->loadTree($vid, $tid, NULL, TRUE);
Chris@0 404 foreach ($_forums as $forum) {
Chris@0 405 // Merge in the topic and post counters.
Chris@0 406 if (($count = $this->getForumStatistics($forum->id()))) {
Chris@0 407 $forum->num_topics = $count->topic_count;
Chris@0 408 $forum->num_posts = $count->topic_count + $count->comment_count;
Chris@0 409 }
Chris@0 410 else {
Chris@0 411 $forum->num_topics = 0;
Chris@0 412 $forum->num_posts = 0;
Chris@0 413 }
Chris@0 414
Chris@0 415 // Merge in last post details.
Chris@0 416 $forum->last_post = $this->getLastPost($forum->id());
Chris@0 417 $forums[$forum->id()] = $forum;
Chris@0 418 }
Chris@0 419
Chris@0 420 $this->forumChildren[$tid] = $forums;
Chris@0 421 return $forums;
Chris@0 422 }
Chris@0 423
Chris@0 424 /**
Chris@0 425 * {@inheritdoc}
Chris@0 426 */
Chris@0 427 public function getIndex() {
Chris@0 428 if ($this->index) {
Chris@0 429 return $this->index;
Chris@0 430 }
Chris@0 431
Chris@0 432 $vid = $this->configFactory->get('forum.settings')->get('vocabulary');
Chris@0 433 $index = $this->entityManager->getStorage('taxonomy_term')->create([
Chris@0 434 'tid' => 0,
Chris@0 435 'container' => 1,
Chris@0 436 'parents' => [],
Chris@0 437 'isIndex' => TRUE,
Chris@0 438 'vid' => $vid
Chris@0 439 ]);
Chris@0 440
Chris@0 441 // Load the tree below.
Chris@0 442 $index->forums = $this->getChildren($vid, 0);
Chris@0 443 $this->index = $index;
Chris@0 444 return $index;
Chris@0 445 }
Chris@0 446
Chris@0 447 /**
Chris@0 448 * {@inheritdoc}
Chris@0 449 */
Chris@0 450 public function resetCache() {
Chris@0 451 // Reset the index.
Chris@0 452 $this->index = NULL;
Chris@0 453 // Reset history.
Chris@0 454 $this->history = [];
Chris@0 455 }
Chris@0 456
Chris@0 457 /**
Chris@0 458 * {@inheritdoc}
Chris@0 459 */
Chris@0 460 public function getParents($tid) {
Chris@0 461 return $this->entityManager->getStorage('taxonomy_term')->loadAllParents($tid);
Chris@0 462 }
Chris@0 463
Chris@0 464 /**
Chris@0 465 * {@inheritdoc}
Chris@0 466 */
Chris@0 467 public function checkNodeType(NodeInterface $node) {
Chris@0 468 // Fetch information about the forum field.
Chris@0 469 $field_definitions = $this->entityManager->getFieldDefinitions('node', $node->bundle());
Chris@0 470 return !empty($field_definitions['taxonomy_forums']);
Chris@0 471 }
Chris@0 472
Chris@0 473 /**
Chris@0 474 * {@inheritdoc}
Chris@0 475 */
Chris@0 476 public function unreadTopics($term, $uid) {
Chris@0 477 $query = $this->connection->select('node_field_data', 'n');
Chris@0 478 $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', [':tid' => $term]);
Chris@0 479 $query->leftJoin('history', 'h', 'n.nid = h.nid AND h.uid = :uid', [':uid' => $uid]);
Chris@0 480 $query->addExpression('COUNT(n.nid)', 'count');
Chris@0 481 return $query
Chris@0 482 ->condition('status', 1)
Chris@0 483 // @todo This should be actually filtering on the desired node status
Chris@0 484 // field language and just fall back to the default language.
Chris@0 485 ->condition('n.default_langcode', 1)
Chris@0 486 ->condition('n.created', HISTORY_READ_LIMIT, '>')
Chris@0 487 ->isNull('h.nid')
Chris@0 488 ->addTag('node_access')
Chris@0 489 ->execute()
Chris@0 490 ->fetchField();
Chris@0 491 }
Chris@0 492
Chris@0 493 /**
Chris@0 494 * {@inheritdoc}
Chris@0 495 */
Chris@0 496 public function __sleep() {
Chris@0 497 $vars = $this->defaultSleep();
Chris@0 498 // Do not serialize static cache.
Chris@0 499 unset($vars['history'], $vars['index'], $vars['lastPostData'], $vars['forumChildren'], $vars['forumStatistics']);
Chris@0 500 return $vars;
Chris@0 501 }
Chris@0 502
Chris@0 503 /**
Chris@0 504 * {@inheritdoc}
Chris@0 505 */
Chris@0 506 public function __wakeup() {
Chris@0 507 $this->defaultWakeup();
Chris@0 508 // Initialize static cache.
Chris@0 509 $this->history = [];
Chris@0 510 $this->lastPostData = [];
Chris@0 511 $this->forumChildren = [];
Chris@0 512 $this->forumStatistics = [];
Chris@0 513 $this->index = NULL;
Chris@0 514 }
Chris@0 515
Chris@0 516 }