Downloadable xml files in Play Framework - scala

I am a Scala/PlayFramework noob here, so please be easy on me :).
I am trying to create an action (serving a GET request) so that when I enter the url in the browser, the browser should download the file. So far I have this:
def sepaCreditXml() = Action {
val data: SepaCreditTransfer = invoiceService.sepaCredit()
val content: HtmlFormat.Appendable = views.html.sepacredittransfer(data)
Ok(content)
}
What it does is basically show the XML in the browser (whereas I actually want it to download the file). Also, I have two problems with it:
I am not sure if using Play's templating "views.html..." is the best idea to create an XML template. Is it good/simple enough or should I use a different solution for this?
I have found Ok.sendFile in the Play's documentation. But it needs a java.io.File. I don't know how to create a File from HtmlFormat.Appendable. I would prefer to create a file in-memory, i.e. no new File("/tmp/temporary.xml").
EDIT: Here SepaCreditTransfer is a case class holding some data. Nothing special.

I think it's quite normal for browsers to visualize XML instead of downloading it. Have you tried to use the application/force-download content type header, like this?
def sepaCreditXml() = Action {
val data: SepaCreditTransfer = invoiceService.sepaCredit()
val content: HtmlFormat.Appendable = views.html.sepacredittransfer(data)
Ok(content).withHeaders("Content-Type" -> "application/force-download")
}

Related

Repopulating form fields in Play Framework

Using the Play Framework (2.6) documentation; I am attempting to handle form submission. But I'm running into an issue in repopulating the form fields - which is what I want to do if it has errors (so users can edit their entry rather than having to re-enter).
newForm.bindFromRequest.fold(
errorForm => {
BadRequest(views.html.form(errorForm))
},
formData => {
val oWriteJso = Json.toJsObject(formData) match {
case x if(x.fields.nonEmpty) => getCreatedFieldValues(x)
case _ => None
}
val oRes = Redirect(routes.Application.index).flashing("success" -> "Entry saved!")
apiC.writeAndRedirect("c", collName, None, oWriteJso)(oRes)(request)
}
)
My issue is that the example in the documentation only shows how to pass errorForm directly to a form template (e.g. views.html.form) rather than being able to render the whole page again (i.e. using views.html.index or a Redirect) with the input form fields being populated from the previous request. I found this answer as the closest to this issue but it is a little old and I am using Scala so wasn't able to implement it. Just have no idea how anyone else is doing this or what the sensible, standard approach is. Thanks for any light on this_
If you use the Play helper functions for generating input tags in your view file. They should populate with your values from the last request.
For example you can create an HTML form in your view file using helper methods like this:
#helper.form(action = routes.Application.userPost()) {
#helper.inputText(userForm("name"))
#helper.inputText(userForm("age"))
}
You can take a look at Play documentation about helpers in view files at the following link:
https://www.playframework.com/documentation/2.7.x/ScalaForms#Showing-forms-in-a-view-template

Rendering Plone SchemaAddForm without main_template?

