Playframework were to put files that get rendered in views - scala

I am using the playframework to render Asciidoc text from a file inside my view.
Since that content is used in my view, I want to be able to put it in the app/views so it gets packaged when deploying with activator dist.
Right now the files get lost after running activator dist. Because the content gets rendered by my view I don't want to put in in public/ or in app/assets.
My view looks versy simple:
#(html: String)(implicit flash: Flash, lang: Lang)
#main(Messages("application.name")){
#Html(html)
}
And my controller sends the String content to the view:
def about = Action { implicit request =>
Ok(views.html.statics.normal(Static.render_file("app/views/adoc/about.adoc")))
}
Where should I put this file? and how to I access it other than with the path from the root?

You can put "internal" documents in the conf folder, it's the equivalent to resources in standard sbt projects.
To access it, you'd use Play.resourceAsStream(). Note that this gives you an java.io.InputStream because your file will be part of the JAR created by activator dist.
Play.resourceAsStream("adoc/about.adoc") map { adocStream =>
Ok(views.html.statics.normal(Static.render_file(adocStream)))
} getOrElse (InternalServerError)

Related

unable to locate files with play framework sendFile function

The goal is to be able to both download and render (e.g. profile pic) images that are either a public asset or a private one.
In the Play Framework docs (ScalaStream), it says:
Play provides easy-to-use helpers for common task of serving a local
file:
def index = Action {
Ok.sendFile(new java.io.File("/tmp/fileToServe.pdf"))
}
If you want to serve this file inline:
def index = Action {
Ok.sendFile(
content = new java.io.File("/tmp/fileToServe.pdf"),
inline = true
)
}
This looks like what is needed to achieve the goal.
Now, I have a directory structure as shown below, and would like to serve the files 1.png and 2.png
MyApp
|_ app
|_ conf
|_ public (all public assets)
|_ images
|_ 1.png
|_ private (more assets, not public)
|_ images
|_ 2.png
|_ ...
I have defined a controller function as follows:
def sendImage() = Action {
// Ok.sendFile(new java.io.File("/public/images/1.png"))
// Ok.sendFile(new java.io.File("assets/images/1.png"))
Ok.sendFile(new java.io.File("/private/images/1.png"))
}
Tried various different paths, absolute, relative, but when I call this controller function from the front-end (React / Axios), it only returns "NoSuchFileException".
However, I am able to render public assets from the front-end simply using:
<img src='/assets/images/1.png' /> // from the public dir
The same path does not work from within the controller. Could not figure out how Play expects its paths.
Currently using Play 2.5
Any ideas? Thanks
inject play.api.Application object in your controller and use the getFile method defined in the application object as shown below.
class JobInstanceController #Inject()(protected val app: Application) extends Controller {
def test = Action {
Ok.sendFile(app.getFile("public/images/1.png"))
}
}
the private folder isn't in the classpath. I think you need to add it to the build.sbt file
unmanagedResourceDirectories in Compile += baseDirectory.value / "private"
see here
http://www.scala-sbt.org/0.13/docs/Howto-Customizing-Paths.html
The comment by #marcospereira is what helped resolve the path, thanks!
When you use / before the file name, this expects the full path of the file and not a relative one. You need to change from /private/images/1.png to private/images/1.png so that it will be relative to your application.
But, if these private files are uploaded by the user, have them inside the application directory would be considered a bad practice. What will happen when you deploy a new version of the application? You will either lose the files or you will need to copy/move them while deploying. So, have an directory that is external to the application to store uploaded files.

How to override an HTML file in a custom module

I am developing a custom module for a payment method in magento 2. Currently I am using cc-form.html from the vendor directory and the module is working fine. see below path.
vendor/magento/module-payment/view/frontend/web/template/payment/cc-form.html
Is there any way to override the HTML file?
Yes, there is. You can look in pub static to see how path to static asset constructed.
How it works
Every asset is accessible from the page by itenter code heres "RequireJS ID". It similar to real path, but varied.
For example file http://magento.vg/static/adminhtml/Magento/backend/en_US/Magento_Theme/favicon.ico.
It's real path is /app/code/Magento/Theme/view/adminhtml/web/favicon.ico.
It's RequireJS ID is Magento_Theme/favicon.ico. This means that file could be accessible via require("text!Magento_Theme/favicon.ico") or similar command.
You can find that RequireJS ID consist with module name and useful part of path (after folder web).
How can I replace a file
So you have file
vendor/magento/module-payment/view/frontend/web/template/payment/cc-form.html
On the page it loaded with src as
http://magento.vg/static/frontend/Magento/luma/en_US/Magento_Payment/template/payment/cc-form.html
So its RequireJS ID is
Magento_Payment/template/payment/cc-form.html
Side note: Inside UI components stuff it equals to Magento_Payment/payment/cc-form. Words "template" and ".html" are added automatically.
And now you can replace this file for application via RequireJS config
var config = {
"map": {
"*": {
"Magento_Payment/template/payment/cc-form.html":
"<OwnBrand>_<OwnModule>/template/payment/cc-form.html"
}
}
};
This code snippet you place in requirejs-config.js file in your module. That is all.

How do I include parent template from any directory in Play Framework

When I have child template for example index.scala.html and parent template main.scala.html, and I am including parent template using code:
#main("MyApp") {}
this template is including correctly. But when I move index.scala.html into directory, for example Location and trying to include parent template using code:
#views.main("MyApp") {}
it does not work.
Structure of my views directory:
I am getting error:
[error] /Users/dev/project/app/scala/pl/moody/views/Location/index.scala.html:1: object main is not a member of package views
[error] #views.main("Moody") {
[error] ^
You don't need to use views prefix, it is auto-imported, so you should use just #main("MyApp") even if children are placed in subdirectories.
I had almost the same problem, I solved that by calling it with the full path like so:
#views.html.admin("Admin Interface") {
}
Try calling it like:
#views.html.main("MyApp") {}
Ok I found working solution. The correct compiled html file is placed under target directory.
In my case target/scala-2.11/twirl/main/scala/com/myProject/views/html/main.template.scala
Given I have this directory now I am able to add it to my index.scala.html and it will looks
#scala.com.myProject.views.html.main("MyApp") {
}
and the same url will be rendering html from controller:
def index = Action {
Ok(scala.com.myProject.views.html.main("MyApp"))
}

Lift won't prepend context path before link if I use `S.render`, how to fix it?

When deploy a lift app to a tomcat container, it will automatically pretend the context path to all the <a>s whose href are starting with "/". (How is lift doing this?)
But in my Boot.scala, I want to show a custom 500 page, that I use S.render to render a template, and found the links are not handled.
My code:
LiftRules.exceptionHandler.prepend {
case (runMode, req, exception) =>
logger.error("Failed at: " + req.uri, exception)
val content = S.render(<lift:embed what="500"/>, req.request)
XmlResponse(content.head, 500, "text/html", req.cookies)
}
You can see the line S.render(<lift:embed what="500"/>, req.request)
It will render the webapp/500.html, but without prepending the context path. When I deploy it to tomcat, the page can't show correctly since the js/css files can't load.
How to fix it?
I found the solution:
val content = req.fixHtml(S.render(<lift:embed what="500"/>, req.request))
Notice the req.fixHtml(), it will prepend context path to the links if possible.

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)