Form with managed files weird behaviour on form error - forms

I have created a content type with an associated image field.
Each user is able to see a list of all the nodes of this content type and should be able to upload a new image in the appropriate field.
I've tried many solutions, but in the end I'm trying with a form and managed files.
In the page with the list of all the nodes I'm creating a lightbox with a form for each node.
Each form is created like this:
function coverupload_form($form, &$form_state, $uid, $relid) {
$form['#attributes']['id'] = 'coverup-'.$relid;
$form_state['storage']['rid'] = $relid;
$cliente = cataloghi_user_edit_get_cliente($uid);
$release = node_load($relid);
$form['cover'] = array(
'#title' => 'Carica la cover per la release '.$release->title,
'#description' => 'I file caricati devono avere estensione \'.jpeg\', risoluzione di 1440x1440 e dimensione massima di 5MB',
'#type' => 'managed_file',
'#upload_location' => 'public://clienti/'.$cliente->title.'/cover',
'#upload_validators' => array(
'file_validate_extensions' => array('jpeg, jpg'),
// Pass the maximum file size in bytes
'file_validate_size' => array(5*1024*1024),
'file_validate_image_resolution' =>array('1440x1440', '1440x1440'),
),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('CARICA'),
);
return $form;
}
function coverupload_form_submit($form, &$form_state) {
$file = file_load($form_state['values']['cover']);
// Change status to permanent.
$file->status = FILE_STATUS_PERMANENT;
// Save.
file_save($file);
$nodo = node_load($form_state['storage']['rid']);
$nodo->field_release_copertina['und'][0] = (array)$file;
if($nodo = node_submit($nodo)) { // Prepare node for saving
node_save($nodo);
}
}
All the forms have display: none, and when the user click on the cover upload button only the corresponding form is showed in the lightbox.
Well, everything works fine when the image is validated.
The problems start when the image is not validated (like if it's below 1440x1440px).
If I check the lightbox with the inspector, the correct number of forms is generated but they all refer to the same node (so they all have id 'coverup-17' for example).
I have checked everything, and it seems like I pass the correct values to the form everytime, so I'm starting to think that it may be a problem connected with my poor understanding of forms.
Would it be better to try a different type of approach?
Thanks and sorry for if this is a bit messy...

I managed to solve the problem.
It depended on the fact that I had multiple instances of the same form on the same page.
I implemented hook_forms().
function mymodule_forms($form_id, $args) {
$forms = array();
if(strpos($form_id, 'coverupload_form_') === 0) {
$forms[$form_id] = array(
'callback' => 'coverupload_form',
'callback arguments' => array($args[0], $args[1]),
);
}
return $forms;
}
and then I changed my form call to
drupal_render(drupal_get_form('coverupload_form_'.$relid, $arg1, $arg2));

Related

Symfony2 - Functional Testing File uploads with dynamically created fields

I'm fighting with functional testing of files uploading.
I will try to simplify my situation. Let's say I have
a company entity, which has 3 fields.
Company {
protected name;
protected tags;
protected images;
}
Images is array of CompanyImage entities, which serve for
storing image files and tags contains array of Tag entities,
which can be m:n connected with companies.
In the form I use jquery, for adding tags and images dynamically.
(you can create images and add them to the company similar to the
collection type symfony tutorial)
Because images and tag arrays are created with jquery, I cannot
simply use something like the turorial line below in the functional test of the company form.
$form['images'][0]->upload('/path/to/image.jpg');
For the setting
of the form values I use simple a little trick described by sstok here
(https://github.com/symfony/symfony/issues/4124)
public function testCompanyCreation() {
...
//option1
$image = new UploadedFile(
'/path/to/image.jpg',
'image.jpg',
'image/jpeg',
123
);
//or option2
//$image = array('tmp_name' => '/path/to/image.jpg', 'name' => 'image.jpg', 'type' => 'image/jpeg', 'size' => 300, 'error' => UPLOAD_ERR_OK);
$companyFormNode = $companyCrawler->selectButton('Create');
$companyForm = $companyFormNode->form();
$values = array(
'company' => array(
'_token' => $companyForm['company[_token]']->getValue(),
'name' => 'test company',
'tags' => array('1'),
'images' => array('0' => (array('file' =>$image))),
),
);
$companySubmitCrawler = $client->request($companyForm->getMethod(), $companyForm->getUri(), $values, $companyForm->getPhpFiles());
}
this works perfectly until I try to upload the image file.
With the option1 I get following exception
Exception: Serialization of 'Symfony\Component\HttpFoundation\File\UploadedFile' is not allowed
when I use option2 I get this
Argument 1 passed to Acme\myBundle\Entity\CompanyImage::setFile() must be an instance of Symfony\Component\HttpFoundation\File\UploadedFile, array given, called in ...\PropertyAccess\PropertyAccessor.php on line 347 and defined (500 Internal Server Error)
I would also like to point out, that the whole form and uploading of the files works without any problems in the browser. I also tried to make the entities serializable, and it didn't help. Do I have a bug somewhere?
I have figured it out (took couple of hours). Files have to be uploaded in a separate array.
$companyForm = $companyFormNode->form();
$values = array(
'company' => array(
'_token' => $companyForm['company[_token]']->getValue(),
'name' => 'test company',
'tags' => array('1')
),
);
$files = array(
'company' => array('images' => array('0' => (array('file' => $image))))
);
$companySubmitCrawler = $client->request(
$companyForm->getMethod(),
$companyForm->getUri(),
$values,
$files
);

Drupal 7: Show fields on a form based on another field and a database lookup

I have a form I created in a custom module using the Form API. It is a fairly basic form with only 4 fields. It basically signs a user up for a job alert system. We are basing it only by email address with a few search parameters. We want people to be able to setup a search agent quickly and anonymously meaning they will NOT be creating a Drupal user account as we don't want them to have to deal with a password etc. They will just put in their email address, check off a few preferences and we will save the data.
Now the issue I need to deal with is allowing the user to edit their preferences later on and/or unsubscribe. Again this is not high security and it doesn't need to be. What I would like to do is initially ask ONLY for their email address in the form and allow them to submit it. I would then check the database to see if we already have an entry for that email address and if so, display the pre-filled form for them to edit or unsubsribe, other wise just show them the blank form. So I am just trying to figure out the best way to go about this. I'm thinking I just have one form with all of the fields including email address, but somehow only display the other fields besides the email address after a successful call to the database. I'm just tripping up on how to accomplish this.
EDIT:
I'm wondering if I can use Drupal's AJAX functionality to accomplish this? I tried this, but I couldn't get it to work. I put an Ajax attribute on my submit button with a wrapper ID and a callback function. I created a form element in my form with blank markup and used a prefix and suffix that created a wrapper div with the ID I used in my AJAX parameter. Then I am thinking in my callback function I can do the database lookup and then return the form elements I need either pre-filled or not into the wrapper div that was created, but when I do this, the form does submit via AJAX and I get the spinning wheel, but no matter what I return in my callback, it does not appear in my output wrapper div. Am I going about this the right way? I also made sure I have $form_state['rebuild'] = TRUE; on my original form.
Here is what I tried and it didn't work.
/**
* Implements hook_form().
*/
function _vista_form($form, &$form_state) {
$form = array();
$form_state['rebuild'] = TRUE;
$form['email'] = array(
'#type' => 'textfield',
'#title' => 'Email',
'#required' => TRUE,
);
$form['render_area'] = array(
'#type' => 'markup',
'#markup' => '',
'#prefix' => '<div id="job-agent-form">',
'#suffix' => '</div>',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#attributes' => array('class' => array('submit')),
'#ajax' => array(
'callback' => '_display_form',
'wrapper' => 'job-agent-form',
'method' => 'replace',
'effect' => 'fade',
),
return $form;
}
function _display_form($form, &$form_state) {
// there are other form elements that would go here also, I just added two for example
$type_options = array(
'VISTA-HealthCare-Partners-Government' => 'Vista Healthcare Partners',
'International' => 'International Locum Tenens',
'Permanent' => 'Permanent Physician',
'US-Locum-Tenens' => 'US Locum Tenens',
);
$form['job_type'] = array(
'#type' => 'checkboxes',
'#multiple' => TRUE,
'#title' => 'Type of Job',
'#options' => $type_options,
'#empty_option' => 'Choose a placement type',
'#empty_value' => 'all',
//'#default_value' => $type_selected,
);
$form['active'] = array(
'#type' => 'checkbox',
'#title' => 'Subscribe/Unsubscribe',
'#default_value' => 1,
);
return $form;
}
I would go for creating all fields in the form than use hook_form_alter() to hide the unneeded ones with ['#access'] = FALSE.

Zend form in a popup (fancybox, lightbox....)

I am developping a web application using Zend and I ran out of ideas for a problem I am having. In just a few words, I am trying to have a contact form in a popup (Fancybox, lightbox, colorbox or whatever...). The whole thing works fine, in the sense that it shows up the contact form in the popup and allows to send emails. However, whenever there are errors (unfilled input or filled wrong), I couldn't get those errors to be displayed on the popup (it actually redirects me back to the form in a normal display (view+layout), to show the errors.
It is perhaps possible but I now thought that perhaps I could more easily bring my error message to a new popup (the contact page, filled unproperly, would lead to a error popup page...). I think this alternative could look cool but am having real trouble doing it. Now my real question is : Can we really make a form on a popup, using Facybox (Lighbox or any other actually ... just want my popup) and Zend? Any Guru outhere??
Thanks a lot
here is the code:
the link for instance:
<a class="popLink" href=" <?php echo $this->url(array('module'=>'default', 'controller'=>'contact', 'action'=>'sendmail')).'?ProID='.$this->proProfil->getProID(); ?>">Contact</a>
the action:
public function sendmailAction()
{
$this->_helper->layout()->setLayout('blank');
$request = $this->getRequest();
$proID = $this->_getParam("ProID");
$professionalsList = new Model_DirPro();
$proName = $professionalsList->getProInfo($proID);
$translate = Zend_Registry::get('translate');
Zend_Validate_Abstract::setDefaultTranslator($translate);
Zend_Form::setDefaultTranslator($translate);
$contactform = new Form_ContactForm();
$contactform->setTranslator($translate);
$contactform->setAttrib('id', 'contact');
$this->view->contactform = $contactform;
$this->view->proName = $proName;
if ($request->isPost()){
if ($contactform->isValid($this->_getAllParams())){
$mailSubject = $contactform->getValue('mailsubject');
if ($contactform->mailattcht->isUploaded()) {
$contactform->mailattcht->receive();
//etc....
the form:
class Form_ContactForm extends Zend_Form
{
public function init ()
{
$this->setName("email");
$this->setMethod('post');
$this->addElement('text', 'mailsubject',
array('filters' => array('StringTrim'),
'validators' => array(), 'required' => true, 'label' => 'Subject:'));
$mailattcht = new Zend_Form_Element_File('mailattcht');
$mailattcht->setLabel('Attach File:')->setDestination(APPLICATION_PATH.'/../public/mails');
$mailattcht->addValidator('Count', false, 1);
$mailattcht->addValidator('Size', false, 8000000);
$mailattcht->addValidator('Extension', false,
'jpg,png,gif,ppt,pptx,doc,docx,xls,xslx,pdf');
$this->addElement($mailattcht, 'mailattcht');
$this->addElement('textarea', 'mailbody',
array('filters' => array('StringTrim'),
'validators' => array(), 'required' => true, 'label' => 'Body:'));
$this->addElement('submit', 'send',
array('required' => false, 'ignore' => true, 'label' => 'Send'));
$this->addElement('hidden', 'return', array(
'value' => Zend_Controller_Front::getInstance()->getRequest()->getRequestUri(),
));
$this->setAttrib('enctype', 'multipart/form-data');
}
}
I would suggest implementing AJAX validation. This would allow for the form to be verified before it is submitted. ZendCasts has a good tutorial on how to accomplish this: http://www.zendcasts.com/ajaxify-your-zend_form-validation-with-jquery/2010/04/
Ajax requests are handled via the contextSwitch action helper. You can to specify the various contexts an action needs to handle (xml or json) in the init method of the controller as follows:
public function init()
{
$this->_helper->contextSwitch()
->addActionContext('send-mail', 'json')
->initContext()
;
}
The request url should contain a "format=json" appended to the query string. This will execute the action and send the response in json format. The default behaviour of JSON context is to extract all the public properties of the view and encode them as JSON. Further details can be found here http://framework.zend.com/manual/en/zend.controller.actionhelpers.html
I found a "probably not the prettiest" working solution, it is to indeed use ajax as mentioned in the previous zendcast for validation to stop the real validation (preventdefault), process the data return the result and if everything's ok restart it.

form with no form_id(Drupal 6.x)

I have this form placed inside a block and it is assigned to right region of my site. Form is displayed just fine. But the submit button doesn't work as intended - to call submit function. So, I did some debugging and found an anomaly that there is no essential data - such as form_id and tokens - drupal normally injected to every form. As I can't figure out the root cause of this, I'm here for pointers of friends from here. Here's an excerpt of my code -
function mymodule_block($op = 'list', $delta = '', $edit = array()) {
switch ($op) {
case 'list':
$blocks['quick_search'] = array(
'info' => t('Quick Search'),
);
return $blocks;
case 'view':
switch ($delta) {
case 'quick_search':
$block['subject'] = t('Quick Search');
$block['content'] = drupal_get_form("block_quick_search");
break;
}
return $block;
}
}
function block_quick_search(&$form_state){
$form = array();
.
.
.
$form['quick_search_submit'] = array(
'#type' => 'submit',
'#value' => t('Search'),
'#submit' => array('mymodule_quick_search'),
);
return $form;
}
function mymodule_quick_search($form, &$form_state){
drupal_goto($base_path,"..............");
}
Thanks in advance
there is no essential data - such as form_id and tokens
This is indeed the reason why form submissions are not processed correctly. Check whether drupal_prepare_form is called on your form and whether it adds those items correctly. It is called by drupal_get_form if he form is not posted (and thusly not retrieved from the cache).
If $form['#token'] and $form['form_id'] are added correctly, I suspect something is wrong with translating the form to HTML. Do you use any custom theming for the form?
Try to pass your submit handler to the main form and not on the item submit like that:
$form['#submit'][] = 'mymodule_quick_search';
it should work.

Arbitrary Form Processing with Drupal

I am writing a module for my organization to cache XML feeds to static files to an arbitrary place on our webserver. I am new at Drupal development, and would like to know if I am approaching this the right way.
Basically I:
Expose a url via the menu hook, where a user can enter in a an output directory on the webserver and press the "dump" button and then have PHP go to drupal and get the feed xml. I don't need help with that functionality, because I actually have a prototype working in Python (outside of Drupal)..
Provide a callback for the form where I can do my logic, using the form parameters.
Here's the menu hook:
function ncbi_cache_files_menu() {
$items = array();
$items['admin/content/ncbi_cache_files'] = array(
'title' => 'NCBI Cache File Module',
'description' => 'Cache Guide static content to files',
'page callback' => 'drupal_get_form',
'page arguments' => array( 'ncbi_cache_files_show_submit'),
'access arguments' => array( 'administer site configuration' ),
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
I generate the form in:
function ncbi_cache_files_show_submit() {
$DEFAULT_OUT = 'http://myorg/foo';
$form[ 'ncbi_cache_files' ] = array(
'#type' => 'textfield',
'#title' => t('Output Directory'),
'#description' => t('Where you want the static files to be dumped.
This should be a directory that www has write access to, and
should be accessible from the foo server'),
'#default_value' => t( $DEFAULT_OUT ),
'#size' => strlen( $DEFAULT_OUT ) + 5,
);
$form['dump'] = array(
'#type' => 'submit',
'#value' => 'Dump',
'#submit' => array( 'ncbi_cache_files_dump'),
);
return system_settings_form( $form );
}
Then the functionality is in the callback:
function ncbi_cache_files_dump( $p, $q) {
//dpm( get_defined_vars() );
$outdir = $p['ncbi_cache_files']['#post']['ncbi_cache_files'];
drupal_set_message('outdir: ' . $outdir );
}
The question: Is this a decent way of processing an arbitrary form in Drupal? I not really need to listen for any drupal hooks, because I am basically just doing some URL and file processing.
What are those arguments that I'm getting in the callback ($q)? That's the form array I guess, with the post values? Is this the best way to get the form parameters to work on?
Thanks for any advice.
You can process forms in two stages, validate and submit.
Validate is for when you want to validate some user provided and raise form errors if some user input was invalid (like an invalid url or email address)
Submit which is the one you use is called if a form passes all of its validations, so at that point if you made a proper validation you will know that the data supplied by the user is okay.
Your submit function should look like this:
function ncbi_cache_files_dump(&$form, &$form_state) {
// $form: an array containing the form data
// $form_state: data about the form, like the data inputted in the form etc.
// code...
}
I think you need 2 separate forms here:
for setting the directory (the one you have now);
for making a dump (another form that would use the configured path).
Also it seems logical to publish the previously saved path as the default value in the settings form (instead of a hard-coded path).
And in general you should check the form input data from the second parameter of the submit callback:
function ncbi_cache_files_dump(&$form, &$form_state) {
$outdir = $form_state['values']['ncbi_cache_files'];
// ...
}