Merge branch 'stable' into ui

This commit is contained in:
alekseybobkov 2016-02-19 22:13:25 -08:00
commit 839eb02136
14 changed files with 171 additions and 116 deletions

View File

@ -34,7 +34,7 @@
"require-dev": {
"fzaninotto/faker": "~1.4",
"phpunit/phpunit": "~4.0",
"phpunit/phpunit-selenium": ">=1.2"
"phpunit/phpunit-selenium": "~1.2"
},
"autoload-dev": {
"classmap": [

View File

@ -613,8 +613,6 @@ class RelationController extends ControllerBehavior
$this->relationObject->addConstraints();
}
$this->controller->relationExtendQuery($query, $this->field);
/*
* Allows pivot data to enter the fray
*/
@ -765,8 +763,6 @@ class RelationController extends ControllerBehavior
if (count($existingIds)) {
$query->whereNotIn($this->relationModel->getQualifiedKeyName(), $existingIds);
}
$this->controller->relationExtendQuery($query, $this->field);
});
}
@ -1204,20 +1200,6 @@ class RelationController extends ControllerBehavior
// Overrides
//
/**
* !!!!
* !!!! WARNING: DO NOT USE - This method is scheduled to be removed
* !!!!
*
* Controller override: Extend the query used for populating the list
* after the default query is processed.
* @param October\Rain\Database\Builder $query
* @param string $field
*/
public function relationExtendQuery($query, $field)
{
}
/**
* Provides an opportunity to manipulate the view widget.
* @param Backend\Classes\WidgetBase $widget

View File

@ -198,6 +198,41 @@ class Table extends WidgetBase
];
}
public function onServerCreateRecord()
{
if ($this->isClientDataSource()) {
throw new SystemException('The Table widget is not configured to use the server data source.');
}
$this->dataSource->createRecord(
post('recordData'),
post('placement'),
post('relativeToKey')
);
return $this->onServerGetRecords();
}
public function onServerUpdateRecord()
{
if ($this->isClientDataSource()) {
throw new SystemException('The Table widget is not configured to use the server data source.');
}
$this->dataSource->updateRecord(post('key'), post('recordData'));
}
public function onServerDeleteRecord()
{
if ($this->isClientDataSource()) {
throw new SystemException('The Table widget is not configured to use the server data source.');
}
$this->dataSource->deleteRecord(post('key'));
return $this->onServerGetRecords();
}
public function onGetDropdownOptions()
{
$columnName = Input::get('column');

View File

@ -106,7 +106,7 @@ The client memory data sources keeps the data in the client memory. The data is
### Server memory data source ($.oc.table.datasource.server)
**TODO:** document this
**TODO:** document this
## Cell processors ($.oc.table.processor)
@ -137,7 +137,7 @@ The drop-down column type can load options from the column configuration or with
blue: Blue
width: 15%
If the `options` element is not presented in the configuration, the options will be loaded with AJAX.
If the `options` element is not presented in the configuration, the options will be loaded with AJAX.
**TODO:** Document the AJAX interface
@ -203,7 +203,7 @@ Columns are defined as array with the `columns` property. The array keys corresp
- `title`
- `type` (string, checkbox, dropdown, autocomplete)
- `width` - sets the column width, can be specified in percents (10%) or pixels (50px). There could be a single column without width specified, it will be stretched to take the available space.
- `readOnly`
- `readOnly` - prevents the cell value from being modified. Default: false.
- `options` (for drop-down elements and autocomplete types)
- `dependsOn` (from drop-down elements)
- validation - defines the column client-side validation rules. See the **Client-side validation** section below.
@ -212,7 +212,7 @@ Columns are defined as array with the `columns` property. The array keys corresp
### table.getDropdownOptions
table.getDropdownOptions - triggered when drop-down options are requested by the client. Parameters:
table.getDropdownOptions - triggered when drop-down options are requested by the client. Parameters:
- `$columnName` - specifies the drop-down column name.
- `$rowData` - an array containing values of all columns in the table row.
@ -220,7 +220,7 @@ table.getDropdownOptions - triggered when drop-down options are requested by the
Example event handler:
```
$table->bindEvent('table.getDropdownOptions',
$table->bindEvent('table.getDropdownOptions',
function ($columnName, $rowData) {
if ($columnName == 'state')
return ['ca'=>'California', 'wa'=>'Washington'];

View File

@ -11,7 +11,7 @@ class ServerEventDataSource extends DataSourceBase
* Return records from the data source.
* @param integer $offset Specifies the offset of the first record to return, zero-based.
* @param integer $count Specifies the number of records to return.
* @return array Returns the records.
* @return array Returns the records.
* If there are no more records, returns an empty array.
*/
public function getRecords($offset, $count)
@ -28,6 +28,33 @@ class ServerEventDataSource extends DataSourceBase
return $this->fireEvent('data.getCount', [], true);
}
/**
* Updates a record in the data source.
* @return void
*/
public function createRecord($data, $placement, $relativeToKey)
{
return $this->fireEvent('data.createRecord', [$data, $placement, $relativeToKey]);
}
/**
* Updates a record in the data source.
* @return void
*/
public function updateRecord($key, $data)
{
$this->fireEvent('data.updateRecord', [$key, $data]);
}
/**
* Removes a record from the data source.
* @return array Returns the remaining records.
*/
public function deleteRecord($key)
{
return $this->fireEvent('data.deleteRecord', [$key], true);
}
/**
* Initializes records in the data source.
* The method doesn't replace existing records and

View File

@ -85,7 +85,8 @@ if(this.options.adding){var addBelowButton=document.createElement('a')
addBelowButton.setAttribute('class','btn table-icon add-table-row-below')
addBelowButton.setAttribute('data-cmd','record-add-below')
this.toolbar.appendChild(addBelowButton)
if(this.navigation.paginationEnabled()||!this.options.rowSorting){addBelowButton.textContent=this.options.btnAddRowLabel}else{addBelowButton.textContent=this.options.btnAddRowBelowLabel
if(this.navigation.paginationEnabled()||!this.options.rowSorting){addBelowButton.textContent=this.options.btnAddRowLabel}
else{addBelowButton.textContent=this.options.btnAddRowBelowLabel
var addAboveButton=document.createElement('a')
addAboveButton.setAttribute('class','btn table-icon add-table-row-above')
addAboveButton.textContent='Add row above'
@ -124,6 +125,7 @@ if(onSuccess)
onSuccess()
if(totalCount==0)
self.addRecord('above',true)
self.$el.trigger('oc.tableUpdateData',[records,totalCount])
self=null})}
Table.prototype.updateColumnWidth=function(){var headerCells=this.headerTable.querySelectorAll('th'),dataCells=this.dataTable.querySelectorAll('tr:first-child td')
for(var i=0,len=headerCells.length;i<len;i++){if(dataCells[i])
@ -491,7 +493,8 @@ return
var row=this.tableObj.activeCell.parentNode,newRow=(!ev.shiftKey||isTab)?row.previousElementSibling:row.parentNode.children[0],cellIndex=forceCellIndex!==undefined?forceCellIndex:this.tableObj.activeCell.cellIndex
if(newRow){var cell=newRow.children[cellIndex]
if(cell)
this.tableObj.focusCell(cell)}else{if(!this.paginationEnabled())
this.tableObj.focusCell(cell)}
else{if(!this.paginationEnabled())
return
if(this.pageIndex>0){var self=this
this.gotoPage(this.pageIndex-1,function navUpPageSuccess(){self.focusCell('bottom',cellIndex)
@ -502,8 +505,7 @@ if(!isTab&&this.tableObj.activeCellProcessor&&!this.tableObj.activeCellProcessor
return
var row=this.tableObj.activeCell.parentNode,newIndex=(!ev.shiftKey||isTab)?this.tableObj.activeCell.cellIndex-1:0
var cell=row.children[newIndex]
if(cell)
this.tableObj.focusCell(cell)
if(cell){this.tableObj.focusCell(cell)}
else{this.navigateUp(ev,row.children.length-1,isTab)}}
Navigation.prototype.navigateRight=function(ev,isTab){if(!this.tableObj.activeCell)
return
@ -511,8 +513,7 @@ if(!isTab&&this.tableObj.activeCellProcessor&&!this.tableObj.activeCellProcessor
return
var row=this.tableObj.activeCell.parentNode,newIndex=!ev.shiftKey?this.tableObj.activeCell.cellIndex+1:row.children.length-1
var cell=row.children[newIndex]
if(cell)
this.tableObj.focusCell(cell)
if(cell){this.tableObj.focusCell(cell)}
else{this.navigateDown(ev,0)}}
Navigation.prototype.navigateNext=function(ev){if(!this.tableObj.activeCell)
return
@ -524,8 +525,7 @@ else
this.navigateLeft(ev,true)
this.tableObj.stopEvent(ev)}
Navigation.prototype.focusCell=function(rowReference,cellIndex){var row=null,tbody=this.tableObj.getDataTableBody()
if(typeof rowReference==='object')
row=rowReference
if(typeof rowReference==='object'){row=rowReference}
else{if(rowReference=='bottom'){row=tbody.children[tbody.children.length-1]}
else if(rowReference=='top'){row=tbody.children[0]}}
if(!row)
@ -533,8 +533,7 @@ return
var cell=row.children[cellIndex]
if(cell)
this.tableObj.focusCell(cell)}
Navigation.prototype.focusCellInReplacedRow=function(rowIndex,cellIndex){if(rowIndex==0)
this.focusCell('top',cellIndex)
Navigation.prototype.focusCellInReplacedRow=function(rowIndex,cellIndex){if(rowIndex==0){this.focusCell('top',cellIndex)}
else{var focusRow=this.tableObj.findRowByIndex(rowIndex)
if(!focusRow)
focusRow=this.tableObj.findRowByIndex(rowIndex-1)
@ -614,10 +613,13 @@ Server.prototype.constructor=Server
Server.prototype.dispose=function(){BaseProto.dispose.call(this)
this.data=null}
Server.prototype.getRecords=function(offset,count,onSuccess){var handlerName=this.tableObj.getAlias()+'::onServerGetRecords'
$.request(handlerName,{data:{offset:offset,count:count}}).done(function(data){onSuccess(data.records,data.count)})}
Server.prototype.createRecord=function(recordData,placement,relativeToKey,offset,count,onSuccess){console.log('createRecord')}
Server.prototype.updateRecord=function(key,recordData){console.log('updateRecord')}
Server.prototype.deleteRecord=function(key,newRecordData,offset,count,onSuccess){console.log('deleteRecord')}
this.tableObj.$el.request(handlerName,{data:{offset:offset,count:count}}).done(function(data){onSuccess(data.records,data.count)})}
Server.prototype.createRecord=function(recordData,placement,relativeToKey,offset,count,onSuccess){var handlerName=this.tableObj.getAlias()+'::onServerCreateRecord'
this.tableObj.$el.request(handlerName,{data:{recordData:recordData,placement:placement,relativeToKey:relativeToKey,offset:offset,count:count}}).done(function(data){onSuccess(data.records,data.count)})}
Server.prototype.updateRecord=function(key,recordData){var handlerName=this.tableObj.getAlias()+'::onServerUpdateRecord'
this.tableObj.$el.request(handlerName,{data:{key:key,recordData:recordData}})}
Server.prototype.deleteRecord=function(key,newRecordData,offset,count,onSuccess){var handlerName=this.tableObj.getAlias()+'::onServerDeleteRecord'
this.tableObj.$el.request(handlerName,{data:{key:key,offset:offset,count:count}}).done(function(data){onSuccess(data.records,data.count)})}
$.oc.table.datasource.server=Server}(window.jQuery);+function($){"use strict";if($.oc.table===undefined)
throw new Error("The $.oc.table namespace is not defined. Make sure that the table.js script is loaded.");if($.oc.table.processor===undefined)
$.oc.table.processor={}
@ -688,6 +690,7 @@ var input=document.createElement('input')
input.setAttribute('type','text')
input.setAttribute('class','string-input')
input.value=this.tableObj.getCellValue(cellElement)
if(this.columnConfiguration.readOnly){input.setAttribute('readonly',true)}
cellContentContainer.appendChild(input)
this.setCaretPosition(input,0)
window.setTimeout(this.focusTimeoutHandler,0)}
@ -827,8 +830,7 @@ if(!activeItemElement){activeItemElement=this.itemListElement.querySelector('ul
if(activeItemElement)
activeItemElement.setAttribute('class','selected')}
if(activeItemElement){window.setTimeout(function(){activeItemElement.focus()},0)}}}
DropdownProcessor.prototype.fetchOptions=function(cellElement,onSuccess){if(this.columnConfiguration.options)
onSuccess(this.columnConfiguration.options)
DropdownProcessor.prototype.fetchOptions=function(cellElement,onSuccess){if(this.columnConfiguration.options){onSuccess(this.columnConfiguration.options)}
else{var row=cellElement.parentNode,cachingKey=this.createOptionsCachingKey(row),viewContainer=this.getViewContainer(cellElement)
viewContainer.setAttribute('class','loading')
if(!this.cachedOptionPromises[cachingKey]){var requestData={column:this.columnName,rowData:this.tableObj.getRowData(row)},handlerName=this.tableObj.getAlias()+'::onGetDropdownOptions'

View File

@ -47,7 +47,7 @@
*/
Server.prototype.getRecords = function(offset, count, onSuccess) {
var handlerName = this.tableObj.getAlias()+'::onServerGetRecords'
$.request(handlerName, {
this.tableObj.$el.request(handlerName, {
data: {
offset: offset,
count: count
@ -71,21 +71,18 @@
* The onSuccess callback parameters: records, totalCount.
*/
Server.prototype.createRecord = function(recordData, placement, relativeToKey, offset, count, onSuccess) {
console.log('createRecord')
// if (placement === 'bottom') {
// // Add record to the bottom of the dataset
// this.data.push(recordData)
// }
// else if (placement == 'above' || placement == 'below') {
// // Add record above or below the passed record key
// var recordIndex = this.getIndexOfKey(relativeToKey)
// if (placement == 'below')
// recordIndex ++
// this.data.splice(recordIndex, 0, recordData)
// }
// this.getRecords(offset, count, onSuccess)
var handlerName = this.tableObj.getAlias()+'::onServerCreateRecord'
this.tableObj.$el.request(handlerName, {
data: {
recordData: recordData,
placement: placement,
relativeToKey: relativeToKey,
offset: offset,
count: count
}
}).done(function(data) {
onSuccess(data.records, data.count)
})
}
/*
@ -95,16 +92,13 @@
* - recordData - the record fields.
*/
Server.prototype.updateRecord = function(key, recordData) {
console.log('updateRecord')
// var recordIndex = this.getIndexOfKey(key)
// if (recordIndex !== -1) {
// recordData[this.tableObj.options.keyColumn] = key
// this.data[recordIndex] = recordData
// }
// else {
// throw new Error('Record with they key '+key+ ' is not found in the data set')
// }
var handlerName = this.tableObj.getAlias()+'::onServerUpdateRecord'
this.tableObj.$el.request(handlerName, {
data: {
key: key,
recordData: recordData
}
})
}
/*
@ -120,20 +114,16 @@
* The onSuccess callback parameters: records, totalCount.
*/
Server.prototype.deleteRecord = function(key, newRecordData, offset, count, onSuccess) {
console.log('deleteRecord')
// var recordIndex = this.getIndexOfKey(key)
// if (recordIndex !== -1) {
// this.data.splice(recordIndex, 1)
// if (this.data.length == 0)
// this.data.push(newRecordData)
// this.getRecords(offset, count, onSuccess)
// }
// else {
// throw new Error('Record with they key '+key+ ' is not found in the data set')
// }
var handlerName = this.tableObj.getAlias()+'::onServerDeleteRecord'
this.tableObj.$el.request(handlerName, {
data: {
key: key,
offset: offset,
count: count
}
}).done(function(data) {
onSuccess(data.records, data.count)
})
}
$.oc.table.datasource.server = Server

View File

@ -174,7 +174,7 @@
var curRecordCount = this.getRecordCount()
if (placement === 'bottom')
return this.calculatePageCount(curRecordCount+1, this.tableObj.options.recordsPerPage)-1
return this.calculatePageCount(curRecordCount + 1, this.tableObj.options.recordsPerPage) - 1
// When a row is added above a current row, the current row just moves down,
// so it's safe to return the current page index
@ -182,8 +182,8 @@
return this.pageIndex
if (placement == 'below') {
if (currentRowIndex == (this.tableObj.options.recordsPerPage-1))
return this.pageIndex+1
if (currentRowIndex == (this.tableObj.options.recordsPerPage - 1))
return this.pageIndex + 1
return this.pageIndex
}
@ -193,7 +193,7 @@
Navigation.prototype.getPageAfterDeletion = function(currentRowIndex) {
if (currentRowIndex == 0 && this.getRowCountOnPage() == 1)
return this.pageIndex == 0 ? 0 : this.pageIndex-1
return this.pageIndex == 0 ? 0 : this.pageIndex - 1
return this.pageIndex
}
@ -228,10 +228,10 @@
if (!this.paginationEnabled())
return
if (this.pageIndex < this.pageCount-1) {
if (this.pageIndex < this.pageCount - 1) {
var self = this
this.gotoPage(this.pageIndex+1, function navDownPageSuccess(){
this.gotoPage(this.pageIndex + 1, function navDownPageSuccess(){
self.focusCell('top', cellIndex)
self = null
})
@ -247,19 +247,20 @@
return
var row = this.tableObj.activeCell.parentNode,
newRow = (!ev.shiftKey || isTab) ?
row.previousElementSibling :
row.parentNode.children[0],
cellIndex = forceCellIndex !== undefined ?
forceCellIndex :
this.tableObj.activeCell.cellIndex
newRow = (!ev.shiftKey || isTab)
? row.previousElementSibling
: row.parentNode.children[0],
cellIndex = forceCellIndex !== undefined
? forceCellIndex
: this.tableObj.activeCell.cellIndex
if (newRow) {
var cell = newRow.children[cellIndex]
if (cell)
this.tableObj.focusCell(cell)
} else {
}
else {
// Try to switch to the previous page if that's possible
if (!this.paginationEnabled())
@ -268,7 +269,7 @@
if (this.pageIndex > 0) {
var self = this
this.gotoPage(this.pageIndex-1, function navUpPageSuccess(){
this.gotoPage(this.pageIndex - 1, function navUpPageSuccess(){
self.focusCell('bottom', cellIndex)
self = null
})
@ -284,17 +285,18 @@
return
var row = this.tableObj.activeCell.parentNode,
newIndex = (!ev.shiftKey || isTab) ?
this.tableObj.activeCell.cellIndex-1 :
0
newIndex = (!ev.shiftKey || isTab)
? this.tableObj.activeCell.cellIndex - 1
: 0
var cell = row.children[newIndex]
if (cell)
if (cell) {
this.tableObj.focusCell(cell)
}
else {
// Try to navigate up if that's possible
this.navigateUp(ev, row.children.length-1, isTab)
this.navigateUp(ev, row.children.length - 1, isTab)
}
}
@ -306,14 +308,15 @@
return
var row = this.tableObj.activeCell.parentNode,
newIndex = !ev.shiftKey ?
this.tableObj.activeCell.cellIndex+1 :
row.children.length-1
newIndex = !ev.shiftKey
? this.tableObj.activeCell.cellIndex + 1
: row.children.length - 1
var cell = row.children[newIndex]
if (cell)
if (cell) {
this.tableObj.focusCell(cell)
}
else {
// Try to navigate down if that's possible
this.navigateDown(ev, 0)
@ -339,12 +342,13 @@
var row = null,
tbody = this.tableObj.getDataTableBody()
if (typeof rowReference === 'object')
if (typeof rowReference === 'object') {
row = rowReference
}
else {
if (rowReference == 'bottom') {
row = tbody.children[tbody.children.length-1]
}
}
else if (rowReference == 'top') {
row = tbody.children[0]
}
@ -359,8 +363,9 @@
}
Navigation.prototype.focusCellInReplacedRow = function(rowIndex, cellIndex) {
if (rowIndex == 0)
if (rowIndex == 0) {
this.focusCell('top', cellIndex)
}
else {
var focusRow = this.tableObj.findRowByIndex(rowIndex)

View File

@ -215,7 +215,7 @@
if (!this.options.height)
this.dataTableContainer = this.tableContainer
else
else
this.dataTableContainer = this.buildScrollbar()
// Build the data table
@ -240,7 +240,8 @@
// new records can only be added to the bottom of the
// table.
addBelowButton.textContent = this.options.btnAddRowLabel
} else {
}
else {
addBelowButton.textContent = this.options.btnAddRowBelowLabel
var addAboveButton = document.createElement('a')
@ -313,7 +314,8 @@
this.unfocusTable()
this.fetchRecords(function onUpdateDataTableSuccess(records, totalCount){
this.fetchRecords(function onUpdateDataTableSuccess(records, totalCount) {
self.buildDataTable(records, totalCount)
if (onSuccess)
@ -322,6 +324,11 @@
if (totalCount == 0)
self.addRecord('above', true)
self.$el.trigger('oc.tableUpdateData', [
records,
totalCount
])
self = null
})
}
@ -526,7 +533,7 @@
this.elementAddClass(this.activeCell, 'active')
}
// If the cell belongs to other row than the currently edited,
// If the cell belongs to other row than the currently edited,
// commit currently edited row to the data source. Update the
// currently edited row key.
var rowKey = this.getCellRowKey(cellElement)
@ -583,7 +590,7 @@
recordData = {},
self = this
recordData[keyColumn] = -1*this.recordsAddedOrDeleted
recordData[keyColumn] = -1 * this.recordsAddedOrDeleted
this.$el.trigger('oc.tableNewRow', [
recordData

View File

@ -174,7 +174,7 @@
/*
* Determines whether the specified element is some element created by the
* processor.
* processor.
*/
Base.prototype.elementBelongsToProcessor = function(element) {
return false

View File

@ -192,8 +192,9 @@
}
DropdownProcessor.prototype.fetchOptions = function(cellElement, onSuccess) {
if (this.columnConfiguration.options)
if (this.columnConfiguration.options) {
onSuccess(this.columnConfiguration.options)
}
else {
// If options are not provided and not found in the cache,
// request them from the server. For dependent drop-downs

View File

@ -91,11 +91,16 @@
input.setAttribute('type', 'text')
input.setAttribute('class', 'string-input')
input.value = this.tableObj.getCellValue(cellElement)
if (this.columnConfiguration.readOnly) {
input.setAttribute('readonly', true)
}
cellContentContainer.appendChild(input)
this.setCaretPosition(input, 0)
// Focus the element in the next frame.
// Focus the element in the next frame.
// http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful
window.setTimeout(this.focusTimeoutHandler, 0)
}

View File

@ -26,6 +26,7 @@ class OctoberMirror extends Command
protected $description = '(Experimental) Generates a mirrored public folder using symbolic links.';
protected $files = [
'.htaccess',
'index.php',
'favicon.ico',
'robots.txt',

View File

@ -57,7 +57,7 @@ trait PropertyContainer
*/
public function setProperties($properties)
{
return $this->properties = $this->validateProperties($properties);
$this->properties = $this->validateProperties($properties);
}
/**