Your IP : 216.73.216.224


Current Path : /var/www/html/administrator/components/com_komento/includes/comment/
Upload File :
Current File : /var/www/html/administrator/components/com_komento/includes/comment/comment.php

<?php
/**
* @package		Komento
* @copyright	Copyright (C) Stack Ideas Sdn Bhd. All rights reserved. 
* @license		GNU/GPL, see LICENSE.php
* Komento is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYRIGHT.php for copyright notices and details.
*/
defined('_JEXEC') or die('Unauthorized Access');

use Foundry\Helpers\StringHelper;

class KomentoComment extends KomentoBase
{
	private $table = null;
	public $error = null;

	public $rownumber = null;
	public $likes = null;
	public $dislikes = null;
	public $liked = null;
	public $disliked = null;
	public $reported = null;
	public $pid = null;
	public $childs = null;
	public $_original = null;
	public $componenttitle = '';
	public $contenttitle = '';
	public $extension = null;
	public $reports = null;
	public $likedUsers = null;
	public $dislikedUsers = null;
	public $author = null;
	public $authorName = null;
	public $authorLink = null;
	public $itemTitle = null;
	public $permalink = null;

	public function __construct($comment = null, $options = [])
	{
		parent::__construct();

		$resetCache = false;
		$cache = true;

		// Determines if the object cache should be cleared.
		if (isset($options['resetCache'])) {
			$resetCache = (bool) $options['resetCache'];
		}

		// Determines if the object should be cached
		if (isset($options['cache'])) {
			$cache = (bool) $options['cache'];
		}

		// we know we want to 'preload' or cache the posts.
		// if ($cache && is_array($comment)) {
		// 	// TODO:: preload comments
		//     $this->loadBatchComments($comment);
		//     return;
		// }

		// The $_comment must always be a table.
		$this->table = KT::table('Comments');

		// If passed in argument is an integer, we load it
		if (is_numeric($comment)) {

			$cacheExists = KT::cache()->exists($comment, 'comment');

			// When the post object has already been loaded before, just reuse it
			if ($cacheExists) {
				$cache = KT::cache()->get($comment, 'comment');

				$this->table = $cache;
				$this->bindProperty($cache);
			}

			// When cache doesn't exist, try to load the post
			if (!$cacheExists && $comment != 0) {
				$this->table->load($comment);
				$this->childs = $this->table->initRepliesCount();
			}
		}

		// If passed in argument is already a comments jtable, just assign it.
		if ($comment instanceof KomentoTableComments) {
			$this->table = $comment;
		}

		if (is_object($comment)) {

			if (!$comment instanceof KomentoTableComments) {
				$this->table = KT::table('Comments');
				$this->table->bind($comment);
			}

			$this->bindProperty($comment);
		}

		// keep a copy of original data
		$this->_original = clone $this->table;

	}

	private function bindProperty($source)
	{
		if (isset($source->rownumber)) { $this->rownumber = $source->rownumber;}
		if (isset($source->liked)) { $this->liked = $source->liked;}
		if (isset($source->disliked)) { $this->disliked = $source->disliked;}
		if (isset($source->reported)) { $this->reported = $source->reported;}
		if (isset($source->pid)) { $this->pid = $source->pid;}
		if (isset($source->childs)) { $this->childs = $source->childs;}
	}

	/**
	 * method to load the comments in batch processing
	 *
	 * @since   3.0
	 * @access  private
	 */
	private function loadBatchComments($comments)
	{
		$ids = [];

		foreach ($comments as $item) {
			if (is_numeric($item)) {
				$ids[] = $item;
			}
		}

		if ($ids) {
			// posts
			$model = KT::model('Comments');
			$comments = $model->loadBatchComments($ids);
		}

		KT::cache()->cacheComments($comments);
	}

	/**
	 * Expands a comment
	 *
	 * @since	3.1.0
	 * @access	public
	 */
	public function expand()
	{
		$params = $this->getParams();
		$params->set('minimized', 0);

		$this->table->params = $params->toString();
		return $this->table->store();
	}

	/**
	 * Minimizes a comment
	 *
	 * @since	3.1.0
	 * @access	public
	 */
	public function minimize()
	{
		$params = $this->getParams();
		$params->set('minimized', 1);

		$this->table->params = $params->toString();
		return $this->table->store();
	}

