aboutsummaryrefslogtreecommitdiff
path: root/bootstrap/comments/backend/classes/hashover.php
diff options
context:
space:
mode:
Diffstat (limited to 'bootstrap/comments/backend/classes/hashover.php')
-rw-r--r--bootstrap/comments/backend/classes/hashover.php440
1 files changed, 440 insertions, 0 deletions
diff --git a/bootstrap/comments/backend/classes/hashover.php b/bootstrap/comments/backend/classes/hashover.php
new file mode 100644
index 0000000..3ab0fdd
--- /dev/null
+++ b/bootstrap/comments/backend/classes/hashover.php
@@ -0,0 +1,440 @@
+<?php
+
+// Copyright (C) 2015-2018 Jacob Barkdull
+// This file is part of HashOver.
+//
+// HashOver is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// HashOver is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with HashOver. If not, see <http://www.gnu.org/licenses/>.
+
+
+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;
+ }
+}