/** * Sends the response as a JSON encoded string * * @param mixed The data (usually an array) to send */ public function sendAsJson($data) { //This function needs to be kept in sync with the implmentation in applicationlight.php if (headers_sent($file, $line)) { throw new Exception("Cannot send response, headers already sent. File: {$file} Line: {$line}"); } // We need to convert $data charset if we're not using UTF-8 if (vB5_String::getTempCharset() != 'UTF-8') { $data = vB5_String::toCharset($data, vB5_String::getTempCharset(), 'UTF-8'); } //If this is IE9, IE10, or IE11 -- we also need to work around the deliberate attempt to break "is IE" logic by the //IE dev team -- we need to send type "text/plain". Yes, we know that's not the standard. if (isset($_SERVER['HTTP_USER_AGENT']) && (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false or strpos($_SERVER['HTTP_USER_AGENT'], 'Trident') !== false)) { header('Content-type: text/plain; charset=UTF-8'); } else { header('Content-type: application/json; charset=UTF-8'); } // IE will cache ajax requests, and we need to prevent this - VBV-148 header('Cache-Control: max-age=0,no-cache,no-store,post-check=0,pre-check=0'); header('Expires: Sat, 1 Jan 2000 01:00:00 GMT'); header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); header("Pragma: no-cache"); if (isset($data['template']) and !empty($data['template'])) { $data['template'] = $this->outputPage($data['template'], false); } echo vB5_String::jsonEncode($data); }
public function actionOutput() { $api = Api_InterfaceAbstract::instance(); $response = $api->callApi('session', 'getGuestSession'); if (is_array($response) and !empty($response['errors'])) { return ''; } $type = !empty($_REQUEST['type']) ? $_REQUEST['type'] : ''; // default rss2 switch ($type) { case 'rss2': case 'rss1': case 'rss': case 'xml': case 'js': $type = $_REQUEST['type']; break; default: $type = 'rss2'; break; } if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) and !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { $response = $api->callApi('external', 'getCacheData', array('type' => $type, 'options' => $_REQUEST)); if (is_array($response) and !empty($response['errors'])) { return ''; } if ($_SERVER['HTTP_IF_NONE_MATCH'] == "\"{$response['cachehash']}\"") { $timediff = strtotime(gmdate('D, d M Y H:i:s') . ' GMT') - strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']); if ($timediff <= $response['cachetime']) { if (SAPI_NAME == 'cgi' or SAPI_NAME == 'cgi-fcgi') { header('Status: 304 Not Modified'); } else { header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified'); } exit; } } } // always disable nohtml $_REQUEST['nohtml'] = 0; $response = $api->callApi('external', 'createExternalOutput', array('type' => $type, 'options' => $_REQUEST)); if (is_array($response) and !empty($response['errors'])) { return ''; } $data = $_REQUEST + array('Pragma' => '', 'Content-Type' => vB5_String::getTempCharset()); $headers = $api->callApi('external', 'getHeadersFromLastOutput', array('type' => $type, 'data' => $data)); if (is_array($headers) and !empty($headers['errors'])) { return ''; } foreach ($headers as $name => $value) { header("{$name}: {$value}"); } return $response; }
/** Responds to a request to create a new user. * **/ public function actionRegistration() { //We need at least a username, email, and password. if (empty($_REQUEST['username']) or empty($_REQUEST['password']) or empty($_REQUEST['email'])) { $this->sendAsJson(array('error' => 'insufficient data')); return; } $username = trim($_REQUEST['username']); $password = trim($_REQUEST['password']); $postdata = array('username' => $username, 'email' => $_REQUEST['email']); if (isset($_REQUEST['month']) and isset($_REQUEST['day']) and !empty($_REQUEST['year'])) { $postdata['birthday'] = $_REQUEST['year'] . '-' . str_pad($_REQUEST['month'], 2, '0', STR_PAD_LEFT) . '-' . str_pad($_REQUEST['day'], 2, '0', STR_PAD_LEFT); } if (!empty($_REQUEST['guardian'])) { $postdata['parentemail'] = $_REQUEST['guardian']; } $vboptions = vB5_Template_Options::instance()->getOptions(); $vboptions = $vboptions['options']; // Coppa cookie check $coppaage = vB5_Cookie::get('coppaage', vB5_Cookie::TYPE_STRING); if ($vboptions['usecoppa'] and $vboptions['checkcoppa']) { if ($coppaage) { $dob = explode('-', $coppaage); $month = $dob[0]; $day = $dob[1]; $year = $dob[2]; $postdata['birthday'] = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-' . str_pad($day, 2, '0', STR_PAD_LEFT); } else { vB5_Cookie::set('coppaage', $_REQUEST['month'] . '-' . $_REQUEST['day'] . '-' . $_REQUEST['year'], 365, 0); } } // Fill in ReCaptcha data $recaptchaData = array(); if (!empty($_REQUEST['recaptcha_challenge_field'])) { $recaptchaData['recaptcha_challenge_field'] = $_REQUEST['recaptcha_challenge_field']; } if (!empty($_REQUEST['recaptcha_response_field'])) { $recaptchaData['recaptcha_response_field'] = $_REQUEST['recaptcha_response_field']; } if (!empty($recaptchaData)) { $_REQUEST['humanverify'] = $recaptchaData + (isset($_REQUEST['humanverify']) ? (array) $_REQUEST['humanverify'] : array()); } $api = Api_InterfaceAbstract::instance(); $data = array('userid' => 0, 'password' => $password, 'user' => $postdata, array(), array(), 'userfield' => !empty($_REQUEST['userfield']) ? $_REQUEST['userfield'] : false, array(), isset($_REQUEST['humanverify']) ? $_REQUEST['humanverify'] : '', array('registration' => true)); // add facebook data if ($api->callApi('facebook', 'isFacebookEnabled') && $api->callApi('facebook', 'userIsLoggedIn')) { $fbUserInfo = $api->callApi('facebook', 'getFbUserInfo'); $data['user']['fbuserid'] = $fbUserInfo['id']; $data['user']['fbname'] = $fbUserInfo['name']; $data['user']['timezoneoffset'] = $fbUserInfo['timezone']; $data['user']['fbjoindate'] = time(); $fb_profilefield_info = $this->getFacebookProfileinfo($fbUserInfo); if (!empty($fb_profilefield_info['birthday']) and empty($data['user']['birthday'])) { $data['user']['birthday'] = $fb_profilefield_info['birthday']; } if (empty($data['userfield'])) { $data['userfield'] = array(); } if ($vboptions['fb_userfield_biography']) { $data['userfield'] += array($vboptions['fb_userfield_biography'] => $fb_profilefield_info['biography']); } if ($vboptions['fb_userfield_location']) { $data['userfield'] += array($vboptions['fb_userfield_location'] => $fb_profilefield_info['location']); } if ($vboptions['fb_userfield_occupation']) { $data['userfield'] += array($vboptions['fb_userfield_occupation'] => $fb_profilefield_info['occupation']); } } // save data $response = $api->callApi('user', 'save', $data); if (!empty($response) and (!is_array($response) or !isset($response['errors']))) { // try to login $loginInfo = $api->callApi('user', 'login', array($username, $password, '', '', '')); if (!isset($loginInfo['errors']) or empty($loginInfo['errors'])) { // browser session expiration vB5_Cookie::set('sessionhash', $loginInfo['sessionhash'], 0, true); vB5_Cookie::set('password', $loginInfo['password'], 0); vB5_Cookie::set('userid', $loginInfo['userid'], 0); $urlPath = ''; if (!empty($_POST['urlpath'])) { $urlPath = base64_decode(trim($_POST['urlpath']), true); } if (!$urlPath or strpos($urlPath, '/auth/') !== false or strpos($urlPath, '/register') !== false or !vB5_Template_Runtime::allowRedirectToUrl($urlPath)) { $urlPath = vB5_Template_Options::instance()->get('options.frontendurl'); } $response = array('urlPath' => $urlPath); } else { if (!empty($loginInfo['errors'])) { $response = array('errors' => $loginInfo['errors']); } } if ($api->callApi('user', 'usecoppa')) { $response['usecoppa'] = true; $response['urlPath'] = vB5_Route::buildUrl('coppa-form|bburl'); } else { if ($vboptions['verifyemail']) { $response['msg'] = 'registeremail'; $response['msg_params'] = array(vB5_String::htmlSpecialCharsUni($postdata['username']), $postdata['email'], vB5_Template_Options::instance()->get('options.frontendurl')); } else { if ($vboptions['moderatenewmembers']) { $response['msg'] = 'moderateuser'; $response['msg_params'] = array(vB5_String::htmlSpecialCharsUni($postdata['username']), vB5_Template_Options::instance()->get('options.frontendurl')); } else { $frontendurl = vB5_Template_Options::instance()->get('options.frontendurl'); $routeProfile = $api->callApi('route', 'getUrl', array('route' => 'profile', 'data' => array('userid' => $loginInfo['userid']), array())); $routeuserSettings = $api->callApi('route', 'getUrl', array('route' => 'settings', 'data' => array('tab' => 'profile'), array())); $routeAccount = $api->callApi('route', 'getUrl', array('route' => 'settings', 'data' => array('tab' => 'account'), array())); $response['msg'] = 'registration_complete'; $response['msg_params'] = array(vB5_String::htmlSpecialCharsUni($postdata['username']), $frontendurl . $routeProfile, $frontendurl . $routeAccount, $frontendurl . $routeuserSettings, $frontendurl); } } } } $this->sendAsJson(array('response' => $response)); }
/** * Converts Unicode entities of the format %uHHHH where each H is a hexadecimal * character to &#DDDD; or the appropriate UTF-8 character based on current charset. * * @param Mixed array or text * * @return string Decoded text */ public static function convertUrlencodedUnicode($text) { if (is_array($text)) { foreach ($text as $key => $value) { $text["{$key}"] = self::convertUrlencodedUnicode($value); } return $text; } $charset = self::getTempCharset(); $return = preg_replace_callback('#%u([0-9A-F]{1,4})#i', function ($matches) use($charset) { return vB5_String::convertUnicodeCharToCharset(hexdec($matches[1]), $charset); }, $text); $lower_charset = strtolower($charset); if ($lower_charset != 'utf-8' and function_exists('html_entity_decode')) { // this converts certain { entities to their actual character // set values; don't do this if using UTF-8 as it's already done above. // note: we don't want to convert >, etc as that undoes the effects of STR_NOHTML $return = preg_replace('#&([a-z]+);#i', '&$1;', $return); if ($lower_charset == 'windows-1251') { // there's a bug in PHP5 html_entity_decode that decodes some entities that // it shouldn't. So double encode them to ensure they don't get decoded. $return = preg_replace('/&#(128|129|1[3-9][0-9]|2[0-4][0-9]|25[0-5]);/', '&#$1;', $return); } $return = @html_entity_decode($return, ENT_NOQUOTES, $charset); } return $return; }
/** * Callback for preg_replace_callback used in handle_bbcode_html */ protected function handleBBCodeTagPregMatch2($matches) { return $this->handle_bbcode_html_tag(vB5_String::htmlSpecialCharsUni($matches[1])); }
public static function getPreheader() { $templater = new vB5_Template('preheader'); if (self::$needCharset) { $templater->register('charset', vB5_String::getTempCharset()); } else { $templater->register('charset', false); } return $templater->render(); }
/** * Determines whether a string contains an [img] tag. * * @param string Text to search * * @return bool Whether the text contains an [img] tag */ protected function containsBbcodeImgTags($text) { // use a bitfield system to look for img, attach, and sigpic tags $hasimage = 0; if (vB5_String::stripos($text, '[/img]') !== false) { $hasimage += self::BBCODE_HAS_IMG; } if (vB5_String::stripos($text, '[/attach]') !== false) { $hasimage += self::BBCODE_HAS_ATTACH; } if (vB5_String::stripos($text, '[/sigpic]') !== false) { // permissions are checked on API method if (!empty($this->parseUserinfo['userid']) and !empty($this->parseUserinfo['sigpic'])) { $hasimage += self::BBCODE_HAS_SIGPIC; } } if (vB5_String::stripos($text, '[/relpath]') !== false) { $hasimage += self::BBCODE_HAS_RELPATH; } return $hasimage; }
/** * Break the keyword search into words * @param string keywords -- keyword string as entered by the user * @return array -- array of word records * array('word' => $word, 'joiner' => {'', 'NOT', 'AND', 'OR'}) * The search implementation is expected to use these to build the search * query. */ private function get_words($keywords) { $is_mb = preg_match('/&#([0-9]+);|[^\\x00-\\x7F]/siU', $keywords); // @todo handleing for thousand and decimal separators for numbers // removing punctuation $origKeywords = $keywords; $keywords = preg_replace('#(?!-)[\\p{Pd}\\p{Pe}\\p{Pf}\\p{Pi}\\p{Po}\\p{Ps}]#' . ($is_mb ? 'u' : ''), ' ', $keywords); // a tokenizing based approach to building a search query preg_match_all('#("[^"]*"|[^\\s]+)#', $keywords, $matches, PREG_SET_ORDER); $token_joiner = null; $words = array(); foreach ($matches as $match) { if ($is_mb) { $match = preg_replace_callback('/&#([0-9]+);/siU', function ($matches) { return vB5_String::convertIntToUtf8($matches[1]); }, $match); } if ($is_mb) { $token = vB_String::vBStrToLower($match[1]); } else { $token = strtolower($match[1]); } //this means that we implicitly have a not joiner. if ($token[0] == '-') { //this effectively means two joiners, which is bad. if ($token_joiner) { $this->add_error('invalid_search_syntax'); } else { $token = substr($token, 1); $token_joiner = 'not'; } } switch ($token) { case 'or': case 'and': case 'not': // this isn't a searchable word, but a joiner $token_joiner = strtoupper($token); break; default: //$lowWord = strtolower($token); if (vB_Api_Search::is_index_word($token, true)) { $words[] = array('word' => $token, 'joiner' => strtoupper($token_joiner)); } else { $this->ignored_keywords[] = $match[1]; } $token_joiner = null; break; } } if (empty($matches) and !empty($origKeywords)) { $this->ignored_keywords[] = $origKeywords; } return $words; }
/** * Performs the actual merging, using edited input from UI. * @param type $data - Contains pairs (value, name) from edit form in addition to the following fields: * * mergePosts - posts to be merged * * destnodeid - target post * * destauthorid - author to be used * * contenttype - target contenttype */ public function mergePosts($input) { $this->inlinemodAuthCheck(); $cleaner = vB::getCleaner(); $data = array(); foreach ($input as $i) { $name = $cleaner->clean($i['name'], vB_Cleaner::TYPE_NOHTML); //mostly the data is either integer or string, although the possibility exists of a //'url_image' switch ($name) { case 'nodeid': case 'url_nopreview': case 'nodeuserid': case 'filedataid': case 'destauthorid': case 'destnodeid': $value = $cleaner->clean($i['value'], vB_Cleaner::TYPE_UINT); break; case 'title': case 'text': case 'reason': $value = $cleaner->clean($i['value'], vB_Cleaner::TYPE_STR); break; case 'url_title': case 'authorname': case 'url': case 'url_image': case 'url_meta': $value = $cleaner->clean($i['value'], vB_Cleaner::TYPE_NOHTML); break; case 'mergePosts': if (!is_array($i['value'])) { $i['value'] = explode(',', $i['value']); } $value = $cleaner->clean($i['value'], vB_Cleaner::TYPE_ARRAY_UINT); break; case 'filedataid[]': //The filedata records are passed as //input[xx][name] filedataid[] //input[xx][value] <integer> $value = $cleaner->clean($i['value'], vB_Cleaner::TYPE_UINT); if (!isset($data['filedataid'])) { $data['filedataid'] = array(); } if (!isset($data['filedataid'][$value])) { $data['filedataid'][$value] = ''; } continue; default: //The title records are passed as //input[xx][name] title_<filedataid> //input[xx][value] <title> if (empty($name)) { continue; } if (substr($name, 0, 6) == 'title_') { $filedataid = substr($name, 6); $filedataid = $cleaner->clean($filedataid, vB_Cleaner::TYPE_UINT); if ($filedataid) { if (!isset($data['filedataid'])) { $data['filedataid'] = array(); } $data['filedataid'][$filedataid] = $cleaner->clean($i['value'], vB_Cleaner::TYPE_NOHTML); } continue; } else { if (preg_match('#^videoitems\\[([\\d]+)#', $name, $matches)) { if (!isset($data['videoitems'])) { $data['videoitems'] = array(); } $videoitems[] = array('videoitemid' => intval($matches[1]), 'url' => $i['value']); } else { if (preg_match('^videoitems\\[new^', $name, $matches)) { if (!isset($data['videoitems'])) { $data['videoitems'] = array(); } foreach ($matches as $video) { $data['videoitems'][] = array('url' => $video['url']); } } } } continue; } if (isset($data[$name])) { if (!is_array($data[$name])) { $data[$name] = array($data[$name]); } $data[$name][] = $value; } else { $data[$name] = $value; } } if (empty($data['mergePosts'])) { throw new vB_Exception_Api('please_select_at_least_one_post'); } // check that the user has permission $nodesToCheck = $data['mergePosts']; $nodesToCheck[] = $data['destnodeid']; foreach ($nodesToCheck as $key => $nodeid) { // this is here just in case for some reason, a nodeid is 0. Shouldn't happen, but // I don't want getChannelPermission to go bonkers from it. if (empty($nodeid)) { unset($nodesToCheck[$key]); continue; } if (!vB::getUserContext()->getChannelPermission('moderatorpermissions', 'canmanagethreads', $nodeid)) { // perhaps we could generate a list of unmergeable nodes and return a warning instead, but // I don't think there's a real use case where a moderator can manage only *some* of the // nodes they're trying to merge. I think that would require multiple channels being involved, and // we don't have a UI for that so I can't test it. As such I'm just going to throw an exception if // *any* of the nodes fail the check. throw new vB_Exception_Api('no_permission'); } } // validate that selected nodes can be merged $mergeInfo = $this->validateMergePosts($data['mergePosts']); if (isset($mergeInfo['error'])) { throw new vB_Exception_Api($mergeInfo['error']); } // validate form fields if (empty($data['destnodeid']) || !array_key_exists($data['destnodeid'], $mergeInfo['destnodes'])) { throw new vB_Exception_Api('invalid_data'); } if (empty($data['destauthorid']) || !array_key_exists($data['destauthorid'], $mergeInfo['destauthors'])) { throw new vB_Exception_Api('invalid_data'); } $destnode = $this->library->getNodeFullContent($data['destnodeid']); $destnode = array_pop($destnode); if ($destnode['starter'] != $destnode['nodeid'] and $destnode['starter'] != $destnode['parentid']) { if (isset($data['tags'])) { unset($data['tags']); } } $type = vB_Types::instance()->getContentTypeClass($destnode['contenttypeid']); $response = vB_Library::instance("content_{$type}")->mergeContent($data); if ($response) { $sources = array_diff($data['mergePosts'], array($data['destnodeid'])); $origDestnode = $destnode; if (!empty($destnode['rawtext'])) { $origRawText = $destnode['rawtext']; } else { if (!empty($destnode['content']['rawtext'])) { $origRawText = $destnode['content']['rawtext']; } else { $origRawText = ''; } } $destnode = $this->getNode($data['destnodeid']); if (!empty($destnode['rawtext'])) { $rawText = $destnode['rawtext']; } else { if (!empty($destnode['content']['rawtext'])) { $rawText = $destnode['content']['rawtext']; } else { $rawText = ''; } } $destnode = $this->getNode($data['destnodeid']); $loginfo = array('nodeid' => $destnode['nodeid'], 'nodetitle' => $destnode['title'], 'nodeusername' => $destnode['authorname'], 'nodeuserid' => $destnode['userid']); // move children to target node $children = vB::getDbAssertor()->assertQuery('vBForum:closure', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, 'parent' => $sources, 'depth' => 1)); $childrenIds = array(); foreach ($children as $child) { $childrenIds[] = $child['child']; } if (!empty($childrenIds)) { $this->moveNodes($childrenIds, $data['destnodeid'], false, false, false); } // remove merged nodes $this->deleteNodes($sources, true, null, false); // Dont log the deletes $loginfo['action'] = array('merged_nodes' => implode(',', $sources)); $vboptions = vB::getDatastore()->getValue('options'); if (vB_Api::instanceInternal('user')->hasPermissions('genericoptions', 'showeditedby') and $destnode['publishdate'] > 0 and $destnode['publishdate'] < vB::getRequest()->getTimeNow() - $vboptions['noeditedbytime'] * 60 or !empty($data['reason'])) { $userinfo = vB::getCurrentSession()->fetch_userinfo(); if ($vboptions['postedithistory']) { $record = vB::getDbAssertor()->getRow('vBForum:postedithistory', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, 'original' => 1, 'nodeid' => $destnode['nodeid'])); // insert original post on first edit if (empty($record)) { vB::getDbAssertor()->assertQuery('vBForum:postedithistory', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_INSERT, 'nodeid' => $origDestnode['nodeid'], 'userid' => $origDestnode['userid'], 'username' => $origDestnode['authorname'], 'dateline' => $origDestnode['publishdate'], 'pagetext' => $origRawText, 'original' => 1)); } // insert the new version vB::getDbAssertor()->assertQuery('vBForum:postedithistory', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_INSERT, 'nodeid' => $destnode['nodeid'], 'userid' => $userinfo['userid'], 'username' => $userinfo['username'], 'dateline' => vB::getRequest()->getTimeNow(), 'reason' => isset($data['reason']) ? vB5_String::htmlSpecialCharsUni($data['reason']) : '', 'pagetext' => $rawText)); } vB::getDbAssertor()->assertQuery('editlog_replacerecord', array('nodeid' => $destnode['nodeid'], 'userid' => $userinfo['userid'], 'username' => $userinfo['username'], 'timenow' => vB::getRequest()->getTimeNow(), 'reason' => isset($data['reason']) ? vB5_String::htmlSpecialCharsUni($data['reason']) : '', 'hashistory' => intval($vboptions['postedithistory']))); } vB_Library_Admin::logModeratorAction($loginfo, 'node_merged_by_x'); return true; } }
/** * Renders the output after preperation. * @see vB5_Template::render() * * @param boolean Whether to suppress the HTML comment surrounding option (for JS, etc) * @param boolean true if we are rendering for a call to /ajax/render/ and we want CSS <link>s separate * * @return string */ public function render($isParentTemplate = true, $isAjaxTemplateRender = false) { static $user = false; if (!$user) { $user = vB5_User::instance(); } $config = vB5_Config::instance(); $this->register('user', $user, true); extract(self::$globalRegistered, EXTR_SKIP | EXTR_REFS); extract($this->registered, EXTR_OVERWRITE | EXTR_REFS); $baseurl = vB5_Template_Options::instance()->get('options.frontendurl'); $baseurl_core = vB5_Template_Options::instance()->get('options.bburl'); $baseurl_login = vB5_Template_Options::instance()->get('options.frontendurl_login'); $baseurl_data = vB5_String::parseUrl($baseurl); if (isset($baseurl_data['path'])) { $baseurl_path = $baseurl_data['path']; } $baseurl_path = isset($baseurl_path) ? $baseurl_path . (substr($baseurl_path, -1) != '/' ? '/' : '') : '/'; //same as cookie path $cookie_prefix = $config->cookie_prefix; $vboptions = vB5_Template_Options::instance()->getOptions(); $vboptions = $vboptions['options']; //this assumes that core is in the core directory which is not something we've generally assumed //however as noncollapsed mode look unlikely to be as useful as we thought, we'll start making that //assumption. However setting a seperate variable means we don't spread that assumption all through //the template code. $baseurl_cdn = $vboptions['cdnurl']; if ($baseurl_cdn) { $baseurl_corecdn = $baseurl_cdn . '/core'; } else { //if we haven't set a cdn url, then let's default to the actual site urls. $baseurl_cdn = $baseurl; $baseurl_corecdn = $baseurl_core; } $vbproducts = vB::getDatastore()->getValue('products'); $preferred_styleid = vB5_Template_Stylevar::instance()->getPreferredStyleId() > 0 ? vB5_Template_Stylevar::instance()->getPreferredStyleId() : $vboptions['styleid']; $preferred_languageid = vB5_User::getLanguageId() > 0 ? vB5_User::getLanguageId() : $vboptions['languageid']; $timenow = time(); self::$renderedTemplateNames[] = $this->template; // debug info for the templates that have been used if ($config->debug) { self::$renderedTemplates[] = array('templateName' => $this->template, 'isParentTemplate' => (bool) $isParentTemplate, 'indent' => str_repeat('|----', count(self::$renderedTemplatesStack))); self::$renderedTemplatesStack[] = $this->template; } // todo: remove this once we can remove notices from template code // allow developers to turn notices off for templates -- to avoid having them turn off notices entirely if ($config->no_template_notices) { $oldReporting = error_reporting(E_ALL & ~E_NOTICE); } if ($config->render_debug) { set_exception_handler(null); set_error_handler('vberror'); // Show which template is being rendered. echo 'Template: ' . $this->template . '<br />'; } $templateCache = vB5_Template_Cache::instance(); $templateCode = $templateCache->getTemplate($this->template); if (is_array($templateCode) and !empty($templateCode['textonly'])) { $final_rendered = $templateCode['placeholder']; } else { if ($templateCache->isTemplateText()) { @eval($templateCode); } else { if ($templateCode !== false) { @(include $templateCode); } } } if ($config->render_debug) { restore_error_handler(); restore_exception_handler(); } if ($config->no_template_notices) { error_reporting($oldReporting); } // always replace placeholder for templates, as they are process by levels $templateCache->replacePlaceholders($final_rendered); if ($isParentTemplate) { // we only replace phrases/urls/nodetext, insert javascript and stylesheets at the parent template $this->renderDelayed($final_rendered, $isAjaxTemplateRender); //Store the configuration information in the session if (!empty(vB5_Config::instance()->php_sessions)) { if (session_status() == PHP_SESSION_NONE) { $expires = vB5_Config::instance()->session_expires; if (!empty($expires) and intval($expires)) { session_cache_expire(intval($expires)); } session_start(); $_SESSION['languageid'] = $preferred_languageid; $_SESSION['userid'] = vB5_User::get('userid'); } } } // debug info for the templates that have been used if ($config->debug) { array_pop(self::$renderedTemplatesStack); } // add template name to HTML source for debugging if (!empty($vboptions['addtemplatename']) and $vboptions['addtemplatename']) { $final_rendered = "<!-- BEGIN: {$this->template} -->{$final_rendered}<!-- END: {$this->template} -->"; } return $final_rendered; }
/** * updates a record * * @param mixed array of nodeid's * @param mixed array of permissions that should be checked. * * @return boolean */ public function update($nodeid, $data) { $channelContentTypeId = vB_Types::instance()->getContentTypeId('vBForum_Channel'); // Verify prefixid if ($this->contenttypeid != $channelContentTypeId and isset($data['prefixid'])) { $this->verifyPrefixid($data['prefixid']); } else { // Channel can't have a prefix unset($data['prefixid']); } // Verify post iconid if ($this->contenttypeid != $channelContentTypeId and isset($data['iconid'])) { $this->verifyPostIconid($data['iconid']); } else { // Channels can't have a post icon unset($data['iconid']); } $timeNow = vB::getRequest()->getTimeNow(); $userContext = vB::getUserContext(); //If this user doesn't have the featured permission and they are trying to set it, //Let's just quietly unset it. if (isset($data['featured'])) { if (!$userContext->getChannelPermission('moderatorpermissions', 'cansetfeatured', $data['parentid'])) { unset($data['featured']); } } //We can't allow directly setting parentid. That should only happen through the node api move function //And there are number of other fields that shouldn't be changed here. We have methods for the ones that can be changed at all. foreach (array('open', 'showopen', 'approved', 'showapproved', 'protected') as $field) { if (isset($data[$field])) { unset($data[$field]); } } if (isset($data['parentid'])) { //Only allow for articles. $content = $this->nodeApi->getNodeFullContent($nodeid); $content = array_pop($content); // you can't move it to the category it's already in if ($data['parentid'] != $content['parentid']) { // only allow this for articles (currently) if ($content['channeltype'] == 'article') { if (!$userContext->getChannelPermission('forumpermissions', 'canmove', $data['parentid']) and !$userContext->getChannelPermission('moderatorpermissions', 'canmassmove', $data['parentid'])) { throw new vB_Exception_Api('no_permission'); } //If we got here, we're O.K. to move. let's do that now. vB_Library::instance('node')->moveNodes($nodeid, $data['parentid']); } } unset($data['parentid']); } //We need to see if we need to update. $prior = vB_Library::instance('node')->getNodeBare($nodeid); if ($this->contenttypeid != $channelContentTypeId) { $content = $this->getFullContent($nodeid); } if (isset($data['publish_now']) and !empty($data['publish_now'])) { $data['publishdate'] = vB::getRequest()->getTimeNow(); } if (empty($data['htmltitle']) and !empty($data['title'])) { $data['htmltitle'] = vB_String::htmlSpecialCharsUni(vB_String::stripTags($data['title']), false); } if (empty($data['urlident']) and !empty($data['title'])) { $data['urlident'] = vB_String::getUrlIdent($data['title']); } // Do not change publishdate or showpublished status unless it was explicitly set while calling update(). if ((!isset($data['publishdate']) or empty($data['publishdate']) and $data['publishdate'] !== 0) and !empty($prior['publishdate'])) { $data['publishdate'] = $prior['publishdate']; } if ($this->isPublished($data)) { $published = 1; } else { $published = 0; } $nodevals = array(); if ($published != $prior['showpublished']) { $nodevals['showpublished'] = $published; } // set default node options if ((empty($data['nodeoptions']) or !is_numeric($data['nodeoptions'])) and $prior['contenttypeid'] != $channelContentTypeId) { $parentFullContent = vB_Library::instance('node')->getNodeFullContent($prior['parentid']); if (!empty($parentFullContent[$prior['parentid']]['channeltype'])) { $data['nodeoptions'] = self::$defaultNodeOptions[$parentFullContent[$prior['parentid']]['channeltype']]; } else { $data['nodeoptions'] = self::$defaultNodeOptions['default']; } // Add or remove any nodeoptions that have been explicitly passed in. // This would have otherwise happened in updateNodeOptions/setNodeOptions // (which is where it happens when adding a node as opposed to updating // a node), but since $data['nodeoptions'] is now defined, setNodeOptions // won't take care of setting these (it will just apply the int // nodeoptions value). $baseNodeOptions = vB_Api::instanceInternal('node')->getOptions(); foreach ($baseNodeOptions as $baseOptionKey => $baseOptionVal) { if (isset($data[$baseOptionKey])) { if (intval($data[$baseOptionKey])) { $data['nodeoptions'] = $data['nodeoptions'] | intval($baseOptionVal); } else { $data['nodeoptions'] = $data['nodeoptions'] & ~intval($baseOptionVal); } } } } //node table data. $data[vB_dB_Query::TYPE_KEY] = vB_dB_Query::QUERY_UPDATE; $data['nodeid'] = $nodeid; $data['lastupdate'] = $timeNow; //If the field passed is in the $nodeFields array then we update the node table. foreach ($data as $field => $value) { if (in_array($field, $this->nodeFields)) { $nodevals[$field] = $value; } } $index = empty($data['noIndex']); unset($data['noIndex']); // Update the content-type specific data if (!is_array($this->tablename)) { $tables = array($this->tablename); } else { $tables = $this->tablename; } $success = true; foreach ($tables as $table) { $structure = $this->assertor->fetchTableStructure('vBForum:' . $table); if (empty($structure) or empty($structure['structure'])) { throw new vB_Exception_Api('invalid_query_parameters'); } $queryData = array(); $queryData[vB_dB_Query::TYPE_KEY] = vB_dB_Query::QUERY_UPDATE; $queryData['nodeid'] = $nodeid; foreach ($structure['structure'] as $fieldname) { if (isset($data[$fieldname])) { $queryData[$fieldname] = $data[$fieldname]; } } //Now we have at least a query type and a nodeid. We put those in above. So if we don't //have at least one other value there's no reason to try an update. if (count($queryData) > 2) { $success = $success and $this->assertor->assertQuery('vBForum:' . $table, $queryData); } } if ($success) { // Handle Attachments // The text library (and most derivatives) can have attachments, // for the others, this is a no-op. $this->handleAttachments('update', $nodeid, $data); //Clear cached query info that would be significantly impacted $events = array('fUserContentChg_' . $prior['userid']); if ($prior['starter']) { $starterNodeInfo = vB_Library::instance('node')->getNodeBare($prior['starter']); $events[] = 'fUserContentChg_' . $starterNodeInfo['userid']; } else { if ($prior['parentid']) { $starterNodeInfo = vB_Library::instance('node')->getNodeBare($prior['parentid']); $events[] = 'fUserContentChg_' . $starterNodeInfo['userid']; } } $this->nodeApi->clearCacheEvents($nodeid); vB_Cache::instance()->allCacheEvent($events); if (isset($nodevals['publishdate']) and $nodevals['publishdate'] > $timeNow) { if (empty($nodevals['unpublishdate']) or $nodevals['unpublishdate'] > $nodevals['publishdate']) { $nodevals['nextupdate'] = $nodevals['publishdate']; } } else { if (isset($nodevals['unpublishdate']) and $nodevals['unpublishdate'] > $timeNow) { $nodevals['nextupdate'] = $nodevals['unpublishdate']; } } // handle approved if (isset($nodevals['approved'])) { if ($nodevals['approved']) { $approved = 1; $queryName = 'approveNode'; } else { $approved = 0; $queryName = 'unapproveNode'; } // set approved to parent... $this->assertor->assertQuery('vBForum:node', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_UPDATE, 'nodeid' => $nodeid, 'approved' => $approved)); // and handle showapproved $this->assertor->assertQuery('vBForum:' . $queryName, array('nodeid' => $nodeid)); unset($nodevals['approved']); } if (isset($nodevals)) { $nodevals[vB_dB_Query::TYPE_KEY] = vB_dB_Query::QUERY_UPDATE; $nodevals['nodeid'] = $nodeid; $success = $this->assertor->assertQuery('vBForum:node', $nodevals); } //We need to compare the current publishdate and unpublishdate values against the // parent. //But we can skip this if neither publish or unpublishdate is set $updateParents = false; if ($published != $prior['showpublished']) { $updateParents = true; //We are concerned about two possibilities. It could have gone from published to unpublished. //In either case we change by totalcount +1 (for ourselves. //Remember that published is always unpublished. //From unpublished to published. if ($published) { $nodeUpdates = $this->nodeLibrary->publishChildren($nodeid); // if $nodeUpdates is empty, that means no change was made to this node or its descendants, // and no parent count changes are necessary. If it's not empty but doesn't have totalcount set, // that means it possibly failed with a DB error. In such a case, we will just not update the // counts but continue updating the node. if (!empty($nodeUpdates) and isset($nodeUpdates['totalcount'])) { // text-counts only change by 1 (or 0 for non-text types), because it only affects the immediate parent $textChange = $this->textCountChange; $textUnPubChange = -1 * $textChange; // Note, below assumes that a DB had been diligent about // keeping track of the count fields correctly. $totalPubChange = $nodeUpdates['totalcount'] - $prior['totalcount'] + $textChange; // we add the text change because self counts for ancestors' total counts $totalUnPubChange = -1 * $totalPubChange; } else { $updateParents = false; } } else { $nodeUpdates = $this->nodeLibrary->unpublishChildren($nodeid); if (!empty($nodeUpdates) and isset($nodeUpdates['totalunpubcount'])) { $textUnPubChange = $this->textCountChange; $textChange = -1 * $textUnPubChange; $totalUnPubChange = $nodeUpdates['totalunpubcount'] - $prior['totalunpubcount'] + $textUnPubChange; $totalPubChange = -1 * $totalUnPubChange; } else { $updateParents = false; } } vB_Library::instance('node')->clearChildCache($nodeid); } //update the parent count if necessary if ($updateParents) { vB_Library::instance('node')->updateParentCounts($nodeid, $textChange, $textUnPubChange, $totalPubChange, $totalUnPubChange, $published); } //update viewperms from childs if needed, do we want this channel specific? if (isset($nodevals['viewperms']) and isset($prior['viewperms']) and $nodevals['viewperms'] != $prior['viewperms']) { vB_Api::instanceInternal('node')->setNodePerms($nodeid, array('viewperms' => $nodevals['viewperms'])); } if ($index) { vB_Api::instanceInternal('Search')->index($nodeid); } // update user tags $tags = !empty($data['tags']) ? explode(',', $data['tags']) : array(); $tagRet = vB_Api::instanceInternal('tags')->updateUserTags($nodeid, $tags); $this->updateNodeOptions($nodeid, $data); // Update childs nodeoptions $this->assertor->assertQuery('vBForum:updateChildsNodeoptions', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_STORED, 'parentid' => $nodeid)); $this->nodeApi->clearCacheEvents(array($nodeid, $prior['parentid'])); $loginfo = array('nodeid' => $prior['nodeid'], 'nodetitle' => $prior['title'], 'nodeusername' => $prior['authorname'], 'nodeuserid' => $prior['userid']); $extra = array(); if ($nodevals !== null && isset($nodevals['title'])) { if ($prior['title'] != $nodevals['title']) { $extra = array('newtitle' => $nodevals['title']); } } vB_Library_Admin::logModeratorAction($loginfo, 'node_edited_by_x', $extra); $updateEditLog = true; if (!vB::getUserContext()->hasPermission('genericoptions', 'showeditedby') and (isset($content[$nodeid]['edit_reason']) and $data['reason'] == $content[$nodeid]['edit_reason'] or !isset($content[$nodeid]['edit_reason']) and empty($data['reason']))) { $updateEditLog = false; } // Clear autosave table of this items entry if (vB::getCurrentSession()->get('userid') and !empty($data['rawtext'])) { $this->assertor->delete('vBForum:autosavetext', array('userid' => vB::getCurrentSession()->get('userid'), 'nodeid' => $nodeid, 'parentid' => $content[$nodeid]['parentid'])); } // Log edit by info if ($updateEditLog and $this->contenttypeid != $channelContentTypeId and isset($content[$nodeid]['rawtext']) and isset($data['rawtext']) and $content[$nodeid]['rawtext'] != $data['rawtext'] and !empty($data['publishdate']) and $prior['publishdate'] and (!empty($data['reason']) or $data['publishdate'] < vB::getRequest()->getTimeNow() - $this->options['noeditedbytime'] * 60)) { $userinfo = vB::getCurrentSession()->fetch_userinfo(); // save the postedithistory if ($this->options['postedithistory']) { $record = $this->assertor->getRow('vBForum:postedithistory', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, 'original' => 1, 'nodeid' => $nodeid)); // insert original post on first edit if (empty($record)) { $this->assertor->assertQuery('vBForum:postedithistory', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_INSERT, 'nodeid' => $nodeid, 'userid' => $content[$nodeid]['userid'], 'username' => $content[$nodeid]['authorname'], 'dateline' => $data['publishdate'], 'pagetext' => $content[$nodeid]['rawtext'], 'original' => 1)); } // insert the new version $this->assertor->assertQuery('vBForum:postedithistory', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_INSERT, 'nodeid' => $nodeid, 'userid' => $userinfo['userid'], 'username' => $userinfo['username'], 'dateline' => vB::getRequest()->getTimeNow(), 'reason' => isset($data['reason']) ? vB5_String::htmlSpecialCharsUni($data['reason']) : '', 'pagetext' => isset($data['rawtext']) ? $data['rawtext'] : '')); } $this->assertor->assertQuery('editlog_replacerecord', array('nodeid' => $nodeid, 'userid' => $userinfo['userid'], 'username' => $userinfo['username'], 'timenow' => vB::getRequest()->getTimeNow(), 'reason' => isset($data['reason']) ? vB5_String::htmlSpecialCharsUni($data['reason']) : '', 'hashistory' => intval($this->options['postedithistory']))); } return true; } $this->nodeApi->clearCacheEvents(array($nodeid, $prior['parentid'])); return false; }
public static function vBVar($value) { return vB5_String::htmlSpecialCharsUni($value); }
function actionResult() { //the api init can redirect. We need to make sure that happens before we echo anything $api = Api_InterfaceAbstract::instance(); $top = ''; if (vB5_Request::get('cachePageForGuestTime') > 0 and !vB5_User::get('userid')) { $fullPageKey = md5(serialize($_REQUEST)); $fullPage = vB_Cache::instance()->read($fullPageKey); if (!empty($fullPage)) { echo $fullPage; exit; } } $preheader = vB5_ApplicationAbstract::getPreheader(); $top .= $preheader; if (vB5_Request::get('useEarlyFlush')) { echo $preheader; flush(); } $serverData = array_merge($_GET, $_POST); $router = vB5_ApplicationAbstract::instance()->getRouter(); $arguments = $router->getArguments(); $userAction = $router->getUserAction(); if (!empty($userAction)) { $api->callApi('wol', 'register', array($userAction['action'], $userAction['params'])); } // if Human verification is required, and we don't have 'q' set in serverData (means the user is using // the quick search box), we redirect user to advanced search page with HV $requirehv = $api->callApi('hv', 'fetchRequireHvcheck', array('search')); if (!empty($serverData['AdvSearch']) or $requirehv and isset($serverData['q'])) { $adv_search = $api->callApi('route', 'getRoute', array('pathInfo' => 'advanced_search', 'queryString' => ''), true); $arguments = $adv_search['arguments']; } elseif ($requirehv) { // Advanced search form submitted if (empty($serverData['humanverify'])) { $serverData['humanverify'] = array(); } $return = $api->callApi('hv', 'verifyToken', array($serverData['humanverify'], 'search')); if ($return !== true) { $adv_search = $api->callApi('route', 'getRoute', array('pathInfo' => 'advanced_search', 'queryString' => ''), true); $arguments = $adv_search['arguments']; $error = $return['errors'][0][0]; } } $pageid = (int) (isset($arguments['pageid']) ? $arguments['pageid'] : $arguments['contentid']); $page = $api->callApi('page', 'fetchPageById', array($pageid, $arguments)); if (!$page) { echo 'Could not find page.'; exit; } $phrases = $api->callApi('phrase', 'fetch', array(array('advanced_search', 'search_results'))); $page['crumbs'] = array(0 => array('title' => $phrases['advanced_search'], 'url' => vB5_Template_Runtime::buildUrl('advanced_search', array(), array(), array('noBaseUrl' => true))), 1 => array('title' => $phrases['search_results'], 'url' => '')); // avoid search page itself being indexed $page['noindex'] = 1; if (!empty($serverData['cookie'])) { $serverData['searchJSON'] = '{"specific":[' . $_COOKIE[$serverData['cookie']] . ']}'; } if (!empty($serverData['searchJSON'])) { if (is_string($serverData['searchJSON'])) { if (preg_match('/[^\\x00-\\x7F]/', $serverData['searchJSON'])) { $serverData['searchJSON'] = vB5_String::toUtf8($serverData['searchJSON'], vB5_String::getTempCharset()); } $serverData['searchJSON'] = json_decode($serverData['searchJSON'], true); } if (!empty($serverData['searchJSON'])) { if (!empty($serverData['searchJSON']['keywords'])) { $serverData['searchJSON']['keywords'] = str_replace(array('"', '\\'), '', $serverData['searchJSON']['keywords']); $serverData['searchJSON']['keywords'] = filter_var($serverData['searchJSON']['keywords'], FILTER_SANITIZE_STRING); } $serverData['searchJSON'] = json_encode($serverData['searchJSON']); } else { $serverData['searchJSON'] = ''; } $page['searchJSON'] = $serverData['searchJSON']; $extra = array('searchJSON' => !empty($serverData['searchJSON']) ? $serverData['searchJSON'] : '{}'); if (!empty($serverData['AdvSearch'])) { $extra['AdvSearch'] = 1; } $page['url'] = str_replace('&', '&', vB5_Route::buildUrl('search', array(), $extra)); //$page['searchJSONStructure'] = json_decode($page['searchJSON'],true); $page['crumbs'][0]['url'] = vB5_Template_Runtime::buildUrl('advanced_search', array(), array('searchJSON' => $page['searchJSON']), array('noBaseUrl' => true)); } elseif (!empty($serverData['q'])) { $serverData['q'] = str_replace(array('"', '\\'), '', $serverData['q']); $serverData['q'] = filter_var($serverData['q'], FILTER_SANITIZE_STRING); $searchType = ''; if (!empty($serverData['type'])) { $serverData['type'] = str_replace(array('"', '\\'), '', $serverData['type']); $serverData['type'] = filter_var($serverData['type'], FILTER_SANITIZE_STRING); $searchType = ',"type":"' . $serverData['type'] . '"'; } $page['searchJSON'] = '{"keywords":"' . $serverData['q'] . '","sort":"title"' . $searchType . '}'; $extra = array('q' => $serverData['q']); if (!empty($serverData['AdvSearch'])) { $extra['AdvSearch'] = 1; } $page['url'] = str_replace('&', '&', vB5_Route::buildUrl('search', array(), $extra)); $page['searchStr'] = $serverData['q']; $page['crumbs'][0]['url'] = vB5_Template_Runtime::buildUrl('advanced_search', array(''), array('searchJSON' => $page['searchJSON']), array('noBaseUrl' => true)); } elseif (!empty($serverData['r'])) { unset($page['crumbs'][0]); $page['url'] = str_replace('&', '&', vB5_Route::buildUrl('search', array(), array('r' => $serverData['r']))); $page['resultId'] = $serverData['r']; if (!empty($serverData['p']) && is_numeric($serverData['p'])) { $page['currentPage'] = intval($serverData['p']); } $page['crumbs'][0]['url'] = vB5_Template_Runtime::buildUrl('advanced_search', array(), array('r' => $serverData['r']), array('noBaseUrl' => true)); } else { return $this->actionIndex(); } $page['ignore_np_notices'] = vB5_ApplicationAbstract::getIgnoreNPNotices(); if (!empty($error)) { $page['error'] = $error; } $templater = new vB5_Template($page['screenlayouttemplate']); $templater->registerGlobal('page', $page); $page = $this->outputPage($templater->render(), false); $fullPage = $top . $page; if (vB5_Request::get('cachePageForGuestTime') > 0 and !vB5_User::get('userid')) { vB_Cache::instance()->write($fullPageKey, $fullPage, vB5_Request::get('cachePageForGuestTime')); } if (!vB5_Request::get('useEarlyFlush')) { echo $fullPage; } else { echo $page; } }