	/**
	 * Magic method to get properties which don't exist on this object but on the table
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function __get($key)
	{
		if (property_exists('KomentoTableComments', $key)) {

			return $this->table->$key;
		}

		if (property_exists($this, $key)) {
			return $this->$key;
		}

		return $this->table->$key;
	}

	/**
	 * Magic method to set properties which don't exist on this object but on the table
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function __set($key, $value = '')
	{
		if (property_exists('KomentoTableComments', $key)) {
			$this->table->$key = $value;
		} else {
			$this->$key = $value;
		}
	}

	/**
	 * Binds the given data to the table
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function bind($data = array(), $allowBindingId = false, $options = [])
	{
		// Perhaps an object is passed in
		if (!is_array($data)) {
			$data = (array) $data;
		}

		// this is a form post. let clean up the data.
		if (isset($data['Itemid']) && $data['Itemid']) {
			$this->postDataCleanup($data);
		}

		// Do not allow caller to bind id as this is a security issue
		if (isset($data['id']) && !$allowBindingId) {
			unset($data['id']);
		}

		// before normalize, we need to bind the data
		$state = $this->table->bind($data, true);

		// lets genereate the preview column
		if ($this->table->comment) {
			$this->table->preview = KT::parser()->parseComment($this->table->comment);
		}

		if (!isset($options['normalizeData'])) {
			$options['normalizeData'] = true;
		}

		// After binding the data, we also need to normalize the data
		if ($options['normalizeData']) {
			$this->normalizeData($options);
		}

		return $state;
	}

	private function postDataCleanup($data)
	{
		if (isset($data['Itemid'])) {
			unset($data['Itemid']);
		}

		if (isset($data['option'])) {
			unset($data['option']);
		}

		if (isset($data['_ts'])) {
			unset($data['_ts']);
		}

		if (isset($data['format'])) {
			unset($data['format']);
		}

		if (isset($data['no_html'])) {
			unset($data['no_html']);
		}

		if (isset($data['task'])) {
			unset($data['task']);
		}

		if (isset($data['namespace'])) {
			unset($data['namespace']);
		}
	}

	/**
	 * Overrides the parent's store behavior
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function save($options = [])
	{
		// Get any save options if available.
		$this->saveOptions = $options;

		if (!isset($this->saveOptions['isEdited'])) {
			$this->saveOptions['isEdited'] = false;
		}

		if (!isset($this->saveOptions['processPostSave'])) {
			$this->saveOptions['processPostSave'] = true;
		}

		// This allows us to perform necessary logics before the post is really saved
		if (!isset($this->saveOptions['ignorePreSave'])) {
			$this->preSave();
		}

		// Now we can store this in the db
		$state = $this->table->store();

		// Try to store the post.
		if (!$state) {
			$this->setError($this->table->getError());
			return false;
		}

		// This allows us to perform necessary logics after the post is really saved.
		if ($this->saveOptions['processPostSave']) {
			$this->postSave();
		}

		return $state;
	}

	/**
	 * Notify user that has been mentioned in post
	 *
	 * @since   4.0
	 * @access  public
	 */
	public function notifyNames()
	{
		$names = KT::string()->detectNames($this->table->comment);

		if (!$names) {
			return;
		}

		// if the avatar integrated with Easysocial, we notify using ES system instead
		if ($this->config->get('layout_avatar_integration') === 'easysocial') {
			KT::easysocial()->notifyNames($names, $this);
			return;
		} 

		$author = $this->getAuthor();

		$data['commentDate'] = $this->getCreatedDate()->format(JText::_('DATE_FORMAT_LC3'));
		$data['commentPermalink'] = $this->getPermalink();;
		$data['commentContent'] = $this->getContent();
		$data['commentAuthorName'] = $author->getName();

		$subject = JText::sprintf('COM_KT_NOTIFICATION_MENTIONED_TITLE', $author->getName(), $this->getItemTitle());
		$template = 'site/emails/comment.mention';

		foreach ($names as $recipient) {
			KT::notification()->insertMailQueue($subject, 'site/emails/comment.mention', $data, $recipient, false);
		}
	}

	/**
	 * Determines if users can reply to this comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function canReplyTo()
	{
		// Replies has been disabled or user doesn't have permissions
		if (!$this->config->get('enable_reply') || !$this->profile->allow('reply_comment')) {
			return false;
		}

		// The depth has already reached maximum level, users shouldn't be able to reply to this comment.
		$maximumLevel = $this->config->get('max_threaded_level');

		if ($maximumLevel != 0 && $this->table->depth > ($maximumLevel - 1)) {
			return false;
		}

		return true;
	}

	/**
	 * Determines if users can report this comment
	 *
	 * @since	4.0
	 * @access	public
	 */
	public function canReport()
	{
		return KT::reports()->isEnabled() && !$this->isMine();
	}

	/**
	 * Determines if the current viewer can manage the comment item
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function canManage()
	{
		if (KT::isSiteAdmin()) {
			return true;
		}

		// Guests are explicitly disallowed
		if ($this->my->guest) {
			return false;
		}

		if ($this->canEdit() || $this->canDelete() || $this->canFeature() || $this->canUnpublish() || $this->canPublish() || $this->canMinimize()) {
			return true;
		}

		return false;
	}

	/**
	 * Determines if the viewer can edit the comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function canEdit()
	{
		// Guests are explicitly disallowed
		if ($this->my->guest) {
			return false;
		}

		if (KT::isSiteAdmin()) {
			return true;
		}

		if ($this->access->allow('edit', $this)) {
			return true;
		}

		return false;
	}

	/**
	 * Determines if the viewer can delete the comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function canDelete()
	{
		// Guests are explicitly disallowed
		if ($this->my->guest) {
			return false;
		}

		if (KT::isSiteAdmin()) {
			return true;
		}

		if ($this->access->allow('delete', $this)) {
			return true;
		}

		return false;
	}

	/**
	 * Determines if the viewer can pin the comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function canFeature()
	{
		// Guests are explicitly disallowed
		if ($this->my->guest) {
			return false;
		}

		if ($this->parent_id) {
			return false;
		}

		if (KT::isSiteAdmin()) {
			return true;
		}

		if ($this->access->allow('stick', $this)) {
			return true;
		}

		return false;
	}

	/**
	 * Determines if the viewer can minimize a comment
	 *
	 * @since	3.1.0
	 * @access	public
	 */
	public function canMinimize()
	{
		if (!$this->config->get('enable_minimize')) {
			return false;
		}
		
		if ($this->my->guest) {
			return false;
		}

		if (KT::isSiteAdmin() || $this->access->allow('minimize', $this)) {
			return true;
		}

		return false;
	}

	/**
	 * Determines if the viewer can unpublish the comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function canUnpublish()
	{
		// Guests are explicitly disallowed
		if ($this->my->guest) {
			return false;
		}

		if (KT::isSiteAdmin()) {
			return true;
		}

		if ($this->access->allow('unpublish', $this)) {
			return true;
		}

		return false;
	}

	/**
	 * Determines if the viewer can submit the comment as spam
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function canSubmitAkismet()
	{
		if ($this->config->get('antispam_akismet_key') && KT::isSiteAdmin()) {
			return true;
		}

		return false;
	}

	/**
	 * Determines if the viewer can submit the comment as spam
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function canSubmitSpam()
	{
		if (KT::isSiteAdmin()) {
			return true;
		}

		return false;
	}

	/**
	 * Determines if the viewer can unpublish the comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function canPublish()
	{
		if (KT::isSiteAdmin()) {
			return true;
		}

		if ($this->access->allow('publish', $this)) {
			return true;
		}

		return false;
	}

	/**
	 * Ensures that the user is allowed to post comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function canPostComments()
	{
		// The user needs to be able to have the appropriate rights
		if (!$this->profile->allow('add_comment')) {
			return false;
		}

		// Ensure that the user isn't flooding
		if ($this->config->get('antispam_flood_control')) {
			$session = KT::session();
			$lastReplied = $session->getLastReplyTime();

			$now = JFactory::getDate();
			$difference = $now->toUnix() - $lastReplied;

			if ($difference && $difference <= $this->config->get('antispam_flood_interval')) {
				$this->setError('COM_KOMENTO_FORM_NOTIFICATION_FLOOD');
				return false;
			}
		}

		return true;
	}

	/**
	 * Publish / Unpublish a comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function publish($type = KOMENTO_STATE_PUBLISHED)
	{
		$model = KT::model('Comments');
		$app = $this->getApplication();

		// We know this comment is being published if the current state is KOMENTO_COMMENT_MODERATE
		$isBeingPublished = $this->table->published == KOMENTO_COMMENT_MODERATE && $type === KOMENTO_STATE_PUBLISHED;

		// Unpublish all the childs first
		$childs = $model->getChilds($this->table->id);
		$hasErrors = false;

		if ($childs) {
			foreach ($childs as $childId) {
				$child = KT::comment($childId);

				$state = $child->publish($type);

				if (!$state) {
					$this->setError($child->getError());
					$hasErrors = true;
				}
			}
		}

		// When there are errors unpublish the childs, we should skip this
		if ($hasErrors) {
			return false;
		}

		$now = JFactory::getDate()->toSql();

		$this->table->published = $type;

		// Allow plugins to prevent publish / unpublishing of comments
		$triggerResult = true;
		$triggerArgs = ['component' => $this->table->component, 'cid' => $this->table->cid, 'comment' => &$this];

		if ($type === KOMENTO_STATE_PUBLISHED) {
			$this->table->publish_up = $now;
			$triggerResult = KT::trigger('onBeforePublishComment', $triggerArgs);
		}

		if ($type === KOMENTO_STATE_UNPUBLISHED) {
			$this->table->publish_down = $now;
			$triggerResult = KT::trigger('onBeforeUnpublishComment', $triggerArgs);
		}

		if ($triggerResult === false) {
			$this->setError('Trigger onBeforePublishComment/onBeforeUnpublishComment false');
			return false;
		}

		// Try to save now
		if (!$this->table->store()) {
			$this->setError($this->table->getError());

			return false;
		}

		// Allow plugins to perform actions after the publish / unpublishing of comments
		$triggerArgs = ['component' => $this->table->component, 'cid' => $this->table->cid, 'comment' => &$this];

		if ($type === KOMENTO_STATE_PUBLISHED) {
			KT::trigger('onAfterPublishComment', $triggerArgs);
		}

		if ($type === KOMENTO_STATE_UNPUBLISHED) {
			KT::trigger('onAfterUnpublishComment', $triggerArgs);
		}

		if ($isBeingPublished) {
			$action = $this->table->parent_id ? 'reply' : 'comment';

			// send email
			if ($this->config->get('notification_enable')) {
				if (($action == 'comment' && $this->config->get('notification_event_new_comment')) || ($action == 'reply' && $this->config->get('notification_event_new_reply'))) {
					KT::notification()->push($action, 'subscribers,author,usergroups', ['commentId' => $this->table->id]);
				}
			}

			// Add activity
			$activity = KT::activity()->process($action, $this->table->id);

			// Manually get the attachment of this comment and process the "upload" activity
			$attachments = $this->getAttachments();

			foreach ($attachments as $attachment) {
				KT::activity()->process('upload', $this);
			}
		}

		return true;
	}

	/**
	 * Pins a comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function feature()
	{
		$model = KT::model('Comments');
		$state = $model->stick($this->table->id);

		if (!$state) {
			$this->setError($model->getError());
			return false;
		}

		// need to reload the feature flag
		$this->table->sticked = 1;

		return true;
	}

	/**
	 * spam a comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function spam()
	{
		$this->table->published = KOMENTO_COMMENT_SPAM;

		// Try to save now
		if (!$this->table->store()) {
			$this->setError($this->table->getError());

			return false;
		}

		// need to clear the report action as well
		$this->removeReport();

		return true;
	}

	/**
	 * flag a comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function flag($type = 0)
	{
		$this->table->flag = $type;

		// Try to save now
		if (!$this->table->store()) {
			$this->setError($this->table->getError());

			return false;
		}

		return true;
	}

	/**
	 * Unpins a comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function unfeature()
	{
		$model = KT::model('Comments');
		$state = $model->unstick($this->table->id);

		if (!$state) {
			$this->setError($model->getError());
			return false;
		}

		// need to reload the feature flag
		$this->table->sticked = 0;

		return true;
	}

	/**
	 * Normalize the data
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function normalizeData($options = [])
	{
		$fromMigration = isset($options['fromMigration']) ? $options['fromMigration'] : '';

		// Normalize the comment before allowing it to save
		if (is_null($this->table->ip) || !$this->table->ip) {
			$ip = @$_SERVER['REMOTE_ADDR'];

			// Some people might be behind a proxy
			if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) {
				$ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
				$ip = array_pop($ip);
			}

			$this->table->ip = $ip;
		}

		$now = JFactory::getDate()->toSql();

		// Normalize the depth of the comment
		if (is_null($this->table->depth)) {
			$this->table->depth = 0;
		}

		// If user id isn't supplied, try to get the current user
		if (is_null($this->table->created_by) && $this->my->id) {
			$this->table->created_by = (int) $this->my->id;
		}

		// If name isn't specified, use the logged in user's name or Guest if not logged in
		if (!$this->table->name) {
			$this->table->name = $this->my->id ? $this->profile->getName() : JText::_('COM_KOMENTO_GUEST');
		}

		// If e-mail isn't specified, use logged in user's email
		if (!$this->table->email && $this->my->id) {
			$this->table->email = $this->profile->email;
		}

		// If creation date isn't supplied, we need to set our own
		if (is_null($this->table->created)) {
			$this->table->created = $now;
		}

		// skip this if the process from migration, because it should respect the publish state from those comment data
		if (!$fromMigration) {
			
			// Default publishing state is enabled
			$this->table->published = 1;

			// Check if the poster's ip address matches any blacklisted ip addresses so that we can enforce moderation state on it
			$blacklist = trim($this->config->get('blacklist_ip', ''));

			if (!empty($blacklist) && $this->table->ip) {
				$blacklist = explode(',', $blacklist);

				if (in_array($this->table->ip, $blacklist)) {
					$this->table->published = 2;
				}
			}

			// Determines if the post should be moderated first
			if ($this->profile->toModerate()) {
				$this->table->published = 2;
			}
		}

		// Honeypot traps
		if ($this->isHoneypotTrapped()) {
			$this->table->published = KOMENTO_COMMENT_SPAM;
			$this->setParam('spam', KT_SPAM_HONEYPOT);
		}

		// Cleantalk antispam detection
		if (KT::cleantalk()->isEnabled() && $this->getCleanTalkSpamLevel() == KOMENTO_CLEANTALK_POSSIBLE_SPAM) {
			$this->table->published = KOMENTO_COMMENT_SPAM;
			$this->setParam('spam', KT_SPAM_CLEANTALK);
		}

		// Akismet antispam detection
		if (KT::akismet()->isEnabled() && $this->isAkismetSpam()) {
			$this->table->published = KOMENTO_COMMENT_SPAM;
			$this->setParam('spam', KT_SPAM_AKISMET);
		}

		// Ensure that the modified date is to the latest time
		if (!$this->table->modified && $this->table->published) {
			$this->table->modified = $now;
		}

		// Ensure that the publish_up date is set
		if (!$this->table->publish_up && $this->table->published) {
			$this->table->publish_up = $now;
		}
	}

	/**
	 * Pre process the comment before we save it.
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function preSave()
	{
		KT::trigger('onBeforeSaveComment', ['component' => $this->table->component, 'cid' => $this->table->cid, 'comment' => &$this]);
		
		if ($this->isNew()) {
			// Calculate and update comment boundaries
			$model = KT::model('Comments');
			$model->updateCommentLftRgt($this->table);
		}

		// updating the depth if needed
		if ($this->parent_id) {
			$parent = KT::table('Comments');
			$parent->load($this->parent_id);

			$this->depth = $parent->depth + 1;
		}

		// Whenever a comment moved from other cid, we reset the parent to 0
		if (isset($this->saveOptions['previousCid']) && $this->saveOptions['previousCid'] > 0) {
			$this->parent_id = 0;
		}

		return true;
	}

	/**
	 * Post saving method happens after a comment is stored on the table.
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function postSave()
	{
		// Process attachments
		if (isset($this->saveOptions['processAttachments']) && $this->saveOptions['processAttachments']) {
			$attachments = $this->input->get('attachments', '', 'default');

			if (is_array($attachments) && !empty($attachments)) {

				$file = KT::file();

				foreach ($attachments as $attachment) {
					$state = $file->attach($attachment, $this->table->id);

					if ($state) {
						KT::activity()->process('upload', $this);
					}
				}
			}
		}

		// Mentions
		if ($this->config->get('enable_mention')) {
			$this->notifyNames();
		}

		// Updating comment lft/rgt for when article id is changed
		if (isset($this->saveOptions['previousCid']) && $this->saveOptions['previousCid'] > 0) {

			// Move childs over
			$this->updateChildsArticle($this->saveOptions['previousCid']);

			// fix current cid
			$this->fixStructure();

			// fix previous cid
			$this->fixStructure($this->saveOptions['previousCid']);
		}

		// notification to article's subscribers.

		// now we add the comment subscriber here.
		// We need to port the codes from controllers/subscriptions.php into a subscriptions library
		$subscribe = false;

		// // Determines if we should be subscribing the user to the thread
		if ($this->config->get('subscription_auto')) {
			$subscribe = true;
		} else {
			// need to get from form post.
			$subscribe = $this->input->get('subscribe', false, 'default');
		}

		//[KOMENTO PAID START]
		// Push notifications
		if (KT::push()->isEnabled()) {
			KT::push()->notify($this);
		}
		//[KOMENTO PAID END]

		if ($this->config->get('enable_subscription') && $subscribe && $this->table->email) {

			$data = [
				'userid' => $this->table->created_by,
				'fullname' => $this->table->name,
				'email' => $this->table->email,
				'commentId' => $this->table->id
			];

			// get user's interval settings. if email digest enabled
			if ($this->config->get('email_digest_enabled')) {

				$defaultInterval = $this->config->get('email_digest_interval');
				$defaultPostCount = 10;

				if ($this->table->created_by) {
					$model = KT::model('Subscription');
					$subscriptions = $model->getUserSubscriptions($this->table->created_by, ['limit' => 1]);
					if ($subscriptions) {
						$sub = $subscriptions[0];

						$defaultInterval = $sub->interval;
						$defaultPostCount = $sub->count;
					}
				}

				$data['interval'] = $defaultInterval;
				$data['postcount'] = $defaultPostCount;
			}

			KT::subscription()->add($this->table->component, $this->table->cid, $data);
		}

		// Set reply datetime in session for flood control
		$session = KT::session();

		if ($this->config->get('antispam_flood_control')) {
			$session->setReplyTime();
		}

		KT::trigger('onAfterSaveComment', ['component' => $this->table->component, 'cid' => $this->table->cid, 'comment' => &$this]);

		// Add activity
		$action = $this->table->parent_id ? 'reply' : 'comment';
		KT::activity()->process($action, $this);

		// Send notifications
		if ($this->config->get('notification_enable')) {

			$isNew = !$this->saveOptions['isEdited'];

			if ($this->isPublished() && $isNew && (($action == 'comment' && $this->config->get('notification_event_new_comment')) || ($action == 'reply' && $this->config->get('notification_event_new_reply')))) {
				KT::notification()->push($action, 'author,usergroups,subscribers', ['commentId' => $this->table->id]);
			}

			if ($this->isPending() && $this->config->get('notification_event_new_pending')) {
				$notifyGroups = 'usergroups';

				if ($this->config->get('notification_event_new_pending_author')) {
					$notifyGroups = 'author,usergroups';
				}

				KT::notification()->push('pending', $notifyGroups, ['commentId' => $this->table->id]);
			}
		}
	}

	public function updateChildsArticle($previousCid)
	{
		$model = KT::model('Comments');
		$model->updateChildsArticle($this->table, $previousCid);
	}
	
	public function fixStructure($oldCid = 0)
	{
		$model = KT::model('comments');

		$cid = $oldCid > 0 ? $oldCid : $this->table->cid;
		$parents = $model->getRootParents($this->table->component, $cid);

		// Set boundary to start from 1
		$boundary = 1;

		// Fix all the parent first
		foreach ($parents as $parent) {
			$model->fixItemStructure($parent, $boundary, 0);
		}

		// Now we start fixing the childrens
		foreach($parents as $parent) {
			$model->fixChildStructure($parent);
		}
	}

	/**
	 * Retrieves the application plugin for the unique item
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getApplication()
	{
		return KT::loadApplication($this->table->component)->load($this->table->cid);
	}

	/**
	 * Retrieve attachments within a comment
	 *
	 * @since   4.0.0
	 * @access  public
	 */
	public function getAttachments()
	{
		static $cache = [];

		if (isset($cache[$this->id])) {
			return $cache[$this->id];
		}


		$items = [];

		$cacheExists = KT::cache()->exists($this->id, 'attachment');
		
		if ($cacheExists) {
			$items = KT::cache()->get($this->id, 'attachment');
		} 

		if (!$cacheExists) {
			$model = KT::model('Uploads');
			$items = $model->getAttachments($this->id);
		}

		$cache[$this->id] = [];

		if ($items) {
			
			$acl = KT::acl();

			foreach ($items as $item) {
				// To satisfy the display from Foundry, we need to standardize these props
				$item->name = $item->filename;
				$item->icon = $item->getIconType();
				$item->title = $item->filename;
				$item->image = $item->isImage() ? $item->getLink() : false;
				$item->size = $item->getSize();
				$item->download = $item->getLink();
				$item->canDelete = $acl->allow('delete_attachment', $this);

				$cache[$this->id][] = $item;
			}
		}

		return $cache[$this->id];
	}

