Using ctools modal forms for node edit

For a drupal 6 site I needed a node edit overlay. This is what I did.

Define the urls ajax/node/%node/edit and nojs/node/%node/edit. The idea is to use path similar to the normal drupal paths just prepended with what ctools modal needs. It needs are minor in that the url path must contain a string nojs and in this case a class attribute ctools-use-modal.

<?php
function annotate_ym_menu() {
 
$items['nojs/node/%node/%'] = array(
   
'page callback' => 'annotate_ym_ajax_node',
   
'page arguments' => array(0, 2, 3),
   
'access arguments' => array('post comments'),
  );
 
$items['ajax/node/%node/%'] = array(
   
'page callback' => 'annotate_ym_ajax_node',
   
'page arguments' => array(0, 2, 3),
   
'access arguments' => array('post comments'),
  );
  return
$items;
}
?>

When including the ctools plumbing the nojs part of urls is replaced by ajax. The lazy way is by

<?php
function annotate_ym_init() {
 
// Load the Ctools modal library and add the modal javascript.
 
ctools_include('modal');
 
ctools_modal_add_js();
}
?>

How do we generate these path? For this use case we use views. By adding the nid and a general text field linking to nojs/node/[nid]/edit and a class attribute ctools-use-modal we are ready to process the request.

The first part is to check for a javascript enabled browser. If no javascript use the path to redirect to the normal behaviour. That is node/[nid]/edit.

<?php
function annotate_ym_ajax_node($js, $node, $command) {
  if (!
ctools_js_load($js)) {
   
drupal_goto(implode('/', array_shift(arg())));
  }
?>

Now we are into ajax world so we need to use the ctools lib to render forms and content. As we are working with nodes only here we need to include the node handling code.

<?php
 
// include node file, necessary for node generation
 
module_load_include('inc', 'node', 'node.pages');
?>

Now we need to distinguish between forms and content.

<?php
  
 
if ($command == 'edit') {
   
$config = array(
     
'form_name' => $node->type . '_node_form',
    );
  }
  else if (
$command == 'delete') {
   
$config = array(
     
'form_name' => 'node_delete_confirm',
    );
  }
  else if (
$command == 'view') {
   
$config = array(
     
'content' => node_view($node, TRUE, TRUE, FALSE),
    );
  }
 
// TODO: view and others?
 
else {
   
// This is bad ajax response :(
   
drupal_goto();
  }
?>

Ready, set, go and render the request by ctools

<?php
  ctools_include
('modal');
 
ctools_include('ajax');

  if (

$config['form_name']) {
   
$form_state = array(
     
'title' => t('Node'),
     
'ajax' => TRUE,
    );
   
$form_state['args'] = array($node);
   
$output = ctools_modal_form_wrapper($config['form_name'], $form_state);
  }
  else {
     
$output = array();
     
$output[] = ctools_modal_command_display(t('Node'), $config['content']);
  }
?>

If $output is empty we are done so we can close the modal window.

<?php
 
if (empty($output)) {
   
$output = array();
   
$output[] = ctools_modal_command_dismiss();
  }
?>

Return the response for the given command.

<?php
  ctools_ajax_render
($output);
}
?>

A feature of my experiment is available on github

Some references:
- ctools module
- ctools ajax sample module
- A peek @ dialog module
- http://zroger.com/node/31
- Putting the comment form in modal by walker2238

Reacties

You don't need to define both of those menu items, you can use the %ctools_js in place of 'nojs' and 'ajax'. Then just pass all your links to it as "nojs" and it will figure out whether it should be nojs or ajax for you.

Eclipse

Great article, thanks for the tips! :)

Thank you sooooooooo much for this tutorial!! I'm a big newbie to ctool, you just saved me so many hours!