Silverstripe: Frontend form with file upload could overwrite existing files - silverstripe

I'm running into a small problem with a project of mine. I have a working frontend form that can be used to put an image into a dataobject and save it. No problem so far, but it's an avatar so when one of my users uploads avatar.png it could be overwritten by another user using the same filename. This is expected Silverstripe behavior, but in my case unwanted.
Is it possible to prepend a string to the image name, for example, the username (which is unique)? In a perfect world that would only happen on the front end of course, but I will settle for less :-)
Regards,
Joost.

Well, in the end I found the following solution. The upload class has a setting, that lets you specify if the image will be replaced or if the image filename will be changed, and uploaded nonetheless. The funny part is that the default is set to not replace.
The formfield class that I am using is UploadField, which extends FileField, that uses the upload class. By default, the UploadField class sets the upload to replace the old file, using Upload->setReplaceFile(true).
I therefore decided to create a new upload class, and replace the old one with that:
class VBUpload extends Upload {
public function setReplaceFile($bool) {
if(strpos($_SERVER["REQUEST_URI"],'admin/')===false) {
$this->replaceFile = false;
} else {
$this->replaceFile = $bool;
}
}
}
I only want the default working changed when called from the front end, and maybe there's a cleaner way to achieve that. But this one is working for me.
The only thing to do next is forcing the system to actually using the class, so I added this to the /mysite/_config/config.yml:
Injector:
Upload:
class: VBUpload
This probably can also be done with a normal extension, but I'll change that when the need arises :-)

Related

ASP.Net Core static file security (images specifically)

I have an issue that seems like a very common requirement, but I'm unable to find any help. Let's say I have an authenticated user uploading private photos to non browsable folder on my server. Each user has their own folder in a large file store, like...
/FileStore/{UserId}/Photos/my_cute_cat.jpg
The file is uploaded and I save a thumbnail of the photo like...
/FileStore/{UserId}/Photos/Thumbs/my_cute_cat_thumb.jpg
That user wants to download their photo. No problem...
User sends a request to download
I authorize the user and make sure they own that particular photo
I serve the file
I need display the thumbnail in a plain old img tag on the user's dashboard. The /Thumbs/ folder is not set up to serve static images. I don't want the /thumbs/ folder to be able to serve static images because they should only be visible to authorized users. What am I supposed to do?
If its just a small thumb nail, consider using embedded base64 image with more details here:
How to display Base64 images in HTML?
You can pass base64 down to the View by encoding the file into a base 64 format as a string explained here:
http://www.devcurry.com/2009/01/convert-string-to-base64-and-base64-to.html
Using this approach or even using a FileActionResult to serve the file through a controller has the big disadvantage of not being able to use a CDN to deliver the cached content. What you can do to help with this is still serve the images statically but give them obscenely long random names which is unguessable. When someone requests the image from you, then you simply provide them with the unguessable url.
First and foremost, if a static file should not be available to the whole world, then your web server should not serve the directory it is in at all. Nothing else will do on that front. If the directory is served, then the image can leak.
That then presents the problem of how to allow the user to access it. The simple answer there is that you need an authorized action that returns the image(s). Basically, that's going to look something like:
[Authorize]
public async IActionResult Image(string image)
{
var file = $"/FileStore/{User.Identity.GetUserId()}/Photos/{image}";
if (!File.Exists(file))
return NotFound();
return File(file);
}
Now, obviously, I don't know about your "FileStore", so the exact code here may need to change. The general idea is that you simply see if this file exists under the user's directory. If it does, they can have it. If not, they don't own it. You also should probably add in a bit more security, such as restricting the image param to only image types, so someone can't try to pull just any arbitrary file. They'd still have to somehow manage to get some aberrant file there, in the first place, but just in case it's better to not take chances.
The return methodology may need to change as well. Right now, I'm assuming a file on the filesystem of the server, so I just return a FileResult with the path. If you're pulling it from some sort of storage account in Azure, AWS, etc. then you'd use HttpClient to make the request, and then you should stream the response from that directly to a FileStreamResult.
I've not tested on linux, but if you make a folder where you have you pictures, you can actually just create a controller method that returns a file. Now, even if you are using a view served from another method, you can still call this file method from this razor view and display the image.
in controller called App I serve the image from a folder called Documents:
public IActionResult File(string id)
{
if (String.IsNullOrEmpty(id))
{
return PhysicalFile(Path.Combine(_env.ContentRootPath, "Documents", "Wrong.png"), "image/jpg");
}
return PhysicalFile(Path.Combine(_env.ContentRootPath, "Documents", id), "image/jpg");
}
in razor (using a bit bootstrap):
<img src="~/App/File/#profilePicture" class="img-fluid" />
use the location of your method that serves the file.

Meteor: Get url of uploaded image in CollectionFS photo-blog example

