Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

3729 lines (2636 sloc) 65.904 kB
#LyX 1.6.5 created this file. For more info see http://www.lyx.org/
\lyxformat 345
\begin_document
\begin_header
\textclass book
\use_default_options false
\language english
\inputencoding auto
\font_roman default
\font_sans default
\font_typewriter default
\font_default_family default
\font_sc false
\font_osf false
\font_sf_scale 100
\font_tt_scale 100
\graphics default
\paperfontsize default
\spacing single
\use_hyperref false
\papersize default
\use_geometry false
\use_amsmath 1
\use_esint 1
\cite_engine basic
\use_bibtopic false
\paperorientation portrait
\secnumdepth 3
\tocdepth 3
\paragraph_separation indent
\defskip medskip
\quotes_language english
\papercolumns 1
\papersides 1
\paperpagestyle default
\tracking_changes false
\output_changes false
\author ""
\author ""
\end_header
\begin_body
\begin_layout Chapter
SiteMap
\begin_inset CommandInset label
LatexCommand label
name "cha:SiteMap"
\end_inset
\end_layout
\begin_layout Standard
SiteMap is a very powerful part of Lift that does essentially what it says:
provides a map (menu) for your site.
Of course, if all it did was generate a set of links on your page, we wouldn't
have a whole chapter dedicated to it.
SiteMap not only handles the basic menu generation functionality, but also
provides:
\end_layout
\begin_layout Itemize
Access control mechanisms that deal not only with whether a menu item is
visible, but also whether the page it points to is accessible
\end_layout
\begin_layout Itemize
Grouping of menu items so that you can easily display portions of menus
where you want them
\end_layout
\begin_layout Itemize
Nested menus so you can have hierarchies
\end_layout
\begin_layout Itemize
Request rewriting (similar to Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sec:URL-Rewriting"
\end_inset
)
\end_layout
\begin_layout Itemize
State-dependent computations for such things as page titles, page-specific
snippets, etc.
\end_layout
\begin_layout Standard
The beauty of SiteMap is that it's very easy to start out with the basic
functionality and then expand on it as you grow.
\end_layout
\begin_layout Section
Basic SiteMap Definition
\begin_inset CommandInset label
LatexCommand label
name "sec:Basic-SiteMap-Definition"
\end_inset
\end_layout
\begin_layout Standard
Let's start with our basic menu for PocketChange
\begin_inset Note Note
status open
\begin_layout Plain Layout
Revise this as needed to match reality, and verify all code in this chapter
\end_layout
\end_inset
.
To keep things simple, we'll just define four menu items to begin:
\end_layout
\begin_layout Enumerate
A home page that displays the user's entries when the user is logged in,
or a welcome page when the user is not
\end_layout
\begin_layout Enumerate
A logout link when the user is logged in, log in and registration links
and pages when the user is not
\end_layout
\begin_layout Enumerate
Pages to view or edit the user's profile, available only when the user is
logged in
\end_layout
\begin_layout Enumerate
A help page, available whether the user is logged in or not
\end_layout
\begin_layout Standard
We'll assume that we have the corresponding pages, "
\family typewriter
homepage
\family default
", "
\family typewriter
login
\family default
", "
\family typewriter
logout
\family default
", and "
\family typewriter
profile
\family default
," written and functional.
We'll also assume that the help page(s) reside under the "
\family typewriter
help
\family default
" subdirectory to keep things neat, and that the entry to help is
\family typewriter
/help/index
\family default
.
\end_layout
\begin_layout Subsection
The Link Class
\begin_inset CommandInset label
LatexCommand label
name "sub:The-Link-Class"
\end_inset
\end_layout
\begin_layout Standard
The Link class
\begin_inset Foot
status open
\begin_layout Plain Layout
\family typewriter
net.liftweb.sitemap.Loc.Link
\end_layout
\end_inset
is a fundamental part of Menu definitions.
The
\family typewriter
Link
\family default
class contains two parameters: a
\family typewriter
List[String]
\family default
of path components, and a boolean value that controls whether prefix matching
is enabled.
The path components represent the portion of the URI following your web
context, split on the "/" character.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Link-Path-Components"
\end_inset
shows how you would use Link to represent the "/utils/index" page.
Of course, instead of
\family typewriter
\begin_inset Quotes eld
\end_inset
utils
\begin_inset Quotes erd
\end_inset
::
\begin_inset Quotes eld
\end_inset
index
\begin_inset Quotes erd
\end_inset
:: Nil
\family default
, you could as easily use
\family typewriter
List(
\begin_inset Quotes eld
\end_inset
utils
\begin_inset Quotes erd
\end_inset
,
\begin_inset Quotes eld
\end_inset
index
\begin_inset Quotes erd
\end_inset
)
\family default
if you prefer.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Link Path Components
\begin_inset CommandInset label
LatexCommand label
name "lst:Link-Path-Components"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val myUtilsIndex = new Link("utils" :: "index" :: Nil, false)
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Prefix matching allows the path components you specify to match any longer
paths as well.
Following our first example, if you wanted to match anything under the
utils directory (say, for access control), you would set the second parameter
to
\family typewriter
true
\family default
, as shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Link-Prefix-Matching"
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Link Prefix Matching
\begin_inset CommandInset label
LatexCommand label
name "lst:Link-Prefix-Matching"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val allUtilPages = new Link("utils" :: Nil, true)
\end_layout
\end_inset
\end_layout
\begin_layout Standard
\begin_inset Note Note
status open
\begin_layout Plain Layout
Clarify trailing slashes and index file behavior (update: David P.
is working on making this simpler)
\end_layout
\end_inset
\end_layout
\begin_layout Subsection
ExtLink
\end_layout
\begin_layout Standard
The ExtLink object can be used to create a Link instance using your own
full link URL.
As its name implies, it would usually be used for an external location.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Using-ExtLink"
\end_inset
shows a menu item that points to a popular website.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Using ExtLink
\begin_inset CommandInset label
LatexCommand label
name "lst:Using-ExtLink"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val goodReference = Menu(Loc("reference",
\end_layout
\begin_layout Plain Layout
ExtLink("http://www.liftweb.net/"),
\end_layout
\begin_layout Plain Layout
"LiftWeb"))
\end_layout
\end_inset
\end_layout
\begin_layout Subsection
Creating Menu Entries
\end_layout
\begin_layout Standard
Menu entries are created using the Menu
\begin_inset Foot
status open
\begin_layout Plain Layout
\family typewriter
net.liftweb.sitemap.Menu
\end_layout
\end_inset
class, and its corresponding Menu object.
A Menu, in turn, holds a Loc
\begin_inset Foot
status collapsed
\begin_layout Plain Layout
\family typewriter
net.liftweb.sitemap.Loc
\end_layout
\end_inset
trait instance, which is where most of the interesting things happen.
A menu can also hold one or more child menus, which we'll cover in Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:Nested-Menus"
\end_inset
.
Note that the Loc object has several implicit methods that make defining
Locs easier, so you generally want to import them into scope
\begin_inset Note Note
status open
\begin_layout Plain Layout
How to best emphasize this (and other notes)?
\end_layout
\end_inset
.
The simplest way is to import
\family typewriter
net.liftweb.sitemap.Loc._
\family default
, but you can import specific methods by name if you prefer.
A Loc can essentially be thought of as a link in the menu, and contains
four basic items:
\end_layout
\begin_layout Enumerate
The name of the Loc: this must be unique across your sitemap because it
can be used to look up specific Menu items if you customize your menu display
(Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:Using-lift-Menu"
\end_inset
)
\end_layout
\begin_layout Enumerate
The link to which the Loc refers: usually this will referernce a specific
page, but Lift allows a single Loc to match based on prefix as well (Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:The-Link-Class"
\end_inset
)
\end_layout
\begin_layout Enumerate
The text of the menu item, which will be displayed to the user: you can
use a static string or you can generate it with a function (Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:menu-LinkText"
\end_inset
)
\end_layout
\begin_layout Enumerate
An optional set of LocParam parameters that control the behavior and appearance
of the menu item (see Sections
\begin_inset CommandInset ref
LatexCommand ref
reference "sec:Customizing-Display"
\end_inset
,
\begin_inset CommandInset ref
LatexCommand ref
reference "sec:SiteMap-Access-Control"
\end_inset
,
\begin_inset CommandInset ref
LatexCommand ref
reference "sec:Misc-Menu"
\end_inset
, and
\begin_inset CommandInset ref
LatexCommand ref
reference "sec:Page-Specific-Rendering"
\end_inset
)
\end_layout
\begin_layout Standard
For our example, we'll tackle the help page link first, because it's the
simplest (essentially, it's a static link).
The definition is shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Help-Menu-Definition"
\end_inset
.
We're assuming that you've imported the Loc implicit methods to keep things
simple.
We'll cover instantiating the classes directly in later sections of this
chapter.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Help Menu Definition
\begin_inset CommandInset label
LatexCommand label
name "lst:Help-Menu-Definition"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val helpMenu = Menu(Loc("helpHome",
\end_layout
\begin_layout Plain Layout
("help" :: "" :: Nil) -> true,
\end_layout
\begin_layout Plain Layout
"Help"))
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Here we've named the menu item "helpHome." We can use this name to refer
back to this menu item elsewhere in our code.
The second parameter is a
\family typewriter
Pair[List[String],Boolean]
\family default
which converts directly to a Link class with the given parameters (see
Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:The-Link-Class"
\end_inset
above).
In this instance, by passing in true, we're saying that anything under
the help directory will also match.
If you just use a List[String], the implicit conversion is to a Link with
prefix matching disabled.
Note that SiteMap won't allow access to any pages that don't match any
Menu entries
\begin_inset Note Note
status open
\begin_layout Plain Layout
emphasize
\end_layout
\end_inset
, so by doing this we're allowing full access to all of the help files without
having to specify a menu entry for each.
The final parameter, "Help," is the text for the menu link, should we choose
to generate a menu link from this SiteMap entry.
\end_layout
\begin_layout Subsection
Nested Menus
\begin_inset CommandInset label
LatexCommand label
name "sub:Nested-Menus"
\end_inset
\end_layout
\begin_layout Standard
The Menu class supports child menus by passing them in as final constructor
parameters.
For instance, if we wanted to have an "about" menu under Help, we could
define the menu as shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Nested-Menu-Definition"
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Nested Menu Definition
\begin_inset CommandInset label
LatexCommand label
name "lst:Nested-Menu-Definition"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val aboutMenu = Menu(Loc("about", "help" :: "about" :: Nil, "About"))
\end_layout
\begin_layout Plain Layout
val helpMenu = Menu(Loc(...as defined above...), aboutMenu)
\end_layout
\end_inset
\end_layout
\begin_layout Standard
When the menu is rendered it will have a child menu for About.
Child menus are only rendered by default when the current page matches
their parent's Loc.
That means that, for instance the following links would show in an "About"
child menu item:
\end_layout
\begin_layout Itemize
\family typewriter
/help/index
\end_layout
\begin_layout Itemize
\family typewriter
/help/usage
\end_layout
\begin_layout Standard
But the following would not:
\end_layout
\begin_layout Itemize
\family typewriter
/index
\end_layout
\begin_layout Itemize
\family typewriter
/site/example
\end_layout
\begin_layout Standard
We'll cover how you can customize the rendering of the menus in Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:Using-lift-Menu"
\end_inset
.
\end_layout
\begin_layout Subsection
Setting the Global SiteMap
\end_layout
\begin_layout Standard
Once you have all of your menu items defined, you need to set them as your
SiteMap.
As usual, we do this in the Boot class by calling the
\family typewriter
setSiteMap
\family default
method on LiftRules, as shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Setting-the-SiteMap"
\end_inset
.
The
\family typewriter
setSiteMap
\family default
method takes a SiteMap object that can be constructed using your menu items
as arguments.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Setting the SiteMap
\begin_inset CommandInset label
LatexCommand label
name "lst:Setting-the-SiteMap"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
LiftRules.setSiteMap(SiteMap(homeMenu, profileMenu, ...))
\end_layout
\end_inset
\end_layout
\begin_layout Standard
When you're dealing with large menus, and in particular when your model
objects create their own menus (see MegaProtoUser, Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:ProtoUser-and-MegaProtoUser"
\end_inset
\begin_inset Note Note
status open
\begin_layout Plain Layout
Should this be added to Mapper, or somehow folded in elsewhere?
\end_layout
\end_inset
), then it can be more convenient to define List[Menu] and set that.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Using-List-Menu-for"
\end_inset
shows this usage.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Using List[Menu] for SiteMap
\begin_inset CommandInset label
LatexCommand label
name "lst:Using-List-Menu-for"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val menus = Menu(Loc("HomePage", "", "Home"),...) ::
\end_layout
\begin_layout Plain Layout
...
\end_layout
\begin_layout Plain Layout
Menu(...) :: Nil
\end_layout
\begin_layout Plain Layout
LiftRules.setSiteMap(SiteMap(menus : _*))
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The key to using List for your menus is to explicitly define the type of
the parameter as "_*" so that it's treated as a set of varargs instead
of a single argument of type List[Menu].
\end_layout
\begin_layout Section
Customizing Display
\begin_inset CommandInset label
LatexCommand label
name "sec:Customizing-Display"
\end_inset
\end_layout
\begin_layout Standard
There are many cases where you may want to change the way that particular
menu items are displayed.
For instance, if you're using a Menu item for access control on a subdirectory,
you may not want the menu item displayed at all.
We'll discuss how you can control appearance, text, etc.
in this section.
\end_layout
\begin_layout Subsection
Hidden
\begin_inset CommandInset label
LatexCommand label
name "sub:menu-Hidden"
\end_inset
\end_layout
\begin_layout Standard
The Hidden LocParam does exactly what it says: hides the menu item from
the menu display.
All other menu features still work.
There is a variety of reasons why you might not want a link displayed.
A common use, shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Hidden-Menus"
\end_inset
, is where the point of the item is to restrict access to a particular subdirect
ory based on some condition.
(We'll cover the
\family typewriter
If
\family default
tag in Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:menu-If"
\end_inset
.)
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Hidden Menus
\begin_inset CommandInset label
LatexCommand label
name "lst:Hidden-Menus"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val receiptImages =
\end_layout
\begin_layout Plain Layout
Menu(Loc("receipts",
\end_layout
\begin_layout Plain Layout
("receipts" :: Nil) -> true,
\end_layout
\begin_layout Plain Layout
"Receipts",
\end_layout
\begin_layout Plain Layout
Hidden, If(...)))
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Note that in this example we've used the implicit conversion from
\family typewriter
Pair[String,Boolean]
\family default
to
\family typewriter
Link
\family default
to make this Menu apply to everything under the "
\family typewriter
receipts
\family default
" directory.
\end_layout
\begin_layout Subsection
Controlling the Menu Text
\begin_inset CommandInset label
LatexCommand label
name "sub:menu-LinkText"
\end_inset
\end_layout
\begin_layout Standard
The
\family typewriter
LinkText
\family default
class is what defines the function that will return the text to display
for a given menu item.
As we've shown, this can easily be set using the implicit conversion for
string
\begin_inset Formula $\rightarrow$
\end_inset
\family typewriter
LinkText
\family default
from
\family typewriter
Loc
\family default
.
As an added bonus, the implicit conversion actually takes a by-name
\family typewriter
String
\family default
for the parameter.
This means that you can just as easily pass in a function to generate the
link text as a static string.
For example, with our profile link we may want to make the link say "<username>
's profile".
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Customizing-Link-Text"
\end_inset
shows how we can do this by defining a helper method, assuming that there's
another method that will return the current user's name (we use the ubiquitous
\family typewriter
Foo
\family default
object here).
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Customizing Link Text
\begin_inset CommandInset label
LatexCommand label
name "lst:Customizing-Link-Text"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
def profileText = Foo.currentUser + "'s profile"
\end_layout
\begin_layout Plain Layout
val profileMenu = Menu(Loc("Profile",
\end_layout
\begin_layout Plain Layout
"profile" :: Nil,
\end_layout
\begin_layout Plain Layout
profileText, ...))
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Of course, if you want you can construct the
\family typewriter
LinkText
\family default
instance directly by passing in a constructor function that returns a
\family typewriter
NodeSeq
\family default
.
The function that you use with
\family typewriter
LinkText
\family default
takes a type-safe input parameter, which we'll discuss in more detail in
Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:Type-Safe-Params"
\end_inset
.
\end_layout
\begin_layout Subsection
Using <lift:Menu>
\begin_inset CommandInset label
LatexCommand label
name "sub:Using-lift-Menu"
\end_inset
\end_layout
\begin_layout Standard
So far we've covered the Scala side of things.
The other half of the magic is the special
\family typewriter
<lift:Menu>
\family default
tag.
It's this tag that handles the rendering of your menus into XHTML.
The Menu tag uses a built-in snippet
\begin_inset Foot
status open
\begin_layout Plain Layout
net.liftweb.builtin.snippet.Menu
\end_layout
\end_inset
to provide several rendering methods.
The most commonly used method is the
\family typewriter
Menu.builder
\family default
snippet.
This snippet renders your entire menu structure as an unordered list (
\family typewriter
<ul>
\family default
in XHTML).
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Rendering-with-Menu.title"
\end_inset
shows an example of using the Menu tag to build the default menu (yes,
it's that easy).
\end_layout
\begin_layout Standard
\begin_inset listings
lstparams "language=XML"
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Rendering with <lift:Menu.title>
\begin_inset CommandInset label
LatexCommand label
name "lst:Rendering-with-Menu.title"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
<div class="menu">
\end_layout
\begin_layout Plain Layout
<lift:Menu.builder />
\end_layout
\begin_layout Plain Layout
</div>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Of course, Lift offers more customization on this snippet than just emitting
some XHTML.
By specifying some prefixed attributes on the tag itself, you can add attribute
s directly to the menu elements.
The following prefixes are valid for attributes:
\end_layout
\begin_layout Itemize
\family typewriter
ul
\family default
- Adds the specified attribute to the
\family typewriter
<ul>
\family default
element that makes up the menu
\end_layout
\begin_layout Itemize
\family typewriter
li
\family default
- Adds the specified attribute to each
\family typewriter
<li>
\family default
element for the menu
\end_layout
\begin_layout Itemize
\family typewriter
li_item
\family default
- Adds the specified attribute to the current page's menu item
\end_layout
\begin_layout Itemize
\family typewriter
li_path
\family default
- Adds the specified attribute to the current page's breadcrumb trail (the
breadcrumb trail is the set of menu items that are direct ancestors in
the menu tree)
\begin_inset Note Note
status collapsed
\begin_layout Plain Layout
Verify and clarify
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The suffix of the attributes represents the name of the HTML attribute that
will be added to that element, and can be anything.
It will be passed directly through.
For instance, we can add CSS
\begin_inset Index
status open
\begin_layout Plain Layout
CSS
\end_layout
\end_inset
classes to our menu and elements fairly easily, as shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Using-Attribues-with-Menu.builder"
\end_inset
.
Notice that we also add a little JavaScript to our current menu item.
\end_layout
\begin_layout Standard
\begin_inset listings
lstparams "language=XML"
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Using Attribues with Menu.builder
\begin_inset CommandInset label
LatexCommand label
name "lst:Using-Attribues-with-Menu.builder"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
<lift:Menu.builder
\end_layout
\begin_layout Plain Layout
li:class="menuitem"
\end_layout
\begin_layout Plain Layout
li_item:class="selectedMenu"
\end_layout
\begin_layout Plain Layout
li_item:onclick="javascript:alert('Already selected!');" />
\end_layout
\end_inset
\end_layout
\begin_layout Standard
In addition to rendering the menu itself, the Menu class offers a few other
tricks.
The
\family typewriter
Menu.title
\family default
\begin_inset Index
status open
\begin_layout Plain Layout
Menu.title
\end_layout
\end_inset
snippet can be used to render the title of the page, which by default is
the name parameter of the Loc for the menu (the first parameter).
If you write your own Loc implementation (Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sec:Writing-Your-Own-Loc"
\end_inset
), or you use the Title
\begin_inset Index
status open
\begin_layout Plain Layout
Title
\end_layout
\end_inset
\family typewriter
LocParam
\family default
(Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:menu-Title"
\end_inset
), you can overide the title to be whatever you'd like.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Rendering-the-Title"
\end_inset
shows how you use
\family typewriter
Menu.title
\family default
.
In this particular example the title will be rendered as "
\family typewriter
Home Page
\family default
".
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Rendering the Menu Title
\begin_inset CommandInset label
LatexCommand label
name "lst:Rendering-the-Title"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
// In Boot:
\end_layout
\begin_layout Plain Layout
val MyMenu = Menu(Loc("Home Page", "index" :: Nil, "Home"))
\end_layout
\begin_layout Plain Layout
// In template (or wherever)
\end_layout
\begin_layout Plain Layout
<title><lift:Menu.title/></title>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The next snippet in the Menu class is
\family typewriter
item
\family default
.
The
\family typewriter
Menu.item
\family default
snippet allows you to render a particular menu item by specifying the name
attribute (matching the first parameter to Loc).
As with
\family typewriter
Menu.builder
\family default
, it allows you to specify additional prefixed attributes for the link to
be passed to the emitted item.
Because it applies these attributes to the link itself, the only valid
prefix is "
\family typewriter
a
\family default
".
Additionally, if you specify child elements for the snippet tag, they will
be used instead of the default link text.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Using-Menu.item"
\end_inset
shows an example using our "
\family typewriter
Home Page
\family default
" menu item defined in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Rendering-the-Title"
\end_inset
.
As you can see, we've added some replacement text as well as specifying
a CSS
\begin_inset Index
status open
\begin_layout Plain Layout
CSS
\end_layout
\end_inset
class for the link.
\end_layout
\begin_layout Standard
\begin_inset listings
lstparams "language=XML"
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Using Menu.item
\begin_inset CommandInset label
LatexCommand label
name "lst:Using-Menu.item"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
<lift:Menu.item name="Home Page"
\end_layout
\begin_layout Plain Layout
a:class="homeLink">
\end_layout
\begin_layout Plain Layout
<b>Go Home</b>
\end_layout
\begin_layout Plain Layout
</lift:Menu.item>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The final snippet that the Menu class provides is the
\family typewriter
Menu.group
\family default
method.
We're going to cover the use of
\family typewriter
Menu.group
\family default
in detail in Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:LocGroup"
\end_inset
.
\end_layout
\begin_layout Section
Access Control
\begin_inset CommandInset label
LatexCommand label
name "sec:SiteMap-Access-Control"
\end_inset
\end_layout
\begin_layout Standard
So far we've covered how to control the display side of Menus; now we'll
take a look at some of the plumbing behind the scenes.
One important function of a Menu is that it controls access to the pages
in your application.
If no Menu matches a given request, then the user gets a 404 Not Found
error.
Other than this binary control of "matches
\begin_inset Formula $\rightarrow$
\end_inset
display" and "doesn't match
\begin_inset Formula $\rightarrow$
\end_inset
don't display",
\family typewriter
SiteMap
\family default
provides for arbitrary access checks through the
\family typewriter
If
\family default
and
\family typewriter
Unless
\family default
\family typewriter
LocParams
\family default
.
\end_layout
\begin_layout Subsection
If
\begin_inset CommandInset label
LatexCommand label
name "sub:menu-If"
\end_inset
\end_layout
\begin_layout Standard
The
\family typewriter
If
\family default
\begin_inset Index
status open
\begin_layout Plain Layout
If
\end_layout
\end_inset
\family typewriter
LocParam
\family default
takes a test function,
\begin_inset Formula $()\Rightarrow Boolean$
\end_inset
, as well as failure message function,
\begin_inset Formula $()\Rightarrow LiftResponse$
\end_inset
, as its arguments.
When the
\family typewriter
Loc
\family default
that uses the
\family typewriter
If
\family default
clause matches a given path, the test function is executed, and if true
then the page is displayed as normal.
If the function evaluates to false, then the failure message function is
executed and its result is sent to the user.
There's an implicit conversion in
\family typewriter
Loc
\family default
from a
\family typewriter
String
\family default
to a response which converts to a
\family typewriter
RedirectWithState
\family default
\begin_inset Index
status open
\begin_layout Plain Layout
RedirectWithState
\end_layout
\end_inset
instance (Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sec:HTTP-redirects"
\end_inset
).
The redirect is to the location specified by
\family typewriter
LiftRules.siteMapFailRedirectLocation
\family default
, which is the root of your webapp ("/") by default.
If you want, you can change this in
\family typewriter
LiftRules
\family default
for a global setting, or you can provide your own
\family typewriter
LiftResponse
\family default
\begin_inset Index
status open
\begin_layout Plain Layout
LiftResponse
\end_layout
\end_inset
.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Using-the-If-LocParam"
\end_inset
shows a revision of the profile menu that we defined in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Customizing-Link-Text"
\end_inset
, extended to check whether the user is logged in.
If the user isn't logged in, we redirect to the login page.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Using the If LocParam
\begin_inset CommandInset label
LatexCommand label
name "lst:Using-the-If-LocParam"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val loggedIn = If(() => User.loggedIn_?,
\end_layout
\begin_layout Plain Layout
() => RedirectResponse("/login"))
\end_layout
\begin_layout Plain Layout
val profileMenu = Menu(Loc("Profile",
\end_layout
\begin_layout Plain Layout
"profile" :: Nil,
\end_layout
\begin_layout Plain Layout
profileText, loggedIn))
\end_layout
\end_inset
\end_layout
\begin_layout Subsection
Unless
\begin_inset CommandInset label
LatexCommand label
name "sub:menu-Unless"
\end_inset
\end_layout
\begin_layout Standard
The
\family typewriter
Unless
\family default
\begin_inset Index
status open
\begin_layout Plain Layout
Unless
\end_layout
\end_inset
\family typewriter
LocParam
\family default
is essentially the mirror of
\family typewriter
If
\family default
.
The exact same rules apply, except that the page is displayed only if the
test function returns false.
The reason that there are two classes to represent this behavior is that
it's generally clearer when a predicate is read as "working" when it returns
true.
\end_layout
\begin_layout Section
Page-Specific Rendering
\begin_inset CommandInset label
LatexCommand label
name "sec:Page-Specific-Rendering"
\end_inset
\end_layout
\begin_layout Standard
Page specific rendering with
\family typewriter
SiteMap
\family default
is an advanced technique that provides a lot of flexibility for making
pages render differently depending on state.
\end_layout
\begin_layout Subsection
The Template Parameter
\begin_inset CommandInset label
LatexCommand label
name "sub:The-Template-Parameter"
\end_inset
\end_layout
\begin_layout Standard
Generally, the template that will be used for a page is derived from the
path of the request.
The
\family typewriter
Template
\family default
\family typewriter
LocParam
\family default
, however, allows you to completely override this mechanism and provide
any template you want by passing in a function
\begin_inset Formula $()\Rightarrow NodeSeq$
\end_inset
.
Going back to our example menus (Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sec:Basic-SiteMap-Definition"
\end_inset
), we'd like the welcome page to show either the user's entries or a plain
welcome screen depending on whether they're logged in.
One approach to this is shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Override-Templates"
\end_inset
.
In this example, we create a
\family typewriter
Template
\family default
class that generates the appropriate template and then bind it into the
home page menu
\family typewriter
Loc
\family default
.
(See the Lift API for more on the Template class.)
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Overriding Templates
\begin_inset CommandInset label
LatexCommand label
name "lst:Override-Templates"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val homepageTempl = Template({ () =>
\end_layout
\begin_layout Plain Layout
<lift:surround with="default" at="content">
\end_layout
\begin_layout Plain Layout
{ if (User.loggedIn_?) {
\end_layout
\begin_layout Plain Layout
<lift:Entries.list />
\end_layout
\begin_layout Plain Layout
} else {
\end_layout
\begin_layout Plain Layout
<lift:embed what="welcome" />
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
</lift:surround>
\end_layout
\begin_layout Plain Layout
})
\end_layout
\begin_layout Plain Layout
val homeMenu = Menu(Loc("Home Page",
\end_layout
\begin_layout Plain Layout
"" :: Nil,
\end_layout
\begin_layout Plain Layout
"Home Page", homepageTempl))
\end_layout
\end_inset
\end_layout
\begin_layout Subsection
The Snippet and LocSnippets Parameters
\end_layout
\begin_layout Standard
Besides overriding the template for a page render (admittedly, a rather
coarse approach), SiteMap has two mechanisms for overriding or defining
the behavior of specific snippets.
The first, Snippet, allows you to define the dispatch for a single snippet
based on the name of the snippet.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Using-the-Snippet-locparam"
\end_inset
shows how we could use Snippet to achieve the same result for the home
page rendering as we just did with the Template parameter.
All we need to do is use the <lift:homepage> snippet on our main page and
the snippet mapping will dispatch based on the state.
(Here we've moved the welcome text into a Utils.welcome snippet.)
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Using the Snippet LocParam
\begin_inset CommandInset label
LatexCommand label
name "lst:Using-the-Snippet-locparam"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val homeSnippet = Snippet("homepage",
\end_layout
\begin_layout Plain Layout
if (User.loggedIn_?) {
\end_layout
\begin_layout Plain Layout
Entries.list _
\end_layout
\begin_layout Plain Layout
} else {
\end_layout
\begin_layout Plain Layout
Utils.welcome _
\end_layout
\begin_layout Plain Layout
})
\end_layout
\begin_layout Plain Layout
val homeMenu = Menu(Loc("Home Page",
\end_layout
\begin_layout Plain Layout
"" :: Nil,
\end_layout
\begin_layout Plain Layout
"Home Page", homeSnippet))
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The LocSnippets trait extends the concept of Snippet to provide a full dispatch
partial function.
This allows you to define multiple snippet mappings associated with a particula
r Loc.
To simplify things, Lift provides a DispatchLocSnippets trait that has
default implementations for
\family typewriter
apply
\family default
and
\family typewriter
isDefinedAt
\family default
; that means you only need to provide a
\family typewriter
dispatch
\family default
method implementation for it to work.
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Using-LocSnippets"
\end_inset
shows an example of using DispatchLocSnippets for a variety of snippets.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Using LocSnippets
\begin_inset CommandInset label
LatexCommand label
name "lst:Using-LocSnippets"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val entrySnippets = new DispatchLocSnippets {
\end_layout
\begin_layout Plain Layout
def dispatch = {
\end_layout
\begin_layout Plain Layout
case "entries" => Entries.list _
\end_layout
\begin_layout Plain Layout
case "add" => Entries.newEntry _
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Subsection
Title
\begin_inset CommandInset label
LatexCommand label
name "sub:menu-Title"
\end_inset
\end_layout
\begin_layout Standard
As we mentioned in Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:Using-lift-Menu"
\end_inset
, the Title LocParam can be used to provide a state-dependent title for
a page.
The Title case class simply takes a function
\begin_inset Formula $(T)\Rightarrow NodeSeq$
\end_inset
, where T is a type-safe parameter (we'll cover this in Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:Type-Safe-Params"
\end_inset
).
Generally you can ignore this parameter if you want to, which is what we
do in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Customizing-the-Title"
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Customizing the Title
\begin_inset CommandInset label
LatexCommand label
name "lst:Customizing-the-Title"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val userTitle = Title((_) =>
\end_layout
\begin_layout Plain Layout
if (User.loggedIn_?) {
\end_layout
\begin_layout Plain Layout
Text(User.name + "'s Account")
\end_layout
\begin_layout Plain Layout
} else {
\end_layout
\begin_layout Plain Layout
Text("Welcome to PocketChange")
\end_layout
\begin_layout Plain Layout
})
\end_layout
\begin_layout Plain Layout
val homeMenu = Menu(Loc("Home Page",
\end_layout
\begin_layout Plain Layout
"" :: Nil,
\end_layout
\begin_layout Plain Layout
"Home Page", homepageTempl, userTitle))
\end_layout
\end_inset
\end_layout
\begin_layout Section
Miscellaneous Menu Functionality
\begin_inset CommandInset label
LatexCommand label
name "sec:Misc-Menu"
\end_inset
\end_layout
\begin_layout Standard
These are LocParams that don't quite fit into the other categories.
\end_layout
\begin_layout Subsection
Test
\begin_inset CommandInset label
LatexCommand label
name "sub:menu-Test"
\end_inset
\end_layout
\begin_layout Standard
Test is intended to be used to ensure that a given request has the proper
parameters before servicing.
With Test, you provide a function,
\begin_inset Formula $(Req)\Rightarrow Boolean$
\end_inset
that is passed the full Req object.
Note that the test is performed when SiteMap tries to locate the correct
menu, as opposed to If and Unless, which are tested after the proper Loc
has been identified.
Returning a false means that this Loc doesn't match the request, so SiteMap
will continue to search through your Menus to find an appropriate Loc.
As an example, we could check to make sure that a given request comes from
Opera (the Req object provides convenience methods to test for different
browsers; see the Lift API for a full list) with the code in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Testing-the-Request"
\end_inset
.
\begin_inset Note Note
status open
\begin_layout Plain Layout
Come up with a better example
\end_layout
\end_inset
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Testing the Request
\begin_inset CommandInset label
LatexCommand label
name "lst:Testing-the-Request"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val onlyOpera = Test(req => req.isOpera)
\end_layout
\begin_layout Plain Layout
val operaMenu = Menu(Loc("Opera", "opera" :: Nil, "Only Opera", onlyOpera))
\end_layout
\end_inset
\end_layout
\begin_layout Subsection
LocGroup
\begin_inset CommandInset label
LatexCommand label
name "sub:LocGroup"
\end_inset
\end_layout
\begin_layout Standard
The LocGroup param allows you to categorize your menu items.
The
\family typewriter
Menu.group
\family default
snippet (mentioned in Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:Using-lift-Menu"
\end_inset
) allows you to render the menu items for a specific group.
A menu item may be associated with one or more groups.
Simply add a LocGroup param with string arguments for the group names,
as shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Categorizing-your-Menu"
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Categorizing Your Menu
\begin_inset CommandInset label
LatexCommand label
name "lst:Categorizing-your-Menu"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
val siteMenu = Menu(Loc(...,LocGroup("admin", "site")))
\end_layout
\end_inset
\end_layout
\begin_layout Standard
In your templates, you then specify the binding of the menu as shown in
Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Binding-a-Menu-group"
\end_inset
.
As you can see, we've also added a prefixed attribute to control the CSS
\begin_inset Index
status open
\begin_layout Plain Layout
CSS
\end_layout
\end_inset
class of the links ("a" is the only valid prefix), and we've added some
body XHTML for display.
In particular, the <menu:bind> tag controls where the menu items are rendered.
If you don't provide body elements, or if you provide body elements without
the <menu:bind> element, your body XHTML will be ignored and the menu will
be rendered directly.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Binding a Menu Group
\begin_inset CommandInset label
LatexCommand label
name "lst:Binding-a-Menu-group"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
<div class="site">
\end_layout
\begin_layout Plain Layout
<ul>
\end_layout
\begin_layout Plain Layout
<lift:Menu.group group="site"
\end_layout
\begin_layout Plain Layout
a:class="siteLink">
\end_layout
\begin_layout Plain Layout
<li><menu:bind /></li>
\end_layout
\begin_layout Plain Layout
</lift:Menu.group>
\end_layout
\begin_layout Plain Layout
</ul>
\end_layout
\begin_layout Plain Layout
</div>
\end_layout
\end_inset
\end_layout
\begin_layout Section
Writing Your Own Loc
\begin_inset CommandInset label
LatexCommand label
name "sec:Writing-Your-Own-Loc"
\end_inset
\end_layout
\begin_layout Standard
As we've shown, there's a lot of functionality available for your Menu items.
If you need more control, though, the Loc trait offers some functionality,
such as rewriting, that doesn't have a direct correspondence in a LocParam
element.
The basic definition of a Loc implementation covers a lot of the same things.
The following vals and defs are abstract, so you must implement them yourself:
\end_layout
\begin_layout Itemize
def name: the name that can be used to retrieve the menu via Menu.item
\end_layout
\begin_layout Itemize
def link: the actual link; you can use the implicit conversions from List[String
] or Pair[List[String],Boolean], or you can create the Link object yourself
\end_layout
\begin_layout Itemize
def text: the text that will be displayed to the user; you can use the implicit
conversion from String, or you can provide your own LinkText instance
\end_layout
\begin_layout Itemize
def params: must return a List[LocParam] that is used to control behavior
as we've shown in the previous sections
\end_layout
\begin_layout Itemize
def defaultParams: used for type-safe rewriting, which we'll cover in Section
\begin_inset CommandInset ref
LatexCommand ref
reference "sub:Type-Safe-Params"
\end_inset
\end_layout
\begin_layout Standard
Essentially, these mirror the params that are required when you use Loc.apply
to generate a Loc.
We're going to write our own Loc implementation for our Expenses in this
section to demonstrate how this works.
Because this overlaps with existing functionality in the PocketChange applicati
on, we'll be using a branch in the PocketChange app.
You can pull the new branch with the command
\end_layout
\begin_layout LyX-Code
git checkout --track -b custom-loc origin/custom-loc
\end_layout
\begin_layout Standard
You can then switch back and forth between the branches with the commands:
\end_layout
\begin_layout LyX-Code
git checkout master
\end_layout
\begin_layout LyX-Code
git checkout custom-loc
\end_layout
\begin_layout Standard
\end_layout
\begin_layout Subsection
Corresponding Functions
\end_layout
\begin_layout Standard
Table
\begin_inset CommandInset ref
LatexCommand ref
reference "tab:LocParam-Methods-in-loc"
\end_inset
lists the LocParams and their corresponding methods in Loc, with notes
to explain any differences in definition or usage.
If yould prefer to use the LocParams instead, just define the
\family typewriter
params
\family default
method on Loc to return a list of the LocParams you want.
\end_layout
\begin_layout Standard
\begin_inset Float table
placement H
wide false
sideways false
status open
\begin_layout Plain Layout
\end_layout
\begin_layout Plain Layout
\align center
\begin_inset Tabular
<lyxtabular version="3" rows="8" columns="3">
<features islongtable="true">
<column alignment="center" valignment="top" width="1in">
<column alignment="center" valignment="top" width="1.5in">
<column alignment="center" valignment="top" width="2.5in">
<row endhead="true" endfirsthead="true">
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
LocParam
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Loc Method
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Notes
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Hidden
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
N/A
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
To make your Loc hidden, add a Hidden LocParam to your params method return
value
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
If/Unless
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
override testAccess
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
You need to return an Either to indicate success (Left[Boolean]) or failure
(Right[Box[LiftResponse]])
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Template
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
override calcTemplate
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Return a Box[NodeSeq]
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Snippet and LocSnippets
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
override snippets
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Snippet is a PartialFunction[String, Box[ParamType]), NodeSeq => NodeSeq],
which lets you use the type-safe parameter to control behavior.
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Title
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
override title
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
You can override "def title" or "def title(in: ParamType)" depending on
whether you want to use type-safe parameters
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Test
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
override doesMatch_?
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
It's your responsibility to make sure that the
\emph on
path
\emph default
of the request matches your Loc, since this method is what SiteMap uses
to find the proper Loc for a request
\end_layout
\end_inset
</cell>
</row>
<row>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
LocGroup
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
override inGroup_?
\end_layout
\end_inset
</cell>
<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" rightline="true" usebox="none">
\begin_inset Text
\begin_layout Plain Layout
Nothing special here
\end_layout
\end_inset
</cell>
</row>
</lyxtabular>
\end_inset
\end_layout
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
LocParam Methods in Loc
\begin_inset CommandInset label
LatexCommand label
name "tab:LocParam-Methods-in-loc"
\end_inset
\end_layout
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Subsection
Type Safe Parameters
\begin_inset CommandInset label
LatexCommand label
name "sub:Type-Safe-Params"
\end_inset
\end_layout
\begin_layout Standard
One of the nice features of Loc is that it allows you to rewrite requests
in a type-safe manner.
What this means is that we can define a rewrite function on our Loc instance
that returns not only a standard RewriteResponse, but also a parameter
that we can define to pass information back to our menu to control behavior.
The reason that this is type-safe is that we define our Loc on the type
of the parameter itself.
For instance, let's expand the functionality of our app so that we have
a page called "acct" that shows the expense entries for a given account.
We would like this page to be viewable only by the owner of the account
under normal circumstances, but to allow them to share it with other members
if they wish to.
Let's start by defining our type-safe parameter class as shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Defining-LedgerInfo"
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Defining AccountInfo
\begin_inset CommandInset label
LatexCommand label
name "lst:Defining-LedgerInfo"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
abstract class AccountInfo
\end_layout
\begin_layout Plain Layout
case object NoSuchAccount extends AccountInfo
\end_layout
\begin_layout Plain Layout
case object NotPublic extends AccountInfo
\end_layout
\begin_layout Plain Layout
case class FullAccountInfo(account : Account,
\end_layout
\begin_layout Plain Layout
entries : List[Expense]) extends AccountInfo
\end_layout
\end_inset
\end_layout
\begin_layout Standard
We define a few case classes to indicate various states.
The FullAccountInfo holds the account itself as well as some flags for
behavior.
Now that we have our parameter type, we can start to define our Loc, as
shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Defining-a-Type-Safe-loc"
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Defining a Type-Safe Loc
\begin_inset CommandInset label
LatexCommand label
name "lst:Defining-a-Type-Safe-loc"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
class AccountLoc extends Loc[AccountInfo] {
\end_layout
\begin_layout Plain Layout
...
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Assuming that an Account instance has a unique string ID, we would like
to use URL rewriting so that we can access a ledger via "/acct/<unique
id>".
Our rewrite function, shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:The-Rewrite-Function"
\end_inset
, handles a few different things at once.
It handles locating the correct account and then checking the permissions
if everything else is OK.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
The Rewrite Function
\begin_inset CommandInset label
LatexCommand label
name "lst:The-Rewrite-Function"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
override def rewrite = Full({
\end_layout
\begin_layout Plain Layout
case RewriteRequest(ParsePath(List("acct", aid), _, _, _), _, _) => {
\end_layout
\begin_layout Plain Layout
Account.findAll(By(Account.stringId, aid)) match {
\end_layout
\begin_layout Plain Layout
case List(account) if account.is_public.is => {
\end_layout
\begin_layout Plain Layout
(RewriteResponse("account" :: Nil),
\end_layout
\begin_layout Plain Layout
FullAccountInfo(account, account.entries))
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
case List(account) => {
\end_layout
\begin_layout Plain Layout
(RewriteResponse("account" :: Nil),
\end_layout
\begin_layout Plain Layout
NotPublic)
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
case _ => {
\end_layout
\begin_layout Plain Layout
(RewriteResponse("account" :: Nil),
\end_layout
\begin_layout Plain Layout
NoSuchAccount)
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
}
\end_layout
\begin_layout Plain Layout
})
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Now that we've defined the transformation from URL to parameter, we need
to define the behaviors based on that parameter.
The account page will show a list of expense entries only if the account
is located and is public.
For this example we'll use a single template and we'll change the snippet
behavior based on our parameter, as shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Defining-Snippet-Behavior"
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Defining Snippet Behavior
\begin_inset CommandInset label
LatexCommand label
name "lst:Defining-Snippet-Behavior"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
override def snippets = {
\end_layout
\begin_layout Plain Layout
case ("entries", Full(NoSuchAccount)) => {ignore : NodeSeq =>
\end_layout
\begin_layout Plain Layout
Text("Could not locate the requested account")}
\end_layout
\begin_layout Plain Layout
case ("entries", Full(NotPublic)) => {ignore : NodeSeq =>
\end_layout
\begin_layout Plain Layout
Text("This account is not publicly viewable")}
\end_layout
\begin_layout Plain Layout
case ("entries", Full(FullAccountInfo(account, List()))) => {ignore :
NodeSeq =>
\end_layout
\begin_layout Plain Layout
Text("No entries for " + account.name.is)}
\end_layout
\begin_layout Plain Layout
case ("entries", Full(FullAccountInfo(account, entries))) =>
\end_layout
\begin_layout Plain Layout
Accounts.show(entries) _
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Standard
In this example, we simply return some text if the
\family typewriter
Account
\family default
can’t be located, isn’t public, or doesn’t have any
\family typewriter
Expense
\family default
entries.
Remember that this function needs to return a snippet function, which expects
a NodeSeq parameter.
This is why we need to include the
\family typewriter
ignore
\family default
parameter as part of our closures.
If our
\family typewriter
Account
\family default
does have entries, we return a real snippet method defined in our
\family typewriter
Accounts
\family default
object.
In our template, we simply use an entries snippet tag, as shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Our-Public-Template"
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Our Public Template
\begin_inset CommandInset label
LatexCommand label
name "lst:Our-Public-Template"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
<lift:surround with="default" at="content">
\end_layout
\begin_layout Plain Layout
<lift:entries eager_eval="true">
\end_layout
\begin_layout Plain Layout
<h1><lift:Menu.title /></h1>
\end_layout
\begin_layout Plain Layout
<lift:embed what="entry_table" />
\end_layout
\begin_layout Plain Layout
</lift:entries>
\end_layout
\begin_layout Plain Layout
</lift:surround>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
We’re using our embedded table template for the body of the table along
with the
\family typewriter
eager_eval
\family default
attribute so that we can use the same markup for all occurrences of our
expense table display.
We can also define the title of the page based on the
\family typewriter
title
\family default
parameter, as shown in Listing
\begin_inset CommandInset ref
LatexCommand ref
reference "lst:Defining-the-Title"
\end_inset
.
\end_layout
\begin_layout Standard
\begin_inset listings
inline false
status open
\begin_layout Plain Layout
\begin_inset Caption
\begin_layout Plain Layout
Defining the Title
\begin_inset CommandInset label
LatexCommand label
name "lst:Defining-the-Title"
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Plain Layout
override def title(param : AccountInfo) = param match {
\end_layout
\begin_layout Plain Layout
case FullAccountInfo(acct, _) =>
\end_layout
\begin_layout Plain Layout
Text("Expense summary for " + acct.name.is)
\end_layout
\begin_layout Plain Layout
case _ => Text("No account")
\end_layout
\begin_layout Plain Layout
}
\end_layout
\end_inset
\end_layout
\begin_layout Subsection
Dynamically Adding Child Menus
\end_layout
\begin_layout Standard
TBW
\end_layout
\begin_layout Subsection
Binding Your Custom Loc
\end_layout
\begin_layout Section
Conclusion
\end_layout
\begin_layout Standard
As we’ve shown in this chapter, SiteMap offers a wide range of functionality
to let you control site navigation and access.
You can customize the display of your individual items using the
\family typewriter
LinkText
\family default
\family typewriter
LocParam
\family default
as well as through the functionality of the built-in
\family typewriter
Menu
\family default
\family typewriter
builder
\family default
and
\family typewriter
item
\family default
snippets.
You can use the
\family typewriter
If
\family default
and
\family typewriter
Unless
\family default
\family typewriter
LocParam
\family default
s to control access to your pages programmatically, and you can use the
\family typewriter
Test
\family default
\family typewriter
LocParam
\family default
to check the request before it's even dispatched.
Page-specific rendering can be customized with the
\family typewriter
Template
\family default
,
\family typewriter
Snippet
\family default
, and
\family typewriter
LocSnippet
\family default
\family typewriter
LocParam
\family default
s, and you can group menu items together via the
\family typewriter
LocGroup
\family default
\family typewriter
LocParam
\family default
.
Finally, you can consolidate all of these functions by writing your own
\family typewriter
Loc
\family default
trait subclass directly, and gain the additional benefit of type-safe URL
rewriting.
Together these offer a rich set of tools for building your web site exactly
they way you want to.
\end_layout
\end_body
\end_document
Jump to Line
Something went wrong with that request. Please try again.