| Server IP : 213.186.33.4 / Your IP : 216.73.216.193 Web Server : Apache System : Linux webm006.cluster103.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64 User : awebpaca ( 35430) PHP Version : 8.5.0 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/a/w/e/awebpaca/blog/libraries/kunena/forum/topic/ |
Upload File : |
<?php
/**
* Kunena Component
* @package Kunena.Framework
* @subpackage Forum.Topic
*
* @copyright (C) 2008 - 2018 Kunena Team. All rights reserved.
* @license https://www.gnu.org/copyleft/gpl.html GNU/GPL
* @link https://www.kunena.org
**/
defined('_JEXEC') or die();
/**
* Class KunenaForumTopic
*
* @property int $category_id
* @property string $subject
* @property int $icon_id
* @property int $locked
* @property int $hold
* @property int $ordering
* @property int $posts
* @property int $hits
* @property int $attachments
* @property int $poll_id
* @property int $moved_id
* @property int $first_post_id
* @property int $first_post_time
* @property int $first_post_userid
* @property string $first_post_message
* @property string $first_post_guest_name
* @property int $last_post_id
* @property int $last_post_time
* @property int $last_post_userid
* @property string $last_post_message
* @property string $last_post_guest_name
* @property string $params
*/
class KunenaForumTopic extends KunenaDatabaseObject
{
/**
* @var int
*/
public $id = null;
public $unread = 0;
public $lastread = 0;
protected $_table = 'KunenaTopics';
protected $_db = null;
protected $_authcache = array();
protected $_authccache = array();
protected $_authfcache = array();
protected $_hold = 1;
protected $_posts = 0;
protected $_pagination = null;
protected static $actions = array(
'none' => array(),
'read' => array('Read'),
'create' => array('NotExists'),
'reply' => array('Read','NotHold','NotMoved','Unlocked'),
'edit' => array('Read','NotMoved','Unlocked','Own'),
'move' => array('Read'),
'approve' => array('Read','NotMoved'),
'delete' => array('Read'),
'undelete' => array('Read'),
'permdelete' => array('Read', 'Permdelete'),
'favorite' => array('Read'),
'subscribe' => array('Read'),
'sticky' => array('Read'),
'lock' => array('Read'),
'rate' => array('Read', 'Unlocked'),
'poll.read' => array('Read', 'Poll'),
'poll.create' => array('Own'),
'poll.edit' => array('Read','Own','NoVotes'),
'poll.delete' => array('Read','Own', 'Poll'),
'poll.vote' => array('Read', 'Poll', 'Vote'),
'post.read' => array('Read'),
'post.thankyou' => array('Read','NotMoved','Unlocked'),
'post.unthankyou' => array('Read','Unlocked'),
'post.reply' => array('Read','NotHold','NotMoved','Unlocked'),
'post.edit' => array('Read','Unlocked'),
'post.move' => array('Read'),
'post.approve' => array('Read'),
'post.delete' => array('Read','Unlocked'),
'post.undelete' => array('Read'),
'post.permdelete' => array('Read', 'Permdelete'),
'post.attachment.read' => array('Read'),
'post.attachment.createimage' => array('Unlocked'),
'post.attachment.createfile' => array('Unlocked'),
'post.attachment.delete' => array(),
// TODO: In the future we might want to restrict this: array('Read','Unlocked'),
);
/**
* @param mixed $properties
*
* @internal
*/
public function __construct($properties = null)
{
if (!empty($this->id))
{
$this->_exists = true;
$this->_hold = $this->hold;
$this->_posts = $this->posts;
}
else
{
parent::__construct($properties);
}
$this->_db = JFactory::getDBO();
}
/**
* Returns KunenaForumTopic object.
*
* @param int $identifier The topic to load - Can be only an integer.
* @param bool $reset
*
* @return KunenaForumTopic
*/
static public function getInstance($identifier = null, $reset = false)
{
return KunenaForumTopicHelper::get($identifier, $reset);
}
/**
* @param null|bool $exists
*
* @return boolean
*/
function exists($exists = null)
{
if ($exists !== null)
{
$this->_hold = $this->hold;
$this->_posts = $this->posts;
}
return parent::exists($exists);
}
/**
* Subscribe / Unsubscribe user to this topic.
*
* @param bool $value True for subscribe, false for unsubscribe.
* @param mixed $user
*
* @return boolean
*/
public function subscribe($value = true, $user = null)
{
$usertopic = $this->getUserTopic($user);
$usertopic->subscribed = (int) $value;
if (!$usertopic->save())
{
$this->setError($usertopic->getError());
return false;
}
return true;
}
/**
* Favorite / unfavorite user to this topic.
*
* @param bool $value True for favorite, false for unfavorite.
* @param mixed $user
*
* @return boolean
*/
public function favorite($value = true, $user = null)
{
$usertopic = $this->getUserTopic($user);
$usertopic->favorite = (int) $value;
if (!$usertopic->save())
{
$this->setError($usertopic->getError());
return false;
}
return true;
}
/**
* @param int $value
*
* @return boolean
*/
public function sticky($value = 1)
{
$this->ordering = (int) $value;
return $this->save();
}
/**
* @param int $value
*
* @return boolean
*/
public function lock($value = 1)
{
$this->locked = (int) $value;
return $this->save();
}
/**
* @param mixed $user
* @param bool|string $glue
*
* @return array|string
*/
public function getKeywords($user = null, $glue = false)
{
return ;
}
/**
* @param int $value
*
* @return boolean
*/
public function publish($value=KunenaForum::PUBLISHED)
{
if ($value < 0 || $value > 3)
{
$value = 0;
}
$this->hold = (int) $value;
$query = new KunenaDatabaseQuery;
$query->update('#__kunena_messages')->set("hold={$this->hold}")
->where("thread={$this->id}")->where("hold={$this->_hold}");
$this->_db->setQuery($query);
try
{
$this->_db->execute();
}
catch (JDatabaseExceptionExecuting $e)
{
KunenaError::displayDatabaseError($e);
return false;
}
return $this->_db->getAffectedRows() ? $this->recount() : $this->save();
}
/**
* Send email notifications from the first post in the topic.
*
* @param null|string $url
*/
public function sendNotification($url = null)
{
// Reload message just in case if it was published by bulk update.
KunenaForumMessageHelper::get($this->first_post_id, true)->sendNotification($url);
}
/**
* @param mixed $user
*
* @return KunenaForumTopicUser
*/
public function getUserTopic($user = null)
{
$usertopic = KunenaForumTopicUserHelper::get($this, $user);
return $usertopic;
}
/**
* @return KunenaUser
*/
public function getAuthor()
{
return KunenaUserHelper::getAuthor($this->first_post_userid, $this->first_post_guest_name);
}
/**
* @return KunenaForumCategory
*/
public function getCategory()
{
return KunenaForumCategoryHelper::get($this->category_id);
}
/**
* @return KunenaForumTopicPoll
*/
public function getPoll()
{
$poll = KunenaForumTopicPollHelper::get($this->poll_id);
$poll->threadid = $this->id;
return $poll;
}
/**
* @return integer
*/
public function getHits()
{
return $this->hits;
}
/**
* Increase hit counter for this topic.
*/
public function hit()
{
$app = JFactory::getApplication();
$lasthit = $app->getUserState('com_kunena.topic.lasthit');
if ($lasthit == $this->id)
{
return;
}
// Update only hit - not entire object
$table = $this->getTable();
$table->id = $this->id;
if ($table->hit())
{
$this->hits++;
$app->setUserState('com_kunena.topic.lasthit', $this->id);
}
}
/**
* @param int|null $limitstart Null if all pages need to be active.
* @param int $limit
* @param int $display
* @param string $prefix
*
* @return JPagination
*/
public function getPagination($limitstart=0, $limit=6, $display=4, $prefix='')
{
if (!$this->_pagination)
{
$this->_pagination = new KunenaPagination($this->posts, $limitstart, $limit, $prefix);
$this->_pagination
->setUri($this->getUri())
->setDisplayedPages($display);
if ($limitstart == null)
{
$this->_pagination->pagesCurrent = 0;
}
}
return $this->_pagination;
}
/**
* @param mixed $user
*
* @return KunenaForumTopicUser
*/
public function getUserInfo($user = null)
{
return KunenaForumTopicUserHelper::get($this->id, $user);
}
/**
* @return KunenaUser
*/
public function getFirstPostAuthor()
{
return KunenaUserHelper::getAuthor($this->first_post_userid, $this->first_post_guest_name);
}
/**
* @return KunenaUser
*/
public function getLastPostAuthor()
{
return KunenaUserHelper::getAuthor($this->last_post_userid, $this->last_post_guest_name);
}
/**
* @return KunenaDate
*/
public function getFirstPostTime()
{
return new KunenaDate($this->first_post_time);
}
/**
* @return KunenaDate
*/
public function getLastPostTime()
{
return new KunenaDate($this->last_post_time);
}
/**
* Resolve/get current topic.
*
* @return KunenaForumTopic Returns this topic or move target if this was moved.
*
* @throws RuntimeException If there is a redirect loop on moved_id.
*
* @since K4.0
*/
public function getTopic()
{
$ids = array();
$topic = $this;
// If topic has been moved, find the new topic
while ($topic->moved_id)
{
if (isset($ids[$topic->moved_id]))
{
throw new RuntimeException(JText::_('COM_KUNENA_VIEW_TOPIC_ERROR_LOOP'), 500);
}
$ids[$topic->moved_id] = 1;
$topic = KunenaForumTopicHelper::get($topic->moved_id);
}
return $topic;
}
/**
* @param string $field
*
* @return integer|string
*/
public function displayField($field)
{
switch ($field)
{
case 'id':
return intval($this->id);
case 'subject':
return KunenaHtmlParser::parseText($this->subject);
}
return '';
}
/**
* @param string $category_icon
*
* @return string
*/
public function getIcon($category_icon = '')
{
return KunenaFactory::getTemplate()->getTopicIcon($this, $category_icon);
}
/**
* @param mixed $hold
*
* @return integer
*/
public function getTotal($hold = null)
{
if ($this->moved_id || !KunenaUserHelper::getMyself()->isModerator($this->getCategory()))
{
return (int) max($this->posts, 0);
}
return KunenaForumMessageHelper::getLocation($this->last_post_id, 'both', $hold) + 1;
}
/**
* @param mixed $hold
*
* @return integer
*/
public function getReplies($hold = null)
{
return (int) max($this->getTotal($hold) - 1, 0);
}
/**
* @param mixed $category
* @param bool $xhtml
* @param null|string $action
*
* @return string
*/
public function getUrl($category = null, $xhtml = true, $action = null)
{
return KunenaRoute::getTopicUrl(
$this, $xhtml, $action,
$category ? KunenaForumCategoryHelper::get($category) : $this->getCategory());
}
/**
* Get permament topic URL without domain.
*
* If you want to add domain (for email etc), you can prepend the output with this:
* JUri::getInstance()->toString(array('scheme', 'host', 'port'))
*
* @param KunenaForumCategory $category
* @param bool $xhtml
* @param string $action
*
* @return string
*/
public function getPermaUrl($category = null, $xhtml = true, $action = null)
{
return $this->getUrl($category, $xhtml, $action);
}
/**
* Get published state in text.
*
* @return string
*
* @since K4.0
*/
public function getState()
{
switch ($this->hold)
{
case 0:
return 'published';
case 1:
return 'unapproved';
case 2:
case 3:
return 'deleted';
}
return 'unknown';
}
/**
* @param mixed $category
* @param string $action
*
* @return JUri|null
*/
public function getUri($category = null, $action = null)
{
$category = $category ? KunenaForumCategoryHelper::get($category) : $this->getCategory();
$Itemid = KunenaRoute::getCategoryItemid($category);
if (!$this->exists() || !$category->exists())
{
return null;
}
if ($action instanceof KunenaForumMessage)
{
$message = $action;
$action = 'post' . $message->id;
}
$uri = JUri::getInstance("index.php?option=com_kunena&view=topic&catid={$category->id}&id={$this->id}&action={$action}&Itemid={$Itemid}");
if ($uri->getVar('action') !== null)
{
$uri->delVar('action');
$mesid = 0;
$limit = max(1, intval(KunenaFactory::getConfig()->messages_per_page));
if (isset($message))
{
$mesid = $message->id;
}
elseif ((string) $action === (string) (int) $action)
{
if ($action > 0)
{
$uri->setVar('limitstart', $action * $limit);
}
}
else
{
switch ($action)
{
case 'first':
$mesid = $this->first_post_id;
break;
case 'last':
$mesid = $this->last_post_id;
break;
case 'unread':
// Special case, improves caching
$uri->setVar('layout', 'unread');
// $mesid = $this->lastread ? $this->lastread : $this->last_post_id;
break;
}
}
if ($mesid)
{
if (KunenaUserHelper::getMyself()->getTopicLayout() != 'threaded' && $mesid > 0)
{
$uri->setFragment($mesid);
$limitstart = intval($this->getPostLocation($mesid) / $limit) * $limit;
if ($limitstart)
{
$uri->setVar('limitstart', $limitstart);
}
}
else {
$uri->setVar('mesid', $mesid);
}
}
}
return $uri;
}
/**
* @param int $mesid
* @param string|null $direction
* @param mixed $hold
*
* @return integer
*/
public function getPostLocation($mesid, $direction = null, $hold = null)
{
if (is_null($direction))
{
$direction = KunenaUserHelper::getMyself()->getMessageOrdering();
}
if (!isset($this->lastread))
{
$this->lastread = $this->last_post_id;
$this->unread = 0;
}
if ($mesid == 'unread')
{
$mesid = $this->lastread;
}
if ($this->moved_id || !KunenaUserHelper::getMyself()->isModerator($this->getCategory()))
{
if ($mesid == 'first' || $mesid == $this->first_post_id)
{
return $direction == 'asc' ? 0 : $this->posts - 1;
}
if ($mesid == 'last' || $mesid == $this->last_post_id)
{
return $direction == 'asc' ? $this->posts - 1 : 0;
}
if ($mesid == $this->unread)
{
return $direction == 'asc' ? $this->posts - max($this->unread, 1) : 0;
}
}
if ($mesid == 'first')
{
$direction = ($direction == 'asc' ? 0 : 'both');
}
if ($mesid == 'last')
{
$direction = ($direction == 'asc' ? 'both' : 0);
}
return KunenaForumMessageHelper::getLocation($mesid, $direction, $hold);
}
/**
* @param array|bool $fields
* @param mixed $user
* @param array|null $safefields
*
* @return KunenaForumMessage
*/
public function newReply($fields = array(), $user = null, $safefields = null)
{
$user = KunenaUserHelper::get($user);
$category = $this->getCategory();
$message = new KunenaForumMessage;
$message->setTopic($this);
$message->parent = $this->first_post_id;
$message->thread = $this->id;
$message->catid = $this->category_id;
$message->name = $user->getName('');
$message->userid = $user->userid;
$message->subject = $this->subject;
$message->ip = !empty($_SERVER ["REMOTE_ADDR"]) ? $_SERVER ["REMOTE_ADDR"] : '';
if ($this->hold)
{
// If topic was unapproved or deleted, use the same state for the new message
$message->hold = $this->hold;
}
else
{
// Otherwise message is either unapproved or published depending if the category is moderated or not
$message->hold = $category->review ? (int) !$category->authorise('moderate', $user, true) : 0;
}
if ($fields === true)
{
$user = KunenaUserHelper::get($this->first_post_userid);
$text = preg_replace('/\[confidential\](.*?)\[\/confidential\]/su', '', $this->first_post_message);
$message->message = "[quote=\"{$user->getName($this->first_post_guest_name)}\" post={$this->first_post_id}]" . $text . "[/quote]";
}
else
{
if ($safefields)
{
$message->bind($safefields);
}
if ($fields)
{
$message->bind($fields, array ('name', 'email', 'subject', 'message' ), true);
}
}
return $message;
}
/**
* @param mixed $user
*
* @return boolean
*/
public function hasNew($user = null)
{
$user = KunenaUserHelper::get($user);
if (!KunenaFactory::getConfig()->shownew || !$user->exists())
{
return false;
}
$session = KunenaFactory::getSession();
if ($this->last_post_time <= $session->getAllReadTime())
{
return false;
}
$userinfo = KunenaForumCategoryUserHelper::get($this->getCategory(), $user);
if ($userinfo->allreadtime && $this->last_post_time <= $userinfo->allreadtime)
{
return false;
}
$read = KunenaForumTopicUserReadHelper::get($this, $user);
if ($this->last_post_time <= $read->time)
{
return false;
}
return true;
}
/**
* @param mixed $user
*
* @return boolean
*/
public function markRead($user = null)
{
$user = KunenaUserHelper::get($user);
if (!KunenaFactory::getConfig()->shownew || !$user->exists())
{
return false;
}
$read = KunenaForumTopicUserReadHelper::get($this, $user);
$read->time = JFactory::getDate()->toUnix();
$read->message_id = $this->last_post_id;
$read->save();
return true;
}
/**
* @deprecated
*
* @param null $user
*
* @return bool
*/
public function markNew($user = null)
{
return $this->markRead($user);
}
/**
* Returns true if user is authorised to do the action.
*
* @param string $action
* @param KunenaUser $user
*
* @return boolean
*
* @since K4.0
*/
public function isAuthorised($action='read', KunenaUser $user = null)
{
if (KunenaFactory::getConfig()->read_only)
{
// Special case to ignore authorisation.
if ($action != 'read')
{
return null;
}
}
return !$this->tryAuthorise($action, $user, false);
}
/**
* Throws an exception if user isn't authorised to do the action.
*
* @param string $action
* @param KunenaUser $user
* @param bool $throw
*
* @return KunenaExceptionAuthorise|null
* @throws KunenaExceptionAuthorise
* @throws InvalidArgumentException
*
* @since K4.0
*/
public function tryAuthorise($action='read', KunenaUser $user = null, $throw = true)
{
// Special case to ignore authorisation.
if ($action == 'none')
{
return null;
}
// Load user if not given.
if ($user === null)
{
$user = KunenaUserHelper::getMyself();
}
if (empty($this->_authcache[$user->userid][$action]))
{
// Unknown action - throw invalid argument exception.
if (!isset(self::$actions[$action]))
{
throw new InvalidArgumentException(JText::sprintf('COM_KUNENA_LIB_AUTHORISE_INVALID_ACTION', $action), 500);
}
// Load category authorisation.
if (!isset($this->_authccache[$user->userid][$action]))
{
$this->_authccache[$user->userid][$action] = $this->getCategory()->tryAuthorise('topic.' . $action, $user, false);
}
$this->_authcache[$user->userid][$action] = $this->_authccache[$user->userid][$action];
if (empty($this->_authcache[$user->userid][$action]))
{
foreach (self::$actions[$action] as $function)
{
if (!isset($this->_authfcache[$user->userid][$function]))
{
$authFunction = 'authorise' . $function;
$this->_authfcache[$user->userid][$function] = $this->$authFunction($user);
}
$error = $this->_authfcache[$user->userid][$function];
if ($error)
{
$this->_authcache[$user->userid][$action] = $error;
break;
}
}
}
}
$exception = $this->_authcache[$user->userid][$action];
// Throw or return the exception.
if ($throw && $exception)
{
throw $exception;
}
return $exception;
}
/**
* @param string $action
* @param mixed $user
* @param bool $silent
*
* @return boolean
* @deprecated K4.0
*/
public function authorise($action = 'read', $user = null, $silent = false)
{
KUNENA_PROFILER ? KunenaProfiler::instance()->start('function ' . __CLASS__ . '::' . __FUNCTION__ . '()') : null;
if ($user === null)
{
$user = KunenaUserHelper::getMyself();
}
elseif (!($user instanceof KunenaUser))
{
$user = KunenaUserHelper::get($user);
}
$exception = $this->tryAuthorise($action, $user, false);
if ($silent === false && $exception)
{
$this->setError($exception->getMessage());
}
KUNENA_PROFILER ? KunenaProfiler::instance()->stop('function ' . __CLASS__ . '::' . __FUNCTION__ . '()') : null;
if ($silent !== null)
{
return !$exception;
}
return $exception ? $exception->getMessage() : null;
}
/**
* Method to load a KunenaForumTopic object by id.
*
* @param null $id The topic id to be loaded.
*
* @return bool True on success.
*/
public function load($id = null)
{
$exists = parent::load($id);
$this->_hold = $this->hold === null ? 1 : $this->hold;
$this->_posts = $this->posts;
return $exists;
}
/**
* Move topic or parts of it into another category or topic.
*
* @param object $target Target KunenaForumCategory or KunenaForumTopic
* @param mixed $ids false, array of message Ids or JDate
* @param bool $shadow Leave visible shadow topic.
* @param string $subject New subject
* @param bool $subjectall Change subject from every message
* @param int $topic_iconid Define a new topic icon
*
* @return boolean|KunenaForumCategory|KunenaForumTopic Target KunenaForumCategory or KunenaForumTopic or false on failure
*/
public function move($target, $ids = false, $shadow = false, $subject = '', $subjectall = false, $topic_iconid = null)
{
// Warning: logic in this function is very complicated and even with full understanding its easy to miss some details!
// Clear authentication cache
$this->_authfcache = $this->_authccache = $this->_authcache = array();
// Cleanup input
if (!($ids instanceof JDate))
{
if (!is_array($ids))
{
$ids = explode(',', (string) $ids);
}
$mesids = array();
foreach ($ids as $id)
{
$mesids[(int) $id] = (int) $id;
}
unset($mesids[0]);
$ids = implode(',', $mesids);
}
$subject = (string) $subject;
// First we need to check if there will be messages left in the old topic
if ($ids)
{
$query = new KunenaDatabaseQuery();
$query->select('COUNT(*)')->from('#__kunena_messages')->where("thread={$this->id}");
if ($ids instanceof JDate)
{
// All older messages will remain (including unapproved, deleted)
$query->where("time<{$ids->toUnix()}");
}
else
{
// All messages that were not selected will remain
$query->where("id NOT IN ({$ids})");
}
$this->_db->setQuery($query);
$oldcount = (int) $this->_db->loadResult();
if ($this->_db->getErrorNum())
{
$this->setError($this->_db->getError());
return false;
}
// So are we moving the whole topic?
if (!$oldcount) {
$ids = '';
}
}
$categoryFrom = $this->getCategory();
// Find out where we are moving the messages
if (!$target || !$target->exists())
{
$this->setError(JText::printf('COM_KUNENA_MODERATION_ERROR_NO_TARGET', $this->id));
return false;
}
elseif ($target instanceof KunenaForumTopic)
{
// Move messages into another topic (original topic will always remain, either as real one or shadow)
if ($target == $this)
{
// We cannot move topic into itself
$this->setError(JText::sprintf('COM_KUNENA_MODERATION_ERROR_SAME_TARGET_THREAD', $this->id, $this->id));
return false;
}
if ($this->moved_id)
{
// Moved topic cannot be merged with another topic -- it has no posts to be moved
$this->setError(JText::sprintf('COM_KUNENA_MODERATION_ERROR_ALREADY_SHADOW', $this->id));
return false;
}
if ($this->poll_id && $target->poll_id)
{
// We cannot currently have 2 polls in one topic -- fail
$this->setError(JText::_('COM_KUNENA_MODERATION_CANNOT_MOVE_TOPIC_WITH_POLL_INTO_ANOTHER_WITH_POLL'));
return false;
}
if ($subjectall) { $subject = $target->subject; }
}
elseif ($target instanceof KunenaForumCategory)
{
// Move messages into category
if ($target->isSection())
{
// Section cannot have any topics
$this->setError(JText::_('COM_KUNENA_MODERATION_ERROR_NOT_MOVE_SECTION'));
return false;
}
// Save category information for later use
$categoryTarget = $target;
if ($this->moved_id)
{
// Move shadow topic and we are done
$this->category_id = $categoryTarget->id;
if ($subject) { $this->subject = $subject; }
$this->save(false);
return $target;
}
if ($shadow || $ids)
{
// Create new topic for the moved messages
$target = clone $this;
$target->exists(false);
$target->id = 0;
$target->hits = 0;
$target->params = '';
}
else
{
// If we just move into another category, we can keep using the old topic
$target = $this;
}
// Did user want to change the subject?
if ($subject)
{
$target->subject = $subject;
}
// Did user want to change the topic icon?
if (!is_null($topic_iconid))
{
$target->icon_id = $topic_iconid;
}
// Did user want to change category?
$target->category_id = $categoryTarget->id;
}
else {
$this->setError(JText::_('COM_KUNENA_MODERATION_ERROR_WRONG_TARGET'));
return false;
}
// For now on we assume that at least one message will be moved (=authorization check was called on topic/message)
// We will soon need target topic id, so save if it doesn't exist
if (!$target->exists())
{
if (!$target->save(false))
{
$this->setError($target->getError());
return false;
}
}
// Move messages (set new category and topic)
$query = new KunenaDatabaseQuery();
$query->update('#__kunena_messages')->set("catid={$target->category_id}")->set("thread={$target->id}")->where("thread={$this->id}");
// Did we want to change subject from all the messages?
if ($subjectall && !empty($subject))
{
$query->set("subject={$this->_db->quote($subject)}");
}
if ($ids instanceof JDate)
{
// Move all newer messages (includes unapproved, deleted messages)
$query->where("time>={$ids->toUnix()}");
}
elseif ($ids)
{
// Move individual messages
$query->where("id IN ({$ids})");
}
$this->_db->setQuery($query);
$this->_db->execute();
if ($this->_db->getErrorNum())
{
$this->setError($this->_db->getError());
return false;
}
// Make sure that all messages in topic have unique time (deterministic without ORDER BY time, id)
$query = "SET @ktime:=0";
$this->_db->setQuery($query);
$this->_db->execute();
if ($this->_db->getErrorNum())
{
$this->setError($this->_db->getError());
return false;
}
$query = "UPDATE #__kunena_messages SET time=IF(time<=@ktime,@ktime:=@ktime+1,@ktime:=time) WHERE thread={$target->id} ORDER BY time ASC, id ASC";
$this->_db->setQuery($query);
$this->_db->execute();
if ($this->_db->getErrorNum())
{
$this->setError($this->_db->getError());
return false;
}
// If all messages were moved into another topic, we need to move poll as well
if ($this->poll_id && !$ids && $target != $this)
{
// Note: We may already have saved cloned target (having poll_id already in there)
$target->poll_id = $this->poll_id;
// Note: Do not remove poll from shadow: information could still be used to show icon etc
$query = "UPDATE #__kunena_polls SET `threadid`={$this->_db->Quote($target->id)} WHERE `threadid`={$this->_db->Quote($this->id)}";
$this->_db->setQuery($query);
$this->_db->execute();
if ($this->_db->getErrorNum())
{
$this->setError($this->_db->getError());
return false;
}
}
// When moving only first message keep poll only on target topic
if ($this->poll_id && $target != $this && $ids)
{
if ($ids && $this->first_post_id)
{
$this->poll_id = 0;
}
}
if (!$ids && $target != $this)
{
// Leave shadow from old topic
$this->moved_id = $target->id;
if (!$shadow)
{
// Mark shadow topic as deleted
$this->hold = 2;
}
}
// Note: We already saved possible target earlier, now save only $this
if (!$this->save(false))
{
return false;
}
if (!$ids && !empty($categoryTarget))
{
// Move topic into another category
// Update user topic information (topic, category)
KunenaForumTopicUserHelper::move($this, $target);
// TODO: do we need this?
//KunenaForumTopicUserReadHelper::move($this, $target);
// Remove topic and posts from the old category
$categoryFrom->update($this, -1, -$this->posts);
// Add topic and posts into the new category
$categoryTarget->update($target, 1, $this->posts);
}
elseif (!$ids)
{
// Moving topic into another topic
// Add new posts, hits and attachments into the target topic
$target->posts += $this->posts;
$target->hits += $this->hits;
$target->attachments += $this->attachments;
// Update first and last post information into the target topic
$target->updatePostInfo(
$this->first_post_id, $this->first_post_time, $this->first_post_userid,
$this->first_post_message, $this->first_post_guest_name);
$target->updatePostInfo(
$this->last_post_id, $this->last_post_time, $this->last_post_userid,
$this->last_post_message, $this->last_post_guest_name);
// Save target topic
if (!$target->save(false))
{
$this->setError($target->getError());
return false;
}
// Update user topic information (topic, category)
KunenaForumTopicUserHelper::merge($this, $target);
// TODO: do we need this?
//KunenaForumTopicUserReadHelper::merge($this, $target);
// Remove topic and posts from the old category
$this->getCategory()->update($this, -1, -$this->posts);
// Add posts into the new category
$target->getCategory()->update($target, 0, $this->posts);
}
else
{
// Both topics have changed and we have no idea how much: force full recount
// TODO: we can do this faster..
$this->recount();
$target->recount();
}
return $target;
}
/**
* Method to save the KunenaForumTopic object to the database.
*
* @param bool $cascade
*
* @return bool True on success.
*
*/
public function save($cascade = true)
{
$topicDelta = $this->delta();
$postDelta = $this->posts - $this->_posts;
$isNew = !$this->exists();
if (!parent::save())
{
return false;
}
$this->_posts = $this->posts;
// Clear authentication cache
$this->_authfcache = $this->_authccache = $this->_authcache = array();
if ($cascade)
{
$category = $this->getCategory();
if (! $category->update($this, $topicDelta, $postDelta))
{
$this->setError($category->getError());
}
}
return true;
}
/**
* Method to put the KunenaForumTopic object on trash this is still present in database.
*
* @return bool True on success.
*/
public function trash()
{
if (!$this->exists())
{
return true;
}
// Clear authentication cache
$this->_authfcache = $this->_authccache = $this->_authcache = array();
$db = JFactory::getDBO();
$queries[] = "UPDATE #__kunena_messages SET hold='2' WHERE thread={$db->quote($this->id)}";
$queries[] = "UPDATE #__kunena_topics SET hold='2' WHERE id={$db->quote($this->id)}";
foreach ($queries as $query)
{
$db->setQuery($query);
try
{
$db->execute();
}
catch (JDatabaseExceptionExecuting $e)
{
KunenaError::displayDatabaseError($e);
}
}
return true;
}
/**
* Method to delete the KunenaForumTopic object from the database.
*
* @param bool $recount
*
* @return bool True on success.
*/
public function delete($recount = true)
{
if (!$this->exists())
{
return true;
}
if (!parent::delete())
{
return false;
}
// Clear authentication cache
$this->_authfcache = $this->_authccache = $this->_authcache = array();
// NOTE: shadow topic doesn't exist, DO NOT DELETE OR CHANGE ANY EXTERNAL INFORMATION
if ($this->moved_id == 0)
{
$db = JFactory::getDBO();
// Delete user topics
$queries[] = "DELETE FROM #__kunena_user_topics WHERE topic_id={$db->quote($this->id)}";
// Delete user read
$queries[] = "DELETE FROM #__kunena_user_read WHERE topic_id={$db->quote($this->id)}";
// Delete rating
$queries[] = "DELETE FROM #__kunena_rate WHERE topic_id={$db->quote($this->id)}";
if ($this->poll_id)
{
// Delete poll (users)
$queries[] = "DELETE FROM #__kunena_polls_users WHERE pollid={$db->quote($this->poll_id)}";
// Delete poll (options)
$queries[] = "DELETE FROM #__kunena_polls_options WHERE pollid={$db->quote($this->poll_id)}";
// Delete poll
$queries[] = "DELETE FROM #__kunena_polls WHERE id={$db->quote($this->poll_id)}";
}
// Delete thank yous
$queries[] = "DELETE t FROM #__kunena_thankyou AS t INNER JOIN #__kunena_messages AS m ON m.id=t.postid WHERE m.thread={$db->quote($this->id)}";
// Delete all messages
$queries[] = "DELETE m, t FROM #__kunena_messages AS m INNER JOIN #__kunena_messages_text AS t ON m.id=t.mesid WHERE m.thread={$db->quote($this->id)}";
foreach ($queries as $query)
{
$db->setQuery($query);
try
{
$db->execute();
}
catch (JDatabaseExceptionExecuting $e)
{
KunenaError::displayDatabaseError($e);
}
}
if ($recount)
{
KunenaUserHelper::recount();
KunenaForumMessageThankyouHelper::recount();
}
}
return true;
}
/**
* @param int $id
* @param int $time
* @param int $userid
* @param string $message
* @param string $name
*/
public function updatePostInfo($id, $time=0, $userid=0, $message='', $name='')
{
if ($id === false)
{
$this->first_post_id = 0;
$this->first_post_time = 0;
$this->first_post_userid = 0;
$this->first_post_message = '';
$this->first_post_guest_name = '';
$this->last_post_id = 0;
$this->last_post_time = 0;
$this->last_post_userid = 0;
$this->last_post_message = '';
$this->last_post_guest_name = '';
return;
}
if (!$this->first_post_time || ($this->first_post_time > $time || ($this->first_post_time == $time && $this->first_post_id >= $id))) {
$this->first_post_id = $id;
$this->first_post_time = $time;
$this->first_post_userid = $userid;
$this->first_post_message = $message;
$this->first_post_guest_name = $name;
}
if ($this->last_post_time < $time || ($this->last_post_time == $time && $this->last_post_id <= $id)) {
$this->last_post_id = $id;
$this->last_post_time = $time;
$this->last_post_userid = $userid;
$this->last_post_message = $message;
$this->last_post_guest_name = $name;
}
}
/**
* @param KunenaForumMessage $message
* @param int $postdelta
*
* @return boolean
*/
public function update($message = null, $postdelta = 0)
{
// Update post count
$this->posts += $postdelta;
$exists = $message && $message->exists();
if (!$this->exists())
{
if (!$exists)
{
$this->setError(JText::_('COM_KUNENA_LIB_TOPIC_NOT_EXISTS'));
return false;
}
$this->id = $message->id;
}
if ($exists && $message->thread == $this->id && $message->hold == $this->hold)
{
// If message belongs into this topic and has same state, we may need to update cache
$this->updatePostInfo($message->id, $message->time, $message->userid, $message->message, $message->name);
}
elseif (!$this->moved_id)
{
if(!isset($this->hold))
{
$this->hold = KunenaForum::TOPIC_DELETED;
}
// If message isn't visible anymore, check if we need to update cache
if (!$exists || $this->first_post_id == $message->id)
{
// If message got deleted and was cached, we need to find new first post
$db = JFactory::getDBO();
$query = "SELECT * FROM #__kunena_messages AS m INNER JOIN #__kunena_messages_text AS t ON t.mesid=m.id
WHERE m.thread={$db->quote($this->id)} AND m.hold={$this->hold} ORDER BY m.time ASC, m.id ASC";
$db->setQuery($query, 0, 1);
try
{
$first = $db->loadObject();
}
catch (JDatabaseExceptionExecuting $e)
{
KunenaError::displayDatabaseError($e);
}
if ($first)
{
$this->first_post_time = 0;
$this->updatePostInfo($first->id, $first->time, $first->userid, $first->message, $first->name);
}
else
{
$this->updatePostInfo(false);
}
}
if (!$exists || $this->last_post_id == $message->id)
{
// If topic got deleted and was cached, we need to find new last post
$db = JFactory::getDBO();
$query = "SELECT * FROM #__kunena_messages AS m INNER JOIN #__kunena_messages_text AS t ON t.mesid=m.id
WHERE m.thread={$db->quote($this->id)} AND m.hold={$this->hold} ORDER BY m.time DESC, m.id DESC";
$db->setQuery($query, 0, 1);
try
{
$last = $db->loadObject();
}
catch (JDatabaseExceptionExecuting $e)
{
KunenaError::displayDatabaseError($e);
}
if ($last)
{
$this->last_post_time = 0;
$this->updatePostInfo($last->id, $last->time, $last->userid, $last->message, $last->name);
}
else
{
$this->updatePostInfo(false);
}
}
}
if (!$this->first_post_id || !$this->last_post_id)
{
// If topic has no visible posts, mark it deleted and recount
$this->hold = $exists ? $message->hold : KunenaForum::TOPIC_DELETED;
$this->recount();
}
if (!($message && $message->exists()) && !$this->posts)
{
return $this->delete();
}
if(!$this->save())
{
return false;
}
if ($exists && $message->userid && abs($postdelta) <= 1)
{
// Update user topic
$usertopic = $this->getUserTopic($message->userid);
if (!$usertopic->update($message, $postdelta))
{
$this->setError($usertopic->getError());
}
// Update post count from user
$user = KunenaUserHelper::get($message->userid);
$user->posts += $postdelta;
if (!$user->save())
{
$this->setError($user->getError());
}
}
else
{
KunenaForumTopicUserHelper::recount($this->id);
// FIXME: optimize
KunenaUserHelper::recount();
}
return true;
}
/**
* @return boolean
*/
public function recount()
{
if (!$this->moved_id)
{
// Recount total posts and attachments
$query = "SELECT COUNT(DISTINCT m.id) AS posts, COUNT(a.id) AS attachments
FROM #__kunena_messages AS m
LEFT JOIN #__kunena_attachments AS a ON m.id=a.mesid
WHERE m.hold={$this->_db->quote($this->hold)} AND m.thread={$this->_db->quote($this->id)}
GROUP BY m.thread";
$this->_db->setQuery($query);
try
{
$result = $this->_db->loadAssoc();
}
catch (JDatabaseExceptionExecuting $e)
{
KunenaError::displayDatabaseError($e);
return false;
}
if (!$result)
{
$this->posts = 0;
// Double check if all posts have been removed from the database
$query = "SELECT COUNT(m.id) AS posts, MIN(m.hold) AS hold
FROM #__kunena_messages AS m
WHERE m.thread={$this->_db->quote($this->id)}
GROUP BY m.thread";
$this->_db->setQuery($query);
try
{
$result = $this->_db->loadAssoc();
}
catch (JDatabaseExceptionExecuting $e)
{
KunenaError::displayDatabaseError($e);
return false;
}
if ($result) {
// Information in the database was wrong, recount topic
$this->hold = $result['hold'];
$this->recount();
}
return true;
}
$this->bind($result);
}
return $this->update();
}
/**
* @return boolean
*/
public function resetvotes()
{
if(!$this->poll_id)
{
return false;
}
$query = "UPDATE #__kunena_polls_options SET votes=0 WHERE pollid={$this->_db->quote($this->poll_id)}";
$this->_db->setQuery($query);
try
{
$this->_db->execute();
}
catch (JDatabaseExceptionExecuting $e)
{
KunenaError::displayDatabaseError($e);
return false;
}
$query = "DELETE FROM #__kunena_polls_users WHERE pollid={$this->_db->quote($this->poll_id)}";
$this->_db->setQuery($query);
try
{
$this->_db->execute();
}
catch (JDatabaseExceptionExecuting $e)
{
KunenaError::displayDatabaseError($e);
return false;
}
return true;
}
// Internal functions
/**
* @param KunenaUser $user
*
* @return KunenaExceptionAuthorise|null
*/
protected function authoriseNotExists(KunenaUser $user)
{
// Check that topic does not exist
if ($this->_exists)
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_NO_ACCESS'), 403);
}
return null;
}
/**
* @param KunenaUser $user
*
* @return KunenaExceptionAuthorise|null
*/
protected function authoriseRead(KunenaUser $user)
{
// Check that user can read topic
if (!$this->exists())
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_NO_ACCESS'), 404);
}
// TODO: Allow owner to see his posts.
if ($this->hold)
{
if (!$user->exists())
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_NO_ACCESS'), 401);
}
$access = KunenaAccess::getInstance();
$hold = $access->getAllowedHold($user->userid, $this->category_id, false);
if (!in_array($this->hold, $hold))
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_NO_ACCESS'), 403);
}
}
return null;
}
/**
* @param KunenaUser $user
*
* @return KunenaExceptionAuthorise|null
*/
protected function authoriseNotHold(KunenaUser $user)
{
// Check that topic is not unapproved or deleted
if ($this->hold)
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_NO_ACCESS'), 403);
}
return null;
}
/**
* @param KunenaUser $user
*
* @return KunenaExceptionAuthorise|null
*/
protected function authoriseNotMoved(KunenaUser $user)
{
// Check that topic is not moved
if ($this->moved_id)
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_NO_ACCESS'), 403);
}
return null;
}
/**
* @param KunenaUser $user
*
* @return null|string
*/
protected function authoriseUnlocked(KunenaUser $user)
{
// Check that topic is not locked or user is a moderator
if ($this->locked && !$user->isModerator($this->getCategory()))
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_POST_ERROR_TOPIC_LOCKED'), 403);
}
return null;
}
/**
* @param KunenaUser $user
*
* @return null|string
*/
protected function authoriseOwn(KunenaUser $user)
{
// Guests cannot own a topic.
if (!$user->exists())
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_POST_NOT_MODERATOR'), 401);
}
// Check that topic owned by the user or user is a moderator
$usertopic = $this->getUserTopic($user);
if (!$usertopic->owner && !$user->isModerator($this->getCategory()))
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_POST_NOT_MODERATOR'), 403);
}
return null;
}
/**
* @param KunenaUser $user
*
* @return null|string
*/
protected function authorisePoll(KunenaUser $user)
{
// Check that user can vote
$poll = $this->getPoll();
if (!$poll->exists())
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_LIB_TOPIC_AUTHORISE_FAILED_NO_POLL'), 404);
}
return null;
}
/**
* @param KunenaUser $user
*
* @return null|string
*/
protected function authoriseVote(KunenaUser $user)
{
// Check that user can vote
$config = KunenaFactory::getConfig();
$poll = $this->getPoll();
$votes = $poll->getMyVotes($user);
if ($votes && $config->pollallowvoteone)
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_LIB_TOPIC_AUTHORISE_FAILED_VOTE_ONLY_ONCE'), 403);
}
if ($votes >= $config->pollnbvotesbyuser)
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_LIB_TOPIC_AUTHORISE_FAILED_VOTE_TOO_MANY_TIMES'), 403);
}
if ($config->polltimebtvotes && $poll->getMyTime($user) + (int) $config->polltimebtvotes > JFactory::getDate()->toUnix())
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_LIB_TOPIC_AUTHORISE_FAILED_VOTE_TOO_EARLY'), 403);
}
if ($this->locked)
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_LIB_TOPIC_AUTHORISE_FAILED_VOTE_POLL_TOPIC_LOCKED'), 403);
}
if ($poll->polltimetolive != '0000-00-00 00:00:00' && $poll->getTimeToLive() < JFactory::getDate()->toUnix())
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_LIB_TOPIC_AUTHORISE_FAILED_VOTE_POLL_EXPIRED'), 403);
}
return null;
}
/**
* @param KunenaUser $user
*
* @return null|string
*/
protected function authoriseNoVotes(KunenaUser $user)
{
$poll = $this->getPoll();
if ($poll->exists() && $poll->getUserCount())
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_LIB_TOPIC_AUTHORISE_FAILED_ONGOING_POLL'), 403);
}
return null;
}
/**
* @param KunenaUser $user
*
* @return null|string
*/
protected function authorisePermdelete(KunenaUser $user)
{
$config = KunenaFactory::getConfig();
if ($user->isAdmin() || $user->isModerator())
{
return null;
}
if ($user->isModerator($this->getCategory()) && !$config->moderator_permdelete || !$user->isModerator($this->getCategory()))
{
return new KunenaExceptionAuthorise(JText::_('COM_KUNENA_POST_ERROR_DELETE_REPLY_AFTER'), 403);
}
return null;
}
/**
* @return integer
*/
protected function delta()
{
if (!$this->hold && $this->_hold)
{
// Create or publish topic
return 1;
}
elseif ($this->hold && !$this->_hold)
{
// Delete or unpublish topic
return -1;
}
return 0;
}
}