Merge branch 'develop' of github.com:octobercms/october into develop
This commit is contained in:
commit
48160088f2
|
|
@ -1,5 +1,6 @@
|
|||
<?php namespace Backend\FormWidgets;
|
||||
|
||||
use Backend\Widgets\Grid;
|
||||
use Backend\Classes\FormWidgetBase;
|
||||
use System\Classes\ApplicationException;
|
||||
|
||||
|
|
@ -7,15 +8,6 @@ use System\Classes\ApplicationException;
|
|||
* Grid
|
||||
* Renders a grid field.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* - allowInsert
|
||||
* - autoInsertRows
|
||||
* - allowRemove
|
||||
* - allowImport
|
||||
* - allowExport
|
||||
* - exportFileName
|
||||
*
|
||||
* @package october\backend
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
|
|
@ -26,23 +18,24 @@ class DataGrid extends FormWidgetBase
|
|||
*/
|
||||
public $defaultAlias = 'datagrid';
|
||||
|
||||
/**
|
||||
* @var array Grid columns
|
||||
*/
|
||||
protected $columns = [];
|
||||
|
||||
/**
|
||||
* @var string Grid size
|
||||
*/
|
||||
protected $size = 'large';
|
||||
|
||||
/**
|
||||
* @var Backend\Widgets\Grid Grid widget
|
||||
*/
|
||||
protected $grid;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->columns = $this->getConfig('columns', []);
|
||||
$this->size = $this->getConfig('size', $this->size);
|
||||
$this->grid = $this->makeGridWidget();
|
||||
$this->grid->bindToController();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -59,125 +52,10 @@ class DataGrid extends FormWidgetBase
|
|||
*/
|
||||
public function prepareVars()
|
||||
{
|
||||
$this->vars['grid'] = $this->grid;
|
||||
$this->vars['name'] = $this->formField->getName();
|
||||
$this->vars['columnHeaders'] = $this->getColumnHeaders();
|
||||
$this->vars['columnDefinitions'] = $this->getColumnDefinitions();
|
||||
$this->vars['columnWidths'] = $this->getColumnWidths();
|
||||
$this->vars['size'] = $this->size;
|
||||
$this->vars['toolbarWidget'] = $this->makeToolbarWidget();
|
||||
$this->vars['value'] = json_encode($this->model->{$this->columnName});
|
||||
}
|
||||
|
||||
protected function makeToolbarWidget()
|
||||
{
|
||||
$toolbarConfig = $this->makeConfig([
|
||||
'alias' => $this->alias . 'Toolbar',
|
||||
'buttons' => $this->getViewPath('_toolbar.htm'),
|
||||
]);
|
||||
|
||||
$toolbarWidget = $this->makeWidget('Backend\Widgets\Toolbar', $toolbarConfig);
|
||||
return $toolbarWidget;
|
||||
}
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
|
||||
protected function getColumnHeaders()
|
||||
{
|
||||
$headers = [];
|
||||
foreach ($this->columns as $key => $column) {
|
||||
$headers[] = isset($column['title']) ? $column['title'] : '???';
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
protected function getColumnWidths()
|
||||
{
|
||||
$widths = [];
|
||||
foreach ($this->columns as $key => $column) {
|
||||
$widths[] = isset($column['width']) ? $column['width'] : '0';
|
||||
}
|
||||
return $widths;
|
||||
}
|
||||
|
||||
protected function getColumnDefinitions()
|
||||
{
|
||||
$definitions = [];
|
||||
foreach ($this->columns as $key => $column) {
|
||||
$item = [];
|
||||
$item['data'] = $key;
|
||||
|
||||
if (isset($column['readOnly']))
|
||||
$item['readOnly'] = $column['readOnly'];
|
||||
|
||||
$item = $this->evalColumnType($column, $item);
|
||||
$definitions[] = $item;
|
||||
}
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
protected function evalColumnType($column, $item)
|
||||
{
|
||||
if (!isset($column['type']))
|
||||
return $item;
|
||||
|
||||
switch ($column['type']) {
|
||||
case 'number':
|
||||
$item['type'] = 'numeric';
|
||||
break;
|
||||
|
||||
case 'currency':
|
||||
$item['type'] = 'numeric';
|
||||
$item['format'] = '$0,0.00';
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
$item['type'] = 'checkbox';
|
||||
break;
|
||||
|
||||
case 'autocomplete':
|
||||
$item['type'] = 'autocomplete';
|
||||
if (isset($column['options'])) $item['source'] = $column['options'];
|
||||
if (isset($column['strict'])) $item['strict'] = $column['strict'];
|
||||
break;
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
//
|
||||
// AJAX
|
||||
//
|
||||
|
||||
public function onAutocomplete()
|
||||
{
|
||||
if (!$this->model->methodExists('getGridAutocompleteValues'))
|
||||
throw new ApplicationException('Model :model does not contain a method getGridAutocompleteValues()');
|
||||
|
||||
$field = post('autocomplete_field');
|
||||
$value = post('autocomplete_value');
|
||||
$data = post('autocomplete_data', []);
|
||||
$result = $this->model->getGridAutocompleteValues($field, $value, $data);
|
||||
if (!is_array($result))
|
||||
$result = [];
|
||||
|
||||
return ['result' => $result];
|
||||
}
|
||||
|
||||
//
|
||||
// Internals
|
||||
//
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function loadAssets()
|
||||
{
|
||||
$this->addCss('vendor/handsontable/jquery.handsontable.css', 'core');
|
||||
$this->addCss('css/datagrid.css', 'core');
|
||||
$this->addJs('vendor/handsontable/jquery.handsontable.js', 'core');
|
||||
$this->addJs('js/datagrid.js', 'core');
|
||||
$this->vars['value'] = json_encode($this->formField->value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -187,4 +65,28 @@ class DataGrid extends FormWidgetBase
|
|||
{
|
||||
return json_decode($value);
|
||||
}
|
||||
|
||||
protected function makeGridWidget()
|
||||
{
|
||||
$config = $this->makeConfig((array) $this->config);
|
||||
$config->dataLocker = '#'.$this->getId('dataLocker');
|
||||
|
||||
$grid = new Grid($this->controller, $config);
|
||||
$grid->alias = $this->alias . 'Grid';
|
||||
$grid->bindEvent('grid.autocomplete', [$this, 'getAutocompleteValues']);
|
||||
|
||||
return $grid;
|
||||
}
|
||||
|
||||
public function getAutocompleteValues($field, $value, $data)
|
||||
{
|
||||
if (!$this->model->methodExists('getGridAutocompleteValues'))
|
||||
throw new ApplicationException('Model :model does not contain a method getGridAutocompleteValues()');
|
||||
|
||||
$result = $this->model->getGridAutocompleteValues($field, $value, $data);
|
||||
if (!is_array($result))
|
||||
$result = [];
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,16 +2,7 @@
|
|||
id="<?= $this->getId() ?>"
|
||||
class="field-datagrid size-<?= $size ?>">
|
||||
|
||||
<?= $toolbarWidget->render() ?>
|
||||
|
||||
<div
|
||||
id="<?= $this->getId('grid') ?>"
|
||||
style="width:100%"
|
||||
class="control-datagrid"
|
||||
data-control="datagrid"
|
||||
data-data-locker="#<?= $this->getId('dataLocker') ?>"
|
||||
data-autocomplete-handler="<?= $this->getEventHandler('onAutocomplete') ?>"
|
||||
></div>
|
||||
<?= $grid->render() ?>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
|
|
@ -21,9 +12,3 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$('#<?= $this->getId('grid') ?>')
|
||||
.data('columns', <?= json_encode($columnDefinitions) ?>)
|
||||
.data('columnHeaders', <?= json_encode($columnHeaders) ?>)
|
||||
.data('columnWidths', <?= json_encode($columnWidths) ?>)
|
||||
</script>
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
<div data-control="toolbar">
|
||||
<a href="#" class="btn btn-sm btn-default oc-icon-plus-square" onclick="$(this).closest('.field-datagrid').find('[data-control=datagrid]').dataGrid('insertRow')">Insert Row</a>
|
||||
<a href="#" class="btn btn-sm btn-default oc-icon-minus-square" onclick="$(this).closest('.field-datagrid').find('[data-control=datagrid]').dataGrid('removeRow')">Delete Row</a>
|
||||
<!-- <a href="#" class="btn btn-sm btn-default oc-icon-floppy-o">Save as CSV</a> -->
|
||||
<!-- <a href="#" class="btn btn-sm btn-default oc-icon-upload">Import CSV</a> -->
|
||||
</div>
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
<?php namespace Backend\Widgets;
|
||||
|
||||
use Backend\Classes\WidgetBase;
|
||||
|
||||
/**
|
||||
* Grid Widget
|
||||
* Renders a search container used for viewing tabular data
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* - allowInsert
|
||||
* - autoInsertRows
|
||||
* - allowRemove
|
||||
* - allowImport
|
||||
* - allowExport
|
||||
* - exportFileName
|
||||
*
|
||||
* @package october\backend
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Grid extends WidgetBase
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public $defaultAlias = 'grid';
|
||||
|
||||
/**
|
||||
* @var array Grid columns
|
||||
*/
|
||||
protected $columns = [];
|
||||
|
||||
/**
|
||||
* @var boolean Show data table header
|
||||
*/
|
||||
protected $showHeader = true;
|
||||
|
||||
/**
|
||||
* @var boolean Insert row button
|
||||
*/
|
||||
protected $allowInsert = true;
|
||||
|
||||
/**
|
||||
* @var boolean Delete row button
|
||||
*/
|
||||
protected $allowRemove = true;
|
||||
|
||||
/**
|
||||
* @var boolean Disable the toolbar
|
||||
*/
|
||||
protected $disableToolbar = false;
|
||||
|
||||
/**
|
||||
* @var mixed Array of data, or callable for data source.
|
||||
*/
|
||||
protected $dataSource;
|
||||
|
||||
/**
|
||||
* @var string HTML element that can [re]store the grid data.
|
||||
*/
|
||||
protected $dataLocker;
|
||||
|
||||
/**
|
||||
* Initialize the widget, called by the constructor and free from its parameters.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->columns = $this->getConfig('columns', []);
|
||||
$this->showHeader = $this->getConfig('showHeader', $this->showHeader);
|
||||
$this->allowInsert = $this->getConfig('allowInsert', $this->allowInsert);
|
||||
$this->allowRemove = $this->getConfig('allowRemove', $this->allowRemove);
|
||||
$this->disableToolbar = $this->getConfig('disableToolbar', $this->disableToolbar);
|
||||
$this->dataLocker = $this->getConfig('dataLocker', $this->dataLocker);
|
||||
$this->dataSource = $this->getConfig('dataSource', $this->dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the widget.
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$this->prepareVars();
|
||||
return $this->makePartial('grid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the view data
|
||||
*/
|
||||
public function prepareVars()
|
||||
{
|
||||
$this->vars['columnHeaders'] = $this->getColumnHeaders();
|
||||
$this->vars['columnDefinitions'] = $this->getColumnDefinitions();
|
||||
$this->vars['columnWidths'] = $this->getColumnWidths();
|
||||
$this->vars['toolbarWidget'] = $this->makeToolbarWidget();
|
||||
|
||||
$this->vars['showHeader'] = $this->showHeader;
|
||||
$this->vars['allowInsert'] = $this->allowInsert;
|
||||
$this->vars['allowRemove'] = $this->allowRemove;
|
||||
$this->vars['disableToolbar'] = $this->disableToolbar;
|
||||
$this->vars['dataLocker'] = $this->dataLocker;
|
||||
}
|
||||
|
||||
protected function makeToolbarWidget()
|
||||
{
|
||||
if ($this->disableToolbar)
|
||||
return;
|
||||
|
||||
$toolbarConfig = $this->makeConfig([
|
||||
'alias' => $this->alias . 'Toolbar',
|
||||
'buttons' => $this->getViewPath('_toolbar.htm'),
|
||||
]);
|
||||
|
||||
$toolbarWidget = $this->makeWidget('Backend\Widgets\Toolbar', $toolbarConfig);
|
||||
$toolbarWidget->vars['allowInsert'] = $this->allowInsert;
|
||||
$toolbarWidget->vars['allowRemove'] = $this->allowRemove;
|
||||
return $toolbarWidget;
|
||||
}
|
||||
|
||||
//
|
||||
// AJAX
|
||||
//
|
||||
|
||||
public function onAutocomplete()
|
||||
{
|
||||
$field = post('autocomplete_field');
|
||||
$value = post('autocomplete_value');
|
||||
$data = post('autocomplete_data', []);
|
||||
$result = $this->fireEvent('grid.autocomplete', [$field, $value, $data], true);
|
||||
return ['result' => $result];
|
||||
}
|
||||
|
||||
public function onDataSource()
|
||||
{
|
||||
if ($this->dataLocker)
|
||||
return;
|
||||
|
||||
$result = $this->dataSource;
|
||||
return ['result' => $result];
|
||||
}
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
|
||||
protected function getColumnHeaders()
|
||||
{
|
||||
if (!$this->showHeader)
|
||||
return false;
|
||||
|
||||
$headers = [];
|
||||
foreach ($this->columns as $key => $column) {
|
||||
$headers[] = isset($column['title']) ? $column['title'] : '???';
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
protected function getColumnWidths()
|
||||
{
|
||||
$widths = [];
|
||||
foreach ($this->columns as $key => $column) {
|
||||
$widths[] = isset($column['width']) ? $column['width'] : '0';
|
||||
}
|
||||
return $widths;
|
||||
}
|
||||
|
||||
protected function getColumnDefinitions()
|
||||
{
|
||||
$definitions = [];
|
||||
foreach ($this->columns as $key => $column) {
|
||||
$item = [];
|
||||
$item['data'] = $key;
|
||||
|
||||
if (isset($column['readOnly']))
|
||||
$item['readOnly'] = $column['readOnly'];
|
||||
|
||||
$item = $this->evalColumnType($column, $item);
|
||||
$definitions[] = $item;
|
||||
}
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
protected function evalColumnType($column, $item)
|
||||
{
|
||||
if (!isset($column['type']))
|
||||
return $item;
|
||||
|
||||
switch ($column['type']) {
|
||||
case 'number':
|
||||
$item['type'] = 'numeric';
|
||||
break;
|
||||
|
||||
case 'currency':
|
||||
$item['type'] = 'numeric';
|
||||
$item['format'] = '$0,0.00';
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
$item['type'] = 'checkbox';
|
||||
break;
|
||||
|
||||
case 'autocomplete':
|
||||
$item['type'] = 'autocomplete';
|
||||
if (isset($column['options'])) $item['source'] = $column['options'];
|
||||
if (isset($column['strict'])) $item['strict'] = $column['strict'];
|
||||
break;
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
//
|
||||
// Internals
|
||||
//
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function loadAssets()
|
||||
{
|
||||
$this->addCss('vendor/handsontable/jquery.handsontable.css', 'core');
|
||||
$this->addCss('css/datagrid.css', 'core');
|
||||
$this->addJs('vendor/handsontable/jquery.handsontable.js', 'core');
|
||||
$this->addJs('js/datagrid.js', 'core');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -83,6 +83,6 @@ class Toolbar extends WidgetBase
|
|||
if (!isset($this->config->buttons))
|
||||
return false;
|
||||
|
||||
return $this->controller->makePartial($this->config->buttons);
|
||||
return $this->controller->makePartial($this->config->buttons, $this->vars);
|
||||
}
|
||||
}
|
||||
|
|
@ -32,17 +32,17 @@
|
|||
colWidths: function(columnIndex) {
|
||||
return self.staticWidths[columnIndex]
|
||||
},
|
||||
height: 400,
|
||||
// height: 400,
|
||||
columns: this.columns,
|
||||
startRows: this.options.startRows,
|
||||
minRows: this.options.minRows,
|
||||
currentRowClassName: 'currentRow',
|
||||
// rowHeaders: true,
|
||||
// rowHeaders: false,
|
||||
// manualColumnMove: true,
|
||||
// manualRowMove: true,
|
||||
fillHandle: false,
|
||||
multiSelect: false,
|
||||
removeRowPlugin: true
|
||||
removeRowPlugin: this.options.allowRemove
|
||||
}
|
||||
|
||||
if (this.options.autoInsertRows)
|
||||
|
|
@ -68,6 +68,13 @@
|
|||
delete handsontableOptions.data
|
||||
}
|
||||
}
|
||||
else if (this.options.sourceHandler) {
|
||||
$.request(self.options.sourceHandler, {
|
||||
success: function(data, textStatus, jqXHR){
|
||||
self.gridInstance.loadData(data.result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.$el.handsontable(handsontableOptions)
|
||||
this.gridInstance = this.$el.handsontable('getInstance')
|
||||
|
|
@ -138,6 +145,8 @@
|
|||
columnWidths: null,
|
||||
columns: null,
|
||||
autocompleteHandler: null,
|
||||
sourceHandler: null,
|
||||
allowRemove: true,
|
||||
confirmMessage: 'Are you sure?'
|
||||
}
|
||||
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
* - Removed contextMenu plugin
|
||||
* - Use autocomplete plugin instead of typeahead
|
||||
* - Custom checkboxes
|
||||
* - Removed native scrollbars
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
@ -2797,9 +2798,7 @@ DefaultSettings.prototype = {
|
|||
allowInvalid: true,
|
||||
invalidCellClassName: 'htInvalid',
|
||||
fragmentSelection: false,
|
||||
readOnly: false,
|
||||
scrollbarModelV: 'dragdealer',
|
||||
scrollbarModelH: 'dragdealer'
|
||||
readOnly: false
|
||||
};
|
||||
|
||||
$.fn.handsontable = function (action) {
|
||||
|
|
@ -3021,8 +3020,6 @@ Handsontable.TableView = function (instance) {
|
|||
data: instance.getDataAtCell,
|
||||
totalRows: instance.countRows,
|
||||
totalColumns: instance.countCols,
|
||||
scrollbarModelV: this.settings.scrollbarModelV,
|
||||
scrollbarModelH: this.settings.scrollbarModelH,
|
||||
offsetRow: 0,
|
||||
offsetColumn: 0,
|
||||
width: this.getWidth(),
|
||||
|
|
@ -3317,9 +3314,6 @@ Handsontable.TableView.prototype.maximumVisibleElementWidth = function (left) {
|
|||
*/
|
||||
Handsontable.TableView.prototype.maximumVisibleElementHeight = function (top) {
|
||||
var rootHeight = this.wt.wtViewport.getWorkspaceHeight();
|
||||
if(this.wt.isNativeScroll) {
|
||||
return rootHeight;
|
||||
}
|
||||
return rootHeight - top;
|
||||
};
|
||||
|
||||
|
|
@ -8004,25 +7998,6 @@ WalkontableScroll.prototype.scrollViewport = function (coords) {
|
|||
, fixedRowsTop = this.instance.getSetting('fixedRowsTop')
|
||||
, fixedColumnsLeft = this.instance.getSetting('fixedColumnsLeft');
|
||||
|
||||
if (this.instance.isNativeScroll) {
|
||||
var TD = this.instance.wtTable.getCell(coords);
|
||||
if (typeof TD === 'object') {
|
||||
var offset = WalkontableDom.prototype.offset(TD);
|
||||
var outerHeight = WalkontableDom.prototype.outerHeight(TD);
|
||||
var scrollY = window.scrollY;
|
||||
var clientHeight = document.documentElement.clientHeight;
|
||||
if (outerHeight < clientHeight) {
|
||||
if (offset.top < scrollY) {
|
||||
TD.scrollIntoView(true);
|
||||
}
|
||||
else if (offset.top + outerHeight > scrollY + clientHeight) {
|
||||
TD.scrollIntoView(false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (coords[0] < 0 || coords[0] > totalRows - 1) {
|
||||
throw new Error('row ' + coords[0] + ' does not exist');
|
||||
}
|
||||
|
|
@ -8437,129 +8412,10 @@ WalkontableScrollbarNative.prototype.destroy = function () {
|
|||
this.$scrollHandler.off('scroll.walkontable');
|
||||
};
|
||||
|
||||
///
|
||||
|
||||
var WalkontableVerticalScrollbarNative = function (instance) {
|
||||
this.instance = instance;
|
||||
this.type = 'vertical';
|
||||
this.cellSize = 23;
|
||||
this.init();
|
||||
|
||||
var that = this;
|
||||
WalkontableCellStrategy.prototype.isLastIncomplete = function () { //monkey patch needed. In future get rid of it to improve performance
|
||||
/*
|
||||
* this.remainingSize = window viewport reduced by sum of all rendered cells (also those before the visible part)
|
||||
* that.sumCellSizes(...) = sum of the sizes of cells that are before the visible part + 1 cell that is partially visible on top of the screen
|
||||
*/
|
||||
return this.remainingSize > that.sumCellSizes(that.offset, that.offset + that.curOuts + 1);
|
||||
};
|
||||
};
|
||||
|
||||
WalkontableVerticalScrollbarNative.prototype = new WalkontableScrollbarNative();
|
||||
|
||||
WalkontableVerticalScrollbarNative.prototype.getLastCell = function () {
|
||||
return this.instance.getSetting('offsetRow') + this.instance.wtTable.tbodyChildrenLength - 1;
|
||||
};
|
||||
|
||||
WalkontableVerticalScrollbarNative.prototype.getTableSize = function () {
|
||||
return this.instance.wtDom.outerHeight(this.TABLE);
|
||||
};
|
||||
|
||||
var partialOffset = 0;
|
||||
|
||||
WalkontableVerticalScrollbarNative.prototype.sumCellSizes = function (from, length) {
|
||||
var sum = 0;
|
||||
while (from < length) {
|
||||
sum += this.instance.getSetting('rowHeight', from);
|
||||
from++;
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
|
||||
WalkontableVerticalScrollbarNative.prototype.applyToDOM = function () {
|
||||
var headerSize = this.instance.wtViewport.getColumnHeaderHeight();
|
||||
this.fixedContainer.style.height = headerSize + this.sumCellSizes(0, this.total) + 'px';
|
||||
this.fixed.style.top = this.measureBefore + 'px';
|
||||
this.fixed.style.bottom = '';
|
||||
};
|
||||
|
||||
WalkontableVerticalScrollbarNative.prototype.scrollTo = function (cell) {
|
||||
var newY = this.tableParentOffset + cell * this.cellSize;
|
||||
this.$scrollHandler.scrollTop(newY);
|
||||
this.onScroll(newY);
|
||||
};
|
||||
|
||||
WalkontableVerticalScrollbarNative.prototype.readSettings = function () {
|
||||
var offset = this.instance.wtDom.offset(this.fixedContainer);
|
||||
this.tableParentOffset = offset.top;
|
||||
this.tableParentOtherOffset = offset.left;
|
||||
this.windowSize = this.$scrollHandler.height();
|
||||
this.windowScrollPosition = this.$scrollHandler.scrollTop();
|
||||
this.offset = this.instance.getSetting('offsetRow');
|
||||
this.total = this.instance.getSetting('totalRows');
|
||||
};
|
||||
|
||||
///
|
||||
|
||||
var WalkontableHorizontalScrollbarNative = function (instance) {
|
||||
this.instance = instance;
|
||||
this.type = 'horizontal';
|
||||
this.cellSize = 50;
|
||||
this.init();
|
||||
};
|
||||
|
||||
WalkontableHorizontalScrollbarNative.prototype = new WalkontableScrollbarNative();
|
||||
|
||||
WalkontableHorizontalScrollbarNative.prototype.getLastCell = function () {
|
||||
return this.instance.wtTable.getLastVisibleColumn();
|
||||
};
|
||||
|
||||
WalkontableHorizontalScrollbarNative.prototype.getTableSize = function () {
|
||||
return this.instance.wtDom.outerWidth(this.TABLE);
|
||||
};
|
||||
|
||||
WalkontableHorizontalScrollbarNative.prototype.applyToDOM = function () {
|
||||
this.fixedContainer.style.paddingLeft = this.measureBefore + 'px';
|
||||
this.fixedContainer.style.paddingRight = this.measureAfter + 'px';
|
||||
};
|
||||
|
||||
WalkontableHorizontalScrollbarNative.prototype.scrollTo = function (cell) {
|
||||
this.$scrollHandler.scrollLeft(this.tableParentOffset + cell * this.cellSize);
|
||||
};
|
||||
|
||||
WalkontableHorizontalScrollbarNative.prototype.readSettings = function () {
|
||||
var offset = this.instance.wtDom.offset(this.fixedContainer);
|
||||
this.tableParentOffset = offset.left;
|
||||
this.tableParentOtherOffset = offset.top;
|
||||
this.windowSize = this.$scrollHandler.width();
|
||||
this.windowScrollPosition = this.$scrollHandler.scrollLeft();
|
||||
this.offset = this.instance.getSetting('offsetColumn');
|
||||
this.total = this.instance.getSetting('totalColumns');
|
||||
};
|
||||
function WalkontableScrollbars(instance) {
|
||||
if(instance.getSetting('scrollbarModelV') === 'native') {
|
||||
instance.update('scrollbarModelH', 'none');
|
||||
}
|
||||
|
||||
switch (instance.getSetting('scrollbarModelV')) {
|
||||
case 'dragdealer':
|
||||
this.vertical = new WalkontableVerticalScrollbar(instance);
|
||||
break;
|
||||
|
||||
case 'native':
|
||||
this.vertical = new WalkontableVerticalScrollbarNative(instance);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (instance.getSetting('scrollbarModelH')) {
|
||||
case 'dragdealer':
|
||||
this.horizontal = new WalkontableHorizontalScrollbar(instance);
|
||||
break;
|
||||
|
||||
case 'native':
|
||||
this.horizontal = new WalkontableHorizontalScrollbarNative(instance);
|
||||
break;
|
||||
}
|
||||
this.vertical = new WalkontableVerticalScrollbar(instance);
|
||||
this.horizontal = new WalkontableHorizontalScrollbar(instance);
|
||||
}
|
||||
|
||||
WalkontableScrollbars.prototype.destroy = function () {
|
||||
|
|
@ -8677,8 +8533,6 @@ function WalkontableSettings(instance, settings) {
|
|||
//presentation mode
|
||||
scrollH: 'auto', //values: scroll (always show scrollbar), auto (show scrollbar if table does not fit in the container), none (never show scrollbar)
|
||||
scrollV: 'auto', //values: see above
|
||||
scrollbarModelH: 'dragdealer', //values: dragdealer, native
|
||||
scrollbarModelV: 'dragdealer', //values: dragdealer, native
|
||||
stretchH: 'hybrid', //values: hybrid, all, last, none
|
||||
currentRowClassName: null,
|
||||
currentColumnClassName: null,
|
||||
|
|
@ -8882,10 +8736,6 @@ function WalkontableTable(instance) {
|
|||
this.columnFilter = new WalkontableColumnFilter();
|
||||
|
||||
this.verticalRenderReverse = false;
|
||||
|
||||
if (this.instance.getSetting('scrollbarModelV') === 'native' || this.instance.getSetting('scrollbarModelH') === 'native') {
|
||||
this.instance.isNativeScroll = true;
|
||||
}
|
||||
}
|
||||
|
||||
WalkontableTable.prototype.refreshHiderDimensions = function () {
|
||||
|
|
@ -8894,7 +8744,7 @@ WalkontableTable.prototype.refreshHiderDimensions = function () {
|
|||
|
||||
var spreaderStyle = this.spreader.style;
|
||||
|
||||
if ((height !== Infinity || width !== Infinity) && !this.instance.isNativeScroll) {
|
||||
if (height !== Infinity || width !== Infinity) {
|
||||
if (height === Infinity) {
|
||||
height = this.instance.wtViewport.getWorkspaceActualHeight();
|
||||
}
|
||||
|
|
@ -8908,13 +8758,9 @@ WalkontableTable.prototype.refreshHiderDimensions = function () {
|
|||
spreaderStyle.top = '0';
|
||||
spreaderStyle.left = '0';
|
||||
|
||||
if (this.instance.getSetting('scrollbarModelV') === 'dragdealer') {
|
||||
spreaderStyle.height = '4000px';
|
||||
}
|
||||
|
||||
if (this.instance.getSetting('scrollbarModelH') === 'dragdealer') {
|
||||
spreaderStyle.width = '4000px';
|
||||
}
|
||||
// For dragdealer
|
||||
spreaderStyle.height = '4000px';
|
||||
spreaderStyle.width = '4000px';
|
||||
|
||||
if (height < 0) { //this happens with WalkontableScrollbarNative and causes "Invalid argument" error in IE8
|
||||
height = 0;
|
||||
|
|
@ -8923,11 +8769,6 @@ WalkontableTable.prototype.refreshHiderDimensions = function () {
|
|||
this.hiderStyle.height = height + 'px';
|
||||
this.hiderStyle.width = width + 'px';
|
||||
}
|
||||
else {
|
||||
spreaderStyle.position = 'relative';
|
||||
spreaderStyle.width = 'auto';
|
||||
spreaderStyle.height = 'auto';
|
||||
}
|
||||
};
|
||||
|
||||
WalkontableTable.prototype.refreshStretching = function () {
|
||||
|
|
@ -8960,9 +8801,6 @@ WalkontableTable.prototype.refreshStretching = function () {
|
|||
}
|
||||
|
||||
var containerHeightFn = function (cacheHeight) {
|
||||
if (that.instance.isNativeScroll) {
|
||||
return 2 * that.instance.wtViewport.getViewportHeight(cacheHeight);
|
||||
}
|
||||
return that.instance.wtViewport.getViewportHeight(cacheHeight);
|
||||
};
|
||||
|
||||
|
|
@ -9075,10 +8913,6 @@ WalkontableTable.prototype.adjustColumns = function (TR, desiredCount) {
|
|||
};
|
||||
|
||||
WalkontableTable.prototype.draw = function (selectionsOnly) {
|
||||
if (this.instance.isNativeScroll) {
|
||||
this.verticalRenderReverse = false; //this is only supported in dragdealer mode, not in native
|
||||
}
|
||||
|
||||
this.rowFilter.readSettings(this.instance);
|
||||
this.columnFilter.readSettings(this.instance);
|
||||
|
||||
|
|
@ -9180,7 +9014,6 @@ WalkontableTable.prototype._doDraw = function () {
|
|||
}
|
||||
|
||||
if (first) {
|
||||
// if (r === 0) {
|
||||
first = false;
|
||||
|
||||
this.adjustAvailableNodes();
|
||||
|
|
@ -9248,9 +9081,7 @@ WalkontableTable.prototype._doDraw = function () {
|
|||
res = this.rowStrategy.add(r, TD, this.verticalRenderReverse);
|
||||
|
||||
if (res === false) {
|
||||
if (!this.instance.isNativeScroll) {
|
||||
this.rowStrategy.removeOutstanding();
|
||||
}
|
||||
this.rowStrategy.removeOutstanding();
|
||||
}
|
||||
|
||||
if (this.rowStrategy.isLastIncomplete()) {
|
||||
|
|
@ -9368,10 +9199,6 @@ WalkontableTable.prototype.refreshSelections = function (selectionsOnly) {
|
|||
*
|
||||
*/
|
||||
WalkontableTable.prototype.getCell = function (coords) {
|
||||
if (this.instance.isNativeScroll) {
|
||||
return this.instance.wtTable.TBODY.querySelectorAll('[data-row="' + coords[0] + '"][data-column="' + coords[1] + '"]')[0];
|
||||
}
|
||||
|
||||
if (this.isRowBeforeViewport(coords[0])) {
|
||||
return -1; //row before viewport
|
||||
}
|
||||
|
|
@ -9425,21 +9252,11 @@ WalkontableTable.prototype.isColumnAfterViewport = function (c) {
|
|||
};
|
||||
|
||||
WalkontableTable.prototype.isRowInViewport = function (r) {
|
||||
if (this.instance.isNativeScroll) {
|
||||
return !!this.instance.wtTable.TBODY.querySelectorAll('[data-row="' + r + '"]')[0];
|
||||
}
|
||||
else {
|
||||
return (!this.isRowBeforeViewport(r) && !this.isRowAfterViewport(r));
|
||||
}
|
||||
return (!this.isRowBeforeViewport(r) && !this.isRowAfterViewport(r));
|
||||
};
|
||||
|
||||
WalkontableTable.prototype.isColumnInViewport = function (c) {
|
||||
if (this.instance.isNativeScroll) {
|
||||
return !!this.instance.wtTable.TBODY.querySelectorAll('[data-column="' + c + '"]')[0];
|
||||
}
|
||||
else {
|
||||
return (!this.isColumnBeforeViewport(c) && !this.isColumnAfterViewport(c));
|
||||
}
|
||||
return (!this.isColumnBeforeViewport(c) && !this.isColumnAfterViewport(c));
|
||||
};
|
||||
|
||||
WalkontableTable.prototype.isLastRowFullyVisible = function () {
|
||||
|
|
@ -9453,22 +9270,10 @@ WalkontableTable.prototype.isLastColumnFullyVisible = function () {
|
|||
function WalkontableViewport(instance) {
|
||||
this.instance = instance;
|
||||
this.resetSettings();
|
||||
|
||||
if (this.instance.isNativeScroll) {
|
||||
var that = this;
|
||||
that.clientHeight = document.documentElement.clientHeight; //browser viewport height
|
||||
$(window).on('resize', function () {
|
||||
that.clientHeight = document.documentElement.clientHeight;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Used by scrollbar
|
||||
WalkontableViewport.prototype.getWorkspaceHeight = function (proposedHeight) {
|
||||
if (this.instance.isNativeScroll) {
|
||||
return this.clientHeight;
|
||||
}
|
||||
|
||||
var height = this.instance.getSetting('height');
|
||||
|
||||
if (height === Infinity || height === void 0 || height === null || height < 1) {
|
||||
|
|
@ -9584,10 +9389,6 @@ WalkontableViewport.prototype.resetSettings = function () {
|
|||
this.columnHeaderHeight = NaN;
|
||||
};
|
||||
function WalkontableWheel(instance) {
|
||||
if (instance.isNativeScroll) {
|
||||
return;
|
||||
}
|
||||
|
||||
//spreader === instance.wtTable.TABLE.parentNode
|
||||
$(instance.wtTable.spreader).on('mousewheel', function (event, delta, deltaX, deltaY) {
|
||||
if (!deltaX && !deltaY && delta) { //we are in IE8, see https://github.com/brandonaaron/jquery-mousewheel/issues/53
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<div class="datagrid-widget">
|
||||
<?php if (!$disableToolbar): ?>
|
||||
<?= $toolbarWidget->render() ?>
|
||||
<?php endif ?>
|
||||
|
||||
<div
|
||||
id="<?= $this->getId('grid') ?>"
|
||||
style="width:100%"
|
||||
class="control-datagrid"
|
||||
data-control="datagrid"
|
||||
data-allow-remove="<?= $allowRemove ? 'true' : 'false' ?>"
|
||||
<?php if ($dataLocker): ?>data-data-locker="<?= $dataLocker ?>"<?php endif ?>
|
||||
data-autocomplete-handler="<?= $this->getEventHandler('onAutocomplete') ?>"
|
||||
data-source-handler="<?= $this->getEventHandler('onDataSource') ?>"
|
||||
></div>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
$('#<?= $this->getId('grid') ?>')
|
||||
.data('columns', <?= json_encode($columnDefinitions) ?>)
|
||||
.data('columnHeaders', <?= json_encode($columnHeaders) ?>)
|
||||
.data('columnWidths', <?= json_encode($columnWidths) ?>)
|
||||
</script>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<div data-control="toolbar">
|
||||
<?php if ($allowInsert): ?>
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="btn btn-sm btn-default oc-icon-plus-square"
|
||||
onclick="$(this).closest('.datagrid-widget').find('[data-control=datagrid]').dataGrid('insertRow')">
|
||||
Insert Row
|
||||
</a>
|
||||
<?php endif ?>
|
||||
<?php if ($allowRemove): ?>
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="btn btn-sm btn-default oc-icon-minus-square"
|
||||
onclick="$(this).closest('.datagrid-widget').find('[data-control=datagrid]').dataGrid('removeRow')">
|
||||
Delete Row
|
||||
</a>
|
||||
<?php endif ?>
|
||||
<!-- <a href="#" class="btn btn-sm btn-default oc-icon-floppy-o">Save as CSV</a> -->
|
||||
<!-- <a href="#" class="btn btn-sm btn-default oc-icon-upload">Import CSV</a> -->
|
||||
</div>
|
||||
|
|
@ -4,7 +4,6 @@ use Lang;
|
|||
use Backend;
|
||||
use BackendMenu;
|
||||
use BackendAuth;
|
||||
use System\Classes\MarkupManager;
|
||||
use Backend\Classes\WidgetManager;
|
||||
use October\Rain\Support\ModuleServiceProvider;
|
||||
|
||||
|
|
@ -93,48 +92,6 @@ class ServiceProvider extends ModuleServiceProvider
|
|||
]);
|
||||
});
|
||||
|
||||
/*
|
||||
* Register markup tags
|
||||
*/
|
||||
MarkupManager::instance()->registerCallback(function($manager){
|
||||
$manager->registerFunctions([
|
||||
// Global helpers
|
||||
'post' => 'post',
|
||||
|
||||
// Form helpers
|
||||
'form_ajax' => ['Form', 'ajax'],
|
||||
'form_open' => ['Form', 'open'],
|
||||
'form_close' => ['Form', 'close'],
|
||||
'form_token' => ['Form', 'token'],
|
||||
'form_session_key' => ['Form', 'sessionKey'],
|
||||
'form_token' => ['Form', 'token'],
|
||||
'form_model' => ['Form', 'model'],
|
||||
'form_label' => ['Form', 'label'],
|
||||
'form_text' => ['Form', 'text'],
|
||||
'form_password' => ['Form', 'password'],
|
||||
'form_checkbox' => ['Form', 'checkbox'],
|
||||
'form_radio' => ['Form', 'radio'],
|
||||
'form_file' => ['Form', 'file'],
|
||||
'form_select' => ['Form', 'select'],
|
||||
'form_select_range' => ['Form', 'selectRange'],
|
||||
'form_select_month' => ['Form', 'selectMonth'],
|
||||
'form_submit' => ['Form', 'submit'],
|
||||
'form_macro' => ['Form', '__call'],
|
||||
'form_value' => ['Form', 'value'],
|
||||
]);
|
||||
|
||||
$manager->registerFilters([
|
||||
// String helpers
|
||||
'slug' => ['Str', 'slug'],
|
||||
'plural' => ['Str', 'plural'],
|
||||
'singular' => ['Str', 'singular'],
|
||||
'finish' => ['Str', 'finish'],
|
||||
'snake' => ['Str', 'snake'],
|
||||
'camel' => ['Str', 'camel'],
|
||||
'studly' => ['Str', 'studly'],
|
||||
]);
|
||||
});
|
||||
|
||||
/*
|
||||
* Register widgets
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use URL;
|
|||
use File;
|
||||
use Lang;
|
||||
use Cache;
|
||||
use Route;
|
||||
use Config;
|
||||
use Request;
|
||||
use Response;
|
||||
|
|
@ -193,7 +194,13 @@ class CombineAssets
|
|||
*/
|
||||
protected function getCombinedUrl($outputFilename = 'undefined.css')
|
||||
{
|
||||
return URL::action('Cms\Classes\Controller@combine', [$outputFilename], false);
|
||||
$combineAction = 'Cms\Classes\Controller@combine';
|
||||
$actionExists = Route::getRoutes()->getByAction($combineAction) !== null;
|
||||
|
||||
if ($actionExists)
|
||||
return URL::action($combineAction, [$outputFilename], false);
|
||||
else
|
||||
return Request::getBasePath().'/combine/'.$outputFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use BackendAuth;
|
|||
use Twig_Environment;
|
||||
use Twig_Loader_String;
|
||||
use System\Classes\ErrorHandler;
|
||||
use System\Classes\MarkupManager;
|
||||
use System\Classes\PluginManager;
|
||||
use System\Classes\SettingsManager;
|
||||
use System\Twig\Engine as TwigEngine;
|
||||
|
|
@ -179,6 +180,44 @@ class ServiceProvider extends ModuleServiceProvider
|
|||
]);
|
||||
});
|
||||
|
||||
/*
|
||||
* Register markup tags
|
||||
*/
|
||||
MarkupManager::instance()->registerCallback(function($manager){
|
||||
$manager->registerFunctions([
|
||||
// Functions
|
||||
'post' => 'post',
|
||||
'link_to' => 'link_to',
|
||||
'link_to_asset' => 'link_to_asset',
|
||||
'link_to_route' => 'link_to_route',
|
||||
'link_to_action' => 'link_to_action',
|
||||
'asset' => 'asset',
|
||||
'action' => 'action',
|
||||
'url' => 'url',
|
||||
'route' => 'route',
|
||||
'secure_url' => 'secure_url',
|
||||
'secure_asset' => 'secure_asset',
|
||||
|
||||
// Classes
|
||||
'str_*' => ['Str', '*'],
|
||||
'url_*' => ['URL', '*'],
|
||||
'html_*' => ['HTML', '*'],
|
||||
'form_*' => ['Form', '*'],
|
||||
'form_macro' => ['Form', '__call'],
|
||||
]);
|
||||
|
||||
$manager->registerFilters([
|
||||
// Classes
|
||||
'slug' => ['Str', 'slug'],
|
||||
'plural' => ['Str', 'plural'],
|
||||
'singular' => ['Str', 'singular'],
|
||||
'finish' => ['Str', 'finish'],
|
||||
'snake' => ['Str', 'snake'],
|
||||
'camel' => ['Str', 'camel'],
|
||||
'studly' => ['Str', 'studly'],
|
||||
]);
|
||||
});
|
||||
|
||||
/*
|
||||
* Register settings
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
<?php namespace System\Classes;
|
||||
|
||||
use Str;
|
||||
use Twig_TokenParser;
|
||||
use Twig_SimpleFilter;
|
||||
use Twig_SimpleFunction;
|
||||
use System\Classes\ApplicationException;
|
||||
use System\Classes\PluginManager;
|
||||
|
||||
/**
|
||||
|
|
@ -189,4 +194,126 @@ class MarkupManager
|
|||
return $this->listExtensions(self::EXTENSION_TOKEN_PARSER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a set of Twig functions for use in a twig extension.
|
||||
* @param array $functions Current collection
|
||||
* @return array
|
||||
*/
|
||||
public function makeTwigFunctions($functions = [])
|
||||
{
|
||||
if (!is_array($functions))
|
||||
$functions = [];
|
||||
|
||||
foreach ($this->listFunctions() as $name => $callable) {
|
||||
|
||||
/*
|
||||
* Handle a wildcard function
|
||||
*/
|
||||
if (strpos($name, '*') !== false && $this->isWildCallable($callable)) {
|
||||
$callable = function($name) use ($callable) {
|
||||
$arguments = array_slice(func_get_args(), 1);
|
||||
$method = $this->isWildCallable($callable, Str::camel($name));
|
||||
return call_user_func_array($method, $arguments);
|
||||
};
|
||||
}
|
||||
|
||||
if (!is_callable($callable))
|
||||
throw new ApplicationException(sprintf('The markup function for %s is not callable.', $name));
|
||||
|
||||
$functions[] = new Twig_SimpleFunction($name, $callable, ['is_safe' => ['html']]);
|
||||
}
|
||||
|
||||
return $functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a set of Twig filters for use in a twig extension.
|
||||
* @param array $filters Current collection
|
||||
* @return array
|
||||
*/
|
||||
public function makeTwigFilters($filters = [])
|
||||
{
|
||||
if (!is_array($filters))
|
||||
$filters = [];
|
||||
|
||||
foreach ($this->listFilters() as $name => $callable) {
|
||||
|
||||
/*
|
||||
* Handle a wildcard function
|
||||
*/
|
||||
if (strpos($name, '*') !== false && $this->isWildCallable($callable)) {
|
||||
$callable = function($name) use ($callable) {
|
||||
$arguments = array_slice(func_get_args(), 1);
|
||||
$method = $this->isWildCallable($callable, Str::camel($name));
|
||||
return call_user_func_array($method, $arguments);
|
||||
};
|
||||
}
|
||||
|
||||
if (!is_callable($callable))
|
||||
throw new ApplicationException(sprintf('The markup filter for %s is not callable.', $name));
|
||||
|
||||
$filters[] = new Twig_SimpleFilter($name, $callable, ['is_safe' => ['html']]);
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a set of Twig token parsers for use in a twig extension.
|
||||
* @param array $parsers Current collection
|
||||
* @return array
|
||||
*/
|
||||
public function makeTwigTokenParsers($parsers = [])
|
||||
{
|
||||
if (!is_array($parsers))
|
||||
$parsers = [];
|
||||
|
||||
$extraParsers = $this->listTokenParsers();
|
||||
foreach ($extraParsers as $obj) {
|
||||
if (!$obj instanceof Twig_TokenParser)
|
||||
continue;
|
||||
|
||||
$parsers[] = $obj;
|
||||
}
|
||||
|
||||
return $parsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a callable type contains a wildcard, also acts as a
|
||||
* utility to replace the wildcard with a string.
|
||||
* @param callable $callable
|
||||
* @param string $replaceWith
|
||||
* @return mixed
|
||||
*/
|
||||
protected function isWildCallable($callable, $replaceWith = false)
|
||||
{
|
||||
$isWild = false;
|
||||
|
||||
if (is_string($callable) && strpos($callable, '*') !== false)
|
||||
$isWild = $replaceWith ? str_replace('*', $replaceWith, $callable) : true;
|
||||
|
||||
if (is_array($callable)) {
|
||||
if (is_string($callable[0]) && strpos($callable[0], '*') !== false) {
|
||||
if ($replaceWith) {
|
||||
$isWild = $callable;
|
||||
$isWild[0] = str_replace('*', $replaceWith, $callable[0]);
|
||||
}
|
||||
else
|
||||
$isWild = true;
|
||||
}
|
||||
|
||||
if (!empty($callable[1]) && strpos($callable[1], '*') !== false) {
|
||||
if ($replaceWith) {
|
||||
$isWild = $isWild ?: $callable;
|
||||
$isWild[1] = str_replace('*', $replaceWith, $callable[1]);
|
||||
}
|
||||
else
|
||||
$isWild = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $isWild;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -52,12 +52,7 @@ class Extension extends Twig_Extension
|
|||
/*
|
||||
* Include extensions provided by plugins
|
||||
*/
|
||||
foreach ($this->markupManager->listFunctions() as $name => $callable) {
|
||||
if (!is_callable($callable))
|
||||
throw new ApplicationException(sprintf('The markup function for %s is not callable.', $name));
|
||||
|
||||
$functions[] = new Twig_SimpleFunction($name, $callable, ['is_safe' => ['html']]);
|
||||
}
|
||||
$functions = $this->markupManager->makeTwigFunctions($functions);
|
||||
|
||||
return $functions;
|
||||
}
|
||||
|
|
@ -76,12 +71,7 @@ class Extension extends Twig_Extension
|
|||
/*
|
||||
* Include extensions provided by plugins
|
||||
*/
|
||||
foreach ($this->markupManager->listFilters() as $name => $callable) {
|
||||
if (!is_callable($callable))
|
||||
throw new ApplicationException(sprintf('The markup filter for %s is not callable.', $name));
|
||||
|
||||
$filters[] = new Twig_SimpleFilter($name, $callable, ['is_safe' => ['html']]);
|
||||
}
|
||||
$filters = $this->markupManager->makeTwigFilters($filters);
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
|
@ -95,13 +85,10 @@ class Extension extends Twig_Extension
|
|||
{
|
||||
$parsers = [];
|
||||
|
||||
$extraParsers = $this->markupManager->listTokenParsers();
|
||||
foreach ($extraParsers as $obj) {
|
||||
if (!$obj instanceof Twig_TokenParser)
|
||||
continue;
|
||||
|
||||
$parsers[] = $obj;
|
||||
}
|
||||
/*
|
||||
* Include extensions provided by plugins
|
||||
*/
|
||||
$parsers = $this->markupManager->makeTwigTokenParsers($parsers);
|
||||
|
||||
return $parsers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use Cms\Classes\Theme;
|
|||
|
||||
class ControllerTest extends TestCase
|
||||
{
|
||||
public function tearDown() {
|
||||
public function tearDown()
|
||||
{
|
||||
Mockery::close();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
use System\Classes\MarkupManager;
|
||||
|
||||
class MarkupManagerTest extends TestCase
|
||||
{
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
include_once base_path().'/tests/fixtures/system/plugins/october/test/Plugin.php';
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
protected static function callProtectedMethod($object, $name, $params = [])
|
||||
{
|
||||
$className = get_class($object);
|
||||
$class = new ReflectionClass($className);
|
||||
$method = $class->getMethod($name);
|
||||
$method->setAccessible(true);
|
||||
return $method->invokeArgs($object, $params);
|
||||
}
|
||||
|
||||
public static function getProtectedProperty($object, $name)
|
||||
{
|
||||
$className = get_class($object);
|
||||
$class = new ReflectionClass($className);
|
||||
$property = $class->getProperty($name);
|
||||
$property->setAccessible(true);
|
||||
return $property->getValue($object);
|
||||
}
|
||||
|
||||
public static function setProtectedProperty($object, $name, $value)
|
||||
{
|
||||
$className = get_class($object);
|
||||
$class = new ReflectionClass($className);
|
||||
$property = $class->getProperty($name);
|
||||
$property->setAccessible(true);
|
||||
return $property->setValue($object, $value);
|
||||
}
|
||||
|
||||
//
|
||||
// Tests
|
||||
//
|
||||
|
||||
public function testIsWildCallable()
|
||||
{
|
||||
$manager = MarkupManager::instance();
|
||||
|
||||
/*
|
||||
* Negatives
|
||||
*/
|
||||
$callable = 'something';
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertFalse($result);
|
||||
|
||||
$callable = ['Form', 'open'];
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertFalse($result);
|
||||
|
||||
$callable = function() { return 'O, Hai!'; };
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertFalse($result);
|
||||
|
||||
/*
|
||||
* String
|
||||
*/
|
||||
$callable = 'something_*';
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'delicious']);
|
||||
$this->assertEquals('something_delicious', $result);
|
||||
|
||||
/*
|
||||
* Array
|
||||
*/
|
||||
$callable = ['Class', 'foo_*'];
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'bar']);
|
||||
$this->assertTrue(isset($result[0]));
|
||||
$this->assertTrue(isset($result[1]));
|
||||
$this->assertEquals('Class', $result[0]);
|
||||
$this->assertEquals('foo_bar', $result[1]);
|
||||
|
||||
$callable = ['My*', 'method'];
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'Class']);
|
||||
$this->assertTrue(isset($result[0]));
|
||||
$this->assertTrue(isset($result[1]));
|
||||
$this->assertEquals('MyClass', $result[0]);
|
||||
$this->assertEquals('method', $result[1]);
|
||||
|
||||
$callable = ['My*', 'my*'];
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'Food']);
|
||||
$this->assertTrue(isset($result[0]));
|
||||
$this->assertTrue(isset($result[1]));
|
||||
$this->assertEquals('MyFood', $result[0]);
|
||||
$this->assertEquals('myFood', $result[1]);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue