package com.matygo.controllers
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import scala.collection.JavaConversions.mapAsScalaMap
import org.scalatra.ScalatraKernel.MultiParamsKey
import org.scalatra.ContentTypeInferrer
import org.scalatra.util.MultiMap
import org.scalatra.CookieSupport
import org.scalatra.RouteMatcher
import org.scalatra.ScalatraFilter
import org.squeryl.PrimitiveTypeMode.transaction
import com.matygo.csv.CSVBuilder
import com.matygo.exceptions.NotLoggedInException
import com.matygo.flow.ExceptionResponse
import com.matygo.flow.FlowRequest
import com.matygo.flow.Render
import com.matygo.implicits.MatygoImplicits
import com.matygo.models.system.MatygoFile
import com.matygo.models.users.User
import com.matygo.models.SproutcoreModel
import com.matygo.responses.Destroyed.DestroyResponse
import com.matygo.responses.Destroyed
import com.matygo.util.MLogFunctions
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import net.liftweb.json.JsonAST.JObject
import net.liftweb.json.JsonAST.JValue
import net.liftweb.json.JsonDSL._
import net.liftweb.json.Printer.compact
import net.liftweb.json.JsonAST
import com.matygo.models.Environment
import com.matygo.models.Environments
import net.liftweb.json.JsonAST.JString
import net.liftweb.json.JsonAST.JArray
import com.matygo.util.SQLUtils
import org.scalatra.FlashMapSupport
import com.matygo.exceptions.NotAuthorizedException
import com.matygo.exceptions.NotFoundException
import org.scalatra.SweetCookies
import org.scalatra.CookieOptions
class MatygoController extends ScalatraFilter with CookieSupport with MatygoImplicits with MLogFunctions with MatygoAuthentication with SQLUtils with FlashMapSupport {
override def routeBasePath = "/"
def json2String(json: JValue) = compact(JsonAST.render(("content" -> json)))
protected def currentUser: Option[User] = {
val user = userFromRequest
if (!(renderLoggedOutContent || allowsLoggedOutContent) && user.isEmpty) throw new NotLoggedInException
user
}
// bindings
protected def bindings = request.getOrElseUpdate("bindings", scala.collection.mutable.Map()).asInstanceOf[scala.collection.mutable.Map[String, Any]]
private def renderLoggedOutContent = request.get("rendersLoggedOutContent") match {
case Some(_) => true
case None => false
}
def rendersLoggedOutContent = {
request("rendersLoggedOutContent") = Boolean.box(true)
}
def allowsLoggedOutContent = false
// Scalatra Kernel Overrides
def kernelName = this.toString
// def servletContext = dispatcher.servletContext
// protected override def doNotFound: Action = () => {
// response.setStatus(404)
// response.getWriter println "Requesting %s but only have %s".format(request.getRequestURI, routes)
// }
// def requestPath = if (dispatcher.request.getPathInfo != null) dispatcher.request.getPathInfo else dispatcher.request.getServletPath
/** Render methods **/
def render(template: String, bindingsA: (String, Any)*) = {
for ((k, v) <- flash) bindings(k) = v
Render(template, params, bindings, bindingsA: _*)
}
private def ourContentTypeInferrer: ContentTypeInferrer = {
case f: MatygoFile => f.inferredContentType
case t: Traversable[_] => t.headOption match {
case Some(element) => if (element.isInstanceOf[JObject] || element.isInstanceOf[SproutcoreModel]) "application/json" else "text/html"
case None => "text/html"
}
case _: JValue => "application/json"
case _: SproutcoreModel => "application/json"
case _: Render => "text/html"
case _: DestroyResponse => "application/json"
case _: CSVBuilder => "text/csv"
case _: String => "text/plain"
case _: Array[Byte] => "application/octet-stream"
case _ => "text/html"
}
override protected def contentTypeInferrer: ContentTypeInferrer = ourContentTypeInferrer orElse super.contentTypeInferrer
private def ourRenderPipeline: PartialFunction[Any, Any] = {
case json: JValue => json2String(json)
case t: Traversable[_] => t.headOption match {
case Some(element) => {
rseq2jvalue(element match {
case model: SproutcoreModel => {
val ct = t.asInstanceOf[Traversable[SproutcoreModel]]
ct.map(_.toJSON(currentUser))
}
case json: JValue => {
t.asInstanceOf[Traversable[JValue]]
}
case a: Any => {
t.map(e => JString(e.toString()))
}
})
}
case None => JArray(Nil)
}
case model: SproutcoreModel => transaction(model.toJSON(currentUser))
case render: Render => render.doTemplating
case destroyed: DestroyResponse => Destroyed(destroyed)
case csv: CSVBuilder => transaction(csv.toCSVString)
case f: MatygoFile => f.fileOnDisk
}
override protected def renderPipeline = ourRenderPipeline orElse super.renderPipeline
override protected def renderResponseBody(actionResult: Any) = {
request("contentWritten") = Long.box(actionResult match {
case array: Array[_] => array.size
case f: File => f.length
case str: String => str.length
case _ => 0
})
super.renderResponseBody(actionResult)
}
override def handle(request: HttpServletRequest, response: HttpServletResponse) {
_request.withValue(request) {
_response.withValue(response) {
transaction {
request("start") = Long.box(System.currentTimeMillis)
super.handle(request, response)
info(reportProfile)
}
}
}
}
error {
case e => {
val (theStatus, message) = ExceptionResponse(e)
val json = json2String(message)
info("Handled invalid action, sending % : %" % (status, json))
import java.io.{ StringWriter, PrintWriter }
e match {
case _: NotLoggedInException => ()
case _: NotAuthorizedException => ()
case _: NotFoundException => {
val stackTrace = e.getStackTrace()
val writer = new StringWriter
for (i <- 0 to 5) {
writer.append(stackTrace(i).toString())
writer.append("\n")
}
warning(writer.toString())
}
case _ => {
// log exception if it's not a loggedin one since those happen a lot
val writer = new StringWriter
e.printStackTrace(new PrintWriter(writer))
error(writer.toString())
// if we are in testing mode lets
// shit out the stack trace
if (Environment.mode == Environments.Testing)
e.printStackTrace()
}
}
status = theStatus
message
}
}
/** Render methods **/
// def render(template: String, bindingsA: (String, Any)*) = {
// for ((k, v) <- flash) bindings(k) = v
// Render(template, params, bindings, bindingsA: _*)
// }
def req = new FlowRequest(request)
/**
* Redefining Get/Post/Put/Delete methods to wrap in transaction and register routes with dispatcher
*
*/
def reportProfile = {
val took = System.currentTimeMillis() - request.getOrElse("start", 0).asInstanceOf[Long]
"[%, %, % ms, %, %, %]" % (request.getMethod, request.getRequestURI, took, request("contentWritten"), getClass.getName, transaction(userFromRequest.map(_.id).getOrElse("")))
}
}