Validating image file in custom jQuery, AJAX, and PHP form (SilverStripe) - php

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();
}
}

Related

How I can check and validate the phone number from an HTML page using php?

I am trying to check and validate the phone number from an HTML page.
I am using the following code to check the phone number:
<?php
class Validation {
public $default_filters = array(
'phone' => array(
'regex'=>'/^\(?(\d{3})\)?[-\. ]?(\d{3})[-\. ]?(\d{4})$/',
'message' => 'is not a valid US phone number format.'
)
);
public $filter_list = array();
function Validation($filters=false) {
if(is_array($filters)) {
$this->filters = $filters;
} else {
$this->filters = array();
}
}
function validate($filter,$value) {
if(in_array($filter,$this->filters)) {
if(in_array('default_filter',$this->filters[$filter])) {
$f = $this->default_filters[$this->filters[$filter]['default_filter']];
if(in_array('message',$this->filters[$filter])) {
$f['message'] = $this->filters[$filter]['message'];
}
} else {
$f = $this->filters[$filter];
}
} else {
$f = $this->default_filters[$filter];
}
if(!preg_match($f['regex'],$value)) {
$ret = array();
$ret[$filter] = $f['message'];
return $ret;
}
return true;
}
}
This code is working fine for US phone number validation. But I do not understand how to pass a complete page to extract and check the valid phone number from an HTML page? Kindly help me and make me understand what I can do to fulfill my requirement.
You want to look into cURL.
cURL is a computer software project providing a library and command-line tool for transferring data using various protocols.
You should get the page from your php script (use cURL or whatever you want).
Find the div / input containing the telephone number from the response cURL give you. You can do that with a library like DomXPath (It allow you to navigate through DOM tree).
https://secure.php.net/manual/en/class.domxpath.php
Get the node values from the telephone input and pass it into your validator.
That is the way i would try it.
Your class does not really play any role in the task you want to accomplish because it's just some generic validation code that was never designed to become a scraper. However, the valuable part of it (the regular expression to determine what a US phone number is) is part of a public property so it can be reused in several ways (extend the class or call it from some other class):
public $default_filters = array(
'phone' => array(
'regex'=>'/^\(?(\d{3})\)?[-\. ]?(\d{3})[-\. ]?(\d{4})$/',
'message' => 'is not a valid US phone number format.'
)
);
E.g.:
// Scraper is a custom class written by you
$scraper = new Scraper('http://example.com');
$scraper->findByRegularExpression((new Validation())->default_filters['phone']['message']);
Of course, this is assuming that you cannot touch the validator code.
I cannot really answer the overall question without either writing a long tutorial or the app itself but here's some quick code to get started:
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTMLFile('http://example.com');
libxml_use_internal_errors(false);
$xpath = new DOMXPath($dom);
foreach ($xpath->query('//text()') as $textNode) {
var_dump($textNode->nodeValue);
}

Handle nicely "POST Content-Length of bytes exceeds the limit of" warning

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"]

Drupal - Add a class to an existing button based on URL

I have a Book Now button on each page of my website. I would like to know which button is selected and don't really want to add 25+ blocks to the site to add the class manually. I can use Google Analytics if I can make the button unique (add an additional class based on the page URL). But I'm not a coder although I'm familiar with both PHP and jQuery.
Hye Michael, after reading your question i have tested your scenario on my local test drupal site. And it's really easy to achieve it. Here is a piece of PHP code you need to put into your block you created.
<?php
$url = current_path();
$class_from_url = explode("/", $url);
echo "<a href=[link_to_whatever] class='". $class_from_url[0] ."'>[Link Title]</a>";
?>
Make sure your "PHP filter" module is enabled which will allow you to select PHP code from "Text formats" under the block body.
For Drupal 7, the best way to accomplish your goal would be to copy the theme_button() function to your theme's template.php file and add some custom code to check the URL and add the class.
YOURTHEME_button($vars) {
$element = $variables ['element'];
$element ['#attributes']['type'] = 'submit';
element_set_attributes($element, array('id', 'name', 'value'));
$element ['#attributes']['class'][] = 'form-' . $element ['#button_type'];
if (!empty($element ['#attributes']['disabled'])) {
$element ['#attributes']['class'][] = 'form-button-disabled';
}
// Check URL to determine what button class to add
$button_class = null;
$current_path = base_path() . request_path();
switch ($current_path) {
'/form1':
$button_class = 'button-for-form1';
break;
'/form2':
$button_class = 'button-for-form2';
break;
}
if ($button_class !== null) {
$element ['#attributes']['class'][] = $button_class;
}
return '<input' . drupal_attributes($element ['#attributes']) . ' />';
}
Note that this method will add the class only for URLs that you explicitly specify, and it ignores any user-supplied parameters that might be included as part of the URL.

php, data not updating after form post in zend?

this might be a bit of a novice question and here is my situation:
i have a upload form for uploading images. and in my editAction i do:
if ($request->isPost()) {
if (isset($_POST['upload_picture']) && $formImageUpload->isValid($_POST)) {
//here i will add the picture name to my database and save the file to the disk.
}
}
$picVal = $this->getmainPic(); // here i do a simple fetch all and get the picture that was just uploaded
$this->view->imagepath = $picVal;
what happens is that the newly uploaded picture doesn't show. I checked the database and the dick and the file is there.
im thinking the problem might be the order of the requests or something similar.
any ideas?
edit: another thing is that in order to make the new image come up i have to do a SHIFT+F5 and not only press the browser refresh button
edit2: more code
i first call the upload to disk function then if that returns success addthe file to the database
$x = $this->uploadToDiskMulty($talentFolderPath, $filename)
if($x == 'success'){
$model->create($data);
}
the upload function
public function uploadToDiskMulty($talentFolderPath, $filename)
{
// create the transfer adapter
// note that setDestiation is deprecated, instead use the Rename filter
$adapter = new Zend_File_Transfer_Adapter_Http();
$adapter->addFilter('Rename', array(
'target' => $filename,
'overwrite' => true
));
// try to receive one file
if ($adapter->receive($talentFolderPath)) {
$message = "success";
} else {
$message = "fail";
}
return $message;
}
If the picture only appears when you do SHIFT+F5 that means it's a caching problem. Your browser doesn't fetch the image when you upload it. Do you use the same file name?

Zend Framework: image upload

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.

Categories