This page is still a beta!

1.6. Case: Templating API for use in plugins

Introduction

You can use TemplaVoilas API for templates in your own plugins if you like. As an example of this, lets look at how the “mininews” extension works:

The mininews extension has three displays of content:

  1. An archive listing of all news in the archive, including a search box and links for browsing to the next page if there are more than 20 news or so.

  2. A detail display which showns a single news item in full

  3. A frontpage teaser listing showing the three most recent news with “read more” links.

Each of these displays are by default rendered by hardcoded HTML in the plugin. The hardcoded HTML is designed to be sufficient in most cases since you can style it all by CSS styles. Thus you might not need to make an alternative template!

However if you would like to restructure the output more than you can do by the CSS styles on the default HTML you can create a TemplaVoila template. The mininews extension supports this.

Selecting the alternative Template Object

Plugin Options - a FlexForm

The way you select an alternative template is by selecting the template object in the plugin options which are rendered by the standard flexform element for plugins:

For those interested this form is generated by the “flexform_ds.xml” configuration in the “mininews” extension:

<T3DataStructure>

<meta>

<langDisable>1</langDisable>

</meta>

<ROOT>

<type>array</type>

<el>

<field_templateObject>

<TCEforms>

<label>LLL:EXT:mininews/locallang_db.php:tt_content.pi_flexform.select_template</label>

<config>

<type>select</type>

<items>

<n0>

<n0></n0>

<n1>0</n1>

</n0>

</items>

<foreign_table>tx_templavoila_tmplobj</foreign_table>

<foreign_table_where>

AND tx_templavoila_tmplobj.pid=###STORAGE_PID### 
AND tx_templavoila_tmplobj.datastructure="EXT:mininews/template_datastructure.xml" 
AND tx_templavoila_tmplobj.parent=0 
ORDER BY tx_templavoila_tmplobj.title

</foreign_table_where>

<size>1</size>
<minitems>0</minitems>
<maxitems>1</maxitems>

</config>

</TCEforms>

</field_templateObject>

</el>

</ROOT>

</T3DataStructure>

Further “mininews” enables the configuration by setting these lines in the “ext_tables.php”

$TCA['tt_content']['types']['list']['subtypes_addlist'][$_EXTKEY.'_pi1']='tx_mininews_frontpage_list;;;;1-1-1,pi_flexform';

t3lib_extMgm::addPiFlexFormValue($_EXTKEY.'_pi1', 'FILE:EXT:mininews/flexform_ds.xml');

This is in fact all you have to do to select an alternative template. Of course the question is - what is a Template Object and how to we create one? This is answered next.

Creating a Template Object

This is done in the “Storage folder” which should have been configured to the website. Here you create a new Template Object an select the Data Structure that the mininews extension provides:

After having done this you close the document here, click the Template Object icon again and select “TemplaVoila”:

Subsequently you can begin the mapping of the Data Structure to the template file (here the example file “mininews/template/mininews_template.html” is used) and after that process you will see something like this:

Notice in particular how each of the three templates are found in the same Data Structure as sheets where the highest root element named “ROOT of multitemplate” represents the three sheets inside:

After the mapping process is complete the alternative template is in place.

The big questions now are:

  1. How can I define a data structure for my plugin just like “mininews” has done?

  2. How can I use any alternative template represented by a Template Object inside my plugin?

These questions are answered next.

Setting up a Data Structure XML file for Template Objects mapping

In “mininews” the data structure that is used for the mapping of templates is found in the file “mininews/template_datastructure.xml”. The contents look like this:

<T3DataStructure>

<sheets>

<!-- The Archive configuration is so large that we have put it into it's own file,
and references it from here: -->
<sArchive>EXT:mininews/template_datastructure_arc.xml</sArchive>
<!-- Single display of mininews items: -->

<sSingle>

<ROOT>

<tx_templavoila>

<title>SINGLE DISPLAY</title>
<description>Select the HTML element which is the container of the
single display of a news article:</description>
                  <tags>div:inner</tags>

</tx_templavoila>

<type>array</type>

<el>

<field_date>

