2007-07-19 12:40:28 +02:00
< ? php
/**
* Base class for all fields that contain other fields .
* Implements sequentialisation - so that when we ' re saving / loading data , we can populate
* a tabbed form properly . All of the children are stored in $this -> children
2008-01-09 05:18:36 +01:00
* @ package forms
* @ subpackage fields - structural
2007-07-19 12:40:28 +02:00
*/
class CompositeField extends FormField {
2008-10-03 17:56:14 +02:00
/**
* @ var FieldSet
*/
2007-07-19 12:40:28 +02:00
protected $children ;
2008-10-03 17:56:14 +02:00
2007-07-19 12:40:28 +02:00
/**
* Set to true when this field is a readonly field
*/
protected $readonly ;
/**
* @ var $columnCount int Toggle different css - rendering for multiple columns
* ( " onecolumn " , " twocolumns " , " threecolumns " ) . The content is determined
* by the $children - array , so wrap all items you want to have grouped in a
* column inside a CompositeField .
* Caution : Please make sure that this variable actually matches the
* count of your $children .
*/
protected $columnCount = null ;
public function __construct ( $children = null ) {
2008-10-08 04:00:12 +02:00
if ( $children instanceof FieldSet ) {
2007-07-19 12:40:28 +02:00
$this -> children = $children ;
} elseif ( is_array ( $children )) {
$this -> children = new FieldSet ( $children );
} else {
$children = is_array ( func_get_args ()) ? func_get_args () : array ();
$this -> children = new FieldSet ( $children );
}
2008-09-11 02:02:49 +02:00
$this -> children -> setContainerField ( $this );
2008-08-20 06:41:55 +02:00
2007-07-19 12:40:28 +02:00
Object :: __construct ();
}
/**
* Returns all the sub - fields , suitable for <% control FieldSet %>
*/
public function FieldSet () {
return $this -> children ;
}
public function setID ( $id ) {
$this -> id = $id ;
}
2007-10-18 03:05:52 +02:00
public function Field () {
return $this -> FieldHolder ();
}
2008-08-20 06:41:55 +02:00
/**
* Accessor method for $this -> children
* @ return FieldSet
*/
public function getChildren () {
return $this -> children ;
}
2008-12-04 23:38:32 +01:00
/**
* @ param FieldSet $children
*/
public function setChildren ( $children ) {
$this -> children = $children ;
}
2007-07-19 12:40:28 +02:00
/**
* Returns the fields nested inside another DIV
*/
function FieldHolder () {
$fs = $this -> FieldSet ();
$idAtt = isset ( $this -> id ) ? " id= \" { $this -> id } \" " : '' ;
$className = ( $this -> columnCount ) ? " field CompositeField { $this -> extraClass () } multicolumn " : " field CompositeField { $this -> extraClass () } " ;
2007-10-18 03:05:52 +02:00
$content = " <div class= \" $className\ " $idAtt > \n " ;
2007-07-19 12:40:28 +02:00
foreach ( $fs as $subfield ) {
if ( $this -> columnCount ) {
$className = " column { $this -> columnCount } " ;
if ( ! next ( $fs )) $className .= " lastcolumn " ;
2007-10-18 03:05:52 +02:00
$content .= " \n <div class= \" { $className } \" > \n " . $subfield -> FieldHolder () . " \n </div> \n " ;
2007-07-19 12:40:28 +02:00
} else if ( $subfield ){
2007-10-18 03:05:52 +02:00
$content .= " \n " . $subfield -> FieldHolder () . " \n " ;
2007-07-19 12:40:28 +02:00
}
}
2007-10-18 03:05:52 +02:00
$content .= " </div> \n " ;
2007-07-19 12:40:28 +02:00
return $content ;
2007-09-16 18:05:05 +02:00
}
/**
* Returns the fields in the restricted field holder inside a DIV .
*/
function SmallFieldHolder () { //return $this->FieldHolder();
$fs = $this -> FieldSet ();
$idAtt = isset ( $this -> id ) ? " id= \" { $this -> id } \" " : '' ;
$className = ( $this -> columnCount ) ? " field CompositeField { $this -> extraClass () } multicolumn " : " field CompositeField { $this -> extraClass () } " ;
$content = " <div class= \" $className\ " $idAtt > " ;
foreach ( $fs as $subfield ) { //echo ' subf'.$subfield->Name();
if ( $this -> columnCount ) {
$className = " column { $this -> columnCount } " ;
if ( ! next ( $fs )) $className .= " lastcolumn " ;
$content .= " <div class= \" { $className } \" > " . $subfield -> FieldHolder () . " </div> " ;
} else if ( $subfield ){
$content .= $subfield -> SmallFieldHolder () . " " ;
}
}
$content .= " </div> " ;
2007-07-19 12:40:28 +02:00
2007-09-16 18:05:05 +02:00
return $content ;
}
2007-07-19 12:40:28 +02:00
/**
* Add all of the non - composite fields contained within this field to the list .
* Sequentialisation is used when connecting the form to its data source
*/
2008-08-12 04:58:48 +02:00
public function collateDataFields ( & $list , $saveableOnly = false ) {
2007-07-19 12:40:28 +02:00
foreach ( $this -> children as $field ) {
if ( is_object ( $field )) {
2008-08-12 04:58:48 +02:00
if ( $field -> isComposite ()) $field -> collateDataFields ( $list , $saveableOnly );
if ( $saveableOnly ) {
$isIncluded = ( $field -> hasData () && ! $field -> isReadonly () && ! $field -> isDisabled ());
} else {
$isIncluded = ( $field -> hasData ());
}
if ( $isIncluded ) {
2007-07-19 12:40:28 +02:00
$name = $field -> Name ();
if ( $name ) {
$formName = ( isset ( $this -> form )) ? $this -> form -> FormName () : '(unknown form)' ;
if ( isset ( $list [ $name ])) user_error ( " collateDataFields() I noticed that a field called ' $name ' appears twice in your form: ' { $formName } '. One is a ' { $field -> class } ' and the other is a ' { $list [ $name ] -> class } ' " , E_USER_ERROR );
$list [ $name ] = $field ;
}
}
}
}
}
function setForm ( $form ) {
foreach ( $this -> children as $f ) if ( is_object ( $f )) $f -> setForm ( $form );
parent :: setForm ( $form );
}
function setColumnCount ( $columnCount ) {
$this -> columnCount = $columnCount ;
}
function isComposite () { return true ; }
function hasData () { return false ; }
2008-01-06 22:55:27 +01:00
public function fieldByName ( $name ) {
return $this -> children -> fieldByName ( $name );
}
2007-07-19 12:40:28 +02:00
/**
* Add a new child field to the end of the set .
*/
public function push ( FormField $field ) {
$this -> children -> push ( $field );
}
2008-11-11 03:35:54 +01:00
/**
* @ uses FieldSet -> insertBefore ()
*/
2007-07-19 12:40:28 +02:00
public function insertBefore ( $field , $insertBefore ) {
2008-11-11 03:35:54 +01:00
$ret = $this -> children -> insertBefore ( $field , $insertBefore );
$this -> sequentialSet = null ;
return $ret ;
2007-09-16 18:05:05 +02:00
}
2008-11-11 03:35:54 +01:00
public function insertAfter ( $field , $insertAfter ) {
$ret = $this -> children -> insertAfter ( $field , $insertAfter );
$this -> sequentialSet = null ;
return $ret ;
}
2008-09-12 06:42:24 +02:00
/**
* Remove a field from this CompositeField by Name .
* The field could also be inside a CompositeField .
*
* @ param string $fieldName The name of the field
* @ 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 ) {
$this -> children -> removeByName ( $fieldName , $dataFieldOnly );
2007-07-19 12:40:28 +02:00
}
public function replaceField ( $fieldName , $newField ) {
return $this -> children -> replaceField ( $fieldName , $newField );
}
2008-09-11 08:24:40 +02:00
function rootFieldSet () {
if ( is_object ( $this -> containerFieldSet )) return $this -> containerFieldSet -> rootFieldSet ();
else return $this -> children ;
}
2007-07-19 12:40:28 +02:00
/**
* Return a readonly version of this field . Keeps the composition but returns readonly
* versions of all the children
*/
public function performReadonlyTransformation () {
$newChildren = new FieldSet ();
2008-12-04 23:38:32 +01:00
$clone = clone $this ;
foreach ( $clone -> getChildren () as $idx => $child ) {
2007-07-19 12:40:28 +02:00
if ( is_object ( $child )) $child = $child -> transform ( new ReadonlyTransformation ());
$newChildren -> push ( $child , $idx );
}
2008-12-04 23:38:32 +01:00
$clone -> children = $newChildren ;
$clone -> readonly = true ;
return $clone ;
2007-07-19 12:40:28 +02:00
}
/**
* Return a readonly version of this field . Keeps the composition but returns readonly
* versions of all the children
*/
public function performDisabledTransformation ( $trans ) {
$newChildren = new FieldSet ();
2008-12-04 23:38:32 +01:00
$clone = clone $this ;
if ( $clone -> getChildren ()) foreach ( $clone -> getChildren () as $idx => $child ) {
2007-07-19 12:40:28 +02:00
if ( is_object ( $child )) {
$child = $child -> transform ( $trans );
}
$newChildren -> push ( $child , $idx );
}
2008-12-04 23:38:32 +01:00
$clone -> children = $newChildren ;
$clone -> readonly = true ;
2007-07-19 12:40:28 +02:00
2008-12-04 23:38:32 +01:00
return $clone ;
2007-07-19 12:40:28 +02:00
}
2007-11-23 02:10:19 +01:00
2007-07-19 12:40:28 +02:00
function IsReadonly () {
return $this -> readonly ;
}
2008-11-11 03:35:54 +01:00
/**
* 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_string ( $field )) $field = $this -> fieldByName ( $field );
if ( ! $field ) return false ;
$i = 0 ;
foreach ( $this -> children as $child ) {
if ( $child -> Name () == $field -> Name ()) return $i ;
$i ++ ;
}
return false ;
}
2008-12-04 23:38:32 +01:00
/**
* Transform the named field into a readonly feld .
*
* @ param string | FormField
*/
function makeFieldReadonly ( $field ) {
$fieldName = ( $field instanceof FormField ) ? $field -> Name () : $field ;
// Iterate on items, looking for the applicable field
foreach ( $this -> children as $i => $item ) {
if ( $item -> isComposite ()) {
$item -> makeFieldReadonly ( $fieldName );
} else {
// Once it's found, use FormField::transform to turn the field into a readonly version of itself.
if ( $item -> Name () == $fieldName ) {
$this -> children -> replaceField ( $fieldName , $item -> transform ( new ReadonlyTransformation ()));
// Clear an internal cache
$this -> sequentialSet = null ;
// A true results indicates that the field was foudn
return true ;
}
}
}
return false ;
}
2007-07-19 12:40:28 +02:00
function debug () {
2007-09-14 20:05:21 +02:00
$result = " $this->class ( $this->name ) <ul> " ;
2007-07-19 12:40:28 +02:00
foreach ( $this -> children as $child ) {
$result .= " <li> " . Debug :: text ( $child ) . " </li> " ;
}
$result .= " </ul> " ;
return $result ;
}
2007-08-23 07:47:54 +02:00
function validate ( $validator ){
$valid = true ;
foreach ( $this -> children as $idx => $child ){
2008-10-08 04:00:12 +02:00
$valid = ( $child && $child -> validate ( $validator ) && $valid );
2007-08-23 07:47:54 +02:00
}
return $valid ;
}
2007-07-19 12:40:28 +02:00
}
?>