Your IP : 216.73.216.224


Current Path : /var/www/html/components/com_kunena/src/Controller/Topic/Item/
Upload File :
Current File : /var/www/html/components/com_kunena/src/Controller/Topic/Item/TopicItemDisplay.php

<?php

/**
 * Kunena Component
 *
 * @package         Kunena.Site
 * @subpackage      Controller.Topic
 *
 * @copyright       Copyright (C) 2008 - 2026 Kunena Team. All rights reserved.
 * @license         https://www.gnu.org/copyleft/gpl.html GNU/GPL
 * @link            https://www.kunena.org
 **/

namespace Kunena\Forum\Site\Controller\Topic\Item;

\defined('_JEXEC') or die();

use Exception;
use Joomla\CMS\Factory;
use Joomla\CMS\Cache\CacheControllerFactoryInterface;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Uri\Uri;
use Joomla\Registry\Registry;
use Kunena\Forum\Libraries\Access\KunenaAccess;
use Kunena\Forum\Libraries\Attachment\KunenaAttachmentHelper;
use Kunena\Forum\Libraries\Controller\KunenaControllerDisplay;
use Kunena\Forum\Libraries\Factory\KunenaFactory;
use Kunena\Forum\Libraries\Forum\Category\KunenaCategory;
use Kunena\Forum\Libraries\Forum\Category\KunenaCategoryHelper;
use Kunena\Forum\Libraries\Forum\Message\KunenaMessageFinder;
use Kunena\Forum\Libraries\Forum\Message\KunenaMessageHelper;
use Kunena\Forum\Libraries\Forum\Message\Thankyou\KunenaMessageThankyouHelper;
use Kunena\Forum\Libraries\Forum\Topic\KunenaTopic;
use Kunena\Forum\Libraries\Forum\Topic\KunenaTopicHelper;
use Kunena\Forum\Libraries\Forum\Topic\Rate\KunenaRateHelper;
use Kunena\Forum\Libraries\Html\KunenaParser;
use Kunena\Forum\Libraries\KunenaPrivate\Message\KunenaPrivateMessageFinder;
use Kunena\Forum\Libraries\Pagination\KunenaPagination;
use Kunena\Forum\Libraries\Route\KunenaRoute;
use Kunena\Forum\Libraries\Template\KunenaTemplate;
use Kunena\Forum\Libraries\User\KunenaUser;
use Kunena\Forum\Libraries\User\KunenaUserHelper;
use RuntimeException;
use stdClass;
use Kunena\Forum\Libraries\Event\KunenaPrepareEvent;

/**
 * Class ComponentTopicControllerItemDisplay
 *
 * @since   Kunena 4.0
 */
class TopicItemDisplay extends KunenaControllerDisplay
{
    /**
     * @var     KunenaUser
     * @since   Kunena 6.0
     */
    public $me;

    /**
     * @var     KunenaCategory
     * @since   Kunena 6.0
     */
    public $category;

    /**
     * @var     KunenaTopic
     * @since   Kunena 6.0
     */
    public $topic;

    /**
     * @var     KunenaPagination
     * @since   Kunena 6.0
     */
    public $pagination;

    /**
     * @var     string
     * @since   Kunena 6.0
     */
    public $headerText;

    /**
     * @var     string
     * @since   Kunena 6.0
     */
    protected $name = 'Topic/Item';

    public $allowed;

    public $cache;

    public $catParams;

    public $categorylist;

    public $message;

    public $messages;

    public $threaded;

    public $userTopic;

    public $quickReply;

    public $image;

    public $params;
    
    public $pmHasBody;

