Fed up with TemplaVoilà (which is end of life anyway)? Here's a squeezed guide to get you started with creating page templates and content elements using the FluidTYPO3 family of extensions.
You can code along this post or grab a copy of @cedricziel's ft3_empty to copy the basic structure and required files.
We need: flux
, fluidpages
, fluidcontent
and vhs
. Install them as usual through the extension manager,
set Flux' debug mode to 1
and check the rewriting of LLL files while you are at it.
This means a really minimalistic extension that will contain all your templates, typoscript and assets in one package.
To do so come up with a short name for your extension and create a folder in typo3conf/ext/
(for simplicity go for
all lowercase letters). We are really creative and name our example quickstart
. Inside that folder we create a
couple more and add some default files:
typo3conf/ext/quickstart
__ Configuration
____ TypoScript
______ setup.txt
__ Resources
____ Private
______ Language
________ locallang.xml
______ Layouts
______ Partials
______ Templates
________ Content
________ Page
____ Public
__ ext_emconf.php
__ ext_icon.gif
__ ext_tables.php
setup.txt
contains typoscript to configure the view by defining where our template files are located (as in any
other Extbase extension):
plugin.tx_quickstart.view {
templateRootPath = EXT:quickstart/Resources/Private/Templates/
partialRootPath = EXT:quickstart/Resources/Private/Partials/
layoutRootPath = EXT:quickstart/Resources/Private/Layouts/
}
Note the extension key which is a
tx_
prefixed, lowercase version of the extension name. Later you will define some more settings here which will be available in your templates then.
ext_tables.php
includes this very typoscript and configures Flux by registering our extension as a provider for page
and content templates:
t3lib_extMgm::addStaticFile($_EXTKEY, 'Configuration/TypoScript', 'Quickstart: a sample extension');
Tx_Flux_Core::registerProviderExtensionKey($_EXTKEY, 'Page');
Tx_Flux_Core::registerProviderExtensionKey($_EXTKEY, 'Content');
You can copy ext_emconf.php
from some other extension (like ft3_empty) and
change it accordingly or use the exension builder to create a fresh one. Here's an example how it should look like:
$EM_CONF[$_EXTKEY] = array(
'title' => 'Quickstart',
'description' => 'This extension provides templates and custom content elements',
'category' => 'misc',
'author' => 'John Doe',
'author_email' => 'mail@some.tld',
'author_company' => 'ACME',
'shy' => '',
'dependencies' => 'cms,extbase,fluidflux,fluidpages,fluidcontent,vhs',
'conflicts' => '',
'priority' => '',
'module' => '',
'state' => 'experimental',
'internal' => '',
'uploadfolder' => 1,
'createDirs' => '',
'modify_tables' => '',
'clearCacheOnLoad' => 1,
'lockType' => '',
'version' => '0.1',
'constraints' => array(
'depends' => array(
'typo3' => '4.5-6.1.99',
'cms' => '',
'extbase' => '',
'fluid' => '',
'flux' => '',
'fluidpages' => '',
'fluidcontent' => '',
'vhs' => '',
),
'conflicts' => array(
),
'suggests' => array(
),
),
'suggests' => array(
),
);
ext_icon.gif
is a 16x16 pixel icon which will show up in the extension manager and can be copied from some this or
some other extension as well.
locallang.xml
contains a skeleton XML structure for translation of labels and will be populated atomagically by Flux
later:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<T3locallang>
<meta type="array">
<type>module</type>
<description>Language labels for extension 'quickstart'</description>
</meta>
<data type="array">
<languageKey index="default" type="array">
</languageKey>
</data>
</T3locallang>
You should now find your provider extension in the extension manager: Go ahead, install it, include its static typoscript in your root template and remove any possible PAGE objects from your root template.
A page template consists of two parts: a layout and a template file. Layouts build the HTML 'skeleton' of your page (the
part between the body
tags) and define the dynamic parts with 'sections'. The representation for the backend of those
sections and any additional fields are defined in templates. To give you a better idea about that here's an example:
Layout file typo3conf/ext/quickstart/Resources/Private/Layouts/Foo.html
<f:layout name="Foo"/>
<div id="page" class="{settings.pageClass}">
<div id="sidebar">
<f:render section="Sidebar"/>
</div>
<div id="content">
<f:render section="Content"/>
</div>
</div>
Template file typo3conf/ext/quickstart/Resources/Private/Templates/Page/Foo.html
{namespace flux=Tx_Flux_ViewHelpers}
{namespace v=Tx_Vhs_ViewHelpers}
<div xmlns="http://www.w3.org/1999/xhtml" lang="en"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:flux="http://fedext.net/ns/flux/ViewHelpers"
xmlns:v="http://fedext.net/ns/vhs/ViewHelpers">
<f:layout name="Foo"/>
<f:section name="Configuration">
<flux:flexform id="foo">
<!-- Input field for Fluid variable 'pageClass' -->
<flux:flexform.field.input name="settings.pageClass" default="some-css-class"/>
<!-- Backend layout grid (TYPO3 6.x and greater only) -->
<flux:flexform.grid>
<flux:flexform.grid.row>
<flux:flexform.grid.column colPos="0" name="Sidebar" style="width: 25%"/>
<flux:flexform.grid.column colPos="1" name="Content" style="width: 75%"/>
</flux:flexform.grid.row>
</flux:flexform.grid>
</flux:flexform>
</f:section>
<f:section name="Content">
<!-- Render colPos=0 in this section -->
<v:content.render column="0"/>
</f:section>
<f:section name="Sidebar">
<!-- Render colPos=1 in this section -->
<v:content.render column="1"/>
</f:section>
</div>
We implement a page template named Foo. To 'connect' template and layout they are equally named Foo.html
and both
declare <f:layout name="Foo"/>
.
The div containers' only purpose is to enable code completion in your favorite IDE and will not be output.
The layout contains some simple HTML structure with two content areas and the outer div container's CSS class is controlled
by a Fluid variable {settings.pageClass}
. The variable is prefixed settings.
which is not required by configuration
but very useful. This will become clear at a later stage.
Per convention layouts have to define a
<f:section name="Main"/>
which is the section that will finally get rendered.
The template defines the backend representation of this layout by providing a flexform and a backend layout grid (only
available in TYPO3 6.x and greater). This flexform is defined with flux
viewhelpers which makes that part really
simple.
The bare minimum for a page layout file is to define a section named Configuration containing a flexform with at least an id to make it selectable in the backend.
In our example we add an input field for the CSS class which is then available in the layout as a Fluid variable of the same name:
<flux:flexform id="foo">
<!-- Input field for Fluid variable 'pageClass' -->
<flux:flexform.field.input name="settings.pageClass" default="some-css-class"/>
[...]
</flux:flexform>
and a grid that will be used as the backend layout:
<flux:flexform id="foo">
[...]
<!-- Backend layout grid (TYPO3 6.x and greater only) -->
<flux:flexform.grid>
<flux:flexform.grid.row>
<flux:flexform.grid.column colPos="0" name="Sidebar" style="width: 25%"/>
<flux:flexform.grid.column colPos="1" name="Content" style="width: 75%"/>
</flux:flexform.grid.row>
</flux:flexform.grid>
</flux:flexform>
The sections Content and Sidebar make use of a vhs
viewhelper to render the content of those columns.
All available viewhelpers and their arguments can be looked up in the reference on fedext.net
You should now be able to select the page layout in the backend by editing a page's properties after clearing all caches.
fluidpages
includes some fine inheritance feature that enables you to select the page template not only for the current
page but also for its children and the chain of inheritance can be interrupted at any level.
If you enabled the LLL rewrite feature in flux
it will create unique label identifiers in
typo3conf/ext/quickstart/Resources/Private/Language/locallang.xml
and use those as default translations at the same time.
Once you are finished developing your templates you can go ahead and exchange the default translations with the real ones.
No worries, existing translations won't be touched.
Inclusion of a page template's global CSS and JS is configured via typoscript in setup.txt
that has been added earlier.
As in any other extbase extension asset files are located in typo3conf/ext/quickstart/Resources/Public
and can of course
be organized in subfolders to your liking. Let's add style.css
and script.js
to our template by adding the following
lines:
[...]
plugin.tx_vhs.settings.asset {
styles {
name = styles
path = EXT:quickstart/Resources/Public/style.css
}
script {
name = script
path = EXT:quickstart/Resources/Public/script.js
}
}
As you can see plugin.tx_vhs.settings.asset
is an array of asset files to be included into your page. It provides a
lot more configuration options we don't need here yet but will be discussed at a later stage. For now we'll stick to the
most important being name
and path
which should be self-explanatory. The array index doesn't follow any special
convention but it's common practice to use the name. Now, make sure those files exist, clear your caches and have a look
at the HTML source of your page to find the assets being included (JS at the bottom of the page).
Content elements are defined similar to page templates and consist of a layout and a template file. Typically the layout simply renders the main section of the template and is shared with several content elements:
<f:layout name="Content"/>
<f:render section="Main"/>
All magic happens in the content element's template file which in its basic structure looks like this:
{namespace flux=Tx_Flux_ViewHelpers}
{namespace v=Tx_Vhs_ViewHelpers}
<div xmlns="http://www.w3.org/1999/xhtml" lang="en"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:flux="http://fedext.net/ns/flux/ViewHelpers"
xmlns:v="http://fedext.net/ns/vhs/ViewHelpers">
<f:layout name="Content"/>
<f:section name="Configuration"/>
<f:section name="Preview"/>
<f:section name="Main"/>
</div>
As you can see we connect the template to the above layout via <f:layout name="Content"/>
, add a section named
Configuration
that will contain our flexform, a Section named Preview
that will get rendered in the backend and
the Main
section as found in the layout that will contain the rendered frontend content.
Now, let's create a content element for a typical use case: a teaserbox consisting of an image, a headline, some teaser
text and an optional link in typo3conf/ext/quickstart/Resources/Private/Templates/Content/Teaser.html
:
{namespace flux=Tx_Flux_ViewHelpers}
{namespace v=Tx_Vhs_ViewHelpers}
<div xmlns="http://www.w3.org/1999/xhtml" lang="en"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:flux="http://fedext.net/ns/flux/ViewHelpers"
xmlns:v="http://fedext.net/ns/vhs/ViewHelpers">
<f:layout name="Content"/>
<f:section name="Configuration">
<flux:flexform id="teaser">
<flux:flexform.field.file name="image" allowed="jpg" uploadFolder="uploads/tx_quickstart" minItems="1" maxItems="1"
size="1"/>
<flux:flexform.field.input name="headline"/>
<flux:flexform.field.text name="teasertext" rows="5" cols="30" required="TRUE"/>
<flux:flexform.field.input name="link">
<flux:flexform.field.wizard.link activeTab="page"/>
</flux:flexform.field.input>
</flux:flexform>
</f:section>
<f:section name="Preview">
<table width="100%">
<tr>
<td width="50%"><v:media.image src="uploads/tx_quickstart/{image}" alt="{headline}" width="100"/></td>
<td width="50%">
<f:format.crop maxCharacters="50">{teasertext}</f:format.crop>
<f:if condition="{link}">
<strong>Link:</strong> {link}
</f:if>
</td>
</tr>
</table>
</f:section>
<f:section name="Main">
<div class="quickstart-teaser">
<article>
<h1>{headline}</h1>
<div class="image-wrapper">
<v:media.image src="uploads/tx_quickstart/{image}" alt="{headline}" width="200"/>
</div>
<div class="text-wrapper">
<f:format.nl2br>{teasertext}</f:format.nl2br>
<f:if condition="{link}">
<f:link.page pageUid="{link}" title="{headline}" class="readmore">read more</f:link.page>
</f:if>
</div>
</article>
</div>
</f:section>
</div>
Configuration
contains the flexform that defines the content element's fields which are wrapped in an outer
<flux:flexform/>
viewhelper. id
is a required argument for this viewhelper and used to generate the translation key
for the element's label (flux.teaser in this case) in TYPO3's New Content Element Wizard. By default a new tab labelled
FCE is created which can be overridden with the viewhelper argument wizardTab
.
There's nothing special about the flexform's fields in this example so to avoid duplicate content please take a look at the reference guide on fedext.net where you can find all required information about their functionality and arguments. One thing to take a closer look at though is the link wizard that is implemented by making it a child of the according input field.
The fields' values are accessible as equally named variables in sections
Preview
andMain
.
Preview
contains some real oldschool table layout to be rendered in the backend and which may look familiar if you worked
with Templavoilà before. Note the usage of <v:media.image />
to avoid issues with displaying images in the backend due
to relative paths.
Main
finally contains the output to be rendered in the frontend and makes use of some fluid viewhelpers you certainly
know already.
After clearing all caches you should now see a new tab FCE in the New Content Element Wizard that makes our new content
element selectable. As described earlier Flux' LLL rewrite feature will - when enabled - take care of generating translation
keys in typo3conf/ext/quickstart/Resources/Private/Language/locallang.xml
.
To be continued...