diff --git a/core/Object.php b/core/Object.php
old mode 100644
new mode 100755
index 6c3decbb9..2fa517ab7
--- a/core/Object.php
+++ b/core/Object.php
@@ -85,7 +85,10 @@ abstract class Object {
$args = func_get_args();
$class = self::getCustomClass(array_shift($args));
$reflector = new ReflectionClass($class);
- return $reflector->newInstanceArgs($args);
+ if($reflector->getConstructor()) {
+ return $reflector->newInstanceArgs($args);
+ }
+ return new $class;
}
private static $_cache_inst_args = array();
diff --git a/css/GridFieldPaginator.css b/css/GridFieldPaginator.css
new file mode 100644
index 000000000..b11b1037a
--- /dev/null
+++ b/css/GridFieldPaginator.css
@@ -0,0 +1,3 @@
+.ss-gridfield-pagination { text-align: center; padding-bottom: 10px; }
+
+.ss-gridfield-pagination-button.loading { background: url(../images/network-save.gif) no-repeat 0% 50%; padding-left: 20px; }
diff --git a/forms/GridField.php b/forms/GridField.php
index c000137ef..43141bf66 100644
--- a/forms/GridField.php
+++ b/forms/GridField.php
@@ -1,7 +1,25 @@
+ * $gridField = new GridField('ExampleGrid', 'Example grid', new DataList('Page'));
+ *
+ *
+ * If you want to modify the output of the grid you can attach a customised
+ * DataGridPresenter that are the actual Renderer of the data. Sapphire provides
+ * a default one if you chooses not to.
+ *
+ * @see GridFieldPresenter
+ * @see SS_List
+ *
* @package sapphire
* @subpackage forms
*/
@@ -10,7 +28,7 @@ class GridField extends FormField {
/**
* @var SS_List
*/
- protected $dataSource = null;
+ protected $list = null;
/**
* @var string
@@ -26,26 +44,43 @@ class GridField extends FormField {
* @var string - the classname of the DataObject that the GridField will display
*/
protected $modelClassName = '';
-
+
+ /**
+ * Url handlers
+ *
+ * @var array
+ */
+ public static $url_handlers = array(
+ '$Action' => '$Action',
+ );
+
/**
* Creates a new GridField field
*
* @param string $name
* @param string $title
- * @param SS_List $datasource
+ * @param SS_List $dataList
* @param Form $form
- * @param string $dataPresenterClassName
+ * @param string|GridFieldPresenter $dataPresenterClassName - can either pass in a string or an instance of a GridFieldPresenter
*/
- public function __construct($name, $title = null, SS_List $datasource = null, Form $form = null, $dataPresenterClassName = 'GridFieldPresenter') {
+ public function __construct($name, $title = null, SS_List $dataList = null, Form $form = null, $dataPresenterClassName = 'GridFieldPresenter') {
parent::__construct($name, $title, null, $form);
- if ($datasource) {
- $this->setDatasource($datasource);
+ if ($dataList) {
+ $this->setList($dataList);
}
$this->setPresenter($dataPresenterClassName);
}
+ /**
+ *
+ * @return string - HTML
+ */
+ public function index() {
+ return $this->FieldHolder();
+ }
+
/**
* @param string $modelClassName
*/
@@ -63,8 +98,8 @@ class GridField extends FormField {
if ($this->modelClassName) {
return $this->modelClassName;
}
- if ($this->datasource->dataClass) {
- return $this->datasource->dataClass;
+ if ($this->list->dataClass) {
+ return $this->list->dataClass;
}
throw new Exception(get_class($this).' does not have a modelClassName');
@@ -113,11 +148,10 @@ class GridField extends FormField {
/**
* Set the datasource
*
- * @param SS_List $datasource
+ * @param SS_List $list
*/
- public function setDataSource(SS_List $datasource) {
- $this->datasource = $datasource;
-
+ public function setList(SS_List $list) {
+ $this->list = $list;
return $this;
}
@@ -126,8 +160,8 @@ class GridField extends FormField {
*
* @return SS_List
*/
- public function getDataSource() {
- return $this->datasource;
+ public function getList() {
+ return $this->list;
}
/**
diff --git a/forms/GridFieldPaginator.php b/forms/GridFieldPaginator.php
new file mode 100644
index 000000000..e847a2da4
--- /dev/null
+++ b/forms/GridFieldPaginator.php
@@ -0,0 +1,277 @@
+totalNumberOfPages = $totalNumberOfPages;
+ $this->currentPage = $currentPage;
+ }
+
+ /**
+ * Returns the rendered template for GridField
+ *
+ * @return string
+ */
+ public function Render() {
+ return $this->renderWith(array($this->template));
+ }
+
+ /**
+ * Returns a url to the last page in the result
+ *
+ * @return string
+ */
+ public function FirstLink() {
+ if($this->haveNoPages()) {
+ return false;
+ }
+ return 1;
+ }
+
+ /**
+ * Returns a url to the previous page in the result
+ *
+ * @return string
+ */
+ public function PreviousLink() {
+ if($this->isFirstPage() || $this->haveNoPages()) {
+ return false;
+ }
+ // Out of bounds
+ if($this->currentPage>$this->totalNumberOfPages){
+ return $this->LastLink();
+ }
+
+ return ($this->currentPage-1);
+ }
+
+ /**
+ * Returns a list of pages with links, pagenumber and if it is the current
+ * page.
+ *
+ * @return ArrayList
+ */
+ public function Pages() {
+ if($this->haveNoPages()) {
+ return false;
+ }
+
+ $list = new ArrayList();
+ for($idx=1;$idx<=$this->totalNumberOfPages;$idx++) {
+ $data = new ArrayData(array());
+ $data->setField('PageNumber',$idx);
+ if($idx == $this->currentPage ) {
+ $data->setField('Current',true);
+ } else {
+ $data->setField('Current',false);
+ }
+
+ $data->setField('Link',$idx);
+ $list->push($data);
+ }
+ return $list;
+ }
+
+ /**
+ * Returns a url to the next page in the result
+ *
+ * @return string
+ */
+ public function NextLink() {
+ if($this->isLastPage() || $this->haveNoPages() ) {
+ return false;
+ }
+ // Out of bounds
+ if($this->currentPage<1) {
+ return $this->FirstLink();
+ }
+ return ($this->currentPage+1);
+ }
+
+ /**
+ * Returns a url to the last page in the result
+ *
+ * @return string
+ */
+ public function LastLink() {
+ if($this->haveNoPages()) {
+ return false;
+ }
+ return ($this->totalNumberOfPages);
+ }
+
+ /**
+ * Are we currently on the first page
+ *
+ * @return bool
+ */
+ protected function isFirstPage() {
+ return (bool)($this->currentPage<=1);
+ }
+
+ /**
+ * Are we currently on the last page?
+ *
+ * @return bool
+ */
+ protected function isLastPage() {
+ return (bool)($this->currentPage>=$this->totalNumberOfPages);
+ }
+
+ /**
+ * Is there only one page of results?
+ *
+ * @return bool
+ */
+ protected function haveNoPages() {
+ return (bool)($this->totalNumberOfPages<=1);
+ }
+
+}
+
+/**
+ * This is the extension that decorates the GridFieldPresenter. Since a extension
+ * can't be a Viewable data it's split like this.
+ *
+ * @see GridField
+ * @package sapphire
+ */
+class GridFieldPaginator_Extension extends Extension {
+
+ /**
+ *
+ * @var int
+ */
+ protected $paginationLimit;
+
+ /**
+ *
+ * @var int
+ */
+ protected $totalNumberOfPages = 1;
+
+ /**
+ *
+ * @var int
+ */
+ protected $currentPage = 1;
+
+ /**
+ *
+ * @return string
+ */
+ public function Footer() {
+ return new GridFieldPaginator($this->totalNumberOfPages, $this->currentPage);
+ }
+
+ /**
+ * NOP
+ */
+ public function __construct() {}
+
+ /**
+ * Set the limit for each page
+ *
+ * @param int $limit
+ * @return GridFieldPaginator_Extension
+ */
+ public function paginationLimit($limit) {
+ $this->paginationLimit = $limit;
+ return $this;
+ }
+
+ /**
+ * Filter the list to only contain a pagelength of items
+ *
+ * @return bool - if the pagination was activated
+ * @see GridFieldPresenter::Items()
+ */
+ public function filterList(SS_List $list, $parameters){
+ if(!$this->canUsePagination($list)) {
+ return false;
+ }
+
+ $currentPage = $parameters->Request->requestVar('page');
+ if(!$currentPage) {
+ $currentPage = 1;
+ }
+
+ $this->totalNumberOfPages = $this->getMaxPagesCount($list);
+
+ if($currentPage<1) {
+ // Current page is below 1, show nothing and save cpu cycles
+ $list->where('1=0');
+ } elseif($currentPage > $this->totalNumberOfPages) {
+ // current page is over max pages, show nothing and save cpu cycles
+ $list->where('1=0');
+ } else {
+ $offset = ($currentPage-1)*$this->paginationLimit;
+ $list->getRange((int)$offset,$this->paginationLimit);
+ }
+ $this->currentPage = $currentPage;
+
+ return true;
+ }
+
+ /**
+ * Helper function that see if the pagination has been set and that the
+ * $list can use pagination.
+ *
+ * @param SS_List $list
+ * @return bool
+ */
+ protected function canUsePagination(SS_List $list) {
+ if(!$this->paginationLimit) {
+ return false;
+ }
+ if(!method_exists($list, 'getRange')) {
+ return false;
+ }
+ if(!method_exists($list, 'limit')){
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @return int
+ */
+ protected function getMaxPagesCount($list) {
+ $list->limit(null);
+ $number = $list->count();
+ $number = ceil($number/$this->paginationLimit);
+ return $number;
+ }
+}
\ No newline at end of file
diff --git a/forms/GridFieldPresenter.php b/forms/GridFieldPresenter.php
index 306ba4098..27144d6d9 100644
--- a/forms/GridFieldPresenter.php
+++ b/forms/GridFieldPresenter.php
@@ -1,7 +1,46 @@
+ * $presenter = new GridFieldPresenter();
+ * $presenter->sort('Title', 'desc');
+ * $gridField = new GridField('ExampleGrid', 'Example grid', new DataList('Page'),null, $presenter);
+ *
+ *
+ * Another example is to change the template for the rendering
+ *
+ *
+ * $presenter = new GridFieldPresenter();
+ * $presenter->setTemplate('MyNiftyGridTemplate');
+ * $gridField = new GridField('ExampleGrid', 'Example grid', new DataList('Page'),null, $presenter);
+ *
+ *
+ * There is also a possibility to add extensions to the GridPresenter. An
+ * example is the DataGridPagination that decorates the GridField with
+ * pagination. Look in the GridFieldPresenter::Items() and the filterList extend
+ * and GridFieldPresenter::Footers()
+ *
+ *
+ * GridFieldPresenter::add_extension('GridFieldPaginator_Extension');
+ * $presenter = new GridFieldPresenter();
+ * // This is actually calling GridFieldPaginator_Extension::paginationLimit()
+ * $presenter->paginationLimit(3);
+ * $gridField = new GridField('ExampleGrid', 'Example grid', new DataList('Page'),null, $presenter);
+ *
+ *
* @see GridField
+ * @see GridFieldPaginator
* @package sapphire
*/
class GridFieldPresenter extends ViewableData {
@@ -46,10 +85,19 @@ class GridFieldPresenter extends ViewableData {
/**
* @param string $template
*/
- function setTemplate($template){
+ public function setTemplate($template){
$this->template = $template;
}
+ /**
+ * The name of the Field
+ *
+ * @return string
+ */
+ public function Name() {
+ return $this->getGridField()->Name();
+ }
+
/**
* @param GridField $GridField
*/
@@ -64,6 +112,14 @@ class GridFieldPresenter extends ViewableData {
return $this->GridField;
}
+ /**
+ *
+ * @param type $extension
+ */
+ public static function add_extension($extension) {
+ parent::add_extension(__CLASS__, $extension);
+ }
+
/**
* Sort the grid by columns
*
@@ -82,27 +138,29 @@ class GridFieldPresenter extends ViewableData {
* @return ArrayList
*/
public function Items() {
- $items = new ArrayList();
+ $items = new ArrayList();
if($this->sorting) {
- $this->setSorting($this->sorting);
+ $this->setSortingOnList($this->sorting);
}
+ //empty for now
+ $list = $this->getGridField()->getList();
- if($sources = $this->getGridField()->getDataSource()) {
+ $parameters = new stdClass();
+ $parameters->Controller = Controller::curr();
+ $parameters->Request = Controller::curr()->getRequest();
+
+ $this->extend('filterList', $list, $parameters);
+
+ if($list) {
+ $numberOfRows = $list->count();
$counter = 0;
-
- foreach($sources as $source) {
- if(!$source) {
- continue;
- }
-
- $itemPresenter = new $this->itemClass($source, $this);
- $itemPresenter->iteratorProperties($counter++, $sources->count());
-
+ foreach($list as $item) {
+ $itemPresenter = new $this->itemClass($item, $this);
+ $itemPresenter->iteratorProperties($counter++, $numberOfRows);
$items->push($itemPresenter);
}
}
-
return $items;
}
@@ -122,22 +180,32 @@ class GridFieldPresenter extends ViewableData {
* @throws Exception
*/
public function Headers() {
- if(!$this->getDatasource()) {
+ if(!$this->getList()) {
throw new Exception(sprintf(
'%s needs an data source to be able to render the form', get_class($this->getGridField())
));
}
-
- $summaryFields = singleton($this->getModelClass())->summaryFields();
-
- return $this->summaryFieldsToList($summaryFields);
+ return $this->summaryFieldsToList($this->FieldList());
+ }
+
+ /**
+ *
+ * @return ArrayList
+ */
+ public function Footers() {
+ $arrayList = new ArrayList();
+ $footers = $this->extend('Footer');
+ foreach($footers as $footer) {
+ $arrayList->push($footer);
+ }
+ return $arrayList;
}
/**
* @return SS_List
*/
- protected function getDataSource() {
- return $this->getGridField()->getDatasource();
+ public function getList() {
+ return $this->getGridField()->getList();
}
/**
@@ -150,12 +218,12 @@ class GridFieldPresenter extends ViewableData {
/**
* Add the combined sorting on the datasource
*
- * If the sorting isn't set in one go on the datasource, only the latest sort
- * will be executed.s
+ * If the sorting isn't set in the datasource, only the latest sort
+ * will be executed.
*
* @param array $sortColumns
*/
- protected function setSorting(array $sortColumns) {
+ protected function setSortingOnList(array $sortColumns) {
$resultColumns = array();
foreach($sortColumns as $column => $sortOrder) {
@@ -163,7 +231,7 @@ class GridFieldPresenter extends ViewableData {
}
$sort = implode(', ', $resultColumns);
- $this->getDataSource()->sort($sort);
+ $this->getList()->sort($sort);
}
/**
diff --git a/javascript/GridFieldPaginator.js b/javascript/GridFieldPaginator.js
new file mode 100644
index 000000000..54b6182ae
--- /dev/null
+++ b/javascript/GridFieldPaginator.js
@@ -0,0 +1,30 @@
+jQuery(function($){
+
+ var onGridClick = function(){
+ var form = $(this).closest("form");
+ var gridField = $(this).closest(".ss-gridfield");
+ $(this).addClass('loading');
+ $.ajax({
+ type: "POST",
+ url: form.attr('action')+'/field/'+gridField.attr('id'),
+ data: form.serialize()+"&page="+$(this).attr('value'),
+ success: function(data) {
+ $(gridField).replaceWith(data);
+ gridInit();
+ },
+ error: function() {
+ alert('There seems like there where some failure when trying to fetch the page, please reload and try again.');
+ }
+
+ });
+
+ return false;
+ }
+
+ var gridInit = function() {
+ $('.ss-gridfield-pagination-button').click(onGridClick);
+ }
+
+ gridInit();
+
+});
\ No newline at end of file
diff --git a/model/DataQuery.php b/model/DataQuery.php
index eb1bfbe46..7d9d98fde 100644
--- a/model/DataQuery.php
+++ b/model/DataQuery.php
@@ -298,13 +298,9 @@ class DataQuery {
* Set the limit of this query
*/
function limit($limit) {
- if($limit) {
- $clone = $this;
- $clone->query->limit($limit);
- return $clone;
- } else {
- return $this;
- }
+ $clone = $this;
+ $clone->query->limit($limit);
+ return $clone;
}
/**
diff --git a/scss/GridFieldPaginator.scss b/scss/GridFieldPaginator.scss
new file mode 100644
index 000000000..6ce64810c
--- /dev/null
+++ b/scss/GridFieldPaginator.scss
@@ -0,0 +1,7 @@
+.ss-gridfield-pagination {
+ text-align: center;
+ padding-bottom: 10px;
+}
+.ss-gridfield-pagination-button.loading{
+ background: url(../images/network-save.gif) no-repeat 0% 50%; padding-left: 20px;
+}
\ No newline at end of file
diff --git a/templates/GridField.ss b/templates/GridField.ss
index ee875c0d4..a6b55f01e 100644
--- a/templates/GridField.ss
+++ b/templates/GridField.ss
@@ -1,7 +1,7 @@
<% require css(sapphire/thirdparty/jquery-ui-themes/smoothness/jquery-ui.css) %>
<% require css(sapphire/css/GridField.css) %>
-