I'd like to be able to pass the URL of an uploaded image in javascript in the tutorial example making a photoblog in meteor.
In that example (in home.js), the helper for templates that render images returns Images.find(), which is used in the image template (image.html) to output html to show the image via:
<img src="{{url}}" />
This works fine, as does the entire tutorial, including S3. However, I'd like to combine it with another project, and that one will require storing and passing around the url under program control.
It would seem that because the template is able to use {{url}}, that in js, one could, in the simplest case, use Images.findOne().url to get at least the first url. E.g., I have modified the given helper to contain this:
Template.home.helpers({
'images': function() {
console.log("url from home helper: = " + Images.findOne().url); //cannot read url property
return Images.find();
}
});
However, this gets the error "cannot read url property..." (and after that, for some reason, the console prints out a huge batch of source code!!) If the template is able to render the field "url" from the collection image object, why can't js see it?
How can I get at the url in javascript?
the url is the function not the property so you have to use Images.findOne().url() not the Images.findOne().url
or
if you are getting the same error that because your findone method return undefined.
There are the possible issues.
Your Images collection are empty.
You did not publish then images and not subscribe the images.
You may be using this call before uploading the images.
I hope this this may solve your issue.

Meteor Check if Template Exists

I have Meteor application that uses Iron-Router in which the rendered screen is dynamic based on the URL.
I have two parameters from the URL, station and screen.
In the routing code:
this.render(stationParam+screenParam, {to: 'content'});
However, I want to be able to check if the template stationParam+screenParam exists.
Is this possible?
All templates are stored as a field of a global object, Template. The question then is: How to check if this global object has a given field?
You have multiple ways to do it. My personal favourite using underscore:
if (_.has(Template, stationParam + screenParam)) { /* ... */ }
Underscore is included in Meteor. If you want to use it in a package, don't forget the api.use('underscore') in your describe callback.
See also: Determining if a javascript object has a given property

Set razor view path to a external folder

Currently we use plugins with razorgenerator. This works ok, but when the view needs to be editted it needs to be compiled every time. This makes us less productive then wanted. I was working on a custom razor viewengine to solve this. To get it working I need to set the view for the area's to a local directory.
I tried
public class PluginRazorViewEngine : RazorViewEngine
{
public PluginRazorViewEngine()
{
var x = this.AreaViewLocationFormats.ToList();
x.Insert(0, "C:/XXXXX/Areas/Module/Views/{1}/{0}.cshtml");
this.AreaViewLocationFormats = x.ToArray();
}
}
Though this gives the error:
The relative virtual path 'C:/XXXXX/Areas/Module/Views/Items/Index.cshtml' is not allowed here.
Is it possible to set the viewpath outside of the project?
I could not find exactly where that error is thrown but I believe you cannot set a path outside of the application the way you are trying to. The RazorViewEngine inherits from BuildManagerViewEngine which in turn inherits from VirtualPathProviderViewEngine. The VirtualPathProviderViewEngine uses the VirtualPathProvider of the host environment.
Thus, it seems you cannot use a direct path and are required to pass in a Virtual path or you could rewrite the underlying layers yourself by directly inheriting from IViewEngine which is what VirtualPathProviderViewEngine inherits from (see image below).
I might be mistaken but I also believe this is enforcing the same principle as specified here that you cannot deliver content from outside the site path.
Have you tried using a relative path to see if that gets mapped correctly. A relative path dependent on the application's root?
Looking through the source of VirtualPathProviderViewEngine you will also notice that the FileExists method of the VirtualPathProvider class is used extensively to find the requested razor file.
Source of inheritance diagram: http://theshravan.net/blog/configure-the-views-search-locations-in-asp-net-mvc/

SilverStripe 3: Load/Create page outside of the CMS

is it possible to create a page outside of the cms?
For example:
I would like to use jquery .load() to load a segment into a current page.
Is it possible to create a html or .ss file somewhere in the theme folder eg: self-contained-page.html so if I then visit www.domain.com/self-contained-paged.html I will be able to visit this page.
While you can of course serve static files, you can also "ajaxify" parts of your page. Not sure if that's what you want to do, but in case someone else is trying to do something similar.
While you could use some fancy tools like pjax or history.js, you can also do it manually. I've recently done this with SS 2.4, but SS 3 should be pretty similar:
In your controller, add a public function so you can access it via /yourpage/load (or whatever you want to call it):
public function load(){
return $this->renderWith(array('AjaxLoad'));
}
In your templates/Layout add a file AjaxLoad.ss.
If you only want to serve that page via your jQuery .load(), simply add the content right inside the file.
If you want to use the piece of content both on your regular page and want to replace it with the ajaxified version, use <% include PageSnippet %> both on the general and the ajaxified page. Then simply define your content in templates/Include/PageSnippet.ss.
You can see it in action at http://www.contentaward.at/content-lab-vienna/608#details (click on the small images at the bottom of the page). Hope this makes it clear.
there is no problem with serving static html files from anywhere in your silverstripe installation, just note to always add the file extension to your urls, as otherwise silverstripe's url routing (using mod_rewrite, see the .htaccess file) will kick in.
also note to always use the full path to the file, so in case you placed your file in 'themes/mytheme/test.html' the url will be 'http://www.domain.com/themes/mytheme/test.html'
You can of course reference a html file in the theme folder just as you would do with a css file, f.e. :
www.domain.com/themes/yourtheme/self-contained-paged.html
If you dont mind to not have it in the theme folder you can also place it into root dir.
Or you can modify your .htaccess and apply some mod_rewrite or redirect rules to point into the theme folder.
If you want to use .ss files you probably have to use CMS pages.

Resources