/** * Alternate version of Index that uses the embed master view. * * @param int $DiscussionID Unique identifier, if discussion has been created. * @param string $DiscussionStub Deprecated. * @param int $Offset * @param int $Limit */ public function embed($DiscussionID = '', $DiscussionStub = '', $Offset = '', $Limit = '') { $this->title(t('Comments')); // Add theme data $this->Theme = c('Garden.CommentsTheme', $this->Theme); Gdn_Theme::section('Comments'); // Force view options $this->MasterView = 'empty'; $this->CanEditComments = false; // Don't show the comment checkboxes on the embed comments page // Add some css to help with the transparent bg on embedded comments if ($this->Head) { $this->Head->addString('<style type="text/css"> body { background: transparent !important; } </style>'); } // Javascript files & options $this->addJsFile('jquery.gardenmorepager.js'); $this->addJsFile('jquery.autosize.min.js'); $this->addJsFile('discussion.js'); $this->removeJsFile('autosave.js'); $this->addDefinition('DoInform', '0'); // Suppress inform messages on embedded page. $this->addDefinition('SelfUrl', Gdn::request()->PathAndQuery()); $this->addDefinition('Embedded', true); // Define incoming variables (prefer querystring parameters over method parameters) $DiscussionID = is_numeric($DiscussionID) && $DiscussionID > 0 ? $DiscussionID : 0; $DiscussionID = getIncomingValue('vanilla_discussion_id', $DiscussionID); $Offset = getIncomingValue('Offset', $Offset); $Limit = getIncomingValue('Limit', $Limit); $vanilla_identifier = getIncomingValue('vanilla_identifier', ''); // Only allow vanilla identifiers of 32 chars or less - md5 if larger if (strlen($vanilla_identifier) > 32) { $vanilla_identifier = md5($vanilla_identifier); } $vanilla_type = getIncomingValue('vanilla_type', 'page'); $vanilla_url = getIncomingValue('vanilla_url', ''); $vanilla_category_id = getIncomingValue('vanilla_category_id', ''); $ForeignSource = array('vanilla_identifier' => $vanilla_identifier, 'vanilla_type' => $vanilla_type, 'vanilla_url' => $vanilla_url, 'vanilla_category_id' => $vanilla_category_id); $this->setData('ForeignSource', $ForeignSource); // Set comment sorting $SortComments = c('Garden.Embed.SortComments') == 'desc' ? 'desc' : 'asc'; $this->setData('SortComments', $SortComments); // Retrieve the discussion record $Discussion = false; if ($DiscussionID > 0) { $Discussion = $this->DiscussionModel->getID($DiscussionID); } elseif ($vanilla_identifier != '' && $vanilla_type != '') { $Discussion = $this->DiscussionModel->GetForeignID($vanilla_identifier, $vanilla_type); } // Set discussion data if we have one for this page if ($Discussion) { // Allow Vanilla.Comments.View to be defined to limit access to embedded comments only. // Otherwise, go with normal discussion view permissions. Either will do. $this->permission(array('Vanilla.Discussions.View', 'Vanilla.Comments.View'), false, 'Category', $Discussion->PermissionCategoryID); $this->setData('Discussion', $Discussion, true); $this->setData('DiscussionID', $Discussion->DiscussionID, true); $this->title($Discussion->Name); // Actual number of comments, excluding the discussion itself $ActualResponses = $Discussion->CountComments; // Define the query offset & limit if (!is_numeric($Limit) || $Limit < 0) { $Limit = c('Garden.Embed.CommentsPerPage', 30); } $OffsetProvided = $Offset != ''; list($Offset, $Limit) = offsetLimit($Offset, $Limit); $this->Offset = $Offset; if (c('Vanilla.Comments.AutoOffset')) { if ($ActualResponses <= $Limit) { $this->Offset = 0; } if ($this->Offset == $ActualResponses) { $this->Offset -= $Limit; } } elseif ($this->Offset == '') { $this->Offset = 0; } if ($this->Offset < 0) { $this->Offset = 0; } // Set the canonical url to have the proper page title. $this->canonicalUrl(discussionUrl($Discussion, pageNumber($this->Offset, $Limit))); // Load the comments. $CurrentOrderBy = $this->CommentModel->orderBy(); if (stringBeginsWith(GetValueR('0.0', $CurrentOrderBy), 'c.DateInserted')) { $this->CommentModel->orderBy('c.DateInserted ' . $SortComments); // allow custom sort } $this->setData('Comments', $this->CommentModel->get($Discussion->DiscussionID, $Limit, $this->Offset), true); if (count($this->CommentModel->where()) > 0) { $ActualResponses = false; } $this->setData('_Count', $ActualResponses); // Build a pager $PagerFactory = new Gdn_PagerFactory(); $this->EventArguments['PagerType'] = 'MorePager'; $this->fireEvent('BeforeBuildPager'); $this->Pager = $PagerFactory->getPager($this->EventArguments['PagerType'], $this); $this->Pager->ClientID = 'Pager'; $this->Pager->MoreCode = 'More Comments'; $this->Pager->configure($this->Offset, $Limit, $ActualResponses, 'discussion/embed/' . $Discussion->DiscussionID . '/' . Gdn_Format::url($Discussion->Name) . '/%1$s'); $this->Pager->CurrentRecords = $this->Comments->numRows(); $this->fireEvent('AfterBuildPager'); } // Define the form for the comment input $this->Form = Gdn::Factory('Form', 'Comment'); $this->Form->Action = url('/post/comment/'); $this->Form->addHidden('CommentID', ''); $this->Form->addHidden('Embedded', 'true'); // Tell the post controller that this is an embedded page (in case there are custom views it needs to pick up from a theme). $this->Form->addHidden('DisplayNewCommentOnly', 'true'); // Only load/display the new comment after posting (don't load all new comments since the page last loaded). // Grab the page title if ($this->Request->get('title')) { $this->Form->setValue('Name', $this->Request->get('title')); } // Set existing DiscussionID for comment form if ($Discussion) { $this->Form->addHidden('DiscussionID', $Discussion->DiscussionID); } foreach ($ForeignSource as $Key => $Val) { // Drop the foreign source information into the form so it can be used if creating a discussion $this->Form->addHidden($Key, $Val); // Also drop it into the definitions so it can be picked up for stashing comments $this->addDefinition($Key, $Val); } // Retrieve & apply the draft if there is one: $Draft = false; if (Gdn::session()->UserID && $Discussion) { $DraftModel = new DraftModel(); $Draft = $DraftModel->get(Gdn::session()->UserID, 0, 1, $Discussion->DiscussionID)->firstRow(); $this->Form->addHidden('DraftID', $Draft ? $Draft->DraftID : ''); } if ($Draft) { $this->Form->setFormValue('Body', $Draft->Body); } else { // Look in the session stash for a comment $StashComment = Gdn::session()->getPublicStash('CommentForForeignID_' . $ForeignSource['vanilla_identifier']); if ($StashComment) { $this->Form->setValue('Body', $StashComment); $this->Form->setFormValue('Body', $StashComment); } } // Deliver JSON data if necessary if ($this->_DeliveryType != DELIVERY_TYPE_ALL) { if ($this->Discussion) { $this->setJson('LessRow', $this->Pager->toString('less')); $this->setJson('MoreRow', $this->Pager->toString('more')); } $this->View = 'comments'; } // Ordering note for JS if ($SortComments == 'desc') { $this->addDefinition('PrependNewComments', '1'); } // Report the discussion id so js can use it. if ($Discussion) { $this->addDefinition('DiscussionID', $Discussion->DiscussionID); } $this->fireEvent('BeforeDiscussionRender'); $this->render(); }
/** * Show all discussions in a particular category. * * @since 2.0.0 * @access public * * @param string $CategoryIdentifier Unique category slug or ID. * @param int $Offset Number of discussions to skip. */ public function index($CategoryIdentifier = '', $Page = '0') { // Figure out which category layout to choose (Defined on "Homepage" settings page). $Layout = c('Vanilla.Categories.Layout'); if ($CategoryIdentifier == '') { switch ($Layout) { case 'mixed': $this->View = 'discussions'; $this->discussions(); break; case 'table': $this->table(); break; default: $this->View = 'all'; $this->all('', CategoryModel::getRootDisplayAs()); break; } return; } else { $Category = CategoryModel::categories($CategoryIdentifier); if (empty($Category)) { throw notFoundException(); } $Category = (object) $Category; Gdn_Theme::section($Category->CssClass); // Load the breadcrumbs. $this->setData('Breadcrumbs', CategoryModel::getAncestors(val('CategoryID', $Category))); $this->setData('Category', $Category, true); $this->title(htmlspecialchars(val('Name', $Category, ''))); $this->description(val('Description', $Category), true); switch ($Category->DisplayAs) { case 'Flat': case 'Heading': case 'Categories': $stopHeadings = val('Depth', $Category) > CategoryModel::instance()->getNavDepth(); CategoryModel::instance()->setStopHeadingsCalculation($stopHeadings); if ($this->SyndicationMethod != SYNDICATION_NONE) { // RSS can't show a category list so just tell it to expand all categories. saveToConfig('Vanilla.ExpandCategories', true, false); } else { // This category is an overview style category and displays as a category list. switch ($Layout) { case 'mixed': $this->View = 'discussions'; $this->discussions($CategoryIdentifier); break; case 'table': $this->table($CategoryIdentifier, $Category->DisplayAs); break; default: $this->View = 'all'; $this->All($CategoryIdentifier, $Category->DisplayAs); break; } return; } break; } Gdn_Theme::section('DiscussionList'); // Figure out which discussions layout to choose (Defined on "Homepage" settings page). $Layout = c('Vanilla.Discussions.Layout'); switch ($Layout) { case 'table': if ($this->SyndicationMethod == SYNDICATION_NONE) { $this->View = 'table'; } break; default: // $this->View = 'index'; break; } $this->setData('CategoryTree', $this->getCategoryTree($CategoryIdentifier, val('DisplayAs', $Category))); // Add a backwards-compatibility shim for the old categories. $this->categoriesCompatibilityCallback = function () use($CategoryIdentifier) { $categories = CategoryModel::getSubtree($CategoryIdentifier, false); return $categories; }; // Setup head $this->Menu->highlightRoute('/discussions'); if ($this->Head) { $this->addJsFile('discussions.js'); $this->Head->addRss(categoryUrl($Category) . '/feed.rss', $this->Head->title()); } // Set CategoryID $CategoryID = val('CategoryID', $Category); $this->setData('CategoryID', $CategoryID, true); // Add modules $this->addModule('NewDiscussionModule'); $this->addModule('DiscussionFilterModule'); $this->addModule('CategoriesModule'); $this->addModule('BookmarkedModule'); // Get a DiscussionModel $DiscussionModel = new DiscussionModel(); $DiscussionModel->setSort(Gdn::request()->get()); $DiscussionModel->setFilters(Gdn::request()->get()); $this->setData('Sort', $DiscussionModel->getSort()); $this->setData('Filters', $DiscussionModel->getFilters()); $CategoryIDs = array($CategoryID); if (c('Vanilla.ExpandCategories')) { $CategoryIDs = array_merge($CategoryIDs, array_column($this->data('Categories'), 'CategoryID')); } $Wheres = array('d.CategoryID' => $CategoryIDs); $this->setData('_ShowCategoryLink', count($CategoryIDs) > 1); // Check permission $this->permission('Vanilla.Discussions.View', true, 'Category', val('PermissionCategoryID', $Category)); // Set discussion meta data. $this->EventArguments['PerPage'] = c('Vanilla.Discussions.PerPage', 30); $this->fireEvent('BeforeGetDiscussions'); list($Offset, $Limit) = offsetLimit($Page, $this->EventArguments['PerPage']); if (!is_numeric($Offset) || $Offset < 0) { $Offset = 0; } $Page = PageNumber($Offset, $Limit); // Allow page manipulation $this->EventArguments['Page'] =& $Page; $this->EventArguments['Offset'] =& $Offset; $this->EventArguments['Limit'] =& $Limit; $this->fireEvent('AfterPageCalculation'); // We want to limit the number of pages on large databases because requesting a super-high page can kill the db. $MaxPages = c('Vanilla.Categories.MaxPages'); if ($MaxPages && $Page > $MaxPages) { throw notFoundException(); } $CountDiscussions = $DiscussionModel->getCount($Wheres); if ($MaxPages && $MaxPages * $Limit < $CountDiscussions) { $CountDiscussions = $MaxPages * $Limit; } $this->setData('CountDiscussions', $CountDiscussions); $this->setData('_Limit', $Limit); // We don't wan't child categories in announcements. $Wheres['d.CategoryID'] = $CategoryID; $AnnounceData = $DiscussionModel->getAnnouncements($Wheres, $Offset, $Limit); $this->AnnounceData = $this->setData('Announcements', $AnnounceData); $Wheres['d.CategoryID'] = $CategoryIDs; // RSS should include announcements. if ($this->SyndicationMethod !== SYNDICATION_NONE) { $Wheres['Announce'] = 'all'; } $this->DiscussionData = $this->setData('Discussions', $DiscussionModel->getWhereRecent($Wheres, $Limit, $Offset)); // Build a pager $PagerFactory = new Gdn_PagerFactory(); $url = CategoryUrl($CategoryIdentifier); $this->EventArguments['PagerType'] = 'Pager'; $this->fireEvent('BeforeBuildPager'); if (!$this->data('_PagerUrl')) { $this->setData('_PagerUrl', $url . '/{Page}'); } $queryString = DiscussionModel::getSortFilterQueryString($DiscussionModel->getSort(), $DiscussionModel->getFilters()); $this->setData('_PagerUrl', $this->data('_PagerUrl') . $queryString); $this->Pager = $PagerFactory->GetPager($this->EventArguments['PagerType'], $this); $this->Pager->ClientID = 'Pager'; $this->Pager->configure($Offset, $Limit, $CountDiscussions, $this->data('_PagerUrl')); $this->Pager->Record = $Category; PagerModule::current($this->Pager); $this->setData('_Page', $Page); $this->setData('_Limit', $Limit); $this->fireEvent('AfterBuildPager'); // Set the canonical Url. $this->canonicalUrl(categoryUrl($Category, pageNumber($Offset, $Limit))); // Change the controller name so that it knows to grab the discussion views $this->ControllerName = 'DiscussionsController'; // Pick up the discussions class $this->CssClass = 'Discussions Category-' . val('UrlCode', $Category); // Deliver JSON data if necessary if ($this->_DeliveryType != DELIVERY_TYPE_ALL) { $this->setJson('LessRow', $this->Pager->toString('less')); $this->setJson('MoreRow', $this->Pager->toString('more')); $this->View = 'discussions'; } // Render default view. $this->fireEvent('BeforeCategoriesRender'); $this->render(); } }
/** * Return the page number from the given variables that may have an offset or a page. * * @param array $Vars The variables that should contain an Offset or Page key. * @param int|string $PageSize The pagesize or the config key of the pagesize. * @return int */ public static function pageNumber($Vars, $PageSize) { if (isset($Vars['Page'])) { return $Vars['Page']; } if (isset($Vars['Offset'])) { if (is_numeric($PageSize)) { return pageNumber($Vars['Offset'], $PageSize, false, Gdn::Session()->IsValid()); } else { return pageNumber($Vars['Offset'], C($PageSize, 30), false, Gdn::Session()->IsValid()); } } return 1; }
<?php $pager = Gdn::controller()->data('Pager'); $pagerType = !$pager->TotalRecords ? 'more' : 'numbered'; $hasNext = true; // Get total page count, allowing override $pageCount = ceil($pager->TotalRecords / $pager->Limit); $currentPage = pageNumber($pager->Offset, $pager->Limit); if ($pagerType === 'numbered' && $currentPage >= $pageCount) { $hasNext = false; } if ($pagerType === 'more' && ($pager->CurrentRecords === false || $pager->CurrentRecords < $pager->Limit)) { $hasNext = false; } $pagerString = '<div class="pager">'; $pagerCount = '<div class="pager-count">'; $pagerCount .= sprintf(t('Page %s of %s'), $currentPage, $pageCount ? $pageCount : 1); $pagerCount .= '</div>'; if ($pagerType === 'more') { $pagerCount = '<div class="pager-count">'; $pagerCount .= sprintf(t('Page %s'), $currentPage); $pagerCount .= '</div>'; } $pagerString .= $pagerCount; $pagerString .= '<nav class="btn-group">'; // Previous if ($currentPage == 1) { $disabled = 'disabled'; } else { $disabled = ''; }