Using ViewHelper inside a partial path - typo3

I'm working on a new extension and my model has the attribute 'type' which can get different strings from the TCA form. Strings only!
The name of the partial that my template should load is inside the 'type' attribute from my model. So here comes my problem. Since TYPO3 4.7.x the .html file names for fluid have to start with an uppercase letter. Inside the 'type' attribute the name of the partial that should be loaded is always lowercase. For that, I wrote a simple view helper that contains only this method:
public function render($string) {
return ucfirst($string);
}
Inside my template I tried to use this view helper for the path to the partial:
{namespace vh=Tx_MyExtension_ViewHelpers}
<f:for each="{obj.subObjects}" as="sub">
<f:render partial="OtherObject/{vh:String.UpperFirstCharacter(string:'{sub.type}')}" arguments="{sub:sub}" />
</f:for>
If I try to load this in the fontend, nothing from my extension will be rendered and there are no error messages anywhere. The problem depends on my view helper, 'cause even if I try to load only this:
{vh:String.UpperFirstCharacter(string:'test')}
{vh:String.UpperFirstCharacter(string:'{sub.type}')}
There is nothing comming back. If I only output {sub.type} it shows me the string that I want, but in lowercase.

Obviously your problem is that you're ViewHelper doesn't do what you want it to do.
First of all, ViewHelper names are to be written in lowerCamelCase.
Second, you don't need to place sub.type in curly braces:
This syntax...
{vh:string.upperFirstCharacter(string:sub.type)}
... should be sufficient.
Fluid will then look for a ViewHelper named
Tx_MyExtension_ViewHelpers_String_UpperFirstCharacter
or namespaced
\My\Extension\ViewHelpers\String\UpperFirstCharacter
Please check that this is the case.

So I found the issue. Fluid can't handle namespaces. So first my ViewHelper looked like this:
<php
namespace TYPO3\MyExtension\ViewHelpers\String;
class UpperFirstCharacterViewHelper ...
Now I changed the name of my class and removed the namespace:
<php
class Tx_MyExtension_ViewHelpers_String_UpperFirstCharacterViewHelper ...
This is how it works. At the moment I work with TYPO3 6.1.6.
Thank you anyway lorenz for your help!
EDIT:
I went fully retarded! Fluid CAN handle namespaces. I just had to set the namespace the right way.
That's how you set the namespace inside the template:
{namespace vh=TYPO3\MyExtension\ViewHelpers}
On top of this comment you can see how my ViewHelper looks like with a namespace.

Related

TYPO3 Custom File Name for rendered images

I wanted to ask if in TYPO3 LTS 11 is a way to change how TYPO3 generates the name for a rendered image?
In the template I use the f:image Viewhelper with cropVariant option to generates the image (done by TYPO3 Image Rendering).
<f:image image="{file}" cropVariant="desktop" />
That Image then has a name something like this: csm_my-image_88ade60319.jpg
Can I define how TYPO3 should name those rendered images?
For example to "my-image_large.jpg"
The cms_ part of the filename is hardcoded in the TYPO3\CMS\Core\Resource\Processing\ImageCropScaleMaskTask::getTargetFileName function. The hash at the end is added in the TYPO3\CMS\Core\Resource\Processing\AbstractGraphicalTask::getTargetFileName function. Not sure why cms_ is added, but the hash is to make sure the file name is unique. The hash is generated using the file and configuration.
So by default it isn't possible. If you really want different file names you can XCLASS TYPO3\CMS\Core\Resource\Processing\ImageCropScaleMaskTask with something like:
<?php
namespace Vendor\MyExtension\Resource\Processing;
class ImageCropScaleMaskTask extends \TYPO3\CMS\Core\Resource\Processing\ImageCropScaleMaskTask
{
public function getTargetFileName()
{
$originalFileName = parent::getTargetFilename();
$fancyFileName = ... // Do your things to change the file name
return $fancyFileName;
}
}
You'll probably also want to add something to configure what the file name should be. So you'd probably also need to XCLASS the TYPO3\CMS\Fluid\ViewHelpers\ImageViewHelper class or create a new ViewHelper to add some settings to the processing instructions.
Just make sure the file name is unique or you might get weird results.

