Skip to content
print.module 27.2 KiB
Newer Older
 * Display printer-friendly versions of Drupal pages
/********************************************************************
 ********************************************************************/

 */
function print_perm() {
  return array('access print', 'administer print');
}

 */
function print_menu($may_cache) {
  $items = array();

Chad Phillips's avatar
Chad Phillips committed
  if ($may_cache) {
    $items[] = array(
      'title' => t('Printer-friendly'),
Chad Phillips's avatar
Chad Phillips committed
      'callback' => 'print_controller',
      'access' => user_access('access print'),
Chad Phillips's avatar
Chad Phillips committed
      'type' => MENU_CALLBACK
    );
    $items[] = array(
      'path' => 'admin/settings/print',
      'title' => t('Printer-friendly'),
João Ventura's avatar
João Ventura committed
      'description' => t('Adds a printer-friendly version link to node pages.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array('print_main_settings'),
      'access'  => user_access('administer print'),
Chad Phillips's avatar
Chad Phillips committed
    );
  }
function print_link($type, $node = NULL, $teaser = FALSE) {
  $print_settings = variable_get('print_settings', print_settings_default());
  // No link is shown for several motives...
  if ( !($teaser) && ($node->type != 'book') && (!$node->parent) &&
      ($print_settings['show_link']) && user_access('access print') &&
      (($type == 'comment' && variable_get('print_display_comment', '0')) ||
      ($type == 'node' && variable_get('print_display_'. $node->type, '1')))) {
    if ($type == 'comment') {
      $query = "comment=$node->cid";
    }

    $links['print'] = array('href' => PRINT_PATH ."/". $node->nid,
                            'title' => $format['text'],
                            'attributes' => $format['attributes'],
                            'query' => $query);
/**
 * Implementation of hook_link_alter().
 */
function print_link_alter(&$node, &$links) {
  foreach ($links AS $module => $link) {
    if (strstr($module, 'book_printer')) {
      $print_settings = variable_get('print_settings', print_settings_default());
      if ($print_settings['book_link']) {
        $format = print_format_link();
        $format['attributes']['title'] = $link['attributes']['title'];
        $links[$module]['href'] = PRINT_PATH ."/". $link['href'];
        $links[$module]['attributes'] = $format['attributes'];
      }
    }
João Ventura's avatar
João Ventura committed
function print_help($path) {
  $print_settings = variable_get('print_settings', print_settings_default());

  if (($print_settings['show_link']) && ($print_settings['show_sys_link']) &&
      user_access('access print') && (preg_match("/^node\//i", $path) == 0)) {
    static $output = FALSE;

    if ($output === FALSE) {
      $output = TRUE;
      return print_insert_link();
function print_form_alter($form_id, &$form) {
  // Add the node-type settings option to activate the printer-friendly version link
  if ('node_type_form' == $form_id) {
    $form['workflow']['print_display'] = array(
      '#type' => 'checkbox',
      '#title' => t('Show printer-friendly version link'),
      '#return_value' => 1,
      '#default_value' => variable_get('print_display_'. $form['#node_type']->type, '1'),
      '#description' => t('Displays the link to a printer-friendly version of the content. Further configuration is available on the !settings.', array('!settings' => l(t('settings page'), 'admin/settings/print' ))),
    );
  }
  elseif ('comment_admin_settings' == $form_id) {
    $form['viewing_options']['print_display_comment'] = array(
      '#type' => 'checkbox',
João Ventura's avatar
João Ventura committed
      '#title' => t('Show printer-friendly version link in individual comments'),
      '#default_value' => variable_get('print_display_comment', '0'),
João Ventura's avatar
João Ventura committed
      '#description' => t('Displays the link to a printer-friendly version of the comment. Further configuration is available on the !settings.', array('!settings' => l(t('settings page'), 'admin/settings/print' ))),
/********************************************************************
 * Admin Settings: Print
 ********************************************************************/

/**
 * Default values of the print_settings variable
 */
function print_settings_default() {
  return array('show_link' => 1, 'show_sys_link' => 1, 'book_link' => 1, 'logo_url' => '', 'css' => '', 'urls' => 1, 'comments' => 0, 'newwindow' => 0, 'sendtoprinter' => 0);
/**
 * Default values of the print_sourceurl_settings variable
 */
function print_sourceurl_settings_default() {
  return array('enabled' => 1, 'date' => 0, 'forcenode' => 0);
}
/**
 * Default values of the print_robot_settings variable
 */
function print_robot_settings_default() {
  return array('noindex' => 1, 'nofollow' => 1, 'noarchive' => 0, 'nocache' => 0);
}
/**
 * Print module settings form
 */
function print_main_settings() {
  $print_settings = variable_get('print_settings', print_settings_default());
  
  $form['print_settings'] = array(
      '#type' => 'fieldset',
      '#tree' => TRUE,
    );
peter john hartman's avatar
peter john hartman committed

  $form['print_settings']['show_link'] = array(
    '#type' => 'radios',
    '#title' => t('Printer-friendly page link'),
    '#default_value' => $print_settings['show_link'],
    '#options' => array(t("Disabled"), t("Enabled")),
    '#description' => t("Enable or disable the printer-friendly page link for each node. Even if the link is disabled, you can still view the print version of a node by going to print/nid where nid is the numeric id of the node."),
  );

  $form['print_settings']['show_sys_link'] = array(
    '#type' => 'checkbox',
    '#title' => t('Show link in system (non-content) pages'),
    '#return_value' => 1,
    '#default_value' => $print_settings['show_sys_link'],
    '#description' => t('Setting this option will add a printer-friendly version page link on pages created by Drupal or the enabled modules.'),
  );

  $form['print_settings']['book_link'] = array(
    '#type' => 'checkbox',
    '#title' => t('Take control of the book module printer-friendly link'),
    '#return_value' => 1,
    '#default_value' => $print_settings['book_link'],
    '#description' => t('Activate this to have the printer-friendly link in book nodes handled by this module. Requires the (core) book module.'),
  );

  $form['print_settings']['logo_url'] = array(
    '#type' => 'textfield',
    '#title' => t('Logo URL'),
    '#default_value' => $print_settings['logo_url'],
    '#description' => t('An alternative logo to display on the printer-friendly version. If left empty, the current theme\'s logo is used.'),
peter john hartman's avatar
peter john hartman committed
    '#type' => 'textfield',
    '#title' => t('Stylesheet URL'),
    '#default_value' => $print_settings['css'],
peter john hartman's avatar
peter john hartman committed
    '#size' => 60,
    '#maxlength' => 64,
    '#description' => t('The URL to your custom print cascading stylesheet, if any. When none is specified, the default module CSS file is used.'),
peter john hartman's avatar
peter john hartman committed
  );
peter john hartman's avatar
peter john hartman committed
    '#type' => 'checkbox',
    '#title' => t('Printer-friendly URLs list'),
    '#return_value' => 1,
    '#default_value' => $print_settings['urls'],
    '#description' => t('If set, a list of the destination URLs for the page links will be displayed at the bottom of the page.'),
  $form['print_settings']['comments'] = array(
    '#type' => 'checkbox',
    '#title' => t('Include comments in printer-friendly version'),
    '#return_value' => 1,
    '#default_value' => $print_settings['comments'],
    '#description' => t('When this option is active, user comments are also included in the printer-friendly version. Requires the comment module.'),
  $form['print_settings']['newwindow'] = array(
    '#title' => t('Open the printer-friendly version in a new window'),
    '#options' => array(t("Disabled"), t("Use HTML target (does not validate as XHTML Strict)"), t("Use Javascript (requires browser support)")),
    '#default_value' => $print_settings['newwindow'],
    '#description' => t('Setting this option will make the printer-friendly version open in a new window/tab.'),
  );

  $form['print_settings']['sendtoprinter'] = array(
    '#type' => 'checkbox',
    '#title' => t('Send to printer'),
    '#return_value' => 1,
    '#default_value' => $print_settings['sendtoprinter'],
    '#description' => t('Automatically calls the browser\'s print function when the printer-friendly version is displayed.'),
  );

  $print_sourceurl_settings = variable_get('print_sourceurl_settings', print_sourceurl_settings_default());

  $form['print_sourceurl_settings'] = array(
      '#type' => 'fieldset',
      '#title' => t('Source URL'),
      '#collapsible' => true,
      '#collapsed' => true,
      '#tree' => true,
  );

  $form['print_sourceurl_settings']['enabled'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display source URL'),
    '#return_value' => 1,
    '#default_value' => $print_sourceurl_settings['enabled'],
    '#description' => t('When this option is selected, the URL for the original page will be displayed at the bottom of the printer-friendly version.'),
  );

  $form['print_sourceurl_settings']['date'] = array(
    '#type' => 'checkbox',
    '#title' => t('Add current time/date to the source URL'),
    '#return_value' => 1,
    '#default_value' => $print_sourceurl_settings['date'],
    '#description' => t('Display the current date and time in the Source URL line.'),
  );

  $form['print_sourceurl_settings']['forcenode'] = array(
    '#type' => 'checkbox',
    '#title' => t('Force use of node ID in source URL'),
peter john hartman's avatar
peter john hartman committed
    '#return_value' => 1,
    '#default_value' => $print_sourceurl_settings['forcenode'],
    '#description' => t('Drupal will attempt to use the page\'s defined alias in case there is one. To force the use of the fixed URL, activate this option.'),
peter john hartman's avatar
peter john hartman committed
  );
  $print_robot_settings = variable_get('print_robot_settings', print_robot_settings_default());

  $form['print_robot_settings'] = array(
      '#type' => 'fieldset',
      '#title' => t('Robots META tags'),
      '#collapsible' => true,
      '#collapsed' => true,
      '#tree' => TRUE,
  );

  $form['print_robot_settings']['noindex'] = array(
    '#type' => 'checkbox',
    '#title' => t('Add noindex'),
    '#return_value' => 1,
    '#default_value' => $print_robot_settings['noindex'],
    '#description' => t('Instruct robots to not index printer-friendly pages. Recommended for good search engine karma.')
peter john hartman's avatar
peter john hartman committed
  );

  $form['print_robot_settings']['nofollow'] = array(
    '#type' => 'checkbox',
    '#title' => t('Add nofollow'),
    '#return_value' => 1,
    '#default_value' => $print_robot_settings['nofollow'],
    '#description' => t('Instruct robots to not follow outgoing links on printer-friendly pages.')
  );

  $form['print_robot_settings']['noarchive'] = array(
    '#type' => 'checkbox',
    '#title' => t('Add noarchive'),
    '#return_value' => 1,
    '#default_value' => $print_robot_settings['noarchive'],
    '#description' => t('Non-standard tag to instruct search engines to not show a "Cached" link for your printer-friendly pages. Recognized by Googlebot.')
  );

  $form['print_robot_settings']['nocache'] = array(
    '#type' => 'checkbox',
    '#title' => t('Add nocache'),
    '#return_value' => 1,
    '#default_value' => $print_robot_settings['nocache'],
    '#description' => t('Non-standard tag to instruct search engines to not show a "Cached" link for your printer-friendly pages')
  return system_settings_form($form);
}

/********************************************************************
 ********************************************************************/

/**
 * Print module path catcher
 */
  // Remove the print/ prefix
  $args = substr($_GET['q'], strlen(PRINT_PATH)+1);

  $cid = $_GET['comment'];

  $nid = $args;
  if (!is_numeric($nid)) {
    // Indirect call with print/alias
    // If there is a path alias with these arguments, generate a printer-friendly version for it
    $path = drupal_get_normal_path($args);
    $ret = preg_match("/^node\/(.*)/i", $path, $matches);
    if ($ret == 1) {
      $nid = $matches[1];
  if (is_numeric($nid)) {
    print_generate_node($nid, $cid);
    $ret = preg_match("/^book\/export\/html\/(.*)/i", $args, $matches);
    if ($ret == 1) {
      // This is a book PF page link, handle trough the book handling functions
      print_generate_book($matches[1]);
    }
    else {
      // If no content node was found, handle the page printing with the 'printable' engine
      print_generate_path($args);
    }
}

/********************************************************************
 ********************************************************************/

/**
 * Generates a meta tag to tell robots what they may index based on module settings
 *
 * @return string
 */
function _print_robots_meta_generator() {
  $robots_settings = variable_get('print_robot_settings', print_robot_settings_default());
  if (!empty($robots_settings['noindex'])) {
  if (!empty($robots_settings['nofollow'])) {
  if (!empty($robots_settings['noarchive'])) {
  if (!empty($robots_settings['nocache'])) {
  if (sizeof($robots_meta) > 0) {
    $robots_meta = isset($robots_meta[1]) ? implode(', ', $robots_meta) : $robots_meta[0];
    $robots_meta = '<meta name="robots" content="'. $robots_meta ."\" />\n";
/**
 * Generates the HTML to insert in the template file
function _print_var_generator($node, $cid = NULL) {
  global $base_url;

  // print module settings
  $print_settings = variable_get('print_settings', print_settings_default());
  $print_sourceurl_settings = variable_get('print_sourceurl_settings', print_sourceurl_settings_default());

  $print["language"] = $GLOBALS['locale'];
  $print["title"] = $node->title;
  $print["robots_meta"] = _print_robots_meta_generator();
//  $print["base_href"] = "<base href=\"".$base_url."/node/".$node->nid."\" />\n";
  $print["base_href"] = "<base href=\"". url("node/$node->nid", NULL, NULL, TRUE) ."\" />\n";
  $print["head"] = drupal_get_html_head();
  $print["favicon"] = theme_get_setting("toggle_favicon") ? "<link rel=\"shortcut icon\" href=\"". theme_get_setting("favicon") ."\" type=\"image/x-icon\"/>\n" : "";
    $print["css"] = "<style type=\"text/css\">@import \"". $print_settings['css'] ."\";</style>\n";
  } 
  else {
    include_once(drupal_get_path('module', 'print') ."/print.css");
    $print["css"] = "<style type=\"text/css\">". ob_get_contents() ."</style>\n";
    ob_end_clean();
  }

  $print["sendtoprinter"] = $print_settings['sendtoprinter'] ? " onload=\"window.print();\"" : "";
    
  $print["logo"] = !empty($print_settings['logo_url']) ? $print_settings['logo_url'] : theme_get_setting('logo');
  $print["logo"] = $print["logo"] ? "<img class=\"print-logo\" src=\"". $print["logo"] ."\" alt=\"\" />\n" : "";

  /* Grab and format the src URL */
  if ($print_sourceurl_settings['enabled'] == 1) {
    if (empty($print_sourceurl_settings['forcenode'])) {
      $print["source_url"] = url("node/$node->nid", NULL, NULL, TRUE);
    } 
    else {
      $print["source_url"] = $base_url .'/'. (((bool)variable_get('clean_url', '0')) ? '' : '?q=') .'node/'. $node->nid;
    if ($cid) {
      $print["source_url"] .= "#comment-$cid";
    }
    $print["printdate"] = $print_sourceurl_settings['date'] ? (" (". t("retrieved on") ." ". format_date(time(), 'small') .")") : "";
    $print["source_url"] = "<strong>". t('Source URL') . $print["printdate"] .":</strong> <a href=\"". $print["source_url"] ."\">". $print["source_url"] ."</a>";
  } 
  else {
  $print["site_name"] = variable_get('site_name', 0) ? (t('Published on') ." ". variable_get('site_name', 0) ." (". l($base_url, $base_url) .")") : "";
  $print["submitted"] = theme_get_setting("toggle_node_info_$node->type") ? t('By') ." ". $node->name : "";
  $print["created"] = theme_get_setting("toggle_node_info_$node->type") ? t('Created') ." ". format_date($node->created, 'small') : "";

  // Display the collected links at the bottom of the page. Code once taken from Kjartan Mannes' project.module
  if (!empty($print_settings['urls'])) {
    $urls = print_friendly_urls();
    $max = count($urls);
    if ($max) {
      $print["pfp_links"] = '';
      for ($i = 0; $i < $max; $i++) {
        $print["pfp_links"] .= '['. ($i + 1) .'] '. $urls[$i] ."<br />\n";
      }
      $print["pfp_links"] = "<p><strong>". t('Links:') ."</strong><br />". $print["pfp_links"] ."</p>";
  $print["footer_message"] = filter_xss_admin(variable_get('site_footer', FALSE)) ."\n". theme('blocks', 'footer') ;
 * We need to manipulate URLs so that they are recorded as absolute in the 
 *  Printer-friendly URLs list, and to add a [n] footnote marker.
 *
 * @return string containing the changed <a> tag
 */
function print_rewrite_urls($matches) {
  // Get value of Printer-friendly URLs setting
  $print_settings = variable_get('print_settings', print_settings_default());
  $pfurls = (!empty($print_settings['urls']));

  //Temporarily convert spaces to %20 so that it isn't split below
  $in_string = false; 
  for ($i=0; $i < strlen($matches[1]); $i++) {
    if ($matches[1][$i] == '"') {
      $in_string = !$in_string;
    }
    if (($matches[1][$i] == ' ') && ($in_string)) {
      $matches[1]=substr_replace($matches[1], "%20", $i, 1);
    }
  }

  // first, split the html into the different tag attributes
  $attribs = preg_split("/\s+/m", $matches[1]);

  for ($i=1; $i < count($attribs); $i++) {
    // If the attribute is href or src, we may need to rewrite the URL in the value
    if (preg_match("/^href|src/i", $attribs[$i]) > 0) {
      // We may need to rewrite the URL, so let's isolate it
      preg_match("/.*?=(.*)/is", $attribs[$i], $urls);
      $url = trim($urls[1], " \t\n\r\0\x0B\"\'");

      if (strpos($url, '://') || preg_match("/^mailto:.*?@.*?\..*?$/iu", $url)) {
        // URL is absolute, do nothing
        $newurl = urldecode($url);
        if (substr($url, 0, 1) == "#") {
          // URL is an anchor tag
          if ($pfurls) {
            $path = substr($_GET['q'], strlen(PRINT_PATH)+1);
            if (is_numeric($path)) {
              $path = "node/$path";
            }
            // Printer-friendly URLs is on, so we need to make it absolute
            $newurl = url($path, NULL, substr(urldecode($url), 1), TRUE);
          // Because base href is the original page, change the link to
          // still be usable inside the print page
          $matches[1] = str_replace($url, $_GET['q'] . $url, $matches[1]);
          // URL is relative, convert it into absolute URL
          $clean_url = (bool)variable_get('clean_url', '0');
          if (substr($url, 0, 1) == "/") {
            // If it starts with '/' just append it to the server name
            $max = strpos($base_url, '/', 7);
            if ($max === false) {
              $server = $base_url;
            }
            else {
              $server = substr($base_url, 0, $max);
            }
            $newurl = $server .'/'. trim(urldecode($url), "/");
          }
          elseif ((!$clean_url) && (preg_match("/^[index.php]?\?q=.*/i", $url))) {
            // If Clean URLs is disabled, and it starts with q=?, just prepend with the base URL
            $newurl = $base_url .'/'. trim(urldecode($url), "/");
          } 
          else {
            $newurl = url(trim(urldecode($url), "/"), NULL, NULL, TRUE);
          $matches[1] = str_replace($url, $newurl, $matches[1]);
        }
      }
    }
João Ventura's avatar
João Ventura committed
  }
  $ret = '<'. $matches[1] .'>';
  if ($attribs[0] == "a") {
    $ret .= $matches[2] .'</a>';
    if (($pfurls) && ($newurl)) {
      $ret .= ' <span class="print-footnote">['. print_friendly_urls(trim(stripslashes($newurl))) .']</span>';
João Ventura's avatar
João Ventura committed
}

/**
 * Auxiliary function to store the Printer-friendly URLs list as static.
 *
 * @return string containing the list of URLs previously stored if $url is 0, 
 *         or the current count otherwise.
 */
function print_friendly_urls($url = 0) {
  static $urls = array();
  if ($url) {
    $urls[] = $url;
    return count($urls);
  }
  return $urls;
}

/**
 * Auxiliary function to fill the Printer-friendly link attributes
 *
 * @return array of formatted attributes
 */
function print_format_link() {
  $print_settings = variable_get('print_settings', print_settings_default());
  $attributes = array('title' => t('Display a printer-friendly version of this page.'), 
                      'class' => 'print-page');
  switch ($print_settings['newwindow']) {
  case 1:
    break;
  case 2:
    $attributes['onclick'] = 'window.open(this.href); return false';
    break;
  }

  return array('text' => t('Printer-friendly version'),
               'attributes' => $attributes);
}

/**
 * Auxiliary function to display a formatted Printer-friendly link
 *
 * @return string
 */
function print_insert_link($path = NULL) {
  if ($path === NULL) {
    $path = PRINT_PATH ."/". $_GET['q'];
  return '<span class="print">'. l($format['text'], $path, $format['attributes'], NULL, NULL, TRUE, FALSE) .'</span>';
/**
 * Auxiliary function to resolve the most appropriate template trying to find
 * a content specific template in the theme or module dir before falling back
 * on a generic template also in those dirs.
 *
 * @return string with the most suitable template filename
 */
function print_get_template($type = NULL) {
  if ($type) {
    // If the node type is known, then try to find that type's template file
    // First in the theme directory
    $filename = drupal_get_path('theme', $GLOBALS['theme_key']) ."/print.$type.tpl.php";
    if (file_exists($filename)) {
      return $filename;
    }
    // Then in the module directory
    $filename = drupal_get_path('module', 'print') ."/print.$type.tpl.php";
    if (file_exists($filename)) {
      return $filename;
    }
  }
  // Search for a generic template file
  // First in the theme directory
  $filename = drupal_get_path('theme', $GLOBALS['theme_key']) ."/print.tpl.php";
  if (file_exists($filename)) {
    return $filename;
  }
  // Then in the module directory
  // This one must always exist (provided with the module!)
  return drupal_get_path('module', 'print') ."/print.tpl.php";
}

/********************************************************************
 * Module Functions : Content renderers
 ********************************************************************/

 * Outputs a printer-friendly page. Used for content types
function print_generate_node($nid, $cid = NULL) {
  // We can take a node id
  $node = node_load(array('nid' => $nid));
  if (!node_access('view', $node)) {
    // Access is denied
    return drupal_access_denied();
  }
  //alert other modules that we are generating a printer-friendly page, so they can choose to show/hide info
  $node->printing = true;
  // Turn off Pagination by the Paging module
  unset($node->pages);
  unset($node->pages_count);

  unset($node->teaser);
  if ($cid === NULL) {
    // Adapted (simplified) version of node_view for Drupal 5.x
    //Render the node content
    $node = node_build_content($node, false, true);
    // Disable fivestar widget output
    unset($node->content["fivestar_widget"]);
    // Disable service links module output
    unset($node->content["service_links"]);
    $node->body = drupal_render($node->content);
  }
  $print_settings = variable_get('print_settings', print_settings_default());

  if (function_exists(comment_render) && (($cid != NULL) || ($print_settings['comments']))) {
    //Print only the requested comment (or if $cid is NULL, all of them)
    $comments = comment_render($node, $cid);
    
    //Remove the comment title hyperlink
    $comments = preg_replace("/(<h3.*?>)(<a.*?>)(.*?)<\/a>(<\/h3>)/", "$1$3$4", $comments);
    //Remove the comment links
    $comments = preg_replace("/\s*<div class=\"links\">.*?<\/div>/sim", "", $comments);
    if ($cid != NULL) {
      // Single comment requested, output only the comment
      unset($node->body);
    }
    $node->body .= $comments;
  }

  node_invoke_nodeapi($node, 'alter', false, true);

  // Convert the a href elements
  $pattern = "@<(a\s[^>]*?)>(.*?)</a>@is";
  $node->body = preg_replace_callback($pattern, "print_rewrite_urls", $node->body);
  $print = _print_var_generator($node, $cid);
  include_once(print_get_template($node->type));
/**
 * Outputs a printer-friendly page. Used for drupal core pages.
 */
function print_generate_path($path) {
  global $base_url;

  $path = drupal_get_normal_path($path);

  menu_set_active_item($path);
  // Adapted from index.php.
  $node = new stdClass();
  $node->body = menu_execute_active_handler();

  // It may happen that a drupal_not_found is called in the above call
  if (preg_match('/404 Not Found/', drupal_get_headers()) == 1) {
    return;
  }

  switch ($node->body) {
  case MENU_NOT_FOUND:
    return drupal_not_found();
    break;
  case MENU_ACCESS_DENIED:
    return drupal_access_denied();
    break;
  }

  // Delete any links area
  $node->body = preg_replace("/\s*<div class=\"links\">.*?<\/div>/sim", "", $node->body);

  // Convert the a href elements
  $pattern = "@<(a\s[^>]*?)>(.*?)</a>@is";
  $node->body = preg_replace_callback($pattern, "print_rewrite_urls", $node->body);

  init_theme();

  $print = _print_var_generator($node);

/**
 * Outputs a printer-friendly page. Used for book pages
 */
function print_generate_book($nid) {
  global $base_url;

  $node = node_load(array('nid' => $nid));
  if (!node_access('view', $node) || (!user_access('see printer-friendly version'))) {
    // Access is denied
    return drupal_access_denied();

  $depth = count(book_location($node)) + 1;
  $node->body = book_recurse($nid, $depth, 'print_node_visitor_html_pre', 'book_node_visitor_html_post');

  // Convert the a href elements
  $pattern = "@<(a\s[^>]*?)>(.*?)</a>@is";
  $node->body = preg_replace_callback($pattern, "print_rewrite_urls", $node->body);

  init_theme();

  $print = _print_var_generator($node);
  // The title is already displayed by the book_recurse, so avoid duplication
  $print["title"] = "";

  include_once(print_get_template($node->type));

/**
 * My own version of book_node_visitor_html_pre so that CCK pages in book nodes
 * come out right
 */
function print_node_visitor_html_pre($node, $depth, $nid) {
  $node = node_build_content($node);

  // Allow modules to make their own additions to the node.

  unset($node->content["book_navigation"]);

  $output .= "<div id=\"node-". $node->nid ."\" class=\"section-$depth\">\n";
  $output .= "<h1 class=\"book-heading\">". check_plain($node->title) ."</h1>\n";
  $output .= drupal_render($node->content);

  return $output;
}