diff --git a/build.sbt b/build.sbt
index 2b6ef8be2..c72048467 100644
--- a/build.sbt
+++ b/build.sbt
@@ -132,7 +132,7 @@ lazy val scalatraScalate = Project(
libraryDependencies += scalate,
description := "Scalate integration with Scalatra"
)
-) dependsOn(scalatraCore % "compile;test->test;provided->provided")
+) dependsOn(scalatraCore % "compile;test->test;provided->provided")
lazy val scalatraJson = Project(
id = "scalatra-json",
diff --git a/scalate/src/main/scala/org/scalatra/scalate/ScalatraFormsHelpers.scala b/scalate/src/main/scala/org/scalatra/scalate/ScalatraFormsHelpers.scala
new file mode 100644
index 000000000..c810962d2
--- /dev/null
+++ b/scalate/src/main/scala/org/scalatra/scalate/ScalatraFormsHelpers.scala
@@ -0,0 +1,101 @@
+package org.scalatra.scalate
+
+import org.fusesource.scalate.servlet.ServletRenderContext
+import org.scalatra.MultiParams
+
+/**
+ * Supplies helper methods to render forms in Scalate templates.
+ */
+trait ScalatraFormsHelpers { self: ServletRenderContext =>
+
+ private val RequestAttributeParamsKey = "org.scalatra.forms.params"
+ private val RequestAttributeErrorsKey = "org.scalatra.forms.errors"
+
+ /**
+ * Render a text field.
+ */
+ def text(name: String, attributes: (String, String)*): Unit = {
+ unescape(s"""""")
+ }
+
+ /**
+ * Render a password field.
+ */
+ def password(name: String, attributes: (String, String)*): Unit = {
+ unescape(s"""""")
+ }
+
+ /**
+ * Render a textarea.
+ */
+ def textarea(name: String, attributes: (String, String)*): Unit = {
+ unescape(s"""""")
+ }
+
+ /**
+ * Render a checkbox.
+ */
+ def checkbox(name: String, value: String, attributes: (String, String)*): Unit = {
+ val checked = if (params(name).contains(value)) "checked" else ""
+ unescape(s"""""")
+ }
+
+ /**
+ * Render a radio button.
+ */
+ def radio(name: String, value: String, attributes: (String, String)*): Unit = {
+ val checked = if (param(name) == value) "checked" else ""
+ unescape(s"""""")
+ }
+
+ /**
+ * Render a select box.
+ */
+ def select(name: String, values: Seq[(String, String)], multiple: Boolean, attributes: (String, String)*): Unit = {
+ val sb = new StringBuilder()
+ sb.append(s"""")
+ unescape(sb.toString)
+ }
+
+ /**
+ * Retrieve an error message of the specified field.
+ */
+ def error(name: String): Option[String] = {
+ Option(request.getAttribute(RequestAttributeErrorsKey)).flatMap { errors =>
+ errors.asInstanceOf[Seq[(String, String)]].find(_._1 == name).map(_._2)
+ }
+ }
+
+ /**
+ * Retrieve all error messages of the specified field.
+ */
+ def errors(name: String): Seq[String] = {
+ Option(request.getAttribute(RequestAttributeErrorsKey)).map { errors =>
+ errors.asInstanceOf[Seq[(String, String)]].collect { case error if error._1 == name => error._2 }
+ }.getOrElse(Nil)
+ }
+
+ private def escape(value: String): String = {
+ value.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """)
+ }
+
+ private def params(name: String): Seq[String] = {
+ Option(request.getAttribute(RequestAttributeParamsKey)).flatMap { params =>
+ params.asInstanceOf[MultiParams].get(name)
+ }.getOrElse(Nil)
+ }
+
+ private def param(name: String): String = {
+ params(name).headOption.getOrElse("")
+ }
+
+ private def attrs(attrs: (String, String)*): String = {
+ attrs.map { case (name, value) => s"""${escape(name)}="${escape(value)}"""" }.mkString(" ")
+ }
+}
diff --git a/scalate/src/main/scala/org/scalatra/scalate/ScalatraRenderContext.scala b/scalate/src/main/scala/org/scalatra/scalate/ScalatraRenderContext.scala
index d08c29436..6cda85fa6 100644
--- a/scalate/src/main/scala/org/scalatra/scalate/ScalatraRenderContext.scala
+++ b/scalate/src/main/scala/org/scalatra/scalate/ScalatraRenderContext.scala
@@ -17,7 +17,7 @@ class ScalatraRenderContext(
engine: TemplateEngine,
out: PrintWriter,
req: HttpServletRequest,
- res: HttpServletResponse) extends ServletRenderContext(engine, out, req, res, kernel.servletContext) {
+ res: HttpServletResponse) extends ServletRenderContext(engine, out, req, res, kernel.servletContext) with ScalatraFormsHelpers {
def flash: scala.collection.Map[String, Any] = kernel match {
case flashMapSupport: FlashMapSupport => flashMapSupport.flash(request)