	/**
	 * Retrieves the date object for the comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getCreatedDate()
	{
		$date = FH::date($this->created, true);

		return $date;
	}

	/**
	 * method port from jtable. to retrieve comment's author.
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function getAuthor()
	{
		if (!$this->created_by) {
			$author = KT::user(0);
		} else {
			$author = KT::user($this->created_by);
		}

		return $author;
	}

	/**
	 * Method to retrive author display name based on the config
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function getAuthorName()
	{
		if (!$this->created_by) {
			return $this->name;
		}

		return $this->getAuthor()->getName();
	}

	/**
	 * Method to retrive author display name based on the config
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function getAuthorEmail()
	{
		if (!$this->created_by) {
			return $this->email;
		}

		return $this->getAuthor()->email;
	}

	/**
	 * Method to retrive author display name based on the config
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function getAuthorWebsite()
	{
		if (!$this->created_by) {
			return $this->url;
		}

		return $this->getAuthor()->email;
	}

	/**
	 * Get parent author profile link
	 *
	 * @since   3.1
	 * @access  public
	 */
	public function getParentAuthorLink()
	{
		static $parentAuthorLinks = [];

		if (!isset($parentAuthorLinks[$this->parent_id])) {
			$parentComment = $this->getParent();
			$author = $parentComment->getAuthor();

			$parentAuthorLinks[$this->parent_id] = $author->getProfileLink($parentComment->getAuthorEmail(), $parentComment->getAuthorWebsite());
		}

		return $parentAuthorLinks[$this->parent_id];
	}

