Advanced modal forms - modal windows with CTools (singe and multistep)

So I client of mine wanted to use popups :) but not just ordinary popups, he wanted to put there, login, registration, node adding, notificiations, user invites for OG, simple menus and even FB login and registration. We also needed to save some user choices before the login/registration and act upon them after the user logs in. So spoiler alert now "if you want to save budget and time" dont't use modal windows or any popups for complex workflows. The reason is that using popups is something like inception, it is a web page inside a webpage, as basically you get completely functioning page in a modal window, but without page refreshing. So you are using ajax for communication in most cases. The reason I don't recommend it as in most cases you will be on your own to build everything, for example, you will not be able to use nice Rules workflow with it or any other drupal modules that could fasten your development and you will need to build everything or most by your own and then also you will come into different obstacles with drupal system.

If you want something cool, you should use something like this, which is pretty similar in looks, but much more compatible with drupal and its workflow so you can use the power of other modules
http://drupaldump.com/cool-loginregister-screens-without-overlays-no-re…

Anyway if you do choose the path to use modal windows, you should first download this https://github.com/jlbellido/ctools_expamples I also attached it to this post, in case it disappears. It has examples for plain modal window, modal window for node form and multistep modal windows. So with that you will be good to go and some testing and practicing will help you understand what is going on.

Here are some tips though:

If you just need simple modal forms for login, register, password reset, you can go with
https://www.drupal.org/project/modal_forms module and with little or no customization cover it all.

If you want just to put some menu item in modal, you can use Ctools Automodal https://www.drupal.org/project/ctools_automodal
and you are set to go.

For handling the forms and what happens on submit, check this example http://deeson-online.co.uk/labs/insert-form-pop-modal-ctools-and-drupal… where you see how to use this two sets of functions
http://drupalcontrib.org/api/drupal/contributions%21ctools%21includes%2…
https://api.drupal.org/api/drupal/includes%21ajax.inc/7

As with those functions you get to display forms, custom htmls, dismiss modal windows, replace/change parts of the page etc.

And now some real quick tips:

To setup modal sizes, you need to add data to Drupal.settings https://www.drupal.org/node/304258
how to do that you can check on this link where it is done for a views view http://drupion.com/blog/10-steps-creating-ctools-modal-window-drupal-7 or just check the Modal Forms module and see what they do with _modal_forms_doheader() which is called with modal_forms_init() in the beginning of module.
Besides the sizes, you can also set the html template used for creation of the modal window with "modalTheme" variable, that is then set in .JS file and loaded from there.

But to override the Drupal.settings setup, just before creation of modal you can do that with setting the output to

      $output[] = ajax_command_css('.ctools-modal-content', array('height' => '320px'));
      $output[] = ajax_command_css('#modalContent', array('height' => '400px'));
      ...

and then rendering it with print ajax_render($output);

To redirect after the submission of the modal use

  $commands[] = ctools_modal_command_dismiss();
  $commands[] = ctools_ajax_command_redirect('some-page');
  print ajax_render($commands);

To add node form, depending on what type of fields you have, you could have problems with either opening or saving and closing modal. This is the code I found to work the best, you need to prepare the node and then render it. In the example below, content type is called "team" and it is mentioned twice in the code.

function modal_forms_custom_add_team($js = NULL) {
  global $user, $language;

  if (!$js) {
    return;
  }

  ctools_include('modal');
  ctools_include('ajax');

  module_load_include('inc', 'node', 'node.pages');

  $node = (object) array(
    'uid' => $user->uid,
    'name' => (isset($user->name) ? $user->name : ''),
    'type' => 'team',
    'language' => $language->language,
    );
  $node->title = NULL;

  node_object_prepare($node);

  $form_state = array(
    'ajax' => TRUE,
    'title' => 'Add a new Team',
    'node' => $node,
    );

  $form_state['build_info']['args'] = array($node);

  $commands = ctools_modal_form_wrapper('team_node_form', $form_state);

  if (!empty($form_state['executed'])) {
    ctools_add_js('ajax-responder');
    $commands[] = ctools_modal_command_dismiss();
    $commands[] = ctools_ajax_command_redirect('some-page/'.$form_state['nid']);
  }

  print ajax_render($commands);
}

Bare in mind, in some cases, you will get some warnings and in the modal case, it will make that ugly alert window and break the modal, so either find a way to fix them or remove error notifications in drupal settings, otherwise you will have hard time making them work.

For multistep ctools modal window forms, you have a bit different approach, please check the attached code for an example and here below are some tips:

Lets say you are using login/register and then create a node form workflow, first problem you could have is menu item access problem. If you set to login menu this
'access callback' => 'user_is_anonymous',
it is not a problem, but if you set it to multistep like above, you will get errors as after you login and go to second step of node creation, this access check will break your form. So you should do some custom access callback or just use something like
'access arguments' => array('access content')
or just
'access callback' => TRUE
which will probably cover all the cases. More here https://www.drupal.org/node/109157

In multistep forms you can create your forms and submit callbacks and validations but what to do with already created forms in system, like user_login/user_register etc. You just do this

   // Define forms
    'forms' => array(
      'register' => array(
        'form id' => 'user_register_form'
        ),
....

in your multistep form defining, and that is it. It will be used.

The problem can occur if you want to override submit callback. You can use hooks like
my_module_form_user_register_form_alter but they will not always work. I had a situation where I had a normal login form and a multistep login form on the same page and for some reason(still not sure why) nornal login form would override the hook above and just ignore the code in it, maybe it was also due to me using custom template for login_form in template.php. Not sure anymore but there is where I solved that issue with



function my_theme_preprocess_user_register_form(&$vars) {
 if (isset($vars['form']['buttons'])){
   $register_path = 'modal/nojs/register_team';
   $vars['form']['actions']['#access'] = FALSE; 
   $vars['form']['#submit'] = array(
    'user_login_submit',
    'ctools_wizard_submit',
    );
 } else{
   $register_path = 'modal/nojs/register';
 }

Which would check if the form has "buttons" array, which is kind of hackish, but it has it only if we are using multistep form, so it is pretty safe way to determine what to do with form.
So if it is a multistep we use first part where I disable the standard Drupal Submit button (another tip) with
$vars['form']['actions']['#access'] = FALSE;
then change submit functions and put them in right order.
This can also be done on hook_FORM_ID_alter in module, but in some cases it will not work. Even with this solution
https://api.drupal.org/api/drupal/modules%21system%21system.api.php/fun…
so change it in template preprocess and that is it.

So this should be it. As I said, don't use it for complex workflows as you will hit problems. But if you must, all of this should help you solve any possible scenario.