public function getCanonicalRoute() { if (!isset($this->canonicalRoute)) { if (empty($this->arguments['nodeid'])) { throw new vB_Exception_NodePermission(); } $nodeApi = vB_Api::instanceInternal('node'); try { // this method will return an error if the user does not have permission $node = $nodeApi->getNode($this->arguments['nodeid']); } catch (vB_Exception_Api $ex) { // throw the proper NodePermission exception to return a 403 status instead of a 500 internal error if ($ex->has_errors('no_permission')) { throw new vB_Exception_NodePermission($this->arguments['nodeid']); } else { // otherwise, just let the caller catch the exception throw $ex; } } $contentApi = vB_Api_Content::getContentApi($node['contenttypeid']); if (!$contentApi->validate($node, vB_Api_Content::ACTION_VIEW, $node['nodeid'], array($node['nodeid'] => $node))) { throw new vB_Exception_NodePermission($node['nodeid']); } $parent = $nodeApi->getNode($node['starter']); $parent['innerPost'] = $this->arguments['nodeid']; $this->canonicalRoute = self::getRoute($node['routeid'], $parent, $this->queryParameters); } return $this->canonicalRoute; }
/** * This was a function wrapper for listPendingPosts but used for current user. * Now returns different information due to post processing steps. * */ public function listPendingPostsForCurrentUser($options = array()) { $result = $this->listPendingPosts(vB::getCurrentSession()->get('userid'), $options); if (isset($result['totalcount'])) { $totalCount = intval($result['totalcount']); } if (isset($result['pageInfo'])) { $pageInfo = $result['pageInfo']; } $contenttypes = array(); $nodes = array(); foreach ($result['nodes'] as $node) { $contenttypeid = $node['contenttypeid']; $nodeid = $node['nodeid']; if (!isset($contenttypes[$contenttypeid])) { $contenttypes[$contenttypeid] = array(); } $contenttypes[$contenttypeid][] = $nodeid; $nodes[$nodeid] = $node; } //For each type, get the content detail. foreach ($contenttypes as $contenttypeid => $nodeList) { if (!empty($nodes)) { $contentApi = vB_Api_Content::getContentApi($contenttypeid); $contentList = $contentApi->getFullContent($nodeList); foreach ($nodes as $nodeid => $node) { foreach ($contentList as $key => $content) { if ($content['nodeid'] == $nodeid) { $nodes[$nodeid]['content'] = $content; break; } } } } } $userApi = vB_Api::instanceInternal('user'); $pmContentType = vB_Types::instance()->getContentTypeId('vBForum_PrivateMessage'); //We need a list of parents for nodes that are neither starters nor replies. $parents = array(); //add parent, visitormessage, and author information foreach ($nodes as $nodeid => $node) { if ($node['content']['starter'] != $node['content']['nodeid'] and $node['content']['starter'] != $node['content']['parentid']) { $parents[$nodeid] = $node['content']['parentid']; } $nodes[$nodeid]['isVisitorMessage'] = $nodes[$nodeid]['content']['isVisitorMessage'] = !empty($node['content']['setfor']); $nodes[$nodeid]['userinfo'] = array('avatar' => $userApi->fetchAvatar($node['content']['userid'], array('avatar'), $node['content']['userinfo']), 'userid' => $node['content']['userid'], 'username' => $node['content']['userinfo']['username']); } //See if we need to add some parent information if (!empty($parents)) { $parentInfo = vB_Api::instanceInternal('node')->getNodes($parents); foreach ($parents as $nodeid => $parentid) { foreach ($parentInfo as $info) { if ($info['nodeid'] == $parentid) { $nodes[$nodeid]['parent'] = $info; } } } } $this->addOptionalContentInfo($nodes, $options); $this->markSubscribed($nodes); $return = array('nodes' => $nodes); if (isset($totalCount)) { $return['totalcount'] = $totalCount; } else { $return['totalcount'] = count($nodes); } if (isset($pageInfo) and !empty($pageInfo)) { $return['pageInfo'] = $pageInfo; } return $return; }
/** * Fetch image information about an attachment * * @param int Node ID * @param string Thumbnail version/size requested (SIZE_* constanst in vB_Api_Filedata) * @param bool Should we include the image content * * @return mixed Array of data, includes: * filesize, dateline, htmltype, filename, extension, and filedataid */ public function fetchImage($id, $type = vB_Api_Filedata::SIZE_FULL, $includeData = true) { if (empty($id) or !intval($id)) { throw new vB_Exception_Api('invalid_request'); } $type = vB_Api::instanceInternal('filedata')->sanitizeFiletype($type); $userContext = vB::getUserContext(); $attachdata = vB::getDbAssertor()->getRow('vBForum:fetchAttachForLoad', array('nodeid' => $id)); $extension = $attachdata['extension']; /* * This might get a bit confusing. Some files, like PDFs, might have an image thumbnail. * In such a case, we treat the thumbnail as an image in terms of permissions. To change * this policy, remove $type. */ $isImg = $this->imageHandler->isImage($extension, $type); if (!$isImg and !$userContext->getChannelPermission('forumpermissions', 'cangetattachment', $id) or $isImg and !$userContext->getChannelPermission('forumpermissions2', 'cangetimgattachment', $id)) { if ($attachdata['userid'] != $userContext->fetchUserId()) { throw new vB_Exception_Api('no_view_permissions'); } } $parent = vB_Api::instanceInternal('node')->getNode($attachdata['parentid']); $contentApi = vB_Api_Content::getContentApi($parent['contenttypeid']); $valid = $contentApi->validate($parent, vB_Api_Content::ACTION_VIEW, $parent['nodeid'], array($parent['nodeid'] => $parent)); //allow viewing attachments for nodes set to public preview even if the node itself //can't be viewed. if (!$valid and empty($parent['public_preview'])) { throw new vB_Exception_Api('no_view_permissions'); } //If the record belongs to this user, or if this user can view attachments //in this section, then this is O.K. if (!empty($attachdata) && $attachdata['filedataid']) { $params = array('filedataid' => $attachdata['filedataid'], 'type' => $type); $record = vB::getDbAssertor()->getRow('vBForum:getFiledataContent', $params); } if (empty($record)) { return false; } return vB_Image::instance()->loadFileData($record, $type, $includeData); }
/** * Get the nodes from a search resultId * * @param int $resultId id of the search result * @param int $perpage pagination - the number of results per page * @param int $pagenumber pagination - the page number * * @return array List of nodes in the resultId */ public function getMoreResults($resultId, $perpage = false, $pagenumber = false, $getStarterInfo = false) { $return_structure = $this->getMoreNodes($resultId, $perpage, $pagenumber); $return_structure['results'] = vB_Library::instance('Node')->getFullContentforNodes($return_structure['nodeIds'], array('withParent' => $getStarterInfo, 'showVM' => 1)); //note that getFullContentforNodes returns an element ['content']['permissions']['canviewthreads'] $userContext = vB::getUserContext(); foreach ($return_structure['results'] as $key => $result) { vB_Api_Content::getContentApi($result['contenttypeid'])->cleanPreviewContent($return_structure['results'][$key]); } return $return_structure; }
/** * This gets a content record based on nodeid including channel and starter information. * * @param int $nodeid * @param bool $contenttypeid optional, defaults to false * @param array optional Options flags: * showVm => appends visitor message node info. * Such as isVisitorMessage flag indicating if node is visitor message and vm_userInfo from the user the visitor message was posted for. * withParent => appends information from the parent. This info will append the 'parentConversation' info if the node is a comment. * * @return array node list ($nodeid=>node record) will only have one item * ***/ public function getNodeFullContent($nodeid, $contenttypeid = false, $options = array()) { if (!is_numeric($nodeid)) { throw new vB_Exception_Api('invalid_data'); } $nodeid = intval($nodeid); $result = $this->library->getNodeFullContent($nodeid, $contenttypeid, $options); //Can happen with a damaged node if (empty($result) or empty($result[$nodeid]) or empty($result[$nodeid]['nodeid'])) { return array(); } foreach ($result as $key => $node) { $contentApi = vB_Api_Content::getContentApi($node['contenttypeid']); if (!$contentApi->validate($node, vB_Api_Content::ACTION_VIEW, $node['nodeid'], array($node['nodeid'] => $node))) { throw new vB_Exception_Api('no_permission'); } } $this->library->removePrivateDataFromNodeList($result); return $result; }
public function __construct($routeInfo, $matches, $queryString = '', $anchor = '') { parent::__construct($routeInfo, $matches, $queryString, $anchor); if (isset($this->arguments['channelid'])) { $cache = vB_Cache::instance(vB_Cache::CACHE_FAST); $hashKey = 'vbRouteChannelInfo_' . $this->arguments['channelid']; $channelInfo = $cache->read($hashKey); if (empty($channelInfo)) { // check if we need to force a styleid $channel = vB_Library::instance('Content_Channel')->getBareContent($this->arguments['channelid']); $channel = array_pop($channel); $channelInfo['styleid'] = $channel['styleid']; $channelInfo['options'] = $channel['options']; $channelInfo['routeid'] = $channel['routeid']; $channelInfo['rss_enabled'] = $channel['rss_enabled']; $channelInfo['rss_route'] = $channel['rss_route']; $channelInfo['title'] = $channel['title']; $cache->write($hashKey, $channelInfo, 1440, array('routeChg_' . $channelInfo['routeid'], 'nodeChg_' . $channelInfo['routeid'])); } if (!empty($channelInfo['styleid'])) { if ($channelInfo['options']['styleoverride']) { // the channel must force the style $this->arguments['forceStyleId'] = $channelInfo['styleid']; } else { // the channel suggests to use this style $this->arguments['routeStyleId'] = $channelInfo['styleid']; } } // rss info $this->arguments['rss_enabled'] = $channelInfo['rss_enabled']; $this->arguments['rss_route'] = $channelInfo['rss_route']; $this->arguments['rss_title'] = $channelInfo['title']; $this->setPageKey('pageid', 'channelid', 'nodeid'); } if (isset($this->arguments['nodeid'])) { if (!empty($this->arguments['userid']) && !empty($this->arguments['contenttypeid']) && !empty($this->arguments['title']) && !empty($this->arguments['parentid'])) { $node['userid'] = $this->arguments['userid']; $node['contenttypeid'] = $this->arguments['contenttypeid']; $node['title'] = $this->arguments['title']; $node['parentid'] = $this->arguments['parentid']; } else { try { $node = vB_Library::instance('node')->getNodeBare($this->arguments['nodeid']); } catch (vB_Exception_Api $e) { if ($e->has_error('invalid_node_id')) { // the node does not exist, send a 404 throw new vB_Exception_404('invalid_page_url'); } else { // rethrow exception throw $e; } } } // privacy check $albumChannel = vB_Api::instance('node')->fetchAlbumChannel($node['nodeid']); if ($node['parentid'] == $albumChannel) { $userInfo = vB_Api::instance('user')->fetchProfileInfo($node['userid']); if ($node['contenttypeid'] == vB_Types::instance()->getContentTypeID('vBForum_Video') and !$userInfo['showVideos'] or $node['contenttypeid'] == vB_Types::instance()->getContentTypeID('vBForum_Gallery') and (!$userInfo['showPhotos'] or !vB::getUserContext()->hasPermission('albumpermissions', 'canviewalbum'))) { throw new vB_Exception_NodePermission($node['nodeid']); } } $contentApi = vB_Api_Content::getContentApi($node['contenttypeid']); if (!$contentApi->validate($node, vB_Api_Content::ACTION_VIEW, $node['nodeid'], array($node['nodeid'] => $node)) and empty($node['public_preview'])) { throw new vB_Exception_NodePermission($node['nodeid']); } if (!empty($this->queryParameters)) { $this->arguments['noindex'] = 1; } if (!empty($node['description'])) { $this->arguments['metadescription'] = $node['description']; } // set user action $this->setUserAction('viewing_topic_x', $node['title'], $this->getFullUrl('fullurl')); // set last crumb $this->breadcrumbs[] = array('title' => $node['title'], 'url' => ''); } $this->arguments['pageSchema'] = 'http://schema.org/ItemPage'; }
/** * Returns an array of all users participating in a discussion * * @param int the nodeid of the discussion * * @return array of user information * * following -- is the participant a follower of the current user (may be NULL) * * userid -- ID of the participant * * username -- Name of the participant * * avatarurl -- Url for the participant's avatar * * starter -- ID of the starter for $nodeid */ public function fetchParticipants($nodeid) { if (!intval($nodeid)) { throw new vB_Exception_Api('invalid_data'); } $currentUser = vB::getCurrentSession()->get('userid'); //We always should have something in $exclude. $exclude = array('-1'); if (intval($currentUser)) { $options = vB::getDatastore()->get_value('options'); if (trim($options['globalignore']) != '') { $exclude = preg_split('#\\s+#s', $options['globalignore'], -1, PREG_SPLIT_NO_EMPTY); } } $node = vB_Api::instanceInternal('node')->getNode($nodeid); $contentApi = vB_Api_Content::getContentApi($node['contenttypeid']); $valid = $contentApi->validate($node, vB_Api_Content::ACTION_VIEW, $node['nodeid'], array($node['nodeid'] => $node)); //if the user can't see the node, then don't allow them to see the participants. if (!$valid) { throw new vB_Exception_Api('no_permission'); } $nodeCTClass = vB_Types::instance()->getContentTypeClass($node['contenttypeid']); switch ($nodeCTClass) { case self::PARTICIPANTS_PM: $queryPart = 'vBForum:getPMRecipientsForMessageOverlay'; $params = array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_STORED, 'nodeid' => $nodeid); break; case self::PARTICIPANTS_POLL: // this seems a bit sketchy. This works (I think) because polls are always the starter due to // frontend restrictions, and no current notification will expect anything different when // calling this on a poll post, but if we have poll replies, and a notification called this function // expecting to get the thread participants, NOT poll voters, this would be a bug... $queryPart = 'vBForum:getNotificationPollVoters'; $params = array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_STORED, 'nodeid' => $nodeid); break; //for a channel we will quietly fail. Trying to look up the participants is too expensive, is a potential DOS //and we don't really need it. //for a channel we will quietly fail. Trying to look up the participants is too expensive, is a potential DOS //and we don't really need it. case self::PARTICIPANTS_CHANNEL: return array(); break; default: // private messages should've been caught by the first case. At this point, we should only be concerned with content // nodes (excluding polls) $queryPart = 'vBForum:fetchParticipants'; $params = array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_STORED, 'nodeid' => $nodeid, 'currentuser' => $currentUser, 'exclude' => $exclude); break; } $members = vB::getDbAssertor()->getRows($queryPart, $params); $participants = array(); foreach ($members as $member) { if (isset($participants[$member['userid']])) { continue; } $participants[$member['userid']] = $member; } $userApi = vB_Api::instanceInternal('user'); foreach ($participants as $uid => $participant) { $participants[$uid]['avatarurl'] = $userApi->fetchAvatar($uid, true, $participant); } return $participants; }