Play: additional public / assets directory - scala

In order to have a clean directory structure, I would like to make an additional assets folder public. I've created a directory 'assets' in my project folder, wrote an 'PictureAssets' controller which is nearly identical to 'controller.Assets', added 'assets' to the playAssetsDirectories in the build.sbt and tried following some route entries without success.
PictureAssets:
package controllers
import play.api.mvc.Action
import play.api.mvc.AnyContent
object PictureAssets extends AssetsBuilder {
def create : Action[AnyContent] = Action {
Ok(views.html.fileUploadForm())
}
}
build.sbt
playAssetsDirectories <+= baseDirectory / "assets"
routes
# GET /file controllers.PictureAssets.at(path="/assets", file="MM1.png")
GET /datei/*file controllers.PictureAssets.at(path="/assets", file)
# GET /datei/*file controllers.Assets.at(path="/assets", file)
If I try to access the URL, either nothing is displayed, or the error 'The image http:9000//localhost/datei/MM1.png cannot be displayed because it contains errors' is displayed or the css references to handled by the 'controller.Assets' don't work any more.
What am I missing?

I think the issue comes from the fact that the at method used is the default one used previously by Assets.
I ran into the same issue at some point last year, where I wanted to serve images that would be stored in a external folder, a folder that is somewhere on disk, and here is how I coded this:
I created a simple controller called Photos, that contained one Action:
object Photos extends Controller {
val AbsolutePath = """^(/|[a-zA-Z]:\\).*""".r
/**
* Generates an `Action` that serves a static resource from an external folder
*
* #param absoluteRootPath the root folder for searching the static resource files.
* #param file the file part extracted from the URL
*/
def at(rootPath: String, file: String): Action[AnyContent] = Action { request =>
val fileToServe = rootPath match {
case AbsolutePath(_) => new File(rootPath, file)
case _ => new File(Play.application.getFile(rootPath), file)
}
if (fileToServe.exists) {
Ok.sendFile(fileToServe, inline = true)
} else {
Logger.error("Photos controller failed to serve photo: " + file)
NotFound
}
}
}
Then, in my routes, I defined the following:
GET /photos/*file controllers.Photos.at(path="/absolute/path/to/photos",file)
This worked just fine for me. Hope this helps.
PS: This was in addition to the normal Assets controller that helped serving js and css files.

Related

Upgrade CI3 to CI4 - configuration files

In my Codeigniter 3, I have a simple settings.php file that looks something like this:
<?php
$config["lang1_HTML"] = "sr-Latn-RS";
$config["lang1_HTML_2"] = "sr-Latn";
$config["lang1_HTML_3"] = "";
$config["lang1_code"] = "SRP";
$config["lang1_flag"] = "/images/flags/rs.png";
$config["sr"] = "lang1";
$config["lang3"] = "en";
$config["lang3_HTML"] = "en-RS";
$config["lang3_HTML_2"] = "en";
$config["lang3_HTML_3"] = "";
$config["lang3_code"] = "ENG";
...
Now I want to upgrade this to CI4. Is there any chance to put this file in app\Config without changing it and still be able to access this array?
Or better is it possible to autoload Settings.php and use it like this?
Upgrading from 3.x to 4.x » Upgrade Configuration
Upgrade Guide
You have to change the values in the default CI4 config files according to the changes in the CI3 files. The config names are pretty
much the same as in CI3.
If you are using custom config files in your CI3 project you have to create those files as new PHP classes in your CI4 project in
app/Config. These classes should be in the Config namespace and should extend CodeIgniter\Config\BaseConfig.
Once you have created all custom config classes, you have to copy the variables from the CI3 config into the new CI4 config class as
public class properties.
Now, you have to change the config fetching syntax everywhere you fetch config values. The CI3 syntax is something like
$this->config->item('item_name');. You have to change this into
config('MyConfigFile')->item_name;.
Step A:
Create a new class app/Config/Settings.php in the Config namespace that extends CodeIgniter\Config\BaseConfig.
Step B:
Copy the variables from the CI3 config into the new CI4 config class as public class properties. I.e:
<?php
namespace Config;
class Settings extends \CodeIgniter\Config\BaseConfig
{
public string $lang1_HTML = "sr-Latn-RS";
public string $lang1_HTML_2 = "sr-Latn";
public string $lang1_HTML_3 = "";
public string $lang1_code = "SRP";
public string $lang1_flag = "/images/flags/rs.png";
public string $sr = "lang1";
// ...
}
Step C:
Change the config fetching syntax everywhere you fetch config values.
I.e: from $this->config->item('lang1_HTML'); to config(\Config\Settings::class)->lang1_HTML;.
Summary:
CodeIgniter 3.x
CodeIgniter 4.x
1. Loading custom config files.
Manual Loading $this->config->load('config_filename');
CodeIgniter 4.x will automatically look for the files in all defined namespaces as well as /app/Config/.
2. Dealing with name collisions.
If you need to load multiple config files, normally they will be merged into one master $config array. To avoid collisions you can set the second parameter to TRUE and each config file will be stored in an array index corresponding to the name of the config file. Load config file: $this->config->load('settings', TRUE); Access item: $this->config->item('settings')['lang1_HTML']
You don't have to worry about this since all config files reside in their own individual classes. Access item: config(\Config\Settings::class)->lang1_HTML
3. Fetching Config items.
$this->config->item('lang1_HTML');
config(\Config\Settings::class)->lang1_HTML
4. Dynamically setting a config item or changing an existing one.
Set item: $this->config->set_item('lang1_HTML', 'sr-Cyrl-ME'); Access set item: $this->config->item('lang1_HTML');
Set item: config(\Config\Settings::class)->lang1_HTML = 'sr-Cyrl-ME' Access set item: config(\Config\Settings::class)->lang1_HTML
5.Auto-loading.
Global Auto-loading: Open the autoload.php file, located at application/config/autoload.php, and add your config file as indicated in the file. $autoload['config'] = array('settings');
Global Auto-loading: There is no need to configure this since the config(...) helper function returns a shared instance of the particular config class by default. Hence, you can always access your configs using: config('config_class_here')->item_name_here;
first go to app config
public $defaultLocale = 'en';
/**
* --------------------------------------------------------------------------
* Negotiate Locale
* --------------------------------------------------------------------------
*
* If true, the current Request object will automatically determine the
* language to use based on the value of the Accept-Language header.
*
* If false, no automatic detection will be performed.
*
* #var bool
*/
public $negotiateLocale = true;
/**
* --------------------------------------------------------------------------
* Supported Locales
* --------------------------------------------------------------------------
*
* If $negotiateLocale is true, this array lists the locales supported
* by the application in descending order of priority. If no match is
* found, the first locale will be used.
*
* #var string[]
*/
public $supportedLocales = ['en','fa'];
go to app create folder language
folder en and folder fa
<?php
en/trans.php
return [
'auth' => [
'validation' => 'information is not valid',
'loggedIn' => 'you are already login',
'notLogIn' => 'you are not login',]];?>
fa/trans.php
<?php
return [
'auth' => [
'validation' => 'خطای اطلاعات وارد شده',
'loggedIn' => 'شما قبل وارد شده بودید',
'notLogIn' => 'شما وارد نشدید',]];?>
read header it send to ci4 --
Accept-Language en
or Accept-Language fa
echo lang('trans.auth.validation');