I'm attempting to write a Plone add-on that requires an "Add new" form. So far, I've managed to get this working very nicely using plone.directives.form.SchemaAddForm. I have the '##create-snippet' view registered in configure.zcml, and it works perfectly when I view the page normally.
However, the ultimate goal of this project is to get this add form into a TinyMCE popup window. I've created a working TinyMCE plugin for another, irrelevant portion of the add-on, and gotten that working well. However, when I try to navigate to my "##create-snippets" view in a tinyMCE window, I get:
LocationError: (Products.Five.metaclass.DirectoryResource2 object at 0x107162fd0, 'main_template')
My understanding of this issue is that, essentially, the SchemaAddForm class (or one of it's super classes, to be exact) wraps the form with the main Plone main_template when it renders the form. Since TinyMCE windows are their own, isolated little worlds, the template isn't available, and, therefore, cannot be rendered....or something like that? Please correct me if I'm way off.
What I really want to know, is if it's possible to set things up so only the form itself will render, without using the main_template? I would REALLY like to be able to take advantage of the schema-based forms (and their built-in validation), but still be able to keep everything within a TinyMCE window.
I've toyed around with creating my own ViewPageTemplateFile() template, and getting the form to render (somehow?) within it, but frankly, I have no idea how....or if that's even possible.
Please feel free to ask for more information if there's something I've omitted. I'm kinda new to this type of Plone development.
The code generating the form:
from Products.Five.browser import BrowserView
from uwosh.snippets.snippet import SnippetManager
from plone.directives.form import SchemaAddForm
import zope.interface
from plone.autoform.form import AutoExtensibleForm
import zope.schema
from plone.directives import form
import z3c
from Products.statusmessages.interfaces import IStatusMessage
_ = zope.i18nmessageid.MessageFactory(u'uwosh.snippets')
class ISnippet(form.Schema):
title = zope.schema.TextLine(
title=u'Title',
description=u'The title to associate with the snippet.',
required=True)
description = zope.schema.TextLine(
title=u'Description',
description=u'A short explanation of the snippet.',
required=True)
body = zope.schema.Text(
title=u'Body',
description=u'The actual content to be rendered on the page.',
required=True)
class SnippetForm(SchemaAddForm):
schema = ISnippet
#z3c.form.button.buttonAndHandler(_('Save'), name='save')
def handleAdd(self, action):
data, errors = self.extractData()
if errors:
self.status = self.formErrorsMessage
return
obj = self.createAndAdd(data)
if obj is not None:
# mark only as finished if we get the new object
self._finishedAdd = True
IStatusMessage(self.request).addStatusMessage(_(u"Snippet saved"), "info")
#z3c.form.button.buttonAndHandler(_(u'Cancel'), name='cancel')
def handleCancel(self, action):
IStatusMessage(self.request).addStatusMessage(_(u"Add New Snippet operation cancelled"), "info")
self.request.response.redirect(self.nextURL())
def create(self, data):
sm = SnippetManager()
#TODO:
#Include support for different folders from this form.
snippet = sm.createSnippet(data['title'], None ,data)
return snippet
def add(self, object):
#Since, for now, snippets are based upon ATDocuments, their creation is fairly staight-forward.
#So, we don't really need separate Add/Create steps.
return
def nextURL(self):
return self.context.absolute_url() + '/##create-snippet'

Sending email with attachment using scala and Liftweb

This is the first time i am integrating Email service with liftweb
I want to send Email with attachments(Like:- Documents,Images,Pdfs)
my code looking like below
case class CSVFile(bytes: Array[Byte],filename: String = "file.csv",
mime: String = "text/csv; charset=utf8; header=present" )
val attach = CSVFile(fileupload.mkString.getBytes("utf8"))
val body = <p>Please research the enclosed.</p>
val msg = XHTMLPlusImages(body,
PlusImageHolder(attach.filename, attach.mime, attach.bytes))
Mailer.sendMail(
From("vyz#gmail.com"),
Subject(subject(0)),
To(to(0)),
)
this code is taken from LiftCookbook its not working like my requirement
its working but only the Attached file name is coming(file.csv) no data in it(i uploaded this file (gsy.docx))
Best Regards
GSY
You don't specify what type fileupload is, but assuming it is of type net.liftweb.http. FileParamHolder then the issue is that you can't just call mkString and expect it to have any data since there is no data in the object, just a fileStream method for retrieving it (either from disk or memory).
The easiest to accomplish what you want would be to use a ByteArrayInputStream and copy the data to it. I haven't tested it, but the code below should solve your issue. For brevity, it uses Apache IO Commons to copy the streams, but you could just as easily do it natively.
val data = {
val os = new ByteArrayOutputStream()
IOUtils.copy(fileupload.fileStream, os)
os.toByteArray
}
val attach = CSVFile(data)
BTW, you say you are uploading a Word (DOCX) file and expecting it to automatically be CSV when the extension is changed? You will just get a DOCX file with a csv extension unless you actually do some conversion.

How can I serve mostly static pages from a scala Play! application like an "About" page in different languages?

I am building an application using the Play! framework version 2.0.4. Some pages that are legally required such as an imprint mostly contain lots (some 10k each) of static text which I would like to provide in different languages.
In versions 1.x there was an #include directive that allowed to construct the actual resource path using the current Lang.
What is the recommended way of implementing sth similar with Play 2.x?
Thank you & best regards,
Erich
I'm not 100% certain how you implement it as of now, but here's what I've come up with.
You could just write your own include helper. Save the following in a file Helpers.scala in your views folder. Explanations are in the code's comments.
package views.html.helper
object include {
import play.api.templates.Html
import play.api.Play
import play.api.Play.current
import play.api.i18n._
// The default is to look in the public dir, but you can change it if necessary
def apply(filePath: String, rootDir: String = "public")(implicit lang: Lang): Html = {
// Split filePath at name and suffix to insert the language in between them
val (fileName, suffix) = filePath.splitAt(filePath.lastIndexOf("."))
// Retrieve the file with the current language, or as a fallback, without any suffix
val maybeFile =
Play.getExistingFile(rootDir + "/" + fileName + "_" + lang.language + suffix).
orElse(Play.getExistingFile(rootDir + "/" + filePath))
// Read the file's content and wrap it in HTML or return an error message
maybeFile map { file =>
val content = scala.io.Source.fromFile(file).mkString
Html(content)
} getOrElse Html("File Not Found")
}
}
Now in your imprint.scala.html you could call it like this:
#()(implicit lang: Lang)
#import helper._
#include("static/imprint.html")
Way showed by Schleichardt was used in play-authenticate to select mail templates in different languages and now it's changed to work with reflections on the controller, so maybe it can be interesting for you. Anyway it was intendent to keep standard templating possibilities (cause each mail need to be personalized before send)
For static info pages, you can just save the code for each language with suffix ie. impressum_en.html, impressum_de.html in the file system and use simple controller which will find file with proper suffix and return its content exactly as it is... all you need probably to return Ok(fileContent) and set the Content-Type manually to text/html.
Other option is doing the similar thing but storing it in the DB, so you can create simple back-end and edit it with browser.
If you still need to replace some elements, you can do it with some ###MARKER### in the code + simple String operations, or with JavaScript on the client side.
Templates in 2.0 work a bit differently. The Scala based templates are compiled. From one template instead of 'including' another, you can make a call to it. I'm not sure exactly what you mean by the language thing. In 2.0 though, a parameter to the template could be the language.
Example template named 'included' in package whateverpackage.
#(lang: Lang)
...
Example calling a template named 'included':
#import whateverpackage._
#included(Lang("en"))
#()(implicit lang: Lang)
#main(Messages("imprint")) {
#lang match {
case Lang("de", _) => { #germanImprint() #* uses app/views/germanImprint.scala.html file *#}
case _ => { #englishImprint() }
}
}

How to serve uploaded files in Play!2 using Scala?

I'm trying to allow users to upload photos to the server and then view them. Uploading happens as described in this guide. Here is the code:
def upload = Action(parse.multipartFormData) { request =>
request.body.file("picture").map { picture =>
import java.io.File
val filename = picture.filename
val contentType = picture.contentType
picture.ref.moveTo(new File("/tmp/picture"))
Ok("File uploaded")
}.getOrElse {
Redirect(routes.Application.index).flashing(
"error" -> "Missing file"
)
}
}
It is unclear to me how to serve the uploaded images back to users that want to see them. Right now I am hosting the server on my own machine, so the code snippet from the guide writes the files to my D: drive, which isn't (and shouldn't be) available from the Internet. As far as I can see there are 2 options:
Store the photos under the /public folder in my project (the one that is dedicated to assets). See here: http://www.playframework.org/documentation/2.0/Assets
Write my own controller that servs images form custom locations from my drive.
For 1, I'm not sure if that is the purpose of assets.
For 2, I have no idea how to write such a controller.
The simple example is
def index = Action {
Ok.sendFile(new java.io.File("/tmp/fileToServe.pdf"))
}
there is "Serving files" section at https://www.playframework.com/documentation/2.4.x/ScalaStream#Serving-files which explains how to serve files
2.0.3 will feature an external Assets controller which might be (mis)used for this. Writing such a controller is no magic though, you have predefined folder where all your uploads are saved, and that's where you read them from. In the database you save the (unique) file name.
A different approach would be to save the uploaded files in the database. We do this with GridFS in MongoDB. A custom controller serves them back to the user. This way your data is stored in one central place, which also makes backups and recoveries simpler.
You can add a new route like this:
GET /myFiles/*file controllers.Assets.at(path="/tmp", file)