The value must be of type 'xx' error by restricted access - typo3

I have a product database extension. Based on Extbase + Fluid.
Everything working like it should be, but I have a Problem with restricted Access.
There are some products only for a certain group of users. When I set a group, and somebody uses a direkt link to this product, the login site should come in case of no login.
It is working for sites, but with products I get the:
"The value must be of type "xx", but was of type "NULL". " error
I also using realUrl.
the enable404forInvalidAlias is set for my extension, so a non-existing product call leads to the 404 page, but unfortunetly I couldn't handle till now the restricted access question.
Version is: 4.5.22
Solution should be work without major update.
UPDATE:
In my showAction the product defined with = NULL default value.
I have already a condition in my fluid template like this:
<f:if condition="{product}">
...
</f:if>
The error message coming from this line:
Tx_Extbase_MVC_Exception_InvalidArgumentValue thrown in file
\typo3\sysext\extbase\Classes\MVC\Controller\Argument.php
in line 389.
I made some debug and the whole showAction runs through.

You can change the action in a way so that it allows for no-instances of your product and check for the existance within fluid:
public function showAction(Tx_YourExt_Domain_Model_Product $product = NULL) ..
in Fluid:
<f:if condition="{product}">
<f:then> <!-- show your product --> </f:then>
<f:else>
<!-- show a login form, e.g. something you have in typoscript -->
</f:else>
</f:if>
Downside of this is, that you can't handle proper 404's easily.
There's an ifAuthenticated viewhelper as well and in combination with the above it should be straight forward to handle proper 404's as well. I'm not sure which TYPO3 version this has been introduced though.

Related

Display full name of logged in TYPO3 frontend user in Fluid

The task is pretty simple: I want to display the full name (first- and lastname) of the current TYPO3 frontend user in Fluid. But somehow, TYPO3 (version 9.5) or Fluid seems to cache data, so a logged in frontend user sometimes sees the name of other another logged user.
Current implementation:
TypoScript:
lib.username = USER_INT
lib.username.userFunc = Vendor\Extension\UserFunc\Username->getUsername
This is a USER_INT, so the output should always be uncached.
Fluid Layout - Default.html:
<f:render partial="Header" section="Top" />
Fluid Partial - Header.html:
<f:section name="Top">
<img src="logo.png">
<f:render partial="Navigation" />
</f:section>
Fluid Partial - Navigation.html
<f:security.ifAuthenticated>
<f:then>
<p>Logged in as: <f:cObject typoscriptObjectPath="lib.username" /></p>
</f:then>
<f:else>
<p>Not logged in</p>
<f:else>
</f:security.ifAuthenticated>
Why does the result of the cObject get cached? Should'nt this always be calculated per request, because lib.username is a USER_INT? I also tried to add a f:cache.disable ViewHelper to the template with no success.
In order to resolve the problem I refactored it to fetch the full name of the fe_user using a JavaScript XHR request to a simple PSR-15 middleware. Is this really the only suitable solution or am I missing something?
Update 17.12.2020
This all works fine. I just spotted a typo in my userFunc, which resulted in the unexpected behavior.
This happens because TYPO3 cache works with frontend user groups, not frontend users. So you will see results cached for the list of user's groups rather than the current user.
Use <v:render.uncache> ... </v:render.uncache> from EXT:vhs to render that part of code uncached. Alternatively you can modify TYPO3 caching to use the current user but this may decrease cache performance and seriously increase amount of cached items.
Beware, that there is a caching problem even with USER_INT and COA_Int see https://forge.typo3.org/issues/37988
You could use only TypoScript for that like:
lib.username = COA_INT
lib.username {
10 = TEXT
10.data = TSFE:fe_user|user|first_name
10.wrap = |
20 = TEXT
20.data = TSFE:fe_user|user|lastname_name
}
Why use TypoScript? It is very limited on what it can bring back. I just finished a tutorial where you can use a ViewHelper to add an ExtBase variable which assigns the whole user object in the FrontEnd and you can use it wherever you want with all it's relations, even the image, which is difficult to get via TypoScript.
TYPO3 tutorial
Best regards
share edit delete flag

fluid template: render image only if it exists

How can I use f:if to see if an image exists on the server before trying to render it?
I have tried the following without success:
<f:if condition="fileadmin/bilder/Header/{data.title}.jpg">
<f:then>
<f:variable name="imageUri" value="fileadmin/bilder/Header/{data.title}.jpg" />
</f:then>
<f:else>
<f:variable name="imageUri" value="fileadmin/bilder/Header/default.jpg" />
</f:else>
</f:if>
{data.title} works correkt. The Problem is that my condition is wrong. It never goes to the else. Probably my condition is interpret as a string und not as an source.
Description here doesn´t help because my Image is on the server.
Render image in fluid template if it exists?
First of all, why would you have a hardcoded path to a file in fileadmin in a template? That's very uncommon, most of the time you have some kind of relation through TYPO3 (e.g. through the media field in the pages table or something like that).
If this is still something you need, you'd have to write a FileExistsViewHelper that checks if the file exists before using it. Here's the documentation on how to write a custom view helper: https://docs.typo3.org/m/typo3/book-extbasefluid/master/en-us/8-Fluid/8-developing-a-custom-viewhelper.html
You'd need to register your filename as an argument with registerArgument and then prepend the path to the TYPO3 installation (to make it an absolute path) and check for this file with file_exists.
Extend from \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper and implement the verdict method.
Your template above just creates a string (as you already guessed).

Controller action - redirect to another template