Scala Play 2.5 Controller class to serve static HTML

I want to serve a static file from a Scala Play controller. I am looking for something that would allow me to do something like this example below.
NOTE: This obviously does not work.
It is very possible that I am look at the problem in the wrong way, I however do NOT want to redirect to the app.html
def loadApplication(): EssentialAction = Action.sync { request =>
val contents = Assets.contentsOf("/public/assets/app.html") //This doesnot return the contents, but that is what I want
Ok(contents)
}
You can just use the Assets and return contents via that. You might have to tweak the path though:
class MyController #Inject() (assets: Assets) extends Controller {
def loadApplication(): Action[AnyContent] = Action.async { request =>
assets.at("/public/assets/", "app.html").apply(request)
}
}
More information may be found in the documentation: https://www.playframework.com/documentation/2.5.x/AssetsOverview#The-Assets-controller
Also note that you can map a route to your assets instead of statically referencing the file from the controller, like so:
GET /assets/*file controllers.Assets.at(path="/public", file)

Index routes in Scala Play Framework 2.5.x

I am super new to the Play Framework and just started my first experiences with the play-scala-rest-api-example. I copied the post package as a new user package, added the UserRouter to the routes and appended the UserRepository to the Module configurations.
Everything works fine, it's just that I get an 404 error when visiting /v1/users. Only /v1/users/ works. For posts both with and without the slash at the end call the index route. I didn't change anything but renaming the classes and objects. Like I said I just started to play around with the framework and possibly it is something super trivial.
Here the routes file:
GET / controllers.HomeController.index
-> /v1/posts api.post.PostRouter
-> /v1/users api.user.UserRouter
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)
`
I found what was missing. So for any else using this tutorial and facing the same problem. You have to add your custom prefix to the app/RequestHandler.scala isREST() method. So the handlerForRequest method automatically adds a trailing slash at the end.
private def isREST(request: RequestHeader) = {
request.uri match {
case uri: String if uri.contains("post") => true
case uri: String if uri.contains("user") => true
case _ => false
}
}
or
private def isREST(request: RequestHeader) = {
request.uri match {
case uri: String if uri.contains("post") | uri.contains("user") => true
case _ => false
}
}
Your file should be
GET / controllers.HomeController.index
GET /v1/posts api.post.PostRouter
GET /v1/users api.user.UserRouter
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)
The -> symbol is for some especial cases that probably you do not need right now.
https://www.playframework.com/documentation/2.5.x/ScalaRouting

Compilation Issues for new views .scala.html to .class

I'm new to the Play framework. I attempted to compile my play framework project following a sample. When I compile the sample, it works fine and has the .scala.html view compiled in the target as .class. I added a new view, but it didn't compile into the target. Any suggestions how to fix this? I tried activator compile with command line, cleaning and re-building the project, building .scala.html individually, but none of the attempts worked. How do you add a new view and compile it in Play 2.4?
package controllers;
import models.Client;
import models.Server;
import models.TestEvent;
import models.TestPlan;
import play.data.Form;
import play.mvc.Controller;
import play.mvc.Result;
import views.formData.testFormData;
public class Application extends Controller {
// Default path request
// public Result index() {return ok(index.render("Your new application is ready."));}
/* Index Route Page
* Returns the page where the form is filled with the arguments passed
* Or an empty form if the id is 0
*/
public static Result getIndex(long id) {
testFormData testData;
// Find the corresponding index result to return (depends on the id)
if(id == 0)
testData = new testFormData();
else
testData = models.TestEvent.makeTestFormData(id);
Form<testFormData> formData = Form.form(testFormData.class).fill(testData);
return ok(index.render(formData,
Client.getClientNameList(),
Server.getServerNameList(),
TestPlan.getTestPlanNameList()));
}
// Process a form submission
// Bind HTTP Post data to an instance of testFormData
// If errors are found, re-render the page displaying the error data
// If errors are not found, re-render the page displaying good data
public static Result postIndex() {
// Retrieve the formData
Form<testFormData> formData = Form.form(testFormData.class).bindFromRequest();
// The retrieved formData has errors
if(formData.hasErrors()) {
return badRequest(index.render(formData,
Client.getClientNameList(),
Server.getServerNameList(),
TestPlan.getTestPlanNameList()));
}
// The formData does not have errors
else {
// Convert the form data into a testEvent Instance
TestEvent testEvent = TestEvent.makeTestEventInstance(formData.get());
return ok(index.render(formData,
Client.getClientNameList(),
Server.getServerNameList(),
TestPlan.getTestPlanNameList()));
}
}
}
Routes:
GET / controllers.Application.getIndex(id:Long ?= 0)
POST / controllers.Application.postIndex()
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
If the server isn't running while you're coding, changes won't be compiled. You can remedy this by turning on the continuous compilation feature, with compilation occurring every time a change is written to the file system.
To do this:
Start activator
Use ~compile
The ~ indicates changes should be compiled immediately.
You can do something similar with the server. By using ~run, changes will be compiled immediately on file system changes and not delayed until the browser is refreshed.

