Categories

(111)
(61)
(141)
(19)

Big Manual for creating CTools popups in Drupal 7

10.04.2015
Big Manual for creating CTools popups in Drupal 7
Author:

Creating CTools popups (modal windows) is not a complicated thing, but it has many important nuances. Therefore, this article is devoted to the various nuances of popup creation.

The simplest CTools popup

To create the simplest Ctools popup, you just need to write one line of JS code Drupal.CTools.Modal.show();. This JS function will create a full-fledged Ctools popup, you will just have to add content. As an example, let us create a popup that will appear when the page loads.

;(function($) {
Drupal.behaviors.firstPopup = {
attach: function (context, settings) {
var content = 'my content';
     var title = 'my title';
     Drupal.CTools.Modal.show();
     $('#modal-title').html(title); // Adding title to popup;
     $('#modal-content').html(content); // Adding content to popup;
   }
}
})(jQuery);

Add this JS to the desired page — and your first, simplest popup is ready. However, it has one drawback — like any other JS, it can output only data generated on the client’s side. Of course, in many cases, this may be enough, but more often Ctools popups are used to output dynamic data from the server. So let us consider more complex, but more common cases of popup creation and usage.

Classic CTools popup

First, let us create a new module called my_popup, where we will set up our experiments.

1. hook_menu

Let us begin with creating the necessary pages through which our modal window will be called.

/**
* Implements hook_menu().
*/
function my_popup_menu() {
   // Popup page.
   $items['first-popup/%ctools_js'] = array(
       'title' => 'My first popup',
       'page arguments' => array(1),
       'access callback' => TRUE,
       'page callback' => 'my_popup_callback',
       'type' => MENU_CALLBACK,
   );
   // Page that will display link to popup.
   $items['first-popup'] = array(
       'title' => 'My first popup',
       'access callback' => TRUE,
       'page callback' => 'my_popup_page',
       'type' => MENU_CALLBACK,
   );
   return $items;
}

We have created two pages.

'first-popup' is the page where we will output a link to the popup. This page is not compulsory and is used in this article only to show pop-ups and create links.

'first-popup/%ctools_js' is the page of the popup callback.

'page callback' are names of functions that will build pages.

'access callback' is responsible for providing access to the page. TRUE means that the page is always available to any user.

'page arguments' is an array of arguments to be submitted to the page callback function. 1 is a serial number of the parameter in the link to the popup, in this case it is %ctools_js, this is the parameter that determines whether JavaScript is enabled in the user's browser.

The 'title' (page title) and 'type' (menu type) options are not obligatory, but it is desirable to set them.

2. Modal window callback function

Now let us create function my_popup_callback, described in hook_menu(), which will build the popup. Here we consider three main options to create these features.

Outputting HTML content in the pop-up

/**
* Popup’s callback function.
 */
function my_popup_callback ($js = NULL) {
  // Content that we place in the popup.
  $popup_content =  t('Hello World') ;
  // Checking JavaScript is enabled.
  if (!$js) {
    // If JavaScript is disabled — outputting content without the popup.
    return $popup_content;
  }
  // If everything is ok and JavaScript is enabled — adding the necessary libraries to work with modal windows.
  ctools_include('modal');
  ctools_include('ajax');
  // Forming a modal window.
  $output = array();
  $output[] = ctools_modal_command_display(t('My first popup'), $popup_content);
  // Outputting the formed window in the browser.
  print ajax_render($output);
  drupal_exit();
}

ctools_modal_command_display() is a function responsible for building a simple modal window. It takes 2 parameters: the first is the popup title, the second is its HTML content.

Outputting the webform in the pop-up

If you need to display not specific content, but a webform in the popup, then the callback-function needs to be changed a little.

For example, let us create a simple webform with a text box and a button.

/**
* Popup’s example form.
*/
function id_example_form($form, $form_state) {
 $form = array();
 $form['some_text'] = array(
   '#title' => t('Some text'),
   '#type' => 'textfield',
 );
 $form['submit'] = array(
   '#type' => 'submit',
   '#value' => t('OK'),
 );
 return $form;
}

And let us rewrite the callback function to call this form in the popup.

