I'd like to pass an extension GP variable to a form that I built outside of the plugin. When trying to do so, I used a COA_INT which turned out to output <INT_SCRIPT. instead of the value.
In order to do so, I built this typoscript object:
lib.gpSWord = COA_INT
lib.gpSWord {
# Protects from XSS!
stdWrap.htmlSpecialChars = 1
10 = TEXT
10 {
stdWrap.data = GP:tx_indexedsearch_pi2|search|sword
}
}
and then tried to output it like this (as suggested here:
<f:form.textfield name="search[sword]" value="<f:format.raw>{f:cObject(typoscriptObjectPath: 'lib.gpSWord')}</f:format.raw>" id="tx-indexedsearch-searchbox-sword" class="tx-indexedsearch-searchbox-sword font-h2" />
And also tried building up the variable before hand using f:variable but nothing worked.
The expected result would be that the variable would be set and the value would then be passed to the form.textfield viewhelper but appearently this does'nt work either.
You should commit the variable in Typoscript (setup) with:
page.10 = FLUIDTEMPLATE
page.10.variables.gpSWord < lib.gpSWord
Then you are able to use it in you Fluidtemplate with:
<f:format.raw>{gpSWord}</f:format.raw>
lib.gpSWord = COA_INT
lib.gpSWord {
stdWrap {
wrap = &tx_indexedsearch_pi2[sword]=|
data = GP:tx_indexedsearch_pi2|sword
if.isTrue.data = GP:tx_indexedsearch_pi2|sword
}
}
Try this for GP.
Using TYPO3 8 LTS, we got many standardized filenames like:
ABC_105-Report.pdf
DEFGH_110-Brochure.ppt
We need to remove whatever is at left of "-" so it becomes a list like this in TYPO3 Frontend:
Report.pdf
Brochure.ppt
We are already using VHS Viewhelpers which contains Format:Eliminiate, Substring so it may be part of the solution.
One possible solution is VHS: Format / PregReplaceViewHelper.
<f:alias map="{filenames: {
0: 'ABC_105-Report.pdf',
1: 'DEFGH_110-Brochure.ppt',
2: 'FilenameWithoutMagicChar.jpg',
3: 'Multiple-Magic-Chars.jpg'}}">
<ul>
<f:for each="{filenames}" as="filename">
<li>
{v:format.pregReplace(
subject: filename,
pattern: '/^[^-]*-/',
replacement: ''
)}
</li>
</f:for>
</ul>
</f:alias>
Result:
Report.pdf
Brochure.ppt
FilenameWithoutMagicChar.jpg
Magic-Chars.jpg
If 'Chars.jpg' is required instead of 'Magic-Chars.jpg', the regular expression is /-.*/.
a very basic typoscript viewhelper:
in fluid:
<f:cObject typoscriptObjectPath="lib.filenameStub" data="{filename}" />
in typoscript:
lib.filenameStub = TEXT
lib.filenameStub {
current = 1
split {
max = 2
token = -
returnKey = 1
}
}
I modified a part of a working typoscript template from:
10 = COA
10.stdWrap.wrap = <div class="toggleBar">|</div>
10 {
...
to
toggleBarDIV = COA
toggleBarDIV.stdWrap.wrap = <div class="toggleBar">|</div>
toggleBarDIV {
...
Now it is not working anymore. Is it not allowed to use names in typoscript?
Where is it allowed and where not? Where can I get more infos?
As far as I know, COA keys must always be numbers (10,20,1,2...);
There is a feature request about using more meaningful keys:
https://forge.typo3.org/issues/45488
In Fluid Template and tx_news, I need to replace line breaks with "\n" for passing into JavaScript function.
If a JavaScript string contains line break, console will print "Unexpected token."
<a onclick="doSomething('{newsItem.bodytext}');">Click me</a>
How can you replace line breaks with "\n" in this example?
AS urbantrout already wrote: you can write an own viewhelper in PHP.
But you also can use a TypoScript-Viewhelper:
<a onclick="doSomething('{newsItem.bodytext -> f:cObject(typoscriptObjectPath: \'lib.nlReplace\')}');">Click me</a>
(as you are in a string you need to escape the inner ')
and some TypoScript like
lib.nlReplace = TEXT
lib.nlReplace {
current = 1
stdWrap.replacement {
1 {
search = #\n#
replace = \\n
useRegExp = 1
}
}
}
You could write your own ViewHelper and use it like this:
{namespace ns=Vendor\ExtensionName\ViewHelpers}
<a onclick="doSomething('{newsItem.bodytext -> ns:viewhelperName()}');">Click me</a>
More infos here: Developing a custom ViewHelper
I am trying to write the following if condition in fluid but it is not working as I would hope.
Condition
As part of a for loop I want to check if the item is the first one or 4th, 8th etc
I would have thought the following would work but it display the code for every iteration.
<f:if condition="{logoIterator.isFirst} || {logoIterator.cycle % 4} == 0">
I have managed to get it working with a nested if but it just feels wrong having the same section of code twice and also having the cycle check use a <f:else> instead of == 0
<f:if condition="{logoIterator.isFirst}">
<f:then>
Do Something
</f:then>
<f:else>
<f:if condition="{logoIterator.cycle} % 4">
<f:else>
Do Something
</f:else>
</f:if>
</f:else>
</f:if>
TYPO3 v8
Updated the answer for TYPO3 v8. This is quoted from Claus answer below:
Updating this information with current situation:
On TYPO3v8 and later, the following syntax is supported which fits
perfectly with your use case:
<f:if condition="{logoIterator.isFirst}">
<f:then>First</f:then>
<f:else if="{logoIterator.cycle % 4}">n4th</f:else>
<f:else if="{logoIterator.cycle % 8}">n8th</f:else>
<f:else>Not first, not n4th, not n8th - fallback/normal</f:else>
</f:if>
In addition there is support for syntax like this:
<f:if condition="{logoIterator.isFirst} || {logoIterator.cycle} % 4">
Is first or n4th
</f:if>
Which can be more appropriate for some cases (in particular when using
a condition in inline syntax where you can't expand to tag mode in
order to gain access to the f:else with the new if argument).
TYPO3 6.2 LTS and 7 LTS
For more complex if-Conditions (like several or/and combinations) you can add your own ViewHelper in your_extension/Classes/ViewHelpers/. You just have to extend Fluids AbstractConditionViewHelper. The simple if-ViewHelper that shipps with Fluid looks like this:
class IfViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper {
/**
* renders <f:then> child if $condition is true, otherwise renders <f:else> child.
*
* #param boolean $condition View helper condition
* #return string the rendered string
* #api
*/
public function render($condition) {
if ($condition) {
return $this->renderThenChild();
} else {
return $this->renderElseChild();
}
}
}
All you have to do in your own ViewHelper is to add more parameter than $condition, like $or, $and, $not etc. Then you just write your if-Conditions in php and render either the then or else child. For your Example, you can go with something like this:
class ExtendedIfViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper {
/**
* renders <f:then> child if $condition or $or is true, otherwise renders <f:else> child.
*
* #param boolean $condition View helper condition
* #param boolean $or View helper condition
* #return string the rendered string
*/
public function render($condition, $or) {
if ($condition || $or) {
return $this->renderThenChild();
} else {
return $this->renderElseChild();
}
}
}
The File would be in your_extension/Classes/ViewHelpers/ExtendedIfViewHelper.php Then you have to add your namespace in the Fluid-Template like this (which enables all your self-written ViewHelpers from your_extension/Classes/ViewHelpers/ in the template:
{namespace vh=Vendor\YourExtension\ViewHelpers}
and call it in your template like this:
<vh:extendedIf condition="{logoIterator.isFirst}" or="{logoIterator.cycle} % 4">
<f:then>Do something</f:then>
<f:else>Do something else</f:else>
</vh:extendedIf>
Edit: updated.
Updating this information with current situation:
On TYPO3v8 and later, the following syntax is supported which fits perfectly with your use case:
<f:if condition="{logoIterator.isFirst}">
<f:then>First</f:then>
<f:else if="{logoIterator.cycle % 4}">n4th</f:else>
<f:else if="{logoIterator.cycle % 8}">n8th</f:else>
<f:else>Not first, not n4th, not n8th - fallback/normal</f:else>
</f:if>
In addition there is support for syntax like this:
<f:if condition="{logoIterator.isFirst} || {logoIterator.cycle} % 4">
Is first or n4th
</f:if>
Which can be more appropriate for some cases (in particular when using a condition in inline syntax where you can't expand to tag mode in order to gain access to the f:else with the new if argument).
v:if.condition will be deprecated in vhs V2.0
use v:if stack instead:
https://github.com/FluidTYPO3/vhs/issues/493
You could also use the If Condition Extend ViewHelper provided by the VHS extension:
<v:if.condition>
<v:if.condition.extend>
{logoIterator.isFirst} || {logoIterator.cycle % 4} == 0
</v:if.condition.extend>
<f:then>Output if TRUE</f:then>
<f:else>Output if FALSE</f:else>
</v:if.condition>
On a side note: the VHS extension provides lots of useful ViewHelpers. I feel a lot of them should be included in TYPO3 Fluid.
For many cases its enough to use an array-comparison - so you don't have to create a custom view-helper.
AND
<f:if condition="{0:user.number,1:user.zip}=={0:123,1:01234}">
OR
<f:if condition="{0:user.number,1:user.zip}!={0:false,1:false}">
Sadly this works just to check if a variable is set and not against a value. But for many cases this is enough.
PS:(with this array comparison you can also compare strings)
In addition to Daniels' answer, I made a ViewHelper that accepts multiple conditions, with either an "and"-mode (default) or an "or"-mode:
<?php
namespace TLID\Contentelements\ViewHelpers;
class IfViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper {
/**
* Checks conditions
*
* #param mixed $checks
* #param string $type
*
* #return boolean whether is array or not
*/
public function render($checks, $type = "and") {
$success = $type === "and" ? true : false;
$doc = new \DOMDocument();
$doc->loadHTML($this->renderChildren());
$xpath = new \DOMXpath($doc);
// get store values
$storeNodes = $xpath->query("//body/store");
$store = "";
foreach ($storeNodes as $storeNode) {
foreach ($storeNode->childNodes as $childNode) {
$store .= $doc->saveHTML($childNode);
}
}
// do the actual check
foreach ($checks as $check) {
if (
($type === "and" && (is_array($check) && count($check) === 0 || is_object($check) && get_object_vars($check) === 0 || empty($check))) ||
(is_array($check) && count($check) !== 0 || is_object($check) && get_object_vars($check) !== 0 || !empty($check))
) {
$success = $type === 'and' ? false : true;
break;
}
}
// render content
$renderQueryElement = $success ? "success" : "failure";
$renderNodes = $xpath->query("//body/" . $renderQueryElement);
$content = "";
foreach ($renderNodes as $renderNode) {
foreach ($renderNode->childNodes as $childNode) {
$content .= $doc->saveHTML($childNode);
}
}
//insert contents
$matches;
$content = preg_replace("/<use[^>]*><\/use>/", $store, $content);
//return rendered content
return $content;
}
}
?>
Though it can be written alot better, it works.
Here is how i use it:
{namespace vhs=TLID\contentelements\ViewHelpers}
<vhs:if checks="{0: settings.link}">
<f:comment><!-- store the content --></f:comment>
<store>
<f:if condition="{images}">
<f:for each="{images}" as="image">
<f:image image="{image}" alt="{image.description}" title="{image.title}" />
</f:for>
</f:if>
<vhs:if checks="{0: settings.headline, 1: settings.text}" type="or">
<success>
<div>
<f:if condition="{settings.headline}"><h2><f:format.nl2br><vhs:shy>{settings.headline}</vhs:shy></f:format.nl2br></h2></f:if>
<f:if condition="{settings.text}"><p><f:format.nl2br><vhs:shy>{settings.text}</vhs:shy></f:format.nl2br></p></f:if>
</div>
</success>
</vhs:if>
</store>
<f:comment><!-- use the content of this container on success --></f:comment>
<success>
<vhs:link href="{settings.link}" target="{settings.target}" class="box">
<use />
</vhs:link>
</success>
<f:comment><!-- use the content of this container on failure --></f:comment>
<failure>
<div class="box">
<use />
</div>
</failure>
</vhs:if>
It additionally has a store-element, because i don't like it to write the same code twice. So you can optionally save some fluid and pass it to both the success and failure containers without the need for repetition.
It is possible to implement complex if conditions with a combination of f:if, v:variable.set and v:math. Use the math ViewHelper to do the magic and store its result in a variable. Then use if comparators to validate and act upon it.
Here is my sample code:
<f:for each="{customers}" as="customer" iteration="iterator">
<v:variable.set name="isFirst" value="{v:math.modulo(a: iterator.cycle, b: settings.itemsperrow, fail: 0)}" />
<f:if condition="{isFirst}==1">
<div class="row">
</f:if>
<div class="col-md-{settings.colWidth}">
<div class="clientlogo_ref">
<f:image src="{customer.logo.originalResource.publicUrl}" />
</div>
</div>
<f:if condition="{isFirst}==0">
</div>
</f:if>
</f:for>
This code begins / ends a grid row for every X item, defined by settings.itemsperrow. This is variable and can be set in the plugin's configuration. It uses modulo to calculate iterator.cycle (counter beginning with 1) mod settings.itemsperrow. If the result is 1, it is the first element of a row. 0 means it is the last, so row must be closed.
Yes it feels wrong but this is the only way you can do it. This is a very good site for viewhelper :: https://fluidtypo3.org/viewhelpers/fluid/master/IfViewHelper.html
For me best way to use 'f:cycle'. If i need devide for rows each 3th elements i just do:
<v:variable.set name="wraper" value='</div><div class="row">' />
<f:for each="{items}" as="item" iteration="itemIterator">
....
<f:cycle values="{0: '', 1: '', 2: '{wraper}'}" as="cycle">
{cycle -> f:format.raw()}
</f:cycle>
...
</f:for>
If you have CObjects help this workaround for a Logical OR:
# Sidebar | 1 ColPos = 78
lib.sidebar1 < styles.content.get
lib.sidebar1.select.where = colPos=78
# Sidebar | 2 ColPos = 79
lib.sidebar2 < styles.content.get
lib.sidebar2.select.where = colPos=79
#LogicalOR
lib.tempLogicalOrSidebar = COA
lib.tempLogicalOrSidebar {
10 < lib.sidebar1
10.stdWrap.override.cObject =< lib.sidebar2
}
FLUID IF CONDITION:
<f:if condition="{f:cObject(typoscriptObjectPath: 'lib.tempLogicalOrSidebar.10')}">
Status 2017:
Here is an example of a modern VHS viewhelper condition using the stack attribute. This example also contains a NULL check and a logical or (||) to show how it is done.
<v:if stack="{0: '{itemId}', 1:'==', 2:NULL, 3: '||', 4: '{itemId}', 5: '==', 6: '{falMedia.uid}'}">
<f:then>
...
</f:then>
</v:if>
Mind that NULL is NOT quoted!