From 8c9560d2880be25aceb06982b0f30d4101cf5b8a Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Wed, 9 May 2012 17:18:16 +0200 Subject: [PATCH] ENHANCEMENT FieldList->setTabPathRewrites() for better backwards compatibility (see #7261) --- forms/FieldList.php | 63 +++++++++++++++++++++++++++++++++++ tests/forms/FieldListTest.php | 61 +++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/forms/FieldList.php b/forms/FieldList.php index b08fe0186d5..7261585e45e 100644 --- a/forms/FieldList.php +++ b/forms/FieldList.php @@ -25,6 +25,12 @@ class FieldList extends ArrayList { * @todo Documentation */ protected $containerField; + + /** + * @var array Ordered list of regular expressions, + * see {@link setTabPathRewrites()}. + */ + protected $tabPathRewrites = array(); public function __construct($items = array()) { if (!is_array($items) || func_num_args() > 1) { @@ -255,6 +261,9 @@ public function hasTabSet() { * @return Tab The found or newly created Tab instance */ public function findOrMakeTab($tabName, $title = null) { + // Backwards compatibility measure: Allow rewriting of outdated tab paths + $tabName = $this->rewriteTabPath($tabName); + $parts = explode('.',$tabName); $last_idx = count($parts) - 1; // We could have made this recursive, but I've chosen to keep all the logic code within FieldList rather than add it to TabSet and Tab too. @@ -291,6 +300,7 @@ public function findOrMakeTab($tabName, $title = null) { * @todo Implement similiarly to dataFieldByName() to support nested sets - or merge with dataFields() */ public function fieldByName($name) { + $name = $this->rewriteTabPath($name); if(strpos($name,'.') !== false) list($name, $remainder) = explode('.',$name,2); else $remainder = null; @@ -555,6 +565,59 @@ public function fieldPosition($field) { return false; } + /** + * Ordered list of regular expressions + * matching a tab path, to their rewrite rule (through preg_replace()). + * Mainly used for backwards compatibility. + * Ensure that more specific rules are placed earlier in the list, + * and that tabs with children are accounted for in the rule sets. + * + * Example: + * $fields->setTabPathRewriting(array( + * // Rewrite specific innermost tab + * '/^Root\.Content\.Main$/' => 'Root.Content', + * // Rewrite all innermost tabs + * '/^Root\.Content\.([^.]+)$/' => 'Root.\\1', + * )); + * + * @param array $rewrites + */ + public function setTabPathRewrites($rewrites) { + $this->tabPathRewrites = $rewrites; + } + + /** + * @return array + */ + public function getTabPathRewrites() { + return $this->tabPathRewrites; + } + + /** + * Support function for backwards compatibility purposes. + * Caution: Volatile API, might be removed in 3.1 or later. + * + * @param String $tabname Path to a tab, e.g. "Root.Content.Main" + * @return String Rewritten path, based on {@link tabPathRewrites} + */ + protected function rewriteTabPath($name) { + $isRunningTest = (class_exists('SapphireTest', false) && SapphireTest::is_running_test()); + foreach($this->getTabPathRewrites() as $regex => $replace) { + if(preg_match($regex, $name)) { + $newName = preg_replace($regex, $replace, $name); + Deprecation::notice('3.0.0', sprintf( + 'Using outdated tab path "%s", please use the new location "%s" instead', + $name, + $newName + )); + return $newName; + } + } + + // No match found, return original path + return $name; + } + /** * Default template rendering of a FieldList will concatenate all FieldHolder values. */ diff --git a/tests/forms/FieldListTest.php b/tests/forms/FieldListTest.php index c236b7052df..26b5a62d5d3 100644 --- a/tests/forms/FieldListTest.php +++ b/tests/forms/FieldListTest.php @@ -789,5 +789,66 @@ function testVisibleAndHiddenFields() { $this->assertNotNull($visible->dataFieldByName('D2')); } + function testRewriteTabPath() { + $fields = new FieldList( + new Tabset("Root", + $tab1Level1 = new Tab("Tab1Level1", + $tab1Level2 = new Tab("Tab1Level2"), + $tab2Level2 = new Tab("Tab2Level2") + ), + $tab2Level1 = new Tab("Tab2Level1") + ) + ); + $fields->setTabPathRewrites(array( + '/Root\.Tab1Level1\.([^.]+)$/' => 'Root.Tab1Level1Renamed.\\1', + '/Root\.Tab1Level1$/' => 'Root.Tab1Level1Renamed', + )); + $method = new ReflectionMethod($fields, 'rewriteTabPath'); + $method->setAccessible(true); + $this->assertEquals( + 'Root.Tab1Level1Renamed', + $method->invoke($fields, 'Root.Tab1Level1Renamed'), + "Doesn't rewrite new name" + ); + $this->assertEquals( + 'Root.Tab1Level1Renamed', + $method->invoke($fields, 'Root.Tab1Level1'), + 'Direct aliasing on toplevel' + ); + $this->assertEquals( + 'Root.Tab1Level1Renamed.Tab1Level2', + $method->invoke($fields, 'Root.Tab1Level1.Tab1Level2'), + 'Indirect aliasing on toplevel' + ); + } + + function testRewriteTabPathFindOrMakeTab() { + $fields = new FieldList( + new Tabset("Root", + $tab1Level1 = new Tab("Tab1Level1Renamed", + $tab1Level2 = new Tab("Tab1Level2"), + $tab2Level2 = new Tab("Tab2Level2") + ), + $tab2Level1 = new Tab("Tab2Level1") + ) + ); + $fields->setTabPathRewrites(array( + '/Root\.Tab1Level1\.([^.]+)$/' => 'Root.Tab1Level1Renamed.\\1', + '/Root\.Tab1Level1$/' => 'Root.Tab1Level1Renamed', + )); + + $this->assertEquals($tab1Level1, $fields->findOrMakeTab('Root.Tab1Level1'), + 'findOrMakeTab() with toplevel tab under old name' + ); + $this->assertEquals($tab1Level1, $fields->findOrMakeTab('Root.Tab1Level1Renamed'), + 'findOrMakeTab() with toplevel tab under new name' + ); + $this->assertEquals($tab1Level2, $fields->findOrMakeTab('Root.Tab1Level1.Tab1Level2'), + 'findOrMakeTab() with nested tab under old parent tab name' + ); + $this->assertEquals($tab1Level2, $fields->findOrMakeTab('Root.Tab1Level1Renamed.Tab1Level2'), + 'findOrMakeTab() with nested tab under new parent tab name' + ); + } }