/**
* Popup’s callback function for form.
*/   
function my_popup_callback ($js = NULL) {
    if (!$js) {
       // If JavaScript is disabled – we output the form without the popup
    		return drupal_get_form('id_example_form');
   }
 ctools_include('ajax');
 ctools_include('modal');
    	 // Setting up the initial form preferences.
 $form_state = array(
   'ajax' => TRUE,
   'title' => t('Form title’'),
 );
 $output = ctools_modal_form_wrapper('id_example_form', $form_state);
 print ajax_render($output);
 drupal_exit();
}


The popup is formed using ctools_modal_form_wrapper() function. It takes two parameters of id form - or rather, the name of the function that builds the form (in this case it is ‘id_example_form’) and the array with its preferences - $form_state. In this array, there is only one compulsory parameter 'ajax' => TRUE.

In this way, we can output in the modal window not only our form, but also any other form in Drupal, you just need to know the function with which it was created.

Outputting forms created through Webform module in the popup

As always, there are exceptions among rules. There are forms in Drupal impossible to output in the above-described method. These are forms created through the popular module Webforms. We consider two ways of outputting such forms in the popup.

In the first case, we will work with the webform as with a separate content type (node).

First of all, you need to install additional Webform AJAX module https://www.drupal.org/project/webform_ajax. Then go to the preferences of the necessary webform and check the AJAX mode. Because the Webform module itself can not work with AJAX.

Without these additional preferences, the webform in the pop-up will not be able to store data and will display an error.

Now let us write a rather original callback function to output the webform in the popup. Suppose we have a webform with id = "webform-client-form-23". Then 23 is the id of the node where our form is stored.

/**
* Callback for 'webform-client-form-23'.
*/
function my_popup_callback($js = NULL) {
       	$webFormNode = node_load(23); // Downloading the node with the webform. (з id 23).
   if (!$js) {
       // If JavaScript ia enabled – we go to the webform.
       return drupal_get_form('webform_client_form_23', $webFormNode, FALSE);
   }


 ctools_include('ajax');
 ctools_include('modal');
 
 $webFormView = node_view($webFormNode); // Generating an array of form parameters.
 $webForm = drupal_render($webFormView); // Forming the webform HTML structure.


 $output = array();
 //Outputting the webform in the popup.
 $output[] = ctools_modal_command_display(t('My WebForm'), $webForm);
 print ajax_render($output);
 drupal_exit();
}

In the latter case, we use a standard feature to output the form in the popup.

/**
* Callback for 'webform-client-form-23'.
*/
function my_popup_callback($js = NULL) {
     $webFormNode = node_load(23);
     if (!$js) {
       return drupal_get_form('webform_client_form_23', $webFormNode, FALSE);
     }
     $form_state = array(
       'title' => $webFormNode->title,
       'ajax' => TRUE,
     );


    	  // Sending the necessary parameters to the webform.
     $form_state['build_info']['args'] = array($webFormNode, FALSE);


    // Outputting the webform in the popup.
     $output = ctools_modal_form_wrapper('webform_client_form_23', $form_state);


   // Closing the popup after the submit button pressing.
      if (!empty($form_state['executed'])) {
       if (!isset($form_state['storage'])) {
         ctools_add_js('ajax-responder');
    		 	// Forming a link to the page.
          $redirect_url = trim($webFormNode->webform['redirect_url']);
$redirect_url = _webform_filter_values($redirect_url, $webFormNode, NULL, NULL, FALSE, TRUE);
         $output[] = ctools_ajax_command_redirect($redirect_url);
       }
       else {
         $form_state['executed'] = array();
         $output = ctools_modal_form_wrapper('webform_client_form_23' , $form_state);
       }
     }
 print ajax_render($output);
 drupal_exit();
}

Both in the former and the latter case, the webform will be output in the pop-up and will preserve all its functional properties.

3. Adding preferences and creating a theme for the popup

In many cases, you can skip this stage, but if is necessary to set up the initial preferences it is much easier to work with in the future. To begin, let us create a function in which we will store the original preferences.

Preferences function

