protected function fetchNodeText() { if (!empty($this->placeHolders)) { // first try with cache $api = Api_InterfaceAbstract::instance(); $cache = $api->cacheInstance(0); $found = $cache->read(array_keys($this->placeHolders)); if (!empty($found)) { $foundValues = array(); foreach ($found as $cacheKey => $parsedText) { if ($parsedText !== false and !empty($this->cacheIdToNodeid[$cacheKey])) { $nodeId = $this->cacheIdToNodeid[$cacheKey]; $placeHolder = $this->placeHolders[$cacheKey]; $this->cache[$placeHolder] = $parsedText; unset($this->placeHolders[$cacheKey]); unset($this->pending[$placeHolder]); } } } if (!empty($this->pending)) { // we still have to parse some nodes, fetch data for them $textDataArray = Api_InterfaceAbstract::instance()->callApi('content_text', 'getDataForParse', array($this->pending)); $templateCache = vB5_Template_Cache::instance(); $phraseCache = vB5_Template_Phrase::instance(); $urlCache = vB5_Template_Url::instance(); // In BBCode parser, the templates of inner BBCode are registered first, // so they should be replaced after the outer BBCode templates. See VBV-4834. //Also- if we have a preview we're likely to need the full text, and vice versa. So if either is requested // let's parse both. $templateCache->setRenderTemplatesInReverseOrder(true); if (empty($this->previewLength)) { $options = Api_InterfaceAbstract::instance()->callApiStatic('options', 'fetchStatic', array('previewLength')); $this->previewLength = $options['previewLength']; } foreach ($this->placeHolders as $cacheKey => $placeHolder) { $nodeId = isset($this->pending[$placeHolder]) ? $this->pending[$placeHolder] : 0; if ($nodeId and !empty($textDataArray[$nodeId])) { //If we got previewtext in textDataArray, we are done. if (isset($textDataArray[$nodeId]['preview_only'])) { $previewText = $parsed = $textDataArray[$nodeId]['previewtext']; $canview = false; } else { $canview = true; list($previewText, $parsed) = $this->parseNode($textDataArray, $nodeId, $this->bbCodeOptions[$placeHolder]); // It's safe to do it here cause we already are in delayed rendering. $templateCache->replacePlaceholders($parsed); $phraseCache->replacePlaceholders($parsed); $urlCache->replacePlaceholders($parsed); // also need to replace phrase & url placeholders for preview text $phraseCache->replacePlaceholders($previewText); $urlCache->replacePlaceholders($previewText); $canview = true; } // writing to cache has been moved from parseNode() to here so that // the cached text has the placeholders replaced. (VBV-9507) // any changes to the node requires update $events = array('nodeChg_' . $nodeId); // need to update cache if channel changes options $events[] = 'nodeChg_' . $textDataArray[$nodeId]['channelid']; // also need to update if phrases have been modified $events[] = 'vB_Language_languageCache'; // write the parsed text values to cache. cache for a week. $cache->write($this->getCacheKey($nodeId, $this->bbCodeOptions[$placeHolder], false, $canview), $parsed, 10080, $events); $cache->write($this->getCacheKey($nodeId, $this->bbCodeOptions[$placeHolder], true, $canview), $previewText, 10080, $events); if ($parsed !== false) { if (stripos($placeHolder, '_pre_') === false) { $this->cache[$placeHolder] = $parsed; } else { $this->cache[$placeHolder] = $previewText; } } } } $templateCache->setRenderTemplatesInReverseOrder(false); } } }
/** * Handles a [USER] tag. Creates a link to the user profile * * @param string The username * @param string The userid * * @return string HTML representation of the tag. */ function handle_bbcode_user($username = '', $userid = '') { $userid = (int) $userid; if ($userid > 0) { // fetch URL $userInfo = array('userid' => $userid, 'username' => $username); $url = vB5_Template_Runtime::buildUrl('profile', $userInfo); vB5_Template_Url::instance()->replacePlaceholders($url); } else { $url = false; } if ($url) { return "<a href=\"{$url}\" class=\"b-bbcode-user js-bbcode-user\" data-userid=\"{$userid}\">{$username}</a>"; } else { return "<span class=\"b-bbcode-user js-bbcode-user\">{$username}</span>"; } }
/** * Handle any delayed rendering. Currently delayed urls and node texts. * * @param string * @param boolean true if we are rendering for a call to /ajax/render/ and we want CSS <link>s separate * * @return string */ protected function renderDelayed(&$final_rendered_orig, $isAjaxTemplateRender = false) { $javascript = vB5_Template_Javascript::instance(); $javascript->insertJs($final_rendered_orig); $javascript->resetPending(); $stylesheet = vB5_Template_Stylesheet::instance(); $stylesheet->insertCss($final_rendered_orig, $isAjaxTemplateRender); $stylesheet->resetPending(); $link = vB5_Template_Headlink::instance(); $link->insertLinks($final_rendered_orig); $link->resetPending(); $phrase = vB5_Template_Phrase::instance(); $phrase->replacePlaceholders($final_rendered_orig); $phrase->resetPending(); // we do not reset pending urls, since they may be required by nodetext vB5_Template_Url::instance()->replacePlaceholders($final_rendered_orig); $nodeText = vB5_Template_NodeText::instance(); $nodeText->replacePlaceholders($final_rendered_orig); $nodeText->resetPending(); $templateCache = vB5_Template_Cache::instance(); $templateCache->replaceTextOnly($final_rendered_orig); //We should keep the debug info for truly last. if (vB5_Frontend_Controller_Bbcode::needDebug()) { $config = vB5_Config::instance(); if (!$config->debug) { return $final_rendered_orig; } self::$renderedTemplateNames[] = 'debug_info'; self::$renderedTemplates[] = array('templateName' => 'debug_info', 'isParentTemplate' => (bool) 0, 'indent' => str_repeat('|----', 2)); $user = vB5_User::instance(); $this->register('user', $user, true); extract(self::$globalRegistered, EXTR_SKIP | EXTR_REFS); extract($this->registered, EXTR_OVERWRITE | EXTR_REFS); $vboptions = vB5_Template_Options::instance()->getOptions(); $vboptions = $vboptions['options']; $renderedTemplates = array('count' => count(self::$renderedTemplates), 'countUnique' => count(array_unique(self::$renderedTemplateNames)), 'templates' => self::$renderedTemplates, 'styleid' => vB5_Template_Stylevar::instance()->getPreferredStyleId()); $cssDebugLog = vB5_Template_Stylesheet::getDebugLog(); $jsDebugLog = vB5_Template_Javascript::instance()->getDebugLog(); $templateCode = $templateCache->getTemplate('debug_info'); if ($templateCache->isTemplateText()) { @eval($templateCode); } else { @(include $templateCode); } $phrase->replacePlaceholders($final_rendered); $phrase->resetPending(); $final_rendered_orig = str_replace('<!-DebugInfo-->', $final_rendered, $final_rendered_orig); } }
public function index($pageid) { //the api init can redirect. We need to make sure that happens before we echo anything $api = Api_InterfaceAbstract::instance(); $top = ''; // We should not cache register page for guest. See VBV-7695. if (vB5_Request::get('cachePageForGuestTime') > 0 and !vB5_User::get('userid') and (empty($_REQUEST['routestring']) or $_REQUEST['routestring'] != 'register' and $_REQUEST['routestring'] != 'lostpw')) { // languageid should be in the pagekey to fix VBV-8095 $fullPageKey = 'vBPage_' . md5(serialize($_REQUEST)) . '_' . vB::getCurrentSession()->get('languageid'); $styleid = vB5_Cookie::get('userstyleid', vB5_Cookie::TYPE_UINT); if (!empty($styleid)) { $fullPageKey .= '_' . $styleid; } $fullPage = vB_Cache::instance(vB_Cache::CACHE_LARGE)->read($fullPageKey); if (!empty($fullPage)) { echo $fullPage; exit; } } $preheader = vB5_ApplicationAbstract::getPreheader(); $top .= $preheader; if (vB5_Request::get('useEarlyFlush')) { echo $preheader; flush(); } $router = vB5_ApplicationAbstract::instance()->getRouter(); $arguments = $router->getArguments(); $userAction = $router->getUserAction(); $pageKey = $router->getPageKey(); $api->callApi('page', 'preload', array($pageKey)); if (!empty($userAction)) { $api->callApi('wol', 'register', array($userAction['action'], $userAction['params'], $pageKey, vB::getRequest()->getScriptPath(), !empty($arguments['nodeid']) ? $arguments['nodeid'] : 0)); } if (isset($arguments['pagenum'])) { $arguments['pagenum'] = intval($arguments['pagenum']) > 0 ? intval($arguments['pagenum']) : 1; } $pageid = (int) (isset($arguments['pageid']) ? $arguments['pageid'] : (isset($arguments['contentid']) ? $arguments['contentid'] : 0)); if ($pageid < 1) { // @todo This needs to output a user-friendly "page not found" page throw new Exception('Could not find page.'); } $page = $api->callApi('page', 'fetchPageById', array($pageid, $arguments)); if (!$page) { // @todo This needs to output a user-friendly "page not found" page throw new Exception('Could not find page.'); } // Go to the first new / unread post for this user in this topic if (!empty($_REQUEST['goto']) and $_REQUEST['goto'] == 'newpost' and !empty($arguments['nodeid']) and !empty($arguments['channelid'])) { if ($this->vboptions['threadmarking'] and vB5_User::get('userid')) { // Database read marking $channelRead = $api->callApi('node', 'getNodeReadTime', array($arguments['channelid'])); $topicRead = $api->callApi('node', 'getNodeReadTime', array($arguments['nodeid'])); $topicView = max($topicRead, $channelRead, time() - $this->vboptions['markinglimit'] * 86400); } else { // Cookie read marking $topicView = intval(vB5_Cookie::fetchBbarrayCookie('discussion_view', $arguments['nodeid'])); if (!$topicView) { $topicView = vB5_User::get('lastvisit'); } } $topicView = intval($topicView); // Get the first unread reply $goToNodeId = $api->callApi('node', 'getFirstChildAfterTime', array($arguments['nodeid'], $topicView)); if (empty($goToNodeId)) { $thread = $api->callApi('node', 'getNodes', array(array($arguments['nodeid']))); if (!empty($thread) and isset($thread[$arguments['nodeid']])) { $goToNodeId = $thread[$arguments['nodeid']]['lastcontentid']; } } if ($goToNodeId) { // Redirect to the new post $urlCache = vB5_Template_Url::instance(); $urlKey = $urlCache->register($router->getRouteId(), array('nodeid' => $arguments['nodeid']), array('p' => $goToNodeId)); $replacements = $urlCache->finalBuildUrls(array($urlKey)); $url = $replacements[$urlKey]; if ($url) { $url .= '#post' . $goToNodeId; if (headers_sent()) { echo '<script type="text/javascript">window.location = "' . $url . '";</script>'; } else { header('Location: ' . $url); } exit; } } } $page['routeInfo'] = array('routeId' => $router->getRouteId(), 'arguments' => $arguments, 'queryParameters' => $router->getQueryParameters()); $page['crumbs'] = $router->getBreadcrumbs(); $page['headlinks'] = $router->getHeadLinks(); $page['pageKey'] = $pageKey; // default value for pageSchema $page['pageSchema'] = 'http://schema.org/WebPage'; $queryParameters = $router->getQueryParameters(); /* * VBV-12506 * this is where we would add other things to clean up dangerous query params. * For VBV-12486, I'll just unset anything here that can't use vb:var in the templates, * but really we should just make a whitelist of expected page object parameters that * come from the query string and unset EVERYTHING else. For the expected ones, we * should also force the value into the expected (and hopefully safer) range */ /* * VBV-12506 * $doNotReplaceWithQueryParams is a list of parameters that the page object usually * gets naturally/internally, and we NEVER want to replace with a user provided query * parameter. (In fact, *when* exactly DO we want to do this???) * If we don't do this, it's a potential XSS vulnerability for the items that we * cannot send through vb:var for whatever reason (title for ex) * and even if they *are* sent through vb:var, the replacements can sometimes just * break the page even when it's sent through vb:var (for example, ?pagetemplateid=%0D, * the new line this inserts in var pageData = {...} in the header template tends to * break things (tested on Chrome). * Furthermore, any script that uses the pageData var would get the user injected data * that might cause more problems down the line. * Parameter Notes: * 'titleprefix' * As these two should already be html escaped, we don't want to double escape * them. So we can't us vb:var in the templates. As such, we must prevent a * malicious querystring from being injected into the page object here. * 'title' * Similar to above, but channels are allowed to have HTML in the title, so * they are intentinoally not escaped in the DB, and the templates can't use * vb:var. * 'pageid', 'channelid', 'nodeid' * These are usually set in the arguments, so the array_merge below usually * takes care of not passing a pageid query string through to the page object, * but I'm leaving them in just in case. */ $doNotReplaceWithQueryParams = array('titleprefix', 'title', 'pageid', 'channelid', 'nodeid', 'pagetemplateid', 'url', 'pagenum', 'tagCloudTitle'); foreach ($doNotReplaceWithQueryParams as $key) { unset($queryParameters[$key]); } $arguments = array_merge($queryParameters, $arguments); foreach ($arguments as $key => $value) { $page[$key] = $value; } $options = vB5_Template_Options::instance(); $page['phrasedate'] = $options->get('miscoptions.phrasedate'); $page['optionsdate'] = $options->get('miscoptions.optionsdate'); // if no meta description, use node data or global one instead, prefer node data if (empty($page['metadescription']) and !empty($page['nodedescription'])) { $page['metadescription'] = $page['nodedescription']; } if (empty($page['metadescription'])) { $page['metadescription'] = $options->get('options.description'); } $config = vB5_Config::instance(); // Non-persistent notices @todo - change this to use vB_Cookie $page['ignore_np_notices'] = vB5_ApplicationAbstract::getIgnoreNPNotices(); $templateCache = vB5_Template_Cache::instance(); $templater = new vB5_Template($page['screenlayouttemplate']); //IMPORTANT: If you add any variable to the page object here, // please make sure you add them to other controllers which create page objects. // That includes at a minimum the search controller (in two places currently) // and vB5_ApplicationAbstract::showErrorPage $templater->registerGlobal('page', $page); $page = $this->outputPage($templater->render(), false); $fullPage = $top . $page; if (!empty($fullPageKey) and is_string($fullPageKey)) { vB_Cache::instance(vB_Cache::CACHE_LARGE)->write($fullPageKey, $fullPage, vB5_Request::get('cachePageForGuestTime'), 'vbCachedFullPage'); } // these are the templates rendered for this page $loadedTemplates = vB5_Template::getRenderedTemplates(); $api->callApi('page', 'savePreCacheInfo', array($pageKey)); if (!vB5_Request::get('useEarlyFlush')) { echo $fullPage; } else { echo $page; } }
/** * Returns the URL for a route with the passed parameters * @param mixed $route - Route identifier (routeid or name) * @param array $data - Data for building route * @param array $extra - Additional data to be added * @param array $options - Options for building URL * - noBaseUrl: skips adding the baseurl * - anchor: anchor id to be added * @return type * @throws vB5_Exception_Api */ public static function buildUrl($route, $data = array(), $extra = array(), $options = array()) { return vB5_Template_Url::instance()->register($route, $data, $extra, $options); }