.. You may want to use the usual include line. Uncomment and adjust the path. .. include:: ../Includes.txt ====================== EXT: Dynamic FlexForms ====================== :Author: Thomas Hempel :Created: 2005-07-14T21:31:03 :Changed: 2007-08-17T20:27:23 :Email: thomas@work.de :Info 2: :Info 3: :Info 4: .. _EXT-Dynamic-FlexForms: EXT: Dynamic FlexForms ====================== Extension Key: **dynaflex** Copyright 2005 - 2007, Thomas Hempel, This document is published under the Open Content License available from http://www.opencontent.org/opl.shtml The content of this document is related to TYPO3 \- a GNU/GPL CMS/Framework available from www.typo3.com .. _Table-of-contents: Table of contents ----------------- **EXT: Dynamic FlexForms 1** **Introduction 1** What does it do? 1 Basics 1 *DCA 2* *Paths 2* *Where to instantiate 2* *The recommend way 2* *The old fashioned way 3* *How configuration is processed 4* *Cleanup the data 4* *Using hooks to alter configurations 4* **Configuration 5** Where the DCA comes from 5 $DCA 5 Single Configuration Array (SCA) 5 Modification Array (MA) 5 *-> method 6* *-> type 7* *-> conditions 7* *-> if 7* *-> source 8* *-> source\_type 8* *-> source\_config 8* *-> field\_config 8* *-> sheet\_config 9* *-> getLabel 9* **HOW-TO 10** Basics (an introduction) 10 Adding a simple field 10 Adding multiple fields 11 Adding fields depending on sources 11 Adding some sheets from an input field 11 Moving fields to a sheet 12 Load DCA from Tsconfig 12 *Configure DynaFlex to load TS and no class 12* *Write TypoScript 12* **Known problems 13** **To-Do list 13** **Changelog 13** .. _Introduction: Introduction ------------ .. _What-does-it-do: What does it do? ^^^^^^^^^^^^^^^^ Dynaflex gives you the possibility to change an array defined by a configuration. That doesn't sounds very cool but it can handle XML data structures inside of such an array. That makes it possible to change the TCA on the fly. The result is, that you can change the TCA before it's displayed in the backend. This modifications are configurable and can depend on entries in database or formfields inside a FlexForm. For example, you can realize that a tab divider is only visible if at least one fe\_user exists. You can insert fields into a FlexForm data structure depending on the number of entries in a specific table. For example, create an input field for every entry in the table be\_users that are not hidden. Actually you can do anything you want, if a functionality is not implemented in the dynaflex extension, you can call a userfunction, that returns a TCA configuration for example. .. _Basics: Basics ^^^^^^ Here I will explain some basic things that are used in the context of this extension manual. You should read this carefully to understand what I'm talking about in the rest of this document. **The dynaflex extension is not for end users but for developers!** .. _DCA: DCA """ DCA stands for **D** ynaflex **C** onfiguration **A** rray. It's very similar to the TCA. If you understood the scheme of the TCA, understanding the DCA shouldn't be a problem. :-) .. _Paths: Paths """"" When I'm talking about paths, I always mean a path inside of an array. In most cases this will be inside the TCA, but it also can be inside a FlexForm data structure (which is also an array after all). If you specify a path, you do it the same way as you'll do it on \*NIX systems. Every key inside the array is like a folder in a file system. There is only one difference to a file system, the path doesn't start with a slash! For example, if you have the following array: .. ### BEGIN~OF~TABLE ### .. _TCA: TCA ~~~ .. container:: table-row a TCA b element1 c subelement1 d .. ### BEGIN~OF~TABLE ### .. container:: table-row a sub1sub1field1 b subsub1value1 .. container:: table-row a sub1sub1field2 b subsub1value2 .. ###### END~OF~TABLE ###### .. _subelement2: subelement2 ~~~~~~~~~~~ .. container:: table-row a subelement2 b subsub2field1 c subsub2value1 .. _element2: element2 ~~~~~~~~ .. container:: table-row a element2 b sub2field1 c sub2value1 d .. _sub2field2: sub2field2 ~~~~~~~~~~ .. container:: table-row a sub2field2 b sub2value2 .. _sub2field3: sub2field3 ~~~~~~~~~~ .. container:: table-row a sub2field3 b sub2value3 .. _sub2field4: sub2field4 ~~~~~~~~~~ .. container:: table-row a sub2field4 b sub2value4 .. ###### END~OF~TABLE ###### If you want to edit the field “subsub1value1” the path to this field will be: :: TCA/element1/subelement1/sub1sub1field1 .. _Where-to-instantiate: Where to instantiate """""""""""""""""""" Before the dynaflex extension will do anything, you have to configure it (section “Configuration”) and than you have to instantiate and call it at the right position of the TYPO3 TCA processing. **Since version 1.7.0 this has be improved dramatically!** Since that version, DynaFlex implements all needed hooks by itself. This has various advantages. On the one hand, you don't have to register the hooks by your own. And the second one is, that through the way of implementation, you don't have to think about how DynaFlex has to be called. And last but not least, with version 1.7.0 I introduced hooks for config loading. That means, that you can alter the DCA of another extension by using a hook that is called while the configuration is loaded. The old way of calling will work as usual of course, but **it is highly recommend to use the new way of calling DynaFlex!!!** .. _The-recommend-way: The recommend way ~~~~~~~~~~~~~~~~~ **Since version 1.7.2** You have to create a new class for your configuration. Just create file that can be called like class.tx\_dynaflextut\_dfconfig.php and place a class in it that fits the usual naming conventions. Inside of this class you just have to define to variables. - $rowChecks – Defines some rules when the DCA should be executed. (since version 1.9.0!!!) - $DCA – Contains the DCA for your table. - $cleanUpField – This defines the table for which the flexform is defined. This field is cleaned up at the end of the modifying process. - $hooks – May contain hooks for altering the DCA for a table Such a file can look like this: :: class tx_dynaflextut_dfconfig { var $rowChecks = array ( // add your checks here ); var $DCA = array ( 0 => array ( 'path' => 'tx_dynaflextut_test/columns/flexdata/config/ds/default', 'modifications' => array ( --- snip --- ) ) ); var $cleanUpField = 'flexdata'; var $hooks = array( 'EXT:dynaflex_tut/class.tx_dynaflextut_dcahooks.php:tx_dynaflextut_dcahooks' ); } You now may ask, where the table is defined that DynaFlex knows when it should call itself. This is defined in the registration. Open the ext\_localconf.php of your extension and register your config class like this: :: $GLOBALS['T3_VAR']['ext']['dynaflex']['tx_dynaflextut_test'][] = 'EXT:dynaflex_tut/class.tx_dynaflextut_dfconfig.php:tx_dynaflextut_dfconfig'; As you can see, in the registration path you have to define for which table you want to register the configuration. In this case we're registering it for the table “tx\_dynaflextut\_test”. If you don't submit a path to a class but the simple string “TS”, DynaFlex will load the DCA not from a class but from the Tsconfig of the page. See section configuration. .. _The-old-fashioned-way: The old fashioned way ~~~~~~~~~~~~~~~~~~~~~ I recommend to use DynaFlex with TYPO3 version 3.8.0 and higher. There is one hook available where the flexform processing should be started. This hook can be found in the befunc class and is called “getFlexFormDS\_postProcessDS”. This is processed whenever a flexform field in the backend is processed. It's highly recommend to use this hook because only with this hook it's possible to create file upload fields (type “group”). I will not try to explain why it is so but it takes me a few hours to find it out. ;-) Anyway, at first you have to register your hook method. This is normally done in the ext\_localconf.php of your extension. The code should look similar to this snippet: :: $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['getFlexFormDSClass'][] = 'EXT:dynaflex_tut/class.tx_dynaflextut_befunc_hooks.php:tx_dynaflextut_befunc_hooks'; This code comes from the dynaflex\_tut extension that is availble from the TER. The hook code looks like this: :: class tx_dynaflextut_befunc_hooks { function getFlexFormDS_postProcessDS(&$dataStructArray, $conf, $row, $table, $fieldName) { global $counter; $counter++; if ($table == 'tx_dynaflextut_test' && t3lib_extMgm::isLoaded('dynaflex') && $counter <= 1 ) { // Load the configuration from a file require_once(t3lib_extMgm::extPath('dynaflex_tut') .'df_config.php'); // And start the dynaflex processing require_once(t3lib_extMgm::extPath('dynaflex') .'class.dynaflex.php'); $dynaflex = t3lib_div::makeInstance('dynaflex'); // debug($GLOBALS['TCA']['tx_dynaflextut_test']); $dynaflex->init($GLOBALS['TCA'], $GLOBALS['T3_VAR']['dynaflex']['dca']); // process DCA and read dataStructArray from index 0 $GLOBALS['TCA'] = $dynaflex->getDynamicTCA(); $dataStructArray = $dynaflex->dataStructArray[0]; // at last cleanup the XML structure in the database $dynaflex->doCleanup('flexdata'); } } } **This code works only with version 1.5.0 and higher!!!** Most things should be clear in this code. Maybe you wonder about the $counter. This workaround is needed to avoid that dynaflex processes the config more than one time. This is needed, because the code is called every time a single flexform field is processed. So we count the number fields that where already processed and call dynaflex only if we are in the first cycle. That is enough to build up the dynamic flexform structure. But to be honest, there is one bid disadvantage of this call. If you use a fieldvalue a source for one or more modifications, the changes take effect after second save and not after the first click on “save”. To avoid this, you can use another hook in the tceforms class. This hook is called “getMainFields\_preProcess”. The registration and call of this hook is likely the same than the befunc hook. But keep in mind that if you use this tceforms hook that dynaflex is processing the modifications twice. I will work on a proper solution for this odd behavior. Register tceform hook: :: $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['getMainFieldsClass'][] = 'EXT:dynaflex_tut/class.tx_dynaflextut_tceforms_hooks.php:tx_dynaflextut_tceforms_hooks'; Tceforms hook: :: function getMainFields_preProcess($table, $row, $pObj) { if ($table == 'tx_dynaflextut_test' && t3lib_extMgm::isLoaded('dynaflex') ) { // Load the configuration from a file require_once(t3lib_extMgm::extPath('dynaflex_tut') .'df_config.php'); // And start the dynaflex processing require_once(t3lib_extMgm::extPath('dynaflex') .'class.dynaflex.php'); $dynaflex = t3lib_div::makeInstance('dynaflex'); $dynaflex->init($GLOBALS['TCA'], $GLOBALS['T3_VAR']['dynaflex']['dca']); $GLOBALS['TCA'] = $dynaflex->getDynamicTCA(); $dynaflex->doCleanup('flexdata'); } } .. _How-configuration-is-processed: How configuration is processed """""""""""""""""""""""""""""" The configuration of dynaflex is processed in the same order as it's keys! That means that you have to keep track of the keys you give your configurations. This is good to know, because you can reference to field you have dynamically created in a modfication step before. I know that sounds a little bit coated but believe me, it can be useful. :-) .. _Regulate-execution: Regulate execution """""""""""""""""" In some cases you might register DynaFlex for general table like tt\_content. This is the case if you want to modify the flexform of a frontend plugin. The problem is, that DynaFlex will execute the modification every time tt\_content is loaded. This means for **every** content element! Since version 1.9 you can define some restriction to make sure that DynaFlex will only start if the dataset we're working on accomplishes some rules. These rules or conditions can be defined in the configuration class in a variable called $rowChecks. $rowChecks is an array of key value pairs where the keys are the fieldnames in the table we are working on and the values are the values the tablefield should have. If any of the tablefields does not have the value that is set in the $rowChecks array, the complete DCA won't be executed! Here is an example. This checks if the CType of the tt\_content element is 'list' (Insert Plugin) and that the selected plugin is 'dynaflex\_tut\_pi1': :: var $rowChecks = array ( 'list_type' => 'dynaflex_tut_pi1', 'CType' => 'list' ); .. _Cleanup-the-data: Cleanup the data """""""""""""""" Since version 1.4.0 you can force a cleanup of your data in the database. The problem is, that the values of a flexform are stored in a XML structure which holds each field you have ever had in your flexform. That is not a problem if the flexforms are static. If you use DynaFlex, fields can disappear from a form and so the data in the database is holding data for a field that doesn't exist. Since version 4.0 of TYPO there is a class called “flexformtools” which provides a method that removes all flexform fields from the datastructure that are not present in the TCA. In DynaFlex this method is used to provide a method that cleans up the database. This method also works for pre 4.0 version of TYPO3. DynaFlex ships with a copy of this flexformtools class. All you have to do is, to call the method “doCleanup” after you have written back the TCA. The following code snippet shows this: :: 1: $dynaflex->init($GLOBALS['TCA'], $dca); 2: 3: // write back the modified TCA 4: $GLOBALS['TCA'] = $dynaflex->getDynamicTCA(); 5: 6: $dynaflex->doCleanup('flexdata'); The argument you pass to this method is the field of the content element that is used to store the data XML. In this case the field is called “flexdata”. This is also the field for which the flexform structure is defined in the TCA. **This is obsolete with version 1.7.0! The cleanup is done automatically by DynaFlex if you use the recommend way of calling it.** .. _Using-hooks-to-alter-configurations: Using hooks to alter configurations """"""""""""""""""""""""""""""""""" Since the new way of calling DynaFlex was introduced it's possible to register hooks that can alter the DCA of other extensions. The only condition is, that the author of the orginal extension has used the new way. If he has not, the hooks are normally not available. The way of registering a hook is the same as you might know it from TYPO3 itself. Only the location is a different one. If you take a look at the section before (Where to instanciate – the recommend way) you'll find an example config class for DynaFlex. There you can see that the hooks are registered inside that config classes in the variable $hooks. Just enter the path to your hook class in such a config class as new array element and register the class for the targeted table. Now everytime time, DynaFlex is processing this table, it will load the regsitered DCA and will look for all hooks that might be registered for it. **Keep in mind that it makes in which order the extensions are installed. The extension that provides the orginal DCA has to be installed before any extension that provides altering hooks!!!** Inside the hook class you have to define a methd called **alterDCA\_onLoad** . This method will receive two arguments. The first one is the DCA with all modifications at the time the hook is called. This variable is called by reference! So you your method don't have to return anything. Just change the DCA. The second parameter is the name of the table we are working on. This is not really needed because you should know for which table you have regsitered your hook but it makes it easier to change the tablename later on if you use it in the alter method. An example hook could look like this: :: function alterDCA_onLoad($currentDCA, $table) { // store the last modification (the move) $lastIndex = count($currentDCA[0]['modifications']) -1; $lastModification = $currentDCA[0]['modifications'][$lastIndex]; // write the new one $currentDCA[0]['modifications'][$lastIndex] = array ( 'method' => 'add', 'path' => 'ROOT/el', 'type' => 'field', 'field_config' => array ( 'name' => 'df_field_fromHook', 'label' => 'A field from a hook', 'config' => array ( 'type' => 'input' ) ) ); // and append the former last one at the end again $currentDCA[0]['modifications'][] = $lastModification; } .. _Configuration: Configuration ------------- As I mentioned above, the dynaflex extension is configurable through an array called DCA. These DCA can beconfigured on various places in your extension or the TYPO3 installation. .. _Where-the-DCA-comes-from: Where the DCA comes from ^^^^^^^^^^^^^^^^^^^^^^^^ The first possibility is to write the DCA directly into the tca.php of your extension. This is was is obsolete and strongly recommended to **not** do it this way! The second way to provide a DCA is from within a DCA class. This is described in section “Where to instantiate” earlier in this document. This is the most common way to configure DynaFlex. A third way is to provide the DCA via TypoScript. If you don't provide a class in the ext\_localconf.php file but a simple “TS”, DynaFlex will not try to load a class but fetches the Tsconfig from the current page. DynaFlex tries to load the path “dynaflex.[tablename]”. .. _DCA: $DCA ^^^^ The DCA itself consists of an unlimited number of subarrays. Every single one of this arrays stands for modifications on a single element inside of an array. A field inside of a backend form for example. We (I) call this a **single configuration array** (SCA). There is only one special Key in the DCA: .. ### BEGIN~OF~TABLE ### .. _workingTable: workingTable """""""""""" .. container:: table-row Key workingTable Datatype string Description With this option you explicit define on which table you want to work. This is useful if you have a special multi table view like columnsOnly. If you don't set this, the workingTable is detected automatically. .. ###### END~OF~TABLE ###### .. _Single-Configuration-Array-SCA: Single Configuration Array (SCA) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The SCA contains some meta information of what should be done and an array of modifications. This table is an overview of the keys in the SCA: .. ### BEGIN~OF~TABLE ### .. _path: path """" .. container:: table-row Key path Datatype path (string) Description The path inside the array that should be modified. See section “Basics > Paths” .. _parseXML: parseXML """""""" .. container:: table-row Key parseXML Datatype boolean Description If this is true, the data that is located inside the path will be passed through the xml2array method of TYPO3 **before** anything is done with the fielddata. If it is false, nothing is done with the data, and it's passed as is to through the rest of the processing. The default value is true. This is needed for some kinds of modification, for example if you want to add simple text in the “showitem” field of the TCA which is not XML at all. .. _modifications: modifications """"""""""""" .. container:: table-row Key modifications Datatype array of ->MA Description An array of modification arrays. See section “$DCA > Modification Array” .. _uid: uid """ .. container:: table-row Key uid Datatype integer Description **OBSOLETE!** This is not needed anymore! Dynaflex gets this UID from the runtime environment when it is called. .. ###### END~OF~TABLE ###### .. _Modification-Array-MA: Modification Array (MA) ^^^^^^^^^^^^^^^^^^^^^^^ The modification array (MA) is the array where all the configuration is stored how the array should be modified. This table is an overview of the keys in the MA: .. ### BEGIN~OF~TABLE ### .. _method: method """""" .. container:: table-row Key method Datatype -> method Description This string names the method that is used for modifying the current element. Valid for method .. _type: type """" .. container:: table-row Key type Datatype -> type Description A detailed description of the method. (see -> type) Valid for method add, move .. _condition: condition """"""""" .. container:: table-row Key condition Datatype -> conditions Description **DEPRECATED!** If you only have a single condition that should be checked, you can use this option. It's recommend to use the key “conditions” instead! Valid for method all .. _conditions: conditions """""""""" .. container:: table-row Key conditions Datatype -> conditions Description Defines in witch cases a modification is performed or not. For a detailed description of the possible keys for a condition (see -> conditions). Valid for method all .. _source: source """""" .. container:: table-row Key source Datatype -> source Description Defines where the base data, for creating the fields, is fetched from. (see -> source) Valid for method add .. _source-type: source\_type """""""""""" .. container:: table-row Key source\_type Datatype -> source\_type Description The type of the source. (see -> source\_type) Valid for method add .. _source-config: source\_config """""""""""""" .. container:: table-row Key source\_config Datatype -> source\_config Description The configuration how the source is fetched. (see -> source\_config) Valid for method add .. _field-config: field\_config """"""""""""" .. container:: table-row Key field\_config Datatype array of -> field\_config Description An array of field configurations. That means that you can configure as much fields as you want, and for every element of the source ALL of this configurations will be performed. For example you can add two fields for every entity inside of an arbitrary table. Valid for method add .. _sheet-config: sheet\_config """"""""""""" .. container:: table-row Key sheet\_config Datatype -> sheet\_config Description Configures how a sheet is inserted into the FlexForm Valid for method add .. _allUserFunc: allUserFunc """"""""""" .. container:: table-row Key allUserFunc Datatype string Description This user function is called when all data was fetched. The data that was fetched is passed to the user function. Dynaflex expects an array with configurations that fit the definition of “field\_config” as result from the userfunction. Valid for method add (types: fields) .. _path: path """" .. container:: table-row Key path Datatype path (string) Description The path inside the FlexForm XML structure where the fields should be added. Valid for method add .. _inside: inside """""" .. container:: table-row Key inside Datatype path (string) Description The path inside the FlexForm, where the routines for adding staticXML are working in. For a simple FlexForm this would be: :: ROOT/el If you want insert staticXML inside of an existing sheet (which can be created by modification before this one!) the path could be something like: :: sheets/mySheet/ROOT/el Valid for method add (types: staticXML) .. _beforeAll: beforeAll """"""""" .. container:: table-row Key beforeAll Datatype integer Description If this is set, the data is inserted before all other elements inside the path that was configured by “inside”. Valid for method add (types: staticXML) .. _after: after """"" .. container:: table-row Key after Datatype string Description The name of the element, after which the data should be inserted. The frame in which this is performed (FlexForm or single sheet) has to be configured by “inside”. Valid for method add (types: staticXML) .. _data: data """" .. container:: table-row Key data Datatype string Description The XML data that insert if the type is staticXML Valid for method add (types: staticXML) .. _label: label """"" .. container:: table-row Key label Datatype ->getLabel Description Defines the label for an inserted sheet. Valid for method add (types: sheet, sheets) .. _name: name """" .. container:: table-row Key name Datatype string Description Defines the name for an inserted sheet. Valid for method add (types: sheet) .. _element: element """"""" .. container:: table-row Key element Datatype string Description The name of the element (field) for which an action should be performed. Valid for method remove .. _elements: elements """""""" .. container:: table-row Key elements Datatype array Description If you want to perform more than one modification for a single condition you can use this key to collect all modifications here. This is just for performance issues because the condition is only checked one time. Valid for method all .. _config: config """""" .. container:: table-row Key config Datatype array Description Normal field configuration like it's used in TCA and -> field\_config -> config. If it used for type “append” this array looks like: :: $config = array ( text => 'my text to append' ); Valid for method add (types: field, append) .. _table: table """"" .. container:: table-row Key table Datatype string Description Identifies a section inside the TYPO3\_CONF\_VARS array. Valid for method move (types: extraFields) .. ###### END~OF~TABLE ###### .. _method: -> method """"""""" Type of method that is used for modification .. ### BEGIN~OF~TABLE ### .. _add: add ~~~ .. container:: table-row Type add Description Instructs the dynaflex extension to add something somewhere. .. _move: move ~~~~ .. container:: table-row Type move Description Moves an element from one position to another.Since version 0.2.0 it also can move fields inside of the showitem section of the TCA. .. _remove: remove ~~~~~~ .. container:: table-row Type remove Description Removes (deletes) an element from the array .. _function-call: function\_call ~~~~~~~~~~~~~~ .. container:: table-row Type function\_call Description Calls a user defined method. The dynaflex extension passes the uid of the dataset the class is processing. (If it is set in the SCA) and the complete data of the field the class is processing. (The field is defined in the SCA and is normally an array with a FlexForm data structure.) The format how the function has to be defined is the same than in the TCA (see user defined fields). It's something like: :: tx_myext_myclass->myUserFunction **It's up to you, that the class file is included as soon as the function is instantiated and the method is called.** .. ###### END~OF~TABLE ###### .. _type: -> type """"""" The type defines what method should be performed exactly. This table is an overview of the possible types for every method (Currently only types for method “add” are implemented!): .. ### BEGIN~OF~TABLE ### .. _staticXML: staticXML ~~~~~~~~~ .. container:: table-row Type staticXML Description Inserts a static XML at a specific position inside the source. The position can be configured by MA->beforeAll and MA->after. The data that inserted is fetched from MA->data. Method add .. _sheet: sheet ~~~~~ .. container:: table-row Type sheet Description Inserts a new sheet inside of an FlexForm. The label is fetched from MA->label and the name from MA->name. Method add .. _sheets: sheets ~~~~~~ .. container:: table-row Type sheets Description Inserts a number of sheets depending on source\_config. Method .. _field: field ~~~~~ .. container:: table-row Type field Description Adds a single field. It's really simple. All you have to configure is MA->config and MA->label. Method add .. _fields: fields ~~~~~~ .. container:: table-row Type fields Description Adds more fields. This can be used to create fields in dependency to database contents. See: - MA->source - MA->source\_type - MA->source\_config - MA->field\_config Method add .. _extraFields: extraFields ~~~~~~~~~~~ .. container:: table-row Type extraFields Description If the type “extraFields” is set with method “move”. Dynaflex will fetch a list of fieldnames from the TYPO3\_CONF\_VARS array a parses them to modify the showitems section in the TCA. Method move .. _append: append ~~~~~~ .. container:: table-row Type append Description Appends some text after the content that is defined in DCA->path. An exampe for this can be adding additional tab dividers to a FlexForm. Such a configuration could look like: :: $DCA = array ( array ( 'path' => 'tx_myext/types/0/showitem', 'parseXML' => false, 'modifications' => array ( array ( 'method' => 'add', 'type' => 'append', 'config' => array ( 'text' => ',--div--;divider 1,myField;;;;1-1-1', ), ), ), ), ); Method add .. ###### END~OF~TABLE ###### .. _conditions: -> conditions """"""""""""" The condition decides if a modification should be performed or not. .. ### BEGIN~OF~TABLE ### .. _if: if ~~ .. container:: table-row Key if Datatype ->if Description the kind of condition that is checked. .. _source: source ~~~~~~ .. container:: table-row Key source Datatype string Description This defines what should be used as base for the condition. Possible values are: - **language** (The language of the current content element e.g. DEF, DE, DK etc.) - **pid** (The uid of the page the content element is located on) - **db** (The default if nothing is set, some data from the database). This requires “table”, “select” and “where” **This only fetches the first row from the result set! Keep that in mind if you want to use the condition data as source for further operations.** - **cce** (Current Content Element, requires cefield. This method has the big advantage, that you can use values from the dataset right away after saving twice. Fixes an annoying issue from older versions. **Since version 1.10.0** ) .. _table: table ~~~~~ .. container:: table-row Key table Datatype string (SQL) Description The table where the compare value is selected from. .. _select: select ~~~~~~ .. container:: table-row Key select Datatype string (SQL) Description The fields that are selected for comparison. **For some kinds of “if”, this have to be a SINGLE field in the table.** .. _where: where ~~~~~ .. container:: table-row Key where Datatype string (SQL) Description The condition for selection. You can use the marker “###uid###” here wich is substituted with the UID of the current content element. .. _cefield: cefield ~~~~~~~ .. container:: table-row Key cefield Datatype string Description Defines the field from which data is fetched if source is “cce”. .. _isXML: isXML ~~~~~ .. container:: table-row Key isXML Datatype boolean Description If this is true, the result from the database is handled as XML. That means, that the result is converted into an array. Otherwise the data is handled as it is. .. _path: path ~~~~ .. container:: table-row Key path Datatype path (string) Description **If isXML is true** and the data value for the comparison is fetched from this path inside of the array that was converted from the XML. **Important!** This path is something different than the flexform structure. If you fetch data from a flexform data field you'll get a flexform data-structure which is pretty different. A path could look like this for example: :: data/sheet_0/lDEF/df_field_0/vDEF .. _compareTo: compareTo ~~~~~~~~~ .. container:: table-row Key compareTo Datatype mixed Description This is the value the value from the database is compared to or a regular expression. This key is used for the “if” types: - isLess - isEqual - isGreater - notEqual - regex .. ###### END~OF~TABLE ###### .. _if: -> if """"" You have to configure when a condition is true or false. Normally you fetch some data from a database and compare the result of that query with a fixed value. How this comparison is done, is decided by **if** . .. ### BEGIN~OF~TABLE ### .. _hasValues: hasValues ~~~~~~~~~ .. container:: table-row Type hasValues Description Returns true if the select query returns anything. .. _isLess: isLess ~~~~~~ .. container:: table-row Type isLess Description Returns true if the result from the database is less than the “compareTo” value. **This only works, if the result of the database is an integer!!!** .. _isEqual: isEqual ~~~~~~~ .. container:: table-row Type isEqual Description Returns true if the result from the database equals the “compareTo” value. This should work with strings and integers. .. _notEqual: notEqual ~~~~~~~~ .. container:: table-row Type notEqual Description Returns true if the result does NOT equal the “compareTo” value. .. _isGreater: isGreater ~~~~~~~~~ .. container:: table-row Type isGreater Description Returns true if the result from the database is bigger than the “compareTo” value. **This only works, if the result of the database is an integer!!!** .. _regex: regex ~~~~~ .. container:: table-row Type regex Description Returns true if the regular expression in “compareTo” matches. This can be useful if you want to check for a substring for example. .. ###### END~OF~TABLE ###### .. _source: -> source """"""""" If you're adding fields, you have various possibilities what is used as base for creating that fields. This table gives you an overview about the possible sources for creating the fields. .. ### BEGIN~OF~TABLE ### .. _db: db ~~ .. container:: table-row Type db Description The data is fetched from the database. The detailed configuration how the data database query is built configured by “source\_config”. (see -> source\_config) .. _conditions: conditions ~~~~~~~~~~ .. container:: table-row Type conditions Description The data is the same as that result that is fetched from the database that was needed to check the condition. **Make sure, that you have a proper configured condition!** (see -> condition) .. _field: field ~~~~~ .. container:: table-row Type field Description The data is fetched from any field inside the form. It doesn't matter if the field is defined in the TCA and has it's own field in the database or if the field is defined inside a flexform datastructure. (see -> source\_config) .. _: \* ~~ .. container:: table-row Type \* Description A free text. In most cases this should be a comma separated list. (see -> source\_type) .. ###### END~OF~TABLE ###### .. _source-type: -> source\_type """"""""""""""" With the source\_type you can configure how the basedata for creating the fields is pre-processed. This is needed to make sure that the routines for creating fields are getting the right data format. .. ### BEGIN~OF~TABLE ### .. _csl: csl ~~~ .. container:: table-row Type csl Description Comma Separated List. Mostly used if the value of -> source is a manual entered text. (see -> source) .. _int: int ~~~ .. container:: table-row Type int Description Treats the source data as integer and converts into a integer. .. _db-row: db\_row ~~~~~~~ .. container:: table-row Type db\_row Description This is needed if you fetch more than a single value as source and fetch the data from a condition. It takes the data and creates a one dimensional array from it. .. _entry-count: entry\_count ~~~~~~~~~~~~ .. container:: table-row Type entry\_count Description The data is used as they come in. This is the default, so you don't have to set “source\_type” if you want to use this type. .. ###### END~OF~TABLE ###### .. _source-config: -> source\_config """"""""""""""""" Configuration, how the data is fetched from a source. This depends on the type of source (Key “source” in MA). This table is an overview of the keys that can be used in this configuration: .. ### BEGIN~OF~TABLE ### .. _table: table ~~~~~ .. container:: table-row Key table Datatype string (SQL) Description The table where the data is fetched from. If this is not set, the data will be fetched directly from the current dataset. This is also the case if the configured table is the same as the table, DynaFlex is working on. usable for source db, field .. _select: select ~~~~~~ .. container:: table-row Key select Datatype string (SQL) Description The select statement. Which fields should be fetched from the table. usable for source db .. _where: where ~~~~~ .. container:: table-row Key where Datatype string (SQL) Description The condition for selecting the datasets. usable for source db .. _db-field: db\_field ~~~~~~~~~ .. container:: table-row Key db\_field Datatype string Description The field inside the db if you want to use a field outside of flexforms. usable for source field .. _path: path ~~~~ .. container:: table-row Key path Datatype path (string) Description The path to the field inside of the flexform structure. **Without fieldname!** usable for source field .. _xml-field: xml\_field ~~~~~~~~~~ .. container:: table-row Key xml\_field Datatype string Description The name of the field inside the flexform structure. **Only needed if path is set** usable for source field .. ###### END~OF~TABLE ###### .. _field-config: -> field\_config """""""""""""""" The field\_config describes what kind of field should be created. In the keys “name” and “label” some replacements will be performed .. ### BEGIN~OF~TABLE ### .. _path: path ~~~~ .. container:: table-row Key path Datatype path (string) Description The path inside the FlexForm where the new field should be placed in. If no sheets are defined, the normal path would be “ROOT/el”. .. _name: name ~~~~ .. container:: table-row Key name Datatype string Description The name of the field. This has to be unique on the FlexForm. If you have a source defined you can use some markers in the name string that will be replaced before the form is rendered. There two static markers ###FINDEX### which is replaced with the index of the fieldconfig starting with 0. If you have defined more than one field in the field\_config (add => fields) the ###FINDEX### is the same for each field. The other static marker is the ###DATA### marker. This thing is replaced if you have a field as source. E.g. If you create a number of fields depending on a value of a field, the ###DATA### marker is replaced with that value. If the source data is a dataset from the database. ###DATA### will also be replaced with the same value as ###FINDEX### **The two static markers are implemented since version 1.2.0!!!** Additionally to this this two static markers you can set each field of the source data as marker. For example if you have tt\_address datasets as source, you can use markers like ###uid###, ###name###, ###city### and so on. Please keep in mind, that you should use any other fields than the uid as identifier for your fields. .. _label: label ~~~~~ .. container:: table-row Key label Datatype ->getLabel Description The label of the field. This can be a simple string, or a reference to locallang element. It's the same as the field label in the TCA. For information which markers can be used here, see above in the description for “name”. .. _config: config ~~~~~~ .. container:: table-row Key config Datatype -> TCA[“config”] Description The configuration of the field. It's the same as in TCA and described in the Core APIs! .. _singleUserFunc: singleUserFunc ~~~~~~~~~~~~~~ .. container:: table-row Key singleUserFunc Datatype string Description Calls a user function for every dataset that is provided by the source. As arguments, dynaflex passes two arrays: - **$dataArray:** An array with two keys “row” and “dfConfig” - **row:** The data that was provided from the source - **dfConfig:** the MA - **$aConfig:** the field\_config (at this time it contains the path) **The user function has to return a field\_config with “path”, “name”, “label” and “config”!** .. ###### END~OF~TABLE ###### .. _sheet-config: -> sheet\_config """""""""""""""" .. ### BEGIN~OF~TABLE ### .. _name: name ~~~~ .. container:: table-row Key name Datatype string Description The name of the sheet that will be created. The index of the sheet (beginning with 0) is added to the name. Example: iIf the name is “sheet” the name of the sheet in the resulting flexform will be “sheet\_X” where X is the index of the sheet. .. _label: label ~~~~~ .. container:: table-row Key label Datatype string Description The label of the sheet. You can use the marker ###SINDEX### which is replaced with the index of the sheet starting with 0. The markers are replaced **before** the value is passed to TYPO3. So if you have a reference to a language file, look for correct indexes in that locallang file. .. _fields: fields ~~~~~~ .. container:: table-row Key fields Datatype Array of -> field\_config Description An array of fields that will be created on that sheet. See “field\_config” for the config of the fields. .. _modifications: modifications ~~~~~~~~~~~~~ .. container:: table-row Key modifications Datatype Array of MA Description For each sheet you can tell DynaFlex to process a subset of modifications. .. ###### END~OF~TABLE ###### .. _getLabel: -> getLabel """"""""""" If you create fields you have to set a label for them. In most cases it should be enough to reference a locallang value but in some cases you might need more. A label definition can be a simple string or an array. Before the label is inserted into the TCA it's allways parsed for some markers (see -> markers). For example to make labels independet from each other through appending the uid or whatever. There are two special markers that are always available! - ###ce\_uid### - Is replaced with the UID of the current content element - ###ce\_pid### - Is replaced with the UID of the page the content element is placed on The most simple type of label is a string. You can add static text or a reference to a locallang value as I said before. The second possibility is to fetch the label from the database. To do so, you have to define the label as an array. Inside of the array you have to define some things that dynaflex knows where to fetch the label from. .. ### BEGIN~OF~TABLE ### .. _table: table ~~~~~ .. container:: table-row Key table Datatype string Description The table where the label should be fetched from. .. _field: field ~~~~~ .. container:: table-row Key field Datatype string Description The field that contains the value for the label. .. _where: where ~~~~~ .. container:: table-row Key where Datatype string Description the where clause for fetching the data. Here you can add some markers that will replaced before executing the query. .. ###### END~OF~TABLE ###### It's importand to recognize, that the query will allways be limited to one! This is hardcoded in dynaflex because it makes no sense to get more than one row from the database. ;-) .. _HOW-TO: HOW-TO ------ In this section I will give you some small how-tos where you can see how different things can be realized with dynaflex. I don't want to and I can't explain every option with an example. But I think this gives you nice introduction and invites you to make your own experiments. :-) All the examples you see here a realized in an tutorial extension which can be downloaded on `http://www.typo3-unleashed.net `_ or from the TER (Extensionkey: dynaflex\_tut). .. _Basics-an-introduction: Basics (an introduction) ^^^^^^^^^^^^^^^^^^^^^^^^ The basic flexform. :: 1: $TCA['tx_dynaflextut_test'] = Array ( 2: 'ctrl' => $TCA['tx_dynaflextut_test']['ctrl'], 3: 'interface' => Array ( 4: 'showRecordFieldList' => 'number,flexdata' 5: ), 6: 'feInterface' => $TCA['tx_dynaflextut_test']['feInterface'], 7: 'columns' => Array ( 8: 'number' => Array ( 9: 'exclude' => 1, 10: 'label' => 'LLL:EXT:dynaflex_tut/locallang_db.xml:tx_dynaflextut_test.number', 11: 'config' => Array ( 12: 'type' => 'input', 13: 'size' => '30', 14: 'eval' => 'int', 15: ) 16: ), 17: 'flexdata' => Array ( 18: 'exclude' => 1, 19: 'label' => 'LLL:EXT:dynaflex_tut/locallang_db.xml:tx_dynaflextut_test.flexdata', 20: 'config' => Array ( 21: 'type' => 'flex', 22: 'ds' => array ( 23: 'default' => '' . 24: ' ' . 25: ' 1' . 26: ' ' . 27: ' ' . 28: ' array' . 29: ' ' . 30: ' ' . 31: ' ' . 32: ' ' . 33: '' 34: ), 35: ), 36: ), 37: ), 38: 'types' => Array ( 39: '0' => Array('showitem' => 'number;;;;1-1-1, flexdata') 40: ), 41: 'palettes' => Array ( 42: '1' => Array('showitem' => '') 43: ) 44: ); .. _Adding-a-simple-field: Adding a simple field ^^^^^^^^^^^^^^^^^^^^^ To insert a single field you should use the “method” “add”. As “type” you use “field”. To define some things for the new field you can use the property “field\_config”. Here you set the “name”, the “label” an the config which is described in the Core APIs. :: 1: array ( 2: 'method' => 'add', 3: 'path' => 'ROOT/el', 4: 'type' => 'field', 5: 'field_config' => array ( 6: 'name' => 'df_field_0', 7: 'label' => 'myfirstdynaflexfield', 8: 'config' => array ( 9: 'type' => 'input' 10: ) 11: ) 12: ) .. _Adding-multiple-fields: Adding multiple fields ^^^^^^^^^^^^^^^^^^^^^^ To add more than one field with a single modfication you set the “type” to “fields”. Now the property “field\_config” is interpreted as an array. Each element of this array is the same as the “field\_config” in the example above. :: 1: array ( 2: 'method' => 'add', 3: 'path' => 'ROOT/el', 4: 'type' => 'fields', 5: 'field_config' => array ( 6: 0 => array ( 7: 'name' => 'df_field_1_a', 8: 'label' => 'myseconddynaflexfield', 9: 'config' => array ( 10: 'type' => 'input' 11: ) 12: ), 13: 1 => array ( 14: 'name' => 'df_field_1_b', 15: 'label' => 'mythirddynaflexfield', 16: 'config' => array ( 17: 'type' => 'input' 18: ) 19: ) 20: ) 21: ), .. _Adding-fields-depending-on-sources: Adding fields depending on sources ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The two examples above where not very impressive. Now we want to add fields depending on a source. In this example we use the be\_users table as source. That means, that we create a field for every backend user that is in the be\_users table and which is not marked as deleted. To achieve this, you have to set some additional properties. First is the “source” which defines what is used as source. The second one is the “source\_type” what defines how the source is handled. The third property is the “source\_config” what configures how the sourcedata is fetched. Now we can use the “field\_config” property to create the fields. In the properties “name” and “label” the markers ###uid### and ###username### are substituted with the value from the source. In fact you can substitute every field that comes from the source. :: 1: array ( 2: 'method' => 'add', 3: 'path' => 'ROOT/el', 4: 'type' => 'fields', 5: 'source' => 'db', 6: 'source_type' => 'entry_count', 7: 'source_config' => array ( 8: 'table' => 'be_users', 9: 'select' => 'uid,username', 10: 'where' => 'deleted=0', 11: ), 12: // keep in mind, that you can create more than one field at once. See the step above 13: 'field_config' => array ( 14: 'name' => 'df_field_###uid###', 15: 'label' => 'DataforUser (###username###)', 16: 'config' => array ( 17: 'type' => 'input' 18: ) 19: ) 20: ), .. _Adding-some-sheets-from-an-input-field: Adding some sheets from an input field ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Now we do something slightly different. The first difference is, that don't add fields but create sheets. And we don't use a query as source but the value of a field that is inside of our TCAform. As “source” we select “field” and we configure the source to fetch the value of the field “db\_field” from the table “tx\_dynaflextut\_test”. The dataset is the same we currently working on. Because we want to add sheets we have the property “sheet\_config” which is handled like “field\_config”. We just have some special properties for the sheets of course. As you can see, the label for each sheet has a marker ###SINDEX###. This marker is substituted with the index of the sheet we create. The name don't have to have a marker because the index is appended automatically. :: 1: array ( 2: 'method' => 'add', 3: 'type' => 'sheets', 4: 'source' => 'field', 5: 'source_config' => array ( 6: 'table' => 'tx_dynaflextut_test', 7: 'db_field' => 'number', 8: ), 9: // configure the sheets 10: 'sheet_config' => array ( 11: 'label' => 'Sheet ###SINDEX###', 12: 'name' => 'sheet', 13: // place some fields on each sheet, replacing and tailing is like for the sheet itself 14: 'fields' => array ( 15: array ( 16: 'label' => 'field on sheet ###SINDEX###', 17: 'name' => 'field', 18: 'config' => array ( 19: 'type' => 'input', 20: 'size' => '30', 21: ) 22: ) 23: ) 24: ) 25: ), .. _Moving-fields-to-a-sheet: Moving fields to a sheet ^^^^^^^^^^^^^^^^^^^^^^^^ As last step we want to have all the fields we cerated before on the first sheet. But this only makes sense if at least one sheet was created (or exists). So we add a condition that checks if the values of the field we use for creating the sheets is greater than 0. As method we use “move”. The only thing we have to define now is the “source” path and the “dest” (destination) path. In this example we move everything from “ROOT/el” (what is the default if no sheets exists) to “sheets/sheet\_0/ROOT/el”. This is the first sheet which is called “sheet\_0”. :: 1: array ( 2: 'method' => 'move', 3: // define the condition when this modification should be executed 4: 'condition' => array ( 5: 'source' => 'db', 6: 'if' => 'isGreater', 7: 'compareTo' => 0, 8: 'table' => 'tx_dynaflextut_test', 9: 'select' => 'number', 10: 'where' => 'uid=###uid###' 11: ), 12: 'source' => 'ROOT/el', 13: 'dest' => 'sheets/sheet_0/ROOT/el' 14: ) .. _Load-DCA-from-Tsconfig: Load DCA from Tsconfig ^^^^^^^^^^^^^^^^^^^^^^ Since version 1.8.0 it is possible to load the DCA from page tsconfig. This makes it possible for admins to provide different forms on various pages. It's pretty easy to use, because the syntax is the same as writing the DCA in an array inside of a class. .. _Configure-DynaFlex-to-load-TS-and-no-class: Configure DynaFlex to load TS and no class """""""""""""""""""""""""""""""""""""""""" Add the following line to your ext\_localconf.php: :: $GLOBALS['T3_VAR']['ext']['dynaflex']['tx_dynaflextut_test'][] = 'TS'; That's all. That tells DynaFlex that for dataelements from table “tx\_dynaflextut\_test” will be modified by a DCA that comes from the tsconfig on the page the element is placed on. .. _Write-TypoScript: Write TypoScript """""""""""""""" Now we'll add the TypoScript that will modify the element by adding a simple input field. :: 0: dynaflex.tx_dynaflextut_test { 1: 0 { 2: path = tx_dynaflextut_test/columns/flexdata/config/ds/default 3: modifications { 4: 0 { 5: method = add 6: path = ROOT/el 7: type = field 8: field_config { 9: label = Field from TS 10: name = tsfield 11: config { 12: type = input 13: } 14: } 15: } 16: } 17: } 18: } As you can see, the code in TyposScript is almost the same as the PHP array definition. It's only a different notation. One thing is a bit different! Please note that you have to add some numbers as keys for the SCAs and MAs (line 1 and 4). This is necessary because TypoScript always awaits array-keys what is not needed by PHP. If you want, you can add a class with some hook definitions after the TS part of course! .. _Known-problems: Known problems -------------- Nothing yet .. _To-Do-list: To-Do list ---------- - Bugfixing! ;) - Adding more features... ? Tell me what you need ! .. _Changelog: Changelog --------- **1.11.0** – Added a new condition type called “regex” (Thanks to Christopher Tan who provided the patch) **1.10.0** – Added a new condition type “cce” which fetches the condition source directly from the current record. **1.9.0** – Added execution conditions **1.8.1** – Added a new source type called “db\_row”. **1.8.0** – Added possibility to load DCA from Tsconfig.Updated manual **1.7.0** – Dramatic improvements for calling DynaFlex.Introduced hooks **1.5.0** – Some bugfixes with function\_call; updated manual and described a better way for instantiating dynaflex **1.4.0** – Bugfixes and cleanup functions added **1.2.0** – Added support for static markers in fieldconfig (###FINDEX###, ###DATA###) **1.1.0** – Added support for file references in TCA **1.0.0** – A lot of new methods and bugfixes (first stable version) **0.2.0** – Added moving of extra fields inside showitem section of TCA **0.1.0** – First official release |img-1| EXT: Dynamic FlexForms - 14 .. ######CUTTER_MARK_IMAGES###### .. |img-1| image:: img-1.png .. :align: left .. :border: 0 .. :height: 34 .. :id: Grafik1 .. :name: Grafik1 .. :width: 123