/**
*Popup’s setings function.
*/
function my_ctools_popup_style() {
   static $added = FALSE;
   if ($added == FALSE) {
       $added = TRUE;
       // Adding the necessary libraries
       ctools_include('modal');
       ctools_include('ajax');
       ctools_modal_add_js();
       // Setting up the preferences for the popup
       $popup_style = array(
           'first-popup-style' => array(
               'modalSize' => array(
                   'type' => 'fixed', // Popup type.
                   'width' => 475, // Width
                   'height' => 'auto', // Height
                   'addHeight' => 700, // Maximum height
               ),
               'modalOptions' => array(
                   'opacity' => (float) 0.8, // Backgroung opacity
                   'background-color' => '#084b57', // Background color
               ),
               'closeText' => '', // Text for the «close» button
               'loadingText' => '', // Text with the popup downloading
               'animation' => 'fadeIn', // Animation type
               'modalTheme' => 'my_custom_theme', // Name of theme to be added
               'animationSpeed' => 'fast', // Popup animation speed
           ),
       );
       drupal_add_js($popup_style, 'setting'); // Adding the preferences
       ctools_add_js('my_popup_style', 'my_popup'); // Adding the theme (the first parameter is the theme file name, the second is the name of the module where this file is)
   }
}

Let us analyze this function in more detail.

Particular attention should be paid to the following parameters:

'first-popup-style' is the name for a group of our pop-up preferences (with it, we can use these settings later);

'modalTheme' is the name of the theme with which we can change the HTML structure of our modal window.

‘type’ can be fixed or scale. If it is fixed, the popup size is set in pixels, if it is scale, the popup size is set in percents by default scale.

'width' is the popup width. If the type is fixed, it is given in pixels, if it is scale, it is given in percent (but 1 is considered to be 100%, so for example 50% is 0.5). The default value is 0.8.

height is the popup height. It is set in a similar way as the width. The default value is 0.8.

'addHeight' is the maximum height in pixels. It is useful only when type = scale, and height = auto.

'animation' sets the animation method when loading the popup. It can take these values: show, fadeIn, slideDown. The defalut value is show.

'animationSpeed' is the animation speed. Can take these values: slow, medium, fast. The default value is fast.

Theme creation

A theme for the popup is a «js» file which describes its structure. The default theme is as follows.

Drupal.theme.prototype.CToolsModalDialog = function () {
  var html = ''
  html += '  <div id="ctools-modal">'
  html += '    <div class="ctools-modal-content">' // panels-modal-content
  html += '      <div class="modal-header">';
  html += '        <a class="close" href="#">';
  html +=            Drupal.CTools.Modal.currentSettings.closeText + Drupal.CTools.Modal.currentSettings.closeImage;
  html += '        </a>';
  html += '        <span id="modal-title" class="modal-title"> </span>';
  html += '      </div>';
  html += '      <div id="modal-content" class="modal-content">';
  html += '      </div>';
  html += '    </div>';
  html += '  </div>';
  return html;
}

As all pop-ups have an exactly identical structure, there can be problems with layout, so we will change it and create our own file for the theme 'my_popup_style.js' (the file name must always match the name that we set in the ctools_add_js() function when describing the preferences). We should place the file in our module folder, or create a «js» folder and keep it there.

Let us describe a new structure for the modal window.

Drupal.theme.prototype.my_custom_theme= function () {
  var html = '';
  html += '<div id="ctools-modal" class="popups-box my-first-popup">';
  html += ' <div class="ctools-modal-content my-popup ">';
  html += ' <span class="popups-close"><a class="close" href="#"></a></span>';
  html += ' <div class="modal-msg"></div>';
  html += ' <div class="modal-scroll"><div id="modal-content" class="modal-content popups-body"></div></div>';
  html += ' </div>';
  html += '</div>';
  return html;
}

Thus, we have somewhat simplified our popup and got rid of unnecessary elements and gave it a class of its own ‘my-first-popup’, which helps to distinguish this popup from the others during layout.

Note the first line of the code - Drupal.theme.prototype.my_custom_theme. Where «my_custom_theme» is the name of the theme that we set when describing the preferences, in the 'modalTheme' parameter. Also, when creating our theme for the pop-up, it is better not to change the standard classes: «popups-close», «ctools-modal-content», etc. If necessary, you can add your own classes.

4. Link to the popup

Well, our popup is almost ready. We just need to create the right link for its calling. There are many ways to create a link. However, the following rules are common to all.

  • The function with the preferences is always added before the link.
  • If you have missed the step with the preference setting for the pop-up, the following libraries MUST be added before the link: ctools_include('modal'); ctools_include('ajax'); ctools_modal_add_js();
  • The link must include a class of the type ctools-modal-[the name of the popup preference group]. Because it is via link that the popup preferences are added.
  • If the preferences have not been set, you need to add only class ctools-use-modal.

