Merge branch '3'

This commit is contained in:
Daniel Hensby 2016-07-20 21:44:26 +01:00
commit f548ddf8a8
No known key found for this signature in database
GPG Key ID: 229831A941962E26
7 changed files with 217 additions and 75 deletions

View File

@ -329,7 +329,8 @@ class Hierarchy extends DataExtension {
foreach($children as $child) { foreach($children as $child) {
$markingMatches = $this->markingFilterMatches($child); $markingMatches = $this->markingFilterMatches($child);
if($markingMatches) { if($markingMatches) {
if($child->$numChildrenMethod()) { // Mark a child node as unexpanded if it has children and has not already been expanded
if($child->$numChildrenMethod() && !$child->isExpanded()) {
$child->markUnexpanded(); $child->markUnexpanded();
} else { } else {
$child->markExpanded(); $child->markExpanded();

View File

@ -172,7 +172,8 @@ We call `setDisplayFields()` directly on the component responsible for their ren
Adding a `GridField` to a page type is a popular way to manage data, Adding a `GridField` to a page type is a popular way to manage data,
but not the only one. If your data requires a dedicated interface but not the only one. If your data requires a dedicated interface
with more sophisticated search and management logic, consider with more sophisticated search and management logic, consider
using the `[ModelAdmin](reference/modeladmin)` interface instead. using the [ModelAdmin](/developer_guides/customising_the_admin_interface/modeladmin)
interface instead.
</div> </div>
![tutorial:tutorial5_project_creation.jpg](../_images/tutorial5_project_creation.jpg) ![tutorial:tutorial5_project_creation.jpg](../_images/tutorial5_project_creation.jpg)

View File

@ -494,13 +494,9 @@ class Form extends RequestHandler {
return true; return true;
} }
// Always allow actions which map to buttons. See httpSubmission() for further access checks. $actions = $this->getAllActions();
$fields = $this->fields->dataFields() ?: array(); foreach ($actions as $formAction) {
$actions = $this->actions->dataFields() ?: array(); if ($formAction->actionName() === $action) {
$fieldsAndActions = array_merge($fields, $actions);
foreach ($fieldsAndActions as $fieldOrAction) {
if ($fieldOrAction instanceof FormAction && $fieldOrAction->actionName() === $action) {
return true; return true;
} }
} }
@ -1734,21 +1730,31 @@ class Form extends RequestHandler {
* @return FormAction * @return FormAction
*/ */
public function buttonClicked() { public function buttonClicked() {
$actions = $this->getAllActions();
foreach ($actions as $action) {
if ($this->buttonClickedFunc === $action->actionName()) {
return $action;
}
}
return null;
}
/**
* Get a list of all actions, including those in the main "fields" FieldList
*
* @return array
*/
protected function getAllActions() {
$fields = $this->fields->dataFields() ?: array(); $fields = $this->fields->dataFields() ?: array();
$actions = $this->actions->dataFields() ?: array(); $actions = $this->actions->dataFields() ?: array();
if(!$actions && !$fields) {
return null;
}
$fieldsAndActions = array_merge($fields, $actions); $fieldsAndActions = array_merge($fields, $actions);
foreach ($fieldsAndActions as $fieldOrAction) { $actions = array_filter($fieldsAndActions, function($fieldOrAction) {
if ($fieldOrAction instanceof FormAction && $this->buttonClickedFunc === $fieldOrAction->actionName()) { return $fieldOrAction instanceof FormAction;
return $fieldOrAction; });
}
}
return null; return $actions;
} }
/** /**

View File

@ -97,6 +97,25 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP
} }
} }
/**
* Return the columns to export
*
* @param GridField $gridField
*
* @return array
*/
protected function getExportColumnsForGridField(GridField $gridField) {
if($this->exportColumns) {
$exportColumns = $this->exportColumns;
} else if($dataCols = $gridField->getConfig()->getComponentByType('GridFieldDataColumns')) {
$exportColumns = $dataCols->getDisplayFields($gridField);
} else {
$exportColumns = singleton($gridField->getModelClass())->summaryFields();
}
return $exportColumns;
}
/** /**
* Generate export fields for CSV. * Generate export fields for CSV.
* *
@ -104,9 +123,7 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP
* @return array * @return array
*/ */
public function generateExportFileData($gridField) { public function generateExportFileData($gridField) {
$csvColumns = ($this->exportColumns) $csvColumns = $this->getExportColumnsForGridField($gridField);
? $this->exportColumns
: singleton($gridField->getModelClass())->summaryFields();
$fileData = array(); $fileData = array();
if($this->csvHasHeader) { if($this->csvHasHeader) {

View File

@ -65,12 +65,12 @@ class HierarchyTest extends SapphireTest {
// Obj 3 has been deleted; let's bring it back from the grave // Obj 3 has been deleted; let's bring it back from the grave
$obj3 = Versioned::get_including_deleted("HierarchyTest_Object", "\"Title\" = 'Obj 3'")->First(); $obj3 = Versioned::get_including_deleted("HierarchyTest_Object", "\"Title\" = 'Obj 3'")->First();
// Check that both obj 3 children are returned // Check that all obj 3 children are returned
$this->assertEquals(array("Obj 3a", "Obj 3b", "Obj 3c"), $this->assertEquals(array("Obj 3a", "Obj 3b", "Obj 3c", "Obj 3d"),
$obj3->AllHistoricalChildren()->column('Title')); $obj3->AllHistoricalChildren()->column('Title'));
// Check numHistoricalChildren // Check numHistoricalChildren
$this->assertEquals(3, $obj3->numHistoricalChildren()); $this->assertEquals(4, $obj3->numHistoricalChildren());
} }
@ -100,11 +100,11 @@ class HierarchyTest extends SapphireTest {
public function testNumChildren() { public function testNumChildren() {
$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj1')->numChildren(), 0); $this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj1')->numChildren(), 0);
$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj2')->numChildren(), 2); $this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj2')->numChildren(), 2);
$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj3')->numChildren(), 3); $this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj3')->numChildren(), 4);
$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj2a')->numChildren(), 2); $this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj2a')->numChildren(), 2);
$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj2b')->numChildren(), 0); $this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj2b')->numChildren(), 0);
$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj3a')->numChildren(), 2); $this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj3a')->numChildren(), 2);
$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj3b')->numChildren(), 0); $this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj3d')->numChildren(), 0);
$obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1'); $obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1');
$this->assertEquals($obj1->numChildren(), 0); $this->assertEquals($obj1->numChildren(), 0);
@ -186,6 +186,53 @@ class HierarchyTest extends SapphireTest {
$this->assertEquals('Obj 2 &raquo; Obj 2a &raquo; Obj 2aa', $obj2aa->getBreadcrumbs()); $this->assertEquals('Obj 2 &raquo; Obj 2a &raquo; Obj 2aa', $obj2aa->getBreadcrumbs());
} }
/**
* @covers Hierarchy::markChildren()
*/
public function testMarkChildrenDoesntUnmarkPreviouslyMarked() {
$obj3 = $this->objFromFixture('HierarchyTest_Object', 'obj3');
$obj3aa = $this->objFromFixture('HierarchyTest_Object', 'obj3aa');
$obj3ba = $this->objFromFixture('HierarchyTest_Object', 'obj3ba');
$obj3ca = $this->objFromFixture('HierarchyTest_Object', 'obj3ca');
$obj3->markPartialTree();
$obj3->markToExpose($obj3aa);
$obj3->markToExpose($obj3ba);
$obj3->markToExpose($obj3ca);
$expected = <<<EOT
<ul>
<li>Obj 3a
<ul>
<li>Obj 3aa
</li>
<li>Obj 3ab
</li>
</ul>
</li>
<li>Obj 3b
<ul>
<li>Obj 3ba
</li>
<li>Obj 3bb
</li>
</ul>
</li>
<li>Obj 3c
<ul>
<li>Obj 3c
</li>
</ul>
</li>
<li>Obj 3d
</li>
</ul>
EOT;
$this->assertSame($expected, $obj3->getChildrenAsUL());
}
public function testGetChildrenAsUL() { public function testGetChildrenAsUL() {
$obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1'); $obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1');
$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2'); $obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
@ -574,6 +621,8 @@ class HierarchyTest_Object extends DataObject implements TestOnly {
'SilverStripe\\ORM\\Versioning\\Versioned', 'SilverStripe\\ORM\\Versioning\\Versioned',
); );
private static $default_sort = 'Title ASC';
public function cmstreeclasses() { public function cmstreeclasses() {
return $this->markingClasses(); return $this->markingClasses();
} }