TYPO3 write name of sys_category into class name at tx_news

I'using TYPO3 8.7 and write the name of a sys_categoryinto the class name of tx_news-template Item.html, like this:
<div class="{f:format.case(value: '{newsItem.firstCategory.title}', mode: 'lower')}"> ...
Now one of my categories has two words, like 'my category', so the classname results:
<div class="my category">
How can I remove the empty ' ' from class name in FLUID?
Thanks for your help.
Also i would think about using the category uid instead of category name. As names are brittle. An editor might rename somthing and your css code breaks. But your pretty save if you use uids
This is not possible by default - However I see the following options:
1) Create a custom ViewHelper: This would be the best solution as you don't need any 3rd party extension as dependency.
2) Misuse a different field of the category record like seo_title, description or similiar
3) You could use the replace VH of EXT:vhs, see https://fluidtypo3.org/viewhelpers/vhs/master/Format/ReplaceViewHelper.html
You could adjust your css to recognize double classes as well.
.category
.my.category
Still the way to go should be unique identifiers instead of names or titles.

tx_news: extend with a second container $contentElements

TYPO3 8.7.4
news 6.0.0
Is it possible to extend news in a news_extend extension with a second container? (like contentElements)
The goal is to place this second container in the related content of the detail page.
Is there an example?
Yes, it's easy. You need to add field to database, configure it in TCA, extend the news model and adjust the detail template.
in news_extend/ext_tables.sql add:
CREATE TABLE tx_news_domain_model_news (
tx_newsextend_content_elements_second text
);
in news_extend/Configuration/TCA/Overrides/tx_news_domain_model_news.php:
$newNewsColumns = [
'tx_newsextend_content_elements_second' => [
// .... here copy the original 'content_elements' field's config from ext news' TCA. update the label to yours.
];
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('tx_news_domain_model_news', $newNewsColumns);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes('tx_news_domain_model_news', 'tx_newsextend_content_elements_second', '', 'after:content_elements');
in news_extend/Resources/Private/Language/locallang_db.xlf add:
...
<trans-unit id="tx_news_domain_model_news.tx_newsextend_content_elements_second">
<source>Additional content elements</source>
</trans-unit>
news_extend/Classes/Domain/Model/News.php:
namespace [my vendor]\NewsExtend\Domain\Model;
class News extends \GeorgRinger\News\Domain\Model\News {
// here copy all uses of contentElement field from original model, only name it txNewsextendContentElementsSecond.
// watch whether it's only declared property and getter/setter (simple fields), or something more is done in the model and do it the same way as there.
// tip: see getContentElementIdList() method
}
register your extension as provider of news' model extending class:
in news_extend/ext_localconf.php add:
$GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['classes']['Domain/Model/News'][] = 'news_extend';
now you can use this in your template:
<f:if condition="{newsItem.txNewsExtendContentElementsSecond}">
<!-- content elements second -->
<f:cObject typoscriptObjectPath="lib.tx_news.contentElementRendering">{newsItem.txNewsExtendContentElementsSecondIdList}</f:cObject>
</f:if>
Above may not just work if you copy-paste it, I'm writing it from my notes. But it will help you to get the idea. Good luck
This should work. Just extend the news tca and model as described in the documentation

Using the output of a partial as a variable in main template

Out of this question: Random image with v:iterator.random | cache issue
I use a partial to render non-cacheable stuff (in this case a random image).
I do this with this code in the main-template:
{v:render.uncache(partial: 'Random-Image', arguments: {iterator: images})}
Have this directly in the template outputs the right thing (the url to an image, for example fileadmin/upload/abc.jpg). But if I want to use this as a variable for the src from <f:image it does not work:
<f:image src="{v:render.uncache(partial: 'Random-Image', arguments: {iterator: images})}" alt="alt text" />
Also set as a variable it with v:variable.set does not work.
All I get is: <!--INT_SCRIPT.0081e57d9fd92c925bb35d79fd9d3f79-->
Also when I debug it:
<f:debug>
{v:render.uncache(partial: 'Random-Image', arguments: {iterator: images})}
</f:debug>
I get <!--INT_SCRIPT.0081e57d9fd92c925bb35d79fd9d3f79-->
So, is it possible to use the output of a partial as a variable? Or is it possible to set a variable in the partial and use it in the main-template?
I think you mixed up two things a bit, so I would like to separate your questions:
1) Is it possible to use the output of a partial as a variable?
Yes, like the way you wanted it. Actually you did it.
But let see a test:
There is a Partial : Test/Message
With the content: "It is a test"
Then in the main template you can use something like this:
<div class="test">
<f:if condition="{f:render(partial:'Test/Message')}
== 'It is a test'">
<f:then>Passed</f:then>
<f:else>Failed</f:else>
</f:if>
</div>
In this case you would see "Passed" and if you change the Partial to "It should failed" then you will get "Failed" rendered.
2) Why do you see <!--INT_SCRIPT.0081e57d9fd92c925bb35d79fd9d3f79--> ?
This is a not cached content, so like COA_INT or USR_INT objects in TypoScript.
You can find a function in the typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php its name is INTincScript_process. It is responsible to find such lines in the code and replace them with the not cached content.
It means, if you render your Template, that partial renders only a reference to a not cached object, but not the content itself.
Finally to suggest a solution to the original problem, try to render the whole image inside the partial not just the path to it. So include the into the partial where the v:iterate.random ViewHelper is used. Then the v:render.uncache should mark the whole image block as not cacheable.

