Field-specific error messages in Play Framework/Scala forms - scala

I want to customize the default error message "This field is required" when using the "nonEmptyText" constaint in the Scala form helper.
Here is an example that I want to customize:
val form = Form(
tuple("email" -> nonEmptyText, "password" -> nonEmptyText)
verifying ("Invalid email or password.", result => result match {
case (email, password) => {
User.authenticate(email, password).isDefined
}
}))
Optimally in my conf/messages file I could provide a field-specific error:
error.email.required=Enter your login email address
error.password.required=You must provide a password
But in the worst case I would be happy with a wildcard message using the field name:
error.required=%s is required
#would evaluate to "password is required", which I would then want to capitalize
I saw this %s expression in some Play 1.x documentation but it does not seem to work anymore.
Thank you in advance for your help!

Try to drop the usage of a nonEmptyText and use a simple text field with a custom validation:
tuple(
"email" -> text.verifying("Enter your login email address", _.isDefined),
"password" -> text.verifying("You must provide a password", _.isDefined)
)
You can then move a step further and exchange the String inside the verifying clause for a call to the play.api.i18n.Messages object:
tuple(
"email" -> text.verifying(Messages("error.email.required"), _.isDefined),
"password" -> text.verifying(Messages("error.password.required"), _.isDefined)
)
Note that this is untested code, but it should point out the direction.
Good Luck

Related

Avoid query parameters duplication when generating a MAC for requests in gatling

I am using gatling (2.1.7) in order to stress test an API.
For of all I have to request a transaction id and shared secret in order to authenticate all subsequent calls.
scenario("API").exec(http("authorize")
.post("/api/v1/xxx/authorize")
.formParam("client_key", "a_very_strong_key")
.check(jsonPath("$.response.txId").saveAs("id"))
.check(jsonPath("$.response.txSecret").saveAs("secret")))
All other calls must contain a query parameter signature which is a mac of the other request parameters.
I wrote this piece of code to do that
scenario("API").exec(http("call")
.get("/api/v1/call")
.queryParam("id", "${id}")
.queryParam("param1", "aaaaaa")
.queryParam("param2", "bbbbbb")
.queryParam("signature", session => sign(session, Map(
"id" -> session("id").as[String],
"param1" -> "aaaaaa",
"param2" -> "bbbbbb"))))
/* ... */
def sign(session: Session, params: Map[String, String]) : String = {
val str = canonicalize(params)
format_mac(session("secret").as[String], str)
}
However I have to duplicate all query parameter names and values in the sign method call and it is clearly a bad practice. It is possible to avoid that ?
You want to sign a request, so use a SignatureCalculator.

How to move reusable code from Play Framework template to separate template (Scala)?

I have this reusable code in the template and it works fine.
#headers(page: String,site:Site, user: User) = #{
page match {
case "home" => Map(
"title" -> "Welcome",
"description" -> "Welcome to our site")
case "profile" => Map(
"title" -> user.name + "-" site.name,
"description" -> "Hello" + user.name)
}
}
Now I want to move it to separate template and include it in main template.
If I put it in separate file like this
#(page: String,site:Site, user: User) = page match {
case "home" => Map(
"title" -> "Welcome",
"description" -> "Welcome to our site")
case "profile" => Map(
"title" -> user.name + "-" site.name,
"description" -> "Hello" + user.name)
}
When I try to get data from it in main template
#templates.layout_headers("home", user, site)("title") I've got error
does not take parameters
complaining on
("title")
part. How this can be fixed?
Your template does not accept a title parameter. You can think of twirl templates as normal scala functions. A template named layout_header.scala.html containing the signature #(page: String, site:Site, user: User) is equivalent to
def layout_header(page: String, site: Site, user: User): play.twirl.api.HtmlFormat.Appendable
Now you are trying to call that function like layout_headers(user, site)("title") which is obviously wrong because of two points. Firstly the parameter page is missing. Secondly the function does not accept a title parameter.
As you are not using a title in layout_headers, just ditch it from your call and add the page parameter:
#templates.layout_headers(page, user, site)
If you need title to be available in your template, add it to the signature:
#(page: String, site:Site, user: User, title: String) = page match {
...
}
which then will be called as
#templates.layout_headers(page, user, site, "Some title")

Fill in boolean field in a form

