My app uses silverstripe to manage a large number of files for downloading/accessing them and some relational metadata.
One requirement is that the files be accessible externally via an API. I have set up the Restfulserver module (https://packagist.org/packages/silverstripe/restfulserver) to accomplish this.
And have extended the File model to allow restful access:
class FileExtension extends DataExtension
{
...
private static $api_access = true;
...
}
This lets me get and download a file with no problems using a GET:
silverstripe/public/api/v1/Silverstripe-Assets-File/(ID)
Which gives me the necessary data to hit the assets/{hash}/(fileName) and download the file.
But it doesn't seem to give me the means to POST a file. POSTing here simply creates the a File record but with no accompanying file in the assets folder. Manually dropping a file in the folder doesnj't work because it is not referenced by the record and does not have an associated Hash.
So how to I upload files without a page controller?
Related
I have a kind of scavenger hunt project in which I am using AngularJS to manage the different questions it may contain. These questions are of different types. Therefore, some may include a file input and some may not. In this project, I am also using Symfony and SonataMediaBundle to manage my files and my images.
Since my html model (mostly my forms) can change depending on the actions of the user, I cannot use Symfony's built-in tool to produce forms. Therefore, all my forms are custom made. This gives me a problem with SonataMediaBundle, when I want some files to be uploaded. If a user selects a file, this file will be sent via POST to a method in the controller, when the form gets sent. Therefore, I want to send this received file to SonataMediaBundle so that it can manage it, but I haven't found anywhere in the documentation how to do such a thing.
Theoretically, it is really simple. In my controller, when I get a file input, I want to let SonataMedia manage the upload (that is the copy to the proper location, etc...) and I have no clue on how I should do that.
Using symfony2 and not utilizing its benefits you are doing a big mistake you should built your app properly but as far as concerned to your question nothing is bounded by symfony but its on your own how you use it.You can get the sonata media manager service from the container and you have to manually set the all the required setters for the media manager and you have to manually work for the validations like file size ,file mimetype etc. Below is the demo how you can store the file in the sonata media bundle
/* Before class use these*/
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Application\Sonata\MediaBundle\Entity\Media;
public function uploadAction()
{
$file = $this->get('request')->files->get('filefieldname');
if (!$file instanceof UploadedFile || !$file->isValid()) {
return new Response(json_encode(array(
'event' => 'uploader:error',
'data' => array(
'message' => 'Missing file.',
),
)));
}
/* validate max min size for the file */
/* validate mime type for the file */
/* Get sonata media manager service from container */
$mediaManager = $this->container->get('sonata.media.manager.media');
/* create new instance of sonata media class in my case entity is located at
* Application\Sonata\MediaBundle\Entity\Media
*/
$media = new Media();
$media->setBinaryContent($file);
$media->setContext('default');
$ImagemimeTypes = array('image/jpeg', 'image/png');
$FilemimeTypes = array('application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/msword', 'application/pdf', 'application/x-pdf');
if (in_array($file->getMimeType(), $FilemimeTypes)) {
$media->setProviderName('sonata.media.provider.file');
}
if (in_array($file->getMimeType(), $ImagemimeTypes)) {
$media->setProviderName('sonata.media.provider.image');
}
/*If you have other providers in your app like vimeo /dailymotion etc
then do set here i have shown you the demo for adding image/file in sonata media */
/*other setters if you want to set like enabled/disable etc*/
$mediaManager->save($media);
// end function
}
But once again there will be alot of rework you have to do which symfony already provides you the ease for
The Bundle is there to close exactly this gap between Symfony and SonataMedia. SonataMedia is made for raw PHP, while the SonataMediaBundle attaches Symfony interfaces to SonataMedia; you're rewriting large part of the Bundle's functionality. To get a good example of how it is done right, look at the Bundle's code ;)
Sorry if the next suggestion is something you considered thoroughly, I just cannot be certain based on the limited information in the question. Symfony forms are highly flexible and provide a lot of critical functionality that you should not try to re-implement yourself (like CSRF-tokens). Most problems have a good solution with the form system. If a form is metamorphic (has many constellation of fields, based on UI interaction) and cannot be handled by multiple form types, you still have options. If you can set a GET parameter indicating which type of form is currently being sent then you can pass that to the FormBuilder. You may even choose to build a single big form for the whole application, which contain every field you ever use - that's a bit of a waste but still better than trying to wire posting+sonata together. The most hurt the first version does are some extra bytes and empty field. The least hurt you try could do is introduce serious security leaks.
I am using codeigniter for a project that is used by a variety of companies.
The default version of our software is up and running and works fine - however some of our customers want slightly different view files for their instance of the system.
Ideally what I would like to do is set a variable (for example VIEW_SUFFIX) and whenever a view file is loaded it would first check if there was a suffix version available if there was use that instead.
For example if the system had a standard view file called 'my_view.php' but one client had a VIEW_SUFFIX of 'client_1' - whenever I called $this->load->view('my_view') if the VIEW_SUFFIX was set it would first check if my_view_client_1 existed (and if it did use that) or if not use the default my_view.php.
I hope that my question is clear enough... If anyone has done this before or can think of a way to do it I would really appreciate it.
EDIT:
Ideally I would like a solution that works without me changing every place that I am calling the view files. Firstly because there are a few files that may want different client versions and also because the view files are called from a lot of controllers
I had a similar requirement for which I created a helper function. Among other things, this function can check for a suffix before loading the specified view file. This function can check for the suffix and check if the file exists before loading it.
Unfortunately, the file checking logic would be a bit brittle. As an alternative, you can implement a MY_Loader class that will override the basic CI_Loader class.
Something like this in your application/core/MY_Loader.php:
class MY_Loader extends CI_Loader {
protected function _ci_load($_ci_data)
{
// Copy paste code from CI with your modifications to check prefix.
}
}
Could you not do this
// some method of creating $client
// probably created at login
$_SESSION['client'] = 'client_1';
$client = (isset($_SESSION['client'])) ? $_SESSION['client'] : '';
$this->load->view("your_view{$client}", $data);
I'm trying to make a form with a multiple file field. Since the docs are quite vague:
http://symfony.com/doc/current/reference/forms/types/file.html
http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html
I've decided to use W3C FileReader API (Based in the doc urls below), to handle files from the client and manage the underliying data from the view to the entity. Currently supports drag&drop, metadata, and multiple selections on the client.
http://www.html5rocks.com/en/tutorials/file/dndfiles/
http://playground.html5rocks.com/#reading_file_metadata
https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
But I want to give the UploadedFile object one more chance, and the MAIN problem I have is that I can't make the file property in my entity (FileUpload type) to store more than one file data. My input looks like this:
<input type="file" id="upload_files" name="upload[files][]" required="required" multiple="multiple" />
In theory If I made the name to be an Array, the fileUpload should contain the files but it doesn't. Can UploadedFile object store multiple files data ? or just single ?
http://api.symfony.com/2.2/Symfony/Component/HttpFoundation/File/UploadedFile.html
Also tried to initialize (in the __construct of the entity) the $files property as an array and modify the setFiles() to store new array index's $this->files[] = $file; ... you know.
But then Symfony tells me that exception:
The form's view data is expected to be an instance of class Symfony\Component\HttpFoundation\File\File, but is a(n) array. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) array to an instance of Symfony\Component\HttpFoundation\File\File.
I'm not familiarized with data transformers. And Can't figure it out how can be done now. Or If it could be really useful to get the UploadedFile Object with every file data.
In synthesis... With this given info, and the code below. Could anyone help me to get the FileUpload object the correct number of files and not just the last added ? Thank You
Made a repo on github: https://github.com/jeflopo/fileupload
For brevity, here are the relevant files:
The form:
https://github.com/jeflopo/fileupload/blob/master/src/Acme/DemoBundle/Form/Type/FileUploadType.php
The Entity:
https://github.com/jeflopo/fileupload/blob/master/src/Acme/DemoBundle/Entity/FileUpload.php
The controller (Just see the uploadAction):
https://github.com/jeflopo/fileupload/blob/master/src/Acme/DemoBundle/Controller/DemoController.php
The View:
https://github.com/jeflopo/fileupload/blob/master/src/Acme/DemoBundle/Resources/views/Demo/upload.html.twig
The JavaScript that handles the files on the client (Doesn't affect to files behaviour in the server):
https://github.com/jeflopo/fileupload/blob/master/src/Acme/DemoBundle/Resources/public/js/upload.js
I created a pull request which fixes the error!
What's left to do now is that you create unique filenames for the uploaded files and then use the move method on each file. If you don't move them the files won't get saved!
As I set mapped to false, your entity doesn't contain the files. You have to create an array with the filenames you just created to save the file paths.
I have a model that looks like this:
class Pdf extends \lithium\data\Model
{
protected $_meta = array('source' => 'fs.files');
protected $_schema = array('_id'=>array('type'=>'id'));
public $file;
/**
* #param $zipfile string The full name and path of a zip file
* #param $filename string
*/
public static function loadFromFile($zipfile, $filename){
$name = 'zip://'.$zipfile.'#'.$filename;
$pdf = Pdf::create();
$pdf->file = file_get_contents($name);
$pdf->filename = $filename;
$pdf->metadata = ["filename" => $filename, "zipfile" => $zipfile];
return $pdf;
}
}
The static function takes the full name of a zip archive and the name of a file in the archive, creates a model, loads content from the archived file, sets some metadata, and returns the model. This is used in a console command I'm writing that will iterate through a list of zip files, each containing a bunch of pdfs, and add the pdfs to the database.
When I save the model, the pdf does indeed get added to the database. I can view all the pdfs from the MongoDB console, i.e. with db.fs.files.find(). I can also get a pdf from the database using the mongofiles command.
However, it would really be nice if instead of storing the pdfs under fs.files I could store them under fs.files.pdfs. This is because I'm planning on also storing jpegs and text documents in the same database.
If I change $_meta in the class to array('source' => 'fs.files.pdfs'), my console command seems to output the pdf document to the terminal instead of adding it (offhand, I'd like to know why Lithium is doing that!)
I've checked the documentation and haven't been able to figure out what I'm doing wrong. Is there any way to get Lithium to store my files in separate collections depending on the model being used?
(My apologies if my terminology is inaccurate; this is my first attempt to use Lithium and MongoDB beyond the basic tutorials.)
However, it would really be nice if instead of storing the pdfs under fs.files I could store them under fs.files.pdfs. This is because I'm planning on also storing jpegs and text documents in the same database.
At the moment, you can't name your source fs.files.pdfs, but pdfs.files and photos.files ...
I've checked the documentation and haven't been able to figure out what I'm doing wrong. Is there any way to get Lithium to store my files in separate collections depending on the model being used?
Take a look to the highlighted lines here: https://github.com/UnionOfRAD/lithium/blob/master/data/source/MongoDb.php#L387-390
Here is how Lithium handles that
Yeah, I believe we discussed this on IRC. This is a limitation of MongoDB, and I don't believe you can have more than one GridFS prefix (someone correct me if I'm wrong).
If you're just looking for a way to manipulate different file types through different models, I'd recommend creating separate models, then modifying their default queries to include 'type' => 'pdf' in the conditions, or something along those lines.
In a CakePHP (2.1) app I'm using themes together with cacheAction. When accessing the view of e.g. /controller/action, its cache file is saved to tmp/views/controller_action.php. When accessing the same view from a mobile url (like m.example.com), I internally use the same app with themed views to simplify the output for the mobile device.
But now there is a problem with the cache: both requests have different hostnames and themes, but the same controller/action and therefore the same filename of the cache files. So when accessing the urls with different hosts or themes, CakePHP returns the same cache file (or more precisely the cache file of the first request). It is not possible to change the name of the cache files depending on a parameter (e.g. hostname or theme).
I tried modifying the parameters of CacheHelper, but without success. Is there a way to change the cache path/prefix for CacheHelper on the fly? Or is there another possibility to realize this behavior?
The only workaround to solve this problem are the following steps:
1) Create an own MyCacheHelper that extends CacheHelper and save it to app/View/Helper/CacheHelper.php. Overwrite the method _writeFile() and extend the row of the $path string with your prefix:
App::uses('Helper', 'Cache');
class MyCacheHelper extends CacheHelper
{
public function _writeFile($content, $timestamp, $useCallbacks = false)
{
// ...
$cache = $prefix.strtolower(Inflector::slug($path));
// ...
}
}
2) Create an own MyDispatcher that extends Dispatcher and save it to app/Lib/Routing/MyDispatcher.php. Overwrite the method cached() and extend the row of the $path string with your prefix:
App::uses('Dispatcher', 'Routing');
class MyDispatcher extends Dispatcher
{
public function cached($path)
{
// ...
$path = $prefix.strtolower(Inflector::slug($path));
// ...
}
}
3) Change the file app/webroot/index.php to use your new dispatcher:
App::uses('MyDispatcher', 'Routing');
$Dispatcher = new MyDispatcher();
4) Update the $helper parameter in your controllers to use MyCache instead of Cache.
That's it. A little bit complicated, but it works as expected! Now you can adjust the $prefix to whatever you need and create unique cache files for e.g. different domains.
Better solution: CakePHP 2.3 now supports a cache prefix in core config:
Configure::write('Cache.viewPrefix', 'YOURPREFIX');
This prefix may be adapted to match the theme name or some other parameters that are different in those requests.