From bcbe9c254dace82d0914e8c14349231867c50825 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Fri, 15 Oct 2010 02:27:59 +0000 Subject: [PATCH] ENHANCEMENT Allowing batch checkbox selection of TableListField rows with TableListField->Markable and TableListField->addSelectOptions() (from r105266) git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@112473 467b73ca-7a2a-4603-9d3b-597d59a354a9 --- css/TableListField.css | 12 +++ forms/TableListField.php | 79 +++++++++++++++++-- javascript/TableListField.js | 55 ++++++++++++- templates/ComplexTableField.ss | 3 + templates/Includes/TableListField_Item.ss | 2 +- .../Includes/TableListField_SelectOptions.ss | 8 ++ templates/RelationComplexTableField.ss | 3 + templates/TableListField.ss | 7 +- tests/forms/TableListFieldTest.php | 49 +++++++++--- tests/forms/TableListFieldTest.yml | 5 ++ 10 files changed, 203 insertions(+), 20 deletions(-) create mode 100644 templates/Includes/TableListField_SelectOptions.ss diff --git a/css/TableListField.css b/css/TableListField.css index 61e3968cb..7f02872d8 100644 --- a/css/TableListField.css +++ b/css/TableListField.css @@ -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; diff --git a/forms/TableListField.php b/forms/TableListField.php index cd66cb257..2370220a9 100755 --- a/forms/TableListField.php +++ b/forms/TableListField.php @@ -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 + * 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 "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) { diff --git a/javascript/TableListField.js b/javascript/TableListField.js index 0259673af..d2148e160 100755 --- a/javascript/TableListField.js +++ b/javascript/TableListField.js @@ -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,7 +127,51 @@ TableListField.prototype = { this._summarise(); }, - refresh: function(e, params, oncomplete) { + /** + * 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
+ <% if Markable %> + <% include TableListField_SelectOptions %> + <% end_if %> <% include TableListField_PageControls %> diff --git a/templates/Includes/TableListField_Item.ss b/templates/Includes/TableListField_Item.ss index e43b413f3..e8bf84af2 100755 --- a/templates/Includes/TableListField_Item.ss +++ b/templates/Includes/TableListField_Item.ss @@ -1,5 +1,5 @@ class="$HighlightClasses"<% end_if %>> - <% if Markable %><% end_if %> + <% if Markable %><% end_if %> <% control Fields %> <% end_control %> diff --git a/templates/Includes/TableListField_SelectOptions.ss b/templates/Includes/TableListField_SelectOptions.ss new file mode 100644 index 000000000..75bd563e8 --- /dev/null +++ b/templates/Includes/TableListField_SelectOptions.ss @@ -0,0 +1,8 @@ + <% if SelectOptions %> +
    +
  • <% _t('TableListField.SELECT', 'Select:') %>
  • + <% control SelectOptions %> +
  • $Value
  • + <% end_control %> +
+ <% end_if %> \ No newline at end of file diff --git a/templates/RelationComplexTableField.ss b/templates/RelationComplexTableField.ss index 11b819b76..a8b78fac4 100644 --- a/templates/RelationComplexTableField.ss +++ b/templates/RelationComplexTableField.ss @@ -1,4 +1,7 @@
+ <% if Markable %> + <% include TableListField_SelectOptions %> + <% end_if %> <% include TableListField_PageControls %>
$MarkingCheckbox$MarkingCheckbox$Value
diff --git a/templates/TableListField.ss b/templates/TableListField.ss index 71d706ca7..0d4e834ea 100755 --- a/templates/TableListField.ss +++ b/templates/TableListField.ss @@ -1,5 +1,10 @@
- <% if Print %><% else %><% include TableListField_PageControls %><% end_if %> + <% if Print %><% else %> + <% if Markable %> + <% include TableListField_SelectOptions %> + <% end_if %> + <% include TableListField_PageControls %> + <% end_if %>
diff --git a/tests/forms/TableListFieldTest.php b/tests/forms/TableListFieldTest.php index bb5403e25..f3a683ca6 100755 --- a/tests/forms/TableListFieldTest.php +++ b/tests/forms/TableListFieldTest.php @@ -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*assertRegExp('/>\s*a2\/b2\/c2\s*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('/]*id="record-Tester-1"[^>]*>[^<]*]*class="markingcheckbox F"[^>]*>/si', $tableHTML); + $this->assertRegExp('/]*id="record-Tester-2"[^>]*>[^<]*]*class="markingcheckbox"[^>]*>/si', $tableHTML); + $this->assertRegExp('/]*id="record-Tester-3"[^>]*>[^<]*]*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"; diff --git a/tests/forms/TableListFieldTest.yml b/tests/forms/TableListFieldTest.yml index 18def3435..25d72fa76 100644 --- a/tests/forms/TableListFieldTest.yml +++ b/tests/forms/TableListFieldTest.yml @@ -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: