For those who are interested, I am posting here (I don't know if this is the right place) a solution to display webforms (webform.module) into the fabulous CTools modal window. The solution is based on the examples found in ctools_ajax_sample.module.

Comments and improvements (in particular regarding the display of node fields) are welcome.

In a custom module :

  1. Create a menu item pointing a page callback, say ctools_ajax_content through hook_menu similarly to example in ctools_ajax_sample.module. This menu path will be used as a link to open the webform.
  2. Add a custom submit function to the webform that shall be displayed in the modal CTools window (through hook_form_alter), say $form['#submit'][] = '_custom_webform_submit';. This must run after the other submit function, so that it is called only when everything goes fine.
  3. The menu callback function calls another function ctools_ajax_modal_webform that handles the display of the webform and the confirmation message after successful validation in the same CTools modal window. By doing so, you can use this function for different purposes (e.g. different webforms).

    <?php
    // Menu callback
    function ctools_ajax_content($js = NULL) {
      $webform_nid = WEBFORM_NID; // Your webform $nid
      return ctools_ajax_modal_webform($js, $webform_nid);
    }
    
    // Handle CTools modal webforms
    // $webform_nid is the nid of the webform
    function ctools_ajax_modal_webform($js = NULL, $webform_nid = NULL) {
      $node = node_load($webform_nid); // Load webform node
      $submission =  (object) array(); // empty object required by webform
        
      // React without the modal
      if (!$js) {
        // Webform requires more parameters than standard forms
        return drupal_get_form('webform_client_form_'.$webform_nid, $node, $submission);
      }
    
      // React with the modal
      // Add modal components
      ctools_include('modal');
      ctools_include('ajax');
      $form_state = array(
        'title' => $node->title,
        'ajax' => TRUE,
      );
      
      // Emulate ctools_modal_form_wrapper() form modal.inc because webform can not be triggered trough drupal_build_form
      // If it can, I'd be glad to understand how
      $form_state += array(
        're_render' => FALSE,
        'no_redirect' => !empty($form_state['ajax']),
      );
    
      // Fetch webform form stuff
      $output = drupal_get_form('webform_client_form_'.$webform_nid, $node, $submission);
      // Merge node content (except webform) and webform form before rendering    
      $node_view = node_view($node); // Prepare webform for rendering
      $node_view['body']['#weight'] = -100; // Ensure node body is at the top
      unset($node_view['webform']); // Prevent displaying twice the webform
      array_unshift( $output['submitted'], $node_view['body']); // Merge
      $node->title = ''; // Remove title to prevent displaying twice
    
      if (!empty($form_state['ajax'])) {
        $output = ctools_modal_form_render($form_state, $output);
      }
    
      // Handle successful submission through session flag - see '_custom_webform_submit' function below
      if ( isset($_SESSION['webform_client_form_'.$webform_nid]) && $_SESSION['webform_client_form_'.$webform_nid] == 'submitted') {
        // Delete session flag    
        unset($_SESSION['webform_client_form_'.$webform_nid]);
        // Fetch confirmation message. It will be displayed in the modal window
        $confirmation['#markup'] = 
          '<div class="popups-confirmation-wrapper">'.
          check_markup($output['#node']->webform['confirmation'], $output['#node']->webform['confirmation_format'], '', TRUE).
          '</div>';    
        $output = array(); // Recreate output
        // Oerwrite the form output if it was successful.
        $output[] = ctools_modal_command_display('Confirmation', $confirmation);
      }
      
      // Render output in modal window
      print ajax_render($output);
      exit;
    }
    
    // Set a session flag to mark the webform as submitted. 
    // This value will be used to trigger display confirmation message in CTools modal window
    function _custom_webform_submit($form, $form_state) {
      $webform_nid = $form['#node']->nid;
      $_SESSION['webform_client_form_'.$webform_nid] = 'submitted';
    };?>
    

Laurent
Agence Web Coheractio

CommentFileSizeAuthor
#26 submit handler.png7.9 KBmsankhala
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mstrelan’s picture

This is a rather timely post as I was looking for the same thing. I don't think it's the right spot for it, but nonetheless here is some feedback.

1. It just prints out a JSON response, how do you link it up properly? Do you need something like ctools-use-modal?

2. What happens with the session variable if you want to submit the same webform twice. For example I have a form "enquire about this artwork" and the user might want to submit it for 5 different artworks.

Agence Web CoherActio’s picture

The above function indeed need the ctools modal stuff around it as shown below.

With regards to the session variable, it is set when webform validates and immediately unset when displaying confirmation message, so that one can submit several time.

Code examples :

Menu item :

/**
 * Implementation of hook_menu().
 */
function MY_MODULE_menu() { 
  $items['form/%ctools_js/contact-us'] = array(
    'title' => t("Contact us"),
    'page callback' => 'ctools_ajax_content',
    'page arguments' => array(1),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  return $items;
}

Load CTools modal stuff. I am using hook_init() because it is used on every page

/**
 * Implementation of hook_init().
 */
function MY_MODULE_init() { 
  ctools_include('ajax');
  ctools_include('modal');
  // Add CTools' javascript to the page.
  ctools_modal_add_js();
  // Customised modal window style
  $modal_style = array(
   ...
  );
  drupal_add_js($modal_style, 'setting');
  // Customised CTools modal js and css
  drupal_add_js(drupal_get_path('theme', 'MY_THEME') . "/modal/modal_popup.js");
  drupal_add_css(drupal_get_path('theme', 'MY_THEME') . "/modal/modal_popup.css");  
}

Laurent
Agence Web Coheractio

mstrelan’s picture

I found a new module Simple Dialog which does the trick for me.

JoshuaBud’s picture

Curious the differences with D6 version of this sample code. I would love to implement with a D6 site and have a form popup to fill out.

Apfel007’s picture

how/what is the "_custom_webform_submit" calling?

It's called with this .. the only problem is, that $form['#submit'][] is not working ..?? do it now with $form['#submit'][2]...

/**
   * Implementation of hook_form_alter().
   *
   */
function webform_modal_form_alter(&$form, $form_state, $form_id) {
  switch ($form_id) {
    case 'webform_client_form_1':
      $form['#submit'][2] = '_custom_webform_submit'; // I use [2]  cause []  is not working... could that be?
      break;
    }
  }
Apfel007’s picture

How is the "function ctools_ajax_modal_webform" called the "second time" - for the confirmation message?

my _SESSION value has 'submitted' BUT the key seems to be empty.. should be : _SESSION['webform_client_form_'.$webform_nid]

but is :
_SESSION[]

and $form['#node']->nid doesn't exists... should be $form['#node']->vid ..??

function _custom_webform_submit($form, $form_state) {
  $webform_nid = $form['#node']->nid; // -> should be ['#node']->vid ??
  $_SESSION['webform_client_form_'.$webform_nid] = 'submitted';
}
Agence Web CoherActio’s picture

The webform shall be set to "No redirect (reload current page)" so that it calls the form another time after submission.

In order to set the session value you need to add the custom validation function in hook_form_alter.

$webform_nid = $form['#node']->nid does exist (use devel and dpm($form) to see all data).

Note this code works only on D7.

Laurent

Apfel007’s picture

will try

lock2007’s picture

Hi,
can you make a tutorial to make it easy for novice ?
Thanks a lot

msakurada’s picture

OK, I like the implementation, had some small issues but was able to power through them. Thanks!

carvalhar’s picture

hi,
i'm using another module (Simple Dialog) and i got a different solution to the same problem with modal vs webform:
http://drupal.org/node/1241242#comment-5431176

wamilton’s picture

// Emulate ctools_modal_form_wrapper() form modal.inc because webform can not be triggered trough drupal_build_form
// If it can, I'd be glad to understand how

Ask and ye shall receive (months later).

Here it is in abbreviated form; I had no need of anything other than the actual form for my modal.

function ctools_ajax_modal_webform($js = NULL, $webform_nid = NULL) {
  $node = node_load($webform_nid);
  $submission =  (object) array();
    
  if (!$js) {
    return drupal_get_form('webform_client_form_'.$webform_nid, $node, $submission);
  }

  ctools_include('modal');
  ctools_include('ajax');
  $form_state = array(
    'title' => $node->title,
    'ajax' => TRUE,
  );

  // Fetch webform form stuff
  //$output = drupal_get_form('webform_client_form_'.$webform_nid, $node, $submission);
  $args = array($node, $submission);
  $form_state['build_info']['args'] = $args;
  $output = ctools_modal_form_wrapper('webform_client_form_'.$webform_nid, $form_state);
  if ( isset($_SESSION['webform_client_form_'.$webform_nid]) && $_SESSION['webform_client_form_'.$webform_nid] == 'submitted') {
    unset($_SESSION['webform_client_form_'.$webform_nid]);
    $confirmation['#markup'] = 
      '<div class="popups-confirmation-wrapper">'.
      drupal_render($whatever_you_like).
      '</div>';
    $output = array(); // Recreate output
    // Overwrite the form output if it was successful.
    $output[] = ctools_modal_command_display('Confirmation', $confirmation);
  }

  print ajax_render($output);
  exit;
}

Setting the $form_state and calling ctools_modal_form_render doesn't affect anything other than the title.

Agence Web CoherActio’s picture

Many thanks Wamilton. l'll give a try.

In order to improve the solution further:

  • Do you have any idea to simplify my code to render the node body with the webform ?
  • Have you tried the code for multistep webforms (this was my next step but I didn't had a chance to investigate) ?

@carvalhar : does you approach work for multistep webform as well?

Many thanks for your feedback!

Laurent
Coheractio : Agence Web Paris Versailles

carvalhar’s picture

@Laurent_t i didn't try with multistep, but if form validation (i mean, a form is submitted and a new page is loaded inside the modal) plays nice, i think it will work fine with multistep also.

wamilton’s picture

@Laurent:

What you get back from ctools_modal_form_wrapper (if it doesn't have messages to stop it) is an array of ajax commands for ajax_render(). You could just concatenate the drupal_render of the node body in its own div (if you need to style it differently) in the html of that command like this:

//...do your other prep
$commands = ctools_modal_form_wrapper($form_id, $form_state);
//you always get just one command (as the code is written today)
$commands[0]['output'] .= 
  '<div class=my-namespaced-class>' .
  drupal_render($node_view['body']) .
  '</div>';
//...do whatever else
print ajax_render($commands);

edit: also didn't try multi-step, but if it didn't work when you tried it before, there's reason to suspect that it may work if you render the form by correctly invoking ctools_modal_form_wrapper() since that actually sets the form_state in the way that the ctools form_wizard expects (except that it wants you to set $form_state['modal'] = TRUE but that's probably just how that tool expects things to be).

Agence Web CoherActio’s picture

Great : many thanks to all of you.

Laurent
Coheractio : Agence Web Paris Versailles

merlinofchaos’s picture

So can this be marked fixed?

Agence Web CoherActio’s picture

Status: Active » Fixed

Done.
If you think this is worth it, I can write a tutorial for this in the documentation section (actually I don't know if there is a section for ctools).

meghanmary’s picture

I would love to see a tutorial.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

osopolar’s picture

This still seems not to work for multistep forms. The result of multistep forms is held in $submission variable, which currently won't be set. Somehow we need to pass the $form_state array to ctools_ajax_modal_webform, but I can't see how.

See also:
Module Modal forms (with ctools): #1326716: Webform module support
#1168980: webform and ctools integration (duplicate of this issue)

qoomir’s picture

wamilton’s picture

Status: Closed (fixed) » Needs work

@osopolar:

See #12: ctools_modal_form_wrapper() takes in form_state, you just need to put in the attributes it expects.

ctools_ajax_modal_webform() is just an example, you need to rewrite it to suit your actual use case. What exactly are you trying to do?

@qoomir:

You seem to have your site paths set up kind of funky: the path in your 404 error is not the same as the one in your hook_menu implementation. What happens if you add 'webdevelopers/' to the front of the path in hook_menu and disable the module and re-enable it?

osopolar’s picture

Status: Needs work » Closed (duplicate)

@wamilton: I am exactly trying to show a multistep webform within CTools modal windows ;)

See Modal forms (with ctools) (which still needs this patch: #1326716: Webform module support).

rv0’s picture

I'm doing something similar with http://drupal.org/project/ctools_automodal
and patch #1396272: Handle submitted forms

might be useful to someone: (simplified code)

In hook menu

  // User data correction request page
  $items['form/path_of_my_form'] = array(
    'title' => 'Name of my form',
    'page callback' => 'drupal_get_form', 
    'page arguments' => array('custom_webform_form'),
    'type' => MENU_NORMAL_ITEM,
    'modal' => TRUE,
    'access callback' => TRUE,
    'file' => 'custom.pages.inc',
  );

In file custom.pages.inc, function custom_webform_form:
$form = drupal_get_form('webform_client_form_'.$nid, node_load($nid), (object) array(), '', '');
return $form;

This seems like a much simpler and shorter way to do it

msankhala’s picture

FileSize
7.9 KB

I tried this code and code in comment #12 but there is problem with this code. its not calling submit handler each time. I don't know why May be its some caching issue. I tried $form['#submit'][2] = '_custom_webform_submit'; also as suggested in comment #5 but that doesn't help. hook_form_alter is called each time and this hook_form_alter will attache custom submit handler to the webform. I confirmed this using chromephp and printing $form array. In $form['#submit'] it show the custom submit handler at index 2 but doesn't call that submit handler.

msankhala’s picture

No reply :)
Finally i found the solution. Here the session variable is used to decide whether to show form or confirmation message. In my case the the custom submit handler is not called so session variable is not set. So it show the form again rather then confirmation message. In form_state array variable there is a index 'executed' which is set to true only if the form submitted successfully otherwise this variable is set to false. So using this variable it is possible to decide whether to show form or confirmation message. So there is no need to define form alter and custom submit handler and no need to set any session variable. Here is code:

function ctools_ajax_modal_webform($js = NULL, $webform_nid = NULL) {
  $node = node_load($webform_nid); // Load webform node
  $submission =  (object) array(); // empty object required by webform
    
  // React without the modal
  if (!$js) {
    // Webform requires more parameters than standard forms
    return drupal_get_form('webform_client_form_'.$webform_nid, $node, $submission);
  }

  // React with the modal
  // Add modal components
  ctools_include('modal');
  ctools_include('ajax');
  $form_state = array(
    //'title' => $node->title,
    'title' => 'Connect with us',
    'ajax' => TRUE,
  );
  
  // Emulate ctools_modal_form_wrapper() form modal.inc because webform can not be triggered trough drupal_build_form
  // If it can, I'd be glad to understand how
  $form_state += array(
    'rebuld' => FALSE,
    're_render' => FALSE,
    'no_redirect' => !empty($form_state['ajax']),
  );

  if (!empty($form_state['ajax'])) {
    //$output = ctools_modal_form_render($form_state, $output);
    $args = array($node, $submission);
    $form_state['build_info']['args'] = $args;
    $output = ctools_modal_form_wrapper('webform_client_form_'.$webform_nid, $form_state);
  }
  if($form_state['executed'] == TRUE){
    $confirm_message = array("#markup" => '<div> Thank you for request. we will contact you within next 48 hours.</div>');
    $confirmation['#markup'] = 
      '<div class="popups-confirmation-wrapper">'.
      drupal_render($confirm_message).
      '</div>';    
    $output = array(); // Recreate output
    // Oerwrite the form output if it was successful.
    $output[] = ctools_modal_command_display('Confirmation', $confirmation);
  }
  
  // Render output in modal window
  print ajax_render($output);
  exit;
}
muschpusch’s picture

#27 doesn't work!

#1326716: Webform module support does work

Ruslan’s picture

Sorry, but your code is wrong.
Function "ctools_modal_form_wrapper" doesn't return form with webform fields:

<form action="/modal_forms/ajax/test" accept-charset="UTF-8" method="post" id="webform-client-form-12" class="webform-client-form" enctype="multipart/form-data"> <div><input type="hidden" name="form_build_id" id="form-25af2969d9055c763f3fcabb7945c70c" value="form-25af2969d9055c763f3fcabb7945c70c" /> <input type="hidden" name="form_token" id="edit-webform-client-form-12-form-token" value="fe3a98ea09eba2e1f1a1d8d48e93702a" /> <input type="hidden" name="form_id" id="edit-webform-client-form-12" value="webform_client_form_12" /> </div></form>

Ruslan’s picture

My code for D6 (webforms + ctools modal window)

function ctools_ajax_modal_webform($js = NULL, $webform_nid = NULL) {
{
	$node = node_load($webform_nid);
	if (!$js) return ;
	ctools_include('modal');
	ctools_include('ajax');
	ctools_include('form');
	$form_state = array(
		'title' => $node->title,
		'ajax' => TRUE,
	);
	$form_state += array(
		'rerender' => FALSE,
		'no_redirect' => !empty($form_state['ajax']),
		'args' => array($node) // It's important!!!
	);
	
	if (!empty($form_state['ajax']))
	{
		$output = ctools_modal_form_wrapper('webform_client_form_'.$webform_nid, $form_state, $product_id);
	}
	if($form_state['executed'] == TRUE)
	{
		$output = array();
		$output[] = ctools_modal_command_dismiss();
		//$output[] = ctools_ajax_command_reload(); // if you need reload main page after close modal window
	}
	ctools_ajax_render($output);
	exit;
}

You can try.

anou’s picture

Hello,
Just to say that for multistep webform, you must change:

<?php 
if ($form_state['executed'] == TRUE) { ...
?>

by

<?php
 if ($form_state['webform_completed'] == TRUE) { ...
?>

Thanks for your code.

Ruslan’s picture

Yea, you right

renesmee’s picture

how to display webform in a popup window on clicking on a image?

Agence Web CoherActio’s picture

You just need to add the class ctools-use-modal to your link.

Laurent

Agence Web CoherActio’s picture

Issue summary: View changes

Added php brackets for readability

agerard’s picture

Clueless question on an old post, from one who has not even written a module yet: where would one actually put this code? I.e. if one were trying to add a confirmation dialog to one of Drupal 7's node edit screens, how/where would one catch the interaction and change it? I've programmed Java apps, but I'm still learning php and working my way up to doing more at the code level in Drupal. I never seem to find explanations/tutorials that bridge the gap between building views and really using the API. With apologies for my ignorance, any pointers to resources in this area would be greatly appreciated!

panditvarun20’s picture

I have a filter form means with some check box and select list. I want to show them on pop up and after submit these checked values should be shown in a block.
How is it possible