View File

@ -20,6 +20,9 @@ HierarchyTest_Object:
obj3c: obj3c:
Parent: =>HierarchyTest_Object.obj3 Parent: =>HierarchyTest_Object.obj3
Title: Obj 3c Title: Obj 3c
obj3d:
Parent: =>HierarchyTest_Object.obj3
Title: Obj 3d
obj2aa: obj2aa:
Parent: =>HierarchyTest_Object.obj2a Parent: =>HierarchyTest_Object.obj2a
Title: Obj 2aa Title: Obj 2aa
@ -32,15 +35,21 @@ HierarchyTest_Object:
obj3ab: obj3ab:
Parent: =>HierarchyTest_Object.obj3a Parent: =>HierarchyTest_Object.obj3a
Title: Obj 3ab Title: Obj 3ab
obj3ba:
Parent: =>HierarchyTest_Object.obj3b
Title: Obj 3ba
obj3bb:
Parent: =>HierarchyTest_Object.obj3b
Title: Obj 3bb
obj3ca:
Parent: =>HierarchyTest_Object.obj3c
Title: Obj 3c
HierarchyHideTest_Object: HierarchyHideTest_Object:
obj4: obj4:
Title: Obj 4 Title: Obj 4
obj4a: obj4a:
Parent: =>HierarchyHideTest_Object.obj4 Parent: =>HierarchyHideTest_Object.obj4
Title: Obj 4a Title: Obj 4a
HierarchyHideTest_SubObject: HierarchyHideTest_SubObject:
obj4b: obj4b:
Parent: =>HierarchyHideTest_Object.obj4 Parent: =>HierarchyHideTest_Object.obj4
Title: Obj 4b

