Insert comma separated list created with HMENU in TEXT.value - typo3

I want to insert my comma separated list created with HMENU at the value of the TEXT item. If I fill the list manually, all working fine, but I can't fill my value from the created hmenu:
temp.pageIds = HMENU
temp.pageIds.entryLevel = 2
temp.pageIds.1 = TMENU
temp.pageIds.1 {
NO.stdWrap.field = uid
NO.allWrap = |,
NO.doNotLinkIt = 1
}
#output temp.pageIds for example 13,53,12,34,
temp.orderedContent = TEXT
temp.orderedContent {
value < temp.pageIds # <------ not working (value = 23,25,57,... working)
split {
token = ,
cObjNum = 1
1 = COA
1 {
10 = CONTENT
10 {
table = tt_content
select {
pidInList.current = 1
where = colPos = 0
}
}
}
}
}
Any ideas?

The value property of a TEXT object will not be interpreted. If you just copy the temp.pageIds-object, it will be just be the literal string HMENU (and have some subproperties), which is not a list of page IDs.
To get it evaluated, notice in the docs that the type of value is of type string/stdWrap, so the value will be filtered through the stdWrap-function. stdWrap has the property cObject, which can be used to interpret content objects.
So this should work:
value.cObject < temp.pageIds

Related

How to get "leveltitle" for HMENU items via TypoScript?

I have a menu (HMENU with special = updated) that gives me the latest sub and sub-sub pages from 3 categories.
The page structure looks like this in the backend:
In addition to the title, I would like to output the name of the respective category (the parent level-1 page).
This is my TypoScript attempt:
lib.myMenu = HMENU
lib.myMenu {
special = updated
special{
value.field = 10,11,12
beginAtLevel = 1
limit = 99
}
1 = TMENU
1{
NO{
doNotLinkIt = 1
stdWrap.cObject = COA
stdWrap.cObject {
10 = TEXT
10{
wrap = <h3>|</h3>
field = seo_title // title
typolink.parameter.field = uid
}
20 = HMENU
20{
wrap = <div class="category attempt-1">|</div>
special = rootline
special.range = 1|1
special.value.field = uid # does not work
1 = TMENU
1.NO.allWrap = |
}
30 = TEXT
30{
wrap = <div class="category attempt-2">|</div>
data = leveltitle : 1 # does not work as expected
}
}
}
}
}
Unfortunately it does not work, because …
special = rootline does not support special.value.
data = leveltitle : 1 uses the ID of the current page, instead of the TMENU item ID.
Does anyone have another approach how I can get the title of the respective category using TypoScript?
Edit: Background information / what this is needed for
With this menu I intend to replace the news module ext:news of an existing project. Instead of news records, pages are now used and this menu creates the list view. Of course a TypoScript page browser will be added.
I would not rebuild the complete menu item generation (NO.doNotLinkIt = 1).
Just use NO.after.cObject = COA.
leveltitle : 1 is correct if you want to have the title for the current page.
The same if you show a rootline menu: it is generated for the current page.
If you want the levelfield for another page you need to build it by yourself.
In typoscript you might use a userfunction. (there is a core function for getting the rootline for a given page id)
If you generate your menu with FLUID you might use a viewhelper. (You might get inspired from this option of viewhelper menu.directory or this option of VH page.breadCrumb in ext:vhs.)
Edit:
you might store the needed information directly in the pagesrecord.
add a new field to the record (or use any unused).
then make sure that each category page contains some page TS_config:
TCADefaults.pages.<yourfield> = CategoryName
With this configuration each new page below will get this value set automatically.
Of course you need to set these values for all existing pages by hand or by some manual queries.
And if the editors are to be prevented from changing this value you need to remove the field from the edit form with this TSConfig on the top-page:
TCEForm.pages.<yourfield>.hide= 1
I have found a pure TypoScript solution that may not be very elegant, but it works:
lib.myMenu = HMENU
lib.myMenu {
special = updated
special{
value.field = 10,11,12
beginAtLevel = 1
limit = 99
}
1 = TMENU
1{
NO{
doNotLinkIt = 1
stdWrap.cObject = COA
stdWrap.cObject {
10 = TEXT
10{
wrap = <h3>|</h3>
field = seo_title // title
typolink.parameter.field = uid
}
20 = HMENU
20{
wrap = <div class="category">|</div>
special = list
special.value.field = pid
1 = TMENU
1{
NO{
doNotLinkIt = 1
stdWrap.override{
# Overwrite it if we are not yet at the category level
if{
# The ID of the page parent to the categories ("Website") is 1618
equals = 1618
value.field = pid
negate = 1
}
cObject = HMENU
cObject{
special = list
special.value.field = pid
1 = TMENU
1.NO.doNotLinkIt = 1
}
}
}
}
}
}
}
}
}
If the menu is to go over further levels, the override must of course be nested even deeper.
Edit – Improvements:
RECORDS instead of HMENU: Bernd Wilke suggested in the comments to use CONTENT instead of HMENU to generate the category title. This caused problems in my tests when sysfolders (as subpages of the categories) were used to structure the items. This could be related to the bug/feature #20933 (Enable working with SysFolders in CONTENT). But RECORDS might be comparable. I tried it and was able to improve the render times a little bit (see table below).
lib.myMenu.1.NO.stdWrap.cObject.20 = RECORDS
lib.myMenu.1.NO.stdWrap.cObject.20{
stdWrap.wrap = <div class="category">|</div>
source.field = pid
tables = pages
conf.pages = TEXT
conf.pages {
field = seo_title // title
stdWrap.override{
# Overwrite it if we are not yet at the category level
if{
# The ID of the page parent to the categories ("Website") is 1618
equals = 1618
value.field = pid
negate = 1
}
cObject = RECORDS
cObject{
source.field = pid
tables = pages
conf.pages = TEXT
conf.pages.field = seo_title // title
}
}
}
}
CONTENT instead of HMENU: if no sysfolders are needed (as of TYPO3 10.4 sysfolders should also work)
lib.myMenu.1.NO.stdWrap.cObject.20 = CONTENT
lib.myMenu.1.NO.stdWrap.cObject.20{
stdWrap.wrap = <div class="category">|</div>
table = pages
select {
uidInList.field = pid
pidInList = 0
selectFields = uid, pid, seo_title, title
}
renderObj = TEXT
renderObj {
field = seo_title // title
stdWrap.override{
# Overwrite it if we are not yet at the category level
if{
# The ID of the page parent to the categories ("Website") is 1618
equals = 1618
value.field = pid
negate = 1
}
cObject = CONTENT
cObject{
table = pages
select {
uidInList.field = pid
pidInList = 0
selectFields = uid, pid, seo_title, title
}
renderObj = TEXT
renderObj.field = seo_title // title
}
}
}
}
userFunc instead of HMENU: Bernd Wilke suggested in his answer to use a userfunction. Even if this is not a pure TypoScript solution, I would have liked to test it to be able to compare the performance. The method getRootLine() has unfortunately been marked as deprecated. Since I'm not an extension developer, it's not clear to me yet how to read the category via a userFunc and if this is actually more effective. If I still come across a solution regarding this, it will be added here.
Rendertime: I have test-implemented the menu in the live site (in the existing template) and compared the website render times with and without the output of the categories to see how much the nested menu affects the performance. The values are average values from 10 measurements. I also limited the output to 20 items, which should be a more realistic value per page later on:
mode
20 items
100 items
without category
99 ms
218 ms
categories via HMENU
133 ms
353 ms
categories via RECORDS
132 ms
331 ms
categories via CONTENT
121 ms
255 ms
categories via userFunc
TBD
TBD
You should neither use a separate HMENU nor a userFunc but a simple LOAD_REGISTER within the menu items of the parent level. This generates register entries, that can be added, changed or restored while looping through the different levels of the HMENU structure.
So you don't need the custom code of a PHP function and you won't get the performance penalty of a nested HMENU.
Basically it's shown in the official documentation, but with a CSS class instead of a title. But of course you could hand over other information from the parent to its children the same way.
Here is the example:
10 = COA
10 {
### left menu table column
10 = LOAD_REGISTER
10 {
ulClass = col-left
}
### right menu table column
20 = LOAD_REGISTER
20 {
ulClass = col-right
}
30 = HMENU
30 {
special = list
special.value = 1
1 = TMENU
# ...
3 = TMENU
3 {
stdWrap {
preCObject = COA
preCObject {
10 = RESTORE_REGISTER
}
dataWrap = <ul class="{register:ulClass}">|</ul>
}
wrap =
SPC = 1
SPC {
allStdWrap {
replacement {
10 {
search = ---
replace =
}
}
dataWrap = </ul>|<ul class="{register:ulClass}">
}
}
}
}
}
And here the link to the full documentation page:
https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/UsingSetting/Register.html

