home | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Lift Framework example source code file (Loc.scala)

This example Lift Framework source code file (Loc.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Lift Framework tags/keywords

anylocparam, boolean, boolean, box, box, full, list, locparam, nil, nodeseq, nodeseq, string, string, t

The Lift Framework Loc.scala source code

/*
 * Copyright 2007-2011 WorldWide Conferencing, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.liftweb
package sitemap

import net.liftweb.http._
import net.liftweb.common._
import net.liftweb.util._
import Helpers._
import auth._

import scala.xml.{NodeSeq, Text}

/**
 * A menu location
 */
trait Loc[T] {
  def name: String

  def link: Loc.Link[T]

  def text: Loc.LinkText[T]

  def overrideValue: Box[T] = Empty

  object requestValue extends RequestVar[Box[T]](defaultRequestValue) {
    override val __nameSalt = randomString(10)
  }

  /**
   * Override this if the default request value should
   * be something other than Empty
   */
  protected def defaultRequestValue: Box[T] = Empty

  /**
   * When the menu item is displayed, what CSS class do we add to the
   * node?
   */
  def cssClassForMenuItem: Box[String] = cacheCssClassForMenuItem.map(_())

  /**
   * By default, this lazy val looks for the MenuCssClass LocParam and
   * uses it.
   */
  protected lazy val cacheCssClassForMenuItem: Box[() => String] = 
    allParams.flatMap {
      case a: Loc.MenuCssClass => List(a)
      case _ => Nil
    }.headOption.map(_.cssClass.func)

  /**
   * Given a value calculate the HREF to this item
   */
  def calcHref(in: T): String = appendQueryParameters(link.createPath(in),
                                                      Full(in))


  /**
   * Calculate HREF to this item using currentValue
   */
  def calcDefaultHref: String =  currentValue.map(p => link.createPath(p)).toOption.
    map(path => appendQueryParameters(path, currentValue)).getOrElse("")


  def defaultValue: Box[T]
  
  /**
  * The value of the Loc based on params (either Loc.Value or Loc.CalcValue)
  */
  def paramValue: Box[T] = calcValue.flatMap(f => f()) or staticValue

  private lazy val staticValue: Box[T] = allParams.collect{case Loc.Value(v: T) => v}.headOption
  
  private lazy val calcValue: Box[() => Box[T]] = params.collect{case Loc.CalcValue(f: Function0[Box[T]]) => f}.headOption

  /**
   * Calculate the Query parameters
   */
  def queryParameters(what: Box[T]): List[(String, String)] =
    addlQueryParams.flatMap(_()) ::: 
  calcQueryParams.flatMap(_(what))

  protected def appendQueryParams(what: T)(nodeSeq: NodeSeq): NodeSeq = 
    Text(appendQueryParameters(nodeSeq.text, Full(what)))

  protected def appendQueryParameters(in: String, what: Box[T]) = 
    Helpers.appendQueryParameters(in, queryParameters(what))

  private lazy val addlQueryParams: List[() => List[(String, String)]] =
    params.collect{case lp: Loc.QueryParameters => lp.f}


  private lazy val calcQueryParams: List[Box[T] => List[(String, String)]] =
    params.collect{case lp: Loc.LocQueryParameters[T] => lp.f}

  /**
  * The current value of the cell: overrideValue or requestValue.is or defaultValue oe paramValue
  */
  def currentValue: Box[T] = overrideValue or requestValue.is or defaultValue or paramValue

  def childValues: List[T] = Nil

  def params: List[Loc.LocParam[T]]

  def allParams: List[Loc.AnyLocParam] = 
    (params.asInstanceOf[List[Loc.AnyLocParam]]) ::: 
     parentParams :::
     siteMap.globalParams

  private def parentParams: List[Loc.AnyLocParam] = 
    _menu match {
      case null => Nil
      case menu => menu._parent match {
        case Full(parentMenu: Menu) => 
          if (!params.collect{case i: Loc.UseParentParams => true}.isEmpty) {
            parentMenu.loc.allParams.asInstanceOf[List[Loc.LocParam[Any]]]
          } else {
            Nil
          }
        case _ => Nil
      }
    }

  private lazy val _placeHolder_? = allParams.contains(Loc.PlaceHolder)

  def placeHolder_? : Boolean = _placeHolder_?

  private lazy val _hideIfNoKids_? = allParams.contains(Loc.HideIfNoKids)

  def hideIfNoKids_? = placeHolder_? || _hideIfNoKids_?

  def siteMap: SiteMap = _menu.siteMap

  def createDefaultLink: Option[NodeSeq] = 
    currentValue.flatMap(p => link.createLink(p)).toOption.
    map(ns => Text(appendQueryParameters(ns.text, currentValue)))


  def createLink(in: T): Option[NodeSeq] = link.createLink(in).toOption.
  map(ns => Text(appendQueryParameters(ns.text, Full(in))))

  override def toString = "Loc("+name+", "+link+", "+text+", "+params+")"

  type LocRewrite =  Box[PartialFunction[RewriteRequest, (RewriteResponse, T)]]

  def rewrite: LocRewrite = Empty

  def rewritePF: Box[LiftRules.RewritePF] = rewrite.map(
    rw =>
    new NamedPartialFunction[RewriteRequest, RewriteResponse] {
      def functionName = rw match {
        case rw: NamedPartialFunction[_, _] => rw.functionName
        case _ => "Unnamed"
      }

      def isDefinedAt(in: RewriteRequest) = rw.isDefinedAt(in)

      def apply(in: RewriteRequest): RewriteResponse = {
        val (ret, param) = rw.apply(in)
        requestValue.set(Full(param))
        ret
      }
    }
  )

  type SnippetTest = PartialFunction[(String, Box[T]), NodeSeq => NodeSeq]

  def snippets: SnippetTest = Map.empty

  /**
   * Is the Loc marked as Stateless (this will force rendering of
   * the page into stateless mode)
   */
  def stateless_? : Boolean = 
    if (Props.devMode) (calcStateless() || reqCalcStateless())
    else (_frozenStateless || reqCalcStateless())

  /**
   * A lazy val used to track statelessness for non-dev mode.
   * By default, it calls calcStateless().
   */
  protected lazy val _frozenStateless = calcStateless()

  /**
   * The method to calculate if this Loc is stateless.  By default
   * looks for the Loc.Stateless Param
   */
  protected def calcStateless(): Boolean = allParams.find {
    case Loc.Stateless => true
    case _ => false
  }.isDefined

  /**
   * Find the stateless calculation Loc params
   */
  protected def findStatelessCalc: (Box[Loc.CalcStateless], Box[Loc.CalcParamStateless[T]]) = (allParams.collect {
    case v @ Loc.CalcStateless(_) => v
  }.headOption,
    allParams.collect {
    case v @ Loc.CalcParamStateless(_) => v
  }.headOption)

  /**
   * The cached Loc params
   */
  protected lazy val _foundStatelessCalc: (Box[Loc.CalcStateless], Box[Loc.CalcParamStateless[T]]) = findStatelessCalc

  protected def foundStatelessCalc: (Box[Loc.CalcStateless], Box[Loc.CalcParamStateless[T]])  =
  if (Props.devMode) findStatelessCalc else _foundStatelessCalc

  /**
   * run the stateless calculation
   */
  protected def reqCalcStateless(): Boolean = {
    val (np, param) = foundStatelessCalc
    (np.map(_.f()) or param.map(_.f(currentValue))) openOr false
  }

  lazy val calcSnippets: SnippetTest = {
    def buildPF(in: Loc.Snippet): PartialFunction[String, NodeSeq => NodeSeq] = {
      new PartialFunction[String, NodeSeq => NodeSeq] {
        def isDefinedAt(s: String) = s == in.name
        def apply(s: String): NodeSeq => NodeSeq = {
          if (isDefinedAt(s)) in.func
          else throw new MatchError()
        }
      }
    }

    val singles = (
      allParams.flatMap{ case v: Loc.Snippet => Some(v);     case _ => None }.toList.map(buildPF) :::
      allParams.flatMap{ case v: Loc.LocSnippets => Some(v); case _ => None }.toList
    )

    if (singles.isEmpty) Map.empty
    else {
      val func: PartialFunction[String, NodeSeq => NodeSeq] = singles match {
        case pf :: Nil => pf
        case pfs => pfs.reduceLeft[PartialFunction[String, NodeSeq => NodeSeq]](_ orElse _)
      }

      new SnippetTest {
        def isDefinedAt(in: (String, Box[T])): Boolean = func.isDefinedAt(in._1)
        def apply(in: (String, Box[T])): NodeSeq => NodeSeq = func.apply(in._1)
      }
    }
  }

  def snippet(name: String): Box[NodeSeq => NodeSeq] = {
    val test = (name, currentValue)

    if ((snippets orElse calcSnippets).isDefinedAt(test)) Full((snippets orElse calcSnippets)(test))
    else Empty
  }

  protected object accessTestRes extends RequestVar[Either[Boolean, Box[() => LiftResponse]]](_testAccess) {
    override val __nameSalt = randomString(10)
  }

  def testAccess: Either[Boolean, Box[() => LiftResponse]] = accessTestRes.is

  protected def _testAccess: Either[Boolean, Box[() => LiftResponse]] = {
    def testParams(what: List[Loc.LocParam[T]]): Either[Boolean, Box[() => LiftResponse]] = what match {
      case Nil => Left(true)

      case Loc.If(test, msg) :: xs => if (!test()) Right(Full(msg)) else testParams(xs)
      case Loc.IfValue(test, msg) :: xs => if (!test(currentValue)) Right(Full(msg)) else testParams(xs)

      case Loc.Unless(test, msg) :: xs => if (test()) Right(Full(msg)) else testParams(xs)
      case Loc.UnlessValue(test, msg) :: xs => if (test(currentValue)) Right(Full(msg)) else testParams(xs)

      case Loc.TestAccess(func) :: xs =>
        func() match {
          case Full(resp) => Right(Full(() => resp))
          case _ => testParams(xs)
        }

      case Loc.TestValueAccess(func) :: xs =>
        func(currentValue) match {
          case Full(resp) => Right(Full(() => resp))
          case _ => testParams(xs)
        }

      case x :: xs => testParams(xs)
    }

    testParams(allParams) match {
      case Left(true) => _menu.testParentAccess
      case x => x
    }
  }

  def earlyResponse: Box[LiftResponse] = {
    def early(what: List[Loc.LocParam[T]]): Box[LiftResponse] = what match {
      case Nil => Empty

      case Loc.EarlyResponse(func) :: xs =>
        func() match {
          case Full(r) => Full(r)
          case _ => early(xs)
        }

      case x :: xs => early(xs)
    }

    early(allParams)
  }

  /**
   * This method can be overridden to provide a template for this Loc
   */
  def calcTemplate: Box[NodeSeq] = Empty

  /**
   * The first Loc.Template or Loc.ValueTemplate in the param list.
   */
  def paramTemplate: Box[NodeSeq] =
    allParams.flatMap {
      case Loc.Template(f) => Some(f());
      case Loc.ValueTemplate(f) => Some(f(currentValue));
      case Loc.TemplateBox(f) => f()
      case Loc.ValueTemplateBox(f) => f(currentValue)
      case _ => None
    }.headOption

  /**
   * The template assocaited with this Loc, if any. Any Loc.Template
   * or Loc.ValueTemplate parameter will take precedence over a value returned
   * by the calcTemplate method.
   */
  def template: Box[NodeSeq] = paramTemplate or calcTemplate

  /**
   * The first Loc.Title in the param list.
   */
  lazy val paramTitle: Box[T => NodeSeq] =
    allParams.flatMap {
      case Loc.Title(f) => Some(f);
      case _ => None
    }.headOption

  /**
   * The title to be displayed for the value associated with this Loc.
   * Any Loc.Title parameter will take precedence over the
   * value returned by the linkText method.
   */
  def title(in: T): NodeSeq = paramTitle.map(_.apply(in)) openOr linkText(in)

  /**
   * The title of the location given the current value associated with this Loc.
   * If no current value is available, this will use the name of this Loc
   * as the title.
   */
  def title: NodeSeq = currentValue.map(title _) openOr Text(name)

  /**
   * The link text to be displayed for a value of type T
   */
  def linkText(in: T): NodeSeq = text.text(in)

  /**
   * The title of the location given the current value associated with this Loc
   */
  def linkText: Box[NodeSeq] = currentValue.map(linkText _)

  private var _menu: Menu = _

  private[sitemap] def menu_=(m: Menu) {
    _menu = m
    m.siteMap.addLoc(this)
  }

  def menu = _menu

  private def testAllParams(params: List[Loc.LocParam[T]], req: Req): Boolean = {
    params.forall {
      case Loc.Test(test) => test(req)
      case _ => true
    }
  }

  def doesMatch_?(req: Req): Boolean = {
    (if (link.isDefinedAt(req)) {
      link(req) match {
        case Full(x) if testAllParams(allParams, req) => x
        case Full(x) => false
        case x => x.openOr(false)
      }
    } else false) && currentValue.isDefined
    // the loc only matches if we've got a current value
  }

  def breadCrumbs: List[Loc[_]] = _menu.breadCrumbs ::: List(this)

  def buildKidMenuItems(kids: Seq[Menu]): List[MenuItem] = {
    kids.toList.flatMap(_.loc.buildItem(Nil, false, false)) ::: supplimentalKidMenuItems
  }

  def supplimentalKidMenuItems: List[MenuItem] =
    for {
      p <- childValues
      l <- link.createLink(p).map(appendQueryParams(p))
    } yield MenuItem(
        text.text(p),
        l, Nil, false, false,
        allParams.flatMap {
          case v: Loc.LocInfo[_] => List(v())
          case _ => Nil
        }
      )

  def buildMenu: CompleteMenu = {
    CompleteMenu(_menu.buildUpperLines(_menu, _menu, buildKidMenuItems(_menu.kids)))
  }

  private[liftweb] def buildItem(kids: List[MenuItem], current: Boolean, path: Boolean): Box[MenuItem] =
    (calcHidden(kids), testAccess) match {
      case (false, Left(true)) => {
          for {
            p <- currentValue
            t <- link.createLink(p).map(appendQueryParams(p))
          } yield new MenuItem(
            text.text(p),
            t, kids, current, path,
            allParams.flatMap {
              case v: Loc.LocInfo[_] => List(v())
              case _ => Nil
            },
            placeHolder_?, this
          )
        }

      case _ => Empty
    }

  protected def calcHidden(kids: List[MenuItem]) = hidden || (hideIfNoKids_? && kids.isEmpty)

  private lazy val _hidden = allParams.contains(Loc.Hidden)

  def hidden = _hidden

  private lazy val groupSet: Set[String] =
    Set(allParams.flatMap{case s: Loc.LocGroup => s.group case _ => Nil} :_*)

  def inGroup_?(group: String): Boolean = groupSet.contains(group)

  def init() {
    params.foreach(_ onCreate(this))
  }

}

trait ConvertableLoc[T] {
  self: Loc[T] =>
    
  /**
   * Converts the String to T that can then be sent to
   * the Loc in createLink
   */
  def convert(str: String): Box[T]
}


/**
 * The Loc companion object, complete with a nice constructor
 */
object Loc {
  type FailMsg = () => LiftResponse

  /**
   * Create a Loc (Location) instance
   *
   * @param name -- the name of the location.  This must be unique across your entire sitemap.
   * It's used to look up a menu item in order to create a link to the menu on a page.
   * @param link -- the Link to the page
   * @param text -- the text to display when the link is displayed
   * @param params -- access test, title calculation, etc.
   */
  def apply(name: String, link: Link[Unit], text: LinkText[Unit], params: LocParam[Unit]*): Loc[Unit] = UnitLoc(name, link, text, params.toList)
  def apply(name: String, link: Link[Unit], text: LinkText[Unit], params: List[LocParam[Unit]]): Loc[Unit] = UnitLoc(name, link, text, params)

  private final case class UnitLoc(
    override val name: String,
    override val link: Link[Unit],
    override val text: LinkText[Unit],
    override val params: List[LocParam[Unit]]
  ) extends Loc[Unit] {
    override def defaultValue: Box[Unit] = Full(())

    init()
  }

  case class DataLoc[T](
    override val name: String,
    override val link: Link[T],
    override val text: LinkText[T],
    override val defaultValue: Box[T],
    xparams: LocParam[T]*
  ) extends Loc[T] {
    override val params = xparams.toList

    init()
  }


  /**
   * Algebraic data type for parameters that modify handling of a Loc
   * in a SiteMap
   */
  trait LocParam[-T] {
    def onCreate(loc: Loc[_]){
    }
  }

  /**
   * A type alias for LocParam instances that are applicable to any Loc
   */
  type AnyLocParam = LocParam[Any]

  /**
   * Indicates that the path denominated by Loc requires HTTP authentication
   * and only a user assigned to this role or to a role that is child-of this role
   * can access it.
   */
  case class HttpAuthProtected(role: (Req) => Box[Role]) extends AnyLocParam {

    override def onCreate(loc: Loc[_]) {
      LiftRules.httpAuthProtectedResource.append(
        new LiftRules.HttpAuthProtectedResourcePF() {
          def isDefinedAt(in: Req) = in.path.partPath == loc.link.uriList
          def apply(in: Req): Box[Role] = role(in)
        }
      )
    }
  }

  /**
   * Allows you to generate an early response for the location rather than
   * going through the whole Lift XHTML rendering pipeline
   */
  case class EarlyResponse(func: () => Box[LiftResponse]) extends AnyLocParam

  /**
   * Tests to see if the request actually matches the requirements for access to
   * the page.  For example, if a parameter is missing from the request, this
   * is a good way to restrict access to the page.
   */
  case class Test(test: Req => Boolean) extends AnyLocParam

  /**
   * If the test returns True, the page can be accessed, otherwise,
   * the result of FailMsg will be sent as a response to the browser.
   * If the Loc cannot be accessed, it will not be displayed in menus.
   *
   * @param test -- the function that tests access to the page
   * @param failMsg -- what to return the the browser (e.g., 304, etc.) if
   * the page is accessed.
   */
  case class If(test: () => Boolean, failMsg: FailMsg) extends AnyLocParam
  case class IfValue[T](test: Box[T] => Boolean, failMsg: FailMsg) extends LocParam[T]

  /**
   * MenuCssClass is used to add css to the Menu node.  The css allows for
   * replacing menu with an icon and other super-fun and helpful things.
   * cssClass is a StringFunc which can either be a String constant or
   * a Function that returns a String.  Thus, you can compute the
   * css based on the current state or you can have a constant.  Syntactically
   * you can use either:
   * <pre>
   * MenuCssClass("foobar")
   * MenuCssClass(() => calculateCssForMyMenuItem())
   * </pre>
   */
  case class MenuCssClass(cssClass: StringFunc) extends AnyLocParam

  /**
   * Unless the test returns True, the page can be accessed, otherwise,
   * the result of FailMsg will be sent as a response to the browser.
   * If the Loc cannot be accessed, it will not be displayed in menus.
   *
   * @param test -- the function that tests access to the page
   * @param failMsg -- what to return the the browser (e.g., 304, etc.) if
   * the page is accessed.
   */
  case class Unless(test: () => Boolean, failMsg: FailMsg) extends AnyLocParam
  case class UnlessValue[T](test: Box[T] => Boolean, failMsg: FailMsg) extends LocParam[T]

  /**
   * Allows extra access testing for a given menu location such that
   * you can generically return a response during access control
   * testing
   */
  case class TestAccess(func: () => Box[LiftResponse]) extends AnyLocParam
  case class TestValueAccess[T](func: Box[T] => Box[LiftResponse]) extends LocParam[T]

  /**
   * Allows a user to specify a template based upon a function from the current
   * value encapsulated in the Loc
   */
  case class Template(template: () => NodeSeq) extends AnyLocParam
  case class ValueTemplate[T](template: Box[T] => NodeSeq) extends LocParam[T]

    /**
   * Allows a user to specify a template based upon a function from the current
   * value encapsulated in the Loc.  Allow the return of Box[NodeSeq] so that it's more
     * friendly to Templates.
   */
  case class TemplateBox(template: () => Box[NodeSeq]) extends AnyLocParam
  case class ValueTemplateBox[T](template: Box[T] => Box[NodeSeq]) extends LocParam[T]

  /**
   * This LocParam may be used to specify a function that calculates a title for the page
   * based upon the current value encapsulated by this Loc.
   */
  case class Title[T](title: T => NodeSeq) extends LocParam[T]

  /**
   * If the Loc is in a group (or groups) like "legal" "community" etc.
   * the groups can be specified and recalled at the top level
   */
  case class LocGroup(group: String*) extends AnyLocParam
  
  /**
  * Calculate the value for the Loc.  This is useful for parameterized
  * menus.  It allows you to calculate the value of the Loc.
  */
  case class CalcValue[T](func: () => Box[T]) extends LocParam[T]
  
  /**
  * The value of Loc
  */
  case class Value[T](value: T) extends LocParam[T]

  /**
   * An extension point for adding arbitrary lazy values to a Loc.
   */
  trait LocInfo[X] extends AnyLocParam {
    def apply(): Box[() => X]
  }

  /**
   * A single snippet that's assocaited with a given location... the snippet
   * name and the snippet function'
   */
  class Snippet(val name: String, _func: => NodeSeq => NodeSeq) extends AnyLocParam {
    /**
     * The NodeSeq => NodeSeq function 
     */
    def func: NodeSeq => NodeSeq = _func
  }

  object Snippet {

    /**
     * A trait that does nothing other than allow the disabiguation of two different call-by-name parameters
     * for apply()
     */
    trait CallByNameDispatchSnippet

    /**
     * Vend the trait that does nothing other than allow the disabiguation of two different call-by-name parameters
     * for apply()
     */
    implicit def vendCallByNameDispatchSnippet: CallByNameDispatchSnippet = new CallByNameDispatchSnippet {}

    /**
     * Build a Loc.Snippet instance out of a name and a DispatchSnippet (or StatefulSnippet, LiftScreen or Wizard).
     * The "render" method will be invoked on the Dispatch snippet
     */
    def apply(name: String, snippet: => DispatchSnippet)(implicit disambiguate: CallByNameDispatchSnippet): Snippet =
      new Snippet(name, ns => snippet.dispatch("render")(ns)) // Issue #919

    /**
     * Build a Loc.Snippet instance for a given name and a function.  Note that the function is call-by-name
     * so that it will be created each time it's used.  This is useful for CSS Selector Transforms
     */
    def apply(name: String, func: => NodeSeq => NodeSeq): Snippet = new Snippet(name, func)
    def unapply(in: Snippet): Option[(String, NodeSeq => NodeSeq)] = Some(in.name -> in.func)
  }

  /**
   * Allows you to create a handler for many snippets that are associated with
   * a Loc
   */
  trait LocSnippets extends PartialFunction[String, NodeSeq => NodeSeq] with AnyLocParam

  /**
   * If this parameter is included, the item will not be visible in the menu, but
   * will still be accessable.
   */
  case object Hidden extends AnyLocParam

  /**
   * If this is a submenu, use the parent Loc's params
   */
  case class UseParentParams() extends AnyLocParam

  /**
   * Calculate additional query parameters to add as a query
   * string to the Loc
   */
  case class QueryParameters(f: () => List[(String, String)]) extends AnyLocParam

  /**
   * Calculate additional query parameters to add as a query
   * string to the Loc
   */
  case class LocQueryParameters[T](f: Box[T] => List[(String, String)]) extends LocParam[T]


  /**
   * If the Loc has no children, hide the Loc itself
   */
  case object HideIfNoKids extends AnyLocParam

  /**
   * Is the Loc a stateless Loc... it will be served
   * in stateless mode
   */
  case object Stateless extends AnyLocParam



  /**
   * The Loc does not represent a menu itself, but is the parent menu for
   * children (implies HideIfNoKids)
   */
  case object PlaceHolder extends AnyLocParam

  /**
   * Extension point for user-defined LocParam instances.
   */
  trait UserLocParam[-T] extends LocParam[T]

  /**
   * A function that calculates the statelessness of the Loc for the given request
   */
  case class CalcStateless(f: () => Boolean) extends AnyLocParam

  /**
   * A function that calculates the statelessness of the Loc for the given request
   * with the parameterized type passed into the function
   */
  case class CalcParamStateless[-T](f: Box[T] => Boolean) extends LocParam[T]

  /**
   * A subclass of LocSnippets with a built in dispatch method (no need to
   * implement isDefinedAt or apply... just
   * def dispatch: PartialFunction[String, NodeSeq => NodeSeq]
   */
  trait DispatchLocSnippets extends LocSnippets {
    def dispatch: PartialFunction[String, NodeSeq => NodeSeq]

    def isDefinedAt(n: String) = dispatch.isDefinedAt(n)

    def apply(n: String) = dispatch.apply(n)
  }

  /**
   * A function that can be used to calculate the link text from the current
   * value encapsulated by the Loc.
   */
  case class LinkText[-T](text: T => NodeSeq)

  /**
  * The companion object to LinkText that contains some helpful implicit conversion
  */
  object LinkText {
    implicit def nodeSeqToLinkText[T](in: => NodeSeq): LinkText[T] = LinkText[T](T => in)
    implicit def strToLinkText[T](in: => String): LinkText[T] = LinkText(T => Text(in))
  }

  /**
   * This defines the Link to the Loc.
   *
   * @param uriList -- the URL to match
   *
   * @param matchHead_? -- false -- absolute match.  true -- match anything
   * that begins with the same path.  Useful for opening a set of directories
   * (for example, help pages)
   */
  class Link[-T](val uriList: List[String], val matchHead_? : Boolean) extends PartialFunction[Req, Box[Boolean]] {
    def this(b: List[String]) = this(b, false)

    def isDefinedAt(req: Req): Boolean = {
      if (matchHead_?) req.path.partPath.take(uriList.length) == uriList
      else uriList == req.path.partPath
    }

    /**
     * Is the Loc external
     */
    def external_? = false

    def apply(in: Req): Box[Boolean] = {
      if (isDefinedAt(in)) Full(true)
      else throw new MatchError("Failed for Link "+uriList)
    }

    /**
     * Override this method to modify the uriList with data from the Loc's value
     */
    def pathList(value: T): List[String] = uriList

    /**
     * Creates a string representation of the path to the Loc.
     */
    def createPath(value: T): String = {
      val path: List[String] = pathList(value).map(Helpers.urlEncode)

      if (matchHead_?) {
        path.mkString("/", "/", "/")
      } else if (SiteMap.rawIndex_? && path == List("index")) {
        "/"
      } else if (path.length > 1 && path.last == "index") {
        path.dropRight(1).mkString("/", "/", "/")
      } else {
        path.mkString("/", "/", "")
      }
    }

    /**
     * Returns the value created by createPath wrapped in a boxed scala.xml.Text element.
     * NOTE: This does not create a clickable HTML link on its own!
     */
    def createLink(value: T): Box[NodeSeq] = Full(Text(createPath(value)))
  }

  object Link {
    def apply(urlLst: List[String], matchHead_? : Boolean, url: String) = {
      new Link[Unit](urlLst, matchHead_?) {
        override def createLink(value: Unit): Box[NodeSeq] = Full(Text(url))
      }
    }

    implicit def strLstToLink(in: Seq[String]): Link[Unit] = new Link[Unit](in.toList)
    implicit def strPairToLink(in: (Seq[String], Boolean)): Link[Unit] = new Link[Unit](in._1.toList, in._2)
  }

  object ExtLink {
    def apply(url: String) = new Link[Unit](Nil, false) {
      override def createLink(value: Unit): Box[NodeSeq] = Full(Text(url))

      /**
       * Is the Loc external
       */
      override def external_? = true
    }
  }

  // @deprecated def alwaysTrue(a: Req) = true
  // @deprecated def retString(toRet: String)(other: Seq[(String, String)]) = Full(toRet)

  implicit def strToFailMsg(in: => String): FailMsg = () => {
    RedirectWithState(
      LiftRules.siteMapFailRedirectLocation.mkString("/", "/", ""),
      RedirectState(Empty, in -> NoticeType.Error)
    )
  }

  implicit def strFuncToFailMsg(in: () => String): FailMsg = strToFailMsg(in())

  implicit def redirectToFailMsg(in: => RedirectResponse): FailMsg = () => in
}

case class CompleteMenu(lines: Seq[MenuItem]) {
  lazy val breadCrumbs: Seq[MenuItem] = lines.flatMap(_.breadCrumbs)
}

case class MenuItem(text: NodeSeq, uri: NodeSeq,  kids: Seq[MenuItem],
                    current: Boolean,
                    path: Boolean,
                    info: List[Box[() => _]]) {

  private var _placeholder = false
  def placeholder_? = _placeholder

  private var _cssClass: Box[String] = Empty
  def cssClass: Box[String] = _cssClass

  def this(text: NodeSeq, uri: NodeSeq,  kids: Seq[MenuItem],
           current: Boolean,
           path: Boolean,
           info: List[Box[() => _]],
           ph: Boolean) = {
    this(text, uri, kids, current, path, info)
    _placeholder = ph
  }

  def this(text: NodeSeq, uri: NodeSeq,  kids: Seq[MenuItem],
           current: Boolean,
           path: Boolean,
           info: List[Box[() => _]],
           ph: Boolean,
           loc: Loc[_]) = {
    this(text, uri, kids, current, path, info)
    _placeholder = ph
    _cssClass = loc.cssClassForMenuItem
  }

  def breadCrumbs: Seq[MenuItem] = {
    if (!path) Nil
    else this :: kids.toList.flatMap(_.breadCrumbs)
  }
}

Other Lift Framework examples (source code examples)

Here is a short list of links related to this Lift Framework Loc.scala source code file:



my book on functional programming

 

new blog posts

 

Copyright 1998-2019 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.