Пример #1
0
 public static function instance()
 {
     if (!isset(self::$instance)) {
         $c = __CLASS__;
         self::$instance = new $c();
     }
     return self::$instance;
 }
Пример #2
0
 public function actionLoadNewPosts()
 {
     /*
     			Bug notes
     
     			There seems to be a "race condition" where pageload_servertime is set to something *before* the creation date of the last reply
     			that is actually included in that page load. This can result in a reply that's already on the page to be loaded (duped)
     			when the user makes a reply. (Probably not a "true" race condition. More likely the pageload time is set, then the rest of the
     			page load including the api call to fetch posts on the page takes several seconds, and someone happened to make a post
     			right between those two events. This is more likely for a busy forum, so we should handle this.)
     			Probably will need to add something to the thread display to handle this somehow, or pass in a list of all nodes on the page
     			and exclude them.
     			**UPDATE:**
     			Added data-node-publishdate to the <li > tags for the posts, and added JS to go through them
     			and pass in the max(publishdate[], timestamp). So this *shouldn't* happen anymore.
     */
     $input = array('parentid' => isset($_POST['parentid']) ? intval($_POST['parentid']) : 0, 'channelid' => isset($_POST['channelid']) ? intval($_POST['channelid']) : 0, 'loadnodeid' => isset($_POST['loadnodeid']) ? intval($_POST['loadnodeid']) : 0, 'timestamp' => isset($_POST['timestamp']) ? intval($_POST['timestamp']) : 0, 'pageload_servertime' => isset($_POST['pageload_servertime']) ? intval($_POST['pageload_servertime']) : 0, 'view' => isset($_POST['view']) ? trim($_POST['view']) : 'stream', 'currentpage' => isset($_POST['currentpage']) ? intval($_POST['currentpage']) : 1, 'pagetotal' => isset($_POST['pagetotal']) ? intval($_POST['pagetotal']) : 0, 'postcount' => isset($_POST['postcount']) ? intval($_POST['postcount']) : 0, 'postsperpage' => isset($_POST['postsperpage']) ? intval($_POST['postsperpage']) : 0, 'commentsperpage' => isset($_POST['commentsperpage']) ? intval($_POST['commentsperpage']) : 0, 'past_page_limit_aware' => isset($_POST['past_page_limit_aware']) ? filter_var($_POST['past_page_limit_aware'], FILTER_VALIDATE_BOOLEAN) : false);
     $api = Api_InterfaceAbstract::instance();
     /*
     			BEGIN >>> Redirect to new page <<<
     			If we're trying to load a nodeid, and currentpage is < pagetotal, this indicates a scenario where
     			a reply was posted on a page that's not the last page. vB4 behavior for this was to redirect browser
     			to the page that the reply is on, so we should do the same.
     */
     if (!empty($input['loadnodeid'])) {
         $usersNewReply = $api->callApi('node', 'getFullContentforNodes', array($input['loadnodeid']));
         $usersNewReply = empty($usersNewReply) ? null : reset($usersNewReply);
     } else {
         $usersNewReply = null;
     }
     if (!empty($usersNewReply) and $input['currentpage'] < $input['pagetotal']) {
         // redirect to loadnode
         $url = $api->callApi('route', 'getUrl', array('route' => $usersNewReply['routeid'], 'data' => $usersNewReply, 'extra' => array('p' => $usersNewReply['nodeid'])));
         if (is_string($url)) {
             $url = vB5_Template_Options::instance()->get('options.frontendurl') . $url;
             // TODO, return a template saying "redirecting... or something. The wait before reload is noticeable."
             return $this->sendAsJson(array('redirect' => $url));
         } else {
             // UNTESTED.
             // todo, send user to same topic, but with ?goto=newpost
             $url = $api->callApi('route', 'getUrl', array('route' => $usersNewReply['routeid'], 'data' => array('nodeid' => $usersNewReply['starter']), 'extra' => array('goto' => 'newpost')));
             $url = vB5_Template_Options::instance()->get('options.frontendurl') . $url;
             return $this->sendAsJson(array('redirect' => $url));
         }
     }
     // END >>> Redirect to new page <<<
     /*
     			BEGIN >>> Fetch new replies under topic <<<
     */
     // based on widget_conversationdisplay search options
     $search_json = array('date' => array('from' => $input['timestamp']), 'channel' => $input['parentid']);
     if ($input['view'] == 'stream') {
         // UNTESTED &  UNSUPPORTED
         // based on vB5_Frontend_Controller_Activity::actionGet()
         $search_json['depth'] = 2;
         $search_json['view'] = 'conversation_stream';
         $search_json['sort']['created'] = 'DESC';
     } else {
         $input['view'] = 'thread';
         $search_json['view'] = 'thread';
         // thread
         $search_json['depth'] = 1;
         $search_json['view'] = 'conversation_thread';
         $search_json['sort']['created'] = 'ASC';
         $search_json['nolimit'] = 1;
         // TODO: remove this?
     }
     $search_json['ignore_protected'] = 1;
     $numAllowed = max($input['postsperpage'] - $input['postcount'], 0);
     if (!empty($usersNewReply)) {
         // Grab 2 extra *just* in case the one immediately after $numAllowed is the new reply
         $perpage = $numAllowed + 2;
     } else {
         $perpage = $numAllowed + 1;
     }
     $functionParams = array($search_json, $perpage, 1);
     $searchResult = Api_InterfaceAbstract::instance()->callApi('search', 'getInitialResults', $functionParams);
     $newReplies = $searchResult['results'];
     // END >>> Fetch new replies under topic <<<
     /*
     			BEGIN >>> Get next page URL <<<
     */
     $routeid = false;
     $firstnode = reset($newReplies);
     if (isset($firstnode['routeid'])) {
         $routeid = $firstnode['routeid'];
     } else {
         // UNTESTED
         $parentnode = $api->callApi('node', 'getNodeFullContent', array('nodeid' => $input['parentid'], 'contenttypeid' => false, 'options' => array('showVM' => 1, 'withParent' => 1)));
         $parentnode = $parentnode[$input['parentid']];
         $routeid = $parentnode['routeid'];
     }
     $nextPageUrl = $api->callApi('route', 'getUrl', array('route' => $routeid, 'data' => array('nodeid' => $input['parentid'], 'pagenum' => $input['currentpage'] + 1), 'extra' => array()));
     $nextPageUrl = vB5_Template_Options::instance()->get('options.frontendurl') . $nextPageUrl;
     // END >>> Get next page URL <<<
     /*
     			BEGIN >>> GENERATE TEMPLATE <<<
     */
     $channelBbcodes = $api->callApi('content_channel', 'getBbcodeOptions', array($input['channelid']));
     // Used for display_contenttype_threadview_header template, post index (ex. #123 link)
     $pagingInfo = array('currentpage' => $input['currentpage'], 'perpage' => $input['postsperpage']);
     // the template automatically calculates what the postIndex should be given the $postIndex *offset* (# of posts already on the page)
     $postIndex = $input['postcount'];
     /*
     			This is handy for debugging. Can remove once this code is stabilized.
     */
     $templateInfo = array();
     $newRepliesSinceTime = false;
     // "New replies since ##:##"
     $moreUnreadReplies = false;
     // "There are more unread replies after the current page. Please click here to..."
     if (!empty($usersNewReply)) {
         // Only show the "new replies since {time}" phrase if
         // other replies can be shown on the page AND there are other posts than the user's new reply.
         if ($numAllowed > 0 and $searchResult['totalRecords'] > 1) {
             $newRepliesSinceTime = true;
         }
     } else {
         $newRepliesSinceTime = true;
     }
     if ($newRepliesSinceTime) {
         $templateInfo['new_replies_since_x'] = true;
         $topHTML = $this->renderPostNoticeTemplate('new_replies_since_x', array('timestamp' => $input['timestamp']));
     } else {
         $topHTML = '';
     }
     /*
      * Go through the replies, render the display template, check if user's new reply is in here.
      */
     $bottomHTML = '';
     $counter = 1;
     $prependFetchTime = false;
     $past_page_limit = false;
     foreach ($newReplies as $node) {
         if ($counter <= $numAllowed) {
             $templateInfo['reply'][$node['nodeid']] = true;
             $extra = array('pagingInfo' => $pagingInfo, 'postIndex' => $postIndex++);
             $topHTML .= $this->renderSinglePostTemplate($node, $input['view'], $channelBbcodes, $extra) . "\n";
             if ($input['loadnodeid'] and $node['nodeid'] == $input['loadnodeid']) {
                 // We don't want to accidentally duplicate the user's reply if it's included here.
                 unset($usersNewReply);
             } else {
                 // Only prepend the "New post(s) since {time}" if there are posts other than the user's post that triggered
                 // this.
                 $prependFetchTime = true;
             }
             $counter++;
             // We only care about this while we're still within limit.
         } else {
             // Let's not show a warning more than once.
             $past_page_limit = true;
             // TODO: Add something for stream view (reverse order)?
             if (empty($input['past_page_limit_aware']) and $input['view'] == 'thread') {
                 $templateInfo['replies_below_on_next_page'] = true;
                 // Put up a warning saying below do not fit on the current page
                 $bottomHTML = $this->renderPostNoticeTemplate('replies_below_on_next_page', array('nextpageurl' => $nextPageUrl));
             }
             if (!empty($usersNewReply)) {
                 // If we've yet to render the user's new reply, there's a possibility that this node is
                 // the user's. Only show the "there are more unread replies" message when there are new
                 // posts OTHER than the user's new reply since the last time they checked ($input['timestamp'])
                 if ($usersNewReply['nodeid'] != $node['nodeid']) {
                     $moreUnreadReplies = true;
                 }
             } else {
                 // If we're not also fetching the user's reply, or we already rendered it within $numAllowed (above),
                 // this reply will always be on the 'second page'.
                 $moreUnreadReplies = true;
             }
         }
     }
     if (!empty($usersNewReply)) {
         $templateInfo['user_own_reply'][$usersNewReply['nodeid']] = true;
         $extra = array('pagingInfo' => $pagingInfo, 'postIndex' => $postIndex++);
         $bottomHTML .= $this->renderSinglePostTemplate($usersNewReply, $input['view'], $channelBbcodes, $extra) . "\n";
     }
     if ($moreUnreadReplies) {
         $templateInfo['more_replies_after_current_page'] = true;
         $bottomHTML .= $this->renderPostNoticeTemplate('more_replies_after_current_page', array('nextpageurl' => $nextPageUrl));
     }
     // END >>> GENERATE TEMPLATE <<<
     /*
     			BEGIN	>>> Return results array <<<
     */
     $results = array();
     $results['success'] = true;
     $results['past_page_limit'] = $past_page_limit;
     $results['timenow'] = vB5_Request::get('timeNow');
     $results['template'] = $topHTML . "\n" . $bottomHTML;
     $results['css_links'] = vB5_Template_Stylesheet::instance()->getAjaxCssLinks();
     $this->sendAsJsonAndCloseConnection($results);
     // END	>>> Return results array <<<
     /*
     			The reason I decided not to just do markread via AJAX + apidetach is that the timenow would be different, since
     			the current session's time and the that apidetach/node/markread call would have a bit of lag. So it's more
     			correct to do it here, and saves a request to do so.
     			We should decouple the "close request" logic from applicationlight's handleAjaxApiDetached() into a separate
     			function, and call it from here.
     */
     // The library markRead() function handles the case when user is a guest. JS needs to handle the case when
     // it's cookie based threadmarking.
     $api->callApi('node', 'markRead', array($input['parentid']));
     return;
 }
