From 7e9fb74be4ec0b1ac4c5442c0916ff2372f38067 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Sun, 26 Jun 2011 20:20:10 +0200 Subject: [PATCH 01/29] 1.) Add formatting option to Columns and Buttons using syntax 'blah blah blah %key_name% blah' 2.) Fixed XSS vulnerability (echo $record['something'] without escaping it) 3.) Add type property to enable explicitly set a type of column and new type email renderer 4.) Add length property specifying maximal length of column --- Button.php | 4 +- Column.php | 531 ++++++++++++++++++++++++++++++++--------------------- Grid.php | 12 +- 3 files changed, 332 insertions(+), 215 deletions(-) diff --git a/Button.php b/Button.php index 248de05..a9f86e0 100644 --- a/Button.php +++ b/Button.php @@ -52,7 +52,7 @@ public function getConfirmationQuestion($row) if (is_callable($this->confirmationQuestion)) { return call_user_func($this->confirmationQuestion, $row); } else { - return $this->confirmationQuestion; + return Grid::formatRecordString($row, $this->confirmationQuestion); } } @@ -103,4 +103,4 @@ protected function createButton($row = null) return $el; } -} \ No newline at end of file +} diff --git a/Column.php b/Column.php index 03874bf..05320d8 100644 --- a/Column.php +++ b/Column.php @@ -1,6 +1,8 @@ + // - /** @var string */ - private $label; + /** @var string */ + private $label; - /** @var callback */ - private $renderer = null; + /** @var callback */ + private $renderer = null; - /** @var bool */ - private $sortable = false; + /** @var int */ + private $maxlen = null; - /** @var string */ - private $dateTimeFormat = "j.n.Y G:i"; + /** @var string */ + private $type = 'string'; - /** @var string|callable */ - private $cellClass = null; + /** @var bool */ + private $sortable = false; - // - - // - - public function setCellClass($class) - { - $this->cellClass = $class; - return $this; - } - - - - public function getCellClass($iterator, $row) - { - if (is_callable($this->cellClass)) { - return call_user_func($this->cellClass, $iterator, $row); - } elseif (is_string($this->cellClass)) { - return $this->cellClass; - } else { - return null; - } - } - - - - /** - * Get label - * @return string - */ - public function getLabel() - { - return $this->label; - } - - - - /** - * Set label - * @param string label - * @return Column - */ - public function setLabel($label) - { - $this->label = $label; - return $this; - } - - - - /** - * Get cell renderer - * @return callback - */ - public function getRenderer() - { - return $this->renderer; - } - - - - /** - * Set cell renderer - * @param callback cell renderer - * @return Column - */ - public function setRenderer($cellRenderer) - { - $this->renderer = $cellRenderer; - return $this; - } - - - - /** - * Is sortable? - * @return bool - */ - public function isSortable() { - return $this->sortable; - } - - - - /** - * Set sortable - * @param bool sortable - * @return Column - */ - public function setSortable($sortable) { - $this->sortable = $sortable; - return $this; - } - - - - /** - * Get sorting - * @return string|null asc, desc or null - */ - public function getSorting() - { - $grid = $this->getGrid(); - if ($grid->sortColumn === $this->getName()) { - return $grid->sortType; - } else { - return null; - } - } - - - - /** - * Get date/time format - * @return string - */ - public function getDateTimeFormat() { - return $this->dateTimeFormat; - } - - - - /** - * Set date/time format - * @param string datetime format - * @return Column - */ - public function setDateTimeFormat($dateTimeFormat) { - $this->dateTimeFormat = $dateTimeFormat; - return $this; - } - - - - /** - * Get grid - * @return Grid - */ - public function getGrid() { - return $this->getParent()->getParent(); - } - - // - - /** - * Render boolean - * @param bool value - */ - public static function renderBoolean($value) - { - $icon = $value ? "check" : "closethick"; - echo ''; - } - - - - /** - * Render datetime - * @param Datetime value - * @param string datetime format - */ - public static function renderDateTime($value, $format) - { - echo $value->format($format); - } - - - - /** - * Default cell renderer - * @param mixed $record - * @param Column $column - */ - public function defaultCellRenderer($record, $column) { - $name = $column->getName(); - $value = $record->$name; - - // boolean - if (is_bool($value)) { - self::renderBoolean($value); - - // date - } elseif ($value instanceof \DateTime) { - self::renderDateTime($value, $this->dateTimeFormat); - - // other - } else { - echo $value; - } - } - - - - /** - * Render cell - * @param mixed record - */ - public function renderCell($record) { - call_user_func($this->renderer ?: array($this, "defaultCellRenderer"), $record, $this); - } - -} \ No newline at end of file + /** @var string */ + private $dateTimeFormat = "j.n.Y G:i"; + + /** @var string|callable */ + private $cellClass = null; + + /** @var string */ + private $format = null; + + // + + // + + public function setCellClass($class) + { + $this->cellClass = $class; + return $this; + } + + + + public function getCellClass($iterator, $row) + { + if (is_callable($this->cellClass)) { + return call_user_func($this->cellClass, $iterator, $row); + } elseif (is_string($this->cellClass)) { + return $this->cellClass; + } else { + return null; + } + } + + + + /** + * Get label + * @return string + */ + public function getLabel() + { + return $this->label; + } + + + + /** + * Set label + * @param string label + * @return Column + */ + public function setLabel($label) + { + $this->label = $label; + return $this; + } + + + + /** + * Get cell renderer + * @return callback + */ + public function getRenderer() + { + return $this->renderer; + } + + + + /** + * Set cell renderer + * @param callback cell renderer + * @return Column + */ + public function setRenderer($cellRenderer) + { + $this->renderer = $cellRenderer; + return $this; + } + + /** + * Set maximal length of cell + * @param $maxlen + * @return Column + */ + public function setLength($maxlen) + { + $this->maxlen = $maxlen; + return $this; + } + + /** + * Get maximal length of cell + * @return int + */ + public function getLength() + { + return $this->maxlen; + } + + /** + * Set the type of cell + * @param string type + * @return Column + */ + public function setType($type) + { + $this->type = $type; + return $this; + } + + /** + * Get the type of cell + * @return string type + */ + public function getType($type) + { + return $this->type; + } + + /** + * Set format of the cell + * @param mixed format + * @return Column + */ + public function setFormat($format) + { + $this->format = $format; + return $this; + } + + /** + * Get the format + * @return mixed + */ + public function getFormat() + { + return $this->format; + } + + + /** + * Is sortable? + * @return bool + */ + public function isSortable() { + return $this->sortable; + } + + + + /** + * Set sortable + * @param bool sortable + * @return Column + */ + public function setSortable($sortable) { + $this->sortable = $sortable; + return $this; + } + + + + /** + * Get sorting + * @return string|null asc, desc or null + */ + public function getSorting() + { + $grid = $this->getGrid(); + if ($grid->sortColumn === $this->getName()) { + return $grid->sortType; + } else { + return null; + } + } + + + + /** + * Get date/time format + * @return string + */ + public function getDateTimeFormat() { + return $this->dateTimeFormat; + } + + + + /** + * Set date/time format + * @param string datetime format + * @return Column + */ + public function setDateTimeFormat($dateTimeFormat) { + $this->dateTimeFormat = $dateTimeFormat; + return $this; + } + + + + /** + * Get grid + * @return Grid + */ + public function getGrid() { + return $this->getParent()->getParent(); + } + + // + + /** + * Render boolean + * @param bool value + */ + public static function renderBoolean($value) + { + $icon = $value ? "check" : "closethick"; + echo ''; + } + + + + /** + * Render datetime + * @param Datetime value + * @param string datetime format + */ + public static function renderDateTime($value, $format) + { + echo $value->format($format); + } + + /** + * Render the text, takes care of length + * @param string $text text to render + * @param int $maxlen maximum length of text + */ + public static function renderText($text, $maxlen) + { + if (is_null($maxlen) || Strings::length($text) < $maxlen) { + echo htmlspecialchars($text, ENT_NOQUOTES); + } else { + echo Html::el('span')->title($text) + ->setText(Strings::truncate($text, $maxlen)); + } + } + + /** + * Render the email address, takes care of length + * @param string $email email address + * @param int $maxlen maximum length of text + */ + public static function renderEmail($email, $maxlen) + { + $el = Html::el('a')->href('mailto:' . $email); + if (is_null($maxlen) || Strings::length($email) < $maxlen) { + echo $el->setText($email); + } else { + echo $el->title($email) + ->setText(Strings::truncate($email, $maxlen)); + } + } + + + /** + * Default cell renderer + * @param mixed $record + * @param Column $column + */ + public function defaultCellRenderer($record, $column) { + $name = $column->getName(); + $value = $record->$name; + + // boolean + if (in_array($this->type, array('bool', 'boolean')) || is_bool($value)) { + self::renderBoolean($value); + + // date + } elseif ($value instanceof \DateTime) { + self::renderDateTime($value, $this->dateTimeFormat); + + // email + } elseif ($this->type == 'email') { + self::renderEmail($value, $this->maxlen); + + // other + } else { + if (!is_null($this->format)) { + $value = Grid::formatRecordString($record, $this->format); + } + self::renderText($value, $this->maxlen); + } + } + + + + /** + * Render cell + * @param mixed record + */ + public function renderCell($record) { + call_user_func($this->renderer ?: array($this, "defaultCellRenderer"), $record, $this); + } + +} diff --git a/Grid.php b/Grid.php index cc7b52e..f6eafc2 100644 --- a/Grid.php +++ b/Grid.php @@ -3,6 +3,7 @@ namespace Gridito; use Nette\ComponentModel\Container, Nette\Environment, Nette\Utils\Paginator; +use Nette\Utils\Strings; /** * Grid @@ -66,6 +67,15 @@ public function __construct(\Nette\ComponentModel\IContainer $parent = null, $na $this->paginator->setItemsPerPage($this->defaultItemsPerPage); } + public static function formatRecordString($record, $formatString) + { + return Strings::replace($formatString, '#%[^%]*%#u', + function ($m) use ($record) { + $m = Strings::trim($m[0], '%'); + return $m != '' ? $record[$m] : "%"; + }); + } + // // @@ -401,4 +411,4 @@ protected function setOptions($object, $options) } } -} \ No newline at end of file +} From 13ae5dd2f56cf081f799a7f1de56af9e7d237910 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Mon, 27 Jun 2011 01:17:04 +0200 Subject: [PATCH 02/29] Add NetteModel (using Nette\Database) --- models/NetteModel.php | 65 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 models/NetteModel.php diff --git a/models/NetteModel.php b/models/NetteModel.php new file mode 100644 index 0000000..28e9b82 --- /dev/null +++ b/models/NetteModel.php @@ -0,0 +1,65 @@ +selection = $selection; + } + + + + public function getItemByUniqueId($uniqueId) + { + $select = clone $this->selection; + return $select->where('?=?', $this->getPrimaryKey(), $uniqueId) + ->fetch(); + } + + + + public function getItems() + { + $select = clone $this->selection; + + list($sortColumn, $sortType) = $this->getSorting(); + if ($sortColumn) { + $select->order("$sortColumn $sortType"); + } + return $select->limit($this->getLimit(), $this->getOffset()) + ->fetchPairs($this->getPrimaryKey()); + } + + + + /** + * Item count + * @return int + */ + protected function _count() + { + return $this->selection->count(); + } + +} From ddf8fb59e19168b040c61831fd0ca945f4415183 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Wed, 29 Jun 2011 16:05:20 +0200 Subject: [PATCH 03/29] NetteModel bug fixed --- models/NetteModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/NetteModel.php b/models/NetteModel.php index 28e9b82..80df8c5 100644 --- a/models/NetteModel.php +++ b/models/NetteModel.php @@ -33,7 +33,7 @@ public function __construct(Selection $selection) public function getItemByUniqueId($uniqueId) { $select = clone $this->selection; - return $select->where('?=?', $this->getPrimaryKey(), $uniqueId) + return $select->where($this->getPrimaryKey(), $uniqueId) ->fetch(); } From 58f0eb8da79a914cc452f732c4a9cb6c571a88ac Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Fri, 1 Jul 2011 13:29:47 +0200 Subject: [PATCH 04/29] Pridany Nette Model --- models/NetteModel.php | 57 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 models/NetteModel.php diff --git a/models/NetteModel.php b/models/NetteModel.php new file mode 100644 index 0000000..075e7ef --- /dev/null +++ b/models/NetteModel.php @@ -0,0 +1,57 @@ +selection = $selection; + } + + public function getItemByUniqueId($uniqueId) + { + $select = clone $this->selection; + return $select->where($this->getPrimaryKey(), $uniqueId) + ->fetch(); + } + + public function getItems() + { + $select = clone $this->selection; + + list($sortColumn, $sortType) = $this->getSorting(); + if ($sortColumn) { + $select->order("$sortColumn $sortType"); + } + return $select->limit($this->getLimit(), $this->getOffset()) + ->fetchPairs($this->getPrimaryKey()); + } + + + /** + * Item count + * @return int + */ + protected function _count() + { + return $this->selection->count(); + } + +} From 4849972dcbe2e937d4718eabd27aa3701ba1bf1d Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Fri, 1 Jul 2011 13:37:59 +0200 Subject: [PATCH 05/29] FIX: XSS --- Column.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Column.php b/Column.php index 03874bf..ad55a6b 100644 --- a/Column.php +++ b/Column.php @@ -213,7 +213,7 @@ public function defaultCellRenderer($record, $column) { // other } else { - echo $value; + echo htmlspecialchars($value, ENT_NOQUOTES); } } @@ -227,4 +227,4 @@ public function renderCell($record) { call_user_func($this->renderer ?: array($this, "defaultCellRenderer"), $record, $this); } -} \ No newline at end of file +} From 18451c21db8708b5e5a8c126da2549d02d9f7d5f Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Fri, 1 Jul 2011 13:44:41 +0200 Subject: [PATCH 06/29] Add property maxlen --- Column.php | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/Column.php b/Column.php index ad55a6b..b2fcffd 100644 --- a/Column.php +++ b/Column.php @@ -18,6 +18,8 @@ class Column extends \Nette\Application\UI\Control /** @var callback */ private $renderer = null; + /** @var int */ + private $maxlen = null; /** @var bool */ private $sortable = false; @@ -33,7 +35,7 @@ class Column extends \Nette\Application\UI\Control public function setCellClass($class) { - $this->cellClass = $class; + $this->cellClass = $class; return $this; } @@ -98,6 +100,25 @@ public function setRenderer($cellRenderer) return $this; } + /** + * Set maximal length of cell + * @param $maxlen + * @return Column + */ + public function setLength($maxlen) + { + $this->maxlen = $maxlen; + return $this; + } + + /** + * Get maximal length of cell + * @return int + */ + public function getLength() + { + return $this->maxlen; + } /** @@ -192,6 +213,21 @@ public static function renderDateTime($value, $format) echo $value->format($format); } + /** + * Render the text, takes care of length + * @param string $text text to render + * @param int $maxlen maximum length of text + */ + public static function renderText($text, $maxlen) + { + if (is_null($maxlen) || Strings::length($text) < $maxlen) { + echo htmlspecialchars($text, ENT_NOQUOTES); + } else { + echo Html::el('span')->title($text) + ->setText(Strings::truncate($text, $maxlen)); + } + } + /** @@ -213,7 +249,7 @@ public function defaultCellRenderer($record, $column) { // other } else { - echo htmlspecialchars($value, ENT_NOQUOTES); + self::renderText($value, $this->maxlen); } } From 159d81bbe17b1ae7cc91aa4d68c9efdeadcc2741 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Fri, 1 Jul 2011 13:47:05 +0200 Subject: [PATCH 07/29] Add property type --- Column.php | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Column.php b/Column.php index b2fcffd..9670b43 100644 --- a/Column.php +++ b/Column.php @@ -20,6 +20,10 @@ class Column extends \Nette\Application\UI\Control /** @var int */ private $maxlen = null; + + /** @var string */ + private $type = 'string'; + /** @var bool */ private $sortable = false; @@ -120,6 +124,25 @@ public function getLength() return $this->maxlen; } + /** + * Set the type of cell + * @param string type + * @return Column + */ + public function setType($type) + { + $this->type = $type; + return $this; + } + + /** + * Get the type of cell + * @return string type + */ + public function getType($type) + { + return $this->type; + } /** * Is sortable? @@ -240,7 +263,7 @@ public function defaultCellRenderer($record, $column) { $value = $record->$name; // boolean - if (is_bool($value)) { + if (in_array($this->type, array('bool', 'boolean')) || is_bool($value)) { self::renderBoolean($value); // date From cd99c438c6eef2606364d4be5404cd2d517cd9de Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Fri, 1 Jul 2011 13:48:33 +0200 Subject: [PATCH 08/29] Add email renderer --- Column.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Column.php b/Column.php index 9670b43..76e90a7 100644 --- a/Column.php +++ b/Column.php @@ -251,6 +251,21 @@ public static function renderText($text, $maxlen) } } + /** + * Render the email address, takes care of length + * @param string $email email address + * @param int $maxlen maximum length of text + */ + public static function renderEmail($email, $maxlen) + { + $el = Html::el('a')->href('mailto:' . $email); + if (is_null($maxlen) || Strings::length($email) < $maxlen) { + echo $el->setText($email); + } else { + echo $el->title($email) + ->setText(Strings::truncate($email, $maxlen)); + } + } /** @@ -270,6 +285,10 @@ public function defaultCellRenderer($record, $column) { } elseif ($value instanceof \DateTime) { self::renderDateTime($value, $this->dateTimeFormat); + // email + } elseif ($this->type == 'email') { + self::renderEmail($value, $this->maxlen); + // other } else { self::renderText($value, $this->maxlen); From d3904e7a76e62da20d5e3a4f25a307e8c7bdb8d2 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Fri, 1 Jul 2011 13:49:18 +0200 Subject: [PATCH 09/29] Fix: forgotten use ... --- Column.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Column.php b/Column.php index 76e90a7..c23f330 100644 --- a/Column.php +++ b/Column.php @@ -1,6 +1,8 @@ Date: Fri, 1 Jul 2011 13:55:42 +0200 Subject: [PATCH 10/29] Add formating --- Button.php | 4 ++-- Column.php | 26 ++++++++++++++++++++++++++ Grid.php | 12 +++++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/Button.php b/Button.php index 248de05..a9f86e0 100644 --- a/Button.php +++ b/Button.php @@ -52,7 +52,7 @@ public function getConfirmationQuestion($row) if (is_callable($this->confirmationQuestion)) { return call_user_func($this->confirmationQuestion, $row); } else { - return $this->confirmationQuestion; + return Grid::formatRecordString($row, $this->confirmationQuestion); } } @@ -103,4 +103,4 @@ protected function createButton($row = null) return $el; } -} \ No newline at end of file +} diff --git a/Column.php b/Column.php index c23f330..11de479 100644 --- a/Column.php +++ b/Column.php @@ -35,6 +35,9 @@ class Column extends \Nette\Application\UI\Control /** @var string|callable */ private $cellClass = null; + /** @var string */ + private $format = null; + // // @@ -146,6 +149,26 @@ public function getType($type) return $this->type; } + /** + * Set format of the cell + * @param mixed format + * @return Column + */ + public function setFormat($format) + { + $this->format = $format; + return $this; + } + + /** + * Get the format + * @return mixed + */ + public function getFormat() + { + return $this->format; + } + /** * Is sortable? * @return bool @@ -293,6 +316,9 @@ public function defaultCellRenderer($record, $column) { // other } else { + if (!is_null($this->format)) { + $value = Grid::formatRecordString($record, $this->format); + } self::renderText($value, $this->maxlen); } } diff --git a/Grid.php b/Grid.php index cc7b52e..00ec0e3 100644 --- a/Grid.php +++ b/Grid.php @@ -3,6 +3,7 @@ namespace Gridito; use Nette\ComponentModel\Container, Nette\Environment, Nette\Utils\Paginator; +use Nette\Utils\Strings; /** * Grid @@ -66,6 +67,15 @@ public function __construct(\Nette\ComponentModel\IContainer $parent = null, $na $this->paginator->setItemsPerPage($this->defaultItemsPerPage); } + public static function formatRecordString($record, $formatString) + { + return Strings::replace($formatString, '#%[^%]*%#u', + function ($m) use ($record) { + $m = Strings::trim($m[0], '%'); + return $m != '' ? $record[$m] : "%"; + }); + } + // // @@ -401,4 +411,4 @@ protected function setOptions($object, $options) } } -} \ No newline at end of file +} From 7e5f06aa3273ff6e642f5770e5b31a7d7c077331 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Sat, 2 Jul 2011 00:34:22 +0200 Subject: [PATCH 11/29] FIX: Posahany merge z testingu --- Column.php | 18 +++++++++--------- css/{gridito.css => _gridito.scss} | 0 2 files changed, 9 insertions(+), 9 deletions(-) rename css/{gridito.css => _gridito.scss} (100%) diff --git a/Column.php b/Column.php index d28de78..11de479 100644 --- a/Column.php +++ b/Column.php @@ -12,13 +12,13 @@ */ class Column extends \Nette\Application\UI\Control { - // + // - /** @var string */ - private $label; + /** @var string */ + private $label; - /** @var callback */ - private $renderer = null; + /** @var callback */ + private $renderer = null; /** @var int */ private $maxlen = null; @@ -29,11 +29,11 @@ class Column extends \Nette\Application\UI\Control /** @var bool */ private $sortable = false; - /** @var string */ - private $type = 'string'; + /** @var string */ + private $dateTimeFormat = "j.n.Y G:i"; - /** @var bool */ - private $sortable = false; + /** @var string|callable */ + private $cellClass = null; /** @var string */ private $format = null; diff --git a/css/gridito.css b/css/_gridito.scss similarity index 100% rename from css/gridito.css rename to css/_gridito.scss From 6db49224419ef0394dc2d46e1469e2eb556f7e67 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Sat, 2 Jul 2011 00:36:49 +0200 Subject: [PATCH 12/29] FIX: delete use Dibi\Fluent from NetteModel --- models/NetteModel.php | 1 - 1 file changed, 1 deletion(-) diff --git a/models/NetteModel.php b/models/NetteModel.php index 075e7ef..565197d 100644 --- a/models/NetteModel.php +++ b/models/NetteModel.php @@ -2,7 +2,6 @@ namespace Gridito; -use DibiFluent; use Nette\Database\Table\Selection; /** From efd90ba29693fc20dd8d19074db1d809943baad2 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Sat, 2 Jul 2011 14:29:56 +0200 Subject: [PATCH 13/29] Zbavena zavislost aplikovania csska na js kode. --- BaseButton.php | 24 +++++++-- css/_gridito.scss | 108 ++++++++++++++++++++++++++++++++-------- js/jquery.ui.gridito.js | 36 +------------- templates/grid.phtml | 16 +++--- 4 files changed, 118 insertions(+), 66 deletions(-) diff --git a/BaseButton.php b/BaseButton.php index f144148..fc64d91 100644 --- a/BaseButton.php +++ b/BaseButton.php @@ -239,11 +239,29 @@ public function handleClick($token, $uniqueId = null) */ protected function createButton($row = null) { + $button = Html::el('a')->href($this->getLink($row)); + $button->class[] = 'gridito-button'; + if ($this->icon && $this->showText) { + $button->class[] = 'button-icon-text'; + } elseif ($this->icon) { + $button->class[] = 'button-icon'; + $button->class[] = 'gridito-hide-text'; + $button->title($this->label); + } else { + $button->class[] = 'button-text'; + } + + if ($this->icon) { + $button->create('span')->class(array('gridito-icon', $this->icon)); + } + $button->create('span class="gridito-text"')->setText($this->label); + return $button; + return Html::el("a") ->href($this->getLink($row)) ->data("gridito-icon", $this->icon) - ->class(array("gridito-button", $this->showText ? null : "gridito-hide-text")) - ->setText($this->label); + ->class(array("gridito-button js-disabled", $this->showText ? null : "gridito-hide-text")) + ->add(Html::el('span')->setText($this->label)); } @@ -259,4 +277,4 @@ public function render($row = null) } } -} \ No newline at end of file +} diff --git a/css/_gridito.scss b/css/_gridito.scss index 6591fcd..d1340c6 100644 --- a/css/_gridito.scss +++ b/css/_gridito.scss @@ -1,3 +1,4 @@ +@import '_jquery.ui.scss'; /** * Gridito CSS */ @@ -24,43 +25,108 @@ div.gridito-flash .ui-icon { .gridito-table { border-collapse: collapse; width: 100%; + @extend .ui-widget; + @extend .ui-widget-content; + th { + padding: 0.5em; + text-align: left; + @extend .ui-widget-header; + .gridito-sorting { + float:right; + margin-left: 0.5em; + position: relative; + top: 0.2em; + .sorting-no, .sorting-asc, .sorting-desc { + @extend .ui-icon; + } + .sorting-no { + opacity: 0.5; + &:hover { + opacity: 1; + } + } + .sorting-no, .sorting-desc:hover { + @extend .ui-icon-carat-2-n-s; + } + .sorting-asc, .sorting-no:hover { + @extend .ui-icon-triangle-1-n; + } + .sorting-desc, .sorting-asc:hover { + @extend .ui-icon-triangle-1-s; + } + } + &:hover .gridito-sorting .sorting-no { + opacity: 1; + } + } + tbody tr:hover { + @extend .ui-state-hover; + } } .gridito-table th { - padding: 0.5em; - text-align: left; } .gridito-table td { padding: 0.5em; border: 1px solid #BBB; + &.highlight { + @extend .ui-state-highlight; + } +} + +.gridito-table tr { + max-height: 2ex; +} + +.gridito-button { + @extend .ui-button; + @extend .ui-widget; + @extend .ui-state-default; + @extend .ui-corner-all; + + &.button-text { + @extend .ui-button-text-only; + } + &.button-icon { + @extend .ui-button-icon-only; + } + &.button-icon-text { + @extend .ui-button-text-icon-primary; + } + &.disabled { + @extend .ui-button-disabled; + @extend .ui-state-disabled; + } + &:hover { + @extend .ui-state-hover; + } + &:active { + @extend .ui-state-active; + } + &:focus { + @extend .ui-state-focus; + } +} +span.gridito-icon { + @extend .ui-button-icon-primary; + @extend .ui-icon; +} +span.gridito-text { + @extend .ui-button-text; } .gridito-table .ui-state-hover .gridito-cell { - font-weight: normal; +font-weight: normal; } .gridito-actioncell .ui-button { - margin: 0 0.5em 0 0; +margin: 0 0.5em 0 0; } .gridito-actioncell .ui-button:first-child { - margin-left: 0; +margin-left: 0; } .gridito-actioncell { - text-align: center; -} - -/* th */ -.gridito-table th .gridito-sorting { - float:right; - margin-left: 0.5em; - position: relative; - top: 0.2em; -} -.gridito-table th .gridito-sorting .ui-icon { - opacity: 0.5 -} -.gridito-table th:hover .gridito-sorting .ui-icon, .gridito-table th .gridito-sorting .ui-icon.ui-icon-triangle-1-s, .gridito-table th .gridito-sorting .ui-icon.ui-icon-triangle-1-n { - opacity: 1 +text-align: center; } /* paginator */ .gridito-paginator { margin-top: 1em; -} \ No newline at end of file +} diff --git a/js/jquery.ui.gridito.js b/js/jquery.ui.gridito.js index f1ebe19..94ace2a 100644 --- a/js/jquery.ui.gridito.js +++ b/js/jquery.ui.gridito.js @@ -2,46 +2,14 @@ $.widget("ui.gridito", { - options: { - - }, - + options: {}, _create: function () { var _this = this; - this.table = this.element.find("table.gridito-table"); - this.table.addClass("ui-widget ui-widget-content"); - this.table.find("th").addClass("ui-widget-header"); - this.table.find("tbody tr").hover(function () { - $(this).addClass("ui-state-hover"); - }, function () { - $(this).removeClass("ui-state-hover"); - }); - - // sorting icons - function initSortingIcons(normalClass, hoverClass) { - _this.table.find("thead th ." + normalClass).hover(function () { - $(this).removeClass(normalClass).addClass(hoverClass); - }, function () { - $(this).removeClass(hoverClass).addClass(normalClass); - }); - }; - - initSortingIcons("ui-icon-carat-2-n-s", "ui-icon-triangle-1-n"); - initSortingIcons("ui-icon-triangle-1-n", "ui-icon-triangle-1-s"); - initSortingIcons("ui-icon-triangle-1-s", "ui-icon-carat-2-n-s"); - // buttons this.element.find("a.gridito-button").each(function () { var el = $(this); - el.button({ - icons: { - primary: el.attr("data-gridito-icon") - }, - text: !el.hasClass("gridito-hide-text"), - disabled: el.hasClass("disabled") - }); // window button if (el.hasClass("gridito-window-button")) { @@ -73,4 +41,4 @@ $.widget("ui.gridito", { }); -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/templates/grid.phtml b/templates/grid.phtml index 0777c1e..3a7b579 100644 --- a/templates/grid.phtml +++ b/templates/grid.phtml @@ -41,9 +41,9 @@ {block tableheadercontent} - - - + + + {$column->getLabel()} {/block} @@ -55,7 +55,7 @@ {block tablebody} - + {control $column:cell $item} @@ -76,17 +76,17 @@ {block paginator} {var $paginator = $control->getPaginator()}
- Previous + Previous {for $i = 1; $i <= $paginator->pageCount; $i++} - {$i} + {$i} {/for} - Next + Next
{/block} {/block} {/if} -{/snippet} \ No newline at end of file +{/snippet} From 9eb53ff3ce1ef0f4d61d4e395eb3f98b64b4aa97 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Thu, 7 Jul 2011 23:09:57 +0200 Subject: [PATCH 14/29] Z nejakeho divneho dovodu count() najskor vsetky data vytiahne z databazy a potom zisti ich pocet. Narozdiel od count('*'), ktory sa databazy iba spyta na pocet, co je vyrazne rychlejsie. --- models/NetteModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/NetteModel.php b/models/NetteModel.php index 075e7ef..e3d52c2 100644 --- a/models/NetteModel.php +++ b/models/NetteModel.php @@ -51,7 +51,7 @@ public function getItems() */ protected function _count() { - return $this->selection->count(); + return $this->selection->count('*'); } } From fc78b9362e2222287cc70c1952486bfcf097f111 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Mon, 11 Jul 2011 15:55:12 +0200 Subject: [PATCH 15/29] FIX: delete function body after return --- BaseButton.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/BaseButton.php b/BaseButton.php index fc64d91..bd6b525 100644 --- a/BaseButton.php +++ b/BaseButton.php @@ -256,12 +256,6 @@ protected function createButton($row = null) } $button->create('span class="gridito-text"')->setText($this->label); return $button; - - return Html::el("a") - ->href($this->getLink($row)) - ->data("gridito-icon", $this->icon) - ->class(array("gridito-button js-disabled", $this->showText ? null : "gridito-hide-text")) - ->add(Html::el('span')->setText($this->label)); } From 78ea2ad146a7e4583ed8b484a22cdf011bc58c6d Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Mon, 11 Jul 2011 15:55:33 +0200 Subject: [PATCH 16/29] Add CheckButton --- Grid.php | 15 +++++++++++++++ css/_gridito.scss | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Grid.php b/Grid.php index 00ec0e3..9922551 100644 --- a/Grid.php +++ b/Grid.php @@ -335,6 +335,21 @@ public function addButton($name, $label = null, array $options = array()) return $button; } + /** + * Add check button + * @param string button name + * @param string label + * @param array options + * @return CheckButton + */ + public function addCheckButton($name, $label = null, array $options = array()) + { + $button = new CheckButton($this["actions"], $name); + $button->setLabel($label); + $this->setOptions($button, $options); + return $button; + } + /** diff --git a/css/_gridito.scss b/css/_gridito.scss index d1340c6..b4f899c 100644 --- a/css/_gridito.scss +++ b/css/_gridito.scss @@ -99,7 +99,7 @@ div.gridito-flash .ui-icon { &:hover { @extend .ui-state-hover; } - &:active { + &:active, &.checked { @extend .ui-state-active; } &:focus { From c7cb29c7ba9a72f8821b6ad70eb9a2538ee238df Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Tue, 12 Jul 2011 13:32:31 +0200 Subject: [PATCH 17/29] FIX: missing file --- CheckButton.php | 125 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 CheckButton.php diff --git a/CheckButton.php b/CheckButton.php new file mode 100644 index 0000000..d3c781a --- /dev/null +++ b/CheckButton.php @@ -0,0 +1,125 @@ +checked = $checked; + return $this; + } + + /** + * Is button enabled + * @param mixed row + * @return bool + */ + public function isEnabled($row = null) + { + return is_bool($this->enabled) ? $this->enabled : call_user_func($this->enabled, $row); + } + + /** + * Is button checked + * @param mixed row + * @return bool + */ + public function isChecked($row = null) + { + return is_bool($this->checked) ? $this->checked : call_user_func($this->checked, $row); + } + + /** + * Set enabled + * @param bool $enabled + * @return CheckButton + */ + public function SetEnabled($enabled = true) + { + $this->enabled = $enabled; + return $this; + } + + + /** + * Is ajax? + * @return bool + */ + public function isAjax() + { + return $this->ajax; + } + + + + /** + * Set ajax mode + * @param bool ajax + * @return Button + */ + public function setAjax($ajax) + { + $this->ajax = (bool) $ajax; + return $this; + } + + + + /** + * Handle click signal + * @param string security token + * @param mixed primary key + */ + public function handleClick($token, $uniqueId = null) + { + parent::handleClick($token, $uniqueId); + + if ($this->getPresenter()->isAjax()) { + $this->getGrid()->invalidateControl(); + } else { + $this->getGrid()->redirect("this"); + } + } + + + + /** + * Create button element + * @param mixed row + * @return Nette\Web\Html + */ + protected function createButton($row = null) + { + $el = parent::createButton($row); + if (!$this->isEnabled($row)) { + $el->href(null); + } + if ($this->isChecked($row)) { + $el->class[] = 'checked'; + } + $el->class[] = $this->isAjax() ? $this->getGrid()->getAjaxClass() : null; + return $el; + } + +} From 4401422fa84fe9d717e9700b472fa5371440103a Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Wed, 13 Jul 2011 19:32:18 +0200 Subject: [PATCH 18/29] Renderer methods changed to just return rendered result, not actualy print it --- Column.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Column.php b/Column.php index 11de479..5d13c33 100644 --- a/Column.php +++ b/Column.php @@ -246,7 +246,7 @@ public function getGrid() { public static function renderBoolean($value) { $icon = $value ? "check" : "closethick"; - echo ''; + return ''; } @@ -258,7 +258,7 @@ public static function renderBoolean($value) */ public static function renderDateTime($value, $format) { - echo $value->format($format); + return $value->format($format); } /** @@ -269,9 +269,9 @@ public static function renderDateTime($value, $format) public static function renderText($text, $maxlen) { if (is_null($maxlen) || Strings::length($text) < $maxlen) { - echo htmlspecialchars($text, ENT_NOQUOTES); + return htmlspecialchars($text, ENT_NOQUOTES); } else { - echo Html::el('span')->title($text) + return Html::el('span')->title($text) ->setText(Strings::truncate($text, $maxlen)); } } @@ -280,14 +280,15 @@ public static function renderText($text, $maxlen) * Render the email address, takes care of length * @param string $email email address * @param int $maxlen maximum length of text + * @return mixed */ public static function renderEmail($email, $maxlen) { $el = Html::el('a')->href('mailto:' . $email); if (is_null($maxlen) || Strings::length($email) < $maxlen) { - echo $el->setText($email); + return $el->setText($email); } else { - echo $el->title($email) + return $el->title($email) ->setText(Strings::truncate($email, $maxlen)); } } @@ -297,6 +298,7 @@ public static function renderEmail($email, $maxlen) * Default cell renderer * @param mixed $record * @param Column $column + * @return mixed */ public function defaultCellRenderer($record, $column) { $name = $column->getName(); @@ -304,22 +306,22 @@ public function defaultCellRenderer($record, $column) { // boolean if (in_array($this->type, array('bool', 'boolean')) || is_bool($value)) { - self::renderBoolean($value); + return self::renderBoolean($value); // date } elseif ($value instanceof \DateTime) { - self::renderDateTime($value, $this->dateTimeFormat); + return self::renderDateTime($value, $this->dateTimeFormat); // email } elseif ($this->type == 'email') { - self::renderEmail($value, $this->maxlen); + return self::renderEmail($value, $this->maxlen); // other } else { if (!is_null($this->format)) { $value = Grid::formatRecordString($record, $this->format); } - self::renderText($value, $this->maxlen); + return self::renderText($value, $this->maxlen); } } @@ -330,7 +332,7 @@ public function defaultCellRenderer($record, $column) { * @param mixed record */ public function renderCell($record) { - call_user_func($this->renderer ?: array($this, "defaultCellRenderer"), $record, $this); + echo call_user_func($this->renderer ?: array($this, "defaultCellRenderer"), $record, $this); } } From 5b5d343f5ebd63ebc7e6b9c4f779b5e97bdd940d Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Thu, 14 Jul 2011 01:07:35 +0200 Subject: [PATCH 19/29] add editable ability --- Column.php | 25 +++++++++++++++++++++++-- Grid.php | 17 +++++++++++++++++ js/jquery.ui.gridito.js | 30 ++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/Column.php b/Column.php index 5d13c33..4b6ba00 100644 --- a/Column.php +++ b/Column.php @@ -29,6 +29,9 @@ class Column extends \Nette\Application\UI\Control /** @var bool */ private $sortable = false; + /** @var bool */ + private $editable = false; + /** @var string */ private $dateTimeFormat = "j.n.Y G:i"; @@ -189,6 +192,16 @@ public function setSortable($sortable) { return $this; } + /** + * Set editable + * @param bool editable + * @return Column + */ + public function setEditable($editable) { + $this->editable = $editable; + return $this; + } + /** @@ -269,7 +282,7 @@ public static function renderDateTime($value, $format) public static function renderText($text, $maxlen) { if (is_null($maxlen) || Strings::length($text) < $maxlen) { - return htmlspecialchars($text, ENT_NOQUOTES); + return Html::el('span')->setText($text); } else { return Html::el('span')->title($text) ->setText(Strings::truncate($text, $maxlen)); @@ -321,7 +334,15 @@ public function defaultCellRenderer($record, $column) { if (!is_null($this->format)) { $value = Grid::formatRecordString($record, $this->format); } - return self::renderText($value, $this->maxlen); + $return = self::renderText($value, $this->maxlen); + if ($this->editable) { + $return->class[] = 'editable'; + $return->data['value'] = $value; + $return->data['url'] = $this->getGrid()->link('edit!'); + $return->data['name'] = $this->getName(); + $return->data['id'] = $this->getGrid()->getModel()->getUniqueId($record); + } + return $return; } } diff --git a/Grid.php b/Grid.php index 9922551..0da56eb 100644 --- a/Grid.php +++ b/Grid.php @@ -51,6 +51,9 @@ class Grid extends \Nette\Application\UI\Control /** @var string|callable */ private $rowClass = null; + /** @var callable */ + private $editHandler = null; + //
// @@ -260,6 +263,20 @@ public function handleChangePage($page) } } + public function setEditHandler($callback) + { + $this->editHandler = $callback; + } + + public function handleEdit() + { + if ($this->presenter->isAjax()) { + $post = \Nette\Environment::getHttpRequest()->getPost(); + call_user_func($this->editHandler, $post); + $this->invalidateControl(); + } + } + public function handleSort($sortColumn, $sortType) diff --git a/js/jquery.ui.gridito.js b/js/jquery.ui.gridito.js index 94ace2a..2adec64 100644 --- a/js/jquery.ui.gridito.js +++ b/js/jquery.ui.gridito.js @@ -42,3 +42,33 @@ $.widget("ui.gridito", { }); })(jQuery); + + +$.extend({ + stopedit : function(who) { + var span = who.data('span'); + span.text(who.val()); + span.attr('data-value', who.val()); + var data = new Object(); + data[span.attr('data-name')] = who.val(); + data['id'] = span.attr('data-id'); + $.post(span.attr('data-url'), data); + who.replaceWith(span); + }, + startedit: function(who) { + var input = $(''); + input.data('span', who); + input.val(who.attr('data-value')); + input.addClass('editable'); + input.blur(function(){$.stopedit($(this))}); + input.keyup(function(event) + {if (event.keyCode == 27) {input.blur()}} + ); + who.replaceWith(input); + input.focus(); + }, +}); + +$('span.editable').live('click', function(event) { + $.startedit($(this)); +}); From 7edbbdd5115a1ec5db72abf09c00d44148ddd002 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Thu, 14 Jul 2011 11:59:33 +0200 Subject: [PATCH 20/29] add IsEditable function --- Column.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Column.php b/Column.php index 4b6ba00..e39a006 100644 --- a/Column.php +++ b/Column.php @@ -202,6 +202,13 @@ public function setEditable($editable) { return $this; } + /** + * Is editable? + * @return bool + */ + public function isEditable() { + return $this->editable; + } /** From a199426ba4c84541a4d1eb59b996479c3e8301cd Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Thu, 14 Jul 2011 11:59:47 +0200 Subject: [PATCH 21/29] fluent interface --- Grid.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Grid.php b/Grid.php index 0da56eb..edaefe4 100644 --- a/Grid.php +++ b/Grid.php @@ -263,11 +263,20 @@ public function handleChangePage($page) } } + /** + * Set edit handler + * @param callback handler + * @return Grid + */ public function setEditHandler($callback) { $this->editHandler = $callback; + return $this; } + /** + * Handle edit + */ public function handleEdit() { if ($this->presenter->isAjax()) { From 9f372ddec4402e39e65317a26d7e036c012be850 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Thu, 14 Jul 2011 12:35:44 +0200 Subject: [PATCH 22/29] Handler provided by user should also care about invalidation. It is not always neccesary to invalidate whole control --- Grid.php | 8 +++++++- templates/grid.phtml | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Grid.php b/Grid.php index edaefe4..c8c5d06 100644 --- a/Grid.php +++ b/Grid.php @@ -281,8 +281,14 @@ public function handleEdit() { if ($this->presenter->isAjax()) { $post = \Nette\Environment::getHttpRequest()->getPost(); + foreach ($post as $column => $value) { + if ($column == 'id' || + $this['columns']->getComponent($column)->isEditable()) { + continue; + } + throw new \Nette\Application\ForbiddenRequestException("Column $column is not editable"); + } call_user_func($this->editHandler, $post); - $this->invalidateControl(); } } diff --git a/templates/grid.phtml b/templates/grid.phtml index 3a7b579..c555f35 100644 --- a/templates/grid.phtml +++ b/templates/grid.phtml @@ -13,12 +13,12 @@ {block grid} {* flash messages *} - {block flashes} + {snippet flashes}
{$flash->message}
- {/block} + {/snippet} {* top toolbar *} {block toptoolbar} From 1429a7fc174c39b506de845defecb57417e089f2 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Sat, 16 Jul 2011 09:21:10 +0200 Subject: [PATCH 23/29] enabled property should exists for all buttons --- BaseButton.php | 31 ++++++++++++++++++++++++++++++- CheckButton.php | 26 -------------------------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/BaseButton.php b/BaseButton.php index bd6b525..d22d875 100644 --- a/BaseButton.php +++ b/BaseButton.php @@ -29,8 +29,32 @@ abstract class BaseButton extends \Nette\Application\UI\PresenterComponent /** @var bool */ private $showText = true; + + /** @var string */ + private $enabled = true; + /** + * Is button enabled + * @param mixed row + * @return bool + */ + public function isEnabled($row = null) + { + return is_bool($this->enabled) ? $this->enabled : call_user_func($this->enabled, $row); + } + + /** + * Set enabled + * @param bool $enabled + * @return CheckButton + */ + public function SetEnabled($enabled = true) + { + $this->enabled = $enabled; + return $this; + } + /** * Get label @@ -239,7 +263,12 @@ public function handleClick($token, $uniqueId = null) */ protected function createButton($row = null) { - $button = Html::el('a')->href($this->getLink($row)); + $button = Html::el('a'); + if ($this->isEnabled()) { + $button->href($this->getLink($row)); + } else { + $button->class[] = 'disabled'; + } $button->class[] = 'gridito-button'; if ($this->icon && $this->showText) { $button->class[] = 'button-icon-text'; diff --git a/CheckButton.php b/CheckButton.php index d3c781a..52f6bc8 100644 --- a/CheckButton.php +++ b/CheckButton.php @@ -16,9 +16,6 @@ class CheckButton extends BaseButton /** @var string */ private $checked = false; - /** @var string */ - private $enabled = true; - /** * Set checked * @param bool $checked @@ -30,15 +27,6 @@ public function SetChecked($checked = true) return $this; } - /** - * Is button enabled - * @param mixed row - * @return bool - */ - public function isEnabled($row = null) - { - return is_bool($this->enabled) ? $this->enabled : call_user_func($this->enabled, $row); - } /** * Is button checked @@ -50,17 +38,6 @@ public function isChecked($row = null) return is_bool($this->checked) ? $this->checked : call_user_func($this->checked, $row); } - /** - * Set enabled - * @param bool $enabled - * @return CheckButton - */ - public function SetEnabled($enabled = true) - { - $this->enabled = $enabled; - return $this; - } - /** * Is ajax? @@ -112,9 +89,6 @@ public function handleClick($token, $uniqueId = null) protected function createButton($row = null) { $el = parent::createButton($row); - if (!$this->isEnabled($row)) { - $el->href(null); - } if ($this->isChecked($row)) { $el->class[] = 'checked'; } From 26b955208decd5cf7eafaf0a5e9f6afae3bc136c Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Sat, 16 Jul 2011 15:47:40 +0200 Subject: [PATCH 24/29] css min height for editable --- css/_gridito.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/css/_gridito.scss b/css/_gridito.scss index b4f899c..fbb6459 100644 --- a/css/_gridito.scss +++ b/css/_gridito.scss @@ -113,6 +113,12 @@ span.gridito-icon { span.gridito-text { @extend .ui-button-text; } +span.editable { + width: 100%; + min-height: 30px; + margin: 0px; + display: block; +} .gridito-table .ui-state-hover .gridito-cell { font-weight: normal; } From 5c17a315c9b3cd581983d02c33d22adf1accc9eb Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Sat, 16 Jul 2011 16:01:55 +0200 Subject: [PATCH 25/29] fix: css, editable text should be centered --- css/_gridito.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/css/_gridito.scss b/css/_gridito.scss index fbb6459..54309a3 100644 --- a/css/_gridito.scss +++ b/css/_gridito.scss @@ -116,6 +116,7 @@ span.gridito-text { span.editable { width: 100%; min-height: 30px; + line-height: 30px; margin: 0px; display: block; } From 1fdf97cc0eccdc9d998f70952d9ad0cb07d9e508 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Sun, 17 Jul 2011 15:18:37 +0200 Subject: [PATCH 26/29] fix: missing row parameter for isEnabled --- BaseButton.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BaseButton.php b/BaseButton.php index d22d875..842c032 100644 --- a/BaseButton.php +++ b/BaseButton.php @@ -264,7 +264,7 @@ public function handleClick($token, $uniqueId = null) protected function createButton($row = null) { $button = Html::el('a'); - if ($this->isEnabled()) { + if ($this->isEnabled($row)) { $button->href($this->getLink($row)); } else { $button->class[] = 'disabled'; From 4da542a292773a5c30e3f30281faeea85d6c83d5 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Thu, 29 Sep 2011 22:13:53 +0200 Subject: [PATCH 27/29] advanced column features --- Column.php | 31 ++++++++++++++++++++----------- js/jquery.ui.gridito.js | 10 +++++++++- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/Column.php b/Column.php index e39a006..731c32e 100644 --- a/Column.php +++ b/Column.php @@ -266,7 +266,11 @@ public function getGrid() { public static function renderBoolean($value) { $icon = $value ? "check" : "closethick"; - return ''; + $el = Html::el('span'); + $el->data['value'] = $value ? '1' : '0'; + $el->data['type'] = 'bool'; + $el->add(Html::el("span class='ui-icon ui-icon-$icon'")); + return $el; } @@ -341,15 +345,7 @@ public function defaultCellRenderer($record, $column) { if (!is_null($this->format)) { $value = Grid::formatRecordString($record, $this->format); } - $return = self::renderText($value, $this->maxlen); - if ($this->editable) { - $return->class[] = 'editable'; - $return->data['value'] = $value; - $return->data['url'] = $this->getGrid()->link('edit!'); - $return->data['name'] = $this->getName(); - $return->data['id'] = $this->getGrid()->getModel()->getUniqueId($record); - } - return $return; + return self::renderText($value, $this->maxlen); } } @@ -360,7 +356,20 @@ public function defaultCellRenderer($record, $column) { * @param mixed record */ public function renderCell($record) { - echo call_user_func($this->renderer ?: array($this, "defaultCellRenderer"), $record, $this); + $column = call_user_func($this->renderer ?: array($this, "defaultCellRenderer"), $record, $this); + if (!($column instanceOf Html)) { + $column = Html::el('span')->setText($column); + } + if ($this->editable) { + $column->class[] = 'editable'; + if (!isset($column->data['value'])) { + $column->data['value'] = $column->getText(); + } + $column->data['url'] = $this->getGrid()->link('edit!'); + $column->data['name'] = $this->getName(); + $column->data['id'] = $this->getGrid()->getModel()->getUniqueId($record); + } + echo $column; } } diff --git a/js/jquery.ui.gridito.js b/js/jquery.ui.gridito.js index 2adec64..2938fd7 100644 --- a/js/jquery.ui.gridito.js +++ b/js/jquery.ui.gridito.js @@ -21,7 +21,8 @@ $.widget("ui.gridito", { win.attr("title", $(this).attr("data-gridito-window-title")); win.load(this.href, function () { win.dialog({ - modal: true + modal: true, + width: 600 }); win.find("input:first").focus(); }); @@ -56,6 +57,13 @@ $.extend({ who.replaceWith(span); }, startedit: function(who) { + if (who.attr('data-type') == 'bool') { + var data = {}; + data[who.attr('data-name')] = who.attr('data-value') === '1' ? '0' : '1'; + data['id'] = who.attr('data-id'); + $.post(who.attr('data-url'), data); + return; + } var input = $(''); input.data('span', who); input.val(who.attr('data-value')); From 3b973d4bdd4cd2c628d8293631ac4248068b2980 Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Sun, 19 Feb 2012 14:54:37 +0100 Subject: [PATCH 28/29] add antispam protection --- Column.php | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Column.php b/Column.php index 731c32e..4a04cbf 100644 --- a/Column.php +++ b/Column.php @@ -41,6 +41,9 @@ class Column extends \Nette\Application\UI\Control /** @var string */ private $format = null; + /** @var string */ + public $spamProtection = null; + //
// @@ -308,11 +311,23 @@ public static function renderText($text, $maxlen) */ public static function renderEmail($email, $maxlen) { - $el = Html::el('a')->href('mailto:' . $email); + if (is_null($this->spamProtection) { + $href = $email; + $text = htmlspecialchars($email, ENT_QUOTES); + } else { + $href = str_replace('@', $this->spamProtection, $email); + $text = str_replace('@', + '@' + . $this->spamProtection + . '', + htmlspecialchars($email, ENT_QUOTES) + ); + } + $el = Html::el('a')->href('mailto:' . $href); if (is_null($maxlen) || Strings::length($email) < $maxlen) { - return $el->setText($email); + return $el->setHtml($text); } else { - return $el->title($email) + return $el->title($href) ->setText(Strings::truncate($email, $maxlen)); } } From 075345ea44392c3903b07d9d22360c20d1c80f0d Mon Sep 17 00:00:00 2001 From: Samuel Hapak Date: Sun, 19 Feb 2012 15:01:16 +0100 Subject: [PATCH 29/29] use visualpaginator instead of coding own --- Grid.php | 37 +++++-------------------------------- templates/grid.phtml | 32 ++++++++++---------------------- 2 files changed, 15 insertions(+), 54 deletions(-) diff --git a/Grid.php b/Grid.php index c8c5d06..ed11a99 100644 --- a/Grid.php +++ b/Grid.php @@ -24,12 +24,6 @@ class Grid extends \Nette\Application\UI\Control /** @var int */ private $defaultItemsPerPage = 20; - /** - * @var int - * @persistent - */ - public $page = 1; - /** * @var string * @persistent @@ -54,9 +48,6 @@ class Grid extends \Nette\Application\UI\Control /** @var callable */ private $editHandler = null; - // - - // public function __construct(\Nette\ComponentModel\IContainer $parent = null, $name = null) { @@ -65,8 +56,10 @@ public function __construct(\Nette\ComponentModel\IContainer $parent = null, $na $this->addComponent(new Container, "toolbar"); $this->addComponent(new Container, "actions"); $this->addComponent(new Container, "columns"); + $this->addComponent(new \VisualPaginator, 'visualPaginator'); + $this['visualPaginator']->onChange[] = callback($this, 'invalidateControl'); - $this->paginator = new Paginator; + $this->paginator = $this['visualPaginator']->getPaginator(); $this->paginator->setItemsPerPage($this->defaultItemsPerPage); } @@ -79,9 +72,6 @@ function ($m) use ($record) { }); } - // - - // /** * @param bool highlight ordered column @@ -248,20 +238,6 @@ public function hasActions() return count($this["actions"]->getComponents()) > 0; } - // - - // - - /** - * Handle change page signal - * @param int page - */ - public function handleChangePage($page) - { - if ($this->presenter->isAjax()) { - $this->invalidateControl(); - } - } /** * Set edit handler @@ -299,11 +275,9 @@ public function handleSort($sortColumn, $sortType) if ($this->presenter->isAjax()) { $this->invalidateControl(); } + $this->paginator->page = 1; } - // - - // /** * Create template @@ -321,18 +295,17 @@ protected function createTemplate($class = null) */ public function render() { - $this->paginator->setPage($this->page); $this->model->setLimit($this->paginator->getLength()); $this->model->setOffset($this->paginator->getOffset()); if ($this->sortColumn && $this["columns"]->getComponent($this->sortColumn)->isSortable()) { $this->model->setSorting($this->sortColumn, $this->sortType); } + $this['visualPaginator']->setClass(array('paginator', $this->ajaxClass)); $this->template->render(); } - // /** diff --git a/templates/grid.phtml b/templates/grid.phtml index c555f35..e886b3b 100644 --- a/templates/grid.phtml +++ b/templates/grid.phtml @@ -14,8 +14,8 @@ {* flash messages *} {snippet flashes} -
- +
+ {$flash->message}
{/snippet} @@ -38,14 +38,13 @@ {block tableheader} + {var $nextSort = array('null' => 'asc', 'asc' => 'desc', 'desc' => 'null')} {block tableheadercontent} - - - - - - {$column->getLabel()} + {var $next = $nextSort[$column->sorting]} + + {$column->label} + {/block} @@ -54,8 +53,8 @@ {block tablebody} - - + + {control $column:cell $item} @@ -73,18 +72,7 @@ {/block} - {block paginator} - {var $paginator = $control->getPaginator()} -
- Previous - - {for $i = 1; $i <= $paginator->pageCount; $i++} - {$i} - {/for} - - Next -
- {/block} + {control visualPaginator} {/block} {/if}