質問

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.

役に立ちましたか?

解決

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 can be found at the CheatSheet corner. Specifically, you can take a look at the stdWrap Menu Cheat Sheet (Direct link).

Furthermore, 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.

他のヒント

You can use the t3lib_pageSelect to fetch your pages menu. Just create a new instance of that in your controller action and give the rootnode to build the menu.

/**
 * fetchNavigationTree
 *
 * @param integer $rootPage
 * @param array $pageTree
 * @return string
 */
protected function fetchNavigationTree($rootPage, $pageTree = array()) {
    $this->pageSelect = t3lib_div::makeInstance('t3lib_pageSelect');
    $this->pageSelect->init(false);
    $pageTree = $this->pageSelect->getMenu(
        $rootPage,
        'uid, pid, title, doktype, url, subtitle, description, media, nav_hide, subtitle',
        'sorting',
        'AND nav_hide = 0',
        0
    );
    foreach($pageTree as &$page) {
        $page['subPages'] = $this->fetchNavigationTree($page['uid'], $pageTree);
        if(empty($page['subPages']) || $page['subPages'] == null) {
            unset($page['subPages']);
        }
    }
    return $pageTree;
}

this question is quite old. But i did a simpler solution using your code as reference. Your code had a bug, that if your menu is larger than 4 submenus or if the first item did not have children at all, it produced invalid JSON.

# Menu as JSON

menuJSON = PAGE

# create absolute urls
menuJSON.config.absRefPrefix = https://www.yourdomain.com/


menuJSON {
    typeNum = 2342
    config {
        disableAllHeaderCode = 1
        doctype = none

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

        # disable debug for valid json
        debug = 0

        # disable xhtml cleaning for valid json
        xhtml_cleanin = 0


        # TODO: disable in production
        no_cache = 1
    }

    10 = HMENU
    10 {
        special = directory
        special.value = 1
        special.depth = 4
        special.forceAbsoluteUrl = 1

        1 = TMENU
        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
                    }
                }
            }
            # same as NO but with , after the uri
            IFSUB < .NO
            IFSUB = 1
            IFSUB {
                stdWrap.cObject {
                    20 {
                        wrap = "uri":"|",
                    }
                }
            }
        }

        # second layer is a bit different
        2 < .1
        2 {
            wrap = |
            stdWrap.wrap = "children": [|]
        }

        # All further sub levels are like levels 2
        3 < .2
        4 < .3
        5 < .4
        6 < .5
        7 < .6
        8 < .7

        # last layer has no children
        8.IFSUB = 0
    }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top