Understanding Lift routing and SiteMap

247 views
Skip to first unread message

Tim Perrett

unread,
Feb 24, 2008, 12:10:43 PM2/24/08
to liftweb
Hey Chaps,

Just trying to get a better understanding of how to implement
parameter routing and use of the SiteMap in lift? In the lift example,
some uri paramaters are passed in the query string (e.g. country=GB)
but I was wondering what the situation was if I wanted something a
little cleaner (e.g. /path/country/gb) - would I need to employ some
kind of front end rewrite or is there support directly in lift to
handle this? (I guess its a-la rails style dispatching)

Also, the wikki page for SiteMap doesn't seem to give an information
on what it is for and how it should be used? Id be happy to start
populating some of these pages and helping out with the Wikki in
general but just need to get a better understanding of what things are
*actually* for [versus what I might think there for ;)]

Cheers

Tim

Steve Jenson

unread,
Feb 24, 2008, 2:43:07 PM2/24/08
to lif...@googlegroups.com
On Sun, Feb 24, 2008 at 9:10 AM, Tim Perrett <goo...@timperrett.com> wrote:
>
> Hey Chaps,
>
> Just trying to get a better understanding of how to implement
> parameter routing and use of the SiteMap in lift? In the lift example,
> some uri paramaters are passed in the query string (e.g. country=GB)
> but I was wondering what the situation was if I wanted something a
> little cleaner (e.g. /path/country/gb) - would I need to employ some
> kind of front end rewrite or is there support directly in lift to
> handle this? (I guess its a-la rails style dispatching)

Routing is done in Boot. Take a look at the example application's Wiki
for an example of how to do it.

http://code.google.com/p/liftweb/source/browse/trunk/liftweb/sites/example/src/main/scala/bootstrap/liftweb/Boot.scala#54

> Also, the wikki page for SiteMap doesn't seem to give an information
> on what it is for and how it should be used? Id be happy to start
> populating some of these pages and helping out with the Wikki in
> general but just need to get a better understanding of what things are
> *actually* for [versus what I might think there for ;)]

SiteMap is basically a whitelist of URLs that can be viewed in your
application along with the rules for who can view them.

The HelloLift example app has a blogging system in it with user authentication.

http://code.google.com/p/liftweb/source/browse/trunk/liftweb/sites/hellolift/src/main/scala/bootstrap/liftweb/Boot.scala#37

If you take a look at Entry.sitemap, it keeps track of it's own URLs
and who can view them. Some of behind authentication and some are
Hidden from the Menu builder.

http://code.google.com/p/liftweb/source/browse/trunk/liftweb/sites/hellolift/src/main/scala/com/hellolift/model/Entry.scala#15

HTH,
Steve

Tim Perrett

unread,
Feb 24, 2008, 4:13:34 PM2/24/08
to liftweb
Hey Steve,

Great - thanks for the links!

val wiki_rewriter: LiftServlet.RewritePf = {
case RewriteRequest(_, path @ ParsePath("wiki" :: page :: _,
_,_), _, _) =>
RewriteResponse("/wiki", ParsePath("wiki" :: Nil, true, false),
TreeMap("wiki_page" -> page ::
path.path.drop(2).zipWithIndex.map(p => ("param"+(p._2 + 1)) ->
p._1) :_*))
}

LiftServlet.addRewriteBefore(wiki_rewriter)

This is a stupid question, but I take it that the "_" means "all" -
same as in the package imports?

Cheers

Tim

David Pollak

unread,
Feb 24, 2008, 4:51:29 PM2/24/08
to lif...@googlegroups.com
'_' is the wildcard... In the case of pattern matching, it means "ignore"  The parameter is neither matched nor extracted.
--
lift, the secure, simple, powerful web framework http://liftweb.net
Collaborative Task Management http://much4.us

David Pollak

unread,
Feb 24, 2008, 6:01:47 PM2/24/08
to lif...@googlegroups.com
Tim,