	/**
	 * Retrieves the parsed contents of the comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getContent($truncate = false, $insertEllipses = false)
	{
		static $result = [];

		$key = $this->id . (int) $truncate . (int) $insertEllipses;

		if (!isset($result[$key])) {
			$contents = $this->table->preview;

			if (!$contents) {
				$contents = KT::parser()->parseComment($this->table->comment);
			}

			if ($truncate) {

				// the content in preview column might have html tags that are being htmlentities, and we need to decode the htmlentities back to it original character
				// before calling strip_tags #520
				$contents = html_entity_decode($contents);

				$insertEllipses = FCJString::strlen($contents) > $truncate;

				$contents = FCJString::substr(strip_tags($contents), 0, $truncate);

				if ($insertEllipses) {
					$contents .= JText::_('COM_KOMENTO_ELLIPSES');
				}
			}

			$result[$key] = $contents;
		}

		return $result[$key];
	}

	/**
	 * Retrieves the address posted with the comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getAddress($charactersLimit = null)
	{
		if (is_null($charactersLimit)) {
			return $this->table->address;
		}

		return FCJString::substr($this->table->address, 0, $charactersLimit) . JText::_('COM_KOMENTO_ELLIPSES');
	}

	/**
	 * Generates the css class for the comment wrapper
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getCustomCss()
	{
		$classes = [];
		$classes[] = 'kmt-item';

		$author = $this->getAuthor();
		$app = $this->getApplication();

		// Usergroup CSS classes
		if ($author->guest) {
			$classes[] = $this->config->get('layout_css_public');
		} else {
			$classes[] = $this->config->get('layout_css_registered');
		}

		if ($author->isAdmin()) {
			$classes[] = $this->config->get('layout_css_admin');
		}

		if ($this->created_by == $app->getAuthorId()) {
			$classes[] = $this->config->get('layout_css_author');
		}

		$groups = $author->getUsergroups();

		if (is_array($groups) && !empty($groups)) {
			foreach ($groups as $group) {
				$classes[] = 'kt-group-' . $group;
			}
		}

		$custom = implode(' ', $classes);

		return $custom;
	}

	/**
	 * Retrieves the indentation styling
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getIndentStyling($spacer = 'margin', $customDepth = null)
	{
		if (!$this->depth || !$this->config->get('enable_threaded')) {
			return;
		}

		$depth = $this->table->depth;

		if (!is_null($customDepth) && $customDepth) {
			$depth = $customDepth;
		}

		$total = $this->config->get('thread_indentation') * $depth;
		$margin = $spacer . '-left';

		if ($this->doc->getDirection() == 'rtl') {
			$margin = $spacer . '-right';
		}

		$style = $margin . ':' . $total . 'px;';

		return $style;
	}

	/**
	 * Retrieves the content item's title
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getItemTitle()
	{
		$app = $this->getApplication();
		$title = $app->getContentTitle();

		return $title;
	}

	/**
	 * Retrieves the content item's state
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getItemState()
	{
		$app = $this->getApplication();
		$state = $app->getContentState();

		return $state;
	}

	/**
	 * Retrieves the component's title
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getComponentTitle()
	{
		$app = $this->getApplication();
		$title = $app->getComponentName();

		return $title;
	}

	/**
	 * Retrieves the date object for the comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getModifiedDate()
	{
		$date = FH::date($this->modified);

		return $date;
	}

	/**
	 * Retrieves the content item's permalink
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getItemPermalink()
	{
		$app = $this->getApplication();
		$permalink = $app->getContentPermalink();

		return $permalink;
	}

	/**
	 * Generates the permalink for the comment.
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getPermalink()
	{
		static $permalinks = [];

		$idx = $this->id;

		if (!isset($permalinks[$idx])) {

			$limitperpage = $this->config->get('max_comments_per_page');
			$defaultsort = $this->config->get('default_sort', 'oldest');
			$sort = $this->input->get('sort', '', 'default');

			$model = KT::model('Comments');
			$pageInfo = '';

			if (! isset($this->rownumber) || !$this->rownumber) {
				// we need to manually count the page info.
				$rownumber = $model->getRowNumber($this->id, $defaultsort);
				if ($rownumber) {
					$this->rownumber = $rownumber;
				}
			}

			$pageInfo = '';

			if (!is_null($this->rownumber) && $this->rownumber) {
				$startpage = $this->rownumber / $limitperpage;
				$startpage = ceil($startpage);

				if ($startpage > 1) {
					$pageInfo = ',' . ($startpage - 1) * $limitperpage;
				}

				if ($sort && $sort != $defaultsort) {
					$pageInfo = ($pageInfo) ? $pageInfo . ',' . $sort : ',0,' . $sort;
				}
			}


			$pid = '';

			if (isset($this->pid) && $this->pid) {
				$parentId = $this->pid;
				$pid = ',' . $parentId;
			} else if (is_null($this->pid) && $this->depth) {

				// if this condition meet, thats means this comment object did not go through formatter.
				// let try to get manually
				$parentId = $model->getParents($this->id, true);

				if ($parentId && $parentId != $this->id) {
					$this->pid = $parentId;
					$pid = ',' . $parentId;
				} else {
					$pid = ',0';
				}

			} else {
				$pid = ',0';
			}

			// $permalinks[$idx] = $this->getItemPermalink() . '#comment-' . base64_encode($this->id . $pageInfo);
			$permalinks[$idx] = $this->getItemPermalink() . '#comment-' . $this->id . $pid . $pageInfo;
		}

		return $permalinks[$idx];
	}


	/**
	 * Retrieves the params
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getParams()
	{
		static $data = [];

		if (!isset($data[$this->id])) {
			$registry = new JRegistry($this->params);

			$data[$this->id] = $registry;
		}
		

		return $data[$this->id];
	}

	/**
	 * Retrieves the parent comment if this comment is a reply
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function getParent()
	{
		static $parents = [];

		if (! isset($parents[$this->parent_id])) {
			$parents[$this->parent_id] = KT::comment($this->parent_id);
		}

		return $parents[$this->parent_id];
	}

	/**
	 * Retrieves a list of replies for a particular comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getReplies()
	{
		static $items = [];

		if (!isset($items[$this->id])) {

			if (KT::cache()->exists($this->id, 'replies')) {

				$replies = KT::cache()->get($this->id, 'replies');

				$comments = [];

				if ($replies) {
					foreach ($replies as $reply) {

						$comment = KT::comment($reply->id);

						// Get actions likes
						if (KT::cache()->exists($comment->id, 'likes')) {
							$comment->likes = KT::cache()->get($comment->id, 'likes');
						} else {
							$comment->likes = $actionsModel->countAction('likes', $comment->id);
						}

						// Get actions dislikes
						if (KT::cache()->exists($comment->id, 'dislikes')) {
							$comment->dislikes = KT::cache()->get($comment->id, 'dislikes');
						} else {
							$comment->dislikes = $actionsModel->countAction('dislikes', $comment->id);
						}

						$comments[] = $comment;
					}
				}

				$items[$this->id] = $comments;

			} else {

				$model = KT::model('Comments');
				$ids = $model->getChilds($this->id);

				$comments = [];

				if ($ids) {
					// preload
					KT::comment($ids);

					foreach ($ids as $id) {
						$comments[] = KT::comment($id);
					}
				}

				$items[$this->id] = $comments;
			}

		}

		return $items[$this->id];
	}

	public static function convertOrphanitem($id)
	{
		$config = KT::getConfig();

		$comment = KT::getTable('comments');
		$comment->load($id);
		$comment->created_by = $config->get('orphanitem_ownership');
		$comment->store();

		return true;
	}

	/**
	 * Sets an error message
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function setError($message = '')
	{
		$this->error = JText::_($message);
	}

	/**
	 * Sets a value in the comment params
	 *
	 * @since	4.0.0
	 * @access	public
	 */
	public function setParam($key, $value)
	{
		$params = $this->getParams();
		$params->set($key, $value);

		$this->params = $params->toString();
		
		return $params;
	}