    /**
     * Prepare topic display.
     *
     * @return  void
     *
     * @throws  null
     * @throws  Exception
     * @since   Kunena 6.0
     */
    protected function before()
    {
        parent::before();

        $catid  = $this->input->getInt('catid');
        $id     = $this->input->getInt('id');
        $mesid  = $this->input->getInt('mesid');
        $start  = $this->input->getInt('limitstart', 0);
        $limit  = $this->input->getInt('limit', 0);
        $Itemid = $this->input->getInt('Itemid');
        $format = $this->input->getInt('format');

        if (!$Itemid && $format != 'feed' && $this->config->sefRedirect) {
            try {
                $itemid = KunenaRoute::fixMissingItemID();

                $params = [
                    'option' => 'com_kunena',
                    'view' => 'topic',
                    'catid' => $catid,
                    'id' => $id,
                    'Itemid' => $itemid
                ];

                return $this->app->redirect(KunenaRoute::_('index.php?' . http_build_query($params), false));
            } catch (Exception $e) {
                throw new RuntimeException('Failed to create controller: ' . $e->getMessage());
            }
        }

        if ($limit < 1 || $limit > 100) {
            $limit = $this->config->messagesPerPage;
        }

        $this->me = KunenaUserHelper::getMyself();

        $this->allowed = md5(serialize(KunenaAccess::getInstance()->getAllowedCategories()));
        $options = ['defaultgroup' => 'com_kunena'];
        $this->cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController('output', $options);

        /*
        if ($cache->start("{$this->ktemplate->name}.common.jump.{$allowed}", 'com_kunena.template'))
         {
         return;
         }*/

        $options    = [];
        $options[] = HTMLHelper::_('select.option', '0', Text::_('COM_KUNENA_FORUM_TOP'));

        // Todo: fix params
        $this->catParams    = ['sections' => 1, 'catid' => 0];
        $this->categorylist = HTMLHelper::_(
            'kunenaforum.categorylist',
            'catid',
            0,
            $options,
            $this->catParams,
            'class="form-select" data-bs-toggle="tooltip" title="' . Text::_('COM_KUNENA_FORUM_TOP') . '" size="1" onchange = "this.form.submit()"',
            'value',
            'text'
        );

        // Load topic and message.
        if ($mesid) {
            // If message was set, use it to find the current topic.
            $this->message = KunenaMessageHelper::get($mesid);
            $this->topic   = $this->message->getTopic();
        } else {
            // Note that redirect loops throw RuntimeException because of we added \Kunena\Forum\Libraries\Forum\Topic\Topic::getTopic() call!
            $this->topic   = KunenaTopicHelper::get($id)->getTopic();
            $this->message = KunenaMessageHelper::get($this->topic->first_post_id);
        }

        // Load also category (prefer the URI variable if available).
        if ($catid && $catid != $this->topic->category_id) {
            $this->category = KunenaCategoryHelper::get($catid);
            $this->category->tryAuthorise();
        } else {
            $this->category = $this->topic->getCategory();
        }

        // Access check.
        $this->message->tryAuthorise();

        // Check if we need to redirect (category or topic mismatch, or resolve permanent URL).
        if ($this->primary) {
            $channels = $this->category->getChannels();

            if (
                $this->message->thread != $this->topic->id
                || ($this->topic->category_id != $this->category->id && !isset($channels[$this->topic->category_id]))
            ) {
                $this->app->redirect($this->message->getUrl(null, false));
            }
        }

        // Load messages from the current page and set the pagination.
        $hold   = KunenaAccess::getInstance()->getAllowedHold($this->me, $this->category->id, false);
        $finder = new KunenaMessageFinder();
        $finder
            ->where('thread', '=', $this->topic->id)
            ->filterByHold($hold);

        $start            = $mesid ? $this->topic->getPostLocation($mesid) : $start;
        $this->pagination = new KunenaPagination($finder->count(), $start, $limit);

        $this->messages = $finder
            ->order('time', $this->me->getMessageOrdering() == 'asc' ? 1 : -1)
            ->start($this->pagination->limitstart)
            ->limit($this->pagination->limit)
            ->find();

        $this->prepareMessages($mesid);
        $doc = $this->app->getDocument();

        if ($this->me->exists()) {
            $pmFinder = new KunenaPrivateMessageFinder();
            $pmFinder->filterByMessageIds(array_keys($this->messages))->order('id');

            if (!$this->me->isModerator($this->category)) {
                $pmFinder->filterByUser($this->me);
            }

            $pms = $pmFinder->find();

            foreach ($pms as $pm) {
                $registry = new Registry($pm->params);
                $posts    = $registry->get('receivers.posts');

                foreach ($posts as $post) {
                    if (!isset($this->messages[$post]->pm)) {
                        $this->messages[$post]->pm = [];
                    }
                }

                $this->messages[$post]->pm[$pm->id] = $pm;
            }
        }

        if ($this->topic->unread) {
            $doc->setMetaData('robots', 'noindex, follow');
        }

        if (!$start) {
            foreach ($doc->_links as $key => $value) {
                if (\is_array($value)) {
                    if (\array_key_exists('relation', $value)) {
                        if ($value['relation'] == 'canonical') {
                            $canonicalUrl               = $this->topic->getUrl();
                            $doc->_links[$canonicalUrl] = $value;
                            unset($doc->_links[$key]);
                            break;
                        }
                    }
                }
            }
        }

        // Run events.
        $params = new Registry();
        $params->set('ksource', 'kunena');
        $params->set('kunena_view', 'topic');
        $params->set('kunena_layout', 'default');

        PluginHelper::importPlugin('kunena');
        KunenaParser::prepareContent($content, 'topic_top');
        $prepareEvent = new KunenaPrepareEvent('onKunenaPrepare', [
            'context' => 'kunena.topic',
            'subject' => $this->topic,
            'params'  => $params,
            'page'    => 0
        ]);
        $this->app->getDispatcher()->dispatch('onKunenaPrepare', $prepareEvent);
        foreach ($this->messages as &$message) {
            $prepareEvent = new KunenaPrepareEvent('onKunenaPrepare', [
                'context' => 'kunena.messages',
                'subject' => $message,
                'params'  => $params,
                'page'    => 0
            ]);
            $this->app->getDispatcher()->dispatch('onKunenaPrepare', $prepareEvent);
        }
        // Get user data, captcha & quick reply.
        $this->userTopic  = $this->topic->getUserTopic();
        $this->quickReply = $this->topic->isAuthorised('reply') && $this->me->exists() && $this->config->quickReply;

        $this->headerText = KunenaParser::parseText($this->topic->displayField('subject'));

        $data                           = new \stdClass();
        $data->{'@context'}             = "https://schema.org";
        $data->{'@type'}                = "DiscussionForumPosting";
        $data->{'url'}                  = Uri::getInstance()->toString(['scheme', 'host', 'port']) . $this->topic->getPermaUrl();
        $data->{'discussionUrl'}        = $this->topic->getPermaUrl();
        $data->{'headline'}             = $this->headerText;
        $data->{'image'}                = $this->docImage();
        $data->{'datePublished'}        = $this->topic->getFirstPostTime()->toISO8601();

        if ($this->message->modified_time !== null) {
            $data->{'dateModified'}         = Factory::getDate($this->message->modified_time)->toISO8601();
        }

        $data->{'author'}               = [];
        $tmp                            = new \stdClass();
        $tmp->{'@type'}                 = "Person";
        $tmp->{'name'}                  = $this->topic->getLastPostAuthor()->username;
        $tmp->{'url'}                   = Uri::getInstance()->toString(['scheme', 'host', 'port']) . $this->topic->getLastPostAuthor()->getURL();
        $data->{'author'}               = $tmp;
        $data->interactionStatistic     = [];
        $tmp2                           = new \stdClass();
        $tmp2->{'@type'}                = "InteractionCounter";
        $tmp2->{'interactionType'}      = "InteractionCounter";
        $tmp2->{'userInteractionCount'} = $this->topic->getReplies();
        $data->interactionStatistic     = $tmp2;
        $tmp3                           = new \stdClass();
        $tmp3->{'@type'}                = "ImageObject";
        $tmp3->{'url'}                  = $this->docImage();
        $tmp4                           = new \stdClass();
        $tmp4->{'@type'}                = "Organization";
        $tmp4->{'name'}                 = $this->config->boardTitle;
        $tmp4->{'logo'}                 = $tmp3;
        $data->publisher                = (array) $tmp4;
        $data->mainEntityOfPage         = [];
        $tmp5                           = new \stdClass();
        $tmp5->{'@type'}                = "WebPage";
        $tmp5->{'name'}                 = Uri::getInstance()->toString(['scheme', 'host', 'port']) . $this->topic->getPermaUrl();
        $data->mainEntityOfPage         = $tmp5;

        if ($this->category->allowRatings && $this->config->ratingEnabled && KunenaRateHelper::getCount($this->topic->id) > 0) {
            $data->aggregateRating  = [];
            $tmp3                   = new \stdClass();
            $tmp3->{'@type'}        = "AggregateRating";
            $tmp3->{'itemReviewed'} = $this->headerText;
            $tmp3->{'ratingValue'}  = KunenaRateHelper::getSelected($this->topic->id) > 0 ? KunenaRateHelper::getSelected($this->topic->id) : 5;
            $tmp3->{'reviewCount'}  = KunenaRateHelper::getCount($this->topic->id);
            $data->aggregateRating  = $tmp3;
        }

        KunenaTemplate::getInstance()->addScriptDeclaration(json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), 'application/ld+json');
    }

    /**
     * Prepare messages for display.
     *
     * @param   int  $mesid  Selected message Id.
     *
     * @return  void
     *
     * @throws  Exception
     * @since   Kunena 6.0
     */
    protected function prepareMessages($mesid)
    {
        // Get thank yous for all messages in the page
        $thankyous = KunenaMessageThankyouHelper::getByMessage($this->messages);

        // First collect ids and users.
        $threaded       = ($this->layout == 'indented' || $this->layout == 'threaded');
        $userlist       = [];
        $this->threaded = [];
        $location       = $this->pagination->limitstart;

        foreach ($this->messages as $message) {
            $message->replynum = ++$location;

            if ($threaded) {
                // Threaded ordering
                if (isset($this->messages[$message->parent])) {
                    $this->threaded[$message->parent][] = $message->id;
                } else {
                    $this->threaded[0][] = $message->id;
                }
            }

            $userlist[(int) $message->userid]      = (int) $message->userid;
            $userlist[(int) $message->modified_by] = (int) $message->modified_by;

            $thankyou_list     = $thankyous[$message->id]->getList();
            $message->thankyou = [];

            if (!empty($thankyou_list)) {
                $message->thankyou = $thankyou_list;
            }
        }
        
        if (!isset($mesid)) {
            $mesidTmp = '';
        } else {
            $mesidTmp = $mesid;
        }

        if (!isset($this->messages[$mesidTmp]) && !empty($this->messages)) {
            $this->message = reset($this->messages);
        }

        if ($threaded) {
            if (!isset($this->messages[$this->topic->first_post_id])) {
                $this->messages = $this->getThreadedOrdering(0, ['edge']);
            } else {
                $this->messages = $this->getThreadedOrdering();
            }
        }

        // Prefetch all users/avatars to avoid user by user queries during template iterations
        KunenaUserHelper::loadUsers($userlist);

        // Prefetch attachments.
        KunenaAttachmentHelper::getByMessage($this->messages);
    }

    /**
     * Change ordering of the displayed messages and apply threading.
     *
     * @param   int    $parent  Parent Id.
     * @param   array  $indent  Indent for the current object.
     *
     * @return  array
     *
     * @throws  Exception
     * @since   Kunena 6.0
     */
    protected function getThreadedOrdering($parent = 0, $indent = [])
    {
        $list = [];

        if (\count($indent) == 1 && $this->topic->getTotal() > $this->pagination->limitstart + $this->pagination->limit) {
            $last = -1;
        } else {
            $last = end($this->threaded[$parent]);
        }

        foreach ($this->threaded[$parent] as $mesid) {
            $message = $this->messages[$mesid];
            $skip    = $message->id != $this->topic->first_post_id
                && $message->parent != $this->topic->first_post_id && !isset($this->messages[$message->parent]);

            if ($mesid != $last) {
                // Default sibling edge
                $indent[] = 'crossedge';
            } else {
                // Last sibling edge
                $indent[] = 'lastedge';
            }

            end($indent);
            $key = key($indent);

            if ($skip) {
                $indent[] = 'gap';
            }

            $list[$mesid]         = $this->messages[$mesid];
            $list[$mesid]->indent = $indent;

            if (empty($this->threaded[$mesid])) {
                // No children node
                // FIXME: $mesid == $message->thread
                $list[$mesid]->indent[] = ($mesid == $message->thread) ? 'single' : 'leaf';
            } else {
                // Has children node
                // FIXME: $mesid == $message->thread
                $list[$mesid]->indent[] = ($mesid == $message->thread) ? 'root' : 'node';
            }

            if (!empty($this->threaded[$mesid])) {
                // Fix edges
                if ($mesid != $last) {
                    $indent[$key] = 'edge';
                } else {
                    $indent[$key] = 'empty';
                }

                if ($skip) {
                    $indent[$key + 1] = 'empty';
                }

                $list += $this->getThreadedOrdering($mesid, $indent);
            }

            if ($skip) {
                array_pop($indent);
            }

            array_pop($indent);
        }

        return $list;
    }

    /**
     * Prepare document.
     *
     * @return  string
     *
     * @throws  Exception
     * @throws  null
     * @since   Kunena 6.0
     */
    protected function docImage()
    {
        if (is_file(JPATH_SITE . '/media/kunena/avatars/' . KunenaFactory::getUser($this->topic->getAuthor()->id)->avatar)) {
            $image = Uri::root() . 'media/kunena/avatars/' . KunenaFactory::getUser($this->topic->getAuthor()->id)->avatar;
        } elseif ($this->topic->getAuthor()->avatar == null) {
            if (is_file(JPATH_SITE . '/' . $this->config->emailHeader)) {
                $image = Uri::base() . $this->config->emailHeader;
            } else {
                $image = Uri::base() . '/media/kunena/email/hero-wide.png';
            }
        } else {
            $image = $this->topic->getAuthor()->getAvatarURL('Profile', '200');
        }

        return $image;
    }

    /**
     * After render update topic data for the user.
     *
     * @return  void
     *
     * @throws  null
     * @throws  Exception
     * @since   Kunena 6.0
     */
    protected function after()
    {
        parent::after();

        $this->topic->hit();

        $this->topic->markRead();

        // Check if subscriptions have been sent and reset the value.
        if ($this->topic->isAuthorised('subscribe') && $this->userTopic->subscribed == 2) {
            $this->userTopic->subscribed = 1;
            $this->userTopic->save();
        }
    }

    /**
     * Prepare document.
     *
     * @return  void
     *
     * @throws  Exception
     * @throws  null
     * @since   Kunena 6.0
     */
    protected function prepareDocument()
    {
        $this->image = '';
        $doc         = $this->app->getDocument();
        $this->setMetaData('og:url', Uri::current(), 'property');
        $this->setMetaData('og:type', 'article', 'property');
        $this->setMetaData('og:title', $this->topic->displayField('subject'), 'property');
        $this->setMetaData('profile:username', $this->topic->getAuthor()->username, 'property');

        $image = $this->docImage();

        $message = KunenaParser::parseText($this->topic->first_post_message);
        $matches = preg_match("/\[img]http(s?):\/\/.*\/img]/iu", $message, $title);

        if ($matches) {
            $image = substr($title[0], 5, -6);
        }

        if ($this->topic->attachments > 0) {
            $attachments = KunenaAttachmentHelper::getByMessage($this->topic->first_post_id);
            $item        = [];

            foreach ($attachments as $attach) {
                $object           = new stdClass();
                $object->path     = $attach->getUrl();
                $object->image    = $attach->isImage();
                $object->filename = $attach->filename;
                $object->folder   = $attach->folder;
                $item             = $object;
            }

            $attach = $item;

            if ($attach) {
                if (is_file(JPATH_SITE . '/' . $attach->folder . '/' . $attach->filename)) {
                    if ($attach->image) {
                        if ($this->config->attachmentProtection) {
                            $url      = $attach->path;
                            $protocol = empty($_SERVER['HTTPS']) ? 'http://' : 'https://';
                            $image    = $protocol . $_SERVER['SERVER_NAME'] . $url;
                        } else {
                            $image = $attach->path;
                        }
                    }
                }
            }
        }

        $multispaces_replaced = '';
        if (!empty($this->topic->first_post_message)) {
            $firstPostMessage = KunenaParser::stripBBCode($this->topic->first_post_message, 160);
            $multispaces_replaced = preg_replace('/\s+/', ' ', $firstPostMessage);
        }

        $this->setMetaData('og:description', $multispaces_replaced, 'property');
        $this->setMetaData('og:image', $image, 'property');
        $this->setMetaData('article:published_time', $this->topic->getFirstPostTime()->toISO8601(), 'property');
        $this->setMetaData('article:section', $this->topic->getCategory()->name, 'property');
        $this->setMetaData('twitter:card', 'summary', 'name');
        $this->setMetaData('twitter:title', $this->topic->displayField('subject'), 'name');
        $this->setMetaData('twitter:image', $image, 'property');
        $this->setMetaData('twitter:description', $multispaces_replaced);

        $robots = $this->app->get('robots');

        if ($robots == 'noindex, follow') {
            $this->setMetaData('robots', 'noindex, follow');
        } elseif ($robots == 'index, nofollow') {
            $this->setMetaData('robots', 'index, nofollow');
        } elseif ($robots == 'noindex, nofollow') {
            $this->setMetaData('robots', 'noindex, nofollow');
        } else {
            $this->setMetaData('robots', 'index, follow');
        }

        $page       = (int) $this->pagination->pagesCurrent;
        $total      = (int) $this->pagination->pagesTotal;
        $headerText = $this->headerText . ($total > 1 && $page > 1 ? " - " . Text::_('COM_KUNENA_PAGES') . " {$page}" : '');

        $pagdata = $this->pagination->getData();

        if ($pagdata->previous->link) {
            $pagdata->previous->link = str_replace('?limitstart=0', '', $pagdata->previous->link);
            $doc->addHeadLink($pagdata->previous->link, 'prev');
        }

        if ($pagdata->next->link) {
            $doc->addHeadLink($pagdata->next->link, 'next');
        }

        if ($page > 1) {
            foreach ($doc->_links as $key => $value) {
                if (\is_array($value)) {
                    if (\array_key_exists('relation', $value)) {
                        if ($value['relation'] == 'canonical') {
                            $canonicalUrl               = KunenaRoute::_();
                            $doc->_links[$canonicalUrl] = $value;
                            unset($doc->_links[$key]);
                            break;
                        }
                    }
                }
            }
        } else {
            $uri = trim(strtok($this->topic->getUrl(), '?'));
            $doc->addHeadLink($uri, 'canonical');
        }

        $menu_item = $this->app->getMenu()->getActive();

        if ($menu_item) {
            $this->params = $menu_item->getParams();
            $subject = $this->topic->subject;

            $this->setTitle($subject);

            $multispaces_replaced_desc = preg_replace('/\s+/', ' ', $this->topic->first_post_message);

            if ($total > 1 && $page > 1) {
                $small = KunenaParser::stripBBCode($multispaces_replaced_desc, 130);

                if (empty($small)) {
                    $small = $headerText;
                }

                $this->setDescription($small . " - " . Text::_('COM_KUNENA_PAGES') . " {$page}");
            } else {
                $small = KunenaParser::stripBBCode($multispaces_replaced_desc, 160);

                if (empty($small)) {
                    $small = $headerText;
                }

                $this->setDescription($small);
            }
        }
    }
}