目次

Version 20, last updated by Debilski at Nov 28 01:17 UTC

Lift’s SiteMap contains a structured representation of all the pages on your site. Anything not listed in the SiteMap will (normally) not exist as part of the public-facing part of the app. This means that routing and access control happens here.

About: SiteMap Basics

The Lift SiteMap is not just a menu that gives users access to important pages on your site, it is a comprehensive representation of every page of your site and the access rules that govern who can access what and when.The Menu.builder snippet then lets you dynamically render those items accessible in the current context.

If you don’t add a page to your SiteMap it will not be displayed by Lift on your web site.

A SiteMap is made up of Menu objects which are hierarchically structured: Each Menu is made from a Loc and optional kid Menus. A Loc is a location in a site and comprises of a unique name, a list of Strings which form a link, a title and a set of parameters of type LocParam.

The easy way

Here’s an easy DSL for creating a SiteMap:

def siteMap() = SiteMap(
  Menu(S ? "Home") / "index",
  Menu(S ? "About") / "about" / "index" submenus (
    Menu(S ? "Management") / "about" / "management",
    Menu(S ? "Goals") / "about" / "goals"),
  Menu("directions", S ? "Directions") / "directions" >> Hidden,
  Menu(S ? "Admin") / "admin" / "index" >> If(loggedIn_?, "You must be logged in"))

S ? “Home” defines the localized link text. Each time the menu link text is to be generated, the expression is evaluated and the localized String for “Home” is returned.

Menu items marked Hidden will not be displayed in the menu hierarchy, but the page can be accessed. You can create links to those pages with:

<lift:menu.item name=“directions”/>

The Admin page will only be rendered if the loggedIn_? function returns true.

Example

Here is an example of SiteMap generation code from the bootstrap.liftweb.Boot.boot method of a simple Lift app:

...
import net.liftweb.http.LiftRules
import net.liftweb.sitemap.Loc
import net.liftweb.sitemap.Loc.Hidden
import net.liftweb.sitemap.Loc.strLstToLink
import net.liftweb.sitemap.Menu
import net.liftweb.sitemap.SiteMap
...
def boot {
...
  val myLoc = Loc("HomePage", "index" :: Nil, "Home Page", Hidden)
  val myMenu = Menu(myLoc)
  val allMenus = myMenu :: User.sitemap
  val mySiteMap = SiteMap(allMenus: _*)
  LiftRules.setSiteMap(mySiteMap)
...

Now comes an explanation…

Loc

Here’s a simple example Loc using Scala 2.8 named arguments to make it absolutely clear what is being set here:

val myLoc = Loc(theName: "HomePage", theLink: new Link(List("index")), theText: "Home Page")

Please notice that the text is a Scala By-Name-Parameter that will be evaluated on every “usage”. That means you could also pass a function returning a string instead of a static string, e.g. in order to i18n your app.

In much of the example code you will see that the Link parameter is given as a List[String], because there is an implicit conversion between a list of Strings and a Link: Loc.strLstToLink(…). So the Loc above would typically be defined with this code:

val myLoc = Loc("HomePage", "index" :: Nil, "Home Page")

… which is exactly the same as the above.

From Requests to Templates

In the Loc defined above we have a Link that refers to “index”. How do we actually get to XHTML that will get returned to the browser? By default Lift looks for an XHTML template file called index.html (or .htm or .xhtml or …) in the root of your app (src/main/webapp at development time if using Maven). It will then process the template, a large enough subject in its own right that it has its own wiki page.

You may organize your templates into subdirectories: new Link(“about” :: “lift” :: Nil) will use a template at src/main/webapp/about/lift.html.

LocParam

The most complex part of a Loc declaration is the final optional LocParam parameters.

LocParam is a Trait extended by Hidden, HideIfNoKids, If, PlaceHolder, and Test amongst others.

You can add LocParam parameters to a Loc to control access to that location on your site and to control the way that the location is displayed in a site map on your web site.

Hidden

For example, if we add Hidden to the Loc definition above, the location is still accessible to users of your site but will not be displayed in any site map.

val myLoc = Loc("HomePage", "index" :: Nil, "Home Page", Hidden)

Test

If you want to only display the page if the page request comes from an iPhone, you can change the code to:

import net.liftweb.sitemap.Loc.Test
val ifIPhone = Test( (r) => r.isIPhone )
val myLoc = Loc("HomePage", "index" :: Nil, "Home Page", ifIPhone)

Of course, this isn’t very helpful in practice as it will display a page not found error if you access this page from any other web browser.

EarlyResponse

If you want code to run before a page is loaded, and afterward either continue rendering the page as normal or return some other HTTP response, use EarlyResponse. It takes a nullary function that returns a Box[LiftResponse]. Return Empty to have Lift proceed as normal, or a Full Box containing a LiftResponse to override the default behavior and return your own response. For a complete list of response types available see LiftResponse.scala. They include JsonResponse for JSON responses, InMemoryResponse to return binary data, and RedirectResponse to trigger a redirect.

Once you have created your Loc objects, you can construct Menu objects either by simply wrapping a Loc in a Menu like this:

val myMenu = Menu(myLoc)

or by assembling menus together in a hierarchy like this:

val myMenu = Menu(myHelpMenuLoc, myUserHelpMenu, myTechnicalHelpMenu)

Here the first argument is a Loc followed by zero or more Menu arguments representing sub-menus of the menu you are creating.

You assemble your top level menus in a list, often by prepending your menu or menus to the default menus for Lift’s user management which are accessed by the User.menus method (defined in trait net.liftweb.mapper.MetaMegaProtoUser).

val allMenus = myMenu :: User.menus

SiteMap

Once you have all your menus you create a SiteMap from your menus:

val mySiteMap = SiteMap(allMenus: _*)

If you are unfamiliar with Scala you may wonder what : _* means. It is a sequence argument marker according to section 6.6 of the Scala Language Specification. SiteMap takes a variable number of arguments all of which should be Menu objects. But allMenus is a List[Menu]. The : _* syntax tells Scala to take a List and pass it to a method as separate arguments.

Finally, you need to tell lift to use your SiteMap. You do this with the command:

LiftRules.setSiteMap(mySiteMap)

Using SiteMap in Templates

To display the menus of your SiteMap in a template all you have to do is add the following tag <lift:Menu.builder /> and it will display your menus as an unordered list by default.

If the xhtml that Lift produces isn’t what you were looking for you can customize the menu by using the following prefixes:

  • ul: Attributes of menu elements.
  • li: Attributes of menu item elements.
  • li_item: Attributes of the currently selected menu
  • top:id: The id attribute of the top-most menu element.
  • outer_tag: The tag of menu elements. By default this is <ul>, though you can chose any tag you want.
  • inner_tag: The tag of menu item elements. By default this is <li>.
  • level: What level of the entire selected menu tree to show. Counting begins at zero, for the top level. Consider a top level menu with ‘item1’, ‘item2’, and ‘item3’ and supposed ‘item1’ has a submenu with items ‘item1a’ and ‘item1b’. Level 0 will show the top level plus the submenus (see next option). If item2 or item3 is selected level 1 will show nothing, while if one of the item1 variants is selected the item1a and item1b entries will be displayed.
  • expand: Whether to expand submenus of the currently selected menu item. The default is true, the other option is false.
  • (for more see the ScalaDoc)

An example would be something like this

<lift:Menu.builder li:class="menu_item" li_item:class="selected" ul:class="menu" />

This would create the following xhtml elements

<ul class="menu">
  <li class="menu_item">menu1</li>
  <li class="menu_item">menu2</li>
  <li class="menu_item selected">menu3</li>
</ul>

A more complicated use might look like this:

<lift:Menu.builder top:id="topMenu" outer_tag="div" inner_tag="div" ul:class="menuSection" li:class="menuItem"
  li_item:id="selectedMenu" li_item:class="selectedItem" li_path:class="parentMenu" />

When you have a top-level item selected:

<div id="topMenu" class="menuSection">
  <div id="selectedMenu" class="selectedItem">
    <span>item1</span>
    <div class="menuSection">
      <div class="menuItem">
        <a href="">item1a</a>
      </div>
    </div>
  </div>
  <div class="menuItem">
    <a href="">item2</a>
  </div>
  <div class="menuItem">
    <a href="">item3</a>
  </div>
</div>

An item from a submenu selected:

<div id="topMenu" class="menuSection">
  <div class="menuItem">
    <a href="...">item1</a>
  </div>
  <div class="parentMenu">
    <span>item2</span>
    <div class="menuSection">
      <div class="menuItem">
        <a href="">item2a</a>
      </div>
      <div id="selectedMenu" class="selectedItem">
        <span>item2b</span>
    </div>
  </div>
  <div class="menuItem"><a href="...">item3</a></div>
</div>

There are also <lift:Menu.item /> and <lift:Menu.group /> tags you can you to display parts of the SiteMap.

Also if you want to access the title of the current page you can use the following tag <lift:Menu.title />. The menu title is the name parameter of the Loc for the menu (i.e. the first parameter).