	/**
	 * Get an error message
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function getError($message = '')
	{
		return $this->error;
	}

	/**
	 * Validates the current comment object
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function validate($data)
	{
		// Check for name field
		$requireName = ($this->config->get('show_name') == 2 && $this->config->get('require_name') == 2) || ($this->my->guest && $this->config->get('show_name') > 0 && $this->config->get('require_name') == 1);

		if (!$this->table->name && $requireName) {
			$this->setError('COM_KOMENTO_VALIDATE_NAME_REQUIRED');
			return false;
		}

		// Check for email field
		$requireEmail = ($this->config->get('show_email') == 2 && $this->config->get('require_email') == 2) || ($this->my->guest && $this->config->get('show_email') > 0 && $this->config->get('require_email') == 1);

		if (!$this->table->email && $requireEmail) {
			$this->setError('COM_KOMENTO_VALIDATE_EMAIL_REQUIRED');
			return false;
		}

		// Check for website field
		$requireUrl = ($this->config->get('show_website') == 2 && $this->config->get('require_website') == 2) || ($this->my->guest && $this->config->get('show_website') > 0 && $this->config->get('require_website') == 1);

		if (!$this->table->url && $requireUrl) {
			$this->setError('COM_KOMENTO_VALIDATE_WEBSITE_REQUIRED');
			return false;
		}

		// Check for terms and conditions acceptance
		if (KT::form()->requireTerms() && $data['tnc'] == 'false') {

			$this->setError('COM_KOMENTO_VALIDATE_TERMS_REQUIRED');
			return false;
		}

		// Ensure that the e-mail is valid against their defined e-mail pattern
		if ($this->table->email) {
			// $pattern = '/S+@S+/';
			$pattern = '/^([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]+)$/i';

			if (!preg_match($pattern, $this->table->email)) {
				$this->setError('COM_KOMENTO_FORM_NOTIFICATION_EMAIL_INVALID');
				return false;
			}
		}

		$this->table->comment = KT::parser()->removeEmptyBBcodes($this->table->comment);
		$this->table->comment = trim($this->table->comment);

		// Ensure that the comment isn't empty
		if ($this->table->comment === '') {
			$this->setError('COM_KOMENTO_FORM_NOTIFICATION_COMMENT_REQUIRED');
			return false;
		}

		// Minimum characters check
		if ($this->config->get('antispam_min_length_enable') && FCJString::strlen($this->table->comment) < $this->config->get('antispam_min_length')) {
			$this->setError(JText::sprintf('COM_KOMENTO_FORM_VALIDATION_TOO_SHORT', $this->config->get('antispam_min_length')));
			return false;
		}

		// Maximum characters check
		if ($this->config->get('antispam_max_length_enable') && FCJString::strlen($this->table->comment) > $this->config->get('antispam_max_length')) {
			$this->setError(JText::sprintf('COM_KOMENTO_FORM_VALIDATION_TOO_LONG', $this->config->get('antispam_max_length')));
			return false;
		}

		// Captcha checks
		$captcha = KT::captcha();

		if ($this->config->get('antispam_captcha_enable') && KT::form()->requireCaptcha() && $captcha) {
			$options = [
				'recaptcha_response_field' => isset($data['recaptchaResponse']) ? $data['recaptchaResponse'] : '',
				'captcha-response' => isset($data['captchaResponse']) ? $data['captchaResponse'] : '',
				'captcha-id' => isset($data['captchaId']) ? $data['captchaId'] : ''
			];

			if (!$captcha->verify($options)) {
				$error = $captcha->getError();

				$this->setError($error);
				return false;
			}
		}

		$blockedWords = $this->config->get('blocked_words');
		$hasBlockedWords = StringHelper::hasBlockedWords($blockedWords, $data['comment']);

		if ($hasBlockedWords) {
			$this->setError(JText::sprintf('COM_KT_CONTAINS_BLOCKED_WORD_MESSAGE', $hasBlockedWords, $blockedWords));

			return false;
		}

		return true;
	}

	/**
	 * Validate the antispams
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function validateSpam()
	{
		// Honeypot spam detection
		if ($this->isHoneypotTrapped() && $this->config->get('antispam_honeypot_rejection_type') === 'high') {
			$this->setError('Comment submitted');
			return false;
		}

		// CleanTalk antispam detection
		if (KT::cleantalk()->isEnabled() && ($this->getCleanTalkSpamLevel() == KOMENTO_CLEANTALK_SPAM)) {
			$this->setError('COM_KOMENTO_FORM_NOTIFICATION_CLEANTALK_SPAM');
			return false;
		}

		$akismetRejection = $this->config->get('antispam_akismet_rejection_type');

		// Akismet antispam detection
		if (KT::akismet()->isEnabled() && $akismetRejection == 'high' && $this->isAkismetSpam()) {
			$this->setError('COM_KOMENTO_FORM_NOTIFICATION_AKISMET_SPAM');
			return false;
		}

		return true;
	}

	/**
	 * Check for cleantalk spam
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function getCleanTalkSpamLevel()
	{
		$cleantalk = KT::cleantalk();
		$isSpam = $cleantalk->validate($this);

		if ($isSpam) {
			return true;
		}

		return false;
	}

	/**
	 * Retrieves the spam type for the comment if it was caught by the antispam filters
	 *
	 * @since   4.0.0
	 * @access  public
	 */
	public function getSpamType()
	{
		static $cache = [];

		if (!isset($cache[$this->id])) {
			$params = $this->getParams();
			$cache[$this->id] = $params->get('spam', false);
		}

		return $cache[$this->id];
	}