Let us consider a few ways of creating a link to the popup. We will display it on page first-popup, which we previously created through the hook_menu.

1. (recommended) Create a link to the popup with a special function ctools_modal_text_button ();

function my_popup_page() {
// Adding the function with preferences.
my_ctools_popup_style();
// Outputting the link.
return ctools_modal_text_button(t('Popup link'), 'first-popup/nojs', t('test popup'), 'ctools-modal-first-popup-style');
}

2. Use the standard Drupal function l();

function my_popup_page() {
// Adding a function with preferences.
my_ctools_popup_style();
// Outputting the link.
return l(t('Popup link'), 'first-popup/nojs', array('attributes'=>array('class'=>array('ctools-use-modal', 'ctools-modal-first-popup-style'))));
}

3. Create a link using Drupal render API.

function my_popup_page() {
// Adding the function with preferences.
my_ctools_popup_style();
// Setting preferences for the link
$link = array(
   '#type' => 'link',
   '#title' => t('Popup link'),
   '#href' => 'first-popup/nojs',
'#attributes' => array('class' => array('ctools-use-modal', 'ctools-modal-first-popup-style')),
    			// Outputting the link
return drupal_render($link);
}

Generally speaking, the way of link creation is not important. The main thing is to specify the correct path to the page with the pop-up and not forget to add classes 'ctools-use-modal' and 'ctools-modal-first-popup-style' in case you need to set your preferences and add your own theme.

Now you can go to 'first-popup' page and look at the results of our work.

CTools popup callback via webform submit

Now let us consider a somewhat more complicated case. Suppose we have a certain webform, for example, information about the user. And suppose the challenge is that when you click the form submit button, the data must be processed in a certain way (for example, undergo validation) and to be displayed in the pop-up via another form that must ask again if we really want to save this information and have two buttons "Yes", "No". Of course, all this must be done without reloading the page.

As you can see, the classic approach to creating pop-ups does not well fit here, though it can be done with JS. However, let us consider another option for it.

To begin with, let us create a form with which the popup will be called.

/**
* First example form.
*/
function my_ctools_popup_first_form($form, $form_state) {
 $form['user_name'] = array(
   '#title' => t('User name'),
   '#type' => 'textfield',
 );


 my_ctools_popup_style();


 $form['submit'] = array(
   '#type' => 'submit',
   '#value' => t('OK'),
   '#ajax' => array(
     'callback' => 'my_ctools_popup_submit_callback',
   ),
   '#attributes' => array('class' => array('ctools-modal-first-popup-style')),
 );
 return $form;
}

This is a common webform, but the 'submit' button has some special features:

First, we added initial preferences through the function my_ctools_popup_style(), described above.

Second, the added attribute '#ajax', which should allow to send webform data without reloading the page to the function 'my_ctools_popup_submit_callback'.

Third, the added class 'ctools-modal-first-popup-style' — in this way we add initial preferences from function my_ctools_popup_style for the future popup.

Now let us create a callback function to submit the first form, in which a popup with the second form will be called.

/**
* Сallback function for first form submit.
*/
function my_ctools_popup_submit_callback($form, &$form_state) {
// Receiving data from the first form
$values = $form_state['values'];


 $form_state = array(
   'title' => t('Example form'),
   'ajax' => TRUE,
   'values' => array(
     'first_fоrm_values' => $values, // Storing data from the first form to have access to them through the second form.
   ),
 );


 // Adding the necessary libraries
 ctools_include('modal');
 ctools_include('ajax');


// Outputting the second form the popup.
 $commands = ctools_modal_form_wrapper('my_ctools_popup_second_form', $form_state);


 return array('#type' => 'ajax', '#commands' => $commands);
}

'my_ctools_popup_second_form' is the name of the function with which we will create the second form — the one to be output in the popup.

/**
* Second example form.
*/
function my_ctools_popup_second_form($form, $form_state) {
 $values = $form_state['values']['first_fоrm_values']; // Getting data from the first form.


 $form['user_name'] = array(
   '#type' => 'html_tag',
   '#tag' => 'h2',
   '#value' => $values['user_name'], // Outputting data from the first form.
 );


 $form['submit_yes'] = array(
   '#type' => 'submit',
   '#value' => t('Yes'),
   '#ajax' => array(
     'callback' => 'my_ctools_popup_confirm_submit',
   ),
 );


 $form['submit_no'] = array(
   '#type' => 'submit',
   '#value' => t('No'),
   '#ajax' => array(
     'callback' => 'my_ctools_popup_confirm_submit',
   ),
 );
 return $form;
}

In this example we just output the data from the first form in the second one through the variable $values['first_fоrm_values'],

Now we just need to create the callback function to submit the second form and output it all on our test page.

/**
* Сallback function for second form submit.
*/
function my_ctools_popup_confirm_submit($form, &$form_state) {
 $value = $form_state['value'];


 // here we are doing something important with the data…

// Adding the necessary libraries
 ctools_include('modal');
 ctools_include('ajax');
//closing the popup. $commands[] = ctools_modal_command_dismiss(); print ajax_render($commands); exit; }

Let us output the first form of the test page and verify the results of our work.

/**
* View firs form on page.
*/
function my_popup_page() {
 $form = drupal_get_form('my_ctools_popup_first_form');
 return drupal_render($form);
}

So, we done a simple but very important thing. We transferred the data from the first form to the second one and output the second form in the pop-up. In this way you can easily work with data at each stage, adding validation for forms, processing the data, make requests to the database and more.

Popup positioning

Ctools popups are positioned on page automatically in the center of the window. However, the automatic positioning is often not convenient because it does not react to dynamic changes of the page (scroll, change of size of the window and of the popup itself).

This problem can be solved with a simple JS that you need to add to the pop-up page.

(function($) {
   Drupal.behaviors.AlingnPopup = {
   attach: function() {
       popup = $('#modalContent');
             // Function align element in the middle of the screen.
       function alignCenter () {
         // Popup size.
         var wpopup = popup.width();
         var hpopup = popup.height();


         // Window size.
         var hwindow = $(window).height();
         var wwindow = $(window).width();
         var Left = Math.max(40, parseInt($(window).width()/2 - wpopup/2));
         var Top = Math.max(40, parseInt($(window).height()/2 - hpopup/2));


         if(hwindow < hpopup) {
           popup.css({'position':'absolute'});
         }
         else {
           popup.css({'position':'fixed'});
         }
         popup.css({'left':Left, 'top':Top});
       };


       alignCenter();


       $(window).resize(function() {
         alignCenter();
       });


       popup.resize(function(){
         alignCenter();
       });
   }
 }
}(jQuery))

This JS will position the popup correctly depending on the size of the monitor and the popup itself, and respond to dynamic changes in the environment.

Mistakes

1) The most common mistake in popup creation looks like this:

An AJAX HTTP error occurred.
HTTP Result Code: 200
Debugging information follows.
...
Status test: OK
…

Most probably it means that there is a mistake is in the callback function. It can be anything - from not added libraries or incorrectly used functions to a forgotten semicolon.

Also, the problem may be in the wrong link, or in hook_menu ().

2) If you click on the link that should call the popup, it leads to the page, though JS are enabled.

Most probably, it means that the JS with the theme for the pop-up is added incorrectly, or or it has errors.

3) Also popups often do not work because of errors in external JS, you need to check the browser console to make sure all JS are working correctly.

22 votes, Rating: 4.6

Read also

1

This is a second portion of useful Drupal modules for social networks integration. Hopefully it would be useful for your website!

2

Need to integrate some social networks to your Drupal website? We've made up a list of the most useful modules for this purpose!

3

TOP Drupal modules helping you to configure the Views according to specific website.

4

Whom do you blame if something goes wrong on your Drupal website? Every system has vulnerabilities, but often its up to developer, how to make the website stable and secure.

5

Drupal module enabling to create and customize rules actions for node.js.

Need a quote? Let's discuss the project

Are you looking for someone to help you with your Drupal Web Development needs? Let’s get in touch and discuss the requirements of your project. We would love to hear from you.

Join the people who have already subscribed!

Want to be aware of important and interesting things happening? We will inform you about new blog posts on Drupal development, design, QA testing and more, as well news about Drupal events.

No charge. Unsubscribe anytime