. class HashOver { public $usage = array (); public $statistics; public $misc; public $setup; public $thread; public $locale; public $commentParser; public $markdown; public $cookies; public $commentCount; public $popularList = array (); public $popularCount = 0; public $rawComments = array (); public $comments = array (); public $ui; public $templater; public function __construct ($mode = 'php', $context = 'normal') { // Store usage context information $this->usage['mode'] = $mode; $this->usage['context'] = $context; // Instantiate and start statistics $this->statistics = new HashOver\Statistics ($mode); $this->statistics->executionStart (); // Instantiate general setup class $this->setup = new HashOver\Setup ($this->usage); // Instantiate class for reading comments $this->thread = new HashOver\Thread ($this->setup); // Instantiate class of miscellaneous functions $this->misc = new HashOver\Misc ($mode); } public function getCommentCount ($locale_key = 'showing-comments') { // Shorter variables $primary_count = $this->thread->primaryCount; $total_count = $this->thread->totalCount; // Subtract deleted comment counts if ($this->setup->countIncludesDeleted === false) { $primary_count -= $this->thread->primaryDeletedCount; $total_count -= $this->thread->totalDeletedCount; } // Decide which locale to use; Exclude "Showing" in API usages $locale_key = ($this->usage['context'] === 'api') ? 'count-link' : $locale_key; // Decide if comment count is pluralized $prime_plural = ($primary_count !== 2) ? 1 : 0; // Get appropriate locale $showing_comments_locale = $this->locale->text[$locale_key]; $showing_comments = $showing_comments_locale[$prime_plural]; // Whether to show reply count separately if ($this->setup->showsReplyCount === true) { // If so, inject top level comment count into count locale string $comment_count = sprintf ($showing_comments, $primary_count - 1); // Check if there are any replies if ($total_count !== $primary_count) { // If so, decide if reply count is pluralized $count_diff = $total_count - $primary_count; $reply_plural = ($count_diff !== 1) ? 1 : 0; // Get appropriate locale $reply_locale = $this->locale->text['count-replies'][$reply_plural]; // Inject total comment count into reply count locale string $reply_count = sprintf ($reply_locale, $total_count - 1); // And append reply count $comment_count .= ' (' . $reply_count . ')'; } // And return count with separate reply count return $comment_count; } // Otherwise inject total comment count into count locale string return sprintf ($showing_comments, $total_count - 1); } // Begin initialization work public function initiate () { // Query a list of comments $this->thread->queryComments (); // Where to stop reading comments if ($this->usage['mode'] !== 'php' and $this->setup->collapsesComments !== false and $this->setup->popularityLimit <= 0 and $this->setup->usesAjax !== false) { // Use collapse limit when collapsing and AJAX is enabled $end = $this->setup->collapseLimit; } else { // Otherwise read all comments $end = null; } // TODO: Fix structure when using starting point $this->rawComments = $this->thread->read (0, $end); // Instantiate locales class $this->locale = new HashOver\Locale ($this->setup); // Instantiate cookies class $this->cookies = new HashOver\Cookies ($this->setup); // Instantiate login class $this->login = new HashOver\Login ($this->setup); // Instantiate comment parser class $this->commentParser = new HashOver\CommentParser ($this->setup); // Generate comment count $this->commentCount = $this->getCommentCount (); // Instantiate markdown class $this->markdown = new HashOver\Markdown (); } // Save various metadata about the page public function defaultMetadata () { // "localhost" equivalent addresses $addresses = array ('127.0.0.1', '::1', 'localhost'); // Check if local metadata is disabled if ($this->setup->allowLocalMetadata !== true) { // If so, do nothing if we're on localhost if (in_array ($_SERVER['REMOTE_ADDR'], $addresses, true)) { return; } } // Attempt to save default page metadata $this->thread->data->saveMeta ('page-info', array ( 'url' => $this->setup->pageURL, 'title' => $this->setup->pageTitle )); } // Get reply array from comments via key protected function &getRepliesLevel (&$level, $level_count, &$key_parts) { for ($i = 1; $i < $level_count; $i++) { if (isset ($level)) { $level =& $level['replies'][$key_parts[$i] - 1]; } } return $level; } // Adds a comment to the popular list if it has enough likes protected function checkPopularity (array $comment, $key, array $key_parts) { $popularity = 0; // Add number of likes to popularity value if (!empty ($comment['likes'])) { $popularity += $comment['likes']; } // Subtract number of dislikes to popularity value if ($this->setup->allowsDislikes === true) { if (!empty ($comment['dislikes'])) { $popularity -= $comment['dislikes']; } } // Add comment to popular comments list if popular enough if ($popularity >= $this->setup->popularityThreshold) { $this->popularList[] = array ( 'popularity' => $popularity, 'comment' => $comment, 'key' => $key, 'parts' => $key_parts ); } } // Parse primary comments public function parsePrimary ($start = 0) { // Initial comments array $this->comments['primary'] = array (); // If no comments were found, setup a default message comment // if ($this->thread->totalCount <= 1) { // $this->comments['primary'][] = array ( // 'title' => $this->locale->text['be-first-name'], // 'avatar' => $this->setup->getImagePath ('first-comment'), // 'permalink' => 'c1', // 'notice' => $this->locale->text['be-first-note'], // 'notice-class' => 'hashover-first' // ); // return; // } // Last existing comment date for sorting deleted comments $last_date = 0; // Allowed comment count $allowed_count = 0; // Where to stop reading comments if ($this->usage['mode'] !== 'php' and $this->setup->collapsesComments !== false and $this->setup->usesAjax !== false) { // Use collapse limit when collapsing and AJAX is enabled $end = $this->setup->collapseLimit; } else { // Otherwise read all comments $end = null; } // Run all comments through parser foreach ($this->rawComments as $key => $comment) { $key_parts = explode ('-', $key); $indentions = count ($key_parts); $status = 'approved'; // Check comment's popularity if ($this->setup->popularityLimit > 0) { $this->checkPopularity ($comment, $key, $key_parts); } // Stop parsing after end point if ($end !== null and $allowed_count >= $end) { continue; } if ($indentions > 1 and $this->setup->streamDepth > 0) { $level =& $this->comments['primary'][$key_parts[0] - 1]; if ($this->setup->replyMode === 'stream' and $indentions > $this->setup->streamDepth) { $level =& $this->getRepliesLevel ($level, $this->setup->streamDepth, $key_parts); $level =& $level['replies'][]; } else { $level =& $this->getRepliesLevel ($level, $indentions, $key_parts); } } else { $level =& $this->comments['primary'][]; } // Set status to what's stored in the comment if (!empty ($comment['status'])) { $status = $comment['status']; } switch ($status) { // Parse as pending notice, viewable and editable by owner and admin case 'pending': { $parsed = $this->commentParser->parse ($comment, $key, $key_parts, false); if (!isset ($parsed['editable'])) { $level = $this->commentParser->notice ('pending', $key, $last_date); break; } $last_date = $parsed['sort-date']; $level = $parsed; break; } // Parse as deletion notice, viewable and editable by admin case 'deleted': { if ($this->login->userIsAdmin === true) { $level = $this->commentParser->parse ($comment, $key, $key_parts, false); $last_date = $level['sort-date']; } else { $level = $this->commentParser->notice ('deleted', $key, $last_date); } break; } // Parse as deletion notice, non-existent comment case 'missing': { $level = $this->commentParser->notice ('deleted', $key, $last_date); break; } // Parse as an unknown/error notice case 'read-error': { $level = $this->commentParser->notice ('error', $key, $last_date); break; } // Otherwise parse comment normally default: { $comment['status'] = 'approved'; $level = $this->commentParser->parse ($comment, $key, $key_parts); $last_date = $level['sort-date']; break; } } $allowed_count++; } // Reset array keys $this->comments['primary'] = array_values ($this->comments['primary']); } // Parse popular comments public function parsePopular () { // Initial popular comments array $this->comments['popular'] = array (); // If no comments or popularity limit is 0, return void if ($this->thread->totalCount <= 1 or $this->setup->popularityLimit <= 0) { return; } // Sort popular comments usort ($this->popularList, function ($a, $b) { return ($b['popularity'] > $a['popularity']); }); // Calculate how many popular comments will be shown $limit = $this->setup->popularityLimit; $count = count ($this->popularList); $this->popularCount = min ($limit, $count); // Run through each popular comment for ($i = 0; $i < $this->popularCount; $i++) { $item =& $this->popularList[$i]; // Parse comment $parsed = $this->commentParser->parse ($item['comment'], $item['key'], $item['parts'], true); // And add it to popular comments $this->comments['popular'][$i] = $parsed; } } // Do final initialization work public function finalize () { // Expire various temporary cookies $this->cookies->clear (); // Various comment count numbers $commentCounts = array ( 'show-count' => $this->commentCount, 'primary' => $this->thread->primaryCount, 'total' => $this->thread->totalCount, 'popular' => $this->popularCount ); // Instantiate UI output class $this->ui = new HashOver\CommentsUI ( $this->setup, $commentCounts ); // Instantiate comment theme templater class $this->templater = new HashOver\Templater ( $this->usage['mode'], $this->setup ); } // Display all comments as HTML public function displayComments () { // Set/update default page metadata $this->defaultMetadata (); // Instantiate PHP mode class $phpmode = new HashOver\PHPMode ( $this->setup, $this->ui, $this->comments ); // Run popular comments through parser if (!empty ($this->comments['popular'])) { foreach ($this->comments['popular'] as $comment) { $this->ui->popularComments .= $phpmode->parseComment ($comment, null, true) . PHP_EOL; } } // Run primary comments through parser if (!empty ($this->comments['primary'])) { foreach ($this->comments['primary'] as $comment) { $this->ui->comments .= $phpmode->parseComment ($comment, null) . PHP_EOL; } } // Start UI output with initial HTML $html = $this->ui->initialHTML (); // End statistics and add them as code comment $html .= $this->statistics->executionEnd (); // Return final HTML return $html; } }