Trying to read all nodes from XML in Velocity Script and see node name

webadmin's Avatar

webadmin

19 Mar, 2015 05:36 PM

I'm attempting to write a menu in Velocity Script, but I'm having trouble figuring out how to do one thing in particular. I'm trying to have the script generate a menu based off an index of a folder. When it's just pages, I can do that without much trouble. The problem is, some of these folders will have sub folders. What I want to do is read every top level node in the XML, and then look at that node to see if it is a page or a folder. If it's a page, I want to display it. If it's a folder, I want to start a sub-loop within it to make an indented set of menu items. But I can't figure out how to cycle through both pages and folders in a single loop. I could do a loop on all the pages, then on the folders, but that would destroy any ordering that the files may be in. Is there a way to do this using Velocity Script?

  1. 1 Posted by webadmin on 19 Mar, 2015 05:37 PM

    webadmin's Avatar

    Sorry... just noticed the typo in the header. That should be "read", not "real".

  2. 2 Posted by Ryan Griffith on 19 Mar, 2015 07:50 PM

    Ryan Griffith's Avatar

    Hi,

    We actually have some sample code posted on Github that I believe does exactly what you described.

    One difference from your description; however, is this uses recursion as opposed to nested loops, which is much more flexible in terms of being able to handle an arbitrary depth of sub-folders.

    Please let me know if you have any questions.

    Thanks!

  3. 3 Posted by webadmin on 19 Mar, 2015 09:31 PM

    webadmin's Avatar

    That looks like it should do what I need it to do. Thanks!

  4. 4 Posted by Ryan Griffith on 20 Mar, 2015 12:21 PM

    Ryan Griffith's Avatar

    Not a problem at all, thank you for following up. I am glad to hear the sample Format will do the trick.

    I'm going to go ahead and close this discussion, please feel free to comment or reply to re-open if you have any additional questions.

    Have a great day!

  5. Ryan Griffith closed this discussion on 20 Mar, 2015 12:21 PM.

  6. webadmin re-opened this discussion on 20 Mar, 2015 01:07 PM

  7. 5 Posted by webadmin on 20 Mar, 2015 01:07 PM

    webadmin's Avatar

    I do have one more question. Is there a way for Velocity script to read what page you're currently on? I was wanting to make the menu highlight the current page.

  8. 6 Posted by Ryan Griffith on 20 Mar, 2015 01:32 PM

    Ryan Griffith's Avatar

    Hi,

    What you can do is check to see if the current attribute is present for the page element within the macro by using something like the following:

    #set ($currentClassName = "")
    #if ($page.getAttribute("current"))
        #set ($currentClassName = " active")
    #end
    
    ...
    
    <a href="${pageLoc}" class="${currentClassName}">$_EscapeTool.xml($thePageTitle.value)</a>
    

    Please let me know if you have any questions.

    Thanks!

  9. 7 Posted by webadmin on 20 Mar, 2015 01:38 PM

    webadmin's Avatar

    Excellent. I'll give that a shot. Thanks!

  10. 8 Posted by webadmin on 20 Mar, 2015 02:48 PM

    webadmin's Avatar

    That worked flawlessly. Thanks!

  11. 9 Posted by Ryan Griffith on 20 Mar, 2015 06:00 PM

    Ryan Griffith's Avatar

    Thank you for following up. I am glad to hear my code snippet did the trick.

    I'm going to go ahead and close this discussion, please feel free to comment or reply to re-open if you have any additional questions.

    Have a great day!

  12. Ryan Griffith closed this discussion on 20 Mar, 2015 06:00 PM.

  13. webadmin re-opened this discussion on 25 Mar, 2015 02:03 PM

  14. 10 Posted by webadmin on 25 Mar, 2015 02:03 PM

    webadmin's Avatar

    Sorry to re-open this again, but I do have one more question. The code works great, and I added in a piece of code to loop through each subfolder to see if the current page is inside it. I did this so that I could apply some styling to that folder heading. I'm wanting to do something similar on a larger scale, and I wanted to know if there was a better way to do it.

    Basically, what I'm trying to do is create an index of the entire site, instead of just one folder. That way, I can put this index into the template, and have the same index be on every page in the site. What I'd like to do then is loop over every folder, figure out if file is in that main folder, and if so, use that folder to create the menu. I'm just wondering if there is a better way to do it than to loop over everything first. Or, if there's an easier way to make sure each page has the correct index on it without the users having to add the index every time they make a new page, that would work too. Thanks.

  15. 11 Posted by Ryan Griffith on 25 Mar, 2015 02:23 PM

    Ryan Griffith's Avatar

    Hi,

    If you need to locate the top level folder that contains the calling page, you could go with the following XPath, which would return the first system-folder that contains a calling page regardless of level.

    #set ($rootFolder = $_XPathTool.selectSingleNode($contentRoot, "system-folder[descendant::system-page[@current]]"))
    

    You would then continue to repeat that same XPath as you traverse down the folder hierarchy.

    Please let me know if you have any questions.

    Thanks!

  16. 12 Posted by webadmin on 25 Mar, 2015 02:37 PM

    webadmin's Avatar

    So, if I'm understanding you correctly, $rootFolder would contain the folder and all the pages beneath it that has the current page inside it?

  17. 13 Posted by Ryan Griffith on 25 Mar, 2015 02:42 PM

    Ryan Griffith's Avatar

    That is correct. It will contain all assets within the top-level folder which contains the calling-page. From there, you would continue to traverse down into the next folder containing the calling page.

    Please let me know if you have any questions.

    Thanks!

  18. 14 Posted by webadmin on 25 Mar, 2015 03:23 PM

    webadmin's Avatar

    That's almost what I need. Is there a way to make sure it only returns the top level folders under Base Folder?

  19. 15 Posted by Ryan Griffith on 25 Mar, 2015 04:03 PM

    Ryan Griffith's Avatar

    Sure can. Are you using the sample sitemap Velocity Format? If so, you would simply adjust this line to the following:

    #set ( $items = $_XPathTool.selectNodes($sib, "system-page | system-folder[descendant::system-page[@current]]") )
    

    Please let me know if you have any questions.

    Thanks!

  20. 16 Posted by webadmin on 25 Mar, 2015 04:31 PM

    webadmin's Avatar

    I meant more the other way around... I want to make sure that the first search that you gave me, this code:

    '#set ($rootFolder = $_XPathTool.selectSingleNode($contentRoot, "system-folder[descendant::system-page[@current]]"))'

    returns only top level folders.

  21. 17 Posted by Ryan Griffith on 25 Mar, 2015 05:43 PM

    Ryan Griffith's Avatar

    Assuming $contentRoot looks something like the following it will, because the XPath is only looking at the root level <system-folder> elements:

    <system-index-block>
        <system-folder>
            <name>level1</name>
            <system-folder>
                <name>level2</name>
                <system-folder current="true">
                    <name>level3</name>
                    <system-page current="true">
                        <name>index</name>
                    </system-page>
                </system-folder>
            </system-folder>
        </system-folder>
    </system-index-block>
    

    The example above would give you the level1 folder element.

    Now, if you were to use //system-folder[descendant::system-page[@current]], it would return all of the folders in the example (ie level1, level2, level3) because // will match all folders at any level that contains a descendant page with the current attribute.

    Please let me know if you have any questions.

    Thanks!

  22. 18 Posted by webadmin on 25 Mar, 2015 05:51 PM

    webadmin's Avatar

    Right now, using the code that you gave earlier, it's only giving me one level. For example, if I were to use the XML you list above, and I was on the page labeled "index", it would only show me the menu for items in the folder labeled "level3". If there were a page in "level2", called "index2" for example, it would show "level2", the index page in it, and then a header for "level3" and the index page in that. That's how I want it to look in all cases. Attached is the code I'm currently using.

  23. 19 Posted by webadmin on 30 Mar, 2015 08:00 PM

    webadmin's Avatar

    Is there anywhere that lists all the velocity script codes available? Every time I try to search for velocity script syntax, I can't find anything useful. For example, I'd love to figure out what the parent node of the current node is, but I can't find anything that will find it. When I try to use "getParent()", it just returns the same node I'm currently on. Is there anywhere I can find full documentation on velocity script?

  24. 20 Posted by webadmin on 30 Mar, 2015 09:08 PM

    webadmin's Avatar

    After further testing, "getParent()" is getting a parent of the current folder, but it's getting the root folder every time, instead of simply one level up. How do I get a single level up from my current level?

  25. 21 Posted by Ryan Griffith on 31 Mar, 2015 01:35 PM

    Ryan Griffith's Avatar

    Hi,

    For example, if I were to use the XML you list above, and I was on the page labeled "index", it would only show me the menu for items in the folder labeled "level3". If there were a page in "level2", called "index2" for example, it would show "level2", the index page in it, and then a header for "level3" and the index page in that

    When you have a moment, please attach sample XML for this use case so I can do some local testing with the Format you provided.

    After further testing, "getParent()" is getting a parent of the current folder, but it's getting the root folder every time, instead of simply one level up. How do I get a single level up from my current level?

    The getParent() method should definitely work. Can you provide the code you are using and sample XML?

    Is there anywhere that lists all the velocity script codes available?

    Aside from the Velocity Format page in our Knowledge base, you could use the Property Tool's outputProperties method to see what type of object you are working with along with available methods and properties.

    Also, keep in mind that most objects in Velocity Formats are actually Java classes, so you can use standard Java documentation as a reference as well. For example, you can use the JDOM Element documentation as a reference when working with the $contentRoot variable or Elements returned by the XPath Tool.

    Please let me know if you have any questions.

    Thanks!

  26. 22 Posted by webadmin on 31 Mar, 2015 02:33 PM

    webadmin's Avatar

    Here's the code and the XML of the site. The code is "side menu.xml", and the site is "full site.xml".

  27. 23 Posted by Ryan Griffith on 31 Mar, 2015 05:53 PM

    Ryan Griffith's Avatar

    Thank you for providing your Format and sample XML.

    Also, when you have a moment please provide the resulting HTML you would expect to see given the sample XML you provided so I have a comparison to work with.

    Thanks!

  28. 24 Posted by webadmin on 31 Mar, 2015 06:05 PM

    webadmin's Avatar

    What it's supposed to do is, if the parent node's name is NOT "system-index-block", it's supposed to set the $rootFolder variable to the parent folder. The idea is that, if you're in the sub menu, the name of the parent folder SHOULD be "system-folder". If it's one of the main folders, like test menu, the parent would be "system-index-block", so it's fine to keep it as is.

    In essence, even if you're clicking on a sub-page, it should appear as in the "correct.jpg" attached. Currently, it looks like that when you're click on one of the test pages, but not when you click on a sub page.

    If it would help, I could set you up an account to view my code inside of Cascade.

  29. 25 Posted by webadmin on 02 Apr, 2015 06:35 PM

    webadmin's Avatar

    I think I found the problem. I was using the wrong XML on the sub menu. I changed a higher level template, and didn't realize that the XML in the sub menu didn't change.

  30. 26 Posted by Ryan Griffith on 02 Apr, 2015 08:08 PM

    Ryan Griffith's Avatar

    Thank you for following up, I am glad to hear you were able to narrow down the issue. To confirm, are you able to generate the navigation you are looking for?

    Thanks!

  31. 27 Posted by webadmin on 02 Apr, 2015 08:39 PM

    webadmin's Avatar

    Yes, I am. I do have one related question, though. I hate to keep asking for syntax, but I'm having a lot of trouble finding syntax that I'm looking for. It's annoying, because I know exactly what the line of code SHOULD be, but I can't find what it actually is. :) Basically, what's the syntax for selecting the node of the current page? I've tried every variant I could think of, but I'm getting nothing back. The only way I was able to do it is to select all pages and loop until I found the one that was marked current.

  32. 28 Posted by Ryan Griffith on 02 Apr, 2015 08:53 PM

    Ryan Griffith's Avatar

    Thank you for following up, I am glad to hear you were able to work things out with the navigation.

    Basically, what's the syntax for selecting the node of the current page? I've tried every variant I could think of, but I'm getting nothing back

    If you are using an Index Block, the following will return the <system-page> element which has @current attribute:

    #set ( $currentPage = $_XPathTool.selectSingleNode($contentRoot, "//system-page[@current]") )
    

    Note the XPath I am using. Using //system-page means "give me all <system-page>elements at all levels in the XML, and[@current]` is filtering out only the element with that attribute present.

    If you are running Cascade 7.10.1 and using the Cascade API (aka Locator Tool), you can simply use $currentPage to access a page object containing information about the calling page.

    Please let me know if you have any questions.

    Thanks!

  33. Ryan Griffith closed this discussion on 20 Apr, 2015 02:27 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac