├── examples
├── test.latte
├── sources
│ ├── nette_source.php
│ ├── doctrine_source.php
│ └── array_source.php
├── basic.php
├── editable.php
└── index.php
├── package.json
├── src
└── Mesour
│ ├── DataGrid
│ ├── Column
│ │ ├── Template
│ │ │ └── Template.latte
│ │ ├── core
│ │ │ ├── IPrependedColumn.php
│ │ │ ├── IExportable.php
│ │ │ ├── IContainer.php
│ │ │ ├── IOrdering.php
│ │ │ ├── IFiltering.php
│ │ │ ├── IColumn.php
│ │ │ ├── IInlineEdit.php
│ │ │ ├── BaseColumn.php
│ │ │ ├── Ordering.php
│ │ │ ├── Filtering.php
│ │ │ └── InlineEdit.php
│ │ ├── SubItem.php
│ │ ├── Status
│ │ │ ├── IStatusItem.php
│ │ │ ├── StatusButton.php
│ │ │ └── StatusDropDown.php
│ │ ├── EmptyData.php
│ │ ├── Selection.php
│ │ ├── Text.php
│ │ ├── Sortable.php
│ │ ├── SubItemButton.php
│ │ ├── Date.php
│ │ ├── Number.php
│ │ ├── Template.php
│ │ ├── Image.php
│ │ ├── Status.php
│ │ └── Container.php
│ ├── Extensions
│ │ ├── SubItem
│ │ │ ├── Items
│ │ │ │ ├── Template.latte
│ │ │ │ ├── CallbackItem.php
│ │ │ │ ├── TemplateItem.php
│ │ │ │ ├── ComponentItem.php
│ │ │ │ ├── GridItem.php
│ │ │ │ └── Item.php
│ │ │ ├── ISubItem.php
│ │ │ └── SubItemExtension.php
│ │ ├── Editable
│ │ │ ├── IEditable.php
│ │ │ └── EditableExtension.php
│ │ ├── SimpleFilter
│ │ │ ├── ISimpleFilter.php
│ │ │ └── SimpleFilterExtension.php
│ │ ├── Ordering
│ │ │ ├── IOrdering.php
│ │ │ └── OrderingExtension.php
│ │ ├── Filter
│ │ │ ├── IFilter.php
│ │ │ └── FilterExtension.php
│ │ ├── Pager
│ │ │ ├── IPager.php
│ │ │ └── PagerExtension.php
│ │ ├── Selection
│ │ │ ├── ISelection.php
│ │ │ ├── Link.php
│ │ │ ├── Links.php
│ │ │ └── SelectionExtension.php
│ │ ├── IHasColumn.php
│ │ ├── Sortable
│ │ │ ├── ISortable.php
│ │ │ └── SortableExtension.php
│ │ ├── Export
│ │ │ ├── IExport.php
│ │ │ └── ExportExtension.php
│ │ ├── IExtension.php
│ │ └── Base.php
│ ├── Exceptions.php
│ ├── Sources
│ │ ├── IGridSource.php
│ │ ├── NetteDbGridSource.php
│ │ ├── DoctrineGridSource.php
│ │ └── ArrayGridSource.php
│ ├── Renderer
│ │ ├── IGridRenderer.php
│ │ ├── GridRenderer.php
│ │ └── GridListRenderer.php
│ ├── TemplateFileTrait.php
│ ├── TemplateFile.php
│ ├── ExtendedGrid.php
│ ├── SubItemGrid.php
│ └── ExtensionStorage.php
│ └── UI
│ └── DataGrid.php
├── README.md
├── composer.json
├── license.md
└── ruleset.xml
/examples/test.latte:
--------------------------------------------------------------------------------
1 | {define test}
2 | test {$name}
3 | {/define}
4 |
5 | {define test2}
6 | test2 {$name}
7 | {/define}
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "mesour-datagrid": "^0.1.0"
4 | },
5 | "devDependencies": {
6 | "eonasdan-bootstrap-datetimepicker": "^4.17.42"
7 | }
8 | }
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Template/Template.latte:
--------------------------------------------------------------------------------
1 | {if $_block !== FALSE}
2 | {includeblock $_template_path}
3 | {include #$_block}
4 | {else}
5 | {include $_template_path}
6 | {/if}
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/SubItem/Items/Template.latte:
--------------------------------------------------------------------------------
1 | {if $_block !== FALSE}
2 | {includeblock $_template_path}
3 | {include #$_block}
4 | {else}
5 | {include $_template_path}
6 | {/if}
7 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Exceptions.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class NoDataSourceException extends \Exception
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/core/IPrependedColumn.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | interface IPrependedColumn
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/core/IExportable.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | interface IExportable extends IColumn
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/core/IContainer.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IContainer extends Mesour\Components\ComponentModel\IComponent, IColumn
18 | {
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Editable/IEditable.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IEditable extends Mesour\DataGrid\Extensions\IExtension
18 | {
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Sources/IGridSource.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IGridSource extends Mesour\Filter\Sources\IFilterSource
18 | {
19 |
20 | public function fetchForExport();
21 |
22 | public function getColumnNames();
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/SimpleFilter/ISimpleFilter.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface ISimpleFilter extends Mesour\Filter\ISimpleFilter, Mesour\DataGrid\Extensions\IExtension
18 | {
19 |
20 | public function beforeCreate();
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/core/IOrdering.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IOrdering extends Mesour\Components\ComponentModel\IComponent, IColumn
18 | {
19 |
20 | public function setOrdering($ordering = true);
21 |
22 | public function hasOrdering();
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Ordering/IOrdering.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IOrdering extends Mesour\DataGrid\Extensions\IExtension
18 | {
19 |
20 | public function setDefaultOrder($key, $sorting = 'ASC');
21 |
22 | public function enableMulti();
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Filter/IFilter.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IFilter extends Mesour\Filter\IFilter, Mesour\DataGrid\Extensions\IExtension
18 | {
19 |
20 | public function setInline($inline = true);
21 |
22 | public function isInline();
23 |
24 | public function beforeCreate();
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Pager/IPager.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IPager extends Mesour\Pager\IPager, Mesour\DataGrid\Extensions\IExtension
18 | {
19 |
20 | public function beforeRender();
21 |
22 | public function reset($hard = false);
23 |
24 | public function handleSetPage($page = null);
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mesour DataGrid
2 |
3 | [](https://github.com/mesour/DataGrid/releases "Latest Stable Version")
4 |
5 | Mesour DataGrid is DataGrid for PHP >= 5.5 with options like to inline edit, export to csv, create sub items, sort data using jQuery.ui.nestedSortable and much more.
6 |
7 | - [Documentation/Demo](http://grid.mesour.com/version3/)
8 | - [API](http://apis.mesour.com/api/DataGrid3.0.0/)
9 | - [Author](http://mesour.com)
10 |
11 | # Install
12 |
13 | - With [Composer](https://getcomposer.org)
14 |
15 | composer require mesour/datagrid
16 |
17 | - Or download source from [GitHub](https://github.com/mesour/DataGrid/releases)
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Selection/ISelection.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | interface ISelection extends Selection\ISelection, Mesour\DataGrid\Extensions\IHasColumn, Mesour\DataGrid\Extensions\IExtension
19 | {
20 |
21 | /**
22 | * @return Links
23 | */
24 | public function getLinks();
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/IHasColumn.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IHasColumn
18 | {
19 |
20 | /**
21 | * @return Mesour\DataGrid\Column\IColumn
22 | */
23 | public function getSpecialColumn();
24 |
25 | /**
26 | * @return string
27 | */
28 | public function getSpecialColumnName();
29 |
30 | /**
31 | * @return bool
32 | */
33 | public function isGetSpecialColumnUsed();
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/SubItem/ISubItem.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface ISubItem extends Mesour\DataGrid\Extensions\IExtension
18 | {
19 |
20 | public function getPageLimit();
21 |
22 | public function getItems();
23 |
24 | public function getOpened();
25 |
26 | public function hasSubItems();
27 |
28 | public function getItem($name);
29 |
30 | public function setPermission($resource, $privilege);
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/core/IFiltering.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IFiltering extends Mesour\Components\ComponentModel\IComponent, IColumn
18 | {
19 |
20 | public function setFiltering($filtering = true);
21 |
22 | public function hasFiltering();
23 |
24 | public function setInline($inline = true);
25 |
26 | public function isInline();
27 |
28 | public function attachToFilter(Mesour\DataGrid\Extensions\Filter\IFilter $filter, $hasCheckers);
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/SubItem.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class SubItem extends EmptyData
16 | {
17 |
18 | public function setText($text)
19 | {
20 | $this->text = $text;
21 | return $this;
22 | }
23 |
24 | public function getBodyAttributes($data, $need = true, $rawData = [])
25 | {
26 | return parent::mergeAttributes([], ['colspan' => $data]);
27 | }
28 |
29 | public function getBodyContent($data, $rawData)
30 | {
31 | $this->tryInvokeCallback([$rawData, $this, $this->text]);
32 | return $this->text;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Sortable/ISortable.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface ISortable extends Mesour\DataGrid\Extensions\IExtension, Mesour\DataGrid\Extensions\IHasColumn
18 | {
19 |
20 | /**
21 | * @param string $columnName
22 | * @return mixed
23 | */
24 | public function setColumnName($columnName);
25 |
26 | public function setPermission($resource, $privilege);
27 |
28 | public function isGetSpecialColumnUsed();
29 |
30 | public function handleSortData($data, $item);
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/core/IColumn.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | interface IColumn extends Render\IColumn, Mesour\Components\Control\IControl
19 | {
20 |
21 | /**
22 | * @param Mesour\DataGrid\Extensions\Filter\IFilter $filter
23 | * @internal
24 | */
25 | public function setFilterReset(Mesour\DataGrid\Extensions\Filter\IFilter $filter);
26 |
27 | public function setDisabled($disabled = true);
28 |
29 | public function isDisabled();
30 |
31 | public function validate(array $rowData, $data = []);
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Status/IStatusItem.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IStatusItem extends Mesour\Components\Control\IAttributesControl
18 | {
19 |
20 | public function isActive($columnName, $data);
21 |
22 | public function setStatus($status, $statusName, $selectionTitle = null);
23 |
24 | /**
25 | * @return array|null [$this->status => $this->statusName]
26 | */
27 | public function getStatusOptions();
28 |
29 | public function getStatus();
30 |
31 | public function setPermission($resource = null, $privilege = null);
32 |
33 | public function getStatusName();
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Renderer/IGridRenderer.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IGridRenderer extends Mesour\Components\Utils\IString
18 | {
19 |
20 | public function getComponent($type);
21 |
22 | public function setComponent($type, $component);
23 |
24 | /**
25 | * @return Mesour\Components\Utils\Html
26 | */
27 | public function getWrapper();
28 |
29 | public function renderGrid();
30 |
31 | public function renderPager();
32 |
33 | public function renderEditable();
34 |
35 | public function renderSelection();
36 |
37 | public function renderFilter();
38 |
39 | public function renderExport();
40 |
41 | public function render();
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/SubItem/Items/CallbackItem.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class CallbackItem extends Item
18 | {
19 |
20 | public function __construct(Mesour\DataGrid\Extensions\SubItem\ISubItem $parent, $name, $description = null)
21 | {
22 | parent::__construct($parent, $name, $description);
23 | }
24 |
25 | public function render($key = null, $rowData = null, $rawData = null)
26 | {
27 | if (is_null($key) || is_null($rowData)) {
28 | return '';
29 | }
30 | return parent::invoke([$rawData], null, null);
31 | }
32 |
33 | public function reset()
34 | {
35 |
36 | }
37 |
38 | public function invoke(array $args = [], $name, $key)
39 | {
40 |
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Export/IExport.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IExport extends Mesour\DataGrid\Extensions\IExtension
18 | {
19 |
20 | public function setFileName($fileName);
21 |
22 | /**
23 | * @param string $delimiter
24 | * @return mixed
25 | */
26 | public function setDelimiter($delimiter = ',');
27 |
28 | public function setCacheDir($dir);
29 |
30 | public function setColumns(array $columns = []);
31 |
32 | /**
33 | * @return Mesour\UI\Button|Mesour\UI\DropDown
34 | */
35 | public function getExportButton();
36 |
37 | public function hasExport(Mesour\Components\ComponentModel\IContainer $column);
38 |
39 | public function handleExport($type = 'all', array $selectedIds = []);
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/SubItem/Items/TemplateItem.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class TemplateItem extends Item
18 | {
19 |
20 | use Mesour\Template\TemplateTrait;
21 |
22 | public function __construct(Mesour\DataGrid\Extensions\SubItem\ISubItem $parent, $name, $description = null)
23 | {
24 | parent::__construct($parent, $name, $description);
25 | }
26 |
27 | public function render()
28 | {
29 | return $this->getTemplateFile();
30 | }
31 |
32 | public function reset()
33 | {
34 |
35 | }
36 |
37 | public function invoke(array $args = [], $name, $key)
38 | {
39 | $arguments = [$this->render()];
40 | $arguments = array_merge($arguments, $args);
41 | return parent::invoke($arguments, $name, $key);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/EmptyData.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class EmptyData extends BaseColumn
18 | {
19 |
20 | protected $text;
21 |
22 | public function setText($text)
23 | {
24 | $this->text = $this->getTranslator()->translate($text);
25 | return $this;
26 | }
27 |
28 | public function getHeaderAttributes()
29 | {
30 | return [];
31 | }
32 |
33 | public function getHeaderContent()
34 | {
35 | return null;
36 | }
37 |
38 | public function getBodyAttributes($data, $need = true, $rawData = [])
39 | {
40 | return ['colspan' => $data];
41 | }
42 |
43 | public function getBodyContent($data, $rawData)
44 | {
45 | $text = Mesour\Components\Utils\Html::el('p', ['class' => 'empty-data']);
46 | $text->setText($this->text);
47 | return $text;
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Sources/NetteDbGridSource.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class NetteDbGridSource extends Mesour\Filter\Sources\NetteDbFilterSource implements IGridSource
19 | {
20 |
21 | private $columnNames = [];
22 |
23 | public function fetchForExport()
24 | {
25 | $selection = $this->getSelection(false);
26 | $this->lastFetchAllResult = [];
27 | $out = [];
28 | foreach ($selection->fetchAll() as $row) {
29 | /** @var Nette\Database\Table\ActiveRow $row */
30 | $this->lastFetchAllResult[] = $row;
31 | $out[] = $this->makeArrayHash($row->toArray());
32 | }
33 | return $out;
34 | }
35 |
36 | public function getColumnNames()
37 | {
38 | if (!count($this->columnNames)) {
39 | $this->columnNames = array_keys($this->getTableColumns($this->getTableName()));
40 | }
41 | return $this->columnNames;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/IExtension.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IExtension extends Mesour\Components\Control\IControl
18 | {
19 |
20 | /**
21 | * @return bool
22 | */
23 | public function isDisabled();
24 |
25 | public function setDisabled($disabled = true);
26 |
27 | /**
28 | * @param IExtension $extension
29 | * @param null $name
30 | * @return mixed
31 | */
32 | public function createInstance(IExtension $extension, $name = null);
33 |
34 | public function gridCreate($data = []);
35 |
36 | public function afterGetCount($count);
37 |
38 | public function beforeFetchData($data = []);
39 |
40 | public function afterFetchData($currentData, $data = [], $rawData = []);
41 |
42 | public function attachToRenderer(Mesour\DataGrid\Renderer\IGridRenderer $renderer, $data = [], $rawData = []);
43 |
44 | public function reset($hard = false);
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Sources/DoctrineGridSource.php:
--------------------------------------------------------------------------------
1 |
18 | */
19 | class DoctrineGridSource extends Mesour\Filter\Sources\DoctrineFilterSource implements IGridSource
20 | {
21 |
22 | private $columnNames = [];
23 |
24 | public function fetchForExport()
25 | {
26 | try {
27 | $this->lastFetchAllResult = $this->cloneQueryBuilder()
28 | ->setMaxResults(null)
29 | ->setFirstResult(null)
30 | ->getQuery()
31 | ->getResult();
32 |
33 | return $this->fixResult(
34 | $this->getEntityArrayAsArrays($this->lastFetchAllResult)
35 | );
36 | } catch (Doctrine\ORM\NoResultException $e) {
37 | return [];
38 | }
39 | }
40 |
41 | public function getColumnNames()
42 | {
43 | if (!count($this->columnNames)) {
44 | $this->columnNames = array_keys($this->getTableColumns($this->getTableName()));
45 | }
46 | return $this->columnNames;
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/examples/sources/nette_source.php:
--------------------------------------------------------------------------------
1 | addPanel(new \Nette\Bridges\DatabaseTracy\ConnectionPanel($connection));
13 |
14 | $selection = $context->table('users')
15 | ->select('users.*')
16 | ->select('group.name group_name');
17 |
18 | $source = new \Mesour\DataGrid\Sources\NetteDbGridSource(
19 | 'users',
20 | 'id',
21 | $selection,
22 | $context,
23 | [
24 | 'group_name' => 'group.name',
25 | 'group' => 'group.name',
26 | 'wallet' => 'wallet.amount',
27 | 'companies' => ':user_companies.company.name',
28 | 'addresses' => ':user_addresses.city',
29 | 'id' => 'users.id',
30 | 'amount' => 'users.amount',
31 | 'name' => 'users.name',
32 | 'wallet_amount' => 'wallet.amount',
33 | 'company_name' => ':user_companies.company.name',
34 | 'address_city' => ':addresses.city',
35 | ]
36 | );
37 |
38 | return $source;
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Selection.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class Selection extends BaseColumn implements IPrependedColumn
19 | {
20 |
21 | use Authorised;
22 |
23 | /**
24 | * @var ISelection
25 | */
26 | protected $selection;
27 |
28 | public function getHeaderAttributes()
29 | {
30 | $this->selection = $this->getGrid()->getExtension('ISelection');
31 | return ['class' => 'act act-select'];
32 | }
33 |
34 | public function getHeaderContent()
35 | {
36 | return $this->selection->create()->create();
37 | }
38 |
39 | public function getBodyAttributes($data, $need = true, $rawData = [])
40 | {
41 | return parent::mergeAttributes($data, ['class' => 'grid-checkbox']);
42 | }
43 |
44 | public function getBodyContent($data, $rawData)
45 | {
46 | return $this->selection->createItem($data[$this->getGrid()->getSource()->getPrimaryKey()]);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/core/IInlineEdit.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | interface IInlineEdit extends Mesour\Components\ComponentModel\IComponent, IColumn
18 | {
19 |
20 | /**
21 | * @param bool $editable
22 | * @return mixed
23 | */
24 | public function setEditable($editable = true);
25 |
26 | /**
27 | * @return bool
28 | */
29 | public function hasEditable();
30 |
31 | public function setReference($table);
32 |
33 | public function getReference();
34 |
35 | /**
36 | * @return Mesour\Editable\Structures\Fields\IStructureElementField|Mesour\Editable\Structures\Fields\IStructureField
37 | */
38 | public function getEditableField();
39 |
40 | /**
41 | * @return Mesour\UI\Button
42 | */
43 | public function createEditButton();
44 |
45 | /**
46 | * @param mixed $data
47 | * @param array $attributes
48 | * @param mixed $itemData
49 | * @return array
50 | */
51 | public function getEditableAttributes($data, array $attributes = [], $itemData = []);
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Base.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | abstract class Base extends Mesour\UI\Control implements IExtension
18 | {
19 |
20 | private $disabled = false;
21 |
22 | /**
23 | * @return Mesour\DataGrid\ExtendedGrid
24 | */
25 | public function getGrid()
26 | {
27 | return $this->getParent();
28 | }
29 |
30 | /**
31 | * @return bool
32 | */
33 | public function isDisabled()
34 | {
35 | return $this->disabled;
36 | }
37 |
38 | public function setDisabled($disabled = true)
39 | {
40 | $this->disabled = (bool) $disabled;
41 | return $this;
42 | }
43 |
44 | public function gridCreate($data = [])
45 | {
46 | }
47 |
48 | public function createInstance(IExtension $extension, $name = null)
49 | {
50 | }
51 |
52 | public function afterGetCount($count)
53 | {
54 | }
55 |
56 | public function beforeFetchData($data = [])
57 | {
58 | }
59 |
60 | public function afterFetchData($currentData, $data = [], $rawData = [])
61 | {
62 | }
63 |
64 | public function attachToRenderer(Mesour\DataGrid\Renderer\IGridRenderer $renderer, $data = [], $rawData = [])
65 | {
66 | }
67 |
68 | public function reset($hard = false)
69 | {
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mesour/datagrid",
3 | "description": "Mesour DataGrid is DataGrid for PHP >= 5.5 with options like to inline edit, export to csv, create sub items, sort data using jQuery.ui.sortable and much more.",
4 | "license": ["BSD-3-Clause","GPL-2.0","GPL-3.0"],
5 | "keywords": ["grid","datagrid","data grid","component"],
6 | "homepage": "http://grid.mesour.com/",
7 | "authors": [{
8 | "name": "Matouš Němec",
9 | "homepage": "http://mesour.com"
10 | }],
11 | "require": {
12 | "php": ">=5.6.0",
13 | "mesour/components": "~3.2.0",
14 | "mesour/button": "~3.2.0",
15 | "mesour/dropdown": "~3.2.0",
16 | "mesour/editable": "~3.2.0",
17 | "mesour/filter": "~3.2.0",
18 | "mesour/icon": "~3.2.0",
19 | "mesour/pager": "~3.2.0",
20 | "mesour/selection": "~3.2.0",
21 | "mesour/sources": "~3.0.0",
22 | "mesour/template": "~3.2.0",
23 | "mesour/table": "~3.2.0"
24 | },
25 | "require-dev": {
26 | "tracy/tracy": "~2.4.0",
27 | "nette/tester": "^1.6",
28 | "latte/latte": "~2.4.0",
29 | "nette/robot-loader": "~2.4.0",
30 | "nette/database": "~2.4.0",
31 | "doctrine/orm": "^2.5",
32 | "mesour/array-manager": "3.0.*@beta",
33 | "slevomat/coding-standard": "^1.0",
34 | "consistence/coding-standard": "^0.10"
35 | },
36 | "minimum-stability": "dev",
37 | "prefer-stable": true,
38 | "suggest": {
39 | "latte/latte": "to use column Template or Template SubItem",
40 | "ext-gd": "to use column Image"
41 | },
42 | "autoload": {
43 | "classmap": ["src/"]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Text.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class Text extends InlineEdit implements IExportable
18 | {
19 |
20 | use Mesour\Components\Security\Authorised;
21 | use Mesour\Icon\HasIcon;
22 |
23 | public function getHeaderAttributes()
24 | {
25 | return array_merge([
26 | 'class' => 'grid-column-' . $this->getName(),
27 | ], parent::getHeaderAttributes());
28 | }
29 |
30 | public function getBodyAttributes($data, $need = true, $rawData = [])
31 | {
32 | $attributes = parent::getBodyAttributes($data);
33 | $attributes['class'] = 'type-text';
34 | return parent::mergeAttributes($data, $attributes);
35 | }
36 |
37 | public function getBodyContent($data, $rawData)
38 | {
39 | $fromCallback = $this->tryInvokeCallback([$this, $rawData]);
40 | if ($fromCallback !== self::NO_CALLBACK) {
41 | return $fromCallback;
42 | }
43 | return parent::getBodyContent($data, $rawData);
44 | }
45 |
46 | public function attachToFilter(Mesour\DataGrid\Extensions\Filter\IFilter $filter, $hasCheckers)
47 | {
48 | parent::attachToFilter($filter, $hasCheckers);
49 | $item = $filter->addTextFilter($this->getName(), $this->getHeader());
50 | $this->setUpFilterItem($item, $hasCheckers);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Sortable.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class Sortable extends BaseColumn implements IPrependedColumn
18 | {
19 |
20 | use Mesour\Icon\HasIcon;
21 |
22 | protected $arrowsIcon = 'arrows';
23 |
24 | /**
25 | * @return Mesour\UI\Button
26 | */
27 | public function getButton()
28 | {
29 | if (!isset($this['button'])) {
30 | $this['button'] = new Mesour\UI\Button();
31 | $this['button']->setSize('btn-sm')
32 | ->setType('default')
33 | ->setAttribute('class', 'move handler', true)
34 | ->setAttribute('href', '#')
35 | ->setIcon($this->arrowsIcon);
36 | }
37 | return $this['button'];
38 | }
39 |
40 | public function getHeaderAttributes()
41 | {
42 | return ['class' => 'sortable-column'];
43 | }
44 |
45 | public function getHeaderContent()
46 | {
47 | $icon = $this->createNewIcon($this->arrowsIcon . ' grid-move');
48 | return $icon;
49 | }
50 |
51 | public function getBodyAttributes($data, $need = true, $rawData = [])
52 | {
53 | return parent::mergeAttributes($data, ['class' => 'grid-sortable']);
54 | }
55 |
56 | public function getBodyContent($data, $rawData)
57 | {
58 | $this->getButton()->setOption('data', $data);
59 | return $this->getButton()->create();
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/SubItem/Items/ComponentItem.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class ComponentItem extends Item
18 | {
19 |
20 | public function __construct(Mesour\DataGrid\Extensions\SubItem\ISubItem $parent, $name, $description = null, $component = null)
21 | {
22 | parent::__construct($parent, $name, $description);
23 | $i = 0;
24 | while ($i < (is_null($this->pageLimit) ? self::DEFAULT_COUNT : $this->pageLimit)) {
25 | if (!$component instanceof Mesour\Components\Control\IControl) {
26 | Mesour\Components\Utils\Helpers::invokeArgs($component, [$this->getParent()->getParent(), $name . $i]);
27 | } else {
28 | $this->getGrid()->addComponent($component, $name . $i);
29 | }
30 | $this->keys[] = $i;
31 | $i++;
32 | }
33 | }
34 |
35 | public function render($key = null)
36 | {
37 | if (is_null($key)) {
38 | return '';
39 | }
40 | /** @var Mesour\Components\Control\IControl $component */
41 | $component = $this->getGrid()->getComponent($this->name . $this->getTranslatedKey($key));
42 | return $component->create();
43 | }
44 |
45 | public function invoke(array $args = [], $name, $key)
46 | {
47 | $arguments = [$this->getGrid()->getComponent($name . $key)];
48 | $arguments = array_merge($arguments, $args);
49 | return parent::invoke($arguments, $name, $key);
50 | }
51 |
52 | public function reset()
53 | {
54 |
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/SubItem/Items/GridItem.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class GridItem extends Item
18 | {
19 |
20 | public function __construct(
21 | Mesour\DataGrid\Extensions\SubItem\ISubItem $parent,
22 | $name,
23 | $description = null,
24 | Mesour\UI\DataGrid $grid = null
25 | )
26 | {
27 | parent::__construct($parent, $name, $description);
28 | $i = 0;
29 | while ($i < (is_null($this->pageLimit) ? self::DEFAULT_COUNT : $this->pageLimit)) {
30 | $currentGrid = clone $grid;
31 | $currentGrid->setName($name . $i);
32 | $this->parent->addComponent($currentGrid, $name . $i);
33 | $this->keys[] = $i;
34 | $i++;
35 | }
36 | }
37 |
38 | public function render($key = null)
39 | {
40 | if (is_null($key)) {
41 | return '';
42 | }
43 | /** @var Mesour\UI\DataGrid $grid */
44 | $grid = $this->parent->getComponent($this->name . $this->getTranslatedKey($key));
45 | return $grid->create();
46 | }
47 |
48 | public function reset()
49 | {
50 | $i = 0;
51 | while ($i <= (is_null($this->pageLimit) ? self::DEFAULT_COUNT : $this->pageLimit)) {
52 | if (isset($this->parent[$this->name . $i])) {
53 | $this->parent[$this->name . $i]->reset(true);
54 | }
55 | $i++;
56 | }
57 | }
58 |
59 | public function invoke(array $args = [], $name, $key)
60 | {
61 | $arguments = [$this->parent->getComponent($name . $key)];
62 | $arguments = array_merge($arguments, $args);
63 | return parent::invoke($arguments, $name, $key);
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Sources/ArrayGridSource.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class ArrayGridSource extends Mesour\Filter\Sources\ArrayFilterSource implements IGridSource
18 | {
19 |
20 | private $columnNames = [];
21 |
22 | /**
23 | * @var Mesour\ArrayManage\Searcher\Select
24 | */
25 | private $exportSelect;
26 |
27 | private function getExportSelect()
28 | {
29 | if (!$this->exportSelect) {
30 | $this->exportSelect = clone $this->getSelect();
31 | }
32 | return $this->exportSelect;
33 | }
34 |
35 | public function where($column, $value = null, $condition = null, $operator = 'and')
36 | {
37 | parent::where($column, $value, $condition, $operator);
38 | $this->getExportSelect()->where($column, $value, $condition, $operator);
39 | return $this;
40 | }
41 |
42 | public function fetchForExport()
43 | {
44 | $out = $this->getExportSelect()->fetchAll();
45 | foreach ($out as $key => $val) {
46 | $this->removeStructureDate($out[$key]);
47 | }
48 | foreach ($out as $key => $val) {
49 | $out[$key] = $this->makeArrayHash($val);
50 | }
51 | $this->lastFetchAllResult = $out;
52 | return $out;
53 | }
54 |
55 | public function getColumnNames()
56 | {
57 | if (count($this->columnNames) === 0) {
58 | $columns = $this->getTableColumns($this->getTableName());
59 |
60 | $data = $this->fetch();
61 | if (!$data) {
62 | return [];
63 | }
64 | $item = (array) $data;
65 | $this->columnNames = array_unique(array_merge(array_keys($item), array_keys($columns)));
66 | }
67 | return $this->columnNames;
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/SubItemButton.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class SubItemButton extends BaseColumn
18 | {
19 |
20 | private $columnName;
21 |
22 | private $key = -1;
23 |
24 | private $opened = false;
25 |
26 | private $twoRows = false;
27 |
28 | public function setColumnName($name)
29 | {
30 | $this->columnName = $name;
31 | return $this;
32 | }
33 |
34 | public function setKey($key)
35 | {
36 | $this->key = $key;
37 | return $this;
38 | }
39 |
40 | public function setOpened($opened = true)
41 | {
42 | $this->opened = (bool) $opened;
43 | return $this;
44 | }
45 |
46 | public function setTwoRows($twoRows = true)
47 | {
48 | $this->twoRows = (bool) $twoRows;
49 | return $this;
50 | }
51 |
52 | public function getHeaderAttributes()
53 | {
54 | return [];
55 | }
56 |
57 | public function getHeaderContent()
58 | {
59 | return null;
60 | }
61 |
62 | public function getBodyAttributes($data, $need = true, $rawData = [])
63 | {
64 | $attributes = ['colspan' => $data, 'class' => 'subgrid-button'];
65 | if ($this->twoRows) {
66 | $attributes['rowspan'] = 2;
67 | }
68 | return parent::mergeAttributes([], $attributes);
69 | }
70 |
71 | public function getBodyContent($data, $rawData)
72 | {
73 | $button = $this['currentButton'] = new Button;
74 |
75 | $button->setType('info')
76 | ->setSize('btn-sm btn-sm-grid')
77 | ->setAttribute('data-mesour', 'ajax')
78 | ->setAttribute('href', $this->getGrid()->getExtension('ISubItem')->createLink('toggleItem', ['key' => $this->key, 'name' => $this->columnName]));
79 | if ($this->opened) {
80 | $button->setIcon('minus');
81 | } else {
82 | $button->setIcon('plus');
83 | }
84 |
85 | $this->tryInvokeCallback([$rawData, $this, $button]);
86 |
87 | return $button->create();
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/TemplateFileTrait.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | trait TemplateTrait
18 | {
19 |
20 | private $file;
21 |
22 | private $block;
23 |
24 | /**
25 | * @var Mesour\Template\ITemplate
26 | */
27 | private $templateEngine;
28 |
29 | /**
30 | * @var Mesour\UI\TemplateFile
31 | */
32 | private $templateFile;
33 |
34 | public function setTempDir($tempDir)
35 | {
36 | $this->templateFile = new Mesour\UI\TemplateFile($this->getEngine(), $tempDir);
37 | return $this;
38 | }
39 |
40 | public function setBlock($block)
41 | {
42 | $this->block = $block;
43 | if ($this->templateFile) {
44 | $this->templateFile->setBlock($block);
45 | }
46 | return $this;
47 | }
48 |
49 | public function setFile($file)
50 | {
51 | $this->file = $file;
52 | if ($this->templateFile) {
53 | $this->templateFile->setFile($file);
54 | }
55 | return $this;
56 | }
57 |
58 | public function setTemplateEngine(Mesour\Template\ITemplate $template)
59 | {
60 | $this->templateEngine = $template;
61 | }
62 |
63 | public function getEngine()
64 | {
65 | if (!$this->templateEngine) {
66 | $this->templateEngine = new Mesour\Template\Latte\LatteTemplate();
67 | }
68 | return $this->templateEngine;
69 | }
70 |
71 | public function renderTemplate()
72 | {
73 | $template = $this->getTemplateFile();
74 | return $template->render(true);
75 | }
76 |
77 | protected function getTemplateFile()
78 | {
79 | if (!$this->templateFile) {
80 | throw new Mesour\InvalidStateException('Temp dir is required. User setTempDir.');
81 | }
82 | if (!$this->file) {
83 | throw new Mesour\InvalidStateException('Template file is required. User setFile.');
84 | } else {
85 | $this->templateFile->setFile($this->file);
86 | }
87 | if ($this->block) {
88 | $this->templateFile->setBlock($this->block);
89 | }
90 | return $this->templateFile;
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Sortable/SortableExtension.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class SortableExtension extends Mesour\DataGrid\Extensions\Base implements ISortable
18 | {
19 |
20 | use Mesour\Components\Security\Authorised;
21 |
22 | private $sortableUsed = false;
23 |
24 | private $columnName = false;
25 |
26 | public function getSpecialColumn()
27 | {
28 | $this->sortableUsed = true;
29 | $this->getGrid()->setAttribute('data-mesour-sortable', $this->createLinkName());
30 | return new Mesour\DataGrid\Column\Sortable;
31 | }
32 |
33 | public function setColumnName($columnName)
34 | {
35 | $this->columnName = $columnName;
36 | return $this;
37 | }
38 |
39 | public function getSpecialColumnName()
40 | {
41 | return $this->columnName;
42 | }
43 |
44 | public function setPermission($resource, $privilege)
45 | {
46 | $this->setPermissionCheck($resource, $privilege);
47 | return $this;
48 | }
49 |
50 | public function isGetSpecialColumnUsed()
51 | {
52 | return $this->sortableUsed;
53 | }
54 |
55 | public function handleSortData($data, $item)
56 | {
57 | if ($this->isDisabled()) {
58 | throw new Mesour\InvalidStateException('Cannot sort data if extension is disabled.');
59 | }
60 | if (!$this->isAllowed()) {
61 | throw new Mesour\InvalidStateException('Invalid permissions.');
62 | }
63 | $params = [];
64 | $itemId = $item;
65 | parse_str($data, $params);
66 | $data = $params[$this->getGrid()->createLinkName()];
67 | foreach ($data as $key => $val) {
68 | if ($val === 'null') {
69 | $data[$key] = null;
70 | }
71 | }
72 | if (!is_array($data)) {
73 | throw new Mesour\InvalidStateException('Empty post data from column sorting.');
74 | }
75 | $this->getGrid()->reset(true);
76 | $this->getGrid()->onSort($data, $itemId);
77 | }
78 |
79 | public function gridCreate($data = [])
80 | {
81 | $this->create();
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Date.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class Date extends InlineEdit implements IExportable
18 | {
19 |
20 | use Mesour\Components\Security\Authorised;
21 | use Mesour\Icon\HasIcon;
22 |
23 | private $format = 'Y-m-d';
24 |
25 | public function setFormat($format)
26 | {
27 | $this->format = $format;
28 | return $this;
29 | }
30 |
31 | /**
32 | * @return string
33 | */
34 | public function getFormat()
35 | {
36 | return $this->format;
37 | }
38 |
39 | public function getHeaderAttributes()
40 | {
41 | return [
42 | 'class' => 'grid-column-' . $this->getName(),
43 | ];
44 | }
45 |
46 | public function getBodyAttributes($data, $need = true, $rawData = [])
47 | {
48 | $attributes = parent::getBodyAttributes($data, $need, $rawData);
49 | $attributes['class'] = 'type-text';
50 | return parent::mergeAttributes(parent::getBodyAttributes($data), $attributes);
51 | }
52 |
53 | public function getBodyContent($data, $rawData)
54 | {
55 | if (!$data[$this->getName()]) {
56 | return '-';
57 | }
58 | if (is_numeric($data[$this->getName()])) {
59 | $date = new \DateTime();
60 | $date->setTimestamp($data[$this->getName()]);
61 | } elseif ($data[$this->getName()] instanceof \DateTime) {
62 | $date = $data[$this->getName()];
63 | } else {
64 | $date = new \DateTime($data[$this->getName()]);
65 | }
66 | $formattedDate = $date->format($this->format);
67 |
68 | $fromCallback = $this->tryInvokeCallback([$this, $rawData, $date, $formattedDate]);
69 | if ($fromCallback !== self::NO_CALLBACK) {
70 | return $fromCallback;
71 | }
72 |
73 | return $formattedDate;
74 | }
75 |
76 | public function attachToFilter(Mesour\DataGrid\Extensions\Filter\IFilter $filter, $hasCheckers)
77 | {
78 | parent::attachToFilter($filter, $hasCheckers);
79 | $item = $filter->addDateFilter($this->getName(), $this->getHeader());
80 | $this->setUpFilterItem($item, $hasCheckers);
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | Licenses
2 | ========
3 |
4 | Good news! You may use Mesour DataGrid under the terms of either
5 | the New BSD License or the GNU General Public License (GPL) version 2 or 3.
6 |
7 | You don't have to notify anyone which license you are using. You can freely
8 | use Mesour DataGrid in commercial projects as long as the copyright header
9 | remains intact.
10 |
11 |
12 | New BSD License
13 | ---------------
14 |
15 | Copyright (c) 2015 Matouš Němec (http://mesour.com)
16 | All rights reserved.
17 |
18 | Redistribution and use in source and binary forms, with or without modification,
19 | are permitted provided that the following conditions are met:
20 |
21 | * Redistributions of source code must retain the above copyright notice,
22 | this list of conditions and the following disclaimer.
23 |
24 | * Redistributions in binary form must reproduce the above copyright notice,
25 | this list of conditions and the following disclaimer in the documentation
26 | and/or other materials provided with the distribution.
27 |
28 | * Neither the name of "Mesour DataGrid" nor the names of its contributors
29 | may be used to endorse or promote products derived from this software
30 | without specific prior written permission.
31 |
32 | This software is provided by the copyright holders and contributors "as is" and
33 | any express or implied warranties, including, but not limited to, the implied
34 | warranties of merchantability and fitness for a particular purpose are
35 | disclaimed. In no event shall the copyright owner or contributors be liable for
36 | any direct, indirect, incidental, special, exemplary, or consequential damages
37 | (including, but not limited to, procurement of substitute goods or services;
38 | loss of use, data, or profits; or business interruption) however caused and on
39 | any theory of liability, whether in contract, strict liability, or tort
40 | (including negligence or otherwise) arising in any way out of the use of this
41 | software, even if advised of the possibility of such damage.
42 |
43 |
44 | GNU General Public License
45 | --------------------------
46 |
47 | GPL licenses are very very long, so instead of including them here we offer
48 | you URLs with full text:
49 |
50 | - [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html)
51 | - [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html)
52 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/core/BaseColumn.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | abstract class BaseColumn extends Table\Column implements IColumn
19 | {
20 |
21 | use Mesour\Components\Security\Authorised;
22 |
23 | /** @var Mesour\Components\Utils\Html */
24 | private $filterResetButton;
25 |
26 | private $disabled = false;
27 |
28 | public function setDisabled($disabled = true)
29 | {
30 | $this->disabled = (bool) $disabled;
31 | return $this;
32 | }
33 |
34 | public function isDisabled()
35 | {
36 | return $this->disabled;
37 | }
38 |
39 | public function setPermission($resource = null, $privilege = null)
40 | {
41 | $this->setPermissionCheck($resource, $privilege);
42 | return $this;
43 | }
44 |
45 | /**
46 | * @param Mesour\DataGrid\Extensions\Filter\IFilter $filter
47 | * @internal
48 | */
49 | public function setFilterReset(Mesour\DataGrid\Extensions\Filter\IFilter $filter)
50 | {
51 | if ($filter->isInline() && !$filter->isDisabled()) {
52 | $this->filterResetButton = $filter->createResetButton();
53 | $this->filterResetButton->setText($this->getTranslator()->translate('Reset filter'));
54 |
55 | $this->filterResetButton->class('btn-xs', true);
56 | }
57 | }
58 |
59 | protected function getFilterResetButton()
60 | {
61 | if ($this->filterResetButton) {
62 | return $this->filterResetButton;
63 | }
64 | return '';
65 | }
66 |
67 | /**
68 | * @param null $subControl
69 | * @return Mesour\UI\DataGrid|Table\ITable
70 | */
71 | final public function getGrid($subControl = null)
72 | {
73 | return $this->getTable($subControl);
74 | }
75 |
76 | public function validate(array $rowData, $data = [])
77 | {
78 | }
79 |
80 | protected function mergeAttributes($data, array $current)
81 | {
82 | $base = self::getBodyAttributes($data, false);
83 | if (isset($base['class']) && isset($current['class'])) {
84 | $base['class'] = $base['class'] . ' ' . $current['class'];
85 | }
86 | return array_merge($current, $base);
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/TemplateFile.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class TemplateFile extends \stdClass implements Mesour\Components\Utils\IString
19 | {
20 |
21 | private $file;
22 |
23 | private $parameters = [];
24 |
25 | public function __construct($tempDir)
26 | {
27 | if (!class_exists('Latte\Engine')) {
28 | throw new Mesour\InvalidStateException('TemplateFile required composer package "latte/latte".');
29 | }
30 | if (!self::$engine) {
31 | self::$engine = new Engine;
32 | }
33 | self::$engine->setTempDirectory($tempDir);
34 | }
35 |
36 | public function setTemplateEngine(Mesour\Template\ITemplate $template)
37 | {
38 | $this->templateEngine = $template;
39 | }
40 |
41 | public function getEngine()
42 | {
43 | if (!$this->templateEngine) {
44 | $this->templateEngine = new Mesour\Template\Latte\LatteTemplate();
45 | }
46 | return $this->templateEngine;
47 | }
48 |
49 | public function setFile($file)
50 | {
51 | $this->file = $file;
52 | }
53 |
54 | public function render($toString = false)
55 | {
56 | if (!$toString) {
57 | self::$engine->render($this->file, $this->parameters);
58 | } else {
59 | return self::$engine->renderToString($this->file, $this->parameters);
60 | }
61 | return '';
62 | }
63 |
64 | public function __toString()
65 | {
66 | try {
67 | return $this->render(true);
68 | } catch (\Exception $e) {
69 | trigger_error($e->getMessage(), E_USER_WARNING);
70 | return '';
71 | }
72 | }
73 |
74 | public function __set($name, $value)
75 | {
76 | $this->parameters[$name] = $value;
77 | }
78 |
79 | public function __get($name)
80 | {
81 | if (!isset($this->parameters[$name])) {
82 | throw new Mesour\OutOfRangeException('Parameter with name ' . $name . ' does not exist.');
83 | }
84 | return $this->parameters[$name];
85 | }
86 |
87 | public function __isset($name)
88 | {
89 | return isset($this->parameters[$name]);
90 | }
91 |
92 | public function __unset($name)
93 | {
94 | unset($this->parameters[$name]);
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Status/StatusButton.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class StatusButton extends Mesour\UI\Button implements IStatusItem
18 | {
19 |
20 | private $status;
21 |
22 | private $selectionTitle = null;
23 |
24 | private $statusName;
25 |
26 | private $callback;
27 |
28 | private $callbackArgs = [];
29 |
30 | public function __construct($name = null, Mesour\Components\ComponentModel\IContainer $parent = null)
31 | {
32 | parent::__construct($name, $parent);
33 |
34 | $this->setSize('btn-sm');
35 | }
36 |
37 | public function setStatus($status, $statusName, $selectionTitle = null)
38 | {
39 | $this->status = $status;
40 | $this->statusName = $this->getTranslator()->translate($statusName);
41 | $this->setTooltip($this->statusName, 'right');
42 | $this->selectionTitle = !is_null($selectionTitle) ? $this->getTranslator()->translate($selectionTitle) : null;
43 | return $this;
44 | }
45 |
46 | /**
47 | * @return array [$this->status => $this->statusName]
48 | */
49 | public function getStatusOptions()
50 | {
51 | return is_null($this->selectionTitle) ? null : [$this->status => $this->selectionTitle];
52 | }
53 |
54 | public function getStatusName()
55 | {
56 | return $this->statusName;
57 | }
58 |
59 | public function setCallback($callback)
60 | {
61 | Mesour\Components\Utils\Helpers::checkCallback($callback);
62 | $this->callback = $callback;
63 | return $this;
64 | }
65 |
66 | public function setCallbackArguments(array $arguments)
67 | {
68 | $this->callbackArgs = $arguments;
69 | return $this;
70 | }
71 |
72 | public function getStatus()
73 | {
74 | return $this->status;
75 | }
76 |
77 | public function isActive($columnName, $data)
78 | {
79 | if (!$this->callback) {
80 | return $data[$columnName] == $this->status ? true : false;
81 | } else {
82 | $args = [$data];
83 | if (count($this->callbackArgs) > 0) {
84 | $args = array_merge($args, $this->callbackArgs);
85 | }
86 | return Mesour\Components\Utils\Helpers::invokeArgs($this->callback, $args);
87 | }
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Pager/PagerExtension.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class PagerExtension extends Mesour\UI\AdvancedPager implements IPager
18 | {
19 |
20 | use Mesour\Components\Security\Authorised;
21 |
22 | /** @var Mesour\Components\Utils\Html|string */
23 | private $createdPager;
24 |
25 | private $disabled = false;
26 |
27 | public function handleSetPage($page = null)
28 | {
29 | if ($this->isDisabled()) {
30 | throw new Mesour\InvalidStateException('Cannot change page if extension is disabled.');
31 | }
32 | $this->getGrid()->reset();
33 | parent::handleSetPage($page);
34 | }
35 |
36 | /** @return Mesour\DataGrid\ExtendedGrid */
37 | public function getGrid()
38 | {
39 | return $this->getParent();
40 | }
41 |
42 | /**
43 | * @return bool
44 | */
45 | public function isDisabled()
46 | {
47 | return $this->disabled;
48 | }
49 |
50 | public function setDisabled($disabled = true)
51 | {
52 | $this->disabled = $disabled;
53 | return $this;
54 | }
55 |
56 | public function gridCreate($data = [])
57 | {
58 | }
59 |
60 | public function createInstance(Mesour\DataGrid\Extensions\IExtension $extension, $name = null)
61 | {
62 | }
63 |
64 | public function reset($hard = false)
65 | {
66 | parent::reset();
67 | }
68 |
69 | public function afterGetCount($count)
70 | {
71 | $this->setCount($count);
72 | $this->beforeRender();
73 | }
74 |
75 | public function beforeFetchData($data = [])
76 | {
77 | $this->createdPager = $this->getForCreate();
78 | $itemsPerPage = $this->getPaginator()->getItemsPerPage();
79 | $this->getGrid()->getSource()->applyLimit($itemsPerPage, ($this->getPaginator()->getPage() - 1) * $itemsPerPage);
80 | }
81 |
82 | public function afterFetchData($currentData, $data = [], $rawData = [])
83 | {
84 | }
85 |
86 | public function attachToRenderer(Mesour\DataGrid\Renderer\IGridRenderer $renderer, $data = [], $rawData = [])
87 | {
88 | $pagerWrapper = $this->getGrid()->getPagerPrototype();
89 | $pagerWrapper->add($this->createdPager);
90 | $renderer->setComponent('pager', $pagerWrapper);
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/core/Ordering.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | abstract class Ordering extends BaseColumn implements IOrdering
19 | {
20 |
21 | private $ordering = true;
22 |
23 | public function setOrdering($ordering = true)
24 | {
25 | $this->ordering = (bool) $ordering;
26 | return $this;
27 | }
28 |
29 | public function hasOrdering()
30 | {
31 | return $this->ordering;
32 | }
33 |
34 | public function validate(array $rowData, $data = [])
35 | {
36 | parent::validate($rowData, $data);
37 |
38 | if ($this->hasOrdering() && count($rowData) > 0) {
39 | $item = reset($rowData);
40 | if (!array_key_exists($this->getName(), $item)) {
41 | throw new Mesour\InvalidStateException(
42 | sprintf('If use ordering, column key "%s" must exists in data.', $this->getName())
43 | );
44 | }
45 | }
46 | }
47 |
48 | public function getHeaderContent()
49 | {
50 | if ($this->ordering) {
51 | /** @var \Mesour\DataGrid\Extensions\Ordering\OrderingExtension $component */
52 | $component = $this->getGrid('ordering');
53 | $ordering = $component->getOrdering($this->getName());
54 |
55 | $link = Mesour\Components\Utils\Html::el('a', [
56 | 'href' => $this->getGrid('ordering')->createLink('ordering', ['key' => $this->getName()]),
57 | 'class' => 'ordering' . (!is_null($ordering) ? (' ' . strtolower($ordering)) : ''),
58 | 'data-mesour' => 'ajax',
59 | ]);
60 |
61 | $icon = $this->createNewIcon('cog');
62 | $link->setText(parent::getHeaderContent());
63 | if ($this instanceof Column\Number || $this instanceof Column\Date) {
64 | $iconName = 'sort-numeric';
65 | } elseif ($this instanceof Column\Status) {
66 | $iconName = 'sort-amount';
67 | } else {
68 | $iconName = 'sort-alpha';
69 | }
70 |
71 | foreach (['-asc no-sort', '-asc order-asc', '-desc order-desc'] as $suffix) {
72 | $icon->setType($iconName . $suffix);
73 | $link->add($icon->render());
74 | }
75 |
76 | return $link . $this->getFilterResetButton();
77 | } else {
78 | return parent::getHeaderContent() . $this->getFilterResetButton();
79 | }
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Status/StatusDropDown.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class StatusDropDown extends Mesour\UI\DropDown implements IStatusItem
18 | {
19 |
20 | private $status;
21 |
22 | private $selectionTitle = null;
23 |
24 | private $statusName;
25 |
26 | private $callback;
27 |
28 | private $callbackArgs = [];
29 |
30 | public function __construct($name = null, Mesour\Components\ComponentModel\IContainer $parent = null)
31 | {
32 | parent::__construct($name, $parent);
33 |
34 | $this->getMainButton()
35 | ->setSize('btn-sm');
36 | }
37 |
38 | public function setStatus($status, $statusName, $selectionTitle = null)
39 | {
40 | $this->status = $status;
41 | $this->statusName = $this->getTranslator()->translate($statusName);
42 | $this->selectionTitle = !is_null($selectionTitle) ? $this->getTranslator()->translate($selectionTitle) : null;
43 | return $this;
44 | }
45 |
46 | public function setPermission($resource = null, $privilege = null)
47 | {
48 | throw new Mesour\NotImplementedException('Use setPermission on links for this dropdown.');
49 | }
50 |
51 | /**
52 | * @return array [$this->status => $this->statusName]
53 | */
54 | public function getStatusOptions()
55 | {
56 | return is_null($this->selectionTitle) ? null : [$this->status => $this->selectionTitle];
57 | }
58 |
59 | public function getStatusName()
60 | {
61 | return $this->statusName;
62 | }
63 |
64 | public function setCallback($callback)
65 | {
66 | Mesour\Components\Utils\Helpers::checkCallback($callback);
67 | $this->callback = $callback;
68 | return $this;
69 | }
70 |
71 | public function setCallbackArguments(array $arguments)
72 | {
73 | $this->callbackArgs = $arguments;
74 | return $this;
75 | }
76 |
77 | public function getStatus()
78 | {
79 | return $this->status;
80 | }
81 |
82 | public function isActive($columnName, $data)
83 | {
84 | if (!$this->callback) {
85 | return $data[$columnName] == $this->status ? true : false;
86 | } else {
87 | $args = [$data];
88 | if (count($this->callbackArgs) > 0) {
89 | $args = array_merge($args, $this->callbackArgs);
90 | }
91 | return Mesour\Components\Utils\Helpers::invokeArgs($this->callback, $args);
92 | }
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/examples/sources/doctrine_source.php:
--------------------------------------------------------------------------------
1 | setMetadataDriverImpl($driver);
17 |
18 | // or if you prefer yaml or XML
19 | //$config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode);
20 | //$config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode);
21 |
22 | // database configuration parameters
23 | if (!isset($conn)) {
24 | $conn = [
25 | 'driver' => 'pdo_mysql',
26 | 'user' => 'root',
27 | 'password' => 'root',
28 | 'dbname' => 'mesour_editable',
29 | ];
30 | }
31 |
32 | // obtaining the entity manager
33 | $entityManager = EntityManager::create($conn, $config);
34 |
35 | $entityManager->getConfiguration()
36 | ->addCustomDatetimeFunction('DATE', \Mesour\Filter\Sources\DateFunction::class);
37 |
38 | $entityManager->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
39 | \Doctrine\DBAL\Types\Type::addType('enum', \Doctrine\DBAL\Types\StringType::class);
40 |
41 | require_once __DIR__ . '/../../vendor/mesour/sources/tests/Entity/User.php';
42 | require_once __DIR__ . '/../../vendor/mesour/sources/tests/Entity/Group.php';
43 | require_once __DIR__ . '/../../vendor/mesour/sources/tests/Entity/UserAddress.php';
44 | require_once __DIR__ . '/../../vendor/mesour/sources/tests/Entity/Company.php';
45 | require_once __DIR__ . '/../../vendor/mesour/sources/tests/Entity/Wallet.php';
46 |
47 | $helperSet = new \Symfony\Component\Console\Helper\HelperSet(
48 | [
49 | 'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($entityManager->getConnection()),
50 | 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager),
51 | ]
52 | );
53 |
54 | $qb = $entityManager->createQueryBuilder();
55 | $qb = $entityManager->createQueryBuilder()
56 | ->select('u')
57 | ->from(Mesour\Sources\Tests\Entity\User::class, 'u')
58 | ->join(\Mesour\Sources\Tests\Entity\Group::class, 'g', \Doctrine\ORM\Query\Expr\Join::WITH, 'u.group = g.id');
59 |
60 | $source = new \Mesour\DataGrid\Sources\DoctrineGridSource(
61 | Mesour\Sources\Tests\Entity\User::class,
62 | 'id',
63 | $qb,
64 | [
65 | 'id' => 'u.id',
66 | 'group_id' => 'u.groups',
67 | 'last_login' => 'u.lastLogin',
68 | 'group' => 'g',
69 | ]
70 | );
71 |
72 | return $source;
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Selection/Link.php:
--------------------------------------------------------------------------------
1 |
16 | *
17 | * @method null onCall($items)
18 | */
19 | class Link
20 | {
21 |
22 | use Mesour\SmartObject;
23 |
24 | private $name;
25 |
26 | private $fixed_name;
27 |
28 | private $confirm = false;
29 |
30 | private $ajax = true;
31 |
32 | /** @var Mesour\Components\Localization\ITranslator */
33 | private $translator;
34 |
35 | public $onCall = [];
36 |
37 | /** @var Mesour\UI\Button */
38 | private $button;
39 |
40 | public function __construct($name, Mesour\UI\Button $button, Mesour\Components\Localization\ITranslator $translator)
41 | {
42 | $this->translator = $translator;
43 |
44 | $name = $this->translator->translate($name);
45 | $this->fixed_name = Mesour\Components\Utils\Helpers::webalize($name);
46 | $this->button = $button;
47 | $this->button->setAttribute('data-mesour-selection', 'ajax');
48 | $this->button->setAttribute('href', '#');
49 | $this->button->setAttribute('data-name', $this->getFixedName());
50 | $this->name = $name;
51 | return $this;
52 | }
53 |
54 | public function setAjax($ajax)
55 | {
56 | $this->ajax = (bool) $ajax;
57 | $this->button->setAttribute('data-mesour-selection', $this->ajax ? 'ajax' : 'none');
58 | return $this;
59 | }
60 |
61 | public function setGridSelection($dataMesourGridSelection)
62 | {
63 | $this->button->setAttribute('data-mesour-gridselection', $dataMesourGridSelection);
64 | return $this;
65 | }
66 |
67 | public function setConfirm($confirm)
68 | {
69 | $this->confirm = $this->translator->translate($confirm);
70 | $this->button->setAttribute('data-confirm', $this->confirm);
71 | return $this;
72 | }
73 |
74 | public function getName()
75 | {
76 | return $this->name;
77 | }
78 |
79 | public function setPermission($resource, $privilege)
80 | {
81 | $this->button->setPermission($resource, $privilege);
82 | return $this;
83 | }
84 |
85 | public function isAllowed()
86 | {
87 | return $this->button->isAllowed();
88 | }
89 |
90 | /**
91 | * @return Mesour\UI\Button
92 | */
93 | public function getButton()
94 | {
95 | return $this->button;
96 | }
97 |
98 | public function getFixedName()
99 | {
100 | return $this->fixed_name;
101 | }
102 |
103 | public function __clone()
104 | {
105 |
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/SimpleFilter/SimpleFilterExtension.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class SimpleFilterExtension extends Mesour\UI\SimpleFilter implements ISimpleFilter
18 | {
19 |
20 | use Mesour\Components\Security\Authorised;
21 |
22 | private $disabled = false;
23 |
24 | /** @var Mesour\Components\Utils\Html|string */
25 | private $createdFilter;
26 |
27 | /**
28 | * @return bool
29 | */
30 | public function isDisabled()
31 | {
32 | return $this->disabled;
33 | }
34 |
35 | public function setDisabled($disabled = true)
36 | {
37 | $this->disabled = (bool) $disabled;
38 | return $this;
39 | }
40 |
41 | public function gridCreate($data = [])
42 | {
43 | $this->onFilter[] = function (ISimpleFilter $currentFilter) {
44 | $this->updateFilter($currentFilter);
45 | $this->getGrid()->onFilter($currentFilter);
46 | };
47 | $this->setSource($this->getGrid()->getSource());
48 |
49 | $this->setOption('data', $data);
50 | $this->createdFilter = $this->create();
51 | $this->updateFilter($this);
52 | }
53 |
54 | /**
55 | * @return Mesour\DataGrid\ExtendedGrid|Mesour\Components\Control\IControl
56 | */
57 | public function getGrid()
58 | {
59 | return $this->getParent();
60 | }
61 |
62 | public function createInstance(Mesour\DataGrid\Extensions\IExtension $extension, $name = null)
63 | {
64 |
65 | }
66 |
67 | public function afterGetCount($count)
68 | {
69 |
70 | }
71 |
72 | public function beforeFetchData($data = [])
73 | {
74 |
75 | }
76 |
77 | public function afterFetchData($currentData, $data = [], $rawData = [])
78 | {
79 |
80 | }
81 |
82 | public function attachToRenderer(Mesour\DataGrid\Renderer\IGridRenderer $renderer, $data = [], $rawData = [])
83 | {
84 | $filterPrototype = $this->getGrid()->getFilterPrototype();
85 | $filterPrototype->add($this->createdFilter);
86 | $renderer->setComponent('filter', $filterPrototype);
87 | }
88 |
89 | public function reset($hard = false)
90 | {
91 |
92 | }
93 |
94 | /**
95 | * @param ISimpleFilter $filter
96 | * @throws
97 | */
98 | private function updateFilter(ISimpleFilter $filter)
99 | {
100 | if (!$this->isDisabled()) {
101 | $source = $this->getGrid()->getSource();
102 | $source->applySimple($filter->getQuery(), $filter->getAllowedColumns());
103 | }
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/src/Mesour/UI/DataGrid.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class DataGrid extends Mesour\DataGrid\SubItemGrid
19 | {
20 |
21 | /**
22 | * @param string $name
23 | * @param string|null $header
24 | * @return Column\Text
25 | */
26 | public function addText($name, $header = null)
27 | {
28 | $column = new Column\Text;
29 | $this->setColumn($column, $name, $header);
30 | return $column;
31 | }
32 |
33 | /**
34 | * @param string $name
35 | * @param string|null $header
36 | * @return Column\Number
37 | */
38 | public function addNumber($name, $header = null)
39 | {
40 | $column = new Column\Number;
41 | $this->setColumn($column, $name, $header);
42 | return $column;
43 | }
44 |
45 | /**
46 | * @param string $name
47 | * @param string|null $header
48 | * @return Column\Date
49 | */
50 | public function addDate($name, $header = null)
51 | {
52 | $column = new Column\Date;
53 | $this->setColumn($column, $name, $header);
54 | return $column;
55 | }
56 |
57 | /**
58 | * @param string $name
59 | * @param string|null $header
60 | * @return Column\Container
61 | */
62 | public function addContainer($name, $header = null)
63 | {
64 | $column = new Column\Container;
65 | $this->setColumn($column, $name, $header);
66 | $column->setFiltering(false)
67 | ->setOrdering(false);
68 | return $column;
69 | }
70 |
71 | /**
72 | * @param string $name
73 | * @param string|null $header
74 | * @return Column\Image
75 | */
76 | public function addImage($name, $header = null)
77 | {
78 | $column = new Column\Image;
79 | $this->setColumn($column, $name, $header);
80 | return $column;
81 | }
82 |
83 | /**
84 | * @param string $name
85 | * @param string|null $header
86 | * @return Column\Status
87 | */
88 | public function addStatus($name, $header = null)
89 | {
90 | $column = new Column\Status;
91 | $this->setColumn($column, $name, $header);
92 | return $column;
93 | }
94 |
95 | /**
96 | * @param string $name
97 | * @param string|null $header
98 | * @return Column\Template
99 | */
100 | public function addTemplate($name, $header = null)
101 | {
102 | $column = new Column\Template;
103 | $this->setColumn($column, $name, $header);
104 | return $column;
105 | }
106 |
107 | /**
108 | * @param string $name
109 | * @param null $header
110 | * @return Column\Text
111 | */
112 | public function addColumn($name, $header = null)
113 | {
114 | return $this->addText($name, $header);
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/ruleset.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | app/config/environment.php
61 | tests/bootstrap.php
62 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Number.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class Number extends InlineEdit implements IExportable
18 | {
19 |
20 | use Mesour\Components\Security\Authorised;
21 | use Mesour\Icon\HasIcon;
22 |
23 | private $decimals = 0;
24 |
25 | private $unit = null;
26 |
27 | private $decimalPoint = '.';
28 |
29 | private $thousandSeparator = ',';
30 |
31 | public function setDecimals($decimals)
32 | {
33 | $this->decimals = (int) $decimals;
34 | return $this;
35 | }
36 |
37 | public function setDecimalPoint($decimalPoint = '.')
38 | {
39 | $this->decimalPoint = $decimalPoint;
40 | return $this;
41 | }
42 |
43 | public function setUnit($unit)
44 | {
45 | $this->unit = $unit;
46 | return $this;
47 | }
48 |
49 | public function setThousandsSeparator($thousandSeparator = ',')
50 | {
51 | $this->thousandSeparator = $thousandSeparator;
52 | return $this;
53 | }
54 |
55 | /**
56 | * @return int
57 | */
58 | public function getDecimals()
59 | {
60 | return $this->decimals;
61 | }
62 |
63 | /**
64 | * @return string
65 | */
66 | public function getDecimalPoint()
67 | {
68 | return $this->decimalPoint;
69 | }
70 |
71 | /**
72 | * @return null
73 | */
74 | public function getUnit()
75 | {
76 | return $this->unit;
77 | }
78 |
79 | /**
80 | * @return string
81 | */
82 | public function getThousandSeparator()
83 | {
84 | return $this->thousandSeparator;
85 | }
86 |
87 | public function getHeaderAttributes()
88 | {
89 | return parent::mergeAttributes(parent::getHeaderAttributes(), [
90 | 'class' => 'grid-column-' . $this->getName(),
91 | ]);
92 | }
93 |
94 | public function getBodyAttributes($data, $need = true, $rawData = [])
95 | {
96 | $attributes = parent::getBodyAttributes($data);
97 | $attributes['class'] = 'type-text';
98 | return parent::mergeAttributes(parent::getBodyAttributes($data), $attributes);
99 | }
100 |
101 | public function getBodyContent($data, $rawData)
102 | {
103 | $formatted = number_format($data[$this->getName()], $this->decimals, $this->decimalPoint, $this->thousandSeparator)
104 | . ($this->unit ? (' ' . $this->unit) : '');
105 |
106 | $fromCallback = $this->tryInvokeCallback([$this, $rawData, $formatted]);
107 | if ($fromCallback !== self::NO_CALLBACK) {
108 | return $fromCallback;
109 | }
110 | return $formatted;
111 | }
112 |
113 | public function attachToFilter(Mesour\DataGrid\Extensions\Filter\IFilter $filter, $hasCheckers)
114 | {
115 | parent::attachToFilter($filter, $hasCheckers);
116 | $item = $filter->addNumberFilter($this->getName(), $this->getHeader());
117 | $this->setUpFilterItem($item, $hasCheckers);
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Template.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class Template extends Filtering implements IExportable
18 | {
19 |
20 | /** @var Mesour\DataGrid\TemplateFile */
21 | private $template;
22 |
23 | private $templateFile;
24 |
25 | private $block;
26 |
27 | public function setBlock($block)
28 | {
29 | $this->block = $block;
30 | return $this;
31 | }
32 |
33 | public function setTempDirectory($path)
34 | {
35 | if (!is_dir($path) && !is_writable($path)) {
36 | throw new Mesour\InvalidStateException('Temp directory must be directory and must be writable.');
37 | }
38 | if ($this->template) {
39 | unset($this->template);
40 | }
41 | $this->template = new Mesour\DataGrid\TemplateFile($path);
42 | return $this;
43 | }
44 |
45 | public function setTemplateFile($file)
46 | {
47 | if (file_exists($file)) {
48 | $this->templateFile = $file;
49 | } else {
50 | throw new Mesour\FileNotFoundException('Template file "' . $file . '" does not exist.');
51 | }
52 | return $this;
53 | }
54 |
55 | public function getHeaderAttributes()
56 | {
57 | return array_merge(
58 | [
59 | 'class' => 'grid-column-' . $this->getName(),
60 | ],
61 | parent::getHeaderAttributes()
62 | );
63 | }
64 |
65 | public function getBodyAttributes($data, $need = true, $rawData = [])
66 | {
67 | $attributes = parent::getBodyAttributes($data);
68 | $attributes['class'] = 'type-template';
69 | return parent::mergeAttributes($data, $attributes);
70 | }
71 |
72 | public function getBodyContent($data, $rawData)
73 | {
74 | $template = $this->getTemplate();
75 |
76 | $this->tryInvokeCallback([$this, $rawData, $template]);
77 |
78 | return trim($template);
79 | }
80 |
81 | private function getTemplate()
82 | {
83 | if (!$this->template) {
84 | throw new Mesour\InvalidStateException(
85 | 'Temp directory is required, use setTempDirectory() on this column.'
86 | );
87 | }
88 | if (!$this->templateFile) {
89 | throw new Mesour\InvalidStateException('Template file is required, use setTemplateFile() on this column.');
90 | }
91 | $this->template->setFile(__DIR__ . '/Template/Template.latte');
92 | $this->template->_template_path = $this->templateFile;
93 | $this->template->_block = false;
94 | if (is_string($this->block)) {
95 | $this->template->_block = $this->block;
96 | }
97 | return $this->template;
98 | }
99 |
100 | public function attachToFilter(Mesour\DataGrid\Extensions\Filter\IFilter $filter, $hasCheckers)
101 | {
102 | parent::attachToFilter($filter, $hasCheckers);
103 | $item = $filter->addTextFilter($this->getName(), $this->getHeader());
104 | $this->setUpFilterItem($item, $hasCheckers);
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Renderer/GridRenderer.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class GridRenderer implements IGridRenderer
18 | {
19 |
20 | private $components = [];
21 |
22 | public function setComponent($type, $component)
23 | {
24 | if (!$component instanceof Mesour\Components\Utils\IString && !is_string($component) && !is_int($component)) {
25 | throw new Mesour\InvalidArgumentException('Component must be string or int or instanceof Mesour\Components\IString');
26 | }
27 | $this->components[$type] = $component;
28 | }
29 |
30 | public function getComponent($type)
31 | {
32 | if (!$this->components[$type]) {
33 | throw new Mesour\InvalidStateException('Component with type ' . $type . ' does not exists. Can use only ' . implode('|', array_keys($this->components)) . '.');
34 | }
35 | return isset($this->components[$type]) ? $this->components[$type] : null;
36 | }
37 |
38 | /**
39 | * @return Mesour\Components\Utils\Html
40 | */
41 | public function getWrapper()
42 | {
43 | return $this->getComponent('wrapper');
44 | }
45 |
46 | public function getSnippetId()
47 | {
48 | return $this->getComponent('snippet');
49 | }
50 |
51 | public function renderGrid()
52 | {
53 | echo $this->getComponent('grid');
54 | }
55 |
56 | public function renderPager()
57 | {
58 | echo $this->getComponent('pager');
59 | }
60 |
61 | public function renderEditable()
62 | {
63 | echo $this->getComponent('editable');
64 | }
65 |
66 | public function renderFilter()
67 | {
68 | echo $this->getComponent('filter');
69 | }
70 |
71 | public function renderExport()
72 | {
73 | echo $this->getComponent('export');
74 | }
75 |
76 | public function renderSelection()
77 | {
78 | echo $this->getComponent('selection');
79 | }
80 |
81 | public function render()
82 | {
83 | echo $this->__toString();
84 | }
85 |
86 | public function __toString()
87 | {
88 | try {
89 | $wrapper = $this->getWrapper();
90 | $wrapper->id($this->getSnippetId());
91 |
92 | if (isset($this->components['filter'])) {
93 | $wrapper->insert(0, $this->getComponent('filter'));
94 | }
95 | if (isset($this->components['editable'])) {
96 | $wrapper->insert(1, $this->getComponent('editable'));
97 | }
98 |
99 | $wrapper->insert(2, $this->getComponent('grid'));
100 |
101 | if (isset($this->components['pager'])) {
102 | $wrapper->insert(3, $this->getComponent('pager'));
103 | }
104 | if (isset($this->components['selection'])) {
105 | $wrapper->insert(4, $this->getComponent('selection'));
106 | }
107 | if (isset($this->components['export'])) {
108 | $wrapper->insert(5, $this->getComponent('export'));
109 | }
110 | $wrapper->insert(6, '
');
111 | return (string) $wrapper;
112 | } catch (\Exception $e) {
113 | trigger_error($e->getMessage(), E_USER_WARNING);
114 | return '';
115 | }
116 | }
117 |
118 | public function __clone()
119 | {
120 | $this->components = [];
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Selection/Links.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class Links implements Mesour\Components\Utils\IString
18 | {
19 |
20 | use Mesour\SmartObject;
21 |
22 | /**
23 | * @var Mesour\Components\Control\IControl|Mesour\Components\Localization\Translatable
24 | */
25 | private $parent;
26 |
27 | /**
28 | * @var Mesour\UI\DropDown
29 | */
30 | private $dropDown;
31 |
32 | /**
33 | * @var Link[]
34 | */
35 | private $links = [];
36 |
37 | public function __construct(ISelection $parent)
38 | {
39 | $this->parent = $parent;
40 | $this->dropDown = new Mesour\UI\DropDown('links', $parent);
41 | $this->dropDown
42 | ->setAttribute('class', 'dropdown selection-dropdown');
43 |
44 | $this->dropDown
45 | ->getMainButton()
46 | ->setText('Selected')
47 | ->setDisabled(true);
48 | }
49 |
50 | public function setParent(ISelection $parent)
51 | {
52 | $this->parent = $parent;
53 | $this->dropDown = clone $this->dropDown;
54 | $parent->removeComponent('links');
55 | $parent->addComponent($this->dropDown, 'links');
56 | }
57 |
58 | /**
59 | * @param string $name
60 | * @return Link
61 | */
62 | public function addLink($name)
63 | {
64 | $button = $this->dropDown->addButton($this->parent->getTranslator()->translate($name));
65 | $link = new Link($name, $button, $this->parent->getTranslator());
66 | $this->links[$link->getFixedName()] = $link;
67 |
68 | return $link;
69 | }
70 |
71 | /**
72 | * @param array $attributes
73 | * @return $this
74 | */
75 | public function addDivider(array $attributes = [])
76 | {
77 | $this->dropDown->addDivider($attributes);
78 | return $this;
79 | }
80 |
81 | /**
82 | * @param string $text
83 | * @param array $attributes
84 | * @return $this
85 | */
86 | public function addHeader($text, array $attributes = [])
87 | {
88 | $this->dropDown->addHeader($text, $attributes);
89 | return $this;
90 | }
91 |
92 | /**
93 | * @param string $fixedName
94 | * @return Link
95 | */
96 | public function getLink($fixedName)
97 | {
98 | return $this->links[$fixedName];
99 | }
100 |
101 | /**
102 | * @return Mesour\UI\DropDown
103 | */
104 | public function getDropDown()
105 | {
106 | return $this->dropDown;
107 | }
108 |
109 | public function create($data = [])
110 | {
111 | $this->dropDown->getControlPrototype()
112 | ->addAttributes([
113 | 'data-mesour-selectiondropdown' => $this->parent->createLinkName(),
114 | ]);
115 | foreach ($this->getLinks() as $link) {
116 | $link->setGridSelection($this->parent->createLinkName());
117 | }
118 | $this->dropDown->setOption('data', $data);
119 | return $this->dropDown->create();
120 | }
121 |
122 | public function __toString()
123 | {
124 | return (string) $this->create();
125 | }
126 |
127 | /**
128 | * @return Link[]
129 | */
130 | public function getLinks()
131 | {
132 | $out = [];
133 | foreach ($this->links as $link) {
134 | if ($link->isAllowed()) {
135 | $out[] = $link;
136 | }
137 | }
138 | return $out;
139 | }
140 |
141 | }
142 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Renderer/GridListRenderer.php:
--------------------------------------------------------------------------------
1 | getWrapperPrototype()
39 | ->class('mesour-datagrid-list', true);
40 | }
41 |
42 | private function getCreateButton()
43 | {
44 | if (!$this->createButton) {
45 | $this->createButton = new Mesour\UI\Button('_create_button');
46 | $this->createButton->setIcon('plus')
47 | ->setClassName('');
48 | $this->createButton->setAttribute('data-grid-is-add', 'true');
49 | }
50 | return $this->createButton;
51 | }
52 |
53 | private function createRemoveButton()
54 | {
55 | $deleteButton = new Mesour\UI\Button('_delete_button');
56 | $deleteButton->setIcon('times')
57 | ->setClassName('');
58 |
59 | $deleteButton->setAttribute('data-grid-is-remove', 'true');
60 |
61 | return $deleteButton;
62 | }
63 |
64 | public function render()
65 | {
66 | $wrapper = $this->getWrapperPrototype();
67 | $column = $this->getColumn();
68 | $hasEditable = $column instanceof Mesour\DataGrid\Column\IInlineEdit && $column->hasEditable();
69 | if ($hasEditable) {
70 | $editableField = $column->getEditableField();
71 | }
72 |
73 | $this->onRender($this, $wrapper);
74 |
75 | foreach ($this->getItems() as $item) {
76 | $li = $this->createLiPrototype();
77 | if ($this->getCallback()) {
78 | Mesour\Components\Utils\Helpers::invokeArgs($this->getCallback(), [$this, $li, $item[0], $item[1], $item[2]]);
79 | } else {
80 | $li->add($item[1]);
81 |
82 | $attributes = $column->getEditableAttributes($item[0], $this->liAttributes, $item[2]);
83 | $li->addAttributes($attributes);
84 |
85 | if ($hasEditable) {
86 | $li->add(' ');
87 | $li->add($column->createEditButton());
88 | }
89 |
90 | if (isset($editableField) && $editableField->hasRemoveRowEnabled()) {
91 | $li->add(' ');
92 | $li->add($this->createRemoveButton());
93 | }
94 | }
95 | $this->onRenderRow($this, $li, $item[0], $item[1]);
96 | $wrapper->add($li);
97 | }
98 |
99 | if (isset($editableField) && $editableField->hasCreateNewRowEnabled()) {
100 | $createLi = $this->createLiPrototype();
101 | $createLi->add($this->getCreateButton());
102 | $wrapper->add($createLi);
103 | }
104 |
105 | return $wrapper;
106 | }
107 |
108 | public function __clone()
109 | {
110 | parent::__clone();
111 |
112 | $this->liAttributes = [];
113 |
114 | if ($this->createButton) {
115 | $this->createButton = clone $this->createButton;
116 | $this->createButton->setAttributes([]);
117 | }
118 | if ($this->removeButton) {
119 | $this->removeButton = clone $this->removeButton;
120 | $this->removeButton->setAttributes([]);
121 | }
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/SubItem/Items/Item.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | abstract class Item
18 | {
19 |
20 | use Mesour\SmartObject;
21 |
22 | const DEFAULT_COUNT = 20;
23 |
24 | /** @var Mesour\DataGrid\Extensions\SubItem\ISubItem */
25 | protected $parent;
26 |
27 | protected $callback;
28 |
29 | protected $checkCallback;
30 |
31 | protected $type;
32 |
33 | protected $name;
34 |
35 | protected $disabled = false;
36 |
37 | protected $description;
38 |
39 | protected $pageLimit;
40 |
41 | protected $keys = [];
42 |
43 | protected $permission = false;
44 |
45 | protected $aliases = [];
46 |
47 | public function __construct(Mesour\DataGrid\Extensions\SubItem\ISubItem $parent, $name, $description = null)
48 | {
49 | $this->parent = $parent;
50 | $this->name = $name;
51 | $this->pageLimit = $this->parent->getPageLimit();
52 | $this->description = $description;
53 | }
54 |
55 | /**
56 | * @return Mesour\DataGrid\SubItemGrid
57 | */
58 | protected function getGrid()
59 | {
60 | return $this->getParent()->getParent();
61 | }
62 |
63 | public function setCallback($callback)
64 | {
65 | Mesour\Components\Utils\Helpers::checkCallback($callback);
66 | $this->callback = $callback;
67 | return $this;
68 | }
69 |
70 | public function setCheckCallback($callback)
71 | {
72 | Mesour\Components\Utils\Helpers::checkCallback($callback);
73 | $this->checkCallback = $callback;
74 | return $this;
75 | }
76 |
77 | public function check($rowData)
78 | {
79 | if ($this->checkCallback) {
80 | Mesour\Components\Utils\Helpers::invokeArgs($this->checkCallback, [$rowData, $this]);
81 | }
82 | }
83 |
84 | public function isDisabled()
85 | {
86 | return $this->disabled;
87 | }
88 |
89 | public function setDisabled($disabled = true)
90 | {
91 | $this->disabled = $disabled;
92 | return $this;
93 | }
94 |
95 | public function setPermission($resource, $privilege)
96 | {
97 | $this->permission = [$this->parent->getUserRole(), $resource, $privilege];
98 | return $this;
99 | }
100 |
101 | public function isAllowed()
102 | {
103 | return !$this->permission
104 | || Mesour\Components\Utils\Helpers::invokeArgs([$this->parent->getAuthorizator(), 'isAllowed'], $this->permission);
105 | }
106 |
107 | public function getName()
108 | {
109 | return $this->name;
110 | }
111 |
112 | /**
113 | * @return Mesour\DataGrid\Column\SubItem
114 | */
115 | protected function getParent()
116 | {
117 | return $this->parent;
118 | }
119 |
120 | public function setDescription($description)
121 | {
122 | return $this->description = $description;
123 | }
124 |
125 | public function getDescription()
126 | {
127 | return $this->description ? $this->description : $this->name;
128 | }
129 |
130 | public function addAlias($forKey, $alias)
131 | {
132 | $this->aliases[$forKey] = $alias;
133 | }
134 |
135 | public function getTranslatedKey($key)
136 | {
137 | return isset($this->aliases[$key]) ? $this->aliases[$key] : $key;
138 | }
139 |
140 | public function invoke(array $args = [], $name, $key)
141 | {
142 | return $this->callback ? Mesour\Components\Utils\Helpers::invokeArgs($this->callback, $args) : null;
143 | }
144 |
145 | public function hasKey($key)
146 | {
147 | return isset($this->parent[$this->name . $key]);
148 | }
149 |
150 | public function getKeys()
151 | {
152 | return $this->keys;
153 | }
154 |
155 | abstract public function render();
156 |
157 | abstract public function reset();
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/core/Filtering.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | abstract class Filtering extends Ordering implements IFiltering
19 | {
20 |
21 | private $filtering = true;
22 |
23 | private $inline = false;
24 |
25 | /** @var Mesour\DataGrid\Extensions\Filter\IFilter */
26 | private $filter;
27 |
28 | protected $filterItemSizeClass = 'btn-xs';
29 |
30 | /**
31 | * @param bool $filtering
32 | * @return $this
33 | */
34 | public function setFiltering($filtering = true)
35 | {
36 | $this->filtering = (bool) $filtering;
37 | return $this;
38 | }
39 |
40 | /**
41 | * @return bool
42 | */
43 | public function hasFiltering()
44 | {
45 | return $this->filtering;
46 | }
47 |
48 | /**
49 | * @param bool $inline
50 | * @return $this
51 | */
52 | public function setInline($inline = true)
53 | {
54 | $this->inline = (bool) $inline;
55 | return $this;
56 | }
57 |
58 | /**
59 | * @return bool
60 | */
61 | public function isInline()
62 | {
63 | return $this->inline;
64 | }
65 |
66 | protected function setFilter(Mesour\DataGrid\Extensions\Filter\IFilter $filter)
67 | {
68 | $this->filter = $filter;
69 | }
70 |
71 | public function attachToFilter(Mesour\DataGrid\Extensions\Filter\IFilter $filter, $hasCheckers)
72 | {
73 | $this->setFilter($filter);
74 | }
75 |
76 | public function validate(array $rowData, $data = [])
77 | {
78 | parent::validate($rowData, $data);
79 |
80 | if ($this->hasFiltering()) {
81 | $dataStructure = $this->getGrid()->getSource()->getDataStructure();
82 | if ($dataStructure->hasColumn($this->getName())) {
83 | $structureColumn = $dataStructure->getColumn($this->getName());
84 | if ($structureColumn instanceof Mesour\Sources\Structures\Columns\BaseTableColumnStructure) {
85 | $this->setFiltering(false);
86 | return;
87 | }
88 | }
89 |
90 | if (count($rowData) > 0) {
91 | $item = reset($rowData);
92 | if (!array_key_exists($this->getName(), $item)) {
93 | throw new Mesour\InvalidStateException(
94 | sprintf('If use filtering, column key "%s" must exists in data.', $this->getName())
95 | );
96 | }
97 | }
98 | }
99 | }
100 |
101 | public function getHeaderAttributes()
102 | {
103 | if (isset($this->filter[$this->getName()]) && $this->inline && $this->filtering) {
104 | return array_merge(
105 | [
106 | 'data-with-filter' => '1',
107 | ],
108 | parent::getHeaderAttributes()
109 | );
110 | }
111 | return parent::getHeaderAttributes();
112 | }
113 |
114 | public function getHeaderContent()
115 | {
116 | $parentContent = parent::getHeaderContent();
117 | if ($this->inline && !$this->isReferencedColumn()) {
118 | $filterItem = $this->filter->getItem($this->getName());
119 | $filterItem->getButtonPrototype()
120 | ->class($this->filterItemSizeClass, true);
121 | $parentContent .= ' ' . $filterItem->render();
122 |
123 | return $parentContent;
124 | } else {
125 | return $parentContent;
126 | }
127 | }
128 |
129 | /**
130 | * @param Mesour\Filter\IFilterItem $item
131 | * @param bool $hasCheckers
132 | */
133 | protected function setUpFilterItem(Mesour\Filter\IFilterItem $item, $hasCheckers)
134 | {
135 | $dataStructure = $this->getGrid()->getSource()->getDataStructure();
136 | if ($dataStructure->hasColumn($this->getName())) {
137 | $column = $dataStructure->getColumn($this->getName());
138 | if ($column instanceof Mesour\Sources\Structures\Columns\BaseTableColumnStructure) {
139 | $item->setMainFilter(false);
140 | }
141 | }
142 |
143 | $item->setCheckers($hasCheckers);
144 | }
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Editable/EditableExtension.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class EditableExtension extends Mesour\UI\Editable implements IEditable
19 | {
20 |
21 | /**
22 | * @var Mesour\Components\Utils\Html
23 | */
24 | private $createdEditable;
25 |
26 | /**
27 | * @return Mesour\DataGrid\ExtendedGrid
28 | */
29 | public function getGrid()
30 | {
31 | return $this->getParent();
32 | }
33 |
34 | public function getDataStructure()
35 | {
36 | try {
37 | return parent::getDataStructure();
38 | } catch (Mesour\InvalidStateException $e) {
39 | $this->setDataStructure(
40 | Mesour\Editable\Structures\DataStructure::fromSource($this->getGrid()->getSource())
41 | );
42 | }
43 | return parent::getDataStructure();
44 | }
45 |
46 | public function gridCreate($data = [])
47 | {
48 |
49 | }
50 |
51 | public function createInstance(Extensions\IExtension $extension, $name = null)
52 | {
53 | }
54 |
55 | public function afterGetCount($count)
56 | {
57 | $editableDataStructure = $this->getDataStructure();
58 | $dataStructure = $this->getGrid()->getSource()->getDataStructure();
59 | foreach ($this->getGrid()->getColumns() as $column) {
60 | if (
61 | $column instanceof Mesour\DataGrid\Column\IInlineEdit
62 | && $column->hasEditable()
63 | && !$editableDataStructure->hasField($this->getName())
64 | ) {
65 | if ($dataStructure->hasColumn($column->getName())) {
66 | $this->determineAndAttachField(
67 | $editableDataStructure,
68 | $column,
69 | $dataStructure->getColumn($column->getName())
70 | );
71 | } else {
72 | $this->determineAndAttachField($editableDataStructure, $column);
73 | }
74 | }
75 | }
76 | }
77 |
78 | private function determineAndAttachField(
79 | Mesour\Editable\Structures\IDataStructure $structure,
80 | Mesour\DataGrid\Column\IColumn $column,
81 | Mesour\Sources\Structures\Columns\IColumnStructure $columnStructure = null
82 | )
83 | {
84 | if ($column instanceof Mesour\DataGrid\Column\Text) {
85 | $structure->addText($column->getName(), $column->getHeader());
86 | } elseif ($column instanceof Mesour\DataGrid\Column\Number) {
87 | $structure->addNumber($column->getName(), $column->getHeader())
88 | ->setDecimalPoint($column->getDecimalPoint())
89 | ->setDecimals($column->getDecimals())
90 | ->setThousandSeparator($column->getThousandSeparator())
91 | ->setUnit($column->getUnit());
92 | } elseif ($column instanceof Mesour\DataGrid\Column\Date) {
93 | $structure->addDate($column->getName(), $column->getHeader())
94 | ->setFormat($column->getFormat());
95 | }
96 | }
97 |
98 | public function beforeFetchData($data = [])
99 | {
100 | $this->createdEditable = $this->create();
101 | $this->getGrid()->setAttribute('data-mesour-editable', $this->createLinkName());
102 |
103 | $editableDataStructure = $this->getDataStructure();
104 | foreach ($this->getGrid()->getColumns() as $column) {
105 | if (
106 | $column instanceof Mesour\DataGrid\Column\IInlineEdit
107 | && !$column->hasEditable()
108 | && $editableDataStructure->hasField($column->getName())
109 | ) {
110 | $editableDataStructure->getField($column->getName())->setDisabled(true);
111 | }
112 | }
113 | }
114 |
115 | public function afterFetchData($currentData, $data = [], $rawData = [])
116 | {
117 | }
118 |
119 | public function attachToRenderer(Mesour\DataGrid\Renderer\IGridRenderer $renderer, $data = [], $rawData = [])
120 | {
121 | $renderer->setComponent('editable', $this->createdEditable);
122 | }
123 |
124 | public function reset($hard = false)
125 | {
126 | }
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Selection/SelectionExtension.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class SelectionExtension extends Mesour\UI\Selection implements ISelection
18 | {
19 |
20 | use Mesour\Components\Localization\Translatable;
21 | use Mesour\Components\Security\Authorised;
22 |
23 | private $selection_used = false;
24 |
25 | private $disabled = false;
26 |
27 | /** @var Mesour\UI\DropDown */
28 | private $links;
29 |
30 | public function __construct($name = null, Mesour\Components\ComponentModel\IContainer $parent = null)
31 | {
32 | parent::__construct($name, $parent);
33 | $this->links = new Links($this);
34 | }
35 |
36 | /**
37 | * @return Links
38 | */
39 | public function getLinks()
40 | {
41 | return $this->links;
42 | }
43 |
44 | public function getSpecialColumn()
45 | {
46 | $this->selection_used = true;
47 | return new Mesour\DataGrid\Column\Selection;
48 | }
49 |
50 | public function getSpecialColumnName()
51 | {
52 | return '_grid_selection';
53 | }
54 |
55 | public function isGetSpecialColumnUsed()
56 | {
57 | return $this->selection_used;
58 | }
59 |
60 | /**
61 | * @return bool
62 | */
63 | public function isDisabled()
64 | {
65 | return $this->disabled;
66 | }
67 |
68 | public function setDisabled($disabled = true)
69 | {
70 | $this->disabled = (bool) $disabled;
71 | return $this;
72 | }
73 |
74 | public function handleOnSelect($name, array $items)
75 | {
76 | $link = $this->getLinks()->getLink($name);
77 | if (!$link->isAllowed()) {
78 | throw new Mesour\InvalidStateException('Invalid permissions.');
79 | }
80 | $link->onCall($items);
81 | }
82 |
83 | public function gridCreate($data = [])
84 | {
85 | }
86 |
87 | public function createInstance(Mesour\DataGrid\Extensions\IExtension $extension, $name = null)
88 | {
89 | }
90 |
91 | public function afterGetCount($count)
92 | {
93 | }
94 |
95 | public function beforeFetchData($data = [])
96 | {
97 | }
98 |
99 | /**
100 | * @return Mesour\DataGrid\ExtendedGrid
101 | */
102 | public function getGrid()
103 | {
104 | return $this->getParent();
105 | }
106 |
107 | public function afterFetchData($currentData, $data = [], $rawData = [])
108 | {
109 | $items = [];
110 | $statuses = [];
111 | foreach ($this->getGrid()->getColumns() as $column) {
112 | if ($column instanceof Mesour\DataGrid\Column\Status) {
113 | foreach ($currentData as $item) {
114 | $currentItem = null;
115 | foreach ($column as $statusItem) {
116 | /** @var Mesour\DataGrid\Column\Status\IStatusItem $statusItem */
117 | $options = $statusItem->getStatusOptions();
118 | if ($options) {
119 | if ($item[$column->getName()] == $statusItem->getStatus()) {
120 | $currentItem = $options;
121 | break;
122 | }
123 | }
124 | }
125 | if (!is_null($currentItem)) {
126 | $statusName = reset($currentItem);
127 | $currentKey = key($currentItem);
128 | $statuses[$currentKey] = $statusName;
129 | $items[$item[$this->getGrid()->getPrimaryKey()]] = (string) $currentKey;
130 | }
131 | }
132 | }
133 | }
134 | $this->setItems($items);
135 | foreach ($statuses as $status => $statusName) {
136 | $this->addStatus($status, $statusName);
137 | }
138 | }
139 |
140 | public function attachToRenderer(Mesour\DataGrid\Renderer\IGridRenderer $renderer, $data = [], $rawData = [])
141 | {
142 | $renderer->setComponent('selection', $this->getLinks()->create($data));
143 | }
144 |
145 | public function reset($hard = false)
146 | {
147 | }
148 |
149 | public function __clone()
150 | {
151 | parent::__clone();
152 | $this->links = clone $this->links;
153 | $this->links->setParent($this);
154 | }
155 |
156 | }
157 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/core/InlineEdit.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | abstract class InlineEdit extends Filtering implements IInlineEdit
18 | {
19 |
20 | private $editable = true;
21 |
22 | private $reference;
23 |
24 | public function getBodyAttributes($data, $need = true, $rawData = [])
25 | {
26 | $attributes = parent::getBodyAttributes($data, $need, $rawData);
27 |
28 | return $this->getEditableAttributes($data, $attributes);
29 | }
30 |
31 | /**
32 | * @return Mesour\UI\Button
33 | */
34 | public function createEditButton()
35 | {
36 | $editButton = new Mesour\UI\Button('_edit_button');
37 | $editButton->setIcon('pencil')
38 | ->setClassName('');
39 |
40 | $editButton->setAttribute('data-grid-is-edit', 'true');
41 |
42 | return $editButton;
43 | }
44 |
45 | public function setEditable($editable = true)
46 | {
47 | $this->editable = (bool) $editable;
48 | return $this;
49 | }
50 |
51 | public function hasEditable()
52 | {
53 | if (!$this->editable) {
54 | return false;
55 | }
56 | $editable = $this->getGrid()->getExtension('IEditable', false);
57 | return $editable instanceof Mesour\DataGrid\Extensions\Editable\IEditable
58 | && $editable->isAllowed()
59 | && !$editable->isDisabled();
60 | }
61 |
62 | /**
63 | * @return Mesour\Editable\Structures\Fields\IStructureElementField|Mesour\Editable\Structures\Fields\IStructureField
64 | */
65 | public function getEditableField()
66 | {
67 | /** @var Mesour\DataGrid\Extensions\Editable\EditableExtension $editable */
68 | $editable = $this->getGrid()->getExtension('IEditable');
69 | return $editable->getDataStructure()->getField($this->getName());
70 | }
71 |
72 | public function getEditableAttributes($data, array $attributes = [], $itemData = [])
73 | {
74 | if ($this->hasEditable()) {
75 | $value = null;
76 | $identifier = null;
77 |
78 | $dataStructure = $this->getGrid()->getSource()->getDataStructure();
79 | if ($dataStructure->hasColumn($this->getName())) {
80 | $column = $dataStructure->getColumn($this->getName());
81 | if (
82 | $column instanceof Mesour\Sources\Structures\Columns\ManyToOneColumnStructure
83 | || $column instanceof Mesour\Sources\Structures\Columns\OneToOneColumnStructure
84 | ) {
85 | $value = $data[$column->getReferencedColumn()];
86 | $identifier = $data[$this->getGrid()->getSource()->getPrimaryKey()];
87 | } elseif (
88 | $itemData && (
89 | $column instanceof Mesour\Sources\Structures\Columns\OneToManyColumnStructure
90 | || $column instanceof Mesour\Sources\Structures\Columns\ManyToManyColumnStructure
91 | )
92 | ) {
93 | $value = $itemData[$column->getTableStructure()->getPrimaryKey()];
94 | }
95 |
96 | $value = $value ?: $data[$this->getName()];
97 |
98 | if (
99 | !$column instanceof Mesour\Sources\Structures\Columns\OneToManyColumnStructure
100 | && !$column instanceof Mesour\Sources\Structures\Columns\ManyToManyColumnStructure
101 | && ((!is_array($value) && $value !== null) || (is_array($value) && count($value) > 0))
102 | ) {
103 | $attributes['data-grid-edit'] = 'true';
104 | }
105 | }
106 |
107 | $valueAttributes = $attributes + [
108 | 'data-grid-value' => $value ?: $data[$this->getName()],
109 | ];
110 | if ($itemData) {
111 | return $valueAttributes;
112 | }
113 |
114 | return $valueAttributes + [
115 | 'data-grid-editable' => $this->getName(),
116 | 'data-grid-id' => $identifier ?: $identifier = $data[$this->getGrid()->getSource()->getPrimaryKey()],
117 | ];
118 | }
119 | return [];
120 | }
121 |
122 | public function setReference($table)
123 | {
124 | $this->reference = (string) $table;
125 | return $this;
126 | }
127 |
128 | public function getReference()
129 | {
130 | return $this->reference;
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Ordering/OrderingExtension.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class OrderingExtension extends Mesour\DataGrid\Extensions\Base implements IOrdering
18 | {
19 |
20 | use Mesour\Components\Security\Authorised;
21 |
22 | private $defaultOrder = [];
23 |
24 | private $disabled = false;
25 |
26 | private $multi = false;
27 |
28 | private $ordering = [];
29 |
30 | /** @var Mesour\Components\Session\ISessionSection */
31 | private $privateSession;
32 |
33 | public function attached(Mesour\Components\ComponentModel\IContainer $parent)
34 | {
35 | parent::attached($parent);
36 | $this->privateSession = $this->getSession()->getSection($this->createLinkName());
37 | $this->ordering = $this->privateSession->get('ordering', []);
38 | return $this;
39 | }
40 |
41 | public function setDefaultOrder($key, $sorting = 'ASC')
42 | {
43 | $this->defaultOrder = [$key, $sorting];
44 | }
45 |
46 | public function setDisabled($disabled = true)
47 | {
48 | $this->disabled = $disabled;
49 | }
50 |
51 | public function isDisabled()
52 | {
53 | return $this->disabled;
54 | }
55 |
56 | public function enableMulti()
57 | {
58 | $this->multi = true;
59 | }
60 |
61 | /**
62 | * Get ordering for column by column ID
63 | * @param int $columnId
64 | * @return NULL|string(ASC|DESC)
65 | */
66 | public function getOrdering($columnId)
67 | {
68 | if (count($this->defaultOrder) > 0
69 | && $this->ordering === 0
70 | && $this->defaultOrder[0] === $columnId
71 | ) {
72 | return $this->defaultOrder[1];
73 | }
74 | if (!isset($this->ordering[$columnId])) {
75 | return null;
76 | } else {
77 | return $this->ordering[$columnId];
78 | }
79 | }
80 |
81 | /**
82 | * @return Mesour\DataGrid\ExtendedGrid
83 | */
84 | public function getGrid()
85 | {
86 | return $this->getParent();
87 | }
88 |
89 | public function applyOrder()
90 | {
91 | $c = count($this->ordering);
92 | if ($c > 0) {
93 | foreach ($this->ordering as $key => $howToOrder) {
94 | if (!in_array($key, $this->getGrid()->getRealColumnNames())) {
95 | unset($this->ordering[$key]);
96 | } else {
97 | $this->getGrid()->getSource()->orderBy($key, $howToOrder);
98 | }
99 | }
100 | }
101 | if ($c === 0 && count($this->defaultOrder) > 0) {
102 | $this->getGrid()->getSource()
103 | ->orderBy($this->defaultOrder[0], $this->defaultOrder[1]);
104 | }
105 | }
106 |
107 | public function reset($hard = false)
108 | {
109 | if ($hard) {
110 | $this->ordering = [];
111 | $this->privateSession->get('set', $this->ordering);
112 | }
113 | }
114 |
115 | public function handleOrdering($key)
116 | {
117 | $pager = $this->getGrid()->getComponent('pager', false);
118 | if ($pager instanceof Mesour\DataGrid\Extensions\Pager\IPager) {
119 | $pager->reset();
120 | }
121 |
122 | if (!isset($this->ordering[$key])) {
123 | $this->ordering[$key] = 'ASC';
124 | } elseif ($this->ordering[$key] === 'ASC') {
125 | $this->ordering[$key] = 'DESC';
126 | } else {
127 | unset($this->ordering[$key]);
128 | }
129 | if (!$this->multi) {
130 | $current = isset($this->ordering[$key]) ? $this->ordering[$key] : null;
131 | if (!is_null($current)) {
132 | $this->ordering = [];
133 | $this->ordering[$key] = $current;
134 | }
135 | }
136 | $this->privateSession->set('ordering', $this->ordering);
137 | }
138 |
139 | public function beforeFetchData($data = [])
140 | {
141 | $this->applyOrder();
142 | }
143 |
144 | public function afterGetCount($count)
145 | {
146 | $this->create();
147 | foreach ($this->getGrid()->getColumns() as $column) {
148 | if ($column instanceof Mesour\DataGrid\Column\IOrdering
149 | && $this->getGrid()->getExtension('IOrdering')->isDisabled()
150 | ) {
151 | $column->setOrdering(false);
152 | }
153 | }
154 | }
155 |
156 | }
157 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Image.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class Image extends BaseColumn
18 | {
19 |
20 | private $maxWidth = 0;
21 |
22 | private $maxHeight = 0;
23 |
24 | private $previewPath;
25 |
26 | private $previewRootPath;
27 |
28 | private $rootPath;
29 |
30 | /**
31 | * Set max width in pixels
32 | * @param mixed $maxWidth 0 = unlimited
33 | * @return $this
34 | */
35 | public function setMaxWidth($maxWidth)
36 | {
37 | $this->maxWidth = (int) $maxWidth;
38 | return $this;
39 | }
40 |
41 | /**
42 | * Set max height in pixels
43 | * @param mixed $maxHeight 0 = unlimited
44 | * @return $this
45 | */
46 | public function setMaxHeight($maxHeight)
47 | {
48 | $this->maxHeight = (int) $maxHeight;
49 | return $this;
50 | }
51 |
52 | /**
53 | * @param string $previewWebPath
54 | * @param null $previewRootPath default $_SERVER['DOCUMENT_ROOT']
55 | * @param null $imageRootPath default $_SERVER['DOCUMENT_ROOT']
56 | * @return $this
57 | * @throws Mesour\InvalidArgumentException
58 | */
59 | public function setPreviewPath($previewWebPath, $previewRootPath = null, $imageRootPath = null)
60 | {
61 | if (!is_null($imageRootPath) && !is_string($imageRootPath)) {
62 | throw new Mesour\InvalidArgumentException('Image root path must be string. ' . gettype($imageRootPath) . ' given.');
63 | } elseif (is_null($imageRootPath)) {
64 | $this->rootPath = $_SERVER['DOCUMENT_ROOT'];
65 | } else {
66 | $this->rootPath = $imageRootPath;
67 | }
68 |
69 | if (!is_null($previewRootPath) && !is_string($previewRootPath)) {
70 | throw new Mesour\InvalidArgumentException('Preview root path must be string. ' . gettype($previewRootPath) . ' given.');
71 | } elseif (is_null($previewRootPath)) {
72 | $this->previewRootPath = $_SERVER['DOCUMENT_ROOT'];
73 | } else {
74 | $this->previewRootPath = $previewRootPath;
75 | }
76 |
77 | $this->previewPath = $previewWebPath;
78 | return $this;
79 | }
80 |
81 | public function getHeaderAttributes()
82 | {
83 | return [
84 | 'class' => 'grid-column-' . $this->getName(),
85 | ];
86 | }
87 |
88 | public function getBodyContent($data, $rawData)
89 | {
90 | $src = $this->tryInvokeCallback([$this, $rawData]);
91 | if ($src === self::NO_CALLBACK) {
92 | $src = $data[$this->getName()];
93 | }
94 |
95 | $img = Mesour\Components\Utils\Html::el('img');
96 | if (!$this->previewPath) {
97 | if ($this->maxWidth > 0) {
98 | $img->style('max-width:' . $this->fixPixels($this->maxWidth));
99 | }
100 | if ($this->maxHeight > 0) {
101 | $img->style('max-height:' . $this->fixPixels($this->maxHeight));
102 | }
103 | } else {
104 | $imageName = str_replace(['/', '\\'], '_', $src);
105 | $imageDir = $this->previewRootPath . '/' . $this->previewPath;
106 | $imagePath = $imageDir . '/' . $imageName;
107 |
108 | @mkdir($imageDir);
109 |
110 | if (!is_dir($imageDir)) {
111 | throw new Mesour\InvalidArgumentException('Image preview dir "' . $imageDir . '" does not exist.');
112 | }
113 |
114 | if (!is_file($imagePath)) {
115 | $image = Mesour\Components\Utils\Image::fromFile($this->createFullPath($src));
116 | $newWidth = $width = $image->getWidth();
117 | $newHeight = $height = $image->getHeight();
118 |
119 | if ($this->maxWidth > 0) {
120 | if ($width > $this->maxWidth) {
121 | $newWidth = $this->maxWidth;
122 | }
123 | }
124 | if ($this->maxHeight > 0) {
125 | if ($height > $this->maxHeight) {
126 | $newHeight = $this->maxHeight;
127 | }
128 | }
129 |
130 | $image->resize($newWidth, $newHeight, Mesour\Components\Utils\Image::FIT);
131 | $image->save($imagePath);
132 | }
133 |
134 | $src = $this->previewPath . '/' . $imageName;
135 | }
136 | $img->src($src);
137 | return $img;
138 | }
139 |
140 | private function createFullPath($imageFile)
141 | {
142 | return $this->rootPath . $imageFile;
143 | }
144 |
145 | private function fixPixels($value)
146 | {
147 | return is_numeric($value) ? ($value . 'px') : $value;
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Status.php:
--------------------------------------------------------------------------------
1 |
16 | *
17 | * @method Mesour\DataGrid\Column\Status\IStatusItem[] getComponents()
18 | * @method Mesour\DataGrid\Column\Status\IStatusItem getComponent($name, $need = false)
19 | */
20 | class Status extends Filtering implements IExportable
21 | {
22 |
23 | use Mesour\Components\Security\Authorised;
24 | use Mesour\Icon\HasIcon;
25 |
26 | static public $no_active_class = 'no-active-button';
27 |
28 | /**
29 | * @param string $name
30 | * @return Mesour\DataGrid\Column\Status\StatusButton
31 | */
32 | public function addButton($name)
33 | {
34 | $button = new Mesour\DataGrid\Column\Status\StatusButton($name);
35 | $this->addComponent($button);
36 | return $button;
37 | }
38 |
39 | /**
40 | * @param string $name
41 | * @return Mesour\DataGrid\Column\Status\StatusDropDown
42 | */
43 | public function addDropDown($name)
44 | {
45 | $dropDown = new Mesour\DataGrid\Column\Status\StatusDropDown($name);
46 | $this->addComponent($dropDown);
47 | return $dropDown;
48 | }
49 |
50 | public function addComponent(Mesour\Components\ComponentModel\IComponent $component, $name = null)
51 | {
52 | if (!$component instanceof Mesour\DataGrid\Column\Status\IStatusItem) {
53 | throw new Mesour\InvalidArgumentException('Can add children for status column only if is instance of Mesour\DataGrid\Column\Status\IStatusItem.');
54 | }
55 | return parent::addComponent($component, $name);
56 | }
57 |
58 | public function setPermission($resource = null, $privilege = null)
59 | {
60 | foreach ($this->getComponents() as $component) {
61 | $component->setPermission($resource, $privilege);
62 | //todo: na dropdownu udělat také set permission aby to nastavilo práva na všech buttonech
63 | }
64 | return $this;
65 | }
66 |
67 | public function getHeaderAttributes()
68 | {
69 | return array_merge([
70 | 'class' => 'grid-column-' . $this->getName() . ' column-status',
71 | ], parent::getHeaderAttributes());
72 | }
73 |
74 | public function getBodyAttributes($data, $need = true, $rawData = [])
75 | {
76 | $class = 'button-component';
77 | $activeCount = 0;
78 | foreach ($this as $button) {
79 | /** @var Mesour\DataGrid\Column\Status\IStatusItem $button */
80 | if ($button->isActive($this->getName(), $data)) {
81 | $class .= ' is-' . $button->getStatus();
82 | $activeCount++;
83 | }
84 | }
85 | if ($activeCount === 0) {
86 | $class .= ' ' . self::$no_active_class;
87 | }
88 | return parent::mergeAttributes($data, ['class' => $class]);
89 | }
90 |
91 | public function getBodyContent($data, $rawData, $export = false)
92 | {
93 | $buttons = $export ? [] : '';
94 |
95 | $activeCount = 0;
96 | foreach ($this as $button) {
97 | /** @var Mesour\DataGrid\Column\Status\IStatusItem $button */
98 | $isActive = false;
99 |
100 | if ($button->isActive($this->getName(), $data)) {
101 | $isActive = true;
102 | }
103 |
104 | $this->tryInvokeCallback([$rawData, $this, $isActive]);
105 |
106 | $button->setOption('data', $data);
107 | if ($isActive && !$export) {
108 | $buttons .= $button->create() . ' ';
109 | $activeCount++;
110 | } elseif ($isActive && $export) {
111 | $buttons[] = $button->getStatusName();
112 | }
113 | }
114 |
115 | $container = Mesour\Components\Utils\Html::el('div', ['class' => 'status-buttons buttons-count-' . $activeCount]);
116 | $container->setHtml($export ? implode('|', $buttons) : $buttons);
117 |
118 | return $export ? trim(strip_tags($container)) : $container;
119 | }
120 |
121 | public function attachToFilter(Mesour\DataGrid\Extensions\Filter\IFilter $filter, $hasCheckers)
122 | {
123 | parent::attachToFilter($filter, $hasCheckers);
124 |
125 | $statuses = [];
126 | foreach ($this->getComponents() as $component) {
127 | $statuses[$component->getStatus()] = $component->getStatusName();
128 | }
129 |
130 | $filter->setCustomReference($this->getName(), $statuses);
131 |
132 | $item = $filter->addTextFilter($this->getName(), $this->getHeader(), $statuses);
133 | $item->setMainFilter(false);
134 | $item->setCheckers($hasCheckers);
135 | $item->setReferenceSettings(Mesour\DataGrid\Extensions\Filter\FilterExtension::PREDEFINED_KEY);
136 | }
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Filter/FilterExtension.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class FilterExtension extends Mesour\UI\Filter implements IFilter
19 | {
20 |
21 | use Mesour\Components\Security\Authorised;
22 |
23 | private $isInline = true;
24 |
25 | private $disabled = false;
26 |
27 | /** @var Mesour\Components\Utils\Html|string */
28 | private $createdFilter;
29 |
30 | protected $filterIcon = 'filter';
31 |
32 | /**
33 | * @return bool
34 | */
35 | public function isDisabled()
36 | {
37 | return $this->disabled;
38 | }
39 |
40 | public function setDisabled($disabled = true)
41 | {
42 | $this->disabled = (bool) $disabled;
43 | return $this;
44 | }
45 |
46 | public function setInline($inline = true)
47 | {
48 | $this->isInline = $inline;
49 | $this->setupInlineFilterAttribute();
50 | return $this;
51 | }
52 |
53 | public function setFilterIcon($filterIcon)
54 | {
55 | $this->filterIcon = $filterIcon;
56 | return $this;
57 | }
58 |
59 | public function isInline()
60 | {
61 | return $this->isInline;
62 | }
63 |
64 | public function gridCreate($data = [])
65 | {
66 | $this->onFilter[] = function (IFilter $currentFilter) {
67 | $this->updateFilter($currentFilter);
68 | $this->getGrid()->onFilter($currentFilter);
69 | };
70 | $this->setSource($this->getGrid()->getSource());
71 | $fullData = $this->beforeCreate();
72 |
73 | foreach ($this->getGrid()->getColumns() as $column) {
74 | if ($column instanceof Mesour\DataGrid\Column\IFiltering) {
75 | if ($column->hasFiltering()) {
76 | $column->attachToFilter($this, count($fullData) > 0);
77 | }
78 | }
79 | }
80 | if (!$this->isInline()) {
81 | $this->setOption('data', $data);
82 | $this->createdFilter = $this->create();
83 | } else {
84 | $this->beforeRender();
85 | $this->createdFilter = $this->createHiddenInput($fullData);
86 | }
87 | $this->updateFilter($this);
88 | }
89 |
90 | /**
91 | * @return Mesour\DataGrid\ExtendedGrid
92 | */
93 | public function getGrid()
94 | {
95 | return $this->getParent();
96 | }
97 |
98 | public function createInstance(Mesour\DataGrid\Extensions\IExtension $extension, $name = null)
99 | {
100 | $this->setupInlineFilterAttribute();
101 | }
102 |
103 | private function setupInlineFilterAttribute()
104 | {
105 | $this->getGrid()->setAttribute(
106 | 'data-mesour-enabled-filter',
107 | (int) ($this->isInline() && !$this->isDisabled())
108 | );
109 | }
110 |
111 | public function afterGetCount($count)
112 | {
113 | foreach ($this->getGrid()->getColumns() as $column) {
114 | if (
115 | $column instanceof Mesour\DataGrid\Column\IFiltering
116 | && $this->isInline()
117 | && isset($this[$column->getName()])
118 | ) {
119 | $column->setInline();
120 | $this[$column->getName()]->setText($this->createNewIcon($this->filterIcon), false);
121 | }
122 | }
123 | }
124 |
125 | public function beforeFetchData($data = [])
126 | {
127 | }
128 |
129 | public function afterFetchData($currentData, $data = [], $rawData = [])
130 | {
131 |
132 | }
133 |
134 | public function attachToRenderer(Mesour\DataGrid\Renderer\IGridRenderer $renderer, $data = [], $rawData = [])
135 | {
136 | $filterPrototype = $this->getGrid()->getFilterPrototype();
137 | $filterPrototype->add($this->createdFilter);
138 | if ($this->createdFilter->getName() === 'input') {
139 | $filterPrototype->add($this->getModal()->create());
140 | }
141 | $renderer->setComponent('filter', $filterPrototype);
142 | }
143 |
144 | public function reset($hard = false)
145 | {
146 | }
147 |
148 | /**
149 | * @param IFilter $filter
150 | * @throws
151 | * @internal
152 | */
153 | private function updateFilter(IFilter $filter)
154 | {
155 | if (!$this->isDisabled()) {
156 | $source = $this->getGrid()->getSource();
157 | foreach ($filter->getValues() as $name => $value) {
158 | $type = isset($value['type']) ? $value['type'] : 'text';
159 | foreach ($value as $key => $val) {
160 | switch ($key) {
161 | case 'checkers':
162 | $source->applyCheckers($name, $val, $type);
163 | break;
164 | case 'custom':
165 | $source->applyCustom($name, $val, $type);
166 | break;
167 | }
168 | }
169 | }
170 | }
171 | }
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/ExtendedGrid.php:
--------------------------------------------------------------------------------
1 |
16 | *
17 | * @method null onSort($data, $itemId)
18 | * @method null onFilter(Extensions\Filter\IFilter|Extensions\SimpleFilter\ISimpleFilter $filter)
19 | */
20 | abstract class ExtendedGrid extends BaseGrid
21 | {
22 |
23 | const FILTER_WRAPPER = 'filter-wrapper';
24 |
25 | const PAGER_WRAPPER = 'pager-wrapper';
26 |
27 | static public $defaults = [
28 | self::WRAPPER => [
29 | 'el' => 'div',
30 | 'attributes' => [
31 | 'class' => 'mesour-datagrid',
32 | ],
33 | ],
34 | self::PAGER_WRAPPER => [
35 | 'el' => 'div',
36 | 'attributes' => [
37 | 'class' => 'mesour-datagrid-pager',
38 | ],
39 | ],
40 | self::FILTER_WRAPPER => [
41 | 'el' => 'div',
42 | 'attributes' => [
43 | 'class' => 'mesour-datagrid-filter',
44 | ],
45 | ],
46 | ];
47 |
48 | public function __construct($name = null, Mesour\Components\ComponentModel\IContainer $parent = null)
49 | {
50 | parent::__construct($name, $parent);
51 | $this->option = self::$defaults;
52 | }
53 |
54 | /** @var Mesour\Components\Utils\Html */
55 | protected $pagerWrapper;
56 |
57 | /** @var Mesour\Components\Utils\Html */
58 | protected $filterWrapper;
59 |
60 | public $onSort = [];
61 |
62 | /**
63 | * @var array
64 | * @deprecated not called
65 | */
66 | public $onEditCell = [];
67 |
68 | public $onFilter = [];
69 |
70 | /**
71 | * @param int $pageLimit
72 | * @return Extensions\Pager\IPager
73 | */
74 | public function enablePager($pageLimit = 20)
75 | {
76 | $pager = $this->getExtension('IPager');
77 | /** @var Extensions\Pager\IPager $pager */
78 | $pager->getPaginator()->setItemsPerPage($pageLimit);
79 | return $pager;
80 | }
81 |
82 | /**
83 | * @param string $cacheDir
84 | * @param string|null $fileName
85 | * @return Extensions\Export\IExport
86 | */
87 | public function enableExport($cacheDir, $fileName = null)
88 | {
89 | return $this->getExtension('IExport')
90 | ->setCacheDir($cacheDir)
91 | ->setFileName($fileName);
92 | }
93 |
94 | /**
95 | * @param bool $inline
96 | * @return Extensions\Filter\IFilter
97 | */
98 | public function enableFilter($inline = true)
99 | {
100 | if ($this->getExtension('ISimpleFilter', false)) {
101 | throw new Mesour\InvalidStateException('Simple filter is already used.');
102 | }
103 | return $this->getExtension('IFilter')->setInline($inline);
104 | }
105 |
106 | /**
107 | * @param array $allowedColumns
108 | * @return Extensions\SimpleFilter\ISimpleFilter
109 | */
110 | public function enableSimpleFilter(array $allowedColumns = [])
111 | {
112 | if ($this->getExtension('IFilter', false)) {
113 | throw new Mesour\InvalidStateException('Normal filter is already used.');
114 | }
115 | return $this->getExtension('ISimpleFilter')->setAllowedColumns($allowedColumns);
116 | }
117 |
118 | /**
119 | * @return Extensions\Selection\ISelection
120 | * @throws Mesour\InvalidArgumentException
121 | */
122 | public function enableRowSelection()
123 | {
124 | return $this->getExtension('ISelection');
125 | }
126 |
127 | /**
128 | * @param string $columnName
129 | * @return Extensions\Sortable\ISortable
130 | * @throws Mesour\InvalidArgumentException
131 | */
132 | public function enableSortable($columnName)
133 | {
134 | return $this->getExtension('ISortable')
135 | ->setColumnName($columnName);
136 | }
137 |
138 | /**
139 | * @return Extensions\Editable\EditableExtension
140 | */
141 | public function enableEditable()
142 | {
143 | return $this->getExtension('IEditable');
144 | }
145 |
146 | public function getPagerPrototype()
147 | {
148 | return $this->pagerWrapper
149 | ? $this->pagerWrapper
150 | : ($this->pagerWrapper = Mesour\Components\Utils\Html::el(
151 | $this->option[self::PAGER_WRAPPER]['el'],
152 | $this->option[self::PAGER_WRAPPER]['attributes']
153 | ));
154 | }
155 |
156 | public function getFilterPrototype()
157 | {
158 | return $this->filterWrapper
159 | ? $this->filterWrapper
160 | : ($this->filterWrapper = Mesour\Components\Utils\Html::el(
161 | $this->option[self::FILTER_WRAPPER]['el'],
162 | $this->option[self::FILTER_WRAPPER]['attributes']
163 | ));
164 | }
165 |
166 | public function create($data = [])
167 | {
168 | $filter = $this->getExtension('IFilter', false);
169 | $this->onRenderColumnHeader[] = function (Column\IColumn $column, $i, $columnCount) use ($filter) {
170 | if ($i + 1 === $columnCount && $filter instanceof Extensions\Filter\IFilter) {
171 | $column->setFilterReset($filter);
172 | }
173 | };
174 | return parent::create($data);
175 | }
176 |
177 | public function __clone()
178 | {
179 | $this->pagerWrapper = null;
180 | $this->filterWrapper = null;
181 | parent::__clone();
182 | }
183 |
184 | }
185 |
--------------------------------------------------------------------------------
/examples/sources/array_source.php:
--------------------------------------------------------------------------------
1 | '1', 'action' => '0', 'group_id' => '1', 'name' => 'Peter', 'surname' => NULL, 'email' => 'john.doe@test.xx', 'last_login' => '2014-09-01 06:27:32', 'amount' => '1561.456542', 'avatar' => 'avatar/01.png', 'sort' => '100', 'timestamp' => '1418255325'],
5 | ['user_id' => '2', 'action' => '1', 'group_id' => '2', 'name' => 'John', 'surname' => 'Doe', 'email' => 'peter.larson@test.xx', 'last_login' => '2014-09-09 13:37:32', 'amount' => '15220.654', 'avatar' => 'avatar/02.png', 'sort' => '160', 'timestamp' => '1418255330'],
6 | ['user_id' => '3', 'action' => '1', 'group_id' => '2', 'name' => 'Claude', 'surname' => 'Graves', 'email' => 'claude.graves@test.xx', 'last_login' => '2014-09-02 14:17:32', 'amount' => '9876.465498', 'avatar' => 'avatar/03.png', 'sort' => '180', 'timestamp' => '1418255311'],
7 | ['user_id' => '4', 'action' => '0', 'group_id' => '3', 'name' => 'Stuart', 'surname' => 'Norman', 'email' => 'stuart.norman@test.xx', 'last_login' => '2014-09-09 18:39:18', 'amount' => '98766.2131', 'avatar' => 'avatar/04.png', 'sort' => '120', 'timestamp' => '1418255328'],
8 | ['user_id' => '5', 'action' => '1', 'group_id' => '1', 'name' => 'Kathy', 'surname' => 'Arnold', 'email' => 'kathy.arnold@test.xx', 'last_login' => '2014-09-07 10:24:07', 'amount' => '456.987', 'avatar' => 'avatar/05.png', 'sort' => '140', 'timestamp' => '1418155313'],
9 | ['user_id' => '6', 'action' => '0', 'group_id' => '3', 'name' => 'Jan', 'surname' => 'Wilson', 'email' => 'jan.wilson@test.xx', 'last_login' => '2014-09-03 13:15:22', 'amount' => '123', 'avatar' => 'avatar/06.png', 'sort' => '150', 'timestamp' => '1418255318'],
10 | ['user_id' => '7', 'action' => '0', 'group_id' => '1', 'name' => 'Alberta', 'surname' => 'Erickson', 'email' => 'alberta.erickson@test.xx', 'last_login' => '2014-08-06 13:37:17', 'amount' => '98753.654', 'avatar' => 'avatar/07.png', 'sort' => '110', 'timestamp' => '1418255327'],
11 | ['user_id' => '8', 'action' => '1', 'group_id' => '3', 'name' => 'Ada', 'surname' => 'Wells', 'email' => 'ada.wells@test.xx', 'last_login' => '2014-08-12 11:25:16', 'amount' => '852.3654', 'avatar' => 'avatar/08.png', 'sort' => '70', 'timestamp' => '1418255332'],
12 | ['user_id' => '9', 'action' => '0', 'group_id' => '2', 'name' => 'Ethel', 'surname' => 'Figueroa', 'email' => 'ethel.figueroa@test.xx', 'last_login' => '2014-09-05 10:23:26', 'amount' => '45695.986', 'avatar' => 'avatar/09.png', 'sort' => '20', 'timestamp' => '1418255305'],
13 | ['user_id' => '10', 'action' => '1', 'group_id' => '3', 'name' => 'Ian', 'surname' => 'Goodwin', 'email' => 'ian.goodwin@test.xx', 'last_login' => '2014-09-04 12:26:19', 'amount' => '1236.9852', 'avatar' => 'avatar/10.png', 'sort' => '130', 'timestamp' => '1418255331'],
14 | ['user_id' => '11', 'action' => '1', 'group_id' => '2', 'name' => 'Francis', 'surname' => 'Hayes', 'email' => 'francis.hayes@test.xx', 'last_login' => '2014-09-03 10:16:17', 'amount' => '5498.345', 'avatar' => 'avatar/11.png', 'sort' => '0', 'timestamp' => '1418255293'],
15 | ['user_id' => '12', 'action' => '0', 'group_id' => '1', 'name' => 'Erma', 'surname' => 'Burns', 'email' => 'erma.burns@test.xx', 'last_login' => '2014-07-02 15:42:15', 'amount' => '63287.9852', 'avatar' => 'avatar/12.png', 'sort' => '60', 'timestamp' => '1418255316'],
16 | ['user_id' => '13', 'action' => '1', 'group_id' => '3', 'name' => 'Kristina', 'surname' => 'Jenkins', 'email' => 'kristina.jenkins@test.xx', 'last_login' => '2014-08-20 14:39:43', 'amount' => '74523.96549', 'avatar' => 'avatar/13.png', 'sort' => '40', 'timestamp' => '1418255334'],
17 | ['user_id' => '14', 'action' => '0', 'group_id' => '3', 'name' => 'Virgil', 'surname' => 'Hunt', 'email' => 'virgil.hunt@test.xx', 'last_login' => '2014-08-12 16:09:38', 'amount' => '65654.6549', 'avatar' => 'avatar/14.png', 'sort' => '30', 'timestamp' => '1418255276'],
18 | ['user_id' => '15', 'action' => '1', 'group_id' => '1', 'name' => 'Max', 'surname' => 'Martin', 'email' => 'max.martin@test.xx', 'last_login' => '2014-09-01 12:14:20', 'amount' => '541236.5495', 'avatar' => 'avatar/15.png', 'sort' => '170', 'timestamp' => '1418255317'],
19 | ['user_id' => '16', 'action' => '1', 'group_id' => '2', 'name' => 'Melody', 'surname' => 'Manning', 'email' => 'melody.manning@test.xx', 'last_login' => '2014-09-02 12:26:20', 'amount' => '9871.216', 'avatar' => 'avatar/16.png', 'sort' => '50', 'timestamp' => '1418255281'],
20 | ['user_id' => '17', 'action' => '1', 'group_id' => '3', 'name' => 'Catherine', 'surname' => 'Todd', 'email' => 'catherine.todd@test.xx', 'last_login' => '2014-06-11 15:14:39', 'amount' => '100.2', 'avatar' => 'avatar/17.png', 'sort' => '10', 'timestamp' => '1418255313'],
21 | ['user_id' => '18', 'action' => '0', 'group_id' => '1', 'name' => 'Douglas', 'surname' => 'Stanley', 'email' => 'douglas.stanley@test.xx', 'last_login' => '2014-04-16 15:22:18', 'amount' => '900', 'avatar' => 'avatar/18.png', 'sort' => '90', 'timestamp' => '1418255332'],
22 | ['user_id' => '19', 'action' => '0', 'group_id' => '3', 'name' => 'Patti', 'surname' => 'Diaz', 'email' => 'patti.diaz@test.xx', 'last_login' => '2014-09-11 12:17:16', 'amount' => '1500', 'avatar' => 'avatar/19.png', 'sort' => '80', 'timestamp' => '1418255275'],
23 | ['user_id' => '20', 'action' => '0', 'group_id' => '3', 'name' => 'John', 'surname' => 'Petterson', 'email' => 'john.petterson@test.xx', 'last_login' => '2014-10-10 10:10:10', 'amount' => '2500', 'avatar' => 'avatar/20.png', 'sort' => '190', 'timestamp' => '1418255275']
24 | ];
25 |
26 | $groups = [
27 | ['id' => '2', 'name' => 'Group 2'],
28 | ['id' => '1', 'name' => 'Group 1'],
29 | ['id' => '3', 'name' => 'Group 3'],
30 | ];
31 |
32 | $source = new \Mesour\DataGrid\Sources\ArrayGridSource('users', 'id', $data, [
33 | 'group' => $groups
34 | ]);
35 |
36 | return $source;
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Column/Container.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class Container extends Filtering implements IExportable, IContainer
19 | {
20 |
21 | use Mesour\Icon\HasIcon;
22 |
23 | /**
24 | * @param string $name
25 | * @param string|null $header
26 | * @return Text
27 | */
28 | public function addText($name, $header = null)
29 | {
30 | $column = new Text;
31 | $this->setColumn($column, $name, $header);
32 | return $column;
33 | }
34 |
35 | /**
36 | * @param string $name
37 | * @param string|null $header
38 | * @return Date
39 | */
40 | public function addDate($name, $header = null)
41 | {
42 | $column = new Date;
43 | $this->setColumn($column, $name, $header);
44 | return $column;
45 | }
46 |
47 | /**
48 | * @param string $name
49 | * @param string|null $header
50 | * @return self
51 | */
52 | public function addContainer($name, $header = null)
53 | {
54 | $column = new self;
55 | $column->setFiltering(false)
56 | ->setOrdering(false);
57 | $this->setColumn($column, $name, $header);
58 | return $column;
59 | }
60 |
61 | /**
62 | * @param string $name
63 | * @param string|null $header
64 | * @return Image
65 | */
66 | public function addImage($name, $header = null)
67 | {
68 | $column = new Image;
69 | $this->setColumn($column, $name, $header);
70 | return $column;
71 | }
72 |
73 | /**
74 | * @param string $name
75 | * @param string|null $header
76 | * @return Status
77 | */
78 | public function addStatus($name, $header = null)
79 | {
80 | $column = new Status;
81 | $this->setColumn($column, $name, $header);
82 | return $column;
83 | }
84 |
85 | /**
86 | * @param string $name
87 | * @param string|null $header
88 | * @return Template
89 | */
90 | public function addTemplate($name, $header = null)
91 | {
92 | $column = new Template;
93 | $this->setColumn($column, $name, $header);
94 | return $column;
95 | }
96 |
97 | /**
98 | * @param string $name
99 | * @return Mesour\UI\Button(
100 | * @throws Mesour\InvalidArgumentException
101 | */
102 | public function addButton($name)
103 | {
104 | $button = new Mesour\UI\Button($name);
105 | $button->setSize('btn-sm');
106 | $this->addComponent($button);
107 | return $button;
108 | }
109 |
110 | /**
111 | * @param string $name
112 | * @return Mesour\UI\DropDown
113 | * @throws Mesour\InvalidArgumentException
114 | */
115 | public function addDropDown($name)
116 | {
117 | $dropDown = new Mesour\UI\DropDown($name);
118 | $dropDown->getMainButton()
119 | ->setSize('btn-sm');
120 | $this->addComponent($dropDown);
121 | return $dropDown;
122 | }
123 |
124 | public function attachToFilter(Mesour\DataGrid\Extensions\Filter\IFilter $filter, $hasCheckers)
125 | {
126 | parent::attachToFilter($filter, $hasCheckers);
127 | $item = $filter->addTextFilter($this->getName(), $this->getHeader());
128 | $this->setUpFilterItem($item, $hasCheckers);
129 | }
130 |
131 | protected function setColumn(Render\IColumn $column, $name, $header = null)
132 | {
133 | $column->setHeader($header);
134 | return $this[$name] = $column;
135 | }
136 |
137 | public function getHeaderAttributes()
138 | {
139 | return [
140 | 'class' => 'grid-column-' . $this->getName() . ' column-container',
141 | ];
142 | }
143 |
144 | public function getBodyAttributes($data, $need = true, $rawData = [])
145 | {
146 | return parent::getBodyAttributes($data, false, $rawData);
147 | }
148 |
149 | public function getBodyContent($data, $rawData, $export = false)
150 | {
151 | if (
152 | !isset($data->{$this->getName()})
153 | && (property_exists($data, $this->getName()) && !is_null($data->{$this->getName()}))
154 | && ($this->hasFiltering() || $this->hasOrdering())
155 | ) {
156 | throw new Mesour\OutOfRangeException('Column with name ' . $this->getName() . ' does not exists in data source.');
157 | }
158 |
159 | $onlyButtons = true;
160 | $container = Mesour\Components\Utils\Html::el('span', ['class' => 'container-content']);
161 | foreach ($this as $control) {
162 | if (!$control instanceof Mesour\UI\Button && !$control instanceof Mesour\UI\DropDown) {
163 | $onlyButtons = false;
164 | }
165 | $span = Mesour\Components\Utils\Html::el('span');
166 |
167 | if ($control instanceof Render\IColumn) {
168 | $span->addAttributes($control->getHeaderAttributes());
169 | $span->addAttributes($control->getBodyAttributes($data));
170 | }
171 |
172 | $fromCallback = $this->tryInvokeCallback([$this, $rawData, $span, $control]);
173 |
174 | if ($fromCallback === self::NO_CALLBACK) {
175 | if ($control instanceof Render\IColumn) {
176 | $content = $control->getBodyContent($data, $rawData);
177 | if (!is_null($content)) {
178 | $span->add($content);
179 | }
180 | } elseif ($control instanceof Mesour\Components\Control\IOptionsControl) {
181 | $control->setOption('data', $data);
182 | $span->add($control->create());
183 | } else {
184 | $span->add($control->render());
185 | }
186 | } else {
187 | $span->add($fromCallback);
188 | }
189 |
190 | $container->add($span);
191 | $container->add(' ');
192 | }
193 | if ($onlyButtons) {
194 | $container->class('only-buttons', true);
195 | }
196 | return $export ? trim(strip_tags($container)) : $container;
197 | }
198 |
199 | }
200 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/SubItem/SubItemExtension.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class SubItemExtension extends Extensions\Base implements ISubItem
19 | {
20 |
21 | use Mesour\Components\Localization\Translatable;
22 | use Mesour\Components\Security\Authorised;
23 |
24 | private $items = [];
25 |
26 | /** @var Mesour\Components\Session\ISessionSection */
27 | private $privateSession;
28 |
29 | public function attached(Mesour\Components\ComponentModel\IContainer $parent)
30 | {
31 | parent::attached($parent);
32 | $this->privateSession = $this->getSession()->getSection($this->createLinkName());
33 | return $this;
34 | }
35 |
36 | public function getPageLimit()
37 | {
38 | $parent = $this->getGrid();
39 | $pager = $parent->getExtension('IPager', false);
40 | if ($pager instanceof Extensions\Pager\IPager) {
41 | return $pager->getPaginator()->getItemsPerPage();
42 | }
43 | return $parent->getSource()->getTotalCount();
44 | }
45 |
46 | /**
47 | * @param string $name
48 | * @param null|string $description
49 | * @param Mesour\UI\DataGrid $grid
50 | * @return Extensions\SubItem\Items\GridItem
51 | */
52 | public function addGridItem($name, $description, Mesour\UI\DataGrid $grid)
53 | {
54 | $this->check($name);
55 | $item = new Extensions\SubItem\Items\GridItem($this, $name, $this->getTranslator()->translate($description), $grid);
56 | $this->items[$name] = $item;
57 | return $item;
58 | }
59 |
60 | public function addTemplateItem($name, $description)
61 | {
62 | $this->check($name);
63 | $item = new Extensions\SubItem\Items\TemplateItem($this, $name, $this->getTranslator()->translate($description));
64 | $this->items[$name] = $item;
65 | return $item;
66 | }
67 |
68 | public function addComponentItem($name, $description, $callback)
69 | {
70 | if (!$callback instanceof Mesour\Components\Control\IControl) {
71 | Mesour\Components\Utils\Helpers::checkCallback($callback);
72 | }
73 | $this->check($name);
74 | $item = new Extensions\SubItem\Items\ComponentItem($this, $name, $this->getTranslator()->translate($description), $callback);
75 | $this->items[$name] = $item;
76 | return $item;
77 | }
78 |
79 | public function addCallbackItem($name, $description)
80 | {
81 | $this->check($name);
82 | $item = new Extensions\SubItem\Items\CallbackItem($this, $name, $this->getTranslator()->translate($description));
83 | $this->items[$name] = $item;
84 | return $item;
85 | }
86 |
87 | public function setPermission($resource, $privilege)
88 | {
89 | $this->setPermissionCheck($resource, $privilege);
90 | return $this;
91 | }
92 |
93 | private function check($name)
94 | {
95 | if (isset($this->items[$name])) {
96 | throw new Mesour\InvalidStateException('Sub item with name ' . $this->items[$name] . ' is already exist.');
97 | }
98 | }
99 |
100 | public function reset($hard = false)
101 | {
102 | $this->privateSession->set('settings', []);
103 | foreach ($this->items as $item) {
104 | /** @var Extensions\SubItem\Items\Item $item */
105 | $item->reset();
106 | }
107 | }
108 |
109 | public function getOpened()
110 | {
111 | $output = [];
112 | $settings = $this->privateSession->get('settings', []);
113 | foreach ($settings as $name => $value) {
114 | if (!isset($this->items[$name])) {
115 | unset($settings[$name]);
116 | continue;
117 | }
118 | /** @var Extensions\SubItem\Items\Item $item */
119 | $item = $this->items[$name];
120 |
121 | $keys = $item->getKeys();
122 | if (count($keys) > 0 && count($keys) < count($settings[$name])) {
123 | while (count($keys) < count($settings[$name])) {
124 | array_shift($settings[$name]);
125 | }
126 | }
127 | foreach ($settings[$name] as $key => $i) {
128 | if (!isset($settings[$name][$key])) {
129 | unset($settings[$name][$key]);
130 | continue;
131 | }
132 | if (!$item->hasKey($settings[$name][$key])) {
133 | $item->addAlias($i, end($keys));
134 | }
135 | $output[$name]['keys'][] = $settings[$name][$key];
136 | if (!isset($output[$name]['item'])) {
137 | $output[$name]['item'] = $this->items[$name];
138 | }
139 | }
140 | }
141 | $this->privateSession->set('settings', $settings);
142 | return $output;
143 | }
144 |
145 | public function getItem($name)
146 | {
147 | if (!isset($this->items[$name])) {
148 | throw new Mesour\InvalidStateException('Item ' . $name . ' does not exist.');
149 | }
150 | return $this->items[$name];
151 | }
152 |
153 | public function getNames()
154 | {
155 | return array_keys($this->items);
156 | }
157 |
158 | public function getItemsCount()
159 | {
160 | return count($this->items);
161 | }
162 |
163 | public function getItems()
164 | {
165 | return $this->items;
166 | }
167 |
168 | public function hasSubItems()
169 | {
170 | return count($this->items) > 0;
171 | }
172 |
173 | public function handleToggleItem($key, $name)
174 | {
175 | $settings = $this->privateSession->get('settings', []);
176 | if (isset($settings[$name])) {
177 | if (in_array($key, $settings[$name])) {
178 | unset($settings[$name][array_search($key, $settings[$name])]);
179 | } else {
180 | $settings[$name][] = $key;
181 | }
182 | } else {
183 | $settings[$name][] = $key;
184 | }
185 | $this->privateSession->set('settings', $settings);
186 | }
187 |
188 | public function gridCreate($data = [])
189 | {
190 | $this->create();
191 | }
192 |
193 | }
194 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/SubItemGrid.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | abstract class SubItemGrid extends ExtendedGrid
18 | {
19 |
20 | private $subItems = [];
21 |
22 | /**
23 | * @return Extensions\SubItem\SubItemExtension
24 | */
25 | public function enableSubItems()
26 | {
27 | $this->addComponent(new Mesour\UI\Control, 'subCol');
28 | $this->addComponent(new Mesour\UI\Control, 'subButton');
29 | return $this->getExtension('ISubItem');
30 | }
31 |
32 | public function create($data = [])
33 | {
34 | $subItem = $this->getExtension('ISubItem');
35 | $this->onRender[] = function (Mesour\UI\DataGrid $dataGrid, $rawData, $fullData) use ($subItem) {
36 | if ($subItem instanceof Extensions\SubItem\ISubItem && $subItem->hasSubItems() && $subItem->isAllowed() && !$subItem->isDisabled()) {
37 | $opened = $subItem->getOpened();
38 | foreach ($fullData as $key => $rowData) {
39 | foreach ($opened as $name => $item) {
40 | if (in_array((string) $key, $item['keys'])) {
41 | /** @var Extensions\SubItem\Items\Item $instance */
42 | $instance = $item['item'];
43 | $translatedKey = $instance->getTranslatedKey($key);
44 | $instance->invoke([$rawData[$key]], $name, $translatedKey);
45 | if (isset($this[$name . $translatedKey])) {
46 | $this->subItems[$key][$name] = $this[$name . $translatedKey];
47 | } else {
48 | $this->subItems[$key][$name] = true;
49 | }
50 | }
51 | }
52 | }
53 | }
54 | };
55 | $this->onAfterRenderRow[] = function ($body, $key, $rawData, $rowData) use ($subItem) {
56 | if ($subItem instanceof Extensions\SubItem\ISubItem && $subItem->isAllowed() && !$subItem->isDisabled()) {
57 | foreach ($subItem->getItems() as $name => $item) {
58 | /** @var Extensions\SubItem\Items\Item $item */
59 | $item->check($rawData);
60 | if (isset($this->subItems[$key][$name])) {
61 | $this->addOpenedSubItemRow($body, $rowData, $name, $key, $item, $rawData);
62 | } else {
63 | $this->addClosedSubItemRow($body, $rowData, $name, $key, $item, $rawData);
64 | }
65 | }
66 | }
67 | };
68 |
69 | return parent::create($data);
70 | }
71 |
72 | protected function setSubItemColumn(Column\IColumn $column, $name, $header = null)
73 | {
74 | $column->setHeader($header);
75 | return $this['subCol'][$name] = $column;
76 | }
77 |
78 | protected function setSubItemButton(Column\IColumn $column, $name, $header = null)
79 | {
80 | $column->setHeader($header);
81 | return $this['subButton'][$name] = $column;
82 | }
83 |
84 | protected function addClosedSubItemRow(Mesour\Table\Render\Body &$body, $rowData, $name, $key, Extensions\SubItem\Items\Item $item, $rawData)
85 | {
86 | if ($item->isDisabled() || !$item->isAllowed()) {
87 | return;
88 | }
89 |
90 | $columnsCount = count($this->getColumns());
91 | $oldName = $name;
92 |
93 | $name = $this->getExtension('ISubItem')->getItem($oldName)->getName() . $key;
94 | $column = new Column\SubItemButton($name);
95 | $column->setColumnName($oldName)
96 | ->setKey($key);
97 |
98 | $this->setSubItemButton($column, $name);
99 |
100 | $rendererFactory = $this->getRendererFactory();
101 | $cell = $rendererFactory->createCell(1, $column, $rawData);
102 | $row = $rendererFactory->createRow($rowData, $rawData);
103 | $row->addCell($cell);
104 | $columnsCount--;
105 |
106 | $subItemColumn = new Column\SubItem();
107 | $subItemColumn->setText($this->getExtension('ISubItem')->getItem($oldName)->getDescription());
108 |
109 | $this->setSubItemColumn($subItemColumn, $name);
110 | $cell = $rendererFactory->createCell($columnsCount, $subItemColumn, $rawData);
111 |
112 | $row->setAttribute('class', 'no-sort');
113 | $row->addCell($cell);
114 | $body->addRow($row);
115 | }
116 |
117 | protected function addOpenedSubItemRow(Mesour\Table\Render\Body &$body, $rowData, $name, $key, Extensions\SubItem\Items\Item $item, $rawData)
118 | {
119 | if ($item->isDisabled() || !$item->isAllowed()) {
120 | return;
121 | }
122 |
123 | $columnsCount = count($this->getColumns());
124 | $columnsCount--;
125 | $oldName = $name;
126 |
127 | $name = $this->getExtension('ISubItem')->getItem($oldName)->getName() . $key;
128 | $content = $this->getExtension('ISubItem')->getItem($oldName)->render($key, $rowData, $rawData);
129 |
130 | $column = new Column\SubItemButton($name);
131 | $column->setColumnName($oldName)
132 | ->setKey($key)
133 | ->setTwoRows()
134 | ->setOpened(true);
135 |
136 | $this->setSubItemButton($column, $name);
137 |
138 | $rendererFactory = $this->getRendererFactory();
139 |
140 | $subItem = new Column\SubItem;
141 | $subItem->setText($content);
142 |
143 | $this->setSubItemColumn($subItem, $name);
144 | $cell = $rendererFactory->createCell($columnsCount, $subItem, $rawData);
145 |
146 | $row = $rendererFactory->createRow($rowData, $rawData);
147 | $row->setAttribute('class', 'no-sort');
148 | $row->addCell($cell);
149 |
150 | $currentRow = $rendererFactory->createRow($rowData, $rawData);
151 | $description = new Column\SubItem;
152 | $description->setText($this->getExtension('ISubItem')->getItem($oldName)->getDescription());
153 |
154 | $this->setSubItemColumn($description, $name . '_des');
155 | $subItemCell = $rendererFactory->createCell($columnsCount, $description, $rawData);
156 |
157 | $currentRow->addCell($rendererFactory->createCell(1, $column, $rawData));
158 | $currentRow->addCell($subItemCell);
159 | $currentRow->setAttribute('class', 'no-sort');
160 | $body->addRow($currentRow);
161 | $body->addRow($row);
162 | }
163 |
164 | }
165 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/ExtensionStorage.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class ExtensionStorage
18 | {
19 |
20 | /**
21 | * @var Mesour\DataGrid\Extensions\IExtension[]|BaseGrid
22 | */
23 | private $parent;
24 |
25 | protected $extensions = [
26 | 'IPager' => [
27 | 'name' => 'pager',
28 | 'interface' => Mesour\DataGrid\Extensions\Pager\IPager::class,
29 | 'class' => Mesour\DataGrid\Extensions\Pager\PagerExtension::class,
30 | ],
31 | 'IFilter' => [
32 | 'name' => 'filter',
33 | 'interface' => Mesour\DataGrid\Extensions\Filter\IFilter::class,
34 | 'class' => Mesour\DataGrid\Extensions\Filter\FilterExtension::class,
35 | ],
36 | 'ISimpleFilter' => [
37 | 'name' => 'filter',
38 | 'interface' => Mesour\DataGrid\Extensions\SimpleFilter\ISimpleFilter::class,
39 | 'class' => Mesour\DataGrid\Extensions\SimpleFilter\SimpleFilterExtension::class,
40 | ],
41 | 'ISortable' => [
42 | 'name' => 'sortable',
43 | 'interface' => Mesour\DataGrid\Extensions\Sortable\ISortable::class,
44 | 'class' => Mesour\DataGrid\Extensions\Sortable\SortableExtension::class,
45 | ],
46 | 'ISelection' => [
47 | 'name' => 'selection',
48 | 'interface' => Mesour\DataGrid\Extensions\Selection\ISelection::class,
49 | 'class' => Mesour\DataGrid\Extensions\Selection\SelectionExtension::class,
50 | ],
51 | 'IEditable' => [
52 | 'name' => 'editable',
53 | 'interface' => Mesour\DataGrid\Extensions\Editable\IEditable::class,
54 | 'class' => Mesour\DataGrid\Extensions\Editable\EditableExtension::class,
55 | ],
56 | 'IOrdering' => [
57 | 'name' => 'ordering',
58 | 'interface' => Mesour\DataGrid\Extensions\Ordering\IOrdering::class,
59 | 'class' => Mesour\DataGrid\Extensions\Ordering\OrderingExtension::class,
60 | ],
61 | 'IExport' => [
62 | 'name' => 'export',
63 | 'interface' => Mesour\DataGrid\Extensions\Export\IExport::class,
64 | 'class' => Mesour\DataGrid\Extensions\Export\ExportExtension::class,
65 | ],
66 | 'ISubItem' => [
67 | 'name' => 'sub_item',
68 | 'interface' => Mesour\DataGrid\Extensions\SubItem\ISubItem::class,
69 | 'class' => Mesour\DataGrid\Extensions\SubItem\SubItemExtension::class,
70 | ],
71 | ];
72 |
73 | public function __construct(BaseGrid $parent)
74 | {
75 | $this->setParent($parent);
76 | }
77 |
78 | public function setParent(BaseGrid $parent)
79 | {
80 | $this->parent = $parent;
81 | }
82 |
83 | public function addNewExtension($name, $componentName, $interface, $className)
84 | {
85 | if (isset($this->extensions[$name])) {
86 | throw new Mesour\InvalidStateException('Extension with name already exists.');
87 | }
88 | if (!interface_exists($interface)) {
89 | throw new Mesour\InvalidStateException('Interface "' . $interface . '" does not exists.');
90 | }
91 | if (!class_exists($className)) {
92 | throw new Mesour\InvalidStateException('Class "' . $className . '" does not exists.');
93 | }
94 |
95 | $checks = [
96 | 'name' => [
97 | 'value' => $componentName,
98 | 'exception_name' => 'component name',
99 | ],
100 | 'interface' => [
101 | 'value' => $interface,
102 | ],
103 | 'class' => [
104 | 'value' => $className,
105 | 'exception_name' => 'class name',
106 | ],
107 | ];
108 |
109 | foreach ($checks as $key => $check) {
110 | foreach ($this->extensions as $extension) {
111 | if ($extension[$key] === $check['value']) {
112 | throw new Mesour\InvalidStateException('Extension with "'
113 | . (isset($check['exception_name']) ? $check['exception_name'] : $key) . '" already exists.');
114 | }
115 | }
116 | }
117 |
118 | $this->extensions[$name] = [
119 | 'name' => $componentName,
120 | 'interface' => $interface,
121 | 'class' => $className,
122 | ];
123 |
124 | return $this;
125 | }
126 |
127 | /**
128 | * @param Extensions\IExtension $extension
129 | * @param string $extensionName
130 | * @return Extensions\IExtension
131 | * @throws Mesour\InvalidStateException
132 | * @throws Mesour\InvalidArgumentException
133 | */
134 | public function set(Extensions\IExtension $extension, $extensionName)
135 | {
136 | if (!isset($this->extensions[$extensionName])) {
137 | throw new Mesour\InvalidStateException('Extension name "' . $extensionName . '" does not exists.');
138 | } else {
139 | if (!$extension instanceof $this->extensions[$extensionName]['interface']) {
140 | throw new Mesour\InvalidArgumentException(
141 | 'Extension must implement interface ' . $this->extensions[$extensionName]['interface'] . ' for extension "' . $extensionName . '".'
142 | );
143 | }
144 | }
145 | $extName = $this->getExtensionName($extension);
146 | if ($extName) {
147 | if (strlen($extension->getName()) === 0) {
148 | throw new Mesour\InvalidStateException('Extension must have a set name.');
149 | }
150 |
151 | /** @var Extensions\IExtension $extension */
152 | $extension->createInstance($extension, $extensionName);
153 |
154 | $this->extensions[$extName]['name'] = $extension->getName();
155 |
156 | return $extension;
157 | } else {
158 | throw new Mesour\OutOfRangeException('Trying to set unknown extension.');
159 | }
160 | }
161 |
162 | public function get($extensionName, $need = true)
163 | {
164 | if (!is_string($extensionName)) {
165 | throw new Mesour\InvalidArgumentException('Extension must be string.');
166 | }
167 | if (isset($this->extensions[$extensionName])) {
168 | $name = $this->extensions[$extensionName]['name'];
169 | $class = $this->extensions[$extensionName]['class'];
170 | $extension = $this->parent->getComponent($name, false);
171 | if (is_null($extension) && $need) {
172 | $instance = $this->set(new $class($name, $this->parent), $extensionName);
173 | if (!isset($this->parent[$name])) {
174 | $this->parent->addComponent($instance);
175 | }
176 | } elseif (is_null($extension)) {
177 | return null;
178 | }
179 | return $this->parent[$name];
180 | } else {
181 | if ($need) {
182 | throw new Mesour\OutOfRangeException('Trying to get unknown extension.');
183 | } else {
184 | return null;
185 | }
186 | }
187 | }
188 |
189 | /**
190 | * @return Extensions\IExtension[]
191 | */
192 | public function getActiveExtensions()
193 | {
194 | $output = [];
195 | foreach ($this->extensions as $interface => $option) {
196 | if (
197 | isset($this->parent[$option['name']])
198 | && !$this->parent[$option['name']]->isDisabled()
199 | && $this->parent[$option['name']]->isAllowed()
200 | ) {
201 | $output[$option['name']] = $this->parent[$option['name']];
202 | }
203 | }
204 | return $output;
205 | }
206 |
207 | /**
208 | * @param Extensions\IExtension $extension
209 | * @return string
210 | */
211 | private function getExtensionName(Extensions\IExtension $extension)
212 | {
213 | foreach ($this->extensions as $name => $ext) {
214 | if (is_subclass_of($extension, $ext['interface'])) {
215 | return $name;
216 | }
217 | }
218 | return false;
219 | }
220 |
221 | }
222 |
--------------------------------------------------------------------------------
/examples/basic.php:
--------------------------------------------------------------------------------
1 | addDirectory(__DIR__ . '/../src');
15 | $loader->setCacheStorage(new Nette\Caching\Storages\FileStorage(__DIR__ . '/temp'));
16 | $loader->register();
17 |
18 | ?>
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
Basic functionality
37 |
38 |
39 |
40 | setRequest($_REQUEST);
50 |
51 | $application->setUserRole('registered');
52 |
53 | $auth = $application->getAuthorizator();
54 |
55 | $auth->addRole('guest');
56 | $auth->addRole('registered', 'guest');
57 |
58 | $auth->addResource('menu');
59 |
60 | $auth->allow('guest', 'menu', ['first', 'second']);
61 | $auth->allow('registered', 'menu');
62 | $auth->deny('registered', 'menu', 'second');
63 |
64 | $grid = new \Mesour\UI\DataGrid('basicDataGrid', $application);
65 |
66 | $wrapper = $grid->getWrapperPrototype();
67 |
68 | $wrapper->class('my-class');
69 |
70 | // TRUE = append
71 | $wrapper->class('my-next-class', true);
72 |
73 | /** @var \Mesour\DataGrid\Sources\IGridSource $source */
74 | $source = require_once __DIR__ . '/sources/' . $sourceFile . '.php';
75 |
76 | $dataStructure = $source->getDataStructure();
77 |
78 | $dataStructure->addManyToOne('group', 'groups', 'group_id', '{name} ({type})');
79 |
80 | $grid->setSource($source);
81 |
82 | $pager = $grid->enablePager(8);
83 |
84 | $filter = $grid->enableFilter();
85 |
86 | $selection = $grid->enableRowSelection();
87 |
88 | $selection = $selection->getLinks();
89 |
90 | $selection->addHeader('Active');
91 |
92 | $selection->addLink('Active')// add selection link
93 | ->onCall[] = function () {
94 | dump('ActivateSelected', func_get_args());
95 | };
96 |
97 | $selection->addLink('Unactive')
98 | ->setAjax(false)// disable AJAX
99 | ->onCall[] = function () {
100 | dump('InactivateSelected', func_get_args());
101 | };
102 |
103 | $selection->addDivider();
104 |
105 | $selection->addLink('Delete')
106 | ->setConfirm('Really delete all selected users?')// set confirm text
107 | ->onCall[] = function () {
108 | dump('DeleteSelected', func_get_args());
109 | };
110 |
111 | $sortable = $grid->enableSortable('sort');
112 |
113 | $export = $grid->enableExport(__DIR__ . '/temp');
114 |
115 | $status = $grid->addStatus('action', 'S')
116 | ->setPermission('menu', 'second');
117 |
118 | $status->addButton('active')
119 | ->setStatus(1, 'Active', 'All active')
120 | ->setIcon('check-circle-o')
121 | ->setType('success')
122 | ->setAttribute('href', '#');
123 |
124 | $status->addButton('inactive')
125 | ->setStatus(0, 'Inactive', 'All inactive')
126 | ->setIcon('times-circle-o')
127 | ->setType('danger')
128 | ->setAttribute('href', '#');
129 |
130 | $grid->addText('name', 'Name');
131 |
132 | $grid->addText('email', 'E-mail');
133 |
134 | $grid->addText('group_name', 'Group');
135 |
136 | $grid->addText('group', 'Group');
137 |
138 | $grid->addNumber('amount', 'Amount')
139 | ->setUnit('CZK');
140 |
141 | $container = $grid->addContainer('blablablablablabla', 'Actions');
142 |
143 | //$container->setDisabled();
144 |
145 | $button = $container->addButton('test_button');
146 |
147 | $button->setIcon('pencil')
148 | ->setType('primary')
149 | ->setAttribute('href', $button->link('http://mesour.com'))
150 | ->setAttribute('target', '_blank');
151 |
152 | $dropDown = $container->addDropDown('test_drop_down')
153 | ->setPullRight()
154 | ->setAttribute('class', 'dropdown');
155 |
156 | $dropDown->addHeader('Test header');
157 |
158 | $first = $dropDown->addButton();
159 |
160 | $first->setText('First button')
161 | ->setAttribute('href', $dropDown->link('/first/'));
162 |
163 | $dropDown->addDivider();
164 |
165 | $dropDown->addHeader('Test header 2');
166 |
167 | $dropDown->addButton()
168 | ->setText('Second button')
169 | ->setConfirm('Test confirm :-)')
170 | ->setAttribute('href', $dropDown->link('/second/'));
171 |
172 | $dropDown->addButton()
173 | ->setText('Third button')
174 | ->setAttribute('href', $dropDown->link('/third/'));
175 |
176 | $mainButton = $dropDown->getMainButton();
177 |
178 | $mainButton->setText('Actions')
179 | ->setType('danger');
180 |
181 | // TEST -------------------------------------------------
182 |
183 | $grid->onRenderBody[] = function(
184 | \Mesour\Table\Render\Table\Body $body,
185 | \Mesour\Table\Render\Table\RendererFactory $rendererFactory,
186 | $rawData,
187 | $data
188 | ) use ($grid) {
189 | $myCustomRowData = [
190 | 'some_key' => 'value',
191 | ];
192 |
193 | $row = $rendererFactory->createRow([], $myCustomRowData);
194 |
195 | $column = new \Mesour\DataGrid\Column\Text('test_column', $grid);
196 |
197 | $column->setCallback(function($column, $myCustomRowData) {
198 | return $myCustomRowData['some_key'];
199 | });
200 |
201 | $cell = $rendererFactory->createCell([], $column, $myCustomRowData);
202 |
203 | $row->addCell($cell);
204 |
205 | $body->addRow($row);
206 | };
207 |
208 | // / TEST -----------------------------------------------
209 |
210 | $time_end = microtime(true);
211 | $time = $time_end - $time_start;
212 |
213 | echo "
Execution time (before render): " . number_format($time, 3, ',', ' ') . " seconds
";
214 |
215 | echo $grid->render();
216 |
217 | $time_end = microtime(true);
218 | $time = $time_end - $time_start;
219 |
220 | echo "
Execution time (after render): " . number_format($time, 3, ',', ' ') . " seconds
";
221 |
222 | ?>
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
--------------------------------------------------------------------------------
/src/Mesour/DataGrid/Extensions/Export/ExportExtension.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class ExportExtension extends Mesour\DataGrid\Extensions\Base implements IExport
18 | {
19 |
20 | use Mesour\Components\Security\Authorised;
21 |
22 | /**
23 | * Cache directory
24 | * @var string
25 | */
26 | private $cache_dir;
27 |
28 | /**
29 | * Restriction for some columns
30 | * @var array
31 | */
32 | private $exportColumns = [];
33 |
34 | /** @var string|null */
35 | private $fileName = null;
36 |
37 | private $delimiter = ',';
38 |
39 | private $file_path;
40 |
41 | /** @var Mesour\UI\Button */
42 | private $createdExport;
43 |
44 | public function setFileName($fileName)
45 | {
46 | if (!is_string($fileName) && !is_null($fileName)) {
47 | throw new Mesour\InvalidArgumentException(
48 | sprintf('Export file name must be string, %s given.', gettype($fileName))
49 | );
50 | }
51 | $this->fileName = $fileName;
52 | return $this;
53 | }
54 |
55 | public function setDelimiter($delimiter = ',')
56 | {
57 | $this->delimiter = $delimiter;
58 | return $this;
59 | }
60 |
61 | public function setCacheDir($dir)
62 | {
63 | if (!is_dir($dir)) {
64 | throw new Mesour\DirectoryNotFoundException('Cache dir is not a directory.');
65 | }
66 | if (!is_writable($dir)) {
67 | throw new Mesour\InvalidStateException('Cache dir is not a writable.');
68 | }
69 | $this->cache_dir = $dir;
70 | return $this;
71 | }
72 |
73 | public function setColumns(array $columns = [])
74 | {
75 | $this->exportColumns = $columns;
76 | return $this;
77 | }
78 |
79 | /**
80 | * @return Mesour\UI\Button|Mesour\UI\DropDown
81 | */
82 | public function getExportButton()
83 | {
84 | if (!isset($this['button'])) {
85 | $filter = $this->getGrid()->getExtension('IFilter', false);
86 | if ($filter instanceof Mesour\DataGrid\Extensions\Filter\IFilter) {
87 | $this['button'] = $dropdown = new Mesour\UI\DropDown;
88 |
89 | $dropdown->setAttribute('class', 'show-export', true)
90 | ->setPullRight();
91 |
92 | if ($filter instanceof Mesour\DataGrid\Extensions\Filter\IFilter) {
93 | $dropdown->addButton('Export filtered')
94 | ->setAttribute('href', $this->createLink('export', ['type' => 'filtered']));
95 | }
96 |
97 | $dropdown->addDivider();
98 | $dropdown->addButton('Export all')
99 | ->setAttribute('href', $this->createLink('export'));
100 |
101 | $button = $dropdown->getMainButton();
102 | } else {
103 | $this['button'] = $button = new Mesour\UI\Button;
104 | $button->setAttribute('class', 'show-export', true)
105 | ->setAttribute('href', $this->createLink('export'));
106 | }
107 | $button->setType('primary')
108 | ->setText('Export');
109 |
110 | }
111 | return $this['button'];
112 | }
113 |
114 | public function gridCreate($data = [])
115 | {
116 | parent::create();
117 |
118 | $this->createdExport = $this->getExportButton();
119 |
120 | $selection = $this->getGrid()->getExtension('ISelection', false);
121 |
122 | if ($selection instanceof Mesour\DataGrid\Extensions\Selection\ISelection) {
123 | if (count($selection->getLinks()->getLinks())) {
124 | $selection->getLinks()
125 | ->addDivider();
126 | }
127 | $selection->getLinks()
128 | ->addLink('Export to CSV')
129 | ->setAjax(false)
130 | ->onCall[] = function (array $selectedItems) {
131 | $ids = array_keys($selectedItems, 'true');
132 | $this->handleExport('selected', $ids);
133 | };
134 | }
135 | }
136 |
137 | public function attachToRenderer(Mesour\DataGrid\Renderer\IGridRenderer $renderer, $data = [], $rawData = [])
138 | {
139 | $this->createdExport->setOption('data', $data);
140 | $renderer->setComponent('export', $this->createdExport->create());
141 | }
142 |
143 | public function hasExport(Mesour\Components\ComponentModel\IContainer $column)
144 | {
145 | return ($column instanceof Mesour\DataGrid\Column\IExportable);
146 | }
147 |
148 | public function handleExport($type = 'all', array $selectedIds = [])
149 | {
150 | if ($this->isDisabled()) {
151 | throw new Mesour\InvalidStateException('Cannot edit cell if extension is disabled.');
152 | }
153 |
154 | /** @var Mesour\DataGrid\Column\IColumn|array|Mesour\DataGrid\Column\BaseColumn $column */
155 | $headerArr = [];
156 | $exportColumns = [];
157 |
158 | if (empty($this->exportColumns)) {
159 | foreach ($this->getGrid()->getColumns() as $column) {
160 | if ($this->hasExport($column) && $column->isAllowed()) {
161 | $exportColumns[] = $column;
162 | }
163 | }
164 | } else {
165 | foreach ($this->exportColumns as $columnVal) {
166 | $usedInColumns = false;
167 | foreach ($this->getGrid()->getColumns() as $column) {
168 | /** @var Mesour\DataGrid\Column\BaseColumn $column */
169 | if ($this->hasExport($column) && $column->isAllowed()) {
170 | if (is_array($columnVal)) {
171 | $columnName = key($columnVal);
172 | } else {
173 | $columnName = $columnVal;
174 | }
175 | if ($columnName === $column->getName()) {
176 | $exportColumns[] = $column;
177 | $usedInColumns = true;
178 | }
179 | }
180 | }
181 | if ($usedInColumns === false) {
182 | $exportColumns[] = $columnVal;
183 | }
184 | }
185 | }
186 | $this->file_path = $this->createFilePath();
187 | $file = fopen($this->file_path, 'w');
188 | fputs($file, chr(0xEF) . chr(0xBB) . chr(0xBF)); // add BOM to fix UTF-8 in Excel
189 | foreach ($exportColumns as $column) {
190 | if ($column instanceof Mesour\DataGrid\Column\IColumn) {
191 | $headerArr[] = $column->getHeader();
192 | } else {
193 | if (is_array($column)) {
194 | $headerArr[] = reset($column);
195 | } else {
196 | $headerArr[] = $column;
197 | }
198 | }
199 | }
200 | fputcsv($file, $headerArr, $this->delimiter);
201 | $first = true;
202 | $exportData = [];
203 | switch ($type) {
204 | case 'selected':
205 | $source = $this->getGrid()->getSource();
206 | $allData = $source->fetchAll();
207 |
208 | foreach ($selectedIds as $selectedId) {
209 | foreach ($allData as $currentData) {
210 | if ($selectedId == $currentData[$source->getPrimaryKey()]) {
211 | $exportData[] = $currentData;
212 | break;
213 | }
214 | }
215 | }
216 | break;
217 | case 'filtered':
218 | $exportData = $this->getGrid()->getSource()->fetchForExport();
219 | break;
220 | default:
221 | $exportData = $this->getGrid()->getSource()->fetchFullData();
222 | }
223 | $rawData = $this->getGrid()->getSource()->fetchLastRawRows();
224 | foreach ($exportData as $key => $data) {
225 | $lineData = [];
226 | foreach ($exportColumns as $column) {
227 | if ($column instanceof Mesour\DataGrid\Column\IColumn) {
228 | $lineData[] = strip_tags($column->getBodyContent($data, $rawData[$key], true));
229 | } else {
230 | if (is_array($column)) {
231 | $columnName = key($column);
232 | } else {
233 | $columnName = $column;
234 | }
235 | if ($first && !isset($data[$columnName]) && !is_null($data[$columnName])) {
236 | throw new Mesour\OutOfRangeException('Column "' . $columnName . '" does not exist in data.');
237 | }
238 | $lineData[] = strip_tags($data[$columnName]);
239 | }
240 | }
241 | fputcsv($file, $lineData, $this->delimiter);
242 | $first = false;
243 | }
244 | fclose($file);
245 |
246 | $this->download($this->file_path, (is_null($this->fileName) ? $this->getGrid()->createLinkName() : $this->fileName) . '.csv');
247 | }
248 |
249 | protected function createFilePath()
250 | {
251 | return sprintf(
252 | $this->cache_dir . '/%s%s.csv',
253 | Mesour\Components\Utils\Helpers::webalize($this->getGrid()->createLinkName()),
254 | time()
255 | );
256 | }
257 |
258 | private function download($filePath, $fileName)
259 | {
260 | ob_clean();
261 | header('Content-Type: applications/octet-stream');
262 | header('Content-Disposition: attachment; filename="' . $fileName . '"');
263 | header('Cache-Control: private, max-age=0, must-revalidate');
264 | header('Pragma: public');
265 | echo file_get_contents($filePath);
266 | exit(1);
267 | }
268 |
269 | public function __destruct()
270 | {
271 | if (is_file($this->file_path)) {
272 | unlink($this->file_path);
273 | }
274 | }
275 |
276 | }
277 |
--------------------------------------------------------------------------------
/examples/editable.php:
--------------------------------------------------------------------------------
1 | addDirectory(__DIR__ . '/../src');
15 | $loader->setCacheStorage(new Nette\Caching\Storages\FileStorage(__DIR__ . '/temp'));
16 | $loader->register();
17 |
18 | ?>
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
34 |
35 |
36 |
37 |
38 |
Basic functionality
39 |
40 |
41 |
42 | getConfiguration()
52 | ->setTempDir(__DIR__ . '/temp');
53 |
54 | $application->setRequest($_REQUEST);
55 |
56 | $application->getUser()->setRoles('registered');
57 |
58 | $auth = $application->getAuthorizator();
59 |
60 | $auth->addRole('guest');
61 | $auth->addRole('registered', 'guest');
62 |
63 | $auth->addResource('menu');
64 |
65 | $auth->allow('guest', 'menu', ['first', 'second']);
66 | $auth->allow('registered', 'menu');
67 | $auth->deny('registered', 'menu', 'second');
68 |
69 | $grid = new \Mesour\UI\DataGrid('basicDataGrid', $application);
70 |
71 | $wrapper = $grid->getWrapperPrototype();
72 |
73 | $wrapper->class('my-class');
74 |
75 | // TRUE = append
76 | $wrapper->class('my-next-class', true);
77 |
78 | /** @var \Mesour\DataGrid\Sources\IGridSource $source */
79 | $source = require_once __DIR__ . '/sources/' . $sourceFile . '.php';
80 |
81 | $dataStructure = $source->getDataStructure();
82 |
83 | $dataStructure->renameColumn('user_addresses', 'addresses');
84 | $dataStructure->renameColumn('groups', 'group');
85 | $dataStructure->renameColumn('wallets', 'wallet');
86 |
87 | /** @var \Mesour\Sources\Structures\Columns\ManyToManyColumnStructure $companiesColumn */
88 | $companiesColumn = $dataStructure->getColumn('companies');
89 | $companiesColumn->setPattern('{name}');
90 |
91 | /** @var \Mesour\Sources\Structures\Columns\OneToManyColumnStructure $addressesColumn */
92 | $addressesColumn = $dataStructure->getColumn('addresses');
93 | $addressesColumn->setPattern('{street}, {zip} {city}, {country}');
94 |
95 | /** @var \Mesour\Sources\Structures\Columns\ManyToOneColumnStructure $groupColumn */
96 | $groupColumn = $dataStructure->getColumn('group');
97 | $groupColumn->setPattern('{name} ({type})');
98 |
99 | /** @var \Mesour\Sources\Structures\Columns\OneToOneColumnStructure $walletColumn */
100 | $walletColumn = $dataStructure->getColumn('wallet');
101 | $walletColumn->setPattern('{amount}');
102 |
103 | $grid->setSource($source);
104 |
105 | $pager = $grid->enablePager(8);
106 |
107 | //$filter = $grid->enableFilter();
108 | $filter = $grid->enableSimpleFilter();
109 |
110 | $selection = $grid->enableRowSelection();
111 |
112 | $selection = $selection->getLinks();
113 |
114 | $selection->addHeader('Active');
115 |
116 | $selection->addLink('Active')// add selection link
117 | ->onCall[] = function () {
118 | dump('ActivateSelected', func_get_args());
119 | };
120 |
121 | $selection->addLink('Unactive')
122 | ->setAjax(false)// disable AJAX
123 | ->onCall[] = function () {
124 | dump('InactivateSelected', func_get_args());
125 | };
126 |
127 | $selection->addDivider();
128 |
129 | $selection->addLink('Delete')
130 | ->setConfirm('Really delete all selected users?')// set confirm text
131 | ->onCall[] = function () {
132 | dump('DeleteSelected', func_get_args());
133 | };
134 |
135 | // EDITABLE
136 |
137 | $editable = $grid->enableEditable();
138 |
139 | $editableStructure = $editable->getDataStructure();
140 |
141 | $editableStructure->addOneToOne('wallet', 'Wallet')
142 | ->enableCreateNewRow();
143 |
144 | $editableStructure->addManyToOne('group', 'Groups')
145 | ->enableEditCurrentRow()
146 | ->enableCreateNewRow()
147 | ->setNullable();
148 |
149 | $editableStructure->addOneToMany('addresses', 'Addresses')
150 | ->enableCreateNewRow()
151 | ->enableRemoveRow();
152 |
153 | $editableStructure->addManyToMany('companies', 'Companies')
154 | ->enableAttachRow()
155 | ->enableCreateNewRow()
156 | ->enableRemoveRow();
157 |
158 | $companyStructure = $editableStructure->getOrCreateElement('companies', 'id');
159 | $companyStructure->addText('name', 'Name');
160 | $companyStructure->addNumber('reg_num', 'Reg. number');
161 | $companyStructure->addBool('verified', 'Verified');
162 |
163 | $walletStructure = $editableStructure->getOrCreateElement('wallets', 'id');
164 | $walletStructure->addNumber('amount', 'Amount')
165 | ->setDecimals(2)
166 | ->setThousandSeparator('.')
167 | ->setDecimalPoint(',');
168 | $walletStructure->addEnum('currency', 'Currency')
169 | ->addValue('CZK', 'CZK')
170 | ->addValue('EUR', 'EUR');
171 |
172 | $groupsStructure = $editableStructure->getOrCreateElement('groups', 'id');
173 | $groupsStructure->addText('name', 'Name');
174 | $groupsStructure->addEnum('type', 'Type')
175 | ->setNullable()
176 | ->addValue('first', 'First')
177 | ->addValue('second', 'Second');
178 | $groupsStructure->addDate('date', 'Date');
179 | $groupsStructure->addNumber('members', 'Members');
180 |
181 | // / EDITABLE
182 |
183 | $grid->enableSortable('sort');
184 |
185 | $status = $grid->addStatus('action', 'S')
186 | ->setPermission('menu', 'second');
187 |
188 | $status->addButton('active')
189 | ->setStatus(1, 'Active', 'All active')
190 | ->setIcon('check-circle-o')
191 | ->setType('success')
192 | ->setAttribute('href', '#');
193 |
194 | $status->addButton('inactive')
195 | ->setStatus(0, 'Inactive', 'All inactive')
196 | ->setIcon('times-circle-o')
197 | ->setType('danger')
198 | ->setAttribute('href', '#');
199 |
200 | $grid->addText('name', 'Name');
201 |
202 | $grid->addText('email', 'E-mail');
203 |
204 | $grid->addText('role', 'Role');
205 |
206 | $grid->addDate('last_login', 'Last login')
207 | ->setFormat('Y-m-d');
208 |
209 | $grid->addText('has_pro', 'Has pro')
210 | ->setAttribute('title', 'Has pro')
211 | ->setCallback(
212 | function (\Mesour\DataGrid\Column\Text $column, $data) {
213 | if($data['has_pro']) {
214 | return 'Yes';
215 | }
216 | return 'No';
217 | }
218 | );
219 |
220 | $grid->addText('group', 'Group')
221 | //->setFiltering(false)
222 | ->setAttribute('title', 'Select group');
223 |
224 | $grid->addText('wallet', 'Wallet')
225 | //->setFiltering(false)
226 | ->setAttribute('title', 'Wallet');
227 |
228 | $grid->addText('addresses', 'Addresses')
229 | //->setFiltering(false)
230 | ;
231 |
232 | $grid->addText('companies', 'Companies')
233 | //->setFiltering(false)
234 | ;
235 |
236 | $grid->addNumber('amount', 'Amount')
237 | ->setUnit('CZK');
238 |
239 | $time_end = microtime(true);
240 | $time = $time_end - $time_start;
241 |
242 | echo "
Execution time (before render): " . number_format($time, 3, ',', ' ') . " seconds
";
243 |
244 | echo $grid->render();
245 |
246 | $time_end = microtime(true);
247 | $time = $time_end - $time_start;
248 |
249 | echo "
Execution time (after render): " . number_format($time, 3, ',', ' ') . " seconds
";
250 |
251 | ?>
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
265 |
266 |
267 |
268 |
269 |
270 |
--------------------------------------------------------------------------------
/examples/index.php:
--------------------------------------------------------------------------------
1 | addDirectory(__DIR__ . '/../src');
17 | $loader->setCacheStorage(new Nette\Caching\Storages\FileStorage(__DIR__ . '/temp'));
18 | $loader->register();
19 |
20 |
21 | ?>
22 |
23 |
24 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
Basic functionality
36 |
37 |
38 |
39 | enablePager(5);
56 |
57 | $_sub_grid->onEditCell[] = function () {
58 | dump(func_get_args());
59 | };
60 |
61 | $_sub_grid->enableSortable('sort');
62 |
63 | $_sub_grid->onSort[] = function () {
64 | dump(func_get_args());
65 | };
66 |
67 | $_sub_grid->addText('name');
68 |
69 | $_sub_grid->addText('surname');
70 |
71 | $_sub_grid->enableFilter();
72 |
73 | $selection = $_sub_grid->enableRowSelection()
74 | ->getLinks();
75 |
76 | $selection->addLink('Active')
77 | ->onCall[] = function () {
78 | dump(func_get_args());
79 | };
80 |
81 | $selection->addLink('Unactive')
82 | ->setAjax(false)
83 | ->onCall[] = function () {
84 | dump(func_get_args());
85 | };
86 |
87 | $selection->addLink('Delete')
88 | ->setConfirm('Really delete all selected users?')
89 | ->onCall[] = function () {
90 | dump(func_get_args());
91 | };
92 |
93 | return $_sub_grid;
94 | }
95 |
96 | function createTestButton(\Mesour\Components\Control\IControl $parent, $name)
97 | {
98 | $button = new \Mesour\UI\Button($name, $parent);
99 |
100 | $button->setText('To mesour.com >>');
101 |
102 | $button->setAttribute('href', $button->link('http://mesour.com'))
103 | ->setAttribute('target', '_blank');
104 |
105 | return $button;
106 | }
107 |
108 | $application->getConfiguration()
109 | ->setTempDir(__DIR__ . '/temp');
110 |
111 | $application->setRequest($_REQUEST);
112 |
113 | $application->getUser()->setRoles('registered');
114 |
115 | $auth = $application->getAuthorizator();
116 |
117 | $auth->addRole('guest');
118 | $auth->addRole('registered', 'guest');
119 |
120 | $auth->addResource('menu');
121 |
122 | $auth->allow('guest', 'menu', ['first', 'second']);
123 | $auth->allow('registered', 'menu');
124 | $auth->deny('registered', 'menu', 'second');
125 |
126 | Mesour\UI\Filter::$maxCheckboxCount = 10;
127 |
128 | $grid = new \Mesour\UI\DataGrid('extendedDataGrid', $application);
129 |
130 | $wrapper = $grid->getWrapperPrototype();
131 |
132 | $wrapper->class('my-class');
133 |
134 | // TRUE = append
135 | $wrapper->class('my-next-class', true);
136 |
137 | /** @var \Mesour\DataGrid\Sources\IGridSource $source */
138 | $source = require_once __DIR__ . '/sources/' . $sourceFile . '.php';
139 |
140 | $dataStructure = $source->getDataStructure();
141 |
142 | /** @var \Mesour\Sources\Structures\Columns\ManyToManyColumnStructure $companiesColumn */
143 | $companiesColumn = $dataStructure->getColumn('companies');
144 | $companiesColumn->setPattern('{name}');
145 |
146 | /** @var \Mesour\Sources\Structures\Columns\OneToManyColumnStructure $addressesColumn */
147 | $addressesColumn = $dataStructure->getColumn('addresses');
148 | $addressesColumn->setPattern('{street}, {zip} {city}, {country}');
149 |
150 | /** @var \Mesour\Sources\Structures\Columns\ManyToOneColumnStructure $groupColumn */
151 | $groupColumn = $dataStructure->getColumn('group');
152 | $groupColumn->setPattern('{name} ({type})');
153 |
154 | /** @var \Mesour\Sources\Structures\Columns\OneToOneColumnStructure $walletColumn */
155 | $walletColumn = $dataStructure->getColumn('wallet');
156 | $walletColumn->setPattern('{amount}');
157 |
158 | for ($x = 0; $x < 8; $x++) {
159 | $sources[] = clone $source;
160 | }
161 |
162 | $grid->setSource($source);
163 |
164 | $pager = $grid->enablePager(8);
165 |
166 | $filter = $grid->enableFilter();
167 |
168 | $grid->onEditCell[] = function () {
169 | dump(func_get_args());
170 | };
171 |
172 | $subItems = $grid->enableSubItems();
173 |
174 | //$subItems->setPermission('menu', 'second');
175 |
176 | $subItems->addCallbackItem('test', 'Test callback item')
177 | //->setPermission('menu', 'second')
178 | ->setCallback(function (User $user) {
179 | return $user->getName() . ' ' . $user->getSurname();
180 | });
181 |
182 | $i = 0;
183 | $subItems->addGridItem('groups', 'User groups', getSubGrid())
184 | //->setPermission('menu', 'second')
185 | ->setCheckCallback(function (User $user, \Mesour\DataGrid\Extensions\SubItem\Items\Item $item) {
186 | /** @var \Mesour\Sources\Tests\Entity\User $user */
187 | if ($user->getId() == 1) {
188 | $item->setDisabled();
189 | } else {
190 | $item->setDisabled(false);
191 | }
192 | })
193 | ->setCallback(function (\Mesour\UI\DataGrid $subGrid, $rowData) use ($sources, & $i) {
194 | $_source = $sources[$i];
195 | $subGrid->setSource($_source);
196 | $i++;
197 | });
198 |
199 | $subItems->addComponentItem('button', 'Component item', 'createTestButton')
200 | //->setPermission('menu', 'second')
201 | ->setCallback(function (\Mesour\UI\Button $button, User $user) {
202 |
203 | $button->setText('Go to mesour.com from: ' . $user->getName() . ' ' . $user->getSurname() . ' >>');
204 | $button->setAttribute('href', $button->link('http://mesour.com', [
205 | 'userId' => $user->getId(),
206 | ]));
207 | });
208 |
209 | $templateItem = $subItems->addTemplateItem('description', 'Template item')
210 | //->setPermission('menu', 'second')
211 | ->setCallback(function (\Mesour\UI\TemplateFile $template, User $user) {
212 |
213 | $template->name = $user->getName() . ' ' . $user->getSurname();
214 | });
215 | $templateItem->setTempDir(__DIR__ . '/temp');
216 | $templateItem->setFile(__DIR__ . '/test.latte');
217 | $templateItem->setBlock('test');
218 |
219 | $selection = $grid->enableRowSelection()
220 | ->getLinks();
221 |
222 | $selection->addHeader('Active');
223 |
224 | $selection->addLink('Active')// add selection link
225 | ->onCall[] = function () {
226 | dump('ActivateSelected', func_get_args());
227 | };
228 |
229 | $selection->addLink('Unactive')
230 | ->setAjax(false)// disable AJAX
231 | ->onCall[] = function () {
232 | dump('InactivateSelected', func_get_args());
233 | };
234 |
235 | $selection->addDivider();
236 |
237 | $selection->addLink('Delete')
238 | ->setConfirm('Really delete all selected users?')// set confirm text
239 | ->onCall[] = function () {
240 | dump('DeleteSelected', func_get_args());
241 | };
242 |
243 | $grid->enableExport(__DIR__ . '/temp');
244 |
245 | $grid->enableSortable('sort');
246 |
247 | $grid->onSort[] = function () {
248 | dump(func_get_args());
249 | };
250 |
251 | $status = $grid->addStatus('action', 'S');
252 |
253 | $status->addButton('active')
254 | ->setStatus(1, 'Active', 'All active')
255 | ->setIcon('check-circle-o')
256 | ->setType('success')
257 | ->setAttribute('href', '#');
258 |
259 | $status->addButton('inactive')
260 | ->setStatus(0, 'Inactive', 'All inactive')
261 | ->setIcon('times-circle-o')
262 | ->setType('danger')
263 | ->setAttribute('href', '#');
264 |
265 | $grid->addImage('avatar', 'Avatar')
266 | ->setPreviewPath('preview', __DIR__, __DIR__ . '/')
267 | ->setMaxHeight(80)// translated as max-height: 80px;
268 | ->setMaxWidth(80); // can use 80, "80px", "1em"...
269 |
270 | $container = $grid->addContainer('surname', 'Name')
271 | ->setFiltering()
272 | ->setOrdering();
273 |
274 | $container->addText('surname');
275 |
276 | $container->addText('name');
277 |
278 | $grid->addText('group', 'Group');
279 |
280 | $grid->addText('email', 'E-mail')
281 | ->setOrdering(false);
282 |
283 | $grid->addNumber('amount', 'Amount')
284 | ->setUnit('EUR');
285 |
286 | $grid->addDate($lastLogin, 'Last login')
287 | ->setFormat('j.n.Y - H:i:s');
288 |
289 | $container = $grid->addContainer('blablablablablabla', 'Actions');
290 |
291 | $button = $container->addButton('test_button');
292 |
293 | $button->setIcon('pencil')
294 | ->setType('primary')
295 | ->setAttribute('href', $button->link('http://mesour.com'))
296 | ->setAttribute('target', '_blank');
297 |
298 | $dropDown = $container->addDropDown('test_drop_down');
299 |
300 | $dropDown->addHeader('Test header');
301 |
302 | $first = $dropDown->addButton();
303 |
304 | $first->setText('First button')
305 | ->setAttribute('href', $dropDown->link('/first/'));
306 |
307 | $dropDown->addDivider();
308 |
309 | $dropDown->addHeader('Test header 2');
310 |
311 | $dropDown->addButton()
312 | ->setText('Second button')
313 | ->setConfirm('Test confirm :-)')
314 | ->setAttribute('href', $dropDown->link('/second/'));
315 |
316 | $dropDown->addButton()
317 | ->setText('Third button')
318 | ->setAttribute('href', $dropDown->link('/third/'));
319 |
320 | $mainButton = $dropDown->getMainButton();
321 |
322 | $mainButton->setText('Actions')
323 | ->setType('danger');
324 |
325 | $time_end = microtime(true);
326 | $time = $time_end - $time_start;
327 |
328 | echo "
Execution time (before render): " . number_format($time, 3, ',', ' ') . " seconds
";
329 |
330 | //dump($_SESSION['Mesour\Components\Session']);
331 |
332 | $grid->render();
333 |
334 | $time_end = microtime(true);
335 | $time = $time_end - $time_start;
336 |
337 | echo "
Execution time (after render): " . number_format($time, 3, ',', ' ') . " seconds
";
338 |
339 | ?>
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
353 |
354 |
355 |
356 |
357 |
--------------------------------------------------------------------------------