View File

@ -199,6 +199,23 @@ class Requirements implements Flushable {
self::backend()->themedCSS($name, $module, $media); self::backend()->themedCSS($name, $module, $media);
} }
/**
* Registers the given themeable javascript as required.
*
* A javascript file in the current theme path name 'themename/javascript/$name.js' is first searched for,
* and it that doesn't exist and the module parameter is set then a javascript file with that name in
* the module is used.
*
* @param string $name The name of the file - eg '/javascript/File.js' would have the name 'File'
* @param string $module The module to fall back to if the javascript file does not exist in the
* current theme.
* @param string $type Comma-separated list of types to use in the script tag
* (e.g. 'text/javascript,text/ecmascript')
*/
public static function themedJavascript($name, $module = null, $type = null) {
return self::backend()->themedJavascript($name, $module, $type);
}
/** /**
* Clear either a single or all requirements * Clear either a single or all requirements
* *
@ -1817,10 +1834,52 @@ class Requirements_Backend
return $this->css($path . $css, $media); return $this->css($path . $css, $media);
} }
} }
throw new \InvalidArgumentException(
if($module) { "The css file doesn't exists. Please check if the file $name.css exists in any context or search for "
return $this->css($module . $css, $media); . "themedCSS references calling this file in your templates."
);
} }
/**
* Registers the given themeable javascript as required.
*
* A javascript file in the current theme path name 'themename/javascript/$name.js' is first searched for,
* and it that doesn't exist and the module parameter is set then a javascript file with that name in
* the module is used.
*
* @param string $name The name of the file - eg '/js/File.js' would have the name 'File'
* @param string $module The module to fall back to if the javascript file does not exist in the
* current theme.
* @param string $type Comma-separated list of types to use in the script tag
* (e.g. 'text/javascript,text/ecmascript')
*/
public function themedJavascript($name, $module = null, $type = null) {
$js = "/javascript/$name.js";
$opts = array(
'type' => $type,
);
$project = project();
$absbase = BASE_PATH . DIRECTORY_SEPARATOR;
$absproject = $absbase . $project;
if(file_exists($absproject . $js)) {
return $this->javascript($project . $js, $opts);
}
foreach(SSViewer::get_themes() as $theme) {
$path = TemplateLoader::instance()->getPath($theme);
$abspath = BASE_PATH . '/' . $path;
if(file_exists($abspath . $js)) {
return $this->javascript($path . $js, $opts);
}
}
throw new \InvalidArgumentException(
"The javascript file doesn't exists. Please check if the file $name.js exists in any context or search for "
. "themedJavascript references calling this file in your templates."
);
} }
/** /**