Good day) I'm building a WordPress plugin LMS allowing user to attach their completed home assignments files via AJAX to their answers. Everything works fine, except one scenario when user's file exceeds the maximum size allowed. In this case my AJAX call returns HTML with following warning:
<b>Warning</b>: POST Content-Length of XXXXX bytes exceeds the limit of XXXXX bytes in <b>Unknown</b> on line <b>0</b><br />
As this kind of warning generated on string 0 of my script, it will die and ignore any other instructions, leaving me this ugly string as a result of my call.
You basically can live with it, as the final user will not see it (just the AJAX call will not have desired result), but still I want to know if there is a chance to jump it over and handle it nicely?
Ok, what kind of research I've already done:
I found this tutorial by Andrew Curioso, and tried to make something like this from it:
if (isset($_SERVER['CONTENT_LENGTH'])) {
if ($_SERVER['CONTENT_LENGTH'] > (substr(ini_get('post_max_size'), -1) * 1024 * 1024)) {
echo 'php errors is so much fun';
}
}
It doesn't give the desired effect, as my script still dies with only positive effect of echoing additional string from IF statement (besides, if you try to do something like wp_send_json(errorsArray), it will not be executed).
Turning off displaying errors with display_errors, error_reporting. Well, it's not what I need here, as it still does not allow me to proceed with script and create custom error handler.
My WP_DEBUG is set to FALSE
What kind of advice I'm not looking for is manually editing max_upload_size, max_post_size etc. As manually editing server files is out of plugin philosophy. And anyway even if you set your max upload size to 10GB, once you will have final user trying to upload 11GB.
SO, to summarize, as we all know, this mechanism is realized on thousands of sites and apps, and I want to know how to maximize my script quality, handling this issue without overkills and bad UX.
Thank for sharing your thoughts :)
UPD1
Here is my AJAX call if it helps:
$('#post-assignment-answer').on('click', function (event) {
event.preventDefault();
tinymce.triggerSave();
var answerContent = tinymce.get('assignment_user_input_textarea').getContent();
var answerFile = $('#assignment-file-upload')[0].files[0];
var formData = new FormData();
formData.append('action', 'post_assignment_answer');
formData.append('security', lucid_single_assignment_params.post_assignment_answer_nonce);
formData.append('post_id', lucid_single_assignment_params.post_id);
formData.append('answer_content', answerContent);
formData.append('answer_file', answerFile);
$.ajax({
url: lucid_single_assignment_params.ajax_url,
type: 'post',
data: formData,
contentType: false,
processData: false,
success: function (result) {
console.log(result);
}
});
});
UPD2:
Added AJAX handling php
<?php
/**
* Post Assignment Answer
*
* #version 1.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
} // Exit if accessed directly
// Create AJAX call $result
$result = array(
'fileTypeError' => false,
'fileSizeError' => false,
'uploadError' => false
);
// Retrieving current post ID
$post_id = $_POST['post_id'];
// Retrieving WYSIWYG content
$answer_content = '';
if (! (trim($_POST['answer_content']) == '') ) {
$answer_content = wp_kses_post($_POST['answer_content']);
}
// Updating WYSIWYG meta field
update_post_meta($post_id, '_answer_content', $answer_content);
// Check if user intends to upload a file
if (!empty($_FILES['answer_file'])) {
// Adding timestamp to file name
$_FILES['answer_file']['name'] = round(microtime(true)) . '_' . $_FILES['answer_file']['name'];
$answer_file = $_FILES['answer_file'];
// Setting up uploaded file type validation
$supported_types = array(
'text/plain' // .txt
);
$arr_file_type = wp_check_filetype(basename($answer_file['name']));
$uploaded_type = $arr_file_type['type'];
// Setting up uploaded file size validation // TODO: This should be optimized
$allowed_size = 8388608;
$uploaded_size = $answer_file['size'];
// Validating, and in a case of success completing upload
if (!in_array($uploaded_type, $supported_types)) {
$result['fileTypeError'] = __('The type of file you\'ve provided is not allowed', 'lucidlms');
} elseif ($uploaded_size > $allowed_size) {
$result['fileSizeError'] = __('The size of file you\'ve provided is exceeding the maximum upload size', 'lucidlms');
} else {
/**
* Override the default upload path.
*
* #param array $dir
* #return array
*/
function lucidlms_assignment_upload_dir( $dir ) {
global $user_ID;
return array(
'path' => $dir['basedir'] . '/lucidlms/assignment/user' . $user_ID,
'url' => $dir['baseurl'] . '/lucidlms/assignment/user' . $user_ID,
'subdir' => ''
) + $dir;
}
// Register path override
add_filter( 'upload_dir', 'lucidlms_assignment_upload_dir' );
$upload = wp_handle_upload($answer_file, array( 'test_form' => false ));
// Set everything back to normal
remove_filter( 'upload_dir', 'lucidlms_user_upload_dir' );
if (isset($upload['error']) && $upload['error'] != 0) {
$result['uploadError'] = sprintf(__('There was an error uploading your file. The error is: %s', 'lucidlms'), $upload['error']);
} else {
// Check if there is an old version of file on the server and delete it
$existing_answer_file = get_post_meta($post_id, '_answer_file', true);
if (! empty($existing_answer_file)) {
global $user_ID;
$upload_dir = wp_upload_dir();
$existing_answer_file_name = pathinfo($existing_answer_file['file'], PATHINFO_BASENAME);
$unlink_path = $upload_dir['basedir'] . '/lucidlms/assignment/user' . $user_ID . '/' . $existing_answer_file_name;
unlink($unlink_path);
}
// Updating post meta
update_post_meta($post_id, '_answer_file', $upload);
}
}
}
wp_send_json($result);
Do read and understand what the error message is telling you:
exceeds the limit of XXXXX bytes in Unknown on line 0
The error is being reported at line 0 because it is being thrown before your PHP code gets to execute. So you can't change the behaviour in the receiving PHP script.
The right approach is to check the size of the data BEFORE you upload it (i.e. in Javascript). But beware:
Javascript reports the size of individual files before they are encoded - Base64 encoding adds an overhead of around a third, plus space for other POST attribute values and names.
The values configured for post_max_size is the maximum that PHP accepts. The maximum that the webserver (e.g. apache)will accept may be lower. For a plugin, perhaps the most appropriate solution would be to allow the administrator to configure the upper limit.
Someone trying to subvert your security can easily bypass any checks in Javascript
Try this link : http://code.tutsplus.com/tutorials/uploading-files-with-ajax--net-21077
In this at upload.php page (which is the main callback function of ajax which uploads the file) you will get the size of the image uploaded in $_FILES["images"]["size"]
Related
I made a plugin that loads 30 randomly generated names + pictures, using the API from uinames.com The plugin works fine and displays what it should. Like this:
But every few times I refresh I get the following:
Warning
: file_get_contents(https://uinames.com/api/?amount=30®ion=Netherlands&ext): failed to open stream: HTTP request failed! HTTP/1.1 508 Loop Detected in
C:\xampp\htdocs\wordpress\wp-content\themes\twentynineteen\functions.php
Now I'm supposed to resolve this through caching, I've followed several tutorials on it but don't quite grasp it
My code:
class PHP_Widget_wpse_12345 extends WP_Widget {
function __construct() {
$opts = array(
'description' => 'Display catfish'
);
parent::WP_Widget(
'test',
'catfish list',
$opts
);
}
function widget($args,$instance) {
$persons = json_decode(file_get_contents('https://uinames.com/api/?amount=30®ion=Netherlands&ext'));
$widget_content = "";
foreach($persons as $person) {
$widget_content .= $person->name . ' ' . $person->surname . "<br>" . "<br/> <img src='$person->photo'> <br/>";
}
echo $widget_content;
}
}
function register_my_widgets() {
register_widget('PHP_Widget_wpse_12345');
}
add_action('widgets_init','register_my_widgets');
Any help is appreciated!
A method that you could use:
set a flag ($reload = true) to test whether or not to get new file
if the cache file exists
get the date/time of the cache file
is the time > refresh time limit
if yes - set the reload flag to true
if no - set the reload flag to false
if reload flag = false
open file and get contents
otherwise
get info from the external web site
get the contents
save the contents to cache file
In Laravel 5.3, I'm trying to catch if an uploaded file has a bigger file size than upload_max_filesize. The upload field is not required.
I tried this method, but it doesn't work
public function checkFile($field)
{
if (request()->hasFile($field)){ // check if field is present
$file = request()->file($field);
if (!$file->isValid()){ // now check if it's valid
return back()->with('error', $file->getErrorMessage());
}
}
}
I can't use just if (!$file->isValid()) because file field is optional and i get a Call to a member function isValid() on null if field is empty.
So I have to check if field is present using if (request()->hasFile($field)), but this doesn't work for big files, since dd(request()->hasFile('picture')) returns false.
Of course I can rely on default Laravel Validator messages, but I get a dummy The picture failed to upload. that doesn't give any clue to the user.
Laravel Validation will work only if the filesize you are uploading is less than the limit set in php.ini.
If you try to upload a file larger than the limit, PHP will not forward the request to Laravel, and will error straight away. Hence, Laravel cannot do anything in this scenario.
One way to fix this is to set a much larger limit in php.ini and then validating the file size in Laravel.
You should consider using the built in Laravel form request validation system. There is a built in validation rule which lets you specify a max file size, you can check out the docs here:
https://laravel.com/docs/5.3/validation#rule-max
Your rule would look something like this:
[
'video' => 'max:256'
]
This would fail if the file uploaded was a greater size than 256kb.
You mentioned that you didn't like Laravel's built in validation error messages. No problem! You can change them in the resources/lang/en/validation.php language file, this is the line you'd need to change:
https://github.com/laravel/laravel/blob/master/resources/lang/en/validation.php#L51
My previous answer handled the case where an uploaded file was bigger than the upload_max_filesize setting in php.ini. But failed when the size of the file made the Request be larger than the post_max_size (another php.ini setting). This case is harder to handle because the inputs (the $_POST global, if we handled plain PHP) get cleared.
I think a Middleware is a good place to do this "validation":
public function handle(Request $request, Closure $next)
{
$post_max_size = ini_get('post_max_size') * 1024 * 1024;
$content_length = $request->server('HTTP_CONTENT_LENGTH') ?: $request->server('CONTENT_LENGTH') ?: 0;
$response = $next($request);
if ($content_length > $post_max_size)
{
return redirect()->back()->with('errors', collect([trans('validation.max.file', ['max' => 2000])]));
}
return $response;
}
(As I said, this won't preserve the input.)
Server side code(In Controller):
Below function is taken from drupal by meustrus author at his stack answer and I taken here as example.
Start with post_max_size
// Returns a file size limit in bytes based on the PHP upload_max_filesize
// and post_max_size
$max_size = parse_size(ini_get('post_max_size'));
// If upload_max_size is less, then reduce. Except if upload_max_size is
// zero, which indicates no limit.
$upload_max = parse_size(ini_get('upload_max_filesize'));
if ($upload_max > 0 && $upload_max < $max_size) {
$max_size = $upload_max;
}
//Get max upload file size limit...
$file_upload_max_size = $max_size;
Public function to parse size
public function parse_size($size) {
$unit = preg_replace('/[^bkmgtpezy]/i', '', $size); // Remove the non-unit characters from the size.
$size = preg_replace('/[^0-9\.]/', '', $size); // Remove the non-numeric characters from the size.
if ($unit) {
// Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
return round($size * pow(1024, stripos('bkmgtpezy', $unit[0])));
}
else {
return round($size);
}
}
Set compact for send 'file_upload_max_size' value to blade
return view('YOURBLADEPATH',compact('file_upload_max_size'));
JS Validation(In Blade):
<script type="text/javascript">
document.forms[0].addEventListener('submit', function( evt ) {
var file = document.getElementById('file').files[0];
if(file && file.size < '{$file_upload_max_size}') { // 10 MB (this size is in bytes)
//Submit form
} else {
//Prevent default and display error
evt.preventDefault();
}
}, false);
In my experience, I use JavaScript to validate file sizes.
*JavaScript
function validate_size(elm) {
var file = elm.files[0];
var max_size = {{ env('UPLOAD_MAX_FILESIZE', 2097152) }}; // in bytes, e.g. 2 MB = 2097152 Bytes
var size_name = {{ floor(env('UPLOAD_MAX_FILESIZE', 2097152) / 1024 / 1024) }}; // result: 2
if (file && file.size < max_size) {
// CONTINUE
} else {
// PREVENT AND DISPLAY ERROR
alert('File size cannot be greater than ' + size_name + ' MB');
// RESET INPUT FILE VALUE
elm.value = '';
}
}
*HTML/Laravel Blade
...
<input type="file" name="photo" onchange="validate_size(this)">
...
Then it will alert the user if they select a file with a size greater than the allowed size.
The default behaviour for Laravel file validator is to reject the file if the upload was not ok, for whatever reason. Validation rules then are not applied, so a "max" rule can't help you here.
You clearly want in this case a custom message for this type of error (max file size exceeded). I think extending the Validator class is an elegant solution.
use Illuminate\Http\UploadedFile;
use Illuminate\Validation\Validator;
class UploadSizeValidator extends Validator
{
protected function validateAttribute($attribute, $rule)
{
$value = $this->getValue($attribute);
if ($value instanceof UploadedFile && $value->getError() != UPLOAD_ERR_OK) {
switch ($value->getError()) {
case UPLOAD_ERR_INI_SIZE:
return $this->addFailure($attribute, 'max_file_size_exceeded', []);
// check additional UPLOAD_ERR_XXX constants if you want to handle other errors
}
}
return parent::validateAttribute($attribute, $rule);
}
}
Now, how do you tell the framework to use your validator instead of the default one? You can set a resolver function on the Validator Factory:
// do this in a 'boot' method of a ServiceProvider
use Illuminate\Support\Facades\Validator;
Validator::resolver(function($translator, $data, $rules, $messages, $customAttributes) {
return new UploadSizeValidator($translator, $data, $rules, $messages, $customAttributes);
});
Finally set an appropriate message for the key 'max_file_size_exceeded' in the validation.php lang file.
I'm working on finishing a simple form for contest entry submissions. Normally we'd use the userforms SilverStripe extension (https://github.com/silverstripe/silverstripe-userforms) but that is a page type, and this form needs to be one section in a page type. I have got something setup right now that may not be be perfect but has done the job before. However, the inclusion of an image upload field is throwing me off.
Typically, outside of a CMS and without a plugin, I would use $_FILES['upload_field_name']['type or name or size'] to validate an upload field. But I am not sure I am able to use that here since I am working within a CMS.
This is what I have so far:
The jQuery + AJAX code:
(function ($) {
$(document).ready(function () {
var SubmitBtn = $('.SubmitEntry');
var FirstName = $('.FirstNameTxt');
var LastName = $('.LastNameTxt');
var EmailAddress = $('.EmailTxt');
var Image = $('.UploadImage');
SubmitBtn.on('click', function (e) {
var required = [FirstName, LastName, EmailAddress, Image];
var containsError = false;
e.preventDefault();
for (i = 0; i < required.length; i++) {
var input = required[i];
if ((input.val() == "")) {
containsError = true;
input.addClass('error-field');
$('.Error-Msg').show();
} else {
input.removeClass('error-field');
}
}
if (containsError == false) {
$('.Error-Msg').hide();
ajax_script();
}
});
function ajax_script() {
var form_data = {
firstname: FirstName.val(),
lastname: LastName.val(),
useremail: EmailAddress.val(),
image: Image.val()
};
$.ajax({
type: "POST",
url: "/home/ContestSubmission",
data: form_data,
dataType: "json"
}).done(function (response) {
for (var i = 0; i < response.length; i++) {
var status = response[i].status;
if (status == "error") {
var errorMessage = response[i].message;
if (response[i].field == "email") {
$('.Error-Msg').show();
$('.Error-Msg').html("<br />" + errorMessage.message);
EmailAddress.addClass("error-field");
}
/* else if image is not valid, show error message */
}
else if (status == "success") {
$('#contest-submissions')[0].reset();
alert("Thank you for your entry!");
}
}
});
}
});
}(jQuery));
$ = jQuery.noConflict();
This is the ContestSubmisson function located within Page.php:
public function ContestSubmission()
{
$fname = $this->getRequest()->postVar('firstname');
$lname = $this->getRequest()->postVar('lastname');
$useremail = $this->getRequest()->postVar('useremail');
$image = $this->getRequest()->postVar('image');
$errorField = "";
$return = array();
if (!filter_var($useremail, FILTER_VALIDATE_EMAIL)) {
$validatonStatus = "error";
$errorField = "email";
$errorList = "The email is not in the correct format. Please re-enter it.";
} /* else if $image is not valid i.e. size or file type */
else {
$contestEntry = new ContestEntrySubmission();
$contestEntry->FirstName = $fname;
$contestEntry->LastName = $lname;
$contestEntry->EmailAddress = $useremail;
$contestEntry->UploadedImage = $image;
$contestEntry->write();
$validatonStatus = "success";
$errorField = "";
$errorList = "";
$from = 'secret#secret.com';
$to = '[testing email address]';
$subject = 'Contest Entry Submission';
$body = "Below is the contest entry submission information:" . "<br /><br />";
$body .= "<strong>First Name:</strong> " . $fname . "<br/>" . "<strong>Last Name:</strong> " . $lname . "<br/>" . "<strong>Email:</strong> " . $useremail . "<br />" . "Image File: " . $image;
$email = new Email($from, $to, $subject, $body);
$email->replyTo($useremail);
$email->send();
}
$return[] = array(
"status" => $validatonStatus,
"field" => $errorField,
"message" => $errorList
);
return json_encode($return);
}
What is throwing me off is what to do with the image when it gets to the Page.php function shown above. Normally the setup above works fine in SilverStripe but now with the addition of an image upload field, I'm confused on what to change/add/edit to validate the file size and type. Examples I have found tend to deal with creating or working with an Upload Field inside of a page template type, which is not what I am looking for here.
Any suggestions would be great. I am just so used to not building these things inside a CMS. I may be making this much more difficult than needed therefore...any tips?
You mentioned that you just stick to JS, HTML, & PHP - that's cool because using the SilverStripe framework is all PHP! Learning how to make a custom form provides greater flexibility over UserForms, so it's handy to add to the ol' utility belt. It sounds like this is a good opportunity to learn something new, which is always fun :)
First and foremost, the tips to things you're missing is easy! There is a vast set of documentation for developers at https://docs.silverstripe.org/
I highly recommend the forms introduction section, which covers off basic form creation. Once you've defined the fields, SilverStripe will do all the rest for you (all the validation & all), leaving you just to deal with what happens after a valid submission.
Depending on your skill level, you may like to begin with one of the great tutorials - the text one is great and covers all the basics (even though it's quite old), you can access it from the tutorials link in the left menu. There's also a more modern video tutorial at http://www.silverstripe.org/learn/lessons/introduction-to-frontend-forms which can be useful for explaining some of the finer points that sometimes gets beginners a bit turned around. You choose which is best for you. There's also API documentation if you'd prefer to just jump straight into the deep end - you'll find that link on the top navigation.
It seems like you've got the front end part down pat, and SilverStripe doesn't really get in the way here at all. Plus it sounds like you're familiar with styling the UserForms, so that's a great bonus already. So at this point I'll assume that nothing here will be new to you on the front end - the default form rendering won't startle you.
Making the form itself is really easy. I'll use your example and try to cobble a very untested example using the inbuilt Form class (which is part of the Framework, not the CMS - a small but I think important distinction to make).
A form submits to itself, and takes care of all the validation for you. Neat! We'll use your contestSubmission function.
public function contestSubmission() {
return Form::create(
$this,
__FUNCTION__,
FieldList::create(
TextField::create('firstname', 'First Name'),
TextField::create('lastname', 'Last Name'),
EmailField::create('useremail', 'Email'),
FileField::create('image', 'Image')
),
FieldList::create(
FormAction::create('emailValidEntry', 'Submit Entry')
),
RequiredFields::create(array('firstname', 'lastname', 'useremail', 'image'))
);
}
You may be wondering what all this ClassName::create($stuff) business is. Think of it as new ClassName($stuff), but with the advantage that you can chain it (like how $('.selector') gives you a new jQuery object, and you can $('.selector').doThings().to().it() - using the ::create() method we can do the same in PHP, as opposed to creating into a variable then operating on it. A nice thing SilverStripe gives us :)
Once your form is all defined (including adjusting the required fields) you only need to worry about what to do with the info once it's collected, which removes a lot of the burden. Cool. We'll create another function for that: emailValidEntry (the name must match that given in the FormAction declared above) - it basically just contains the bit in your else block. I'll tidy it up for the Email bits to use templates too though.
public function emailValidEntry($data, $form) {
$contestEntry = ContestEntrySubmission::create();
$form->saveInto($contestEntry);
$contestEntry->write();
$data['ImageEmail'] = base64_encode(file_get_contents($data['UploadedImage']['tmp_name']));
Email::create(
'from#address', //from
'testing#address', //to
'Contest Entry Submission' //subject
)->setTemplate('ContestEntryEmail')
->populateTemplate(ArrayData::create($data))
->replyTo($data['EmailAddress'])
->attach($contestEntry->UploadedImage()->Filename)
->send();
$form->setMessage('Thank you for your submission!', 'good');
$this->redirectBack();
}
The 'do stuff with my form submission' method takes 2 arguements: an associative array of FieldName => Value, and the full instance of the Form object itself, with all the fields having their submitted values set.
OK, so there's a slight bit of complexity going on to get the base64 encode for the email embed, although I think attaching it is safer so I've shown that too.
The template ContestEntryEmail.ss (placed somewhere in your theme, maybe templates/emails or something) might look something like:
Below is the contest entry submission information: <br /><br />
<strong>First Name:</strong> $FirstName<br />
<strong>Last Name:</strong> $LastName<br />
<strong>Email:</strong> $EmailAddress<br />
<strong>Image File (see attached if not shown here):</strong><img src="$ImageEmail" alt="Submitted Image" />
Where $ImageEmail uses the base64 encoded image data to embed directly into the email.
But it seems like the recommended way would be to just attach it. From a quick google it seems that Outlook and Gmail both will block the inlined version shown in the template there.
Hopefully this is all rather not too TL;DR & helpful - good luck & happy form creating!
My top tip would be to check the MIME type of the image with PHP. This checks the actual file make-up to verify that it is in fact an image and not just a random file that has been renamed to have an image extension
Here is some sample code you could include
// Check for valid upload
if (($_FILES["picture"]["error"]) == "4") //no file detected
// error codes explained at http://php.net/manual/en/features.file-upload.errors.php
{
echo "<h3>No file has been detected. Please select a file to upload.</h3>";
exit();
}
if ($_FILES['picture']['error'] == "1" || $_FILES['picture']['error'] == "2" || ($_FILES['picture']['size'] > 71680)) //define your max_file_size 71680 is 70KB - not very big
//file too big
{
echo("<h3>Upload unsuccessful: File exceeds maximum allowed size</h3>");
echo("<p>Try compressing your photo or upload a different photo</p>");
exit();
}
//if file present and of acceptable size
if ($_FILES['picture']['error'] != "1" && $_FILES['picture']['error'] != "2" && $_FILES['picture']['error'] != "4" && $_FILES['picture']['size'] < 71680) {
//then check to see if the type of file is an image file
//check for mimetype as at http://php.net/manual/en/features.file-upload.php
$fileinfo = new finfo(FILEINFO_MIME_TYPE);
if (false === $ext = array_search(
$fileinfo->file($_FILES['picture']['tmp_name']),
array(
'png' => 'image/png','gif' => 'image/gif','jpg' => 'image/jpeg',
),
true
))
{
echo('<h3>The file chosen is not an acceptable image file (.jpg/.png or .gif)</h3>');
echo("Please upload a .jpg/.png or .gif image");
exit();
}
}
I am trying to install a Magento package, but I get No file was uploaded
Its coming from this code because $_FILES is an empty array in /downloader/Maged/Controller.php
/**
* Install uploaded package
*/
public function connectInstallPackageUploadAction()
{
if (!$_FILES) {
echo "No file was uploaded";
return;
}
if(empty($_FILES['file'])) {
echo "No file was uploaded";
return;
}
$info =& $_FILES['file'];
if(0 !== intval($info['error'])) {
echo "File upload problem";
return;
}
$target = $this->_mageDir . DS . "var/" . uniqid() . $info['name'];
$res = move_uploaded_file($info['tmp_name'], $target);
if(false === $res) {
echo "Error moving uploaded file";
return;
}
$this->model('connect', true)->installUploadedPackage($target);
#unlink($target);
}
It might be worth noting that product uploads work fine.
The only log output I get is
2014-07-03T18:44:15+00:00 ERR (3): Warning: array_key_exists() expects parameter 2 to be array, null given in /var/www/vhosts/example.com/httpdocs/app/code/core/Mage/Captcha/Model/Observer.php on line 166
exception.log was empty
Make sure that your var folder in magento installation is fully writable. 777 permission. All folders and files.
You can try uploading a small dummy file first to check if the error stays the same.
There is a file upload limit which might be reached.
File upload often fails due to upload_max_filesize or post_max_size being too small as mentioned in Common Pitfalls section of the PHP documentation.
Use firebug in firefox to check if your form does has enctype="multipart/form-data".
Check the user group it was created with,
To explain, recently I had some file saving issues. Turned out I had created the folder using the Root user for the server, and the CPanel user ( the one php was running under ) didn't have permission to write in folders owned by the Root account, even when setting the permissions to 777.
Just a thought.
First check if your installation is configured properly
see#http://php.net/manual/en/features.file-upload.common-pitfalls.php
Also, if you upload with PUT/xhr the file is on the input stream
$in = fopen('php://input','r');
see#http://php.net/manual/en/features.file-upload.put-method.php and https://stackoverflow.com/a/11771857/2645347,
this would explain the empty $FILES array, in case all else is ok and the upload works via xhr/PUT.
$_FILES is an associative array of items uploaded to the current script via the HTTP POST method. All uploaded files are stored in $HTTP_POST_FILES contains the same initial information, but is not a superglobal. So, ... be sure that method is POST
Always check that your form contains correct enctype:
<form ... enctype="multipart/form-data"> ... </form>
Sometimes happens that when someone upload multiples file, $_FILES return empty. This could happen when I select files that exceed some size. The problem can be in the POST_MAX_SIZE configuration.
On
app/code/core/mage/captcha/model/observer.php
change
public function checkUserLoginBackend($observer)
{
$formId = 'backend_login';
$captchaModel = Mage::helper('captcha')->getCaptcha($formId);
$loginParams = Mage::app()->getRequest()->getPost('login');
$login = array_key_exists('username', $loginParams) ? $loginParams['username'] : null;
if ($captchaModel->isRequired($login)) {
if (!$captchaModel->isCorrect($this->_getCaptchaString(Mage::app()->getRequest(), $formId))) {
$captchaModel->logAttempt($login);
Mage::throwException(Mage::helper('captcha')->__('Incorrect CAPTCHA.'));
}
}
$captchaModel->logAttempt($login);
return $this;
}
to
public function checkUserLoginBackend($observer)
{
$formId = 'backend_login';
$captchaModel = Mage::helper('captcha')->getCaptcha($formId);
$login = Mage::app()->getRequest()->getPost('username');
if ($captchaModel->isRequired($login)) {
if (!$captchaModel->isCorrect($this->_getCaptchaString(Mage::app()->getRequest(), $formId))) {
$captchaModel->logAttempt($login);
Mage::throwException(Mage::helper('captcha')->__('Incorrect CAPTCHA.'));
}
}
$captchaModel->logAttempt($login);
return $this;
}
Your issue is:
"Captcha Observer throws an error if login in RSS feed" issue #208
or if you wish you could only replace the variable $login to be like this:
$login = array_key_exists('username', array($loginParams)) ? $loginParams['username'] : null;
You may try out below points.
Use Magento Varien File Uploaded Classes to Upload the files.
Magento File Uploader
1) Check enctype="multipart/form-data" in your form.
2) Use Magento Form Key in your form.
3) Use Varien file uploader to upload your files using below links answers.
I want to upload an image with Zend Framework version 1.9.6. The uploading itself works fine, but I want a couple of other things as well ... and I'm completely stuck.
Error messages for failing to upload an image won't show up.
If a user doesn't enter all the required fields but has uploaded an image then I want to display the uploaded image in my form. Either as an image or as a link to the image. Just some form of feedback to the user.
I want to use Zend_ Validate_ File_ IsImage. But it doesn't seem to do anything.
And lastly; is there some automatic renaming functionality?
All ideas and suggestions are very welcome. I've been struggling for two days now.
These are simplified code snippets:
myform.ini
method = "post"
elements.title.type = "text"
elements.title.options.label = "Title"
elements.title.options.attribs.size = 40
elements.title.options.required = true
elements.image.type = "file"
elements.image.options.label = "Image"
elements.image.options.validators.isimage.validator = "IsImage"
elements.submit.type = "submit"
elements.submit.options.label = "Save"
TestController
<?php
class Admin_TestController extends Zend_Controller_Action
{
public function testAction ()
{
$config = new Zend_Config_Ini(MY_SECRET_PATH . 'myform.ini');
$f = new Zend_Form($config);
if ($this->_request->isPost())
{
$data = $this->_request->getPost();
$imageElement = $f->getElement('image');
$imageElement->receive();
//$imageElement->getValue();
if ($f->isValid($data))
{
//save data
$this->_redirect('/admin');
}
else
{
$f->populate($data);
}
}
$this->view->form = $f;
}
}
?>
My view just echo's the 'form' variable.
First, put this at the start of your script:
error_reporting(E_ALL);//this should show all php errors
I think the error messages are missing from the form because you re-populate the form before you display it. I think that wipes out any error messages. To fix that, remove this part:
else
{
$f->populate($data);
}
To show the uploaded image in the form, just add a div to your view template, like this:
<div style="float:right"><?=$this->image?></div>
If the image uploaded ok, then populate $view->image with an img tag.
As for automatic re-naming, no, it's not built in, but it's very easy. I'll show you how below.
Here's how I handle my image uploads:
$form = new Zend_Form();
$form->setEnctype(Zend_Form::ENCTYPE_MULTIPART);
$image = new Zend_Form_Element_File('image');
$image->setLabel('Upload an image:')
->setDestination($config->paths->upload)
->setRequired(true)
->setMaxFileSize(10240000) // limits the filesize on the client side
->setDescription('Click Browse and click on the image file you would like to upload');
$image->addValidator('Count', false, 1); // ensure only 1 file
$image->addValidator('Size', false, 10240000); // limit to 10 meg
$image->addValidator('Extension', false, 'jpg,jpeg,png,gif');// only JPEG, PNG, and GIFs
$form->addElement($image);
$this->view->form = $form;
if($this->getRequest()->isPost())
{
if(!$form->isValid($this->getRequest()->getParams()))
{
return $this->render('add');
}
if(!$form->image->receive())
{
$this->view->message = '<div class="popup-warning">Errors Receiving File.</div>';
return $this->render('add');
}
if($form->image->isUploaded())
{
$values = $form->getValues();
$source = $form->image->getFileName();
//to re-name the image, all you need to do is save it with a new name, instead of the name they uploaded it with. Normally, I use the primary key of the database row where I'm storing the name of the image. For example, if it's an image of Person 1, I call it 1.jpg. The important thing is that you make sure the image name will be unique in whatever directory you save it to.
$new_image_name = 'someNameYouInvent';
//save image to database and filesystem here
$image_saved = move_uploaded_file($source, '/www/yoursite/images/'.$new_image_name);
if($image_saved)
{
$this->view->image = '<img src="/images/'.$new_image_name.'" />';
$form->reset();//only do this if it saved ok and you want to re-display the fresh empty form
}
}
}
First, have a look at the Quick Start tutorial. Note how it has an ErrorController.php that will display error messages for you. Also note how the application.ini has these lines to cause PHP to emit error messages, but make sure you're in the "development" environment to see them (which is set in public/.htaccess).
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
Second, ZF has a renaming filter for file uploads:
$upload_elt = new Zend_Form_Element_File('upload');
$upload_elt
->setRequired(true)
->setLabel('Select the file to upload:')
->setDestination($uploadDir)
->addValidator('Count', false, 1) // ensure only 1 file
->addValidator('Size', false, 2097152) // limit to 2MB
->addValidator('Extension', false, 'doc,txt')
->addValidator('MimeType', false,
array('application/msword',
'text/plain'))
->addFilter('Rename', implode('_',
array($this->_user_id,
$this->_upload_category,
date('YmdHis'))))
->addValidator('NotExists', false, $uploadDir)
;
Some of the interesting things above:
mark the upload as required (which your .ini doesn't seem to do)
put all the uploads in a special directory
limit file size and acceptable mime types
rename upload to myuser_category_timestamp
don't overwrite an existing file (unlikely, given our timestamp scheme, but let's make sure anyway)
So, the above goes in your form. In the controller/action that receives the upload, you could do this:
$original_filename = $form->upload->getFileName(null, false);
if ($form->upload->receive()) {
$model->saveUpload(
$this->_identity, $form->upload->getFileName(null, false),
$original_filename
);
}
Note how we capture the $original_filename (if you need it) before doing receive(). After we receive(), we do getFileName() to get the thing that the rename filter picked as the new filename.
Finally, in the model->saveUpload method you could store whatever stuff to your database.
Make sure your view also outputs any error messages that you generate in the controller: loading errors, field validation, file validation. Renaming would be your job, as would other post processing such as by image-magick convert.
When following lo_fye's listing I experienced problems with custom decorators.
I do not have the default File Decorator set and got the following exception:
Warning: Exception caught by form: No file decorator found... unable to render file element Stack Trace:
The Answer to this is that one of your decrators must implement the empty interface Zend_Form_Decorator_Marker_File_Interface
Also sometimes it happens to bug when using an ajax request. Try it without an ajax request and don't forget the multipart form.