There are two concepts at work.  The first is URL Re-writing and the second is page access.

As SteveJ pointed out, the re-writing and SiteMap are defined in Boot (early and only once in the process of starting lift).

Re-writing is a recursive process that happens very early in the response cycle.

When the request comes in, it is recursively re-written and query parameters are optionally extracted.  You could implement a Rails-style "the third parameter is 'id'" scheme very easily here.  Take a look at how the wiki is done in sites/example.

SiteMap is a whitelist (which run-time tested access control) based on the re-written URL.  SiteMap is designed to make sure that the user only accesses the URLs that the user is supposed to and it can also be used to generate site navigation and breadcrumbs.  It differs from Rails' controller-by-controller access control in that it puts all the access control rules in one place.  The advantage is that this information can be used to generate navigation.

Does this help?

Thanks,

David

On 2/24/08, Tim Perrett <goo...@timperrett.com> wrote:

Tim Perrett

unread,
Feb 25, 2008, 12:11:43 PM2/25/08
to liftweb
Awesome, thanks for your replys guys!

Cheers

Tim

Tim Perrett

unread,
Mar 25, 2008, 1:56:15 PM3/25/08
to liftweb
Sorry to bring this topic back up but im struggling to understand some
of the concepts at work here!

Im looking at the rewriter code:

val wiki_rewriter: LiftRules.RewritePf = {
case RewriteRequest( path @ ParsePath("wiki" :: page :: _, _,_),
_, _) =>
RewriteResponse("wiki" :: Nil,
Map("wiki_page" -> page :: path.path.drop(2).zipWithIndex.map(p
=> ("param"+(p._2 + 1)) -> p._1) :_*))
}
LiftRules.addRewriteBefore(wiki_rewriter)

I get that it looks at the incomming url and then it takes the match,
and starts to map it out to the "wiki_page" page, but what I not
really getting is the last bit:

Map("wiki_page" -> page :: path.path.drop(2).zipWithIndex.map(p =>
("param"+(p._2 + 1)) -> p._1) :_*))

I cant find the Map method in the scala docs and im not really 100%
sure where the path variable has come from!?

Otherwise, the "example" under sites in the lift trunk doesnt use a
SiteMap. Whats the deal with that, as I noticed the unconference site
also didn't use a SiteMap? I know its for fine grained URL security
and for that reason id quite like to understand how to use it properly
and if there are times when its just not appropriate?

I found this thread ( http://groups.google.com/group/liftweb/browse_thread/thread/79cdc0152c96a4f7
), which interested me. In that example, they talk about urls such as /
timesheets/reports/list, with SiteMap entries like:

Menu(Loc("ReportList", "/timesheets/reports/list", "Report List",
Hidden))

Would someone be so kind as to pick this apart for sake of example? I
understand that /lift/ is a 'view first' framework, so these dont map
onto a snippet or anything as they can be re-used, but how on earth
does it know where to route that request?! My first thought was that
there must be a view file under ./webapp/timesheets/reports called
list.html? But even when you generate a basic lift app from the
archetype, the default SiteMap includes:

Menu(Loc("Home", "/", "Home"))

But there is no file called "Home" - unless this is some kind of
special case and it knows to look for index.html when the path is "/"?

If someone would be so kind as to fill in some of the blanks that
would be brilliant!

Cheers

Tim

TylerWeir

unread,
Mar 25, 2008, 3:01:40 PM3/25/08
to liftweb
>> My first thought was that
>>there must be a view file under ./webapp/timesheets/reports called
>>list.html?

You got it, so:
Loc("ReportList", "/timesheets/reports/list", "Report List", Hidden)

The location is named "ReportList", the url is "/timesheets/reports/
list" and the link name will be "Report List."

"Hidden" means is will not appear in the sitemap, to have it appear,
do this:
Loc("ReportList", "/timesheets/reports/list", "Report List")