Пример #3
0
 /** Fetches the nodes info applying the following filter criteria. **/
 public function actionApplyFollowingFilter()
 {
     $result = array('lastDate' => 0, 'total' => 0, 'total_with_sticky' => 0, 'template' => '', 'css_links' => array());
     $filters = $_REQUEST['filters'];
     $followerId = isset($filters['followerid']) ? intval($filters['followerid']) : intval(vB::getUserContext()->fetchUserId());
     if (!empty($followerId)) {
         $followFilters = array();
         if (isset($filters['checkSince']) and is_numeric($filters['checkSince'])) {
             $followFilters['filter_time'] = $filters['checkSince'] + 1;
         } else {
             $followFilters['filter_time'] = isset($filters['filter_time']) ? $filters['filter_time'] : 'time_all';
         }
         $followFilters['filter_sort'] = isset($filters['filter_sort']) ? $filters['filter_sort'] : 'sort_recent';
         $typeFilter = isset($filters['filter_show']) ? $filters['filter_show'] : 'show_all';
         $followType = isset($filters['filter_follow']) ? $filters['filter_follow'] : 'follow_all';
         // Now we set the user options
         $options = array('perpage' => isset($filters['per-page']) ? intval($filters['per-page']) : 20);
         if (isset($filters['pagenum']) and !empty($filters['pagenum'])) {
             $options['page'] = intval($filters['pagenum']);
         }
         if (isset($filters['nodeid']) and !empty($filters['nodeid'])) {
             $options['parentid'] = intval($filters['nodeid']);
         }
         $contentTypeClass = ($typeFilter and strcasecmp($typeFilter, 'show_all') != 0) ? $typeFilter : '';
         $api = Api_InterfaceAbstract::instance();
         $resultNodes = $api->callApi('follow', 'getFollowingContentForTab', array('userid' => $followerId, 'type' => $followType, 'filters' => $followFilters, 'contenttypeclass' => $contentTypeClass, 'options' => $options));
         $templater = new vB5_Template('profile_following');
         $templater->register('nodes', $resultNodes['nodes']);
         $templater->register('showChannelInfo', $filters['showChannelInfo']);
         $result['template'] = $templater->render(true, true);
         foreach ($resultNodes['nodes'] as $nodeid => $node) {
             $result['lastDate'] = max($result['lastDate'], $node['content']['publishdate']);
         }
         $result['total'] = $result['total_with_sticky'] = $resultNodes['totalcount'];
         $result['pageinfo'] = array('pagenumber' => $resultNodes['paginationInfo']['currentpage'], 'showseemore' => $resultNodes['paginationInfo']['showseemore']);
         $result['css_links'] = vB5_Template_Stylesheet::instance()->getAjaxCssLinks();
     }
     $this->sendAsJson($result);
 }