<tx_templavoila>

<title>Date</title>
<description>News date</description>
<tags>*:inner</tags>

<sample_data>

<n0>6th August 10:34</n0>
<n1>29/12 2003</n1>

</sample_data>

</tx_templavoila>                                                                                               

</field_date>

<field_header>

<tx_templavoila>

<title>Header</title>
<description>Header field.</description>
<tags>*:inner</tags>

<sample_data>

<n0>People on mars!</n0>
<n1>Snow in Sydney</n1>

</sample_data>

</tx_templavoila>                                                                                               

</field_header>

<field_teaser>

<tx_templavoila>

<title>Teaser</title>
<description>Teaser field.</description>
<tags>*:inner</tags>

<sample_data>

<n0>Capthurim Chanaan vero genuit Sidonem primogenitum et
Heth Iebuseum quoque </n0>

</sample_data>

</tx_templavoila>                                                                                               

</field_teaser>

<field_bodytext>

<tx_templavoila>

<title>Bodytext</title>
<description>Bodytext field</description>
<tags>*:inner</tags>

<sample_data>

<n0><![CDATA[
<p><strong>Filii Ham Chus et Mesraim Phut et Chanaan</strong> filii autem Chus Saba et Evila Sabatha et Rechma et Sabathaca porro filii Rechma Saba et Dadan Chus autem genuit Nemrod iste coepit esse potens in terra Mesraim vero genuit Ludim et Anamim et Laabim et Nepthuim Phethrosim quoque et Chasluim de quibus egressi sunt Philisthim et.</p>
<p>Capthurim Chanaan vero genuit Sidonem primogenitum et Heth Iebuseum quoque et Amorreum et Gergeseum Evheumque et Aruceum et Asineum Aradium quoque et Samareum et Ematheum filii Sem Aelam et Assur et Arfaxad et Lud et Aram et Us et Hul et Gothor et Mosoch Arfaxad autem genuit Sala qui et ipse genuit Heber porro Heber nati sunt duo filii nomen uni Phaleg quia in diebus eius divisa est terra et nomen fratris eius Iectan Iectan autem genuit Helmodad et Saleph et Asermoth et Iare Aduram quoque et Uzal et Decla Ebal etiam et Abimahel et Saba necnon et Ophir et Evila et Iobab omnes isti filii Iectan Sem Arfaxad Sale.</p>
]]></n0>

</sample_data>

</tx_templavoila>                                                                                               

</field_bodytext>

<field_url>

<type>attr</type>

<tx_templavoila>

<title>"Back" URL.</title>
<description>Map to a-tags href-attribute of the link back to
archive listing.</description>
<tags>a:attr:href</tags>

<sample_data>

<n0>javascript:alert('You click this link!');</n0>

</sample_data>

</tx_templavoila>                                                                       

</field_url>

</el>                                                                                   
</ROOT>                 

</sSingle>

                
<!-- Frontpage display of a few mininews teaser items: -->

<sFrontpage>

<ROOT>

<tx_templavoila>

<title>FRONTPAGE LISTING</title>
<description>Select the HTML element which is the container of the
frontpage listing display of a news articles:</description>
<tags>div:inner</tags>

</tx_templavoila>

<type>array</type>

<el>

<field_fpListing>

<type>array</type>
<section>1</section>

<tx_templavoila>

<title>Archive Listing container</title>

<description></description>

<tags>div,table:inner</tags>

</tx_templavoila>

<el>

<element_even>

<type>array</type>

<tx_templavoila>

<title>Element Container, Even</title>

<description></description>

<tags>*:outer</tags>

</tx_templavoila>

<el>

<field_date>

<tx_templavoila>

<title>Date</title>
<description>News date</description>
<tags>*:inner</tags>

<sample_data>

<n0>6th August 10:34</n0>
<n1>29/12 2003</n1>

</sample_data>

</tx_templavoila>                                                                                               

</field_date>

<field_header>

<tx_templavoila>

<title>Header</title>
<description>Header field.</description>
<tags>*:inner</tags>

<sample_data>

<n0>People on mars!</n0>
<n1>Snow in Sydney</n1>