	/**
	 * Check for akismet spam
	 *
	 * @since   3.0
	 * @access  public
	 */
	public function isAkismetSpam()
	{
		$akismet = KT::akismet();
		$isSpam = $akismet->isSpam($this);

		if ($isSpam) {
			return true;
		}

		return false;
	}

	/**
	 * Check for honeypot traps
	 *
	 * @since   4.0.0
	 * @access  public
	 */
	public function isHoneypotTrapped()
	{
		$config = KT::config();
		
		if (!$config->get('antispam_honeypot_enabled')) {
			return false;
		}

		$honeypot = KT::honeypot();
		$trapped = $honeypot->isTrapped();

		return $trapped;
	}


	/**
	 * Determines if the comment has childs
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function hasReplies()
	{
		static $items = [];

		if (!isset($items[$this->id])) {

			if (isset($this->childs)) {
				$items[$this->id] = $this->childs;
			} else {
				$items[$this->id] = $this->getRepliesCount();
			}
		}

		return $items[$this->id];
	}


	public function getRepliesCount()
	{
		$repliesCnt = 0;

		if (is_null($this->childs)) {

			$boundary = ($this->rgt - $this->lft) - 1;

			if (!$this->parent_id && $boundary > 0) {
				$repliesCnt = floor($boundary / 2);
			}

			$this->childs = $repliesCnt;
		}

		return $this->childs;
	}

	/**
	 * Determines if a comment has location
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function hasLocation()
	{
		if ($this->address || ($this->address && $this->latitude && $this->longitude)) {
			return true;
		}

		return false;
	}

	/**
	 * Determines if a comment has geometry location
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function hasGeometryLocation()
	{
		if ($this->address && $this->latitude && $this->longitude) {
			return true;
		}

		return false;
	}

	/**
	 * Get Map url based on setting
	 *
	 * @since	4.0
	 * @access	public
	 */
	public function getMapUrl()
	{
		if ($this->config->get('location_service_provider') === 'osm') {
			return '//www.openstreetmap.org/?mlat=' . $this->latitude . '&mlon=' . $this->longitude . '#map=15/' . $this->latitude . '/' . $this->longitude;
		}

		return '//maps.google.com/maps?z=15&q=' . $this->latitude . ',' . $this->longitude;
	}

