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

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

Related

TYPO3 MenuProcessor shows root page when empty?

I have a basic menu element using the Pages field and the MenuProcessor.
In the fluid template I only want to output something only if there are pages in the Page field. But if the field is empty the MenuProcessor adds the root page to the array.
How do I prevent the root page being added to what should be an empty array?
typoscript looks like this:
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
10 {
special = list
special.value.field = pages
levels = 1
as = menuItems
expandAll = 1
includeNotInMenu = 1
titleField = nav_title // title
}
}
I suppose it is a very special edge case (which could be handled in the menu processor, you might open a ticket on https://forge.typo3.org ).
As you have identified the reason with empty input parameters you might build a condition on that case.
Either in FLUID or in typoscript.
In typoscript you could add a stdWrap function:
10 {
:
if.isTrue.field = pages
}
Did you try the entryLevel?
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
10 {
entryLevel = 0
}
}

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.

typoscript CONTENT table sys_language_uid=-1 no result

I want to get my "cal" categories.
If i use the code below, i do not get any result, because the sys_language_uid of the categories is -1 (All languages).
If i change the sys_language_uid's to 0 (default language), i get my categories.
Where is the background for this?
lib.eventcategories = CONTENT
lib.eventcategories {
table = tx_cal_category
select {
selectFields = uid
pidInList = 47
}
renderObj = COA
renderObj {
10 = TEXT
10.field = uid
10.noTrimWrap = |test: | - |
}
}
Look at the TS reference. Here you see that you can disable the language handling. With the following setting you can also activate the handling for "not default" language.

How link to URL of single news article via CONTENT object

I am using the typoscript below to display news content via a CONTENT object. Everything is working great except the link doesn't go to the page of the article. Is there a way to tell the typolink that this is a news article and that it should use the cached CoolURI link for it?
Also, the additionalParameter I'm trying to append to the querystring isn't appearing.
temp.MMtest = COA
temp.MMtest {
10 = CONTENT
10.table = tt_news
#10.select.pidInList = 170 # Uid of the sysfolder where News records are stored
10.select.pidInList = 18
10.select.recursive = 10
#10.select.where = uid=10 # Uid of an existing News record
10.select.andWhere = deleted=0 AND hidden=0
10.renderObj = COA
10.renderObj {
10 = TEXT
10.field = title
10.wrap = Title: |<br>
10.typolink.parameter.field=uid
typolink.parameter.dataWrap=|
#typolink.additionalParams.insertData=1
typolink.additionalParams.data=&my_extra_param=something
#if.isTrue.field=header
}
10.renderObj.20=IMAGE
10.renderObj.20{
wrap=|
# show it only if inserted
stdWrap.if.isTrue.field=image
stdWrap.typolink.parameter.field=uid
file.import=uploads/pics/
file.import.field=image
file.width=100
file.height=100
}
}
I do not know, which parameters you need, so in short:
10.typolink {
# you need a page to link too
parameter = PAGE_ID_OF_SINGLE_VIEW
# create an cacheable link, that does not depend on cooluri or realurl.
useCacheHash = 1
# add the additional params
additionalParams.wrap = &tx_ttnews[uid]=|
# data expects special commands
# "&my_extra_param=something" cannot work on .data
additionalParams.data = field:uid
}
If you need more then one additionalParams i would do it this way:
10.typolink {
parameter = PAGE_ID_OF_SINGLE_VIEW
useCacheHash = 1
# Create an Content Object Array
# so you can separat the different entries
# the cObject will return &tx_ttnews[uid]=123&what[ever]=hardcodedvalue
# additionalParams is filled with that string and added to the url
additionalParams.cObject = COA
additionalParams.cObject {
10 = TEXT
10.wrap = &tx_ttnews[uid]=|
10.field = uid
20 = TEXT
20.wrap = &what[ever]=|
20.value = hardcodedvalue
}
}
You do not need
10.select.andWhere = deleted=0 AND hidden=0
this is added automatically by the CONTENT Object.
Read more about typolink about COA and about stdWrap and its property data and finally have a look at getText.

typo3, typoscript, TMENU/HMEN: Special formatting for item after current item

It there any way to apply special formatting for the menu item immediately after the current item,
that is CUR + 1. Something like:
lib.menu = HMENU
lib.menu {
1 = TMENU {
NO = 1
NO = {
...
}
...
# The currently selected item
CUR = 1
CUR {
allWrap = ...
}
# The next item
CUR + 1 = 1
CUR + 1 {
allWrap = ...
}
}
}
I would appreciate any feedback you might have. Also alternatives: Can write a PHP class/function instead of writing this in typoscript.
Unfortunately this is not possible with just TypoScript. I don't really see a good solution with PHP really either. You could however do it with PHP, but you will need to load the same menu items like in your menu itself inside PHP. Which makes it very inefficient and not flexible at all.
I would say add a special class attribute to the link or wrap and perform your formatting with JavaScript. Where the item coming after the active one is modified.
So I manage to solve this myself. The answers is to use a so-called register to mark if we've passed the current object or not. For example:
lib.menu = HMENU
lib.menu {
1 = TMENU {
NO = 1
NO = {
# Render using Common Object Array (COA)
stdWrap.cObject = COA
stdWrap.cObject {
# Normal Case (However the an item should normally be rendered
10 = TEXT
10 {
if {
isTrue.data = register:cid
value = NORMAL RENDERING
}
}
20 = TEXT
20 {
if {
isTrue.data = register:data
value = RENDERING (IMMEDIATELY) AFTER THE CURRENT ITEM
}
}
# Unset the register (after we've done with our special formatting)
30 = LOAD_REGISTER
30.cid= 0
}
}
...
# The currently selected item
CUR = 1
CUR {
# Render using Common Object Array (COA)
stdWrap.cObject = COA
stdWrap.cObject {
# However the Current item should normally be rendered
10 = TEXT
10.field = title
# Mark that we've reached the current item
20 = LOAD_REGISTER
20.cid= 1
}
}
}
}
A register, set using LOAD_REGISTER, is basically a type of run-time variable, which can be set and reset in the course of iterating through the menu items (or whatever). As such it can be used to note our progress through the menu items, particularly noting if we've passed the current menu item (CUR) or not.
rant begin Hardly an elegant solution considering that typoscript is domain-specific language which is mainly used for this normatting stuff like menus./rant end