Kai Mattern Kai Mattern - 26 days ago 15
JSON Question

How to create a JSON menu via Typoscript in Typo3 (4.5.x)

I am trying to create a JSON sitemap for a Typo3 website, which I will then consume in some other page.

I have achieved to get something vaguely resembling JSON, but am stuck at some things:


  1. I would prefer if all items will display the "children" array, even if it is empty. Although typo should always include it, it does not. This makes creating valid JSON extremely difficult.

  2. The "children" array is always displayed after the corresponding first level entry, not as part of it. If you uncomment the NO.allWrap, the wrap will close before "children", not after it. Which makes creating valid JSON a mess.



So the question is: Is there any better way doing this? Does anyone have an extension that will create such a JSON response? Can someone figure out, how to move the sub entries into cObject, so that I can use the wrap functions to also display the missing "},"?

TypoScript Template:

config.disableAllHeaderCode = 1
config.doctype = none

# set json header
config.additionalHeaders = Content-type:application/json

# keep typo3 from "tidying up" -> perfectly valid json
config.xhtml_cleaning = 0

# Delete and reset the page object
page = PAGE

# Page context
page.10 = HMENU
page.10 {
special = directory
// Page id of help
special.value = 33

1 = TMENU
1.expAll = 1
# wraps all entries (outer array)
1.wrap = [|]
1 {
noBlur = 1
# wraps only entires of 1 level, not the children entry
#NO.allWrap = {|},
NO.linkWrap = |
NO.ATagBeforeWrap = 0
NO.doNotLinkIt = 1
NO.stdWrap.htmlSpecialChars = 0

NO.stdWrap.cObject = COA
NO.stdWrap.cObject {
# gamekey/translation key taken from the abstract
5 = TEXT
5.value = {
10 = TEXT
10 {
field = abstract
htmlSpecialChars = 1
wrap = "key":"|",
}
# Link
20 = TEXT
20 {
field = uid
htmlSpecialChars = 1
wrap = "link":"/|"
typolink.parameter.field = uid
typolink.returnLast = url
}
}
}


2 < .1
2.wrap = ,"children": [|]}

}


Output:

[
{"key":"page_a","link":"/de/a.html"
{"key":"page_b","link":"/de/b.html","children":
[
{"key":"page_c","link":"/de/c.html"
{"key":"page_d","link":"/de/d.html"
{"key":"page_e","link":"/de/e.html"
]}
{"key":"page_f","link":"/de/f.html","children":
[
{"key":"page_g","link":"/de/content/g.html"
]
}
]


Solution (valid json):

lib.header >

config.disableAllHeaderCode = 1
config.doctype = none

# set json header
config.additionalHeaders = Content-type:application/json

# keep typo3 from "tidying up" -> perfectly valid json
config.xhtml_cleaning = 0

# Delete and reset the page object
page = PAGE

# Page context
page.10 = HMENU
page.10 {
special = directory
// Page id of help
special.value = 576
}

page.10.1 = TMENU
page.10.1 {
expAll = 1
noBlur = 1
wrap = [|]

NO {
wrapItemAndSub = {|}, |*| {|}, |*| {|}
linkWrap = |
ATagBeforeWrap = 0
doNotLinkIt = 1
stdWrap.htmlSpecialChars = 0

stdWrap.cObject = COA
stdWrap.cObject {
# gamekey/translation key taken from the abstract
10 = TEXT
10 {
field = title
htmlSpecialChars = 1
wrap = "title":"|",
}

# Link
20 = TEXT
20 {
field = uid
htmlSpecialChars = 1
wrap = "uri":"/|",
typolink.parameter.field = uid
typolink.returnLast = url
}
}
}
}

# second layer is a bit different
page.10.2 = TMENU
page.10.2 {
expAll = 1
noBlur = 1
wrap = |
stdWrap.wrap = "children": [|]

NO {
wrapItemAndSub = {|}, |*| {|}, |*| {|}
linkWrap = |
ATagBeforeWrap = 0
doNotLinkIt = 1
stdWrap.htmlSpecialChars = 0

stdWrap.cObject = COA
stdWrap.cObject {
# gamekey/translation key taken from the abstract
10 = TEXT
10 {
field = title
htmlSpecialChars = 1
wrap = "title":"|",
}

# Link
20 = TEXT
20 {
field = uid
htmlSpecialChars = 1
wrap = "uri":"/|"
typolink.parameter.field = uid
typolink.returnLast = url
}
}
}
IFSUB = 1
IFSUB {
wrapItemAndSub = {|}, |*| {|}, |*| {|}
linkWrap = |
ATagBeforeWrap = 0
doNotLinkIt = 1
stdWrap.htmlSpecialChars = 0

stdWrap.cObject = COA
stdWrap.cObject {
# gamekey/translation key taken from the abstract
10 = TEXT
10 {
field = title
htmlSpecialChars = 1
wrap = "title":"|",
}

# Link
20 = TEXT
20 {
field = uid
htmlSpecialChars = 1
wrap = "uri":"/|",
typolink.parameter.field = uid
typolink.returnLast = url
}
}
}
}

# All further sub levels are like levels 2
page.10.3 < page.10.2
page.10.4 < page.10.3


Please note that you would have to include another IFSUB if you can have nodes on level 1 without children. In my case, this is not possible.

Answer

For menu creating, take a look at wrapItemAndSub which should help you to make the parent element wrap around the children. A good overview over the available wraps is here (CheatSheet corner): http://docs.typo3.org/~mbless/git.typo3.org/Documentation/DocsTypo3Org.git/Resources/Public/CheatSheets/stdwrap-menu.png

You also take a look at the different menu states, especially IFSUB to detect whether there are any children at all.

You have to apply a bit of reversed logic here. More specific states match first, otherwise you will fallback to NO. Therefore if you want to react on a no-children condition, then you have to put your normal settings to IFSUB and use NO for the no-children case.

Comments