</sample_data>

</tx_templavoila>                                                                                               

</field_header>

<field_teaser>

<tx_templavoila>

<title>Teaser</title>
<description>Teaser field.</description>
<tags>*:inner</tags>

<sample_data>

<n0>Capthurim Chanaan vero genuit Sidonem primogenitum et
Heth Iebuseum quoque </n0>

</sample_data>

</tx_templavoila>                                                                                               

</field_teaser>

<field_url>

<type>attr</type>

<tx_templavoila>

<title>"MORE" URL.</title>
<description>Map to a-tags href-attribute of the link pointing
to the archive!</description>
<tags>a:attr:href</tags>

<sample_data>

<n0>javascript:alert('You click this link!');</n0>

</sample_data>

</tx_templavoila>                                                                       

</field_url>

</el>                                                   

</element_even>

</el>

</field_fpListing>

</el>                           
</ROOT>                 
</sFrontpage>           

</sheets>

</T3DataStructure>

If you study this long codelisting you will find that it only configures the DS for the templates “SINGLE DISPLAY” and “FRONTPAGE LISTING” - the “ARCHIVE LISTING” is actually found in another file referred to by this line:

<sArchive>EXT:mininews/template_datastructure_arc.xml</sArchive>

You can study the contents of this file by yourself.

Anyways, the question remains: How does mininews configure that this XML file should be available for Template Objects to point to? This is found in ext_tables.php:

// Adding datastructure for Mininews:

$GLOBALS['TBE_MODULES_EXT']['xMOD_tx_templavoila_cm1']['staticDataStructures'][]=array(

    'title' => 'Mininews Template',

    'path' => 'EXT:'.$_EXTKEY.'/template_datastructure.xml',

    'icon' => '',

    'scope' => 0,

);

$_EXTKEY contains the value “mininews” as usual in a ext_tables.php file for an extension.

By this configuration the DS will appear in the Data Structure selector box:

At this point we have:

  1. FlexForm configuration needed to select template record in the “Insert Plugin” type Content Element

  2. A Data Structure (DS) in an XML file which can be used for mapping a template HTML-file to the DS.

  3. You should also have an example template-HTML file which can demonstrate the mapping of your DS.

All that is left is to actually use the template in the plugin.

Getting the value of the Plugin FlexForm field

In the mininews plugin class we first need to detect if a Template Object record is pointed at and if so make sure it is used.

Detecting the Template Object (TO) record

In the “mininews/pi1/class.tx_mininews_pi1.php” file the class contains these two variables:

        // TemplaVoila specific:

    var $TA='';                    // If TemplaVoila is used and a TO record is found, this array will be loaded with Template Array.

    var $TMPLobj='';            // Template Object