I have a login form which checks if the user selected "remember me" and if the condition is met, it will automatically fill in the email field.
This is my Form object:
val loginForm = Form(
tuple(
"email" -> email,
"password" -> text(minLength = 3),
"remember" -> boolean
)
)
There may be a KV pair stored in the session ("remember" -> email: String). I have a working function which returns this email as an Option[String].
This is the function which renders the login page. views.html.login is a Template that accepts a Form object and an optional message: String which is used for relaying any authentication errors.
def login = Action {
request => {
logRequest(request)
if (UserInfo.isAuth(request.session)) {
Redirect(routes.Application.index())
}
else {
val email = UserInfo.getRememberedEmail(request.session)
if (email.isDefined) {
Ok(views.html.login(loginForm.fill(email.get, "", true)))
}
else {
Ok(views.html.login(loginForm))
}
}
}
}
I think the problem occurs at Ok(views.html.login(loginForm.fill(email.get, "", true))). When checking the form for errors, this is what I found:
{"remember":["error.boolean"]}

Play file upload form with additional fields

I have run into a problem with a Play 2.1.0 form that contains a file upload and an additional input field. I use
def uploadTaxonomy() = Action(parse.multipartFormData) {
implicit request =>
request.body.file("xml").map { file =>
val xml = scala.io.Source.fromFile(file.ref.file).mkString
taxonomyForm.bindFromRequest().fold(
formWithErrors => BadRequest(views.html.index(formWithErrors)),
result => {
Taxonomies.create(result._1, xml)
Redirect(routes.Application.index())
}
)
}.getOrElse {
Redirect(routes.Application.index())
}
}
and my form is this:
val taxonomyForm = Form(
tuple(
"label" -> text,
"xml" -> text
)
)
The problem is that bindFromRequest() always fails (causing a bad request to be returned to the client).
Does anybody have an idea where the problem might lie?
Note: I am aware that there is a bug in 2.1.0 that manifests when no files are selected in an upload field; it does not seem to be related, however.
As far as I know the xml should not be part of the form definition as you get it directly from the request body.

Handling Error on Insert PlayFramework 2.0 Scala

I'm having problem inserting (or updating) new data to database using anorm in Play Framework 2.0. My model is a blog post defined in the following code:
case class Post(title: String,
content: String,
created: String,
lastUpdate: String,
writer: Long,
id: Long = 0)
Then I do this on the insert function:
def create(title: String, content: String, userId: Long) = {
DB.withConnection { implicit connection =>
SQL("INSERT INTO post (title, content, created, writer) VALUES ({title}, {content}, NOW(), {writer})")
.on(
'title -> title,
'content -> content,
'writer -> userId).executeUpdate()
}
And the form:
val postForm = Form(
tuple(
"title" -> nonEmptyText,
"content" -> nonEmptyText))
I did not have a userId field in the form because I don't trust the user's input on their own id. I get it from session. Anyway, that's why I can't directly put the validation code in postForm's declaration (because I think I can't access session both from the form and from model). And here's when it becomes ugly. If the post is invalid, anorm throws an Exception so I need to pass the error to user AFTER the fold function like:
postForm.bindFromRequest.fold(
formWithErrors => BadRequest(views.html.post.newPost(formWithErrors)),
newPost => {
try {
val userId = request.session.get("id").getOrElse("0")
models.Post.create(newPost._1, newPost._2, userId.toLong)
} catch {
case e => BadRequest(views.html.post.newPost(postForm.fill(newPost)))
}
Redirect(routes.Application.index)
})
First, the try-catch is ugly. Second, the BadRequest call don't work. What am I doing wrong? What is the best way to handle errors in insert / update? I have the same problem with a login form, but it's not as bad as this because I can actually handle errors at login form declaration.
Thanks before.
Assuming the error is on an integrity constraint like 0 not existing in the database, what about something like this ?
postForm.bindFromRequest.fold(
formWithErrors => BadRequest(views.html.post.newPost(formWithErrors)),
newPost => {
request.session.get("id").map({ userId=>
models.Post.create(newPost._1, newPost._2, userId.toLong)
Redirect(routes.Application.index)
}).getOrElse{
BadRequest(views.html.post.newPost(postForm.fill(newPost)))
}
}
)
You don't give invalid information to anorm and don't get an error ...
ps : I don't have a scala compiler on hand to check the syntax right now, I might have missed a paren or mixed a paren with a curly :)