diff --git a/forms/FieldList.php b/forms/FieldList.php new file mode 100755 index 000000000..4b6af6065 --- /dev/null +++ b/forms/FieldList.php @@ -0,0 +1,556 @@ + 1) { + $items = func_get_args(); + } + + parent::__construct($items); + + foreach ($items as $item) { + if ($item instanceof FormField) $item->setContainerFieldSet($this); + } + } + + /** + * Return a sequential set of all fields that have data. This excludes wrapper composite fields + * as well as heading / help text fields. + */ + public function dataFields() { + if(!$this->sequentialSet) $this->collateDataFields($this->sequentialSet); + return $this->sequentialSet; + } + + public function saveableFields() { + if(!$this->sequentialSaveableSet) $this->collateDataFields($this->sequentialSaveableSet, true); + return $this->sequentialSaveableSet; + } + + protected function flushFieldsCache() { + $this->sequentialSet = null; + $this->sequentialSaveableSet = null; + } + + protected function collateDataFields(&$list, $saveableOnly = false) { + foreach($this as $field) { + if($field->isComposite()) $field->collateDataFields($list, $saveableOnly); + + if($saveableOnly) { + $isIncluded = ($field->hasData() && !$field->isReadonly() && !$field->isDisabled()); + } else { + $isIncluded = ($field->hasData()); + } + if($isIncluded) { + $name = $field->Name(); + if(isset($list[$name])) { + $errSuffix = ""; + if($this->form) $errSuffix = " in your '{$this->form->class}' form called '" . $this->form->Name() . "'"; + else $errSuffix = ''; + user_error("collateDataFields() I noticed that a field called '$name' appears twice$errSuffix.", E_USER_ERROR); + } + $list[$name] = $field; + } + } + } + + /** + * Add an extra field to a tab within this fieldset. + * This is most commonly used when overloading getCMSFields() + * + * @param string $tabName The name of the tab or tabset. Subtabs can be referred to as TabSet.Tab or TabSet.Tab.Subtab. + * This function will create any missing tabs. + * @param FormField $field The {@link FormField} object to add to the end of that tab. + * @param string $insertBefore The name of the field to insert before. Optional. + */ + public function addFieldToTab($tabName, $field, $insertBefore = null) { + // This is a cache that must be flushed + $this->flushFieldsCache(); + + // Find the tab + $tab = $this->findOrMakeTab($tabName); + + // Add the field to the end of this set + if($insertBefore) $tab->insertBefore($field, $insertBefore); + else $tab->push($field); + } + + /** + * Add a number of extra fields to a tab within this fieldset. + * This is most commonly used when overloading getCMSFields() + * + * @param string $tabName The name of the tab or tabset. Subtabs can be referred to as TabSet.Tab or TabSet.Tab.Subtab. + * This function will create any missing tabs. + * @param array $fields An array of {@link FormField} objects. + */ + public function addFieldsToTab($tabName, $fields, $insertBefore = null) { + $this->flushFieldsCache(); + + // Find the tab + $tab = $this->findOrMakeTab($tabName); + + // Add the fields to the end of this set + foreach($fields as $field) { + // Check if a field by the same name exists in this tab + if($insertBefore) { + $tab->insertBefore($field, $insertBefore); + } elseif($tab->fieldByName($field->Name())) { + // It exists, so we need to replace the old one + $this->replaceField($field->Name(), $field); + } else { + $tab->push($field); + } + } + } + + /** + * Remove the given field from the given tab in the field. + * + * @param string $tabName The name of the tab + * @param string $fieldName The name of the field + */ + public function removeFieldFromTab($tabName, $fieldName) { + $this->flushFieldsCache(); + + // Find the tab + $tab = $this->findOrMakeTab($tabName); + $tab->removeByName($fieldName); + } + + /** + * Removes a number of fields from a Tab/TabSet within this FieldSet. + * + * @param string $tabName The name of the Tab or TabSet field + * @param array $fields A list of fields, e.g. array('Name', 'Email') + */ + public function removeFieldsFromTab($tabName, $fields) { + $this->flushFieldsCache(); + + // Find the tab + $tab = $this->findOrMakeTab($tabName); + + // Add the fields to the end of this set + foreach($fields as $field) $tab->removeByName($field); + } + + /** + * Remove a field from this FieldSet by Name. + * The field could also be inside a CompositeField. + * + * @param string $fieldName The name of the field or tab + * @param boolean $dataFieldOnly If this is true, then a field will only + * be removed if it's a data field. Dataless fields, such as tabs, will + * be left as-is. + */ + public function removeByName($fieldName, $dataFieldOnly = false) { + if(!$fieldName) { + user_error('FieldSet::removeByName() was called with a blank field name.', E_USER_WARNING); + } + $this->flushFieldsCache(); + + foreach($this->items as $i => $child) { + if(is_object($child)){ + $childName = $child->Name(); + if(!$childName) $childName = $child->Title(); + + if(($childName == $fieldName) && (!$dataFieldOnly || $child->hasData())) { + array_splice( $this->items, $i, 1 ); + break; + } else if($child->isComposite()) { + $child->removeByName($fieldName, $dataFieldOnly); + } + } + } + } + + /** + * Replace a single field with another. Ignores dataless fields such as Tabs and TabSets + * + * @param string $fieldName The name of the field to replace + * @param FormField $newField The field object to replace with + * @return boolean TRUE field was successfully replaced + * FALSE field wasn't found, nothing changed + */ + public function replaceField($fieldName, $newField) { + $this->flushFieldsCache(); + foreach($this->items as $i => $field) { + if(is_object($field)) { + if($field->Name() == $fieldName && $field->hasData()) { + $this->items[$i] = $newField; + return true; + + } else if($field->isComposite()) { + if($field->replaceField($fieldName, $newField)) return true; + } + } + } + return false; + } + + /** + * Rename the title of a particular field name in this set. + * + * @param string $fieldName Name of field to rename title of + * @param string $newFieldTitle New title of field + * @return boolean + */ + function renameField($fieldName, $newFieldTitle) { + $field = $this->dataFieldByName($fieldName); + if(!$field) return false; + + $field->setTitle($newFieldTitle); + + return $field->Title() == $newFieldTitle; + } + + /** + * @return boolean + */ + public function hasTabSet() { + foreach($this->items as $i => $field) { + if(is_object($field) && $field instanceof TabSet) { + return true; + } + } + + return false; + } + + /** + * Returns the specified tab object, creating it if necessary. + * + * @todo Support recursive creation of TabSets + * + * @param string $tabName The tab to return, in the form "Tab.Subtab.Subsubtab". + * Caution: Does not recursively create TabSet instances, you need to make sure everything + * up until the last tab in the chain exists. + * @param string $title Natural language title of the tab. If {@link $tabName} is passed in dot notation, + * the title parameter will only apply to the innermost referenced tab. + * The title is only changed if the tab doesn't exist already. + * @return Tab The found or newly created Tab instance + */ + public function findOrMakeTab($tabName, $title = null) { + $parts = explode('.',$tabName); + + // We could have made this recursive, but I've chosen to keep all the logic code within FieldSet rather than add it to TabSet and Tab too. + $currentPointer = $this; + foreach($parts as $k => $part) { + $parentPointer = $currentPointer; + $currentPointer = $currentPointer->fieldByName($part); + // Create any missing tabs + if(!$currentPointer) { + if(is_a($parentPointer, 'TabSet')) { + // use $title on the innermost tab only + if($title && $k == count($parts)-1) { + $currentPointer = new Tab($part, $title); + } else { + $currentPointer = new Tab($part); + } + $parentPointer->push($currentPointer); + } else { + $withName = ($parentPointer->hasMethod('Name')) ? " named '{$parentPointer->Name()}'" : null; + user_error("FieldSet::addFieldToTab() Tried to add a tab to object '{$parentPointer->class}'{$withName} - '$part' didn't exist.", E_USER_ERROR); + } + } + } + + return $currentPointer; + } + + /** + * Returns a named field. + * You can use dot syntax to get fields from child composite fields + * + * @todo Implement similiarly to dataFieldByName() to support nested sets - or merge with dataFields() + */ + public function fieldByName($name) { + if(strpos($name,'.') !== false) list($name, $remainder) = explode('.',$name,2); + else $remainder = null; + + foreach($this->items as $child) { + if(trim($name) == trim($child->Name()) || $name == $child->id) { + if($remainder) { + if($child->isComposite()) { + return $child->fieldByName($remainder); + } else { + user_error("Trying to get field '$remainder' from non-composite field $child->class.$name", E_USER_WARNING); + return null; + } + } else { + return $child; + } + } + } + } + + /** + * Returns a named field in a sequential set. + * Use this if you're using nested FormFields. + * + * @param string $name The name of the field to return + * @return FormField instance + */ + public function dataFieldByName($name) { + if($dataFields = $this->dataFields()) { + foreach($dataFields as $child) { + if(trim($name) == trim($child->Name()) || $name == $child->id) return $child; + } + } + } + + /** + * Inserts a field before a particular field in a FieldSet. + * + * @param FormField $item The form field to insert + * @param string $name Name of the field to insert before + */ + public function insertBefore($item, $name) { + $this->onBeforeInsert($item); + $item->setContainerFieldSet($this); + + $i = 0; + foreach($this->items as $child) { + if($name == $child->Name() || $name == $child->id) { + array_splice($this->items, $i, 0, array($item)); + return $item; + } elseif($child->isComposite()) { + $ret = $child->insertBefore($item, $name); + if($ret) return $ret; + } + $i++; + } + + return false; + } + + /** + * Inserts a field after a particular field in a FieldSet. + * + * @param FormField $item The form field to insert + * @param string $name Name of the field to insert after + */ + public function insertAfter($item, $name) { + $this->onBeforeInsert($item); + $item->setContainerFieldSet($this); + + $i = 0; + foreach($this->items as $child) { + if($name == $child->Name() || $name == $child->id) { + array_splice($this->items, $i+1, 0, array($item)); + return $item; + } elseif($child->isComposite()) { + $ret = $child->insertAfter($item, $name); + if($ret) return $ret; + } + $i++; + } + + return false; + } + + /** + * Push a single field into this FieldSet instance. + * + * @param FormField $item The FormField to add + * @param string $key An option array key (field name) + */ + public function push($item, $key = null) { + $this->onBeforeInsert($item); + $item->setContainerFieldSet($this); + return parent::push($item, $key = null); + } + + /** + * Handler method called before the FieldSet is going to be manipulated. + */ + protected function onBeforeInsert($item) { + $this->flushFieldsCache(); + if($item->Name()) $this->rootFieldSet()->removeByName($item->Name(), true); + } + + + /** + * Set the Form instance for this FieldSet. + * + * @param Form $form The form to set this FieldSet to + */ + public function setForm($form) { + foreach($this as $field) $field->setForm($form); + } + + /** + * Load the given data into this form. + * + * @param data An map of data to load into the FieldSet + */ + public function setValues($data) { + foreach($this->dataFields() as $field) { + $fieldName = $field->Name(); + if(isset($data[$fieldName])) $field->setValue($data[$fieldName]); + } + } + + /** + * Return all fields + * in a form - including fields nested in {@link CompositeFields}. + * Useful when doing custom field layouts. + * + * @return FieldSet + */ + function HiddenFields() { + $hiddenFields = new HiddenFieldSet(); + $dataFields = $this->dataFields(); + + if($dataFields) foreach($dataFields as $field) { + if($field instanceof HiddenField) $hiddenFields->push($field); + } + + return $hiddenFields; + } + + /** + * Transform this FieldSet with a given tranform method, + * e.g. $this->transform(new ReadonlyTransformation()) + * + * @return FieldSet + */ + function transform($trans) { + $this->flushFieldsCache(); + $newFields = new FieldSet(); + foreach($this as $field) { + $newFields->push($field->transform($trans)); + } + return $newFields; + } + + /** + * Returns the root field set that this belongs to + */ + function rootFieldSet() { + if($this->containerField) return $this->containerField->rootFieldSet(); + else return $this; + } + + function setContainerField($field) { + $this->containerField = $field; + } + + /** + * Transforms this FieldSet instance to readonly. + * + * @return FieldSet + */ + function makeReadonly() { + return $this->transform(new ReadonlyTransformation()); + } + + /** + * Transform the named field into a readonly feld. + * + * @param string|FormField + */ + function makeFieldReadonly($field) { + $fieldName = ($field instanceof FormField) ? $field->Name() : $field; + $srcField = $this->dataFieldByName($fieldName); + $this->replaceField($fieldName, $srcField->performReadonlyTransformation()); + } + + /** + * Change the order of fields in this FieldSet by specifying an ordered list of field names. + * This works well in conjunction with SilverStripe's scaffolding functions: take the scaffold, and + * shuffle the fields around to the order that you want. + * + * Please note that any tabs or other dataless fields will be clobbered by this operation. + * + * @param array $fieldNames Field names can be given as an array, or just as a list of arguments. + */ + function changeFieldOrder($fieldNames) { + // Field names can be given as an array, or just as a list of arguments. + if(!is_array($fieldNames)) $fieldNames = func_get_args(); + + // Build a map of fields indexed by their name. This will make the 2nd step much easier. + $fieldMap = array(); + foreach($this->dataFields() as $field) $fieldMap[$field->Name()] = $field; + + // Iterate through the ordered list of names, building a new array to be put into $this->items. + // While we're doing this, empty out $fieldMap so that we can keep track of leftovers. + // Unrecognised field names are okay; just ignore them + $fields = array(); + foreach($fieldNames as $fieldName) { + if(isset($fieldMap[$fieldName])) { + $fields[] = $fieldMap[$fieldName]; + unset($fieldMap[$fieldName]); + } + } + + // Add the leftover fields to the end of the list. + $fields = $fields + array_values($fieldMap); + + // Update our internal $this->items parameter. + $this->items = $fields; + + $this->flushFieldsCache(); + } + + /** + * Find the numerical position of a field within + * the children collection. Doesn't work recursively. + * + * @param string|FormField + * @return Position in children collection (first position starts with 0). Returns FALSE if the field can't be found. + */ + function fieldPosition($field) { + if(is_object($field)) $field = $field->Name(); + + $i = 0; + foreach($this->dataFields() as $child) { + if($child->Name() == $field) return $i; + $i++; + } + + return false; + } + +} + +/** + * A field list designed to store a list of hidden fields. When inserted into a template, only the + * input tags will be included + * + * @package forms + * @subpackage fields-structural + */ +class HiddenFieldList extends FieldList { + function forTemplate() { + $output = ""; + foreach($this as $field) { + $output .= $field->Field(); + } + return $output; + } +} \ No newline at end of file diff --git a/forms/FieldSet.php b/forms/FieldSet.php old mode 100755 new mode 100644 index 53d0049e8..e3ae4b8eb --- a/forms/FieldSet.php +++ b/forms/FieldSet.php @@ -1,558 +1,17 @@ 1) { - $items = func_get_args(); - } + user_error( + 'FieldSet is deprecated, please use FieldList instead.', E_USER_NOTICE + ); - parent::__construct($items); - - foreach ($items as $item) { - if ($item instanceof FormField) $item->setContainerFieldSet($this); - } - } - - /** - * Return a sequential set of all fields that have data. This excludes wrapper composite fields - * as well as heading / help text fields. - */ - public function dataFields() { - if(!$this->sequentialSet) $this->collateDataFields($this->sequentialSet); - return $this->sequentialSet; - } - - public function saveableFields() { - if(!$this->sequentialSaveableSet) $this->collateDataFields($this->sequentialSaveableSet, true); - return $this->sequentialSaveableSet; - } - - protected function flushFieldsCache() { - $this->sequentialSet = null; - $this->sequentialSaveableSet = null; - } - - protected function collateDataFields(&$list, $saveableOnly = false) { - foreach($this as $field) { - if($field->isComposite()) $field->collateDataFields($list, $saveableOnly); - - if($saveableOnly) { - $isIncluded = ($field->hasData() && !$field->isReadonly() && !$field->isDisabled()); - } else { - $isIncluded = ($field->hasData()); - } - if($isIncluded) { - $name = $field->Name(); - if(isset($list[$name])) { - $errSuffix = ""; - if($this->form) $errSuffix = " in your '{$this->form->class}' form called '" . $this->form->Name() . "'"; - else $errSuffix = ''; - user_error("collateDataFields() I noticed that a field called '$name' appears twice$errSuffix.", E_USER_ERROR); - } - $list[$name] = $field; - } - } - } - - /** - * Add an extra field to a tab within this fieldset. - * This is most commonly used when overloading getCMSFields() - * - * @param string $tabName The name of the tab or tabset. Subtabs can be referred to as TabSet.Tab or TabSet.Tab.Subtab. - * This function will create any missing tabs. - * @param FormField $field The {@link FormField} object to add to the end of that tab. - * @param string $insertBefore The name of the field to insert before. Optional. - */ - public function addFieldToTab($tabName, $field, $insertBefore = null) { - // This is a cache that must be flushed - $this->flushFieldsCache(); - - // Find the tab - $tab = $this->findOrMakeTab($tabName); - - // Add the field to the end of this set - if($insertBefore) $tab->insertBefore($field, $insertBefore); - else $tab->push($field); - } - - /** - * Add a number of extra fields to a tab within this fieldset. - * This is most commonly used when overloading getCMSFields() - * - * @param string $tabName The name of the tab or tabset. Subtabs can be referred to as TabSet.Tab or TabSet.Tab.Subtab. - * This function will create any missing tabs. - * @param array $fields An array of {@link FormField} objects. - */ - public function addFieldsToTab($tabName, $fields, $insertBefore = null) { - $this->flushFieldsCache(); - - // Find the tab - $tab = $this->findOrMakeTab($tabName); - - // Add the fields to the end of this set - foreach($fields as $field) { - // Check if a field by the same name exists in this tab - if($insertBefore) { - $tab->insertBefore($field, $insertBefore); - } elseif($tab->fieldByName($field->Name())) { - // It exists, so we need to replace the old one - $this->replaceField($field->Name(), $field); - } else { - $tab->push($field); - } - } + parent::__construct(!is_array($items) || func_num_args() > 1 ? func_get_args(): $items); } - /** - * Remove the given field from the given tab in the field. - * - * @param string $tabName The name of the tab - * @param string $fieldName The name of the field - */ - public function removeFieldFromTab($tabName, $fieldName) { - $this->flushFieldsCache(); - - // Find the tab - $tab = $this->findOrMakeTab($tabName); - $tab->removeByName($fieldName); - } - - /** - * Removes a number of fields from a Tab/TabSet within this FieldSet. - * - * @param string $tabName The name of the Tab or TabSet field - * @param array $fields A list of fields, e.g. array('Name', 'Email') - */ - public function removeFieldsFromTab($tabName, $fields) { - $this->flushFieldsCache(); - - // Find the tab - $tab = $this->findOrMakeTab($tabName); - - // Add the fields to the end of this set - foreach($fields as $field) $tab->removeByName($field); - } - - /** - * Remove a field from this FieldSet by Name. - * The field could also be inside a CompositeField. - * - * @param string $fieldName The name of the field or tab - * @param boolean $dataFieldOnly If this is true, then a field will only - * be removed if it's a data field. Dataless fields, such as tabs, will - * be left as-is. - */ - public function removeByName($fieldName, $dataFieldOnly = false) { - if(!$fieldName) { - user_error('FieldSet::removeByName() was called with a blank field name.', E_USER_WARNING); - } - $this->flushFieldsCache(); - - foreach($this->items as $i => $child) { - if(is_object($child)){ - $childName = $child->Name(); - if(!$childName) $childName = $child->Title(); - - if(($childName == $fieldName) && (!$dataFieldOnly || $child->hasData())) { - array_splice( $this->items, $i, 1 ); - break; - } else if($child->isComposite()) { - $child->removeByName($fieldName, $dataFieldOnly); - } - } - } - } - - /** - * Replace a single field with another. Ignores dataless fields such as Tabs and TabSets - * - * @param string $fieldName The name of the field to replace - * @param FormField $newField The field object to replace with - * @return boolean TRUE field was successfully replaced - * FALSE field wasn't found, nothing changed - */ - public function replaceField($fieldName, $newField) { - $this->flushFieldsCache(); - foreach($this->items as $i => $field) { - if(is_object($field)) { - if($field->Name() == $fieldName && $field->hasData()) { - $this->items[$i] = $newField; - return true; - - } else if($field->isComposite()) { - if($field->replaceField($fieldName, $newField)) return true; - } - } - } - return false; - } - - /** - * Rename the title of a particular field name in this set. - * - * @param string $fieldName Name of field to rename title of - * @param string $newFieldTitle New title of field - * @return boolean - */ - function renameField($fieldName, $newFieldTitle) { - $field = $this->dataFieldByName($fieldName); - if(!$field) return false; - - $field->setTitle($newFieldTitle); - - return $field->Title() == $newFieldTitle; - } - - /** - * @return boolean - */ - public function hasTabSet() { - foreach($this->items as $i => $field) { - if(is_object($field) && $field instanceof TabSet) { - return true; - } - } - - return false; - } - - /** - * Returns the specified tab object, creating it if necessary. - * - * @todo Support recursive creation of TabSets - * - * @param string $tabName The tab to return, in the form "Tab.Subtab.Subsubtab". - * Caution: Does not recursively create TabSet instances, you need to make sure everything - * up until the last tab in the chain exists. - * @param string $title Natural language title of the tab. If {@link $tabName} is passed in dot notation, - * the title parameter will only apply to the innermost referenced tab. - * The title is only changed if the tab doesn't exist already. - * @return Tab The found or newly created Tab instance - */ - public function findOrMakeTab($tabName, $title = null) { - $parts = explode('.',$tabName); - - // We could have made this recursive, but I've chosen to keep all the logic code within FieldSet rather than add it to TabSet and Tab too. - $currentPointer = $this; - foreach($parts as $k => $part) { - $parentPointer = $currentPointer; - $currentPointer = $currentPointer->fieldByName($part); - // Create any missing tabs - if(!$currentPointer) { - if(is_a($parentPointer, 'TabSet')) { - // use $title on the innermost tab only - if($title && $k == count($parts)-1) { - $currentPointer = new Tab($part, $title); - } else { - $currentPointer = new Tab($part); - } - $parentPointer->push($currentPointer); - } else { - $withName = ($parentPointer->hasMethod('Name')) ? " named '{$parentPointer->Name()}'" : null; - user_error("FieldSet::addFieldToTab() Tried to add a tab to object '{$parentPointer->class}'{$withName} - '$part' didn't exist.", E_USER_ERROR); - } - } - } - - return $currentPointer; - } - - /** - * Returns a named field. - * You can use dot syntax to get fields from child composite fields - * - * @todo Implement similiarly to dataFieldByName() to support nested sets - or merge with dataFields() - */ - public function fieldByName($name) { - if(strpos($name,'.') !== false) list($name, $remainder) = explode('.',$name,2); - else $remainder = null; - - foreach($this->items as $child) { - if(trim($name) == trim($child->Name()) || $name == $child->id) { - if($remainder) { - if($child->isComposite()) { - return $child->fieldByName($remainder); - } else { - user_error("Trying to get field '$remainder' from non-composite field $child->class.$name", E_USER_WARNING); - return null; - } - } else { - return $child; - } - } - } - } - - /** - * Returns a named field in a sequential set. - * Use this if you're using nested FormFields. - * - * @param string $name The name of the field to return - * @return FormField instance - */ - public function dataFieldByName($name) { - if($dataFields = $this->dataFields()) { - foreach($dataFields as $child) { - if(trim($name) == trim($child->Name()) || $name == $child->id) return $child; - } - } - } - - /** - * Inserts a field before a particular field in a FieldSet. - * - * @param FormField $item The form field to insert - * @param string $name Name of the field to insert before - */ - public function insertBefore($item, $name) { - $this->onBeforeInsert($item); - $item->setContainerFieldSet($this); - - $i = 0; - foreach($this->items as $child) { - if($name == $child->Name() || $name == $child->id) { - array_splice($this->items, $i, 0, array($item)); - return $item; - } elseif($child->isComposite()) { - $ret = $child->insertBefore($item, $name); - if($ret) return $ret; - } - $i++; - } - - return false; - } - - /** - * Inserts a field after a particular field in a FieldSet. - * - * @param FormField $item The form field to insert - * @param string $name Name of the field to insert after - */ - public function insertAfter($item, $name) { - $this->onBeforeInsert($item); - $item->setContainerFieldSet($this); - - $i = 0; - foreach($this->items as $child) { - if($name == $child->Name() || $name == $child->id) { - array_splice($this->items, $i+1, 0, array($item)); - return $item; - } elseif($child->isComposite()) { - $ret = $child->insertAfter($item, $name); - if($ret) return $ret; - } - $i++; - } - - return false; - } - - /** - * Push a single field into this FieldSet instance. - * - * @param FormField $item The FormField to add - * @param string $key An option array key (field name) - */ - public function push($item, $key = null) { - $this->onBeforeInsert($item); - $item->setContainerFieldSet($this); - return parent::push($item, $key = null); - } - - /** - * Handler method called before the FieldSet is going to be manipulated. - */ - protected function onBeforeInsert($item) { - $this->flushFieldsCache(); - if($item->Name()) $this->rootFieldSet()->removeByName($item->Name(), true); - } - - - /** - * Set the Form instance for this FieldSet. - * - * @param Form $form The form to set this FieldSet to - */ - public function setForm($form) { - foreach($this as $field) $field->setForm($form); - } - - /** - * Load the given data into this form. - * - * @param data An map of data to load into the FieldSet - */ - public function setValues($data) { - foreach($this->dataFields() as $field) { - $fieldName = $field->Name(); - if(isset($data[$fieldName])) $field->setValue($data[$fieldName]); - } - } - - /** - * Return all fields - * in a form - including fields nested in {@link CompositeFields}. - * Useful when doing custom field layouts. - * - * @return FieldSet - */ - function HiddenFields() { - $hiddenFields = new HiddenFieldSet(); - $dataFields = $this->dataFields(); - - if($dataFields) foreach($dataFields as $field) { - if($field instanceof HiddenField) $hiddenFields->push($field); - } - - return $hiddenFields; - } - - /** - * Transform this FieldSet with a given tranform method, - * e.g. $this->transform(new ReadonlyTransformation()) - * - * @return FieldSet - */ - function transform($trans) { - $this->flushFieldsCache(); - $newFields = new FieldSet(); - foreach($this as $field) { - $newFields->push($field->transform($trans)); - } - return $newFields; - } - - /** - * Returns the root field set that this belongs to - */ - function rootFieldSet() { - if($this->containerField) return $this->containerField->rootFieldSet(); - else return $this; - } - - function setContainerField($field) { - $this->containerField = $field; - } - - /** - * Transforms this FieldSet instance to readonly. - * - * @return FieldSet - */ - function makeReadonly() { - return $this->transform(new ReadonlyTransformation()); - } - - /** - * Transform the named field into a readonly feld. - * - * @param string|FormField - */ - function makeFieldReadonly($field) { - $fieldName = ($field instanceof FormField) ? $field->Name() : $field; - $srcField = $this->dataFieldByName($fieldName); - $this->replaceField($fieldName, $srcField->performReadonlyTransformation()); - } - - /** - * Change the order of fields in this FieldSet by specifying an ordered list of field names. - * This works well in conjunction with SilverStripe's scaffolding functions: take the scaffold, and - * shuffle the fields around to the order that you want. - * - * Please note that any tabs or other dataless fields will be clobbered by this operation. - * - * @param array $fieldNames Field names can be given as an array, or just as a list of arguments. - */ - function changeFieldOrder($fieldNames) { - // Field names can be given as an array, or just as a list of arguments. - if(!is_array($fieldNames)) $fieldNames = func_get_args(); - - // Build a map of fields indexed by their name. This will make the 2nd step much easier. - $fieldMap = array(); - foreach($this->dataFields() as $field) $fieldMap[$field->Name()] = $field; - - // Iterate through the ordered list of names, building a new array to be put into $this->items. - // While we're doing this, empty out $fieldMap so that we can keep track of leftovers. - // Unrecognised field names are okay; just ignore them - $fields = array(); - foreach($fieldNames as $fieldName) { - if(isset($fieldMap[$fieldName])) { - $fields[] = $fieldMap[$fieldName]; - unset($fieldMap[$fieldName]); - } - } - - // Add the leftover fields to the end of the list. - $fields = $fields + array_values($fieldMap); - - // Update our internal $this->items parameter. - $this->items = $fields; - - $this->flushFieldsCache(); - } - - /** - * Find the numerical position of a field within - * the children collection. Doesn't work recursively. - * - * @param string|FormField - * @return Position in children collection (first position starts with 0). Returns FALSE if the field can't be found. - */ - function fieldPosition($field) { - if(is_object($field)) $field = $field->Name(); - - $i = 0; - foreach($this->dataFields() as $child) { - if($child->Name() == $field) return $i; - $i++; - } - - return false; - } - -} - -/** - * A fieldset designed to store a list of hidden fields. When inserted into a template, only the - * input tags will be included - * - * @package forms - * @subpackage fields-structural - */ -class HiddenFieldSet extends FieldSet { - function forTemplate() { - $output = ""; - foreach($this as $field) { - $output .= $field->Field(); - } - return $output; - } -} - -?> +} \ No newline at end of file