From d75de1369f25fb8af019d8775b4484cf9827cf92 Mon Sep 17 00:00:00 2001 From: Cristian Torres Date: Sat, 2 Apr 2016 11:20:27 -0600 Subject: [PATCH 1/7] themedScript Added A function to call javascript files just by its name as it occurs with themedCSS --- view/Requirements.php | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/view/Requirements.php b/view/Requirements.php index 76cad5ffd..f6f81e9c4 100644 --- a/view/Requirements.php +++ b/view/Requirements.php @@ -174,6 +174,23 @@ class Requirements implements Flushable { public static function themedCSS($name, $module = null, $media = null) { return 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 themedScript($name, $module = null, $type = null) { + return self::backend()->themedScript($name, $module, $type); + } /** * Clear either a single or all requirements @@ -1331,6 +1348,38 @@ class Requirements_Backend { } } + /** + * 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 themedScript($name, $module = null, $type = null) { + $theme = SSViewer::get_theme_folder(); + $project = project(); + $absbase = BASE_PATH . DIRECTORY_SEPARATOR; + $abstheme = $absbase . $theme; + $absproject = $absbase . $project; + $js = "/javascript/$name.js"; + + if(file_exists($absproject . $js)) { + $this->javascript($project . $js); + } elseif($module && file_exists($abstheme . '_' . $module.$js)) { + $this->javascript($theme . '_' . $module . $js); + } elseif(file_exists($abstheme . $js)) { + $this->javascript($theme . $js); + } elseif($module) { + $this->javascript($module . $js); + } + } + /** * Output debugging information. */ From 2f84c3431bb6cd99596bc6d22decd9424eb2ad4c Mon Sep 17 00:00:00 2001 From: Cristian Torres Date: Wed, 18 May 2016 16:47:38 -0600 Subject: [PATCH 2/7] UPDATE: Throw exceptions on file missing The class will throw exceptions whenever the css or js file is not found in any context and the site is in development mode --- view/Requirements.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/view/Requirements.php b/view/Requirements.php index f6f81e9c4..0ee811ca4 100644 --- a/view/Requirements.php +++ b/view/Requirements.php @@ -188,8 +188,8 @@ class Requirements implements Flushable { * @param string $type Comma-separated list of types to use in the script tag * (e.g. 'text/javascript,text/ecmascript') */ - public static function themedScript($name, $module = null, $type = null) { - return self::backend()->themedScript($name, $module, $type); + public static function themedJavascript($name, $module = null, $type = null) { + return self::backend()->themedJavascript($name, $module, $type); } /** @@ -1345,6 +1345,8 @@ class Requirements_Backend { $this->css($theme . $css, $media); } elseif($module) { $this->css($module . $css, $media); + } elseif (Director::isDev()) { + throw Exception("The css file doesn't exists. Please check if the file $name.css exists in any context or search for themedCSS references calling this file in your templates."); } } @@ -1361,7 +1363,7 @@ class Requirements_Backend { * @param string $type Comma-separated list of types to use in the script tag * (e.g. 'text/javascript,text/ecmascript') */ - public function themedScript($name, $module = null, $type = null) { + public function themedJavascript($name, $module = null, $type = null) { $theme = SSViewer::get_theme_folder(); $project = project(); $absbase = BASE_PATH . DIRECTORY_SEPARATOR; @@ -1377,6 +1379,8 @@ class Requirements_Backend { $this->javascript($theme . $js); } elseif($module) { $this->javascript($module . $js); + } elseif (Director::isDev()) { + throw Exception("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."); } } From 2b1dd99be11420f78532b3016cfac7a7f394abf9 Mon Sep 17 00:00:00 2001 From: Loz Calver Date: Fri, 15 Jul 2016 16:47:27 +0100 Subject: [PATCH 3/7] Refactor duplicate code in Form --- forms/Form.php | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/forms/Form.php b/forms/Form.php index cea8d311d..b303d25fb 100644 --- a/forms/Form.php +++ b/forms/Form.php @@ -480,13 +480,9 @@ class Form extends RequestHandler { return true; } - // Always allow actions which map to buttons. See httpSubmission() for further access checks. - $fields = $this->fields->dataFields() ?: array(); - $actions = $this->actions->dataFields() ?: array(); - - $fieldsAndActions = array_merge($fields, $actions); - foreach ($fieldsAndActions as $fieldOrAction) { - if ($fieldOrAction instanceof FormAction && $fieldOrAction->actionName() === $action) { + $actions = $this->getAllActions(); + foreach ($actions as $formAction) { + if ($formAction->actionName() === $action) { return true; } } @@ -1648,23 +1644,33 @@ class Form extends RequestHandler { * @return FormAction */ public function buttonClicked() { - $fields = $this->fields->dataFields() ?: array(); - $actions = $this->actions->dataFields() ?: array(); - - if(!$actions && !$fields) { - return null; - } - - $fieldsAndActions = array_merge($fields, $actions); - foreach ($fieldsAndActions as $fieldOrAction) { - if ($fieldOrAction instanceof FormAction && $this->buttonClickedFunc === $fieldOrAction->actionName()) { - return $fieldOrAction; + $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(); + $actions = $this->actions->dataFields() ?: array(); + + $fieldsAndActions = array_merge($fields, $actions); + $actions = array_filter($fieldsAndActions, function($fieldOrAction) { + return $fieldOrAction instanceof FormAction; + }); + + return $actions; + } + /** * Return the default button that should be clicked when another one isn't * available. From 9282662293b28cce056abf88e94123c8cdd172f4 Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Fri, 15 Jul 2016 17:00:33 +0100 Subject: [PATCH 4/7] Fixing bad syntax from PR --- view/Requirements.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/view/Requirements.php b/view/Requirements.php index 1bac7c08f..08c5eef5d 100644 --- a/view/Requirements.php +++ b/view/Requirements.php @@ -1359,8 +1359,8 @@ class Requirements_Backend { $this->css($theme . $css, $media); } elseif($module) { $this->css($module . $css, $media); - } elseif (Director::isDev()) { - throw Exception("The css file doesn't exists. Please check if the file $name.css exists in any context or search for themedCSS references calling this file in your templates."); + } else { + throw new InvalidArgumentException("The css file doesn't exists. Please check if the file $name.css exists in any context or search for themedCSS references calling this file in your templates."); } } From 4c856c4377d8a55ae5a58ff537256be37bd8089a Mon Sep 17 00:00:00 2001 From: Anton Smith Date: Tue, 19 Jul 2016 08:26:07 +1200 Subject: [PATCH 5/7] Use columns from gridfield for export --- forms/gridfield/GridFieldExportButton.php | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/forms/gridfield/GridFieldExportButton.php b/forms/gridfield/GridFieldExportButton.php index 307399e20..d6eeb6610 100644 --- a/forms/gridfield/GridFieldExportButton.php +++ b/forms/gridfield/GridFieldExportButton.php @@ -91,6 +91,25 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP return SS_HTTPRequest::send_file($fileData, $fileName, 'text/csv'); } } + + /** + * 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. @@ -100,9 +119,7 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP */ public function generateExportFileData($gridField) { $separator = $this->csvSeparator; - $csvColumns = ($this->exportColumns) - ? $this->exportColumns - : singleton($gridField->getModelClass())->summaryFields(); + $csvColumns = $this->getExportColumnsForGridField($gridField); $fileData = ''; if($this->csvHasHeader) { From 43dcde5197e0995225235f69937ed57bec0cd7e2 Mon Sep 17 00:00:00 2001 From: madmatt Date: Wed, 20 Apr 2016 11:21:14 +1200 Subject: [PATCH 6/7] FIX: Hierarchy was incorrectly unexpanding nodes that had been previously expanded --- model/Hierarchy.php | 3 +- tests/model/HierarchyTest.php | 59 ++++++++++++++++++++++++++++++++--- tests/model/HierarchyTest.yml | 14 ++++++++- 3 files changed, 69 insertions(+), 7 deletions(-) diff --git a/model/Hierarchy.php b/model/Hierarchy.php index 6ccc6ebc5..3e8cf1c67 100644 --- a/model/Hierarchy.php +++ b/model/Hierarchy.php @@ -274,7 +274,8 @@ class Hierarchy extends DataExtension { foreach($children as $child) { $markingMatches = $this->markingFilterMatches($child); 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(); } else { $child->markExpanded(); diff --git a/tests/model/HierarchyTest.php b/tests/model/HierarchyTest.php index 735845529..5fee85703 100644 --- a/tests/model/HierarchyTest.php +++ b/tests/model/HierarchyTest.php @@ -59,12 +59,12 @@ class HierarchyTest extends SapphireTest { // Obj 3 has been deleted; let's bring it back from the grave $obj3 = Versioned::get_including_deleted("HierarchyTest_Object", "\"Title\" = 'Obj 3'")->First(); - // Check that both obj 3 children are returned - $this->assertEquals(array("Obj 3a", "Obj 3b", "Obj 3c"), + // Check that all obj 3 children are returned + $this->assertEquals(array("Obj 3a", "Obj 3b", "Obj 3c", "Obj 3d"), $obj3->AllHistoricalChildren()->column('Title')); // Check numHistoricalChildren - $this->assertEquals(3, $obj3->numHistoricalChildren()); + $this->assertEquals(4, $obj3->numHistoricalChildren()); } @@ -94,11 +94,11 @@ class HierarchyTest extends SapphireTest { public function testNumChildren() { $this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj1')->numChildren(), 0); $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', 'obj2b')->numChildren(), 0); $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'); $this->assertEquals($obj1->numChildren(), 0); @@ -180,6 +180,53 @@ class HierarchyTest extends SapphireTest { $this->assertEquals('Obj 2 » Obj 2a » 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 = << +
  • Obj 3a +
      +
    • Obj 3aa +
    • +
    • Obj 3ab +
    • +
    +
  • +
  • Obj 3b +
      +
    • Obj 3ba +
    • +
    • Obj 3bb +
    • +
    +
  • +
  • Obj 3c +
      +
    • Obj 3c +
    • +
    +
  • +
  • Obj 3d +
  • + + +EOT; + + $this->assertSame($expected, $obj3->getChildrenAsUL()); + } + public function testGetChildrenAsUL() { $obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1'); $obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2'); @@ -539,6 +586,8 @@ class HierarchyTest_Object extends DataObject implements TestOnly { "Versioned('Stage', 'Live')", ); + private static $default_sort = 'Title ASC'; + public function cmstreeclasses() { return $this->markingClasses(); } diff --git a/tests/model/HierarchyTest.yml b/tests/model/HierarchyTest.yml index b3cdd8231..cb5da66d1 100644 --- a/tests/model/HierarchyTest.yml +++ b/tests/model/HierarchyTest.yml @@ -20,6 +20,9 @@ HierarchyTest_Object: obj3c: Parent: =>HierarchyTest_Object.obj3 Title: Obj 3c + obj3d: + Parent: =>HierarchyTest_Object.obj3 + Title: Obj 3d obj2aa: Parent: =>HierarchyTest_Object.obj2a Title: Obj 2aa @@ -31,4 +34,13 @@ HierarchyTest_Object: Title: Obj 3aa obj3ab: Parent: =>HierarchyTest_Object.obj3a - Title: Obj 3ab \ No newline at end of file + 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 From 319d6d293e873d989c55473a0aa7ae1679010474 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Wed, 20 Jul 2016 21:12:42 +1200 Subject: [PATCH 7/7] BUG Fix doclink (#5827) Fixes #5721 --- docs/en/01_Tutorials/05_Dataobject_Relationship_Management.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/en/01_Tutorials/05_Dataobject_Relationship_Management.md b/docs/en/01_Tutorials/05_Dataobject_Relationship_Management.md index c52e6f576..918978f4f 100644 --- a/docs/en/01_Tutorials/05_Dataobject_Relationship_Management.md +++ b/docs/en/01_Tutorials/05_Dataobject_Relationship_Management.md @@ -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, but not the only one. If your data requires a dedicated interface 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. ![tutorial:tutorial5_project_creation.jpg](../_images/tutorial5_project_creation.jpg)