Mvvmcross binding file url to imageview

I try to bind imageview with local image file. In android, I can use setImageUrl to set image from a file outside resource folder. I read N+1 kitten example and try to use file url instead web url for my project.
The layout of image view
<Mvx.MvxImageView
android:id="#+id/advisor_message_picture"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginLeft="8dp"
android:layout_alignParentRight="true"
local:MvxBind="ImageUrl MessageImage, Converter = Image" />
The converter use to join file name and file directory url. Android view file will set the FileDir
public class ImageConverter : MvxValueConverter<string, string>
{
public static string FileDir;
protected override string Convert(string value, Type targetType, object parameter, CultureInfo culture)
{
return FileDir + "/" + value;
}
}
Update After the answer
I first copy or download to file to the Context.FilesDir.Path and check it with SetImageUrl, the image show up.
view.FindViewById<ImageView>(Resource.Id.advisor_message_picture).SetImageURI( new FileService(_context).CopyFileFromAssetsToStorage("image.png"));
Then I set the FileUrl of converter using same path and file name
ImageConverter.FileDir = FilesDir.Path;
In ViewModel
_messageImage = "image.png";
private string _messageImage;
public string MessageImage
{
get { return _messageImage; }
set { _messageImage = value; RaisePropertyChanged(() => MessageImage); }
}
It works now. The problem is I misunderstood the binding time of viewmodel
For Asset's you can bind using AssetImagePath using the ResourceLoader plugin. However, due to a sticky-fingers editing bug, this custom binding does currently need to added to your Setup - see https://github.com/slodge/MvvmCross/issues/372 for bug details
For files stored using the file plugin (which defaults to Context.FilesDir.Path - see https://github.com/slodge/MvvmCross/blob/v3/Plugins/Cirrious/File/Cirrious.MvvmCross.Plugins.File.Droid/MvxAndroidFileStore.cs#L39), you can use path directly.
For files stored in some custom FileDir determined within your app, you'll need to provide a path relative to Context.FilesDir.Path in order for the plugin to load it.
For further debugging, you could add breakpoints or trace to https://github.com/slodge/MvvmCross/blob/v3/Plugins/Cirrious/DownloadCache/Cirrious.MvvmCross.Plugins.DownloadCache.Droid/MvxAndroidLocalFileImageLoader.cs#L28 - or you could build and register your own IMvxLocalFileImageLoader<Bitmap> implementation that knows about your file paths.