All Downloads are FREE. Search and download functionalities are using the official Maven repository.

skinny.controller.feature.ScalateTemplateEngineFeature.scala Maven / Gradle / Ivy

There is a newer version: 2.5.2
Show newest version
package skinny.controller.feature

import org.fusesource.scalate.TemplateEngine
import org.fusesource.scalate.layout.DefaultLayoutStrategy
import skinny._
import skinny.micro.context.SkinnyContext
import skinny.micro.contrib.ScalateSupport
import skinny.micro.contrib.scalate.SkinnyScalateRenderContext

import scala.annotation.tailrec
import java.io.PrintWriter
import java.text.DecimalFormat

/**
 * Scalate implementation of TemplateEngineSupport.
 *
 * This is basically same as Scalatra's Scalate support, but the convention of template file path is inspired by Ruby on Rails.
 *
 * {{{
 *   render("/members/index")
 * }}}
 *
 * The above code expects "src/main/webapp/WEB-INF/views/members/index.html.ssp" by default.
 *
 * If you need to use scaml instead,
 *
 * {{{
 *   override lazy val scalateExtension: String = "scaml"
 * }}}
 *
 * And then, Skinny expects "src/main/webapp/WEB-INF/views/members/index.html.scaml"
 */
trait ScalateTemplateEngineFeature extends TemplateEngineFeature
    with ScalateSupport {
  //with ScalateUrlGeneratorSupport {

  /**
   * To deal with exceptions.
   *
   * http://www.scalatra.org/guides/views/scalate.html#toc_303
   */
  override def isScalateErrorPageEnabled = false

  /**
   * Overrides to make the template path simpler.
   *
   * @return paths
   */
  override protected def defaultTemplatePath: List[String] = List("/WEB-INF/views")

  /**
   * Creates TemplateEngine instance for Skinny app.
   *
   * @param config configuration
   * @return TemplateEngine instance
   */
  override protected def createTemplateEngine(config: ConfigT) = {
    val engine = super.createTemplateEngine(config)
    engine.layoutStrategy = new DefaultLayoutStrategy(engine, TemplateEngine.templateTypes.map("/WEB-INF/layouts/default." + _): _*)
    engine.packagePrefix = "templates"
    engine
  }

  /**
   * Creates a RenderContext instance for Skinny app.
   */
  override protected def createRenderContext(out: PrintWriter)(
    implicit
    ctx: SkinnyContext
  ): SkinnyScalateRenderContext = {
    val context = super.createRenderContext(out)(ctx)
    context.numberFormat = scalateRenderContextNumberFormat
    context
  }

  protected def createSkinnyScalateRenderContext(ctx: SkinnyContext): SkinnyScalateRenderContext = {
    createRenderContext(ctx.response.getWriter)(ctx)
      .asInstanceOf[SkinnyScalateRenderContext]
  }

  /**
   * Creates a DecimalFormat instance to be use by default.
   */
  def scalateRenderContextNumberFormat: DecimalFormat = {
    val df = new DecimalFormat
    df.setGroupingUsed(false) // prevent commas from being inserted into numbers
    df
  }

  /**
   * Scalate extension names. Skinny will search for templates in this order.
   *
   * If you'd like to change the search order, or you want to
   * restrict the search to fewer template languages, override this attribute.
   *
   * Note that removing unnecessary items from this list will improve the
   * performance of the template engine.
   */
  def scalateExtensions: List[String] = List("ssp", "jade", "scaml", "mustache")

  /**
   * The Scalate template type that is searched for first.
   */
  def preferredScalateExtension = scalateExtensions.head

  /**
   * Returns the actual template path for the name.
   *
   * @param path path name
   * @param format format (HTML,JSON,XML...)
   * @return actual path
   */
  override protected def templatePaths(path: String)(implicit format: Format = Format.HTML): List[String] = {
    scalateExtensions.map(toTemplatePath(path, format, _))
  }

  /**
   * Predicates the template path is available.
   *
   * @param path path name
   * @param format format (HTML,JSON,XML...)
   * @return true/false
   */
  override protected def templateExists(path: String)(implicit format: Format = Format.HTML): Boolean = {
    val exists = findFirstAvailableTemplate(templatePaths(path)).isDefined
    if ((SkinnyEnv.isDevelopment() || SkinnyEnv.isTest()) && !exists && format == Format.HTML) {
      generateWelcomePageIfAbsent(path)
      true
    } else {
      exists
    }
  }

  @tailrec
  private def findFirstAvailableTemplate(templatePaths: List[String]): Option[String] = templatePaths match {
    case Nil => None
    case p :: ps => {
      val t = findTemplate(p)
      if (t.isDefined) t else findFirstAvailableTemplate(ps)
    }
  }

  /**
   * Generates a sample page for absent page.
   */
  protected def generateWelcomePageIfAbsent(path: String)(implicit format: Format = Format.HTML): Unit = {
    import org.apache.commons.io.FileUtils
    import java.io.File
    val filePath = servletContext.getRealPath(s"/WEB-INF/views/${toTemplatePath(path, format, preferredScalateExtension)}")
    val file = new File(filePath)
    val code: String = preferredScalateExtension match {
      case "jade" =>
        """h3 Welcome
          |hr
          |p(class="alert alert-success")
          |  b TODO:
          |  | This is an auto-generated file by
          |  a(href="http://skinny-framework.org/") Skinny framework
          |  |!
          |""".stripMargin
      case "scaml" =>
        """%h3 Welcome
          |%hr
          |p(class="alert alert-success")
          |  %b TODO:
          |  This is an auto-generated file by
          |  %a(href="http://skinny-framework.org/") Skinny framework
          |  !
          |""".stripMargin
      case _ =>
        """

Welcome

|
|

| TODO: This is an auto-generated file by Skinny framework!
|

|""".stripMargin } FileUtils.write(file, code) } /** * Renders body with template. * * @param path path name * @param format format (HTML,JSON,XML...) * @return true/false */ override protected def renderWithTemplate(path: String)( implicit ctx: SkinnyContext, format: Format = Format.HTML ): String = { // Note: templateExists() should already have been called, so we know we have an actual template val templatePath = findFirstAvailableTemplate(templatePaths(path)).getOrElse("missing-template") layoutTemplate(templatePath, requestScope(ctx).toMap.toSeq: _*)(ctx) } /** * Replaces layout template for this action. * * @param path the layout template path, including extension, e.g. "custom.jade" */ def layout(path: String)(implicit ctx: SkinnyContext): ScalateTemplateEngineFeature = { if (request(ctx) != null) { val _path = path.replaceFirst("^/", "").replaceAll("//", "/").replaceFirst("/$", "") templateAttributes(ctx) += ("layout" -> s"/WEB-INF/layouts/${_path}") this } else { // TODO this method doesn't work in beforeAction, more investigation logger.warn("You cannot specify layout here. Put this into action methods.") this } } protected def toTemplatePath(path: String, format: Format, ext: String): String = { s"${path}.${format.name}.${ext}".replaceAll("//", "/").replaceFirst("^/", "") } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy