mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
ENHANCEMENT Allowing batch checkbox selection of TableListField rows with TableListField->Markable and TableListField->addSelectOptions()
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/branches/2.4@105266 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
ca873121fa
commit
ba2aabd702
@ -181,6 +181,18 @@ form .TableField .message {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.TableListField .selectOptions {
|
||||
overflow: auto;
|
||||
font: 1.3em;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.TableListField .selectOptions li {
|
||||
float: left;
|
||||
margin: 0px 5px;
|
||||
}
|
||||
|
||||
.TableListField .PageControls {
|
||||
margin: 5px 0;
|
||||
text-align:center;
|
||||
|
@ -88,6 +88,11 @@ class TableListField extends FormField {
|
||||
|
||||
public $MarkableTitle = null;
|
||||
|
||||
/**
|
||||
* @var array See {@link SelectOptions()}
|
||||
*/
|
||||
protected $selectOptions = array();
|
||||
|
||||
/**
|
||||
* @var $readOnly boolean Deprecated, please use $permssions instead
|
||||
*/
|
||||
@ -1199,14 +1204,57 @@ JS
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* #########################
|
||||
* Highlighting
|
||||
* #########################
|
||||
*/
|
||||
function setHighlightConditions($conditions) {
|
||||
$this->highlightConditions = $conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link SelectOptions()} for introduction.
|
||||
*
|
||||
* @param $options array Options to add, key being a unique identifier of the action,
|
||||
* and value a title for the rendered link element (can contain HTML).
|
||||
* The keys for 'all' and 'none' have special behaviour associated
|
||||
* through TableListField.js JavaScript.
|
||||
* For any other key, the JavaScript automatically checks all checkboxes contained in
|
||||
* <td> elements with a matching classname.
|
||||
*/
|
||||
function addSelectOptions($options){
|
||||
foreach($options as $k => $title)
|
||||
$this->selectOptions[$k] = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one all more table's {@link $selectOptions}
|
||||
*
|
||||
* @param $optionsNames array
|
||||
*/
|
||||
function removeSelectOptions($names){
|
||||
foreach($names as $name){
|
||||
unset($this->selectOptions[trim($name)]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the table's {@link $selectOptions}.
|
||||
* Used to toggle checkboxes for each table row through button elements.
|
||||
*
|
||||
* Requires {@link Markable()} to return TRUE.
|
||||
* This is only functional with JavaScript enabled.
|
||||
*
|
||||
* @return DataObjectSet of ArrayData objects
|
||||
*/
|
||||
function SelectOptions(){
|
||||
if(!$this->selectOptions) return;
|
||||
|
||||
$selectOptionsSet = new DataObjectSet();
|
||||
foreach($this->selectOptions as $k => $v) {
|
||||
$selectOptionsSet->push(new ArrayData(array(
|
||||
'Key' => $k,
|
||||
'Value' => $v
|
||||
)));
|
||||
}
|
||||
return $selectOptionsSet;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1381,6 +1429,27 @@ class TableListField_Item extends ViewableData {
|
||||
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\" />";
|
||||
}
|
||||
|
||||
/**
|
||||
* According to {@link TableListField->selectOptions}, each record will check if the options' key on the object is true,
|
||||
* if it is true, add the key as a class to the record
|
||||
*
|
||||
* @return string Value for a 'class' HTML attribute.
|
||||
*/
|
||||
function SelectOptionClasses(){
|
||||
$tagArray = array('markingcheckbox');
|
||||
$options = $this->parent->selectOptions;
|
||||
if(!empty($options)){
|
||||
foreach($options as $optionKey => $optionTitle){
|
||||
if($optionKey !== 'all' && $optionKey !== 'none'){
|
||||
if($this->$optionKey) {
|
||||
$tagArray[] = $optionKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode(" ",$tagArray);
|
||||
}
|
||||
|
||||
function HighlightClasses() {
|
||||
$classes = array();
|
||||
foreach($this->parent->highlightConditions as $condition) {
|
||||
|
@ -5,7 +5,7 @@ TableListField.prototype = {
|
||||
|
||||
initialize: function() {
|
||||
var rules = {};
|
||||
|
||||
|
||||
rules['#'+this.id+' table.data a.deletelink'] = {
|
||||
onclick: this.deleteRecord.bind(this)
|
||||
};
|
||||
@ -36,7 +36,12 @@ TableListField.prototype = {
|
||||
// do nothing for clicks in marking box cells (e.g. if checkbox is missed)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// rules for selection options on click event
|
||||
rules['#'+this.id+' .selectOptions a'] = {
|
||||
onclick: this.markRecords.bind(this)
|
||||
};
|
||||
|
||||
// initialize summary (if needed)
|
||||
// TODO Breaks with nested divs
|
||||
var summaryCols = $$('tfoot tr.summary td', this);
|
||||
@ -122,6 +127,50 @@ TableListField.prototype = {
|
||||
this._summarise();
|
||||
},
|
||||
|
||||
/**
|
||||
* according to the clicked element in "Select bar", mark records that have same class as the element.
|
||||
*/
|
||||
markRecords: function(e){
|
||||
var el = Event.element(e);
|
||||
if(el.nodeName != "a") el = Event.findElement(e,"a");
|
||||
|
||||
if(el.rel == "all"){
|
||||
this.markAll();
|
||||
}else if(el.rel == 'none') {
|
||||
this.unmarkAll();
|
||||
}else{
|
||||
this.unmarkAll();
|
||||
var records = $$('#' + this.id + ' td.' + el.rel + ' input.checkbox');
|
||||
var i=0;
|
||||
for(i; i<records.length; i++){
|
||||
records[i].checked = 'checked';
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* mark all record in current view of the table
|
||||
*/
|
||||
markAll: function(e){
|
||||
var records = $$('#'+this.id+' td.markingcheckbox input.checkbox');
|
||||
var i=0;
|
||||
for(i; i<records.length; i++){
|
||||
records[i].checked = 'checked';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* unmark all records in current view of the table
|
||||
*/
|
||||
unmarkAll: function(e){
|
||||
var records = $$('#'+this.id+' td.markingcheckbox input.checkbox');
|
||||
var i=0;
|
||||
for(i; i<records.length; i++){
|
||||
records[i].checked = '';
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function(e) {
|
||||
if(e) {
|
||||
var el = Event.element(e);
|
||||
|
@ -1,5 +1,8 @@
|
||||
<div id="$id" class="$CSSClasses field" href="$CurrentLink">
|
||||
<div class="middleColumn">
|
||||
<% if Markable %>
|
||||
<% include TableListField_SelectOptions %>
|
||||
<% end_if %>
|
||||
<% include TableListField_PageControls %>
|
||||
<table class="data">
|
||||
<thead>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<tr id="record-$Parent.id-$ID"<% if HighlightClasses %> class="$HighlightClasses"<% end_if %>>
|
||||
<% if Markable %><td width="16" class="markingcheckbox">$MarkingCheckbox</td><% end_if %>
|
||||
<% if Markable %><td width="16" class="$SelectOptionClasses">$MarkingCheckbox</td><% end_if %>
|
||||
<% control Fields %>
|
||||
<td class="field-$Title.HTMLATT $FirstLast $Name">$Value</td>
|
||||
<% end_control %>
|
||||
|
8
templates/Includes/TableListField_SelectOptions.ss
Normal file
8
templates/Includes/TableListField_SelectOptions.ss
Normal file
@ -0,0 +1,8 @@
|
||||
<% if SelectOptions %>
|
||||
<ul class="selectOptions">
|
||||
<li><% _t('TableListField.SELECT', 'Select:') %></li>
|
||||
<% control SelectOptions %>
|
||||
<li><a rel="$Key" href="#" title="$Key">$Value</a></li>
|
||||
<% end_control %>
|
||||
</ul>
|
||||
<% end_if %>
|
@ -1,4 +1,7 @@
|
||||
<div id="$id" class="$CSSClasses" href="$CurrentLink">
|
||||
<% if Markable %>
|
||||
<% include TableListField_SelectOptions %>
|
||||
<% end_if %>
|
||||
<% include TableListField_PageControls %>
|
||||
<table class="data">
|
||||
<thead>
|
||||
|
@ -1,5 +1,10 @@
|
||||
<div id="$id" class="$CSSClasses field">
|
||||
<% if Print %><% else %><% include TableListField_PageControls %><% end_if %>
|
||||
<% if Print %><% else %>
|
||||
<% if Markable %>
|
||||
<% include TableListField_SelectOptions %>
|
||||
<% end_if %>
|
||||
<% include TableListField_PageControls %>
|
||||
<% end_if %>
|
||||
<table class="data">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -22,7 +22,7 @@ class TableListFieldTest extends SapphireTest {
|
||||
), new FieldSet());
|
||||
|
||||
$result = $table->FieldHolder();
|
||||
|
||||
|
||||
// Do a quick check to ensure that some of the D() and getE() values got through
|
||||
$this->assertRegExp('/>\s*a2\s*</', $result);
|
||||
$this->assertRegExp('/>\s*a2\/b2\/c2\s*</', $result);
|
||||
@ -36,7 +36,7 @@ class TableListFieldTest extends SapphireTest {
|
||||
$item4 = $this->objFromFixture('TableListFieldTest_Obj', 'four');
|
||||
$item5 = $this->objFromFixture('TableListFieldTest_Obj', 'five');
|
||||
|
||||
/* In this simple case, the source items should just list all the data objects specified */
|
||||
// In this simple case, the source items should just list all the data objects specified
|
||||
$table = new TableListField("Tester", "TableListFieldTest_Obj", array(
|
||||
"A" => "Col A",
|
||||
"B" => "Col B",
|
||||
@ -48,7 +48,7 @@ class TableListFieldTest extends SapphireTest {
|
||||
$form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
|
||||
$table
|
||||
), new FieldSet());
|
||||
|
||||
|
||||
$items = $table->sourceItems();
|
||||
$this->assertNotNull($items);
|
||||
|
||||
@ -61,7 +61,7 @@ class TableListFieldTest extends SapphireTest {
|
||||
$item5->ID => "a5"
|
||||
), $itemMap);
|
||||
}
|
||||
|
||||
|
||||
function testFirstPageOfPaginatedSourceItemGeneration() {
|
||||
$item1 = $this->objFromFixture('TableListFieldTest_Obj', 'one');
|
||||
$item2 = $this->objFromFixture('TableListFieldTest_Obj', 'two');
|
||||
@ -69,7 +69,7 @@ class TableListFieldTest extends SapphireTest {
|
||||
$item4 = $this->objFromFixture('TableListFieldTest_Obj', 'four');
|
||||
$item5 = $this->objFromFixture('TableListFieldTest_Obj', 'five');
|
||||
|
||||
/* With pagination enabled, only the first page of items should be shown */
|
||||
// With pagination enabled, only the first page of items should be shown
|
||||
$table = new TableListField("Tester", "TableListFieldTest_Obj", array(
|
||||
"A" => "Col A",
|
||||
"B" => "Col B",
|
||||
@ -81,13 +81,13 @@ class TableListFieldTest extends SapphireTest {
|
||||
$form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
|
||||
$table
|
||||
), new FieldSet());
|
||||
|
||||
|
||||
$table->ShowPagination = true;
|
||||
$table->PageSize = 2;
|
||||
|
||||
$items = $table->sourceItems();
|
||||
$this->assertNotNull($items);
|
||||
|
||||
|
||||
$itemMap = $items->toDropdownMap("ID", "A") ;
|
||||
$this->assertEquals(array(
|
||||
$item1->ID => "a1",
|
||||
@ -102,7 +102,7 @@ class TableListFieldTest extends SapphireTest {
|
||||
$item4 = $this->objFromFixture('TableListFieldTest_Obj', 'four');
|
||||
$item5 = $this->objFromFixture('TableListFieldTest_Obj', 'five');
|
||||
|
||||
/* With pagination enabled, only the first page of items should be shown */
|
||||
// With pagination enabled, only the first page of items should be shown
|
||||
$table = new TableListField("Tester", "TableListFieldTest_Obj", array(
|
||||
"A" => "Col A",
|
||||
"B" => "Col B",
|
||||
@ -114,18 +114,46 @@ class TableListFieldTest extends SapphireTest {
|
||||
$form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
|
||||
$table
|
||||
), new FieldSet());
|
||||
|
||||
|
||||
$table->ShowPagination = true;
|
||||
$table->PageSize = 2;
|
||||
$_REQUEST['ctf']['Tester']['start'] = 2;
|
||||
|
||||
$items = $table->sourceItems();
|
||||
$this->assertNotNull($items);
|
||||
|
||||
|
||||
$itemMap = $items->toDropdownMap("ID", "A") ;
|
||||
$this->assertEquals(array($item3->ID => "a3", $item4->ID => "a4"), $itemMap);
|
||||
}
|
||||
|
||||
function testSelectOptionsAddRemove() {
|
||||
$table = new TableListField("Tester", "TableListFieldTest_Obj", array(
|
||||
"A" => "Col A",
|
||||
));
|
||||
$this->assertNull($table->SelectOptions(), 'Empty by default');
|
||||
|
||||
$table->addSelectOptions(array("F"=>"FieldF", 'G'=>'FieldG'));
|
||||
$this->assertEquals($table->SelectOptions()->map('Key', 'Value'), array("F"=>"FieldF",'G'=>'FieldG'));
|
||||
|
||||
$table->removeSelectOptions(array("F"));
|
||||
$this->assertEquals($table->SelectOptions()->map('Key', 'Value'), array("G"=>"FieldG"));
|
||||
}
|
||||
|
||||
function testSelectOptionsRendering() {
|
||||
$table = new TableListField("Tester", "TableListFieldTest_Obj", array(
|
||||
"A" => "Col A",
|
||||
));
|
||||
$table->Markable = true;
|
||||
|
||||
$table->addSelectOptions(array("F"=>"FieldF"));
|
||||
$tableHTML = $table->FieldHolder();
|
||||
$this->assertContains('rel="F"', $tableHTML);
|
||||
|
||||
$this->assertRegExp('/<tr[^>]*id="record-Tester-1"[^>]*>[^<]*<td[^>]*class="markingcheckbox F"[^>]*>/si', $tableHTML);
|
||||
$this->assertRegExp('/<tr[^>]*id="record-Tester-2"[^>]*>[^<]*<td[^>]*class="markingcheckbox"[^>]*>/si', $tableHTML);
|
||||
$this->assertRegExp('/<tr[^>]*id="record-Tester-3"[^>]*>[^<]*<td[^>]*class="markingcheckbox F"[^>]*>/si', $tableHTML);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get that visiting the field's URL returns the content of the field.
|
||||
* This capability is used by ajax
|
||||
@ -191,6 +219,7 @@ class TableListFieldTest_Obj extends DataObject implements TestOnly {
|
||||
"A" => "Varchar",
|
||||
"B" => "Varchar",
|
||||
"C" => "Varchar",
|
||||
"F" => "Boolean",
|
||||
);
|
||||
static $default_sort = "A";
|
||||
|
||||
|
@ -3,22 +3,27 @@ TableListFieldTest_Obj:
|
||||
A: a1
|
||||
B: b1
|
||||
C: c1
|
||||
F: true
|
||||
two:
|
||||
A: a2
|
||||
B: b2
|
||||
C: c2
|
||||
F: false
|
||||
three:
|
||||
A: a3
|
||||
B: b3
|
||||
C: c3
|
||||
F: true
|
||||
four:
|
||||
A: a4
|
||||
B: b4
|
||||
C: c4
|
||||
D: false
|
||||
five:
|
||||
A: a5
|
||||
B: b5
|
||||
C: c5
|
||||
F: true
|
||||
|
||||
TableListFieldTest_CsvExport:
|
||||
exportone:
|
||||
|
Loading…
Reference in New Issue
Block a user