Later, in the listView function you find this initialization which detects the record. Comments below

   1: function listView($content,$conf)    {

   2:

   3:         // Init FlexForm configuration for plugin:

   4:     $this->pi_initPIflexForm();

   5:     

   6:         // Looking for TemplaVoila TO record and if found, initialize template object:

   7:     if (t3lib_extMgm::isLoaded('templavoila'))    {

   8:         $field_templateObject = $this->pi_getFFvalue($this->cObj->data['pi_flexform'],'field_templateObject');

   9:         if (intval($field_templateObject))    {

  10:             $this->TMPLobj = t3lib_div::makeInstance('tx_templavoila_htmlmarkup');

  11:             $this->TA = $this->TMPLobj->getTemplateArrayForTO(intval($field_templateObject));

  12:             if (is_array($this->TA))    {

  13:                 $this->TMPLobj->setHeaderBodyParts($this->TMPLobj->tDat['MappingInfo_head'],$this->TMPLobj->tDat['MappingData_head_cached']);                

  14:             }

  15:         }

  16:     }

  1. Line 4 initializes the “pi_flexform” field in $this->cObj->data. This will convert the field from being a string with the XML data to being an array with the same XML converted to PHP array by t3lib_div::xml2array()

  2. Line 7 checks if TemplaVoila is loaded -which it must be of course!

  3. Line 8 requests the value of “field_templateObject” in the FlexForm content of “pi_flexform”

  4. Line 9 sees in that value is an integer - which means it points to a Template Object record “uid”

  5. In line 10 we create an instance of the “tx_templavoila_htmlmarkup” class which will be our API for merging our data from mininews with the template from the Template Object record.

  6. Line 11 loads the Template Array from the TO pointed to by $field_templateObject.

  7. Line 12 checks if the Template Array was set - this is the case if there was mapping information found in the TO.

  8. Line 13 will set possible header sections if any should be defined in the TO.

Now, in the rest of the mininews class we can just check if $this->TA is an array and if so use templavoilas API for merging data and template. This is shown next.

Merging Data with Template markup

In order to not make this too lengthy I will just cut out some examples.

Repeated list rows

The first example is how to accumulate content for list rows. This is basically done by a loop, traversing over the elements and for each iteration calling an API function in TemplaVoila with two arguments, the appropriate part of the ->TA variable (Template Array = cached template markup) and an array with the mininews data.

   1:     // Create list of elements:

   2: $elements='';

   3: while($this->internal['currentRow'] = mysql_fetch_assoc($res))    {

   4:     $elements.=$this->TMPLobj->mergeDataArrayToTemplateArray(

   5:         $this->TA['sub']['sArchive']['sub']['field_archiveListing']['sub']['element_even'],

   6:         array(

   7:             'field_date' => $this->getFieldContent('datetime'),

   8:             'field_header' => $this->pi_list_linkSingle($this->getFieldContent('title'),$this->internal['currentRow']['uid'],1),

   9:             'field_teaser' => nl2br(trim(t3lib_div::fixed_lgd($this->getFieldContent('teaser_list'),$this->conf['frontPage.']['teaserLgd'])))

  10:         )

  11:     );

  12: }

In this listing you can see that the array with data from mininews has three keys, “field_date”, “field_header” and “field_teaser”. These corresponds with three elements found in the Data Structure XML for the “ARCHIVE LISTING” template:

  10:                   <field_archiveListing>

  11:                           <type>array</type>

  12:                           <section>1</section>

  13:                           <tx_templavoila>

  14:                                   <title>Archive Listing container</title>

  15:                                   <description></description>

  16:                                   <tags>div,table:inner</tags>

  17:                           </tx_templavoila>

  18:                           <el>

  19:                                   <element_even>

  20:                                           <type>array</type>

  21:                                           <tx_templavoila>

  22:                                                   <title>Element Container, Even</title>

  23:                                                   <description></description>

  24:                                                   <tags>*:outer</tags>

  25:                                           </tx_templavoila>

  26:                                           <el>

  27:                                                   <field_date>

  28:                                                           <tx_templavoila>

  29:                                                                   <title>Date</title>

  30:                                                                   <description>News date</description>

  31:                                                                   <tags>*:inner</tags>

  32:                                                                   <sample_data>

  33:                                                                           <n0>6th August 10:34</n0>

  34:                                                                           <n1>29/12 2003</n1>

  35:                                                                   </sample_data>

  36:                                                           </tx_templavoila>                                                                                              

  37:                                                   </field_date>

  38:                                                   <field_header>

  39:                                                           <tx_templavoila>

  40:                                                                   <title>Header</title>

  41:                                                                   <description>Header field.</description>

  42:                                                                   <tags>*:inner</tags>

  43:                                                                   <sample_data>

  44:                                                                           <n0>People on mars!</n0>

  45:                                                                           <n1>Snow in Sydney</n1>

  46:                                                                   </sample_data>

  47:                                                           </tx_templavoila>                                                                                              

  48:                                                   </field_header>

  49:                                                   <field_teaser>

  50:                                                           <tx_templavoila>

  51:                                                                   <title>Teaser</title>

  52:                                                                   <description>Teaser field.</description>

  53:                                                                   <tags>*:inner</tags>

  54:                                                                   <sample_data>

  55:                                                                           <n0>Capthurim Chanaan vero genuit Sidonem primogenitum et Heth Iebuseum quoque </n0>

  56:                                                                   </sample_data>

  57:                                                           </tx_templavoila>                                                                                              

  58:                                                   </field_teaser>

This is a part of the Data Structure which is nested inside of

<T3DataStructure><sheets><sArchive><ROOT><el>

I point this out because you can see the logic of the variable

$this->TA['sub']['sArchive']['sub']['field_archiveListing']['sub']['element_even']

in the code listing from this. Basically, if you substitute “sub” with “el” you can almost read that this variable will contain the markup for

<T3DataStructure><sheets><sArchive><ROOT><el><field_archiveListing><el><element_even>
$this->TA['sub']['sArchive']['sub']['field_archiveListing']['sub']['element_even']

Putting it all together

After having accumulated the list rows (and some other stuff) the values on the outer levels are also composed into a similar API call whose output is finally returned:

   1:     // Wrap the elements in their containers:            

   2: $out = $this->TMPLobj->mergeDataArrayToTemplateArray(

   3:         $this->TA['sub']['sArchive'],

   4:         array(

   5:             'field_archiveListing' => $elements,

   6:             'field_browseBox_cellsContainer' => $br_elements,

   7:             'field_searchBox_sword' => htmlspecialchars($this->piVars['sword']),

   8:             'field_searchBox_submitUrl' => htmlspecialchars(t3lib_div::getIndpEnv('REQUEST_URI')),

   9:             'field_browseBox_displayRange' => $rangeLabel,

  10:             'field_browseBox_displayCount' => $this->internal['res_count']

  11:         )

  12:     );    

  13:     

  14: return $out;

This time you will see that the accumulated content of the list rows ($elements) is added to the key “field_archiveListing”. For all the other fields you can look them up in the DS as well:

...
 105: 
 106:                   <!--
 107:                           Defining mappings for the search box:
 108:                   -->
 109:                   <field_searchBox_submitUrl>
 110:                           <type>attr</type>
 111:                           <tx_templavoila>
 112:                                   <title>Search form action</title>
 113:                                   <description>URL of the news-search; Map to the action-attribute of the search form.</description>
 114:                                   <tags>form:attr:action</tags>
 115:                                   <sample_data>
 116:                                           <n0>javascript:alert('Hello, you pressed the search button!');return false;</n0>
 117:                                   </sample_data>
 118:                           </tx_templavoila>
 119:                   </field_searchBox_submitUrl>
 120:                   <field_searchBox_sword>
 121:                           <type>attr</type>
 122:                           <tx_templavoila>
 123:                                   <title>Search word field</title>
 124:                                   <description>Search word; Map to the forms input-fields value-attribute.</description>
 125:                                   <tags>input:attr:value</tags>
 126:                                   <sample_data>
 127:                                           <n0>Strawberry Jam</n0>
 128:                                           <n1>Jack Daniels</n1>
 129:                                           <n2>Flowers</n2>
 130:                                   </sample_data>
 131:                           </tx_templavoila>
 132:                   </field_searchBox_sword>
 133: 
 134:                   <!--
 135:                           Defining mappings for the browse box, display note:
 136:                   -->
 137:                   <field_browseBox_displayRange>
 138:                           <tx_templavoila>
 139:                                   <title>Range</title>
 140:                                   <description>Map to position where "x-y" should be outputted (showing which records are displayed)</description>
 141:                                   <tags>*:inner</tags>
 142:                                   <sample_data>
 143:                                           <n0>1-10</n0>
 144:                                           <n1>20 to 30</n1>
 145:                                   </sample_data>
 146:                           </tx_templavoila>
 147:                   </field_browseBox_displayRange>
 148:                   <field_browseBox_displayCount>
 149:                           <tx_templavoila>
 150:                                   <title>Count</title>
 151:                                   <description>Map to position where the total number of found records should be outputted.</description>
 152:                                   <tags>*:inner</tags>
 153:                                   <sample_data>
 154:                                           <n0>123</n0>
 155:                                           <n1>3402</n1>
 156:                                   </sample_data>
 157:                           </tx_templavoila>
 158:                   </field_browseBox_displayCount>
...

Thats all!