Codeigniter FILE upload validation failure - validation

My app uses Codeigniter validation successfully in multiple controller for various input fields. But when it comes to uploading and validating an uploaded image the validation complains.
I have the following form:
require_once('head.php');
echo 'Update Avatar';
if(validation_errors())
echo ''.validation_errors().'';
if($info)
echo '<div class="info">'.$info.'</div>';
$attributes = array('class' => 'updateavatarform', 'id' => 'updateavatarform');
echo form_open_multipart('user/avatar', $attributes);
echo '<div>Select Image</div>';
$data = array(
'name' => 'avatar',
'id' => 'avatar',
'value' => '',
'maxlength' => '',
'size' => '48',
);
echo form_upload($data);
echo '<br/><br/>';
echo form_submit('submit', 'Submit');
echo form_close();
require_once('footer.php');
The controller looks like:
function avatar()
{
$data['user'] = $this->authorize->isLoggedIn();
if(!$data['user'])
$this->authorize->protectUser();
$data['title'] = 'Evcenter User Update Avatar';
$data['keywords'] = 'alternative fuel';
$data['description'] = 'evcenter.org';
$data['info'] = FALSE;
if($_POST)
{
$this->load->model('User_model', '', TRUE);
$this->load->library('form_validation');
$input['avatar'] = trim($this->input->post('avatar', TRUE));
$this->form_validation->set_rules('avatar', 'Avatar', 'trim|required|xss_clean');
if($this->form_validation->run() == FALSE)
{
$this->load->view('avatar', $data);
}
else
{
$avatar = $this->User_model->getAvatar($data['user']['user_id']);
$old_avatar = $this->config->item('avatarpath').$avatar['avatar'];
unset($old_avatar);
$input['avatar'] = $this->uploadAvatar();
$input['id'] = $data['user']['user_id'];
$this->User_model->updateAvatar($input);
$data['info'] = 'Your avatar has been updated';
$this->load->view('avatar', $data);
}
}
else
{
$this->load->view('avatar', $data);
}
}
The validation throws the following error with or w/o an uploaded image "The Avatar field is required." Needless to say $this->uploadAvatar(); works when called from the register controller.
Can anyone see what's wrong?
Do FILE uploads need to be validated differently than text input?

Correct, files need to be validated differently than text inputs - as they are, not text inputs !
From the docs:
function do_upload()
{
$config['upload_path'] = './uploads/';
$config['allowed_types'] = 'gif|jpg|png';
$config['max_size'] = '100';
$config['max_width'] = '1024';
$config['max_height'] = '768';
$this->load->library('upload', $config);
if ( ! $this->upload->do_upload())
{
$error = array('error' => $this->upload->display_errors());
$this->load->view('upload_form', $error);
}
else
{
$data = array('upload_data' => $this->upload->data());
$this->load->view('upload_success', $data);
}
}
note the call to $this->upload->do_upload() and $this->upload->display_errors()
Uploading in CI with text fields can be annoying (imo), I would upload the file first in your controller, then if it is successful do the rest of the POST data and update your database.
That way if there's a problem with the upload later on, you won't have invalid records.

