Zend Framework - Validating contents of upload (secondary validation) - php

I'm currently having users upload a MS Word Document where I am checking a version within the XML. The controller currently checks isValid() and then hits a library that does the parsing and extraction (since word is an archive). Now since it's technically "valid" already, I need to check the validity again through the library. What is the best course of action in Zend Framework for this?
Cheers from Kohana Land ;)

I think I understand what you are looking for.
You are currently calling is valid against the form, your file passes the form validation (correct size, extension ...) now you need to unpack the file and validate the contents.
I'm going to assume you already have the code to validate the contents and just want to understand how that might be used in the controller.'
public function anyAction() {
$form = new Form();
//test for $_POST
if ($this->getRequest()->isPost(){
//Test form for validity
if ($form->isValid($this->getRequest()->getPost()){
//will receive file upload (unless disabled in element) and filter form values,
//based on filters attached to the elements.
$data = $form->getValues();
//placeholder for whatever code is required to validate contents of file
$validateFile = new MyFileValidator();
//test for valid file contents
if ($validateFile->isValid($data['file']){
//Do some Stuff
}
//if file contents is not valid, display form and populate values with unfiltered values
$form->populate($this->getRequest()->getPost());
}
//if form is not valid, it should stay populated and display element errors
}
//if not post send form to view
$this->view->form = $form;
}
This example should provide the basic controller workflow for this type of problem. I hope it addresses your question.

Related

Symfony2: doing form validation before the form is posted

Going to try my best to explain this...
In my app I have the need to display the validation errors on a form when the user loads the form initially. In short, they've entered data and saved it, but now the data has been checked and we've detect errors they need to correct before they can fully submit the form. (It's multi step form that can be filled out over multiple sessions...think big.)
Previously I was doing something like:
THIS DOESN'T WORK IN SYMFONY >=2.8.10 See this answer
$entity // passed in as param on action method
$form = $this->createForm(..., $entity);
$csrfToken = 'random_string'; // retrieved from FormConfigInterface
// perform the submit but don't clear missing
$form->submit(['_token' => $csrfToken], false);
This was working, but seems to have broken in Symfony 2.8.10+, but works in 2.8.9. In 2.8.10+ there are no validation errors (the form is considered valid).
I'm able to retrieve the validation errors in a ConstraintViolationListInterface, but I can't find a way to "merge" that with the form (which I think would be the prefered way). Since I couldn't, I tried the above which "fake" submits the form...which seems like a bad idea.
Is there a better/proper way?
(Note: the form is much more complicated and includes validation groups...but I'm not concerned about that or the error in 2.8.10+ at this point.)

Cannot initialize variable using beforeValidate

I have a custom behavior, specified in AppModel.php, that automatically creates a field based on the selected language. Thus, depending on the chosen language, name_eng -> name or name_fra -> name.
...
$virtualField = sprintf($model->name.'.'.$name.'_%s', Configure::read('Config.language'));
$virtualFields[$name] = $virtualField;
$model->virtualFields = $virtualFields;
...
This part works.
The issue arises when I submit the edit form, get a validation error and the field isn't available when the edit view is displayed with error prompts. I believe this is due to either my behavior not being called in this process or $this->request->data being created using form data?
I figured that I would initialize the values using beforeValidate. However, it's not working out: the field still doesn't exist once I've submitted the form which gives me this error:
In AppModel.php:
function beforeValidate(array $options = array()){
//hard coded for test purposes
$this->data['CertificateType']['name'] = $this->data['CertificateType']['name_'.Configure::read('Config.language')]
return true;
}
In the view (edit.ctp):
echo $this->request->data['CertificateType']['name'];
Essentially, how can I replicate the functionality of my custom behavior and initialize my field after a form has been submitted but doesn't validate?
The needed logic was eventually put in AppController.php. Everything works fine now.

Yii: Keep files uploaded after validation fail?

If I upload a file with YII and another rule fails, then the user has to pick the file again. What is the easiest way to avoid this?
For example, I have a rule that the title must be 20 characters at most. The user types in 21 letters. He chooses a file to upload. When the user is returned to the page, the file is no longer there and he must choose it again, and effectively upload it again. This is very frustrating, especially now when the user will be required to upload up to ten files.
I know Drupal works like this. If you upload and other rules fail, the files appear as screenshots when you return to the form. How can I get the same functionality on YII?
UPDATE
If I could get that requirement covered with this extension and not require that the user presses start upload, I would be home free
The original plugin that xupload wraps, there is an additional callback option you could use: .done().
In the xupload wiki, the way to access those additional options would be this way:
<?php
$this->widget('xupload.XUpload', array(
// ... other attributes
'options' => array(
//This is the submit callback that will gather
//the additional data corresponding to the current file
'submit' => "js:function (e, data) {
var inputs = data.context.find(':input');
data.formData = inputs.serializeArray();
return true;
}"
),
));
?>
Source
You could probably just change the submit part to done, and let that save the URL/path of the uploaded file to a temporary hidden field, and move your validation to that hidden field instead, so the user does not have to re-upload a file again.
I moved away from this plugin to the coco uploader as it was easier to implement.
You could enable client side validation and AJAX validation. So your regular attributes will be validated before the form is sent and the file gets uploaded.
You can do it with session.
In your controller
// Here I have taken Users as model. you should replace it as your need.
$model=new Users;
if(isset($_POST['Users']))
{
$model->attributes=$_POST['Users'];
//save file in session if User has actually selected a file and there weren't any errors.
if(isset($_FILES['Users']) && $_FILES['Users']['error']['image'] == 0){
Yii::app()->session['image'] = $_FILES['Users'];
}
if(isset(Yii::app()->session['image']) && !empty(Yii::app()->session['image'])){
$model->image = Yii::app()->session['image'];
$model->image = CUploadedFile::getInstance($model,'image');
}
if($model->save())
{
if(!empty($model->image)){
$model->image->saveAs(Yii::app()->basePath.'/images/'.time()."_".$model->image->name);
unset(Yii::app()->session['image']);
//File has successfully been uploaded.
}
// redirect to other page.
}
}
else{
// remember to unset the session variable if it's a get request.
unset(Yii::app()->session['image']);
}
And in your view file
//Your form fields
//This is to show user that he has already selected a file. You could do it in more sofisticated way.
if(isset(Yii::app()->session['image']) && !empty(Yii::app()->session['image'])) {
echo "<label>".Yii::app()->session['image']['name']['image']."</label><br>";
}
//File uplaod field.
//More form Fields.
Hope that helps.

PHP: What's a better way to process form data?

On my website, I have user accounts that are configurable with forms that allow users to update everything from first and last names to privacy settings. I use the following function to update the database with that input. (Note that the following code uses WordPress-specific features.)
function update_account() {
global $current_user; get_currentuserinfo();
require_once( ABSPATH . WPINC . '/registration.php' );
$uid = $current_user->ID;
// First Name
if(isset($_POST['first_name']) && $_POST['first_name'] <> $current_user->first_name) {
wp_update_user( array(
'ID' => $uid, 'first_name' => esc_attr($_POST['first_name'])
));
}
// ...and so on 43 more times...
}
This feels like the wrong way to process forms. This also looks like it will negatively impact server performance when there are multiple users and frequent updates, given that the if-then-else conditions for every field, even fields not on a particular page, force checking each field for input.
Moreover, since form data can be expected to remain relatively constant, I added the <> operator to prevent the function from updating fields where there has not been any change, but I suspect this also means that every field is still evaluated for change. To make matters worse, adding new fields -- there are already 44 fields in total -- is an unwieldy process.
What's a better way to process form data?
Keep an array of the fields you will be processing with this code, and iterate over it. This works if all your attributes are strings, for example. If you have different data types such as boolean flags to handle differently from the strings, you may wish to group them into their own array.
// All the fields you wish to process are in this array
$fields = array('first_name', 'last_name', 'others',...'others99');
// Loop over the array and process each field with the same block
foreach ($fields as $field) {
if(isset($_POST[$field]) && $_POST[$field] != $current_user->{$field}) {
wp_update_user( array(
'ID' => $uid, $field => esc_attr($_POST[$field])
));
}
}
There's a lot of things missing with your implementation. I don't know what kinds of data you're allowing the user to manipulate but most usually have some kind of requirements to be acceptable. Like not having certain characters, not being blank, etc. I don't see any validation occurring, so how do you handle values that might be undesirable? And what happens when you receive bad data? How do you inform the user of the bad data and prompt them to correct it?
If we abstract the situation a bit we can come up with generalizations and implement an appropriate solution.
Basically form fields [can] have a default value, a user specified value [on form review], validation requirements and validation errors [with messages]. A form is a collection of fields that upon form submit needs to be validated and if invalid, re-displayed to the user with instructive corrective prompts.
If we create a form class that encapsulates the above logic we can instantiate and use it to pass around our controller/views. Oops, I was just assuming you were using an Model/View/Controller type framework, and I'm not really familiar with wordPress so I don't know if that is exactly applicable. But the principle still applies. On the page where you both display or process the form, here's some pseudo logic how how it might look.
function update_account()
{
// initialize a new form class
$form = new UserAccountInfoForm();
// give the form to your view for rendering
$this->view->form = $form;
// check if form was posted [however your framework provides this check]
if(!Is_Post())
return $this->render('accountform.phtml');
// check if posted form data validates
if(!$form->isValid($_POST))
{
// if the form didn't validate re-display the form
// the view takes care of displaying errors, with the help of its
// copy of the $form object
return $this->render('accountform.phtml');
}
// form validated, so we can use the supplied values and update the db
$values = $form->getValues(); // returns an array of ['fieldname'=>'value']
// escape the values of the array
EscapeArrayValues($values);
// update db
wp_update_user($values);
// inform the user of successful update via flash message
$this->flashMessage('Successfully updated profile');
// go back to main profile page
$this->redirect('/profile');
That makes your controller relatively clean an easy to work with. The view gets some love and care to, utilizing the $form value to display the form correctly. Technically, you can implement a method in the form class to give you the form html, but for simplicity I'm just going to assume your form html is manually coded in accountform.phtml and it just uses $form to get field info
<form action='post'>
<label>first name</label> <input class='<?=$this->form->getElement('first_name')->hasError() ? "invalid":""?>' type='text' name='first_name' value="<?=$this->form->getElement('first_name')->getValue()"/> <span class='errmsg'><?=$this->form->getElement('first_name')->getError()?></span><br/>
<label>last name</label> <input class='<?=$this->form->getElement('last_name')->hasError() ? "invalid":""?>' type='text' name='last_name' value="<?=$this->form->getElement('last_name')->getValue()"/> <span class='errmsg'><?=$this->form->getElement('last_name')->getError()?></span><br/>
<label>other</label> <input class='<?=$this->form->getElement('other')->hasError() ? "invalid":""?>' type='text' name='other' value="<?=$this->form->getElement('other')->getValue()"/> <span class='errmsg'><?=$this->form->getElement('other')->getError()?></span><br/>
<input type='submit' value='submit'/>
</form>
Here the pseudo code relies on the form class method "getElement" which returns the field class instance for the specified field name (which would be created an initialized in the constructor of your form class). Then on the field class methods "hasError" and "getError" to check if the field validated correctly. If the form has not be submitted yet, then these return false and blank, but if the form was posted and invalid, then they will have been set appropriately in the validate method when it was called. Also "getValue" would return either the value supplied by the user when the form was submitted, or if the form has not been submitted, the default value as specified when the field class was instantiated and initialized.
Obviously this pseudo code is relying on a lot of magic that you'd have to implement if you roll your own solution--and it's certainly doable. However, at this point I'll direct you to the Zend Framework Zend_Form components. You can use zend framework components by themselves without having to utilize the entire framework and application structure too. You might also find similar form component solutions from other frameworks but I wouldn't know about those (we are a Zend Framework shop at my work place).
Hopefully this hasn't been too complicated, and you know where to go from here. Of course just ask if you need any clarification.

Drupal 6 Validation for Form Callback Function

I have a simple form with a select menu on the node display page. Is there an easy way to validate the form in my callback function? By validation I don't mean anything advanced, just to check that the values actually existed in the form array. For example, without ajax, if my select menu has 3 items and I add a 4th item and try to submit the form, drupal will give an error saying something similar to "an illegal choice was made, please contact the admin."
With ajax this 4th item you created would get saved into the database. So do I have to write validation like
if ($select_item > 0 && $select_item <= 3) {
//insert into db
}
Or is there an easier way that will check that the item actually existed in the form array? I'm hoping there is since without ajax, drupal will not submit the form if it was manipulated. Thanks.
EDIT:
So I basically need this in my callback function?
$form_state = array('storage' => NULL, 'submitted' => FALSE);
$form_build_id = $_POST['form_build_id'];
$form = form_get_cache($form_build_id, $form_state);
$args = $form['#parameters'];
$form_id = array_shift($args);
$form_state['post'] = $form['#post'] = $_POST;
$form['#programmed'] = $form['#redirect'] = FALSE;
drupal_process_form($form_id, $form, $form_state);
To get $_POST['form_build_id'], I sent it as a data param, is that right? Where I use form_get_cache, looks like there is no data. Kind of lost now.
Since you're already using AJAX, why not just write a bit of jQuery to only allow form submission if the choice is within the list of legal choices? This can be done within the custom module it already looks like you're working on (using drupal_add_js()).
It is not especially 'easy', but the standard way to do it would be to use Drupals Forms API for the callback submission as well - that way, you'll get the same validation that would happen on a non js submit.
Take a look at Adding dynamic form elements using AHAH. While it does not match your scenario exactly (they rebuild the form on the callback to add new elements, not to save data), the explanation of the processing workflow is pretty helpful.
Then there are several modules that try to offer AJAX form submission in a generic way - you could check their code on how to do it (or maybe just use them ;)
Ajax submit (only has a dev version)
Ajax (has an 'official' release)
Finally, there are efforts to put better support this functionality into core in Drupal 7 - the related discussions might also help.

Categories