unable to locate files with play framework sendFile function - scala

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.

Related

Virtual File System and Areas

I'm trying to use Areas in my ASP.NET Core ABP project like so:
Folder Structure
I'm trying to add a single file bundle like this:
<abp-script src="/Areas/Community/Pages/Mentors/Index.js" />
When I try running the page I get the following error:
AbpException: Could not find the bundle file '/Areas/Community/Pages/Mentors/Index.js' from IWebContentFileProvider
The documentation says the files can be located in Pages, Views, Components, and Themes but it seems limiting if it doesn't also support areas. Do I need to add a route somewhere so the virtual file system can find it?
Update:
I found the source code in \Volo.Abp.AspNetCore\Volo\Abp\AspNetCore\VirtualFileSystem\AbpAspNetCoreContentOptions.cs
where it sets the AllowedExtraWebContentFolders list:
AllowedExtraWebContentFolders = new List<string>
{
"/Pages",
"/Views",
"/Themes",
"/Components"
};
Is there any way to add to this list?
You can configure it in the module's ConfigureServices method.
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAspNetCoreContentOptions>(options =>
{
options.AllowedExtraWebContentFolders.Add("/Areas");
});
}

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"))
}

Playframework were to put files that get rendered in views

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)

Can't override the standard skeleton views in Symfony2 GeneratorBundle

I don't manage to override the skeleton views of the generatorBundle.
I've first tried by adding my view in /app/Resources/SensioGeneratorBundle/skeleton/crud/views/index.html.twig
It didn't worked so I tried to create a new Bundle extending SensioGeneratorBundle and copy the my view in its Resources folder.
I already manage to use themes for twig forms, but I need to personalize the views generated by the doctrine:generate:crud command.
First of all: The corresponding skeleton views are located here:
vendor/bundles/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud
Quick and dirty you should be fine by overriding these view files - but thats not what we want ;)
In:
vendor/bundles/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCrudCommand.php
there is an accessor for the Generator:
protected function getGenerator()
{
if (null === $this->generator) {
$this->generator = new DoctrineCrudGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/crud');
}
return $this->generator;
}
One can try to override this method in your extending Bundle and set a different $skeletonDir in the constructor.
Edit:
Quick example in my test environment how it can be achieved (I only made a quick test ;):
Generate a new bundle for the custom generator: php app/console generate:bundle and follow the instructions. A route is not needed. I chose for this example: Acme/CrudGeneratorBundle (Or use an existing bundle)
Create a folder called "Command" in the newly created bundle directory.
Place a command class in this folder.
<?php
//src/Acme/CrudGeneratorBundle/Command/MyDoctrineCrudCommand.php
namespace Acme\CrudGeneratorBundle\Command;
use Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGenerator;
class MyDoctrineCrudCommand extends \Sensio\Bundle\GeneratorBundle\Command\GenerateDoctrineCrudCommand
{
protected function configure()
{
parent::configure();
$this->setName('mydoctrine:generate:crud');
}
protected function getGenerator()
{
$generator = new DoctrineCrudGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/crud');
$this->setGenerator($generator);
return parent::getGenerator();
}
}
Copy the vendor/bundles/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud to your Resources (in my example "src/Acme/CrudGeneratorBundle/Resources/crud")
This was the best solution for me:
symfony2-how-to-override-core-template
doesn't add a command but modifies the skeleton for that particular bundle.