.. include:: /Includes.rst.txt .. _template-creation-by-example: Template Creation by example ============================ In this section we will show you some of the techniques you got to know in the course so far, in the interaction with our sample extension *sjr_offers*. We will focus on practical solutions for repeating problems. The directory structure of the extension is shown in Figure 8-2. We are using both layouts and partials, to avoid double code. Inside *Scripts* we put JavaScript code that we use for animations and for a Date picker in the frontend. .. figure:: /Images/8-Fluid/figure-8-2.png :align: center Figure 8-2: Folder structure of layouts, templates and partials inside the extension sjr_offers The extension *sjr_offers* has an ``OfferController`` and an ``OrganizationController``. Using the ``OfferController``, offers can be displayed as a list using the method ``indexAction()`` or as single view using the method ``showAction()`` method. Also offers can be created using the method ``newAction()`` and available offers can be edited using the method ``editAction()``. The ``OrganizationController`` incorporates the same actions for organizations, with exception of the creation of organizations. Within the folder :file:`EXT:sjr_offers/Resources/Private/Templates` we have created a folder for each controller, without the suffix *Controller* in the name. Each action method has its own HTML template. There is also no suffix *Action* allowed in the name. Setting up the HTML basic framework ---------------------------------------------------- The various templates have many common elements. First we define the basic framework by a common layout (see the section :ref:`creating-a-consistent-look-and-feel-with-layouts` earlier in this chapter) and store repeating code in partials (see the section ":ref:`moving-repeating-snippets-to-partials`" earlier in this chapter). The basic framework of our templates looks as follows:: In most templates we are referencing the layout ``default``, that should build the "frame" of our plugin output. The actual template resides in a section with the name ``content``. The layout definition is stored in the HTML file *EXT:sjr_offers/Resources/Private/Layouts/default.html* ::
A section ``content`` of the respective template is rendered and after this a message to the frontend user is shown if necessary. The complete content of the plugin is then "packed" in a ``div`` container. The message - a so called *flash message* - will be created inside our sample extension in the controller, e.g. at unauthorized access (see also the sections for edit and delete controller actions in :ref:`chapter 7 `):: public function updateAction(\MyVendor\SjrOffers\Domain\Model\Offer $offer) { $administrator = $offer->getOrganization()->getAdministrator(); if ($this->accessControlService->isLoggedIn($administrator)) { // ... } else { $this->flashMessages->add('Please log in.'); } // ... } Store functions in ViewHelper ------------------------------------------------- With this the base framework of our plugin output is ready. In the templates of our sample extension there still exists some repeating jobs, which can be stored in ViewHelper classes. One requirement for the extension is, that the organizations can edit their (and only their) offers in the frontend. We have to control the access at different levels, so that not every website user can change the data. We have discussed the different level of the access control already in chapter 7. One of those levels are the templates. Elements for editing the data, like forms, links and icons, should only displayed when an authorized administrator of the organization is logged in as frontend user (see figure 8-3). In chapter 7 we suggested the ``IfAuthenticatedViewHelper`` and the ``AccessControlService``, that we had implemented for this purpose. .. figure:: /Images/8-Fluid/figure-8-3.png :align: center Figure 8-3: Single view of an organization with its offers (left) and the same view with shown editing symbols (right) Another repeating job is the formatting of numbers and date intervals, For example how the date is displayed for the *Offerperiod* (*Angebotszeitraum*) in Figure 8-3. An offer can have a minimum and/or a maximum amount of attendees for example. If none of this is given, nothing should be displayed. If only one of these values is given the value should be prefixed with from respectively to. We store these jobs in a ``NumericalRangeViewHelper`` and call it in our template like this: ``{offer.ageRange}`` Alternatively you can use the inline notation of Fluid (therefore see the box :ref:`Inline Notation Versus Tag Based Notation ` earlier in this chapter): ``{offer.ageRange->sjr:format.numericRange()}`` The `NumericRangeViewHelper` is implemented as follows: .. code-block:: php namespace MyVendor\SjrOffers\ViewHelpers\Format; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; class NumericRangeViewHelper extends AbstractViewHelper { /** * @param \MyVendor\SjrOffers\Domain\Model\NumericRangeInterface $range The range * @return string Formatted range */ public function render(\MyVendor\SjrOffers\Domain\Model\NumericRangeInterface $range = NULL) { $output = ''; if ($range === NULL) { $range = $this->renderChildren(); } if ($range instanceof \MyVendor\SjrOffers\Domain\Model\NumericRangeInterface) { $minimumValue = $range->getMinimumValue(); $maximumValue = $range->getMaximumValue(); if (empty($minimumValue) && !empty($maximumValue)) { $output = 'bis ' . $maximumValue; } elseif (!empty($minimumValue) && empty($maximumValue)) { $output = 'ab ' . $minimumValue; } else { if ($minimumValue === $maximumValue) { $output = $minimumValue; } else { $output = $minimumValue . ' - ' . $maximumValue; } } } return $output; } } The method render() has the optional argument ``$range``. This is important for the inline notation. When this argument is not set (also ``NULL``), the code between the starting and ending tag is processed ("normal" notation) by calling the method ``renderChildren()``. Is the result an object that implements the NumericRangeInterface then the described use cases are checked step by step and the resulting string is returned. In a similar manner the ``DateRangeViewHelper`` was implemented. Design a form ------------------------------------------------- At the end we show you another sample for designing a form for editing the basic data of an organization. You find the associated template *edit.html* in the folder *EXT:sjr_offers/Resources/Private/Templates/Organization/*. .. code-block:: html {namespace sjr=MyVendor\SjrOffers\ViewHelpers}






The form is enclosed in the tags of the ``IfAuthenticatedViewHelper``. If the access is granted than the form is displayed, otherwise the content of the partial ``accessError`` is displayed. ::
You are not authorized to execute this action. Please first log in with your username and password.
With the declaration of ``object="{organization}"`` the proper form is bound to the assigned ``Organization`` object in the ``editAction()``.TODO: Rewrite sentence The form consists of input fields that are created by Fluid with the ``form.textbox`` ViewHelper respectively the ``form.textarea`` ViewHelper. Each form field is bound to their specific property of the ``Organization`` object using ``property="telefaxNumber"``. The attribute value of the concrete object is inserted in the form fields during rendering of the page. When submitting the form, the data is send as POST parameters to the method ``updateAction()``. When the entered data is not valid, the method ``editActon()`` is called again and an error message is displayed. We have stored the HTML code for the error message in a partial ``formErrors`` (see :file:`EXT:sjr_offers/Resources/Private/Partials/formErrors.html`). In this partial, the name of the form that relates to the error message is given as ``formName``::

{errorDetail.message}

.. sidebar:: Localize error messages The error messages of the default validators that are delivered with Extbase are not localized in version 1.2 (TYPO3 4.4). You can translate the messages yourself by replacing the before described partial ``formErrors`` with the following code: .. code-block:: html

In the file :file:`EXT:sjr_offers/Resources/Private/Language/locallang.xml` you have to write for example:: This solution is only an agreement. The default localization of the error messages is planned for a future version of Extbase.TODO: rework for current Extbase version