itemsPerPage = $itemsPerPage ?: GridFieldPaginator::config()->get('default_items_per_page'); } /** * Determine what happens when this component is used with a list that isn't {@link SS_Filterable}. * * - true: An exception is thrown * - false: This component will be ignored - it won't make any changes to the GridField. * * By default, this is set to true so that it's clearer what's happening, but the predefined * {@link GridFieldConfig} subclasses set this to false for flexibility. * * @param bool $throwExceptionOnBadDataType * @return $this */ public function setThrowExceptionOnBadDataType($throwExceptionOnBadDataType) { $this->throwExceptionOnBadDataType = $throwExceptionOnBadDataType; return $this; } /** * See {@link setThrowExceptionOnBadDataType()} * * @return bool */ public function getThrowExceptionOnBadDataType() { return $this->throwExceptionOnBadDataType; } /** * Check that this dataList is of the right data type. * Returns false if it's a bad data type, and if appropriate, throws an exception. * * @param SS_List $dataList * @return bool */ protected function checkDataType($dataList) { if($dataList instanceof SS_Limitable) { return true; } else { if($this->throwExceptionOnBadDataType) { throw new LogicException( get_class($this) . " expects an SS_Limitable list to be passed to the GridField."); } return false; } } /** * * @param GridField $gridField * @return array */ public function getActions($gridField) { if(!$this->checkDataType($gridField->getList())) { return []; } return array('paginate'); } /** * * @param GridField $gridField * @param string $actionName * @param string $arguments * @param array $data * @return void */ public function handleAction(GridField $gridField, $actionName, $arguments, $data) { if(!$this->checkDataType($gridField->getList())) return; if($actionName !== 'paginate') { return; } $state = $this->getGridPagerState($gridField); $state->currentPage = (int)$arguments; } protected $totalItems = 0; /** * Retrieves/Sets up the state object used to store and retrieve information * about the current paging details of this GridField * @param GridField $gridField * @return GridState_Data */ protected function getGridPagerState(GridField $gridField) { $state = $gridField->State->GridFieldPaginator; // Force the state to the initial page if none is set $state->currentPage(1); return $state; } /** * * @param GridField $gridField * @param SS_List $dataList * @return SS_List */ public function getManipulatedData(GridField $gridField, SS_List $dataList) { if(!$this->checkDataType($dataList)) return $dataList; $state = $this->getGridPagerState($gridField); // Update item count prior to filter. GridFieldPageCount will rely on this value $this->totalItems = $dataList->count(); $startRow = $this->itemsPerPage * ($state->currentPage - 1); // Prevent visiting a page with an offset higher than the total number of items if($startRow >= $this->totalItems) { $state->currentPage = 1; $startRow = 0; } if(!($dataList instanceof SS_Limitable) || ($dataList instanceof UnsavedRelationList)) { return $dataList; } return $dataList->limit((int)$this->itemsPerPage, (int)$startRow); } /** * Determines arguments to be passed to the template for building this field * * @param GridField $gridField * @return ArrayData If paging is available this will be an ArrayData * object of paging details with these parameters: * */ public function getTemplateParameters(GridField $gridField) { if(!$this->checkDataType($gridField->getList())) { return null; } $state = $this->getGridPagerState($gridField); // Figure out which page and record range we're on $totalRows = $this->totalItems; if (!$totalRows) { return null; } $totalPages = (int)ceil($totalRows/$this->itemsPerPage); if($totalPages == 0) { $totalPages = 1; } $firstShownRecord = ($state->currentPage - 1) * $this->itemsPerPage + 1; if($firstShownRecord > $totalRows) { $firstShownRecord = $totalRows; } $lastShownRecord = $state->currentPage * $this->itemsPerPage; if($lastShownRecord > $totalRows) { $lastShownRecord = $totalRows; } // If there is only 1 page for all the records in list, we don't need to go further // to sort out those first page, last page, pre and next pages, etc // we are not render those in to the paginator. if($totalPages === 1){ return new ArrayData(array( 'OnlyOnePage' => true, 'FirstShownRecord' => $firstShownRecord, 'LastShownRecord' => $lastShownRecord, 'NumRecords' => $totalRows, 'NumPages' => $totalPages )); } else { // First page button $firstPage = new GridField_FormAction($gridField, 'pagination_first', 'First', 'paginate', 1); $firstPage->addExtraClass('btn btn-secondary btn--no-text font-icon-angle-double-left ss-gridfield-firstpage'); if($state->currentPage == 1) { $firstPage = $firstPage->performDisabledTransformation(); } // Previous page button $previousPageNum = $state->currentPage <= 1 ? 1 : $state->currentPage - 1; $previousPage = new GridField_FormAction($gridField, 'pagination_prev', 'Previous', 'paginate', $previousPageNum); $previousPage->addExtraClass('btn btn-secondary btn--no-text font-icon-angle-left ss-gridfield-previouspage'); if($state->currentPage == 1) $previousPage = $previousPage->performDisabledTransformation(); // Next page button $nextPageNum = $state->currentPage >= $totalPages ? $totalPages : $state->currentPage + 1; $nextPage = new GridField_FormAction($gridField, 'pagination_next', 'Next', 'paginate', $nextPageNum); $nextPage->addExtraClass('btn btn-secondary btn--no-text font-icon-angle-right ss-gridfield-nextpage'); if($state->currentPage == $totalPages) $nextPage = $nextPage->performDisabledTransformation(); // Last page button $lastPage = new GridField_FormAction($gridField, 'pagination_last', 'Last', 'paginate', $totalPages); $lastPage->addExtraClass('btn btn-secondary btn--no-text font-icon-angle-double-right ss-gridfield-lastpage'); if($state->currentPage == $totalPages) { $lastPage = $lastPage->performDisabledTransformation(); } // Render in template return new ArrayData(array( 'OnlyOnePage' => false, 'FirstPage' => $firstPage, 'PreviousPage' => $previousPage, 'CurrentPageNum' => $state->currentPage, 'NumPages' => $totalPages, 'NextPage' => $nextPage, 'LastPage' => $lastPage, 'FirstShownRecord' => $firstShownRecord, 'LastShownRecord' => $lastShownRecord, 'NumRecords' => $totalRows )); } } /** * * @param GridField $gridField * @return array */ public function getHTMLFragments($gridField) { $forTemplate = $this->getTemplateParameters($gridField); if(!$forTemplate) { return null; } $template = SSViewer::get_templates_by_class($this, '_Row', __CLASS__); return array( 'footer' => $forTemplate->renderWith( $template, array('Colspan' => count($gridField->getColumns())) ) ); } /** * @param int $num * @return $this */ public function setItemsPerPage($num) { $this->itemsPerPage = $num; return $this; } /** * @return Int */ public function getItemsPerPage() { return $this->itemsPerPage; } }