Assign register value to TYPO3 CASE key

Am making some complex menus and would like to use CASE (or similar) to determine the number of submenus in a given branch in order to determine the style of menu to use.
Code:
5 = HMENU
5 {
entryLevel = -1
1 = TMENU
1 {
expAll = 1
NO = 1
NO {
...
}
IFSUB = 1
IFSUB {
10 = CASE
10 {
key.data = {register:count_menuItems}
1 = COA
1 {
data = {field:title}
data.insertData = 1
}
2 = COA
2 {
data = {field:title}
data.insertData = 1
}
default = COA
default {
data = {field:title}
data.insertData = 1
}
}
wrapItemAndSub = |
}
}
2 = TMENU
2 {
maxItems = 2
expAll = 1
...
}
}
How can I get CASE to work? I've tried it with and without the braces.
you should get more information how to access fields, register and other data in typoscript.
if you have a property you mostly can modify the way to get other information than a constant text.
In your example it is the key property where constants are not meaningful.
if you want to access a field of the 'current' record/data you just use key.field = fieldname
if it is other data you modify it to key.data = register:registername
accessing a field can be done with key.data = field:fieldname
If you want these data connected to other information you could use a wrap:
key.data = register:registername
key.wrap = prefix- | -suffix
Notice: the parts of the wrap are trimmed before they are connected
another way would be an inline notation where you even can use multiple values:
key = {register:registername}-with-{field:fieldname}
key.insertData = 1
here you have two replacements. each has to be wrapped in braces {} and you need to tell TYPO3 that there are replacements to do: insertData = 1
special case TEXTobject:
10 = TEXT
10.value = constant Text
20 = TEXT
20.field = fieldname
30 = TEXT
30.data = register:registername
40 = TEXT
40.value = register is '{register:registername}' and field is '{field:fieldname}'
40.insertData = 1
ADDED:
see the manual of the typoscript data type getText where you can find what else can be used instead of register:
then the manual entry for data which is a property of the function .stdWrap and of type getText.
This entry is followed by the property field stating, it is a shortcut for data = field:
(This explaines why your COA with .data results in anything, as doing a .stdWrap.data on any object will replace the object's content.)
be aware that field (either as property or as key of getText) will select
a field of the current record, which might vary dependent on context:
for page rendering it is the record of the current page (table pages),
for rendering a content element it is the element (table tt_content),
inside a filesProcessor it is a file (table sys_file_reference`),
in the renderObj of CONTENT, RECORDS, or split it is the selction you define.
Found the answer. As far as I can tell, CASE works on stdwrap.cObjects and so the code
10 = CASE
10 {
key.data = {register:count_menuItems}
...
}
should be
stdWrap.cObject = CASE
stdWrap.cObject {
key.data = register:count_menuItems
if.isTrue.data = register:count_menuItems
...
}
This way it works.

TYPO3 - simpler way, rendering content from colPos into Fluidtemplate?

The official TYPO3 Documentation explains how to create (or copy) and use a lib.dynamicContent to render columns into a Fluidtemplate.
I do not understand exactly whats going on in this example.
The TypoScript there is:
lib.dynamicContent = COA
lib.dynamicContent {
10 = LOAD_REGISTER
10.colPos.cObject = TEXT
10.colPos.cObject {
field = colPos
ifEmpty.cObject = TEXT
ifEmpty.cObject {
value.current = 1
ifEmpty = 0
}
}
20 = CONTENT
20 {
table = tt_content
select {
orderBy = sorting
where = colPos={register:colPos}
where.insertData = 1
}
}
90 = RESTORE_REGISTER
}
I use this snippet in a ton of TYPO3 projects and often had asked myself whats going on there.
I have changed this by experimenting a bit and ended with:
lib {
dynamicContent = COA
dynamicContent {
10 = CONTENT
10 {
table = tt_content
select {
orderBy = sorting
where {
data = field:colPos
wrap = colPos=|
}
}
}
}
}
That seems to do "exactly the same" thing - it outputs my content when called via cObject ViewHelper.
Can somebody explain if or why this is the worse way to render Content?
Here's the link to the lib.dynamicContent-doc: https://docs.typo3.org/c/typo3/cms-fluid-styled-content/master/en-us/Installation/InsertingContentPageTemplate/Index.html#based-on-the-fluidtemplate-content-object-cobj
Here you go!
you can try this,
# Clear out any constants in this reserved room!
styles.content >
# get content
styles.content.get = CONTENT
styles.content.get {
table = tt_content
select.orderBy = sorting
select.where = colPos=0
}
# Left Column
styles.content.getLeft < styles.content.get
styles.content.getLeft.select.where = colPos=1
# Right content
styles.content.getRight < styles.content.get
styles.content.getRight.select.where = colPos=2
Also, you can use variable in the fluid page object, check this out:
lib.pageTemplate = FLUIDTEMPLATE
lib.pageTemplate {
variables {
content = CONTENT
content {
table = tt_content
select.orderBy = sorting
select.where = colPos=0
}
contentRight = CONTENT
contentRight {
table = tt_content
slide = -1
select.orderBy = sorting
select.where = colPos=2
}
}
}
You can find out more here:
Adding the page content to a fluid template
Typo3 7.6 typoscript problems with markers
Hope this make sense, Cheer...!
You should look at this snippet together with some information about the Fluid view helper <f:cObject> which can be found here: https://docs.typo3.org/other/typo3/view-helper-reference/9.5/en-us/typo3/fluid/latest/CObject.html
As you can see there are the parameters data, currentValueKey and table that will be handed over to the typoscriptObjectPath, which is why the snippet makes perfect sense. The reason is, that it's a bit hard to put the different options into the where clause of the CONTENT object. So it increases readability and those registers can be easily extended.
So the register in this example is used to put in either the value of the data field colPos or if that is empty it will take the current value from the currentValueKey and if that is empty too it will fall back to a value of 0 to make sure the query won't produce an exception.
lib.dynamicContent = COA
lib.dynamicContent {
10 = LOAD_REGISTER
10.colPos.cObject = TEXT
10.colPos.cObject {
field = colPos
ifEmpty.cObject = TEXT
ifEmpty.cObject {
value.current = 1
ifEmpty = 0
}
}
20 = CONTENT
20 {
table = tt_content
select {
orderBy = sorting
where = colPos={register:colPos}
where.insertData = 1
}
}
90 = RESTORE_REGISTER
}
We used a modified version of that snippet to sneak in some more parameter values for the CONTENT object.
So we can hand over a data field pageUid, if that is not set we will use the uid of the current page. This will be overriden if the current or the target page is configured to show content from another page and finally we can trigger a slide with another data field.
lib.dynamicContent = COA
lib.dynamicContent {
5 = LOAD_REGISTER
5 {
colPos.cObject = TEXT
colPos.cObject {
field = colPos
ifEmpty.cObject = TEXT
ifEmpty.cObject {
value.current = 1
ifEmpty = 0
}
}
pageUid.cObject = TEXT
pageUid.cObject {
field = pageUid
ifEmpty.data = TSFE:id
}
contentFromPid.cObject = TEXT
contentFromPid.cObject {
data = DB:pages:{register:pageUid}:content_from_pid
data.insertData = 1
}
}
20 = CONTENT
20 {
table = tt_content
slide = -1
slide.if.isTrue.field = slide
select {
includeRecordsWithoutDefaultTranslation = 1
orderBy = sorting
where = {#colPos}={register:colPos}
where.insertData = 1
pidInList.data = register:pageUid
pidInList.override.data = register:contentFromPid
}
}
90 = RESTORE_REGISTER
}
This enables us to make use of the <f:cObject> view helper while triggering additional parameters just by handing over some more values within the data array.

TypoScript / Typo3: How to build sub-content of a page using header_link value?

How can I build a sub-content (nested content) of a page using typoscript with reference to the parent's header_link field?
Desired output is something like:
<h1>Title of parent</h1>
<p class="prt">Body of parent</p>
<h3>Title of Child</h3>
<p class="cld">Contents of child</p>
Sample of my Typoscript:
temp.myParentVal = CONTENT
temp.myParentVal {
table = tt_content
select {
begin = 1
orderBy = sorting
where = (colPos = 1)
}
renderObj = COA
renderObj {
10 = TEXT
10 {
required = 1
wrap = <h1> | </h1>
stdWrap.field = header
}
20 = TEXT
20 {
required = 1
wrap = <p class="prt"> | </p>
stdWrap.field = bodytext
}
# #
# WHAT SHOULD I DO HERE TO SHOW THE CHILD CONTENT OF THIS PAGE #
# (REFERENCED BY header_link FIELD IN THE PARENT'S ROW), #
# WHICH HAS THE FORMAT OF <child_pid#child_uid>, EG.'11#28' #
# #
stdWrap.wrap = <div> | </div>
}
I would like to make use of parent's header_link field value to generate the content for the child. (I'm using TYPO3 v. 6.2.14)
You can use a nested CONTENT element and split the header_link via Regex like this:
30 = CONTENT
30 {
table = tt_content
select {
uidInList {
field = header_link
stdWrap.replacement.10 {
search = /^.+#/
replace =
useRegExp = 1
}
}
pidInList {
field = header_link
stdWrap.replacement.10 {
search = /#.+$/
replace =
useRegExp = 1
}
}
}
renderObj = COA
renderObj {
10 = TEXT
10.value {
required = 1
wrap = <h3> | </h3>
field = header
}
20 = TEXT
20.value {
required = 1
wrap = <p class="cld"> | </p>
field = bodytext
}
}
}
Also, I wanted to suggest that you use the field property directly on the TEXT elements (which works). But I looked it up in the docs:
stdWrap properties are available on the very rootlevel of the object. This is non-standard! You should use these stdWrap properties consistently to those of the other cObjects by accessing them through the property "stdWrap".
https://docs.typo3.org/typo3cms/TyposcriptReference/ContentObjects/Text/Index.html
As value is a stdWrap object, you could use it as I did above.

typoscript issue with LOAD_REGISTER and if condition to fill a list

What I'm trying to do is quite complex and an Extbase extension is involved...
Step by step, what I'm trying to do:
An Extbase plugin decides, if certain navigation elements should be marked.
This plugin has one action for each navigation element.
The returned value (0 or 1) from each action in TS is stored on the stack (LOAD_REGISTER).
A list of page UIDs is build by checking against the stored values (0,1).
The navigation COA is modified using this list of page UIDs.
Here is the typoscript code I'm using:
// load information, if pages lack info, into register
10 = LOAD_REGISTER
10 {
lacksAnfahrt {
cObject = USER_INT
cObject {
userFunc = tx_extbase_core_bootstrap->run
pluginName = Pa_klinik_data_edit
extensionName = Hplusinfo
controller = SpitalInfoPA
switchableControllerActions {
SpitalInfoPA {
1 = completeAnfahrt
}
}
}
}
lacksAktivitaeten < .lacksAnfahrt
lacksAktivitaeten.cObject.switchableControllerActions.SpitalInfoPA.1 = completeAktivitaeten
lacksBildergalerie < .lacksAnfahrt
lacksBildergalerie.cObject.switchableControllerActions.SpitalInfoPA.1 = completeBildergalerie
// build a list of PIDs that are going to be marked in navigation
lackPIDs.cObject = COA
lackPIDs.cObject {
10 = TEXT
10 {
value = {$config.PIDLists.anfahrt},
if {
value = 1
equals.data = register:lacksAnfahrt
}
}
20 < .10
20.value = {$config.PIDLists.bildergalerie},
20.if.equals.data = register:lacksBildergalerie
30 < .10
30.value = {$config.PIDLists.aktivitaeten},
30.if.equals.data = register:lacksAktivitaeten
// don't let the comma separated list end with a comma
99 = TEXT
99.value = 0
} // lackPIDs
} // REGISTER
// mark incomplete pages with a red exclamation mark
20 { // = HMENU
1 { // = TMENU
NO { // = 1
stdWrap.wrap = |<span class="warning lacksInfo">!</span>
stdWrap.wrap.if {
value.data = register:lackPIDs
isInList.field = uid
}
}
}
}
If i print out register:lacksBildergalerie and all the others, their values are correct (0 or 1).
But the lackPIDslist always empty (except of the 0 at the end)... There must be something wrong with the middle part:
10 {
value = {$nav.PIDLists.anfahrt},
if {
value = 1
equals.data = register:lacksAnfahrt
}
}
This evaluation seams to return false in any case.
I also tried with different if function like:
10 {
value = {$nav.PIDLists.anfahrt},
if {
isTrue.data = register:lacksAnfahrt
}
}
But this doesn't solve the problem.
Just overlooked that the other registers are using USER_INT as well