Parameter and view naming collisions in Play/Scala templates

I am new to Play Framework and still trying to wrap my head around some things with the new Scala template engine.
Let's say I have the following package structure:
app/
app/controllers/Items.scala
app/models/Item.scala
app/views/layouts/page.scala.html
app/views/item/show.scala.html
app/views/item/details.scala.html //partial
And this is my item/show template:
#(item: Item, form: Form[Item])(implicit flash: Flash)
#layout.page() {
#*want to include details partial, wont work due to item param*#
#item.details(item)
}
Since including another template (e.g. including item/details above) is the exact same syntax as accessing a template parameter (e.g. item above), obviously this existing naming convention won't work without something changing.
I know I can rename my "app.views.item" package to "app.views.items", and rely on singular/plural forms to differentiate the view from the param name, but this does not seem like a very straightforward solution. Also what if I really want the parameter name to be the same as the view package?
One idea I have is to prepend all my views with an extra top level package:
app/views/views/item/details.scala.html
So the include syntax would be #views.item.details(), but again this is obviously a hack.
What is a good way to avoid this issue? How can I better organize my code to avoid such naming collisions?
Most other template engines use operations like "include" or "render" to specify a partial include. I don't mean to offend anyone here, but is the Play Scala template engine syntax so terse that it actually dictates the organization of code?
3 solutions:
First
Typpicaly for partial templates you should use tags as described in the docs, where app/views/tags folder is a base:
file: app/views/tags/product.scala.html
in the templates (no initial import required in the parent view full syntax will allow you to avoid name-clash: #tags.packageName.tagName()):
<div id="container">
#tags.product(item)
</div>
Of course in your case you can also use packages in the base folder
file: app/views/tags/item/product.scala.html
<div id="container">
#tags.item.product(item)
</div>
I'm pretty sure that'll solve your problem.
Second
To avoid clash without changing package's name you can just rename the item in your view, also I recommend do not use a form name for the Form[T] as it can conflict with helpers:
#(existingItem: Item, existingItemForm: Form[Item])(implicit flash: Flash)
#layout.page() {
#item.details(existingItem)
}
Third
If you'll fill your Form[Item] before passing to the view with given Item object, you don't need to pass both, as most probably you can get data from the form:
#(itemForm: Form[Item])(implicit flash: Flash)
#layout.page() {
<div>Name of item is: #itemForm("name").value (this is a replacemnet for ##existingItem.name </div>
#item.details(itemForm)
}
Of course in you product.scala.html you'll need to change the #(item: Item) param to #(itemForm: Form[Item])