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.

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

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.

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.

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

Now we need to distinguish between forms and content.

   
  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

  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.

  if (empty($output)) {
    $output = array();
    $output[] = ctools_modal_command_dismiss();
  }

Return the response for the given command.

  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