I need a page on my Drupal 7 project site to display within a jQuery modal dialog box. Adding content to a jQuery dialog is straightforward with the Dialog module. The issue was the dialog box needed to contain the contents of a Panel. Previously I’ve embeded Views using views_embed_view(), but I’ve never tried to embed a Panel. A web search wouldn’t give me a quick answer, so I worked it out. Note: This article skims the initial implementation of a jQuery Dialog with the Dialog module, plus assumes you understand how to create a custom module.

Implementing hook_menu_alter() with dvm(), told me everything I needed to know. Temporary hook_menu_alter():

function module_menu_alter(&$items) {
  dvm($items);
}

Important part of the output is the menu definition for the page needed in the dialog:

  '[page_url]/panel' => array(
    'access callback' => 'ctools_access_menu',
    'access arguments' => array(
      array(
        'type' => 'none',
        'settings' => NULL,
      ),
    ),
    'page callback' => 'page_manager_page_execute',
    'page arguments' => array(
      '[page_machine_name]',
    ),
    'load arguments' => array(
      '[page_machine_name]',
      '%index',
      '%map',
    ),
    'file' => 'plugins/tasks/page.inc',
    'type' => 0,
    'module' => 'page_manager',
  ),

The response from a menu item function is a string, perfect for AJAX output. So, we’ll make the Page Manager think my new menu callback is the Drupal menu system. The original simple hook_menu() item from my module:

  $items['[page_url]/%ctools_js'] = array(
    'page callback' => 'module_ajax_callback',
    'page arguments' => array(1),
    'access arguments' => array('access content'),
  );

The new menu item mimics the Page Manager menu item becoming:

  $items['[page_url]/%ctools_js'] = array(
    'access arguments' => array('access content'),
    'page callback' => '[module_name]_ajax_callback',
    'page arguments' => array(
      1,
      '[page_machine_name]',
    ),
    'load arguments' => array(
      '[page_machine_name]',
      '%index',
      '%map',
    ),
  );

We’ll pass the all information page_manager_page_execute() expects, plus whether JavaScript is available to the new callback function. Notice, ctools_access_menu(), the original ctools page access access function will no longer be called. I am implementing hook_permissions() for my use, but you may need to consider other options to manage access.

The AJAX Panels page jQuery Dialog Drupal Menu callback function:

function [module_name]_ajax_callback($js, $subtask_id, &$map, $index) {
  // Be sure the inc file containing page_manager_page_execute is loaded
  ctools_include('page', 'page_manager', 'plugins/tasks');

  // Fake menu call to Page Manager to render panel
  $param_arr = array($subtask_id, &$map, $index);
  $output = call_user_func_array('page_manager_page_execute', $param_arr);

  //If JS is enabled, output as JSON
  if ($js) {
    $options = array(
      // Get the stored title and set to the dialog title.
      'title' => drupal_set_title(),
      // Adding dialog customizations.
      'resizable' => false,
      'dragable' => false,
    );
    $commands = array(dialog_command_display($output, $options));
    print ajax_render($commands);
    // Stop further output.
    exit;
  }
  // No JS, output a regular page.
  return $output;
}

This function should work for most uses. Let me know if you run into any issues with it.

When I can find some time, I think I’ll implement this as a real module…

Update 2020-10-12: I didn’t find the time.