Helpful? Worse?

Ty


On Mar 25, 1:56 pm, Tim Perrett <goo...@timperrett.com> wrote:
> Sorry to bring this topic back up but im struggling to understand some
> of the concepts at work here!
>
> Im looking at the rewriter code:
>
>     val wiki_rewriter: LiftRules.RewritePf = {
>       case RewriteRequest( path @ ParsePath("wiki" :: page :: _, _,_),
> _, _) =>
>       RewriteResponse("wiki" :: Nil,
>       Map("wiki_page" -> page :: path.path.drop(2).zipWithIndex.map(p
> => ("param"+(p._2 + 1)) -> p._1) :_*))
>     }
>     LiftRules.addRewriteBefore(wiki_rewriter)
>
> I get that it looks at the incomming url and then it takes the match,
> and starts to map it out to the "wiki_page" page, but what I not
> really getting is the last bit:
>
> Map("wiki_page" -> page :: path.path.drop(2).zipWithIndex.map(p =>
> ("param"+(p._2 + 1)) -> p._1) :_*))
>
> I cant find the Map method in the scala docs and im not really 100%
> sure where the path variable has come from!?
>
> Otherwise, the "example" under sites in the lift trunk doesnt use a
> SiteMap. Whats the deal with that, as I noticed the unconference site
> also didn't use a SiteMap? I know its for fine grained URL security
> and for that reason id quite like to understand how to use it properly
> and if there are times when its just not appropriate?
>
> I found this thread (http://groups.google.com/group/liftweb/browse_thread/thread/79cdc0152...

Tim Perrett

unread,
Mar 25, 2008, 4:48:55 PM3/25/08
to liftweb
Hey Tyler,

Thanks thats helpfull - I've just been having a play with the code to
try and understand the workings... I think I get it all now :-D

One thing tho - URLs that have parameters passed to them (e.g. /
product/show/12345, where 12345 is the hypothetical product id ); is
it needed to somehow allow for that parameter in the SiteMap, or is
that just handled via the Rewriting?

Cheers

Tim

David Pollak

unread,
Mar 25, 2008, 4:56:12 PM3/25/08
to lif...@googlegroups.com

You can handle it in re-writing or set the Link.matchOnPrefix flag and
it'll match anything that begins with the link.
> Cheers
>
> Tim
>
> >
>

Tim Perrett

unread,
Apr 6, 2008, 9:38:32 AM4/6/08
to liftweb
Hey Chaps,

Sorry to drag this one out of the archives, but i just wanted to try
and clarify something as Id like to write a wikki article about it:

I have the following code and im trying to fully understand the
parameter handling within /lift/

LiftRules.addRewriteBefore {
/* accepts the request /goat and displays /badger.html */
case RewriteRequest(ParsePath("goat" :: Nil, _,_), _, _) =>
RewriteResponse(List("badger"))
/* accepts /dog/dfgfd/dfgfd or /dog/* (anything at all) */
case RewriteRequest(ParsePath("dog" :: _, _,_), _, _) =>
RewriteResponse( List("badger") )
/* accepts /fox/something/dgfdg/dfgd and displays /something/foo
*/
case RewriteRequest(ParsePath("fox" :: something, _,_), _, _) =>
RewriteResponse( List("something", "foo"), Map("category" -
> something) )
}
/* SiteMap*/
val entries = Menu(Loc("Home", "/", "Home")) ::
Menu(Loc("Foo", "/badger", "Foo")) ::
Menu(Loc("Directory Foo", "/something/foo",
"Directory Foo")) :: Nil
LiftRules.setSiteMap(SiteMap(entries:_*))

However, the response:

RewriteResponse( List("something", "foo"), Map("category" ->
something) )

gives an error. When i look at the code for the unconference project
it does something very similar without problem and was just wondering
what was wrong with it? I have the following questions:

- the :: something definition - this is just a variable placeholder
for what will actually be in the url isnt it? Or does it *need* to be
the same name as whats defined? (e.g. /fox/something ?)
- Is there a better way to control parameter passing rather than
using _ ?
- In the response, RewriteResponse( List("something", "foo"),
Map("category" -> something) ) isnt the Map the mapping of the request
parameter into something that is bindable in the snippets?

Sorry for all these questions!

Cheers

Tim

David Pollak

unread,
Apr 6, 2008, 5:41:26 PM4/6/08
to lif...@googlegroups.com
Tim,


Tim Perrett wrote:
>
>
> However, the response:
>
> RewriteResponse( List("something", "foo"), Map("category" ->
> something) )
>
> gives an error.

Runtime? Compile-time? Stacktrace or error text please.


> When i look at the code for the unconference project
> it does something very similar without problem and was just wondering
> what was wrong with it? I have the following questions:
>
> - the :: something definition - this is just a variable placeholder
> for what will actually be in the url isnt it? Or does it *need* to be
> the same name as whats defined? (e.g. /fox/something ?)
>

This is all done with Scala's pattern matching.

"foo" :: something
-- and --
"foo" :: something :: _
-- and --
"foo" :: something :: Nil

Are different. In the first example, something is a List[String]
containing all the items after "foo" so "/foo" will match this pattern
and something will be Nil. "/foo/bar/baz" will also match and something
will be List("bar", "baz")

The second line matches "/foo/bar" where something = "bar" and
"/foo/bar/baz" where something is still "bar".

The third line matches "/foo/bar" where something = "bar" but will not
match "/foo" or "/foo/bar/baz"

You can read more about Pattern matching at
http://www.scala-lang.org/intro/patmatch.html and
http://www.scala-lang.org/intro/extractors.html


> - Is there a better way to control parameter passing rather than
> using _ ?
>

I don't understand the question.


> - In the response, RewriteResponse( List("something", "foo"),
> Map("category" -> something) ) isnt the Map the mapping of the request
> parameter into something that is bindable in the snippets?
>

This binds the contents of the something variable to a request parameter
that can be accessed via S.param or in the RequestState. This can be
accessed in a snippet with S.param("category") which will return a
Can[String]. The Can will be Full if the "category" parameter exists.

Thanks,

David

Tim Perrett

unread,
Apr 6, 2008, 6:56:37 PM4/6/08
to liftweb
> This is all done with Scala's pattern matching.
>
> "foo" :: something
>  -- and --
> "foo" :: something :: _
>  -- and --
> "foo" :: something :: Nil
>
> Are different.  In the first example, something is a List[String]
> containing all the items after "foo" so "/foo" will match this pattern
> and something will be Nil.  "/foo/bar/baz" will also match and something
> will be List("bar", "baz")
>
> The second line matches "/foo/bar" where something = "bar" and
> "/foo/bar/baz" where something is still "bar".
>
> The third line matches "/foo/bar" where something = "bar" but will not
> match "/foo" or "/foo/bar/baz"

Ahhhhhh - that was easily the biggest help ever. I finally get that
now! Doh. If its no problem with anyone i'll post this onto the wiki
tomorrow as from a n00b perspective thats really not obvious straight
away and has foxed me longer than anything else in /lift/ (perhaps
thats just me?!). That has cleared so much up for me now thanks Dave.

> This binds the contents of the something variable to a request parameter
> that can be accessed via S.param or in the RequestState.  This can be
> accessed in a snippet with S.param("category") which will return a
> Can[String].  The Can will be Full if the "category" parameter exists.

Marvelous - So presumably then in the snippet you just check the
contents of the Full, and if it has contents yeild it with openOr on
Full?

Thanks ever so much, that makes sense now at last! Just worked up a
little example trying various URL schemes and it works splendidly :-D

Happy Days

Tim
Reply all
Reply to author
Forward
0 new messages