	/**
	 * Determines if the comment is unpublished
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function isUnpublished()
	{
		return $this->table->published == KOMENTO_COMMENT_UNPUBLISHED;
	}

	/**
	 * Determines if the comment is pending
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function isPending()
	{
		return $this->table->published == KOMENTO_COMMENT_MODERATE;
	}

	/**
	 * Determines if the comment is minimized
	 *
	 * @since	3.1.0
	 * @access	public
	 */
	public function isMinimized()
	{
		static $items = [];

		if (!isset($items[$this->id])) {
			$params = $this->getParams();
			$items[$this->id] = $params->get('minimized', false);
		}

		return $items[$this->id];
	}

	/**
	 * Determines if the comment is new
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function isNew()
	{
		return $this->_original->id ? false : true;
	}

	/**
	 * Determines if this is a parent comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function isParent()
	{
		return !$this->parent_id;
	}

	/**
	 * Determines if the comment is spam
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function isSpam()
	{
		return $this->table->published == KOMENTO_COMMENT_SPAM;
	}

	/**
	 * Determines if the comment is reported
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function isReport()
	{
		$model = KT::model('Actions');
		$state = $model->countAction('report', $this->table->id);

		return $state;
	}

	/**
	 * Determines if the comment is published
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function isPublished()
	{
		return $this->table->published == KOMENTO_COMMENT_PUBLISHED;
	}

	/**
	 * Determines if the comment is a pinned comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function isFeatured()
	{
		return $this->table->sticked > 0;
	}

	/**
	 * Determines if the comment has been edited before
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function isEdited()
	{
		return $this->table->modified_by > 0;
	}

	/**
	 * Allows caller to delete a comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function delete()
	{
		// @onBeforeDeleteComment
		$args = array('component' => $this->table->component, 'cid' => $this->table->cid, 'comment' => &$this);
		$result = KT::trigger('onBeforeDeleteComment', $args);

		if ($result === false) {
			$this->setError('Trigger onBeforeDeleteComment false');
			return false;
		}

		$state = $this->table->delete();

		if (!$state) {
			$this->setError('Comment delete failed');
			return false;
		}

		// @onAfterDeleteComment
		KT::trigger('onAfterDeleteComment', $args);

		// Always delete the replies no matter what.
		$model = KT::model('Comments');
		$model->deleteChilds($this->table->id);

		// Clear activities
		$activitiesModel = KT::model('Activity');
		$activitiesModel->delete($this->table->id);

		// Clear actions
		$actionsModel = KT::model('Actions');
		$actionsModel->removeAction('all', $this->table->id, 'all');

		// Process activities
		KT::activity()->process('remove', $this);

		// Remove attachments
		KT::file()->clearAttachments($this->table->id);

		return true;
	}

	/**
	 * Generates an export-able data that is safe for viewing
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function export()
	{
		$data = (object) get_object_vars($this->table);

		return $data;
	}

	/**
	 * Allows caller to remove reports from a particular comment
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function removeReport()
	{
		$model = KT::model('Actions');
		$state = $model->removeAction('report', $this->table->id);

		return $state;
	}

	/**
	 * Retrieve commment ratings
	 *
	 * @since	3.0
	 * @access	public
	 */
	public function getRatings()
	{
		return $this->table->ratings / 2;
	}

	/**
	 * Determine whether the comment is ownself.
	 *
	 * @since	4.0.0
	 * @access	public
	 */
	public function isMine()
	{
		if ((int) $this->created_by === (int) $this->my->id) {
			return true;
		}

		return false;
	}
}