I am looking for a way to redirect to another template if certain conditions are fulfilled.
Like:
public funtion redirectAction() {
if(certain conditions = TRUE){
REDIRECT TO ANOTHER TEMPLATE WITH DIFFERENT CONTENT
}
}
I've seen some expressions in existing actions:
$this->redirect('list');
In this case, 'list' is the default List.html template, right?
So I thought if I replaced the 'list' with a different template name, that would solve the problem. But i get a opps error.
Or is there any other solution of calling a different template?
Thank you very much!
with a redirect in the controller you always redirect to another function, not a template.
For example with
$this->redirect('list2');
You would then redirect to the function list2Action() and this would expect a Template List2.html
Often however, it is easier to use a switch in fluid.
for example:
<f:if condition="{abc} == 1">
<f:then>
<f:render partial="TempalteAbcTrue" arguments="{_all}" />
</f:then>
<f:else>
<f:render partial="TempalteAbcFalse" arguments="{_all}" />
</f:else>
If you used the normal folder structure of extbase you have a "Layouts", "Templates" and "Partials" folder.
With the example above you can create 2 files in the folder "Partials": "TempalteAbcTrue.html" and "TempalteAbcFalse.html".

Dynamicly change TYPO3 fluid Layout by typeNum

I am using the extension fluidpages and want to switch the layout by the typeNum.
is it possible to change the f:layout by by an condition?
This wont work:
<f:layout name="{f:if(condition: '{typeNum} == 666', then: 'PageAjax', else: 'Page')}"/>
Suggested approach:
<f:layout name="Page/{typeNum}"/>
Required files:
Resources/Private/Layouts/Page/0.html
Resources/Private/Layouts/Page/666.html
Please note: this only works if the {typeNum} variable is guaranteed to exist - if it does not, you will face a "template file not found" error with an empty filename. To avoid this, you can use the VHS extension's v:var.convert ViewHelper to ensure a proper value:
<f:layout name="Page/{typeNum -> v:var.convert(type: 'integer')}"/>
I got the same problem recently, and found the solution after a few researchings.
The problem is, you can not use nested fluid like <f:if>, <f:cObject> or others in <f:layout>. Otherwise, you will get a fatal error in the cache file, saying call to a member function getViewHelper() on a non-object. And when you look at the cache file, you will find it's because $self is not defined.
Therefore, my solution is, searching public function getLayoutName( in \TYPO3\CMS\Fluid\Core\Compiler\TemplateCompiler, and adding \$self = \$this; before \$currentVariableContainer = \$renderingContext->getTemplateVariableContainer();, just like generateCodeForSection()
I have now added the Condition in the Layout template. I get the typeNum from typoscript.
<f:if condition="{f:cObject(typoscriptObjectPath:'plugin.nc_template.settings.pageLayout')} == 'Default'">
<p>Default Template</p>
</f:if>
<f:if condition="{f:cObject(typoscriptObjectPath:'plugin.nc_template.settings.pageLayout')} == 'Ajax'">
<p>Ajax Template</p>
</f:if>
I found an example on the fedext Page, but could not get this to run:
https://fedext.net/blog/archive.html?tx_news_pi1[news]=55&tx_news_pi1[%40widget_0][currentPage]=8&cHash=f9c3165598a28d2aa98fd30ef115bb75
Can´t you use an if-statement instead?
IMHO this is easier to read - and if you need to add more arguments which depends on typeNum, it would stay readable.
<f:if condition="{typeNum} == 666">
<f:then>
<f:layout name="PageAjax">
</f:then>
<f:else>
<f:layout name="Page">
</f:else>
</f:if>

Why does the first assert work, but not the second?

I'm using Watir WebDriver with Firefox.
Here are two asserts for the same element. The first works, but not the second:
assert_match(/Please add user to GMT/, #browser.small(:class, "error").text)
assert_match(/Please add user to GMT/, #browser.div(:class, "eight mobile-three columns").small(:class, "error").text)
I need the second assert to work, because there are 8 error messages on the page, which are presented if the user does not populate 8 mandatory fields. All 8 errors have the identical HTML. So, I need to be able to step down from the parent.
Here is the HTML:
<div class="eight mobile-three columns">
<a id="btnAddUserToGMT" class="success radius button expand error" onclick="AddUserToGMT();" data-reveal-id="addToGMT" href="#"> … </a>
<small class="error" style="margin-top:0px;">
Please add user to GMT
</small>
</div>
Here is the error message:
Watir::Exception::UnknownObjectException: unable to locate element, using {:class=>"error", :tag_name=>"small"}
C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-webdriver-0.6.4/lib/watir-webdriver/elements/element.rb:490:in `assert_exists'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-webdriver-0.6.4/lib/watir-webdriver/elements/element.rb:85:in `text'
C:/Documents and Settings/Asserts_01.rb:22:in `testBasic'
The complaint for the second assert is:
unable to locate element, using {:class=>"error", :tag_name=>"small"}
And yet that same using was OK for the first assert.
Problem solved.
After discussion with the developer, it appears that by some unusual manipulation of automatically generated HTML, the text of the error message appears at its correct location on the page. But the assertion must be based on a different tag, which is specified at some completely different position on the page.
For example, I was trying to assert on this code at the correct position:
<small class="error" style="margin-top:0px;">
Gender is required
</small
Even unique xPath generated by FirePath failed to find this.
What I should have asserted on was a HIDDEN tag on a completely different part of the page.
<input id="errorMsgGenderID" name="errorMsgGenderID" type="hidden" value="Gender is required" />
There were several such tags for each mandatory field that was not populated, all bunched together on the same line. They were all "input" tags, which puzzled me.