PageRenderTime 59ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/www/app/AdminModule/components/Datagrid/DataGrid.php

https://github.com/bazo/Mokuji
PHP | 1205 lines | 564 code | 253 blank | 388 comment | 99 complexity | 2bcc4fa0fcfe25280c1bc5ffc32a408d MD5 | raw file
Possible License(s): BSD-3-Clause, MIT
  1. <?php
  2. /**
  3. * This source file is subject to the "New BSD License".
  4. *
  5. * For more information please see http://nettephp.com
  6. *
  7. * @author Roman Sklenář
  8. * @copyright Copyright (c) 2009 Roman Sklenář (http://romansklenar.cz)
  9. * @license New BSD License
  10. * @link http://nettephp.com/extras/datagrid
  11. */
  12. /**
  13. * A data bound list control that displays the items from data source in a table.
  14. * The DataGrid control allows you to select, sort, and manage these items.
  15. *
  16. * <code>
  17. * $grid = new DataGrid;
  18. * $grid->bindDataTable($model->findAll($model->table)->toDataSource());
  19. *
  20. * $grid->addColumn('column', 'Column caption')->addFilter();
  21. * $grid['column']->getCellPrototype()->style('text-align: center');
  22. *
  23. * $grid->addActionColumn('Actions');
  24. * $grid->addAction('Edit', 'Item:edit');
  25. *
  26. * $presenter->addComponent($grid, 'componentName');
  27. * </code>
  28. *
  29. * @author Roman Sklenář
  30. * @copyright Copyright (c) 2009 Roman Sklenář (http://romansklenar.cz)
  31. * @license New BSD License
  32. * @example http://nettephp.com/extras/datagrid
  33. * @package Nette\Extras\DataGrid
  34. * @version $Id$
  35. */
  36. class DataGrid extends Control implements ArrayAccess, INamingContainer
  37. {
  38. /** @persistent int */
  39. public $page = 1;
  40. /** @persistent string */
  41. public $order = '';
  42. /** @persistent string */
  43. public $filters = '';
  44. /** @persistent int */
  45. public $itemsPerPage = 15;
  46. /** @var array */
  47. public $displayedItems = array('all', 5, 10, 15, 20, 50, 100);
  48. /** @var bool multi column order */
  49. public $multiOrder = TRUE;
  50. /** @var bool disables ordering for all columns */
  51. public $disableOrder = FALSE;
  52. /** @var string */
  53. public $defaultOrder;
  54. /** @var string */
  55. public $defaultFilters;
  56. /** @var array */
  57. public $operations = array();
  58. /** @var array of valid callback(s) */
  59. protected $onOperationSubmit;
  60. /** @var bool can datagrid save his state into session? */
  61. public $rememberState = FALSE;
  62. /** @var int|string session timeout (default: until is browser closed) */
  63. public $timeout = 0;
  64. /** @var IDataGridRenderer */
  65. protected $renderer;
  66. /** @var DibiDataSource */
  67. protected $dataSource;
  68. /** @var Paginator */
  69. protected $paginator;
  70. /** @var string */
  71. public $keyName;
  72. /** @var string */
  73. protected $receivedSignal;
  74. /** @var ActionColumn */
  75. protected $currentActionColumn;
  76. /** @var bool was method render() called? */
  77. protected $wasRendered = FALSE;
  78. /** @var ITranslator */
  79. protected $translator;
  80. /**
  81. * Data grid constructor.
  82. * @return void
  83. */
  84. public function __construct()
  85. {
  86. parent::__construct(); // intentionally without any arguments (because of session loadState)
  87. $this->paginator = new Paginator;
  88. $this->paginator->itemsPerPage = 10;
  89. $session = $this->getSession();
  90. if (!$session->isStarted()) {
  91. $session->start();
  92. }
  93. }
  94. /**
  95. * Binds data source to data grid.
  96. * @param DibiDataSource
  97. * @throws DibiException
  98. * @return void
  99. */
  100. public function bindDataTable(DibiDataSource $dataSource)
  101. {
  102. $this->dataSource = $dataSource;
  103. $this->paginator->itemCount = count($dataSource);
  104. }
  105. /**
  106. * Getter / property method.
  107. * @return DibiDataSource
  108. */
  109. public function getDataSource()
  110. {
  111. return $this->dataSource;
  112. }
  113. /********************* public getters and setters *********************/
  114. /**
  115. * Getter / property method.
  116. * Generates list of pages used for visual control. Use for your custom paginator rendering.
  117. * @return array
  118. */
  119. public function getSteps($count = 15)
  120. {
  121. // paginator steps
  122. $arr = range(max($this->paginator->firstPage, $this->page - 3), min($this->paginator->lastPage, $this->page + 3));
  123. $quotient = ($this->paginator->pageCount - 1) / $count;
  124. for ($i = 0; $i <= $count; $i++) {
  125. $arr[] = round($quotient * $i) + $this->paginator->firstPage;
  126. }
  127. sort($arr);
  128. return array_values(array_unique($arr));
  129. }
  130. /**
  131. * Getter / property method.
  132. * @return Paginator
  133. */
  134. public function getPaginator()
  135. {
  136. return $this->paginator;
  137. }
  138. /**
  139. * Setter / property method.
  140. * @param mixed callback(s) to handler(s) which is called after data grid form operation is submited.
  141. * @return void
  142. */
  143. public function setOnOperationSubmit($callback)
  144. {
  145. if (!is_array($this->onOperationSubmit)) {
  146. $this->onOperationSubmit = array();
  147. }
  148. $this->onOperationSubmit[] = $callback;
  149. }
  150. /**
  151. * Getter / property method.
  152. * @return array
  153. */
  154. public function getOnOperationSubmit()
  155. {
  156. return $this->onOperationSubmit;
  157. }
  158. /********************* Iterators getters *********************/
  159. /**
  160. * Iterates over datagrid rows.
  161. * @throws InvalidStateException
  162. * @return DibiResultIterator
  163. */
  164. public function getRows()
  165. {
  166. if (!$this->dataSource instanceof DibiDataSource) {
  167. throw new InvalidStateException("Data source has not been set or has invalid data type. You must set data source before you want get rows.");
  168. }
  169. return $this->dataSource->getIterator();
  170. }
  171. /**
  172. * Iterates over datagrid columns.
  173. * @param string
  174. * @throws InvalidArgumentException
  175. * @return ArrayIterator
  176. */
  177. public function getColumns($type = 'IDataGridColumn')
  178. {
  179. $columns = new ArrayObject();
  180. foreach ($this->getComponents(FALSE, $type) as $column) {
  181. $columns->append($column);
  182. }
  183. return $columns->getIterator();
  184. }
  185. /**
  186. * Iterates over datagrid filters.
  187. * @param string
  188. * @throws InvalidArgumentException
  189. * @return ArrayIterator
  190. */
  191. public function getFilters($type = 'IDataGridColumnFilter')
  192. {
  193. $filters = new ArrayObject();
  194. foreach ($this->getColumns() as $column) {
  195. if ($column->hasFilter()) {
  196. $filter = $column->getFilter();
  197. if ($filter instanceof $type) {
  198. $filters->append($column->getFilter());
  199. }
  200. }
  201. }
  202. return $filters->getIterator();
  203. }
  204. /**
  205. * TODO: throw new DeprecatedException
  206. * Iterates over all datagrid actions.
  207. * @param string
  208. * @throws InvalidArgumentException
  209. * @return ArrayIterator
  210. */
  211. public function getActions($type = 'IDataGridAction')
  212. {
  213. $actions = new ArrayObject();
  214. foreach ($this->getColumns('ActionColumn') as $column) {
  215. if ($column->hasAction()) {
  216. foreach ($column->getActions() as $action) {
  217. if ($action instanceof $type) {
  218. $actions->append($action);
  219. }
  220. }
  221. }
  222. }
  223. return $actions->getIterator();
  224. }
  225. /********************* general data grid behavior *********************/
  226. /**
  227. * Does data grid has any row?
  228. * @return bool
  229. */
  230. public function hasRows()
  231. {
  232. return count($this->getRows()) > 0;
  233. }
  234. /**
  235. * Does data grid has any column?
  236. * @param string
  237. * @return bool
  238. */
  239. public function hasColumns($type = NULL)
  240. {
  241. return count($type == NULL ? $this->getColumns() : $this->getColumns($type)) > 0;
  242. }
  243. /**
  244. * Does any of datagrid columns has a filter?
  245. * @param string
  246. * @return bool
  247. */
  248. public function hasFilters($type = NULL)
  249. {
  250. return count($type == NULL ? $this->getFilters() : $this->getFilters($type)) > 0;
  251. }
  252. /**
  253. * Does datagrid has any action?
  254. * @param string
  255. * @return bool
  256. */
  257. public function hasActions($type = NULL)
  258. {
  259. return count($type == NULL ? $this->getActions() : $this->getActions($type)) > 0;
  260. }
  261. /**
  262. * Does datagrid has any operation?
  263. * @return bool
  264. */
  265. public function hasOperations()
  266. {
  267. return count($this->operations) > 0;
  268. }
  269. /********************* component's state *********************/
  270. /**
  271. * Loads params
  272. * @param array
  273. * @return void
  274. */
  275. public function loadState(array $params)
  276. {
  277. if ($this->rememberState) {
  278. $session = $this->getStateSession();
  279. if (!isset($session->currentState)) {
  280. $session->currentState = $session->initState;
  281. }
  282. if (isset($session->currentState)) {
  283. $cs = $session->currentState;
  284. $is = $session->initState;
  285. foreach ($cs as $key => $value) {
  286. if ($cs[$key] != $is[$key]) {
  287. // additional input validation
  288. switch ($key) {
  289. case 'page': $value = ($value > 0 ? $value : 1); break;
  290. case 'order': break;
  291. case 'filters': break;
  292. case 'itemsPerPage': break;
  293. }
  294. $params[$key] = $value;
  295. }
  296. }
  297. }
  298. }
  299. parent::loadState($params);
  300. }
  301. /**
  302. * Save params
  303. * @param array
  304. * @return void
  305. */
  306. public function saveState(array & $params)
  307. {
  308. parent::saveState($params);
  309. if ($this->rememberState) {
  310. $session = $this->getStateSession();
  311. // backup component's state
  312. if (!isset($session->initState)) {
  313. $session->initState = array(
  314. 'page' => $this->page,
  315. 'order' => $this->order,
  316. 'filters' => $this->filters,
  317. 'itemsPerPage' => $this->itemsPerPage,
  318. );
  319. }
  320. // save component's state into session
  321. $session->currentState = $params;
  322. $session->setExpiration($this->timeout);
  323. }
  324. }
  325. /**
  326. * Restores component's state.
  327. * @param string
  328. * @return void
  329. */
  330. public function restoreState()
  331. {
  332. $session = $this->getStateSession();
  333. // restore components's init state
  334. if (isset($session->initState)) {
  335. $is = $session->initState;
  336. $this->page = $is['page'];
  337. $this->order = $is['order'];
  338. $this->filters = $is['filters'];
  339. $this->itemsPerPage = $is['itemsPerPage'];
  340. }
  341. $session->remove();
  342. }
  343. /**
  344. * Returns array of classes persistent parameters.
  345. * @param string class name
  346. * @return array
  347. */
  348. public static function getPersistentParams()
  349. {
  350. return array('page', 'order', 'filters', 'itemsPerPage');
  351. }
  352. /********************* signal handlers ********************/
  353. /**
  354. * Changes page number.
  355. * @param int
  356. * @return void
  357. */
  358. public function handlePage($goto)
  359. {
  360. $this->page = ($goto > 0 ? $goto : 1);
  361. $this->invalidateControl();
  362. if (!$this->presenter->isAjax()) $this->redirect('this');
  363. }
  364. /**
  365. * Changes column sorting order.
  366. * @param string
  367. * @param string
  368. * @return void
  369. */
  370. public function handleOrder($by, $dir)
  371. {
  372. // default ordering
  373. if (empty($this->order) && !empty($this->defaultOrder)) {
  374. parse_str($this->defaultOrder, $list);
  375. if (isset($list[$by])) $this->order = $this->defaultOrder;
  376. unset($list);
  377. }
  378. parse_str($this->order, $list);
  379. if ($dir == NULL) {
  380. if (!isset($list[$by])) {
  381. if (!$this->multiOrder) $list = array();
  382. $list[$by] = 'a';
  383. } elseif ($list[$by] === 'd') {
  384. if ($this->multiOrder) unset($list[$by]);
  385. else $list[$by] = 'a';
  386. } else {
  387. $list[$by] = 'd';
  388. }
  389. } else {
  390. if (!$this->multiOrder) $list = array();
  391. $list[$by] = $dir;
  392. }
  393. $this->order = http_build_query($list, '', '&');
  394. $this->invalidateControl();
  395. if (!$this->presenter->isAjax()) $this->redirect('this');
  396. }
  397. /**
  398. * Prepare filtering.
  399. * @param string
  400. * @return void
  401. */
  402. public function handleFilter($by)
  403. {
  404. $filters = array();
  405. foreach ($by as $key => $value) {
  406. if ($value !== '') $filters[$key] = $value;
  407. }
  408. $this->filters = http_build_query($filters, '', '&');
  409. $this->invalidateControl();
  410. if (!$this->presenter->isAjax()) $this->redirect('this');
  411. }
  412. /**
  413. * Change number of displayed items.
  414. * @param string
  415. * @return void
  416. */
  417. public function handleItems($value)
  418. {
  419. if ($value < 0) {
  420. throw new InvalidArgumentException("Parametr must be non-negative number, '$value' given.");
  421. }
  422. $this->itemsPerPage = $value;
  423. $this->invalidateControl();
  424. if (!$this->presenter->isAjax()) $this->redirect('this');
  425. }
  426. /**
  427. * Change component's state.
  428. * @param string
  429. * @return void
  430. */
  431. public function handleReset()
  432. {
  433. $this->restoreState();
  434. $this->invalidateControl();
  435. if (!$this->presenter->isAjax()) $this->redirect('this');
  436. }
  437. /********************* submit handlers *********************/
  438. /**
  439. * Data grid form submit handler.
  440. * @param AppForm
  441. * @return void
  442. */
  443. public function formSubmitHandler(AppForm $form)
  444. {
  445. $this->receivedSignal = 'submit';
  446. // was form submitted?
  447. if ($form->isSubmitted()) {
  448. $values = $form->getValues();
  449. if ($form['filterSubmit']->isSubmittedBy()) {
  450. $this->handleFilter($values['filters']);
  451. } elseif ($form['pageSubmit']->isSubmittedBy()) {
  452. $this->handlePage($values['page']);
  453. } elseif ($form['itemsSubmit']->isSubmittedBy()) {
  454. $this->handleItems($values['items']);
  455. } elseif ($form['resetSubmit']->isSubmittedBy()) {
  456. $this->handleReset();
  457. } elseif ($form['operationSubmit']->isSubmittedBy()) {
  458. if (!is_array($this->onOperationSubmit)) {
  459. throw new InvalidStateException('No user defined handler for operations; assign valid callback to operations handler into DataGrid::$operationsHandler variable.');
  460. }
  461. } else {
  462. throw new InvalidStateException("Unknown submit button.");
  463. }
  464. }
  465. if (!$this->presenter->isAjax()) $this->presenter->redirect('this');
  466. }
  467. /********************* applycators (call before rendering only) *********************/
  468. /**
  469. * Aplycators caller - filters data grid items.
  470. * @return void
  471. */
  472. protected function filterItems()
  473. {
  474. // must be in this order
  475. $this->applyDefaultFiltering();
  476. $this->applyDefaultSorting();
  477. $this->applyItems();
  478. $this->applyFiltering();
  479. $this->applySorting();
  480. $this->applyPaging();
  481. }
  482. /**
  483. * Applies default sorting on data grid.
  484. * @return void
  485. */
  486. protected function applyDefaultSorting()
  487. {
  488. if (empty($this->order) && !empty($this->defaultOrder)) {
  489. $this->order = $this->defaultOrder;
  490. }
  491. }
  492. /**
  493. * Applies default filtering on data grid.
  494. * @return void
  495. */
  496. protected function applyDefaultFiltering()
  497. {
  498. if (empty($this->filters) && !empty($this->defaultFilters)) {
  499. $this->filters = $this->defaultFilters;
  500. }
  501. }
  502. /**
  503. * Applies paging on data grid.
  504. * @return void
  505. */
  506. protected function applyPaging()
  507. {
  508. $this->paginator->page = $this->page;
  509. $this->paginator->itemCount = count($this->dataSource);
  510. if ($this->wasRendered && $this->paginator->itemCount < 1 && !empty($this->filters)) {
  511. // NOTE: don't use flash messages (because you can't - header already sent)
  512. $this->getTemplate()->flashes[] = (object) array(
  513. 'message' => $this->translate("Used filters did not match any items."),
  514. 'type' => 'info',
  515. );
  516. }
  517. $this->dataSource->applyLimit($this->paginator->length, $this->paginator->offset);
  518. }
  519. /**
  520. * Applies sorting on data grid.
  521. * @return void
  522. */
  523. protected function applySorting()
  524. {
  525. $i = 1;
  526. parse_str($this->order, $list);
  527. foreach ($list as $field => $dir) {
  528. $this->dataSource->orderBy($field, $dir === 'a' ? dibi::ASC : dibi::DESC);
  529. $list[$field] = array($dir, $i++);
  530. }
  531. return $list;
  532. }
  533. /**
  534. * Applies filtering on data grid.
  535. * @return void
  536. */
  537. protected function applyFiltering()
  538. {
  539. if (!$this->hasFilters()) return;
  540. parse_str($this->filters, $list);
  541. foreach ($list as $column => $value) {
  542. if ($value !== '') {
  543. $this[$column]->applyFilter($value);
  544. }
  545. }
  546. }
  547. /**
  548. * Applies filtering on data grid.
  549. * @return void
  550. */
  551. protected function applyItems()
  552. {
  553. $value = (int) $this->itemsPerPage;
  554. if ($value == 0) {
  555. $this->itemsPerPage = $this->paginator->itemsPerPage = count($this->dataSource);
  556. } else {
  557. $this->itemsPerPage = $this->paginator->itemsPerPage = $value;
  558. }
  559. }
  560. /********************* renderers *********************/
  561. /**
  562. * Sets data grid renderer.
  563. * @param IDataGridRenderer
  564. * @return void
  565. */
  566. public function setRenderer(IDataGridRenderer $renderer)
  567. {
  568. $this->renderer = $renderer;
  569. }
  570. /**
  571. * Returns data grid renderer.
  572. * @return IDataGridRenderer|NULL
  573. */
  574. public function getRenderer()
  575. {
  576. if ($this->renderer === NULL) {
  577. $this->renderer = new DataGridRenderer;
  578. }
  579. return $this->renderer;
  580. }
  581. /**
  582. * Renders data grid.
  583. * @return void
  584. */
  585. public function render()
  586. {
  587. if (!$this->wasRendered) {
  588. $this->wasRendered = TRUE;
  589. if (!$this->hasColumns() || (count($this->getColumns('ActionColumn')) == count($this->getColumns()))) {
  590. $this->generateColumns();
  591. }
  592. if ($this->disableOrder) {
  593. foreach ($this->getColumns() as $column) {
  594. $column->orderable = FALSE;
  595. }
  596. }
  597. if ($this->hasActions() || $this->hasOperations()) {
  598. if ($this->keyName == NULL) {
  599. throw new InvalidStateException("Name of key for operations or actions was not set for DataGrid '" . $this->getName() . "'.");
  600. }
  601. }
  602. if (!count($this->dataSource)) {
  603. $this->flashMessage($this->translate("Empty datasource given."), 'info');
  604. }
  605. // NOTE: important!
  606. $this->filterItems();
  607. // TODO: na r20 funguje i: $this->getForm()->isSubmitted()
  608. if ($this->isSignalReceiver('submit')) {
  609. $this->regenerateFormControls();
  610. }
  611. }
  612. $args = func_get_args();
  613. array_unshift($args, $this);
  614. $s = call_user_func_array(array($this->getRenderer(), 'render'), $args);
  615. echo mb_convert_encoding($s, 'HTML-ENTITIES', 'UTF-8');
  616. }
  617. /**
  618. * Template factory.
  619. * @return ITemplate
  620. */
  621. protected function createTemplate()
  622. {
  623. $template = parent::createTemplate();
  624. if ($this->getTranslator() !== NULL) {
  625. $template->setTranslator($this->getTranslator());
  626. }
  627. //$template->registerFilter('Nette\Templates\CurlyBracketsFilter::invoke');
  628. return $template;
  629. }
  630. /********************* components handling *********************/
  631. /**
  632. * Component factory.
  633. * @see Nette/ComponentContainer#createComponent()
  634. */
  635. protected function createComponentForm($name)
  636. {
  637. // NOTE: signal-submit on form disregard component's state
  638. // because form is created directly by Presenter in signal handling phase
  639. // and this principle is used to detect submit signal
  640. if (!$this->wasRendered) {
  641. $this->receivedSignal = 'submit';
  642. }
  643. $form = new AppForm($this, $name);
  644. $form->setTranslator($this->getTranslator());
  645. FormControl::$idMask = 'frm-datagrid-' . $this->getUniqueId() . '-%s-%s';
  646. $form->onSubmit[] = array($this, 'formSubmitHandler');
  647. $form->addSubmit('resetSubmit', 'Reset state');
  648. $form->addSubmit('filterSubmit', 'Apply filters');
  649. $form->addSelect('operations', 'Selected:', $this->operations);
  650. $form->addSubmit('operationSubmit', 'Send')->onClick = $this->onOperationSubmit;
  651. // page input
  652. $form->addText('page', 'Page', 1);
  653. $form['page']->setDefaultValue($this->page);
  654. $form->addSubmit('pageSubmit', 'Change page');
  655. // items per page selector
  656. $form->addSelect('items', 'Items per page', array_combine($this->displayedItems, $this->displayedItems));
  657. $form['items']->setDefaultValue($this->itemsPerPage);
  658. $form->addSubmit('itemsSubmit', 'Change');
  659. // generate filters FormControls
  660. if ($this->hasFilters()) {
  661. $defaults = array();
  662. $sub = $form->addContainer('filters');
  663. foreach ($this->getFilters() as $filter) {
  664. $sub->addComponent($filter->getFormControl(), $filter->getName());
  665. // NOTE: must be setted after is FormControl conntected to the form
  666. $defaults[$filter->getName()] = $filter->value;
  667. }
  668. $sub->setDefaults($defaults);
  669. }
  670. // checker
  671. if ($this->hasOperations()) {
  672. $sub = $form->addContainer('checker');
  673. if ($this->isSignalReceiver('submit')) {
  674. // NOTE: important!
  675. $ds = clone $this->dataSource;
  676. $this->filterItems();
  677. }
  678. foreach ($this->getRows() as $row) {
  679. $sub->addCheckbox($row[$this->keyName], $row[$this->keyName]);
  680. }
  681. if (isset($ds)) $this->dataSource = $ds;
  682. }
  683. $renderer = $form->getRenderer();
  684. $renderer->wrappers['controls']['container'] = NULL;
  685. $renderer->wrappers['label']['container'] = NULL;
  686. $renderer->wrappers['control']['container'] = NULL;
  687. $form->setRenderer($renderer);
  688. return;
  689. }
  690. /**
  691. * Returns data grid's form component.
  692. * @param bool throw exception if form doesn't exist?
  693. * @return AppForm
  694. */
  695. public function getForm($need = TRUE)
  696. {
  697. return $this->getComponent('form', $need);
  698. }
  699. /**
  700. * Generates filter controls and checker's checkbox controls
  701. * @param AppForm
  702. * @return void
  703. */
  704. protected function regenerateFormControls()
  705. {
  706. $form = $this->getForm();
  707. // regenerate checker's checkbox controls
  708. if ($this->hasOperations()) {
  709. $values = $form->getValues();
  710. $form->removeComponent($form['checker']);
  711. $sub = $form->addContainer('checker');
  712. foreach ($this->getRows() as $row) {
  713. $sub->addCheckbox($row[$this->keyName], $row[$this->keyName]);
  714. }
  715. if (!empty($values['checker'])) {
  716. $form->setDefaults(array('checker' => $values['checker']));
  717. }
  718. }
  719. // for selectbox filter controls update values if was filtered over column
  720. if ($this->hasFilters()) {
  721. parse_str($this->filters, $list);
  722. foreach ($this->getFilters() as $filter) {
  723. if ($filter instanceof SelectboxFilter) {
  724. $filter->generateItems();
  725. }
  726. if ($this->filters === $this->defaultFilters && ($filter->value !== NULL || $filter->value !== '')) {
  727. if (!in_array($filter->getName(), array_keys($list))) $filter->value = NULL;
  728. }
  729. }
  730. }
  731. // page input & items selectbox
  732. $form['page']->setValue($this->paginator->page); // intentionally page from paginator
  733. $form['items']->setValue($this->paginator->itemsPerPage);
  734. }
  735. /**
  736. * Allows operations and adds checker (column filled by checkboxes).
  737. * @param array list of operations (selectbox items)
  738. * @param mixed valid callback handler which provides rutines from $operations
  739. * @param string column name used to identifies each item/record in data grid (name of primary key of table/query from data source is recomended)
  740. * @return void
  741. */
  742. public function allowOperations(array $operations, $callback = NULL, $key = NULL)
  743. {
  744. $this->operations = $operations;
  745. if ($key != NULL && $this->keyName == NULL) {
  746. $this->keyName = $key;
  747. }
  748. if ($callback != NULL && $this->onOperationSubmit == NULL) {
  749. $this->setOnOperationSubmit($callback);
  750. }
  751. }
  752. /**
  753. * Generates columns from datasource.
  754. * @return void
  755. */
  756. protected function generateColumns()
  757. {
  758. if ($this->hasColumns('ActionColumn')) {
  759. $columns = $this->getColumns('ActionColumn');
  760. foreach ($columns as $column) {
  761. unset($this[$column->getName()]);
  762. }
  763. }
  764. $ds = clone $this->dataSource;
  765. $row = $ds->select('*')->fetch();
  766. $keys = array_keys((array)$row);
  767. foreach ($keys as $key) $this->addColumn($key);
  768. if (isset($columns)) {
  769. foreach ($columns as $column) {
  770. $this[$column->getName()] = $column;
  771. $this->setCurrentActionColumn($column);
  772. }
  773. }
  774. }
  775. /********************* component factories *********************/
  776. /**
  777. * Adds column of textual values.
  778. * @param string control name
  779. * @param string column label
  780. * @param int maximum number of dislayed characters
  781. * @return TextColumn
  782. */
  783. public function addColumn($name, $caption = NULL, $maxLength = NULL)
  784. {
  785. return $this[$name] = new TextColumn($caption, $maxLength);
  786. }
  787. /**
  788. * Adds column of numeric values.
  789. * @param string control name
  790. * @param string column label
  791. * @param int number of digits after the decimal point
  792. * @return NumericColumn
  793. */
  794. public function addNumericColumn($name, $caption = NULL, $precision = 2)
  795. {
  796. return $this[$name] = new NumericColumn($caption, $precision);
  797. }
  798. /**
  799. * Adds column of date-represented values.
  800. * @param string control name
  801. * @param string column label
  802. * @param string date format
  803. * @return DateColumn
  804. */
  805. public function addDateColumn($name, $caption = NULL, $format = '%x')
  806. {
  807. return $this[$name] = new DateColumn($caption, $format);
  808. }
  809. /**
  810. * Adds column of boolean values (represented by checkboxes).
  811. * @param string control name
  812. * @param string column label
  813. * @return CheckboxColumn
  814. */
  815. public function addCheckboxColumn($name, $caption = NULL)
  816. {
  817. return $this[$name] = new CheckboxColumn($caption);
  818. }
  819. /**
  820. * Adds column of graphical images.
  821. * @param string control name
  822. * @param string column label
  823. * @return ImageColumn
  824. */
  825. public function addImageColumn($name, $caption = NULL)
  826. {
  827. return $this[$name] = new ImageColumn($caption);
  828. }
  829. /**
  830. * Adds column which provides moving entries up or down.
  831. * @param string control name
  832. * @param string column label
  833. * @param string destination or signal to handler which do the move rutine
  834. * @param array textual labels for generated links
  835. * @param bool use ajax? (add class DataGridColumn::$ajaxClass into generated link)
  836. * @return PositionColumn
  837. */
  838. public function addPositionColumn($name, $caption = NULL, $destination = NULL, array $moves = NULL, $useAjax = TRUE)
  839. {
  840. return $this[$name] = new PositionColumn($caption, $destination, $moves);
  841. }
  842. /**
  843. * Adds column which represents logic container for data grid actions.
  844. * @param string control name
  845. * @param string column label
  846. * @param bool
  847. * @return ActionColumn
  848. */
  849. public function addActionColumn($name, $caption = NULL, $setAsCurrent = TRUE)
  850. {
  851. $column = new ActionColumn($caption);
  852. if ($setAsCurrent) {
  853. $this->setCurrentActionColumn($column);
  854. }
  855. return $this[$name] = $column;
  856. }
  857. /**
  858. * @param ActionColumn
  859. * @return void
  860. */
  861. public function setCurrentActionColumn(ActionColumn $column)
  862. {
  863. $this->currentActionColumn = $column;
  864. }
  865. /**
  866. * Action factory.
  867. * @param string textual title
  868. * @param string textual link destination
  869. * @param Html element which is added to a generated link
  870. * @param bool use ajax? (add class self::$ajaxClass into generated link)
  871. * @param mixed generate link with argument? (if yes you can specify name of parameter
  872. * otherwise variable DataGrid::$keyName will be used and must be defined)
  873. * @return DataGridAction
  874. */
  875. public function addAction($title, $signal, $icon = NULL, $useAjax = FALSE, $key = DataGridAction::WITH_KEY)
  876. {
  877. if (!$this->hasColumns('ActionColumn')) {
  878. throw new InvalidStateException('No ActionColumn defined. Use DataGrid::addActionColumn before you add actions.');
  879. }
  880. return $this->currentActionColumn->addAction($title, $signal, $icon, $useAjax, $key);
  881. }
  882. /********************* translator ********************/
  883. /**
  884. * Sets translate adapter.
  885. * @param ITranslator
  886. * @return void
  887. */
  888. public function setTranslator(ITranslator $translator = NULL)
  889. {
  890. $this->translator = $translator;
  891. }
  892. /**
  893. * Returns translate adapter.
  894. * @return ITranslator|NULL
  895. */
  896. final public function getTranslator()
  897. {
  898. return $this->translator;
  899. }
  900. /**
  901. * Returns translated string.
  902. * @param string
  903. * @return string
  904. */
  905. public function translate($s)
  906. {
  907. $args = func_get_args();
  908. return $this->translator === NULL ? $s : call_user_func_array(array($this->getTranslator(), 'translate'), $args);
  909. }
  910. /********************* interface \ISignalReceiver *********************/
  911. /**
  912. * Checks if component is signal receiver.
  913. * @param string signal name
  914. * @return bool
  915. */
  916. public function isSignalReceiver($signal = TRUE)
  917. {
  918. if ($signal == 'submit') {
  919. return $this->receivedSignal === 'submit';
  920. } else {
  921. return $this->getPresenter()->isSignalReceiver($this, $signal);
  922. }
  923. // TODO: zatim musi byt reseno takto protoze nize uvedene reseni neni funkcni
  924. // TODO: dokud nebude vyreseno toto tema http://forum.nettephp.com/cs/1813-metoda-issignalreceiver-v-komponentach
  925. // TODO: pak odstranit i promennou receivedSignal
  926. //return $this->getPresenter()->isSignalReceiver($signal == 'submit' ? $this->getForm() : $this, $signal);
  927. }
  928. /********************* backend *********************/
  929. /**
  930. * @return SessionNamespace
  931. */
  932. protected function getStateSession()
  933. {
  934. return $this->getSession()->getNamespace('Nette.Extras.DataGrid/' . $this->getName() . '/states');
  935. }
  936. /**
  937. * @return Session
  938. */
  939. protected function getSession()
  940. {
  941. return Environment::getSession();
  942. }
  943. /**
  944. * Renders table grid and return as string.
  945. * @return string
  946. */
  947. public function __toString()
  948. {
  949. $s = call_user_func_array(array($this->getRenderer(), 'render'), array($this));
  950. return mb_convert_encoding($s, 'HTML-ENTITIES', 'UTF-8');
  951. }
  952. }