Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Pub/App.php
<?php

namespace XF\Pub;

use
XF\Container;
use
XF\Http\Response;
use
XF\Mvc\Renderer\AbstractRenderer;
use
XF\Mvc\Reply\AbstractReply;

use function
in_array, intval;

class
App extends \XF\App
{
    public static
$allowPageCache = true;

    protected
$isServedFromCache = false;

    protected
$preLoadLocal = [
       
'bannedIps',
       
'bbCodeCustom',
       
'discouragedIps',
       
'forumTypes',
       
'notices',
       
'noticesLastReset',
       
'routeFilters',
       
'routesPublic',
       
'styles',
       
'userFieldsInfo',
       
'threadFieldsInfo',
       
'threadPrefixes',
       
'threadTypes'
   
];

    public function
initializeExtra()
    {
       
$container = $this->container;

       
$container['app.classType'] = 'Pub';
       
$container['app.defaultType'] = 'public';

       
$container['router'] = function (Container $c)
        {
            return
$c['router.public'];
        };
       
$container['session'] = function (Container $c)
        {
            return
$c['session.public'];
        };

       
$container['pageCache'] = function(Container $c)
        {
           
$options = $c['config']['pageCache'];
            if (!
$options['enabled'])
            {
                return
null;
            }

           
$cache = $this->cache('page', false);
            if (!
$cache)
            {
                return
null;
            }

           
$pageCache = new \XF\PageCache($c['request'], $cache, $options['lifetime']);
            if (!
$pageCache->isRequestCacheable())
            {
                return
null;
            }

            if (
$options['routeMatches'] && !$pageCache->routeMatchesPrefixes((array)$options['routeMatches']))
            {
                return
null;
            }

           
$pageCache->setRecordSessionActivity($options['recordSessionActivity']);

           
$onSetup = $options['onSetup'];
            if (
$onSetup instanceof \Closure)
            {
               
$result = $onSetup($pageCache);
                if (
$result === false)
                {
                    return
null;
                }
            }

            return
$pageCache;
        };
    }

    public function
setup(array $options = [])
    {
       
parent::setup($options);
       
$this->assertConfigExists();

       
$this->fire('app_pub_setup', [$this]);
    }

    public function
start($allowShortCircuit = false)
    {
       
parent::start($allowShortCircuit);

       
$this->fire('app_pub_start_begin', [$this]);

       
$request = $this->request();
       
$guestCacher = self::$allowPageCache ? $this->pageCache() : false;
       
$guestCacheChecked = false;

        if (
$allowShortCircuit)
        {
            switch (
$request->getRequestUri())
            {
                case
'/browserconfig.xml':
                case
'/crossdomain.xml':
                case
'/favicon.ico':
                case
'/robots.txt':
                   
$response = $this->response();
                   
$response->httpCode(404);
                    return
$response;
            }

           
$extendedUrl = ltrim($request->getExtendedUrl(), '/');
           
$sitemapCounter = null;
            if (
$extendedUrl == 'sitemap.xml')
            {
               
$sitemapCounter = 0;
            }
            else if (
preg_match('#^sitemap-(\d+)\.xml$#', $extendedUrl, $match))
            {
               
$sitemapCounter = intval($match[1]);
            }

            if (
$sitemapCounter !== null)
            {
               
/** @var \XF\Sitemap\Renderer $renderer */
               
$renderer = $this['sitemap.renderer'];
                return
$renderer->outputSitemap($this->response(), $sitemapCounter);
            }

            if (
$guestCacher && $guestCacher->isDefinitelyGuest())
            {
               
$cacheResponse = $guestCacher->getCachedPage($this);
                if (
$cacheResponse)
                {
                   
$this->isServedFromCache = true;
                    return
$cacheResponse;
                }
               
$guestCacheChecked = true;
            }
        }

       
$session = $this->session();
        if (!
$session->exists())
        {
           
$this->onSessionCreation($session);
        }

       
$user = $this->getVisitorFromSession($session);
        \
XF::setVisitor($user);

        if (
$allowShortCircuit && !$user->user_id && $guestCacher && !$guestCacheChecked)
        {
           
$cacheResponse = $guestCacher->getCachedPage($this);
            if (
$cacheResponse)
            {
               
$this->isServedFromCache = true;
                return
$cacheResponse;
            }
        }

       
$visitor = \XF::visitor();
        if (
$visitor->user_id)
        {
           
$languageId = $visitor->language_id;
        }
        else
        {
           
$styleId = intval($request->getCookie('style_id', 0));
           
$languageId = intval($request->getCookie('language_id', 0));
           
$username = $request->filter('_xfUsername', 'str', '');

           
$visitor->setReadOnly(false);
           
$visitor->setAsSaved('username', $username);
           
$visitor->setAsSaved('style_id', $styleId);
           
$visitor->setAsSaved('language_id', $languageId);
           
$visitor->setReadOnly(true);
        }

       
$language = $this->language($languageId);
        if (!
$language->isUsable($visitor))
        {
           
$language = $this->language(0);
        }

       
$language->setTimeZone($visitor->timezone);
        \
XF::setLanguage($language);

       
$this->updateUserCaches();
       
$this->updateModeratorCaches();

       
$this->fire('app_pub_start_end', [$this]);

        return
null;
    }

    protected function
updateUserCaches()
    {
       
$visitor = \XF::visitor();
       
$session = $this->session();

        if (!
$visitor->user_id)
        {
            return;
        }

        if (
$this->options()->enableNotices)
        {
            if (!
$session->keyExists('dismissedNotices'))
            {
               
$updateDismissed = true;
            }
            else
            {
               
$sessionLastNoticeUpdate = intval($session->get('lastNoticeUpdate'));
               
$dbLastNoticeReset = $this->get('notices.lastReset');
               
$updateDismissed = ($dbLastNoticeReset > $sessionLastNoticeUpdate);
            }

            if (
$updateDismissed)
            {
               
$session->dismissedNotices = $this->repository('XF:Notice')->getDismissedNoticesForUser($visitor);
               
$session->lastNoticeUpdate = \XF::$time;
            }
        }

        if (!
$session->promotionChecked)
        {
           
$session->promotionChecked = true;

           
// if we've recently been active, let cron handle it
           
if ($visitor->getValue('last_activity') < \XF::$time - 1800)
            {
               
/** @var \XF\Repository\UserGroupPromotion $userGroupPromotionRepo */
               
$userGroupPromotionRepo = $this->repository('XF:UserGroupPromotion');
               
$userGroupPromotionRepo->updatePromotionsForUser($visitor);
            }
        }

        if (
$this->options()->enableTrophies && !$session->trophyChecked)
        {
           
$session->trophyChecked = true;

           
// if we've recently been active, let cron handle it
           
if ($visitor->getValue('last_activity') < \XF::$time - 1800)
            {
               
/** @var \XF\Repository\Trophy $trophyRepo */
               
$trophyRepo = $this->repository('XF:Trophy');
               
$trophyRepo->updateTrophiesForUser($visitor);
            }
        }

        if (!
$session->keyExists('previousActivity'))
        {
           
$session->previousActivity = $visitor->getValue('last_activity'); // skip the getter to get what's in the DB
       
}

        if (
$visitor->alerts_unviewed && !$session->alertCountChecked)
        {
           
$session->alertCountChecked = true;

           
// count unread/unviewed alerts if last activity was over 30 days ago (the alert expiry cut off)
           
if ($visitor->getValue('last_activity') < \XF::$time - (30 * 86400))
            {
               
/** @var \XF\Repository\UserAlert $alertRepo */
               
$alertRepo = $this->repository('XF:UserAlert');
               
$alertRepo->updateUnviewedCountForUser($visitor);
               
$alertRepo->updateUnreadCountForUser($visitor);
            }
        }

        if (
$visitor->last_summary_email_date === 0)
        {
           
$visitor->fastUpdate('last_summary_email_date', \XF::$time);
        }
    }

    protected function
updateModeratorCaches()
    {
       
$visitor = \XF::visitor();
       
$session = $this->session();

        if (!
$visitor->is_moderator)
        {
            return;
        }

       
$sessionReportCounts = $session->reportCounts;
       
$registryReportCounts = $this->container->reportCounts;

        if (
$sessionReportCounts === null
           
|| ($sessionReportCounts && ($sessionReportCounts['lastBuilt'] < $registryReportCounts['lastModified']))
        )
        {
           
/** @var \XF\Finder\Report $reportsFinder */
           
$reportsFinder = $this->finder('XF:Report');
           
$reports = $reportsFinder->isActive()->fetch()->filterViewable();

           
$total = 0;
           
$assigned = 0;

            foreach (
$reports AS $reportId => $report)
            {
               
$total++;
                if (
$report->assigned_user_id == $visitor->user_id)
                {
                   
$assigned++;
                }
            }

           
$reportCounts = [
               
'total' => $total,
               
'assigned' => $assigned,
               
'lastBuilt' => $registryReportCounts['lastModified']
            ];

           
$session->reportCounts = $reportCounts;
        }

       
$sessionUnapprovedCounts = $session->unapprovedCounts;
       
$registryUnapprovedCounts = $this->container->unapprovedCounts;

        if (
$sessionUnapprovedCounts === null
           
|| ($sessionUnapprovedCounts && ($sessionUnapprovedCounts['lastBuilt'] < $registryUnapprovedCounts['lastModified']))
        )
        {
           
/** @var \XF\Repository\ApprovalQueue $approvalQueueRepo */
           
$approvalQueueRepo = $this->repository('XF:ApprovalQueue');

           
$unapprovedItems = $approvalQueueRepo->findUnapprovedContent()->fetch();
           
$approvalQueueRepo->addContentToUnapprovedItems($unapprovedItems);
           
$unapprovedItems = $approvalQueueRepo->filterViewableUnapprovedItems($unapprovedItems);

           
$unapprovedCounts = [
               
'total' => $unapprovedItems->count(),
               
'lastBuilt' => $registryUnapprovedCounts['lastModified']
            ];

           
$session->unapprovedCounts = $unapprovedCounts;
        }
    }

    protected function
onSessionCreation(\XF\Session\Session $session)
    {
       
$loginUserId = $this->loginFromRememberCookie($session);

        if (!
$loginUserId)
        {
           
$this->request()->populateFromSearch($this->response());
        }
    }

    protected function
loginFromRememberCookie(\XF\Session\Session $session)
    {
       
$rememberCookie = $this->request()->getCookie('user');
        if (!
$rememberCookie)
        {
            return
null;
        }

       
/** @var \XF\Repository\UserRemember $rememberRepo */
       
$rememberRepo = $this->repository('XF:UserRemember');
        if (!
$rememberRepo->validateByCookieValue($rememberCookie, $remember))
        {
           
$this->response()->setCookie('user', false);
            return
null;
        }

       
/** @var \XF\Repository\User $userRepo */
       
$userRepo = $this->repository('XF:User');
       
$user = $userRepo->getVisitor($remember->user_id);
        if (!
$user)
        {
            return
null;
        }

       
$trustKey = $this->request()->getCookie('tfa_trust');

       
/** @var \XF\Repository\Tfa $tfaRepo */
       
$tfaRepo = $this->repository('XF:Tfa');
        if (
$tfaRepo->isUserTfaConfirmationRequired($user, $trustKey))
        {
           
$session->tfaLoginUserId = $user->user_id;
           
$session->tfaLoginDate = time();
           
$session->tfaLoginRedirect = true;

            return
null;
        }

       
$session->changeUser($user);

       
/** @var \XF\Repository\Ip $ipRepo */
       
$ipRepo = $this->repository('XF:Ip');
       
$ipRepo->logCookieLoginIfNeeded($user->user_id, $this->request()->getIp());

       
/** @var \XF\Entity\UserRemember $remember */
       
$remember->extendExpiryDate();
       
$remember->save();

        return
$remember->user_id;
    }

    public function
preRender(AbstractReply $reply, $responseType)
    {
       
$visitor = \XF::visitor();

       
$viewOptions = $reply->getViewOptions();
        if (!empty(
$viewOptions['style_id']))
        {
           
$styleId = $viewOptions['style_id'];
           
$forceStyle = true;
        }
        else
        {
           
$styleId = $visitor->style_id;
           
$forceStyle = false;
        }

       
/** @var \XF\Style $style */
       
$style = $this->container->create('style', $styleId);
        if (
$style['style_id'] == $styleId)
        {
           
// true if the style matches the requested one; if it didn't just accept it
           
if (!$forceStyle && !$style->isUsable($visitor))
            {
               
$style = $this->container->create('style', 0);
            }
        }

       
$this->templater()->setStyle($style);

        if (!empty(
$viewOptions['sessionActivity']) && self::$allowPageCache && !\XF::visitor()->user_id)
        {
           
$guestCacher = $this->pageCache();
            if (
$guestCacher)
            {
               
$guestCacher->setSessionActivity($viewOptions['sessionActivity']);
            }
        }

       
parent::preRender($reply, $responseType);
    }

    public function
complete(Response $response)
    {
       
parent::complete($response);

        if (
$this->container->isCached('session'))
        {
           
$session = $this->session();
            if (
$session->isStarted() && $session->hasData())
            {
               
$session->save();
               
$session->applyToResponse($response);
            }
           
// don't save empty sessions as it would generally be pointless
       
}

        if (
self::$allowPageCache && !$this->isServedFromCache && !\XF::visitor()->user_id)
        {
           
$guestCacher = $this->pageCache();
            if (
$guestCacher)
            {
               
$guestCacher->saveToCache($response, $this);
            }
        }

       
$this->fire('app_pub_complete', [$this, &$response]);
    }

    protected function
renderPageHtml($content, array $params, AbstractReply $reply, AbstractRenderer $renderer)
    {
       
$templateName = $params['template'] ?? 'PAGE_CONTAINER';
        if (!
$templateName)
        {
            return
$content;
        }

       
$templater = $this->templater();

        if (!
strpos($templateName, ':'))
        {
           
$templateName = 'public:' . $templateName;
        }

       
$pageSection = $reply->getSectionContext();
        if (isset(
$params['section']))
        {
           
$pageSection = $params['section'];
           
$reply->setSectionContext($pageSection);
        }
       
$params['pageSection'] = $pageSection;

       
$params['controller'] = $reply->getControllerClass();
       
$params['action'] = $reply->getAction();
       
$params['actionMethod'] = 'action' . \XF\Util\Php::camelCase($reply->getAction(), '-');

       
$params['classType'] = $this->container('app.classType');
       
$params['containerKey'] = $reply->getContainerKey();
       
$params['contentKey'] = $reply->getContentKey();

        if (
$reply instanceof \XF\Mvc\Reply\View)
        {
           
$params['view'] = $reply->getViewClass();
           
$params['template'] = $reply->getTemplateName();
        }
        else if (
$reply instanceof \XF\Mvc\Reply\Error || $reply->getResponseCode() >= 400)
        {
           
$params['template'] = 'error';
        }
        else if (
$reply instanceof \XF\Mvc\Reply\Message)
        {
           
$params['template'] = 'message_page';
        }

       
$params['fromSearch'] = $this->request()->getFromSearch();
       
$params['pageStyleId'] = $templater->getStyleId();

       
$navTree = $this->getNavigation($params, $pageSection)['tree'];
       
$params['navTree'] = $navTree;

       
// note that this intentionally only selects a top level entry
       
if (isset($navTree[$pageSection]))
        {
           
$selectedNavEntry = $navTree[$pageSection];
        }
        else
        {
           
$defaultNavId = $this->get('defaultNavigationId');
           
$selectedNavEntry = $navTree[$defaultNavId] ?? null;
        }

       
$params['selectedNavEntry'] = $selectedNavEntry;
       
$params['selectedNavChildren'] = !empty($selectedNavEntry['children']) ? $selectedNavEntry['children'] : [];

       
$params['content'] = $content;
       
$params['notices'] = $this->getNoticeList($params)->getNotices();

       
$skipSidebarWidgets = $params['skipSidebarWidgets'] ?? false;

       
// TODO: These positions should receive some context (could just pass in $params but we want this for non global positions too)
       
if (!$skipSidebarWidgets)
        {
           
$topWidgets = $templater->widgetPosition('pub_sidebar_top');
           
$bottomWidgets = $templater->widgetPosition('pub_sidebar_bottom');
           
$templater->modifySidebarHtml('_xfWidgetPositionPubSidebarTop', $topWidgets, 'prepend');
           
$templater->modifySidebarHtml('_xfWidgetPositionPubSidebarBottom', $bottomWidgets, 'append');

           
$params['sidebar'] = $templater->getSidebarHtml();
           
$params['sideNav'] = $templater->getSideNavHtml();
        }

       
$this->fire('app_pub_render_page', [$this, &$params, $reply, $renderer]);

        return
$templater->renderTemplate($templateName, $params);
    }

    protected function
getNavigation(array $params, $selectedNav = '')
    {
       
$navigation = null;

       
$file = \XF\Util\File::getCodeCachePath() . '/' . $this->container['navigation.file'];
        if (
file_exists($file))
        {
           
$closure = include($file);
            if (
$closure)
            {
               
$navigation = $this->templater()->renderNavigationClosure($closure, $selectedNav, $params);
            }
        }

        if (!
$navigation || !isset($navigation['tree']))
        {
           
$navigation = [
               
'tree' => [],
               
'flat' => []
            ];
        }

       
$this->fire('navigation_setup', [$this, &$navigation['flat'], &$navigation['tree']]);

        return
$navigation;
    }

    protected function
getNoticeList(array $pageParams)
    {
       
$class = $this->extendClass('XF\NoticeList');
       
/** @var \XF\NoticeList $noticeList */
       
$noticeList = new $class($this, \XF::visitor(), $pageParams);

       
$dismissedNotices = $this->session()->dismissedNotices;
        if (
$dismissedNotices)
        {
           
$noticeList->setDismissed($dismissedNotices);
        }
       
$this->addDefaultNotices($noticeList, $pageParams);

        if (
$this->options()->enableNotices)
        {
            foreach (
$this->container('notices') AS $key => $notice)
            {
               
$noticeList->addConditionalNotice($key, $notice['notice_type'], $notice['message'], $notice);
            }
        }

       
$this->fire('notices_setup', [$this, $noticeList, $pageParams]);

        return
$noticeList;
    }

    protected function
addDefaultNotices(\XF\NoticeList $noticeList, array $pageParams)
    {
       
$options = $this->options();
       
$visitor = \XF::visitor();
       
$templater = $this->templater();

        if (\
XF::$debugMode && \XF::$versionId != $options->currentVersionId)
        {
           
$noticeList->addNotice('upgrade_pending', 'block',
               
$templater->renderTemplate('public:notice_upgrade_pending', $pageParams),
                [
'display_style' => 'accent']
            );
        }

        if (!
$options->boardActive && $visitor->is_admin)
        {
           
$noticeList->addNotice('board_closed', 'block',
               
$templater->renderTemplate('public:notice_board_closed', $pageParams),
                [
'display_style' => 'accent']
            );
        }

        if (
$visitor->user_id && in_array($visitor->user_state, ['email_confirm', 'email_confirm_edit']))
        {
           
$noticeList->addNotice('confirm_email', 'block',
               
$templater->renderTemplate('public:notice_confirm_email', $pageParams)
            );
        }

        if (
$visitor->user_id && $visitor->user_state == 'email_bounce')
        {
           
$noticeList->addNotice('email_bounce', 'block',
               
$templater->renderTemplate('public:notice_email_bounce', $pageParams)
            );
        }

        if (
$visitor->user_id && $visitor->user_state == 'moderated')
        {
           
$noticeList->addNotice('moderated', 'block',
               
$templater->renderTemplate('public:notice_moderated', $pageParams)
            );
        }

        if (
$visitor->canUsePushNotifications())
        {
           
$noticeList->addNotice('enable_push', 'bottom_fixer',
               
$templater->renderTemplate('public:notice_enable_push', $pageParams),
                [
                   
'display_style' => 'custom',
                   
'css_class' => 'notice--primary notice--enablePush js-enablePushContainer'
               
]
            );
        }

        if (!
$visitor->user_id && $this->options()->showFirstCookieNotice)
        {
           
$noticeList->addNotice('cookies', 'bottom_fixer',
               
$templater->renderTemplate('public:notice_cookies', $pageParams),
                [
                   
'dismissible' => true,
                   
'notice_id' => -1,
                   
'custom_dismissible' => true,
                   
'display_style' => 'custom',
                   
'css_class' => 'notice--primary notice--cookie'
               
]
            );
        }
    }

   
/**
     * @return \XF\PageCache|null
     */
   
public function pageCache()
    {
        return
$this->container['pageCache'];
    }

    public function
isServedFromCache(): bool
   
{
        return
$this->isServedFromCache;
    }
}