I have a project with files and when I create new project I have form created with FormBuilder and there I have a field from FileType and it is converted then to UploadedFile and it works fine. What I want is to be able to upload after that files without having to edit the project. A simple "Add more files" button. No other fields required and therefore - no need for FormBuilder. Also I don't want to have "save" button after "Add more files" is clicked. I want to click it choose my files and when I click "Open" the form to be submited. This I have achieved with the following code
<form action="{{ path('image_upload', { 'id': project.id }) }}" method="post" name="form">
<div class="col-md-2" id="imageUpload">
<span>Add More Files</span>
<input type="file" readonly="readonly" id="imageUpload" name="image_upload[]" onchange="form.submit()" multiple/><div class="upload_icon"></div>
</div>
</form>
As you can see this form is only with this one button which onChange submits it. My problem is that after submission the files are not UploadedFile type and the Service I have for uploading images doesnot work. I tried creating an instance of UploadedFile on my own but it requires filePath which I have no idea how to take.
This is what is submitted
From this I don't know how to take filePath to actually add it to the constructor of UploadedFile. Also when I debug what happens during creation of the project (where I use FileType) I see that the filePath is "C:\xampp\tmp" which I have no idea from where it comes. My first thought was to simply add it to the constructor of UploadedFile with this code
$file = new UploadedFile("C:\\xampp\\tmp",$file);
When I run it I receive this error -
The file "C:\xampp\tmp" does not exist
So to recap - this is the path which $file->getPath() gives me when I use FileType, but without it when I have simple upload button, it doesnot exist.
At this point I surrendered and was like "Okay...If it wants me to use FormBuilder so badly - then I will". And here is the code in AddFileType (which is related to no entity or anything)
$builder->
add("addFiles",FileType::class,array(
'label'=>'Add More Files',
'multiple'=> true,
'mapped'=> false,
"required"=>false
));
And here is the other problem - I want to be able to submit the form without submit button. Simply when I click "Add More Files" and select some files the form to be submited for me.
My question - Is there a way to get the filePath when using simple upload button and therefore create an instance of UploadedFile and send it to my database from my Service. If not - is there a way with FormBuilder to make it so when FileType button is clicked to submit the form without any submit button required.
I think you need to set mapped to true, because by setting it to false it won't be a relation between this field and the database's field
$builder->
add("addFiles",FileType::class,array(
'label'=>'Add More Files',
'multiple'=> true,
'mapped'=> true,
"required"=>false
));
public function addArticleAction(Request $request){
//get pic from http request
$Image = $this->getRequest()->files->get('artPic');
//generate a unique name for pic
$picName = $this->generateUniqueFileName().'-'.$Image->getClientOriginalName();
//move pic to a directory
$Image->move(
$this->getParameter('pic_directory'),
$picName
);
//store picName in db
$article = new FlashInfo;
$article->setPhoto($picName);
//other data
$request = $request->request->all();
$article->setTitle($request['arTitle']);
$article->setTitle($request['arText']);
$article->setTitle($request['artSource']);
return new Response(json_encode(array('status'=>'success')));
}
Related
I have a page with a form for creating users. A user has an hobby, which can be created on the same page by clicking on the second button which opens the page for creating a hobby. After creating the hobby, the previous user form should be shown with the user input inserted before going to the hobby page.
Is there a way to do something like with typo3 flow / fluid without using AJAX?
I tried to submit the input to a different action by clicking on the createHobby button --> The action redirects to the new hobby page, where the user can create the hobby and after creation it should redirect back to the user form with the already filled out input fields by the user .
I used...
<input type='submit' value='Create' formaction='/hobby/create' />`
to achive this, but it seems there are some problems with the uris... I get following error:
#1301610453: Could not resolve a route and its corresponding URI for the given parameters.
I think the using the attribute formaction is not a good solution for every case, as it is not supported by IE < 10 as you can see here. I think a JavaScript backport should also be considered (dynamically change the action attribute of the form when clicking on the second button, before actually submitting the form).
Concerning your error, you should not – and probably never – use direct HTML input, instead try to focus on Fluid ViewHelpers, which allow TYPO3 to create the correct HTML input.
Try this instead:
<f:form.submit value="Create" additionalAttributes="{formaction: '{f:uri.action(controller: \'hobby\', action: \'create\')}'}" />
You can make an $this->forward(...) in an initializeActiondepending on an param of your action.
Lets imagine your default Form action is "create". So you need an initializeCreateAction:
public function initializeCreateAction()
{
if ($this->arguments->hasArgument('createHobby')) {
$createHobby = $this->request->getArgument('createHobby');
if ($createHobby) {
$this->forward('create', 'Hobby', NULL, $this->request->getArguments());
}
}
}
Now you must name your input createHobby and assign your createAction this param:
In fluid:
<f:form.button type="submit" name="createHobby" value="1">Create Hobby</f:form.button>
In your Controller:
public function createAction($formData, $createHobby = false)
{
...
}
can you explain something more ... what you show has nothing to do with typo3, I don't know where you inserted that, what version of typo3 , using any extension extra ?
I've a front-end form with a file input where anybody (no registered users) can upload an image that will be attached to a custom meta field in the back-end. To preview the image I'm using the old iframe technique. My form looks like this:
<form id="upload-photo" method="post" target="preview-iframe" action="<?= get_template_directory_uri() ?>/inc/upload.php" enctype="multipart/form-data" >
<div id="preview"><img src="" alt="" /></div>
<iframe id="preview-iframe" name="preview-iframe" src=""></iframe>
<input type="file" name="author_photo" />
<input type="hidden" id="attachment" name="attachment" value=""/>
<button type="submit" id="upload">Upload</button>
</form>
Then I use WordPress built-in functions to handle the upload and move the file into the media gallery. I use the hidden field to store the WordPress id of the attachment so if users decide to change the picture by uploading a new one then the old one would get removed. This is my PHP:
<?php
define('WP_USE_THEMES', false);
require_once '../../../../wp-load.php';
require_once(ABSPATH .'wp-admin/includes/image.php');
require_once(ABSPATH .'wp-admin/includes/file.php');
require_once(ABSPATH .'wp-admin/includes/media.php');
if (isset($_POST['attachment'])) {
wp_delete_attachment($_POST['attachment'], true);
}
foreach ($_FILES as $file => $data) {
if ($data['error'] === UPLOAD_ERR_OK) {
$attachment = media_handle_upload($file, null);
}
}
echo wp_get_attachment_image($attachment, 'author', 0, array('id' => $attachment));
?>
And finally the jQuery that glues it all together:
var $preview = $('#preview'),
$iframe = $('#preview-iframe'),
$attachment = $('#attachment');
$('#upload').click(function() {
$iframe.load(function() {
var img = $iframe.contents().find('img')[0];
$preview.find('img').attr('src', img.src);
$attachment.val(img.id);
});
});
Everything works perfect but there are few issues with this simple approach:
If JavaScript is disabled images don't get removed
If the user uploads a file then refreshes the site and then uploads and other image, then the previous one wouldn't get deleted because the previous attachment ID doesn't exist due to the refresh.
A malicious user could edit the hidden attachment field with a different ID.
I though about uploading the files to a /temp folder for previewing purposes only and then run a cron job every X time to empty it out. But how do I then make use of WordPress functions to move the image from /temp to the gallery once the whole form has been submitted so I can get and attachment id to link to the post?
Notice that I've two forms, one for handling the image, and the global form with all the content that will be posted and that already works since I can post the new post as "draft" and admins have the power to decide. But how to do this for images securely? How to preview an image and put it in the gallery only if the form has been posted successfully?
I know about the FileReader API but I need compatibility for IE8+ so that won't do. I'm also aware of all the Flash and Silverlight solutions but that's not an option either. Also please don't just link to WordPress plugins, I'm trying to learn here.
Ok, it seems I'm answering my own questions again. This is how I solved it. I found a WordPress function media_handle_sideload that lets you upload files from other locations and not only files from the $_FILES array like the previous function.
So I went with my initial approach now that I know about that function. I basically upload the file to a /temp folder for preview purposes and give it a unique id that I store into the hidden field. When the user submits the overall form and passes validation I take the ID that was stored and find out if the file exists and if so I move it to the gallery. This solves most of my concerns about security because even if a malicious user finds an existing unique ID (unlikely but possible) the file wouldn't get removed like before, but just moved into the gallery (not a big deal).
Finally I set-up a cron job to empty out the temp folder every X amount of time.
Using plupload jquery queue plugin as in this example, how can I submit the form on upload complete? I attempted to add a <input type="submit" /> button, and click this button without first clicking the Start upload. This triggers the uploader.start() correctly, and then $('form').submit() - however the $_POST data only contains: 'uploader_count' => string '0' (length=1). If I first click the Start upload button however, the proper POST vars are populated.
How can I trigger .submit() and be sure the correct file upload post parameters are present (E.G. $_POST['uploader_count']) ?
Turns out this is a bug in plupload, and is also present in the official example at:
http://www.plupload.com/example_queuewidget.php
(Queing files, then hit submit only sends POST data $_POST['uploader_count'] == 0) omitting any file info
We added a button which does this:
var plupload = form.find('.plupload-element'), uploader;
event.preventDefault();
if (plupload.length && plupload.pluploadQueue) {
uploader = plupload.pluploadQueue();
uploader.bind('StateChanged', function(uploader) {
// Submit the form if all the files got uploaded.
if (uploader.total && uploader.files && uploader.total.uploaded === uploader.files.length) {
form.trigger('submit');
}
how can I select multiple files to upload like Facebook or Gmail or Flickr?
<button>Upload files</button>
When you press the Upload files button, the OpenDialogBox appears, and you select multiple files using CTRL KEY... then press open and the upload begins...
Thanks!
Right click on the attach link in gmail and you will see your evil friend flash.
This example will not begin the upload before you submit the form, but I will throw it in the mix in case you would just like to upload multiple files with one form submit (using jQuery).
I have used "uploadify" with good results, if you are looking for a Flash script (Be forewarned, Flash does not deal with Session variables).
I like to use the jQuery clone() function. It keeps things simple.
This in your form:
<div id="Uploadcontainer">
<input type="file" name="uploadfiles[]" class="uploadfile" />
</div>
<a id="extraUpload" href="#">Add another field</a>
And this for the jQuery:
/**********************
FILE UPLOAD
***********************/
$(document).ready(function(){
$("#extraUpload").click(function () {
$('.uploadfile:last').clone().appendTo('#Uploadcontainer').val("");
return false;
});
});
When the link with the id extraUpload (#extraUpload) is clicked, the last element in the Document Object Model (DOM, or the last element on "the html page") with the class of uploadfile (.uploadfile:last) is duplicated with clone()... and added to the end of the div #Uploadcontainer with appendTo()... then, the value of the input field that was added is made blank using val() without any value.
Been scratching my head for too long on this: Using jquery.form, (http://malsup.com/jquery/form) with PHP ... my $_FILES['someimage'] gets set but the error number is always UPLOAD_ERR_NO_FILE, size is also 0.
The JavaScript:
$('form input[type=file]').change(function () {
$(this).clone().appendTo('#imgform');
$('#imgform').ajaxForm();
$('#imgform').ajaxSubmit({
type: 'POST'
});
});
Which appends to:
<form id="imgform" method="POST" action="/api/images.php" enctype="multipart/form-data"></form>
From another form which has bog-standard file inputs.
PHP logs are clean, but var_dumping $_FILES always shows that the index is set to the name of the form element ... but no data.
Thanks guys!
(Sorry, I know jQuery-like questions are too frequent round these parts).
EDIT
I found Clone a file input element in Javascript which contains further information and suggested alternatives.
What I decided to do is have a single form for non JavaScript browsers, and JavaScript/jQuery breaks the single form into three forms:
Head form -> File upload form -> tail form
Then I can post the file upload async, and when the tail's submit is clicked, glue the form together into a POST as they are just text fields.
Two things I see when I try to run this. Since you are cloning then appending, I wonder if your file input exists within the context of the form. If not, then $('form input[type=file]') will never find the element to be cloned.
Perhaps the biggest problem, though, is in the way browsers handle file upload controls. You cannot programmatically set a value on a file input control - otherwise it would be trivial as a web developer to automatically set the file upload value to "c:\Files\MyPasswordFile.txt" and automatically submit the form invisibly to the user.
When I changed your code to this:
<input type="file" name="imageFile" />
<form id="imgform" method="POST" action="/api/images.php" enctype="multipart/form-data">
</form>
<script>
$('input[type=file]').change(function() {
alert("ACTION");
$(this).clone().appendTo('#imgform');
//$('#imgform').ajaxForm();
//$('#imgform').ajaxSubmit(
// {
// type: 'POST'
// }
// );
});
</script>
I can see the behavior as above - the field is cloned and appended - but it has no value. Since part of the clone process involves setting the field value - this would violate that security restriction and thus fails.
You can't post files using ajax as javascript cannot access any local hard drive for security reasons.
There are ways to mimic ajax posting using iFrames. This link is a good example.
http://www.ajaxf1.com/tutorial/ajax-file-upload-tutorial.html