*Assuming you already happen to have a MY_Form_Validation library that extends the built in CodeIgniter stuff.*
First
I added some functions to validate portions of the $_FILE upload based on this:
http://devbro.com/testing/ci_form_validation/
I just copied the functions, not the entire file. I don't need the custom run or execute methods. Just the validation methods. (I already have a customization that allows me to mix the form_validation config file AND the controller rules together.)
/**
* tests to see if a required file is uploaded
*
* #param mixed $file
*/
function file_required($file)
{
if($file['size']===0)
{
$this->CI->form_validation->set_message('file_required','Uploading a file for %s is required.');
return FALSE;
}
return TRUE;
}
/**
* tests the file extension for valid file types
*
* #param mixed $file
* #param mixed $type
*/
function file_allowed_type($file,$type)
{
//is type of format a,b,c,d? -> convert to array
$exts = explode(',',$type);
//is $type array? run self recursively
if(count($exts)>1)
{
foreach($exts as $v)
{
$rc = $this->file_allowed_type($file,$v);
if($rc===TRUE)
{
return TRUE;
}
}
}
//is type a group type? image, application, word_document, code, zip .... -> load proper array
$ext_groups = array();
$ext_groups['image'] = array('jpg','jpeg','gif','png');
$ext_groups['application'] = array('exe','dll','so','cgi');
$ext_groups['php_code'] = array('php','php4','php5','inc','phtml');
$ext_groups['word_document'] = array('rtf','doc','docx');
$ext_groups['compressed'] = array('zip','gzip','tar','gz');
$ext_groups['pdf'] = array('pdf');
if(array_key_exists($exts[0],$ext_groups))
{
$exts = $ext_groups[$exts[0]];
}
//get file ext
$file_ext = strtolower(strrchr($file['name'],'.'));
$file_ext = substr($file_ext,1);
if(!in_array($file_ext,$exts))
{
$this->CI->form_validation->set_message('file_allowed_type',"%s should be $type.");
return false;
}
else
{
return TRUE;
}
}
etc, etc.
Then
I added the rules I wanted to my form_validation.php config file, but I treat my FILE input as if were included in the $_POST. Of course it isn't, but I will fix that in a moment. DON'T use the other types of validations built into CodeIgniter, only use your FILE validations!
$config = array(
'form/index' => array(
array( 'field'=>'my_upload_file', 'label'=>'File To Upload', 'rules'=>'file_required|file_allowed_type[pdf]'),
...
Finally
In my controller, add the $_FILE['my_upload_file'] to the $_POST array
if ( isset($_FILES['my_upload_file']) )
{
$_POST['my_upload_file'] = $_FILES['my_upload_file'];
}
I think the big caveat is if you use the $_POST to populate your models or other actions. My code projects specifically grabs elements out of input->post() and I don't use mass assignment for much of anything. If you do use mass assignment, I would assume you messed up your assumptions.

Related

Codeigniter 3 application bug: converting title to slug does not work if there are diacritics in the title

I am working on a basic blog application with Codeigniter 3.1.8 and Bootstrap 4.
The single post URL is made from the post's title with url_title($this->input->post('title'), 'dash', TRUE). The entire post crearing function is this:
public function create() {
// Only logged in users can create posts
if (!$this->session->userdata('is_logged_in')) {
redirect('login');
}
$data = $this->get_data();
$data['tagline'] = "Add New Post";
if ($data['categories']) {
foreach ($data['categories'] as &$category) {
$category->posts_count = $this->Posts_model->count_posts_in_category($category->id);
}
}
$this->form_validation->set_rules('title', 'Title', 'required');
$this->form_validation->set_rules('desc', 'Short description', 'required');
$this->form_validation->set_rules('body', 'Body', 'required');
$this->form_validation->set_error_delimiters('<p class="error-message">', '</p>');
if($this->form_validation->run() === FALSE){
$this->load->view('partials/header', $data);
$this->load->view('dashboard/create-post');
$this->load->view('partials/footer');
} else {
// Create slug (from title)
$slug = url_title($this->input->post('title'), 'dash', TRUE);
$slugcount = $this->Posts_model->slug_count($slug, null);
if ($slugcount > 0) {
$slug = $slug."-".$slugcount;
}
// Upload image
$config['upload_path'] = './assets/img/posts';
$config['allowed_types'] = 'jpg|jpeg|png';
$config['max_size'] = '2048';
$this->load->library('upload', $config);
if(!$this->upload->do_upload()){
$errors = array('error' => $this->upload->display_errors());
$post_image = 'default.jpg';
} else {
$data = array('upload_data' => $this->upload->data());
$post_image = $_FILES['userfile']['name'];
}
$this->Posts_model->create_post($post_image, $slug);
$this->session->set_flashdata('post_created', 'Your post has been created');
redirect('/');
}
}
I was convinced it worked fine, until I added a post that had diacritics in its title. "La mulți ani România!" results in the slug la-mul??i-ani-rom??nia instead of la-multi-ani-romania.
I tried to fix thos issues with Codeigniter's convert_accented_characters():
$slug = convert_accented_characters(url_title($this->input->post('title'), 'dash', TRUE));
It does not work even thou I did load the test helper:
$autoload['helper'] = array('url', 'form', 'date', 'text');
What other options do I have? How can I fix this problem?
The order in which I used the url_title() and convert_accented_characters() was wrong.
This is the correct one:
$slug = url_title(convert_accented_characters($this->input->post('title')), 'dash', TRUE);
It works fine now.

In Drupal, how to validate the contents of a file uploaded using webforms and *not* upload if it fails validation?

I am implementing a site that accepts archives with directory structure in a specific fashion. I want to check the directory structure in the zipfile before accepting it. I tried the following (please see comments inline):
<?php
Using Webform Validation:
// using the webform validation module and its hooks
function testsuite_ziptest_webform_validation_validators()
{
return array(
"validate_zip_file"=> array(
'name' => "testsuite: Validate Zipfile" ,
'component_types' => array(
'select',
'file',
),
'description' => t('Verifies that the contents of the zipfile adhere to the specifications of testsuite.'),
)
);
}
function testsuite_ziptest_webform_validation_validate($validator_name, $items, $components, $rule)
{
$errors = array();
if($items)
{
switch($validator_name)
{
case "validate_zip_file":
drupal_set_message(t('Validate function called'));
foreach($items as $key=>$value)
{
drupal_set_message($key);
$v = _webform_validation_flatten_array($value);
drupal_set_message($v);
}
// tried to get the $fid and access the file using the fid.
// item_6 is the key of the file field that I selected while
// enabling webform validation.
// This fails saying no such file exists when the ziparchive
// object tries to open it.
$fid = $items['item_6'];
if(!empty($fid))
{
$za = new ZipArchive();
$file = file_load($fid);
$za->open($file->uri);
for($i = 0; $i < $za->numFiles; $i++)
{
$stat = $za->statIndex($i);
drupal_set_message($stat['name']);
}
$za->close();
}
break;
}
}
return $errors;
}
Using hook_file_validate
// this works, but there might be other files that may
// be uploaded to the site and I only want it to trigger
// when the file is uploaded as a part of a webform, not
// for all file uploads.
function testsuite_ziptest_file_validate($file)
{
if(!empty($file->filename))
{
$za = new ZipArchive();
$za->open($file->uri);
for($i = 0; $i < $za->numFiles; $i++)
{
$stat = $za->statIndex($i);
drupal_set_message($stat['name']);
}
$za->close();
}
}
Using Forms API (?)
// The following two methods that uses the form api on the webform
// has the same issue as the webform validation module. I can't get
// any reference to the file.
// There is a reference through a "completed form" key but I don't know
// if this is best practice
// die statements were used for debugging
function testsuite_ziptest_form_alter(&$form, &$form_state, $form_id)
{
if($form_id == 'webform_client_form_1')
{
array_unshift($form['#validate'], 'testsuite_ziptest_form_validate');
return $form;
}
}
function testsuite_ziptest_form_validate($form, &$form_state)
{
echo '<pre>'; print_r($form_state); echo '</pre>';
die();
$fid = $form_state['values']['submitted']['attachment'];
if(!empty($fid))
{
$za = new ZipArchive();
$file = file_load($fid);
$za->open($file->uri);
for($i = 0; $i < $za->numFiles; $i++)
{
$stat = $za->statIndex($i);
drupal_set_message($stat['name']);
}
$za->close();
}
else
{
}
die();
return;
}
Thanks!
I think you miss a point when you've done your function. You simply forgot to send the error back.
In the Webforms validation process, you have to send back some elements in the $errors array if something gone wrong.
$errors[$key] = t('%item is not good', array('%item' => $components[$key]['name']));
Here is an example of using this method.
In the Drupal form validation process, you have to use form_set_error if something is not good, with the combo name and the error message. The validation then stop automatically and the form will not be submitted...
And in the hook_file_validate method, you also have to send back an array of errors, witch will be use by the validator to stop the submission (with form_set_error).
Its Working Example :
function yourmoduleNma_file_validate($file,$validators)
{
$errors = array();
$filename = $file->filename;
$isValid_Extention_Size = explode('.',$filename);
if(count($isValid_Extention_Size) > 2){
$errors[] = 'Extention is not valid of this file';
}
elseif($file->filesize <= 0)
{
$errors[] = 'File size should be greater than 0 Bytes and less than 5 MB';
}
return $errors;
}

codeigniter uploading image from form

I have a field of my form (which is uploading personal picture). So the user selects image from pc and submit the form. I am later on handling all posted data via:
$this->input->post()
My method for insertion into database is:
public function add_user()
{
$data = array(
'membership'=>$this->input->post('membership_type'),
'fullname'=>$this->input->post('fullname'),
'username'=>$this->input->post('username'),
'password'=>md5($this->input->post('password')),
'email'=>$this->input->post('email'),
'city'=>$this->input->post('city'));
'profilepic'=>$this->input->post('profilepic'));
$this->db->insert('members',$data);
}
Now what I want to insert in the profilepic field is the path to the image on the server. I know am doing it wrong above because this way am inserting the picture posted to profilepic. I need some correction please. Is there a function that can perform upload and return the path? but again how can I associate the upload of picture with upload of user data?
Regards,
EDIT: Tried the code provided below and got this:
A PHP Error was encountered
Severity: Notice
Message: Use of undefined constant full_path - assumed 'full_path'
Filename: models/membership_model.php
Line Number: 29
A PHP Error was encountered
Severity: Warning
Message: Cannot modify header information - headers already sent by
(output started at
/home2/xsysdeve/public_html/system/core/Exceptions.php:185)
Filename: core/Common.php
Line Number: 438
Please try this one:
public function add_user()
{
$config['upload_path'] = '/file_path/';
$config['allowed_types'] = 'gif|jpg|png|jpeg';
$this->load->library('upload', $config);
$this->upload->do_upload('profilepic');
$data_upload_files = $this->upload->data();
$image = $data_upload_files[full_path];
$data = array(
'membership'=>$this->input->post('membership_type'),
'fullname'=>$this->input->post('fullname'),
'username'=>$this->input->post('username'),
'password'=>md5($this->input->post('password')),
'email'=>$this->input->post('email'),
'city'=>$this->input->post('city'));
'profilepic'=>$image;
$this->db->insert('members',$data);
}
For more information visit this link: http://ellislab.com/codeigniter/user-guide/libraries/file_uploading.html
You can also try using 2 CI functions and check for the POST of the textual data as well as the image validation:
class SomeForm extends CI_Controller{
function add_user() //this function is your form
{
$this->load->view('templates/header');
$this->load->view('new_form_entry', array('error' => ' ' ));
$this->load->view('templates/footer');
}
function add_user_status() //this function is your form processing
{
$config['upload_path'] = './assets/images';
$config['allowed_types'] = 'gif|jpg|png';
//$config['max_size'] = '2000';
//$config['max_width'] = '340';
//$config['max_height'] = '190';
$this->load->library('upload', $config);
if ( ! $this->upload->do_upload())
{
//check for errors with the upload
$error = array('error' => $this->upload->display_errors());
$this->load->view('templates/header');
$this->load->view('new_form_entry', $error);
$this->load->view('templates/footer');
}
else
{
//upload the new image
$upload_data = $this->upload->data();
$image_name = $upload_data['file_name'];
if($_POST){
$data = array(
'author'=>$_POST['author'],
'name'=>$_POST['name'],
'image_thumb'=>'../assets/images/'.$image_name,
'video'=>$_POST['video'],
'title'=>$_POST['title'],
'body'=>$_POST['body'],
'created' => date('Y-m-d H:i:s')
);
//insert the new post
$this->your_database_model_name->insert_entry($data);
}
redirect(base_url().'SomeForm/index');
}
}
}

How to output uploading errors as flashdata - CodeIgniter

I'm wanting to display any errors produced while uploading a file by redirecting the user back to the same page they used to upload the file. The thing is that I pre-populate some inputs on that page, so I can't use redirect(controller/method_to_load_page) as that wouldn't carry out the data population.
Using $this->load->view('add_page',$error); won't work either (the way that I'm using it), because although that will show the errors, it won't show the input fields with the pre-populated data.
This is the method in my controller which loads the form to upload a file and other fields:
public function add_products_page()
{
//get each categories subcategory and place them in their own variable
$data['categories1'] = $this->admin_model->getSubcategories('1');
$data['error'] = '';
$this->load->view('add_page',$data);
}
And from that page, the user can fill in the form and choose an image. The form will then be submitted to this method in the same controller:
public function add_book()
{
$id = $this->admin_model->add_book();
$config['file_name'] = $id;
$config['upload_path'] = './images/';
$config['allowed_types'] = 'jpg';
$this->load->library('upload', $config);
if ( ! $this->upload->do_upload())
{
$error = array('error' => $this->upload->display_errors());
$this->load->view('add_page', $error);
}
else
{
redirect('admin/add_products_page', 'refresh');
}
}
If there are no errors, the redirect to add_products_page() method is okay, however when there's an error, the view 'add_page' is loaded and the errors are shown, but my pre-populated field isn't populated anymore because the method to do that isn't being called.
So I really would like to know how I can both load my view by calling the method add_products_page to populate my input fields, and also show errors when they occur. Please if someone could help I would appreciate it greatly.
Why post to a different method and then redirect? You can handle it all in the same method.
public function add_products_page()
{
if ($this->input->server('REQUEST_METHOD') == 'POST')
{
$result = $this->_add_book();
if ($result['is_error'])
{
// TODO: whatever it takes to show an error message
$data['error'] = $result['message'];
}
else
{
// TODO: whatever it takes to show a success message
$data['error'] = '';
}
}
//get each categories subcategory and place them in their own variable
$data['categories1'] = $this->admin_model->getSubcategories('1');
$this->load->view('add_page',$data);
}
public function _add_book() // add underscore so it's not directly accessible
{
$result = array(
'is_error' => TRUE,
'message' => '',
);
$id = $this->admin_model->add_book();
$config['file_name'] = $id;
$config['upload_path'] = './images/';
$config['allowed_types'] = 'jpg';
$this->load->library('upload', $config);
if ( ! $this->upload->do_upload())
{
$error = array('error' => $this->upload->display_errors());
// set an error message
$result['message'] = $error;
}
else
{
// set a success message
$result['is_error'] = FALSE;
$result['message'] = 'SOME SUCCESS MSG';
}
return $result;
}