Пример #4
0
 /**
  * Returns a string containing the rendered template
  * @see vB5_Frontend_Controller_Ajax::actionRender
  * @param string $templateName
  * @param array $data
  * @return string
  */
 public static function staticRenderAjax($templateName, $data = array())
 {
     $rendered = self::staticRender($templateName, $data, true, true);
     $css = vB5_Template_Stylesheet::instance()->getAjaxCssLinks();
     return array('template' => $rendered, 'css_links' => $css);
 }
Пример #5
0
 public static function includeCssFile()
 {
     $stylesheets = func_get_args();
     $stylesheet = vB5_Template_Stylesheet::instance();
     return $stylesheet->getCssFile($stylesheets[0]);
 }
Пример #6
0
 protected function processTopics($nodes, $stickynodes, $maxpages = 0)
 {
     $result = array('total' => 0, 'total_with_sticky' => 0, 'lastDate' => 0, 'template' => '', 'pageinfo' => array('pagenumber' => 1, 'totalpages' => 1), 'css_links' => array());
     $templater = new vB5_Template('display_Topics');
     $canmoderate = false;
     if (!isset($nodes['errors']) and !empty($nodes['results'])) {
         foreach ($nodes['results'] as $key => $node) {
             //only include the starter
             if ($node['content']['contenttypeclass'] == 'Channel' or $node['content']['starter'] != $node['content']['nodeid']) {
                 unset($nodes['results'][$key]);
             } else {
                 $result['lastDate'] = max($result['lastDate'], $node['content']['publishdate']);
             }
             if (!empty($node['content']['permissions']['canmoderate']) and !$canmoderate) {
                 $canmoderate = 1;
                 $templater->register('canmoderate', $canmoderate);
             }
         }
         $templater->register('topics', $nodes['results']);
         $result['total_with_sticky'] = $result['total'] = count($nodes['results']);
         $result['pageinfo']['pagenumber'] = $nodes['pagenumber'];
         $result['pageinfo']['totalpages'] = (!empty($maxpages) and $maxpages < $nodes['totalpages']) ? $maxpages : $nodes['totalpages'];
         $result['pageinfo']['resultId'] = $nodes['resultId'];
     } elseif (isset($nodes['errors'])) {
         $templater->register('topics', $nodes);
     }
     if (!isset($stickynodes['errors']) and !empty($stickynodes['results'])) {
         $result['total_with_sticky'] = $result['total'] + count($stickynodes['results']);
         $sticky_templater = new vB5_Template('display_Topics');
         $sticky_templater->register('topics', $stickynodes['results']);
         $sticky_templater->register('topic_list_class', 'sticky-list');
         if (!$canmoderate and empty($nodes['results'])) {
             //It is safe to assume that if user has canmoderate permission for the first topic node in a forum, he/she has the same permission for all the nodes.
             $firstTopic = reset($stickynodes['results']);
             $canmoderate = $firstTopic['content']['permissions']['canmoderate'];
         }
         $sticky_templater->register('canmoderate', $canmoderate);
         $result['template'] .= "\n" . $sticky_templater->render() . "\n";
         $templater->register('no_header', 1);
     }
     if (!empty($nodes['results']) or empty($stickynodes['results'])) {
         $result['template'] .= "\n" . $templater->render(true, true) . "\n";
         $result['css_links'] = vB5_Template_Stylesheet::instance()->getAjaxCssLinks();
     }
     return $result;
 }