Codeigniter - form validation doesn't work for files

i need to set a input file as required into my Codeigniter Controller.
This is my form_validation:
$this->form_validation->set_rules('copertina','Foto principale','required|xss_clean');
and this is the form:
<?php echo form_open_multipart('admin/canile/nuovo'); ?>
<li class="even">
<label for="copertina">Foto principale <span>*</span></label>
<div class="input"><input type="file" name="copertina" value="<?php echo set_value('copertina'); ?>" id="copertina" /></div>
</li>
<?php echo form_close(); ?>
But after the submit the form say that the file is not set, so the required clausole fails...how can i fix it?
File upload data is not stored in the $_POST array, so cannot be validated using CodeIgniter's form_validation library. File uploads are available to PHP using the $_FILES array.
It maybe possible to directly manipulate the $_POST array using data from the $_FILES array, before running form validation, but I haven't tested this. It's probably best to just check the upload library process for errors.
In addition, it is not possible, for security reasons, to (re-)set the value on page reload.
To make validation to work for files you have to check whether is it empty.
like,
if (empty($_FILES['photo']['name']))
{
$this->form_validation->set_rules('userfile', 'Document', 'required');
}
you can solve it by overriding the Run function of CI_Form_Validation
copy this function in a class which extends CI_Form_Validation .
This function will override the parent class function . Here i added only a extra check which can handle file also
/**
* Run the Validator
*
* This function does all the work.
*
* #access public
* #return bool
*/
function run($group = '') {
// Do we even have any data to process? Mm?
if (count($_POST) == 0) {
return FALSE;
}
// Does the _field_data array containing the validation rules exist?
// If not, we look to see if they were assigned via a config file
if (count($this->_field_data) == 0) {
// No validation rules? We're done...
if (count($this->_config_rules) == 0) {
return FALSE;
}
// Is there a validation rule for the particular URI being accessed?
$uri = ($group == '') ? trim($this->CI->uri->ruri_string(), '/') : $group;
if ($uri != '' AND isset($this->_config_rules[$uri])) {
$this->set_rules($this->_config_rules[$uri]);
} else {
$this->set_rules($this->_config_rules);
}
// We're we able to set the rules correctly?
if (count($this->_field_data) == 0) {
log_message('debug', "Unable to find validation rules");
return FALSE;
}
}
// Load the language file containing error messages
$this->CI->lang->load('form_validation');
// Cycle through the rules for each field, match the
// corresponding $_POST or $_FILES item and test for errors
foreach ($this->_field_data as $field => $row) {
// Fetch the data from the corresponding $_POST or $_FILES array and cache it in the _field_data array.
// Depending on whether the field name is an array or a string will determine where we get it from.
if ($row['is_array'] == TRUE) {
if (isset($_FILES[$field])) {
$this->_field_data[$field]['postdata'] = $this->_reduce_array($_FILES, $row['keys']);
} else {
$this->_field_data[$field]['postdata'] = $this->_reduce_array($_POST, $row['keys']);
}
} else {
if (isset($_POST[$field]) AND $_POST[$field] != "") {
$this->_field_data[$field]['postdata'] = $_POST[$field];
} else if (isset($_FILES[$field]) AND $_FILES[$field] != "") {
$this->_field_data[$field]['postdata'] = $_FILES[$field];
}
}
$this->_execute($row, explode('|', $row['rules']), $this->_field_data[$field]['postdata']);
}
// Did we end up with any errors?
$total_errors = count($this->_error_array);
if ($total_errors > 0) {
$this->_safe_form_data = TRUE;
}
// Now we need to re-set the POST data with the new, processed data
$this->_reset_post_array();
// No errors, validation passes!
if ($total_errors == 0) {
return TRUE;
}
// Validation fails
return FALSE;
}
Have you looked at this ->
http://codeigniter.com/user_guide/libraries/file_uploading.html
<?php
class Upload extends CI_Controller {
function __construct()
{
parent::__construct();
$this->load->helper(array('form', 'url'));
}
function index()
{
$this->load->view('upload_form', array('error' => ' ' ));
}
function do_upload()
{
$config['upload_path'] = './uploads/';
$config['allowed_types'] = 'gif|jpg|png';
$config['max_size'] = '100';
$config['max_width'] = '1024';
$config['max_height'] = '768';
$this->load->library('upload', $config);
if ( ! $this->upload->do_upload())
{
$error = array('error' => $this->upload->display_errors());
$this->load->view('upload_form', $error);
}
else
{
$data = array('upload_data' => $this->upload->data());
$this->load->view('upload_success', $data);
}
}
}
?>
Update as per comment:
You can check using plain php if you like ...
$errors_file = array(
0=>'Success!',
1=>'The uploaded file exceeds the upload_max_filesize directive in php.ini',
2=>'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
3=>'The uploaded file was only partially uploaded',
4=>'No file was uploaded',
6=>'Missing a temporary folder',
7=>'Cannot write file to disk'
);
if($_FILES['form_input_file_name']['error'] == 4) {
echo 'No file uploaded';
}
if($_FILES['form_input_file_name']['error'] == 0) {
echo 'File uploaded... no errors';
}

Resources