Newer
Older
/**
* @file
* Contains the functions to generate Printer-friendly pages.
*
* This file is included by the core PF module, and includes all the
* functions necessary to generate a PF version of the original page
* in HTML format.
/**
* Generate an HTML version of the printer-friendly page
*
* @see print_controller()
*/
$args = func_get_args();
$cid = isset($_GET['comment']) ? (int)$_GET['comment'] : NULL;
João Ventura
committed
$node = print_controller($path, $link['format'], $cid);
if ($node) {
// Handle the query
$query = $_GET;
unset($query['q']);
$html = theme('print', array('node' => $node, 'query' => $query, 'format' => $link['format']));
drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
João Ventura
committed
print $html;
João Ventura
committed
$nodepath = (isset($node->nid)) ? 'node/' . $node->nid : drupal_get_normal_path($path);
db_merge('print_page_counter')
->key(array('path' => $nodepath))
->fields(array(
->expression('totalcount', 'totalcount + 1')
* Select the print generator function based on the page type
*
* Depending on the type of node, this functions chooses the appropriate
* generator function.
*
* path of the original page
* comment ID of the individual comment to be rendered
João Ventura
committed
* @param string $view_mode
* (optional) view mode to be used when rendering the content
*
* @return object
* node-like object to be used in the print template
*
* @see _print_generate_node()
* @see _print_generate_path()
* @see _print_generate_book()
João Ventura
committed
function print_controller($path, $format, $cid = NULL, $view_mode = PRINT_VIEW_MODE) {
if (empty($path)) {
// If no path was provided, let's try to generate a page for the referer
global $base_url;
$ref = $_SERVER['HTTP_REFERER'];
$path = preg_replace("!^$base_url/!", '', $ref);
João Ventura
committed
if (($path === $ref) || empty($path)) {
$path = variable_get('site_frontpage', 'node');
if ($alias = drupal_lookup_path('source', $path)) {
// Indirect call with print/alias
// If there is a path alias with these arguments, generate a printer-friendly version for it
$path = $alias;
$parts = explode('/', $path);
if (($parts[0] == 'node') && (count($parts) > 1) && ctype_digit($parts[1])) {
array_shift($parts);
$path = implode('/', $parts);
}
$revision_view = preg_match('!^[\d]*/revisions/[\d]*/view$!', $path);
if (ctype_digit($parts[0]) && ((count($parts) == 1) || $revision_view)) {
$vid = $revision_view ? $parts[2] : NULL;
João Ventura
committed
$node = _print_generate_node($path, $format, $vid, $cid, $view_mode);
$ret = preg_match('!^book/export/html/(.*)!i', $path, $matches);
if ($ret == 1) {
// This is a book PF page link, handle trough the book handling functions
$node = _print_generate_book($matches[1], $format);
}
else {
// If no content node was found, handle the page printing with the 'printable' engine
João Ventura
committed
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
return $node;
}
/**
* Implements hook_preprocess_HOOK().
*/
function print_preprocess_print(&$variables) {
global $language;
$node = $variables['node'];
$format = $variables['format'];
$path = drupal_get_path_alias(empty($node->nid) ? $node->path : "node/$node->nid");
static $hooks = NULL;
if (!isset($hooks)) {
drupal_theme_initialize();
$hooks = theme_get_registry();
}
$variables['page']['#show_messages'] = FALSE;
// Stolen from theme() so that ALL preprocess functions are called
$hook = 'page';
$info = $hooks[$hook];
if (isset($info['preprocess functions']) || isset($info['process functions'])) {
$variables['theme_hook_suggestions'] = array();
foreach (array('preprocess functions', 'process functions') as $phase) {
if (!empty($info[$phase])) {
foreach ($info[$phase] as $processor_function) {
if (function_exists($processor_function)) {
// We don't want a poorly behaved process function changing $hook.
$hook_clone = $hook;
$processor_function($variables, $hook_clone);
}
}
}
}
}
$logo_url = FALSE;
switch (variable_get('print_logo_options', PRINT_LOGO_OPTIONS_DEFAULT)) {
case 1: // theme's
$logo_url = theme_get_setting('logo');
break;
case 2: // user-specifed
$logo_url = strip_tags(variable_get('print_logo_url', PRINT_LOGO_URL_DEFAULT));
break;
}
$logo_url = preg_replace('!^' . base_path() . '!', '', $logo_url);
João Ventura
committed
$variables['print_logo'] = $logo_url ? theme('image', array('path' => $logo_url, 'alt' => variable_get('site_name', 'Drupal'), 'attributes' => array('class' => 'print-logo', 'id' => 'logo'))) : NULL;
$variables['print_node'] = $node;
$variables['content'] = $node->content;
$variables['scripts'] = drupal_get_js();
$variables['footer_scripts'] = drupal_get_js('footer');
$variables['sourceurl_enabled'] = variable_get('print_sourceurl_enabled', PRINT_SOURCEURL_ENABLED_DEFAULT);
$variables['url'] = url($path, array('absolute' => TRUE, 'query' => $variables['query']));
$variables['source_url'] = url(variable_get('print_sourceurl_forcenode', PRINT_SOURCEURL_FORCENODE_DEFAULT) ? drupal_get_normal_path($path) : $path, array('alias' => TRUE, 'absolute' => TRUE, 'query' => $variables['query']));
João Ventura
committed
$variables['cid'] = isset($node->cid) ? $node->cid : NULL;
$variables['print_title'] = check_plain($node->title);
$variables['head'] = drupal_get_html_head();
$variables['robots_meta'] = _print_robots_meta_generator();
$variables['css'] = _print_css_generator($variables['expand_css']);
João Ventura
committed
if (variable_get('print_html_sendtoprinter', PRINT_HTML_SENDTOPRINTER_DEFAULT) && ($format == 'html')) {
drupal_add_js('misc/drupal.js', array('weight' => JS_LIBRARY));
$window_close = (variable_get('print_html_new_window', PRINT_HTML_NEW_WINDOW_DEFAULT) && variable_get('print_html_windowclose', PRINT_HTML_WINDOWCLOSE_DEFAULT)) ? 'window.close();' : '';
João Ventura
committed
$variables['sendtoprinter'] = '<script type="text/javascript">(function ($) { Drupal.behaviors.print = {attach: function(context) {$(window).load(function() {window.print();' . $window_close . '})}}})(jQuery);</script>';
João Ventura
committed
}
$type = (isset($node->type)) ? $node->type : '';
$nid = (isset($node->nid)) ? $node->nid : '';
$variables['theme_hook_suggestions'] = array();
$variables['theme_hook_suggestions'][] = "print__node__{$type}";
$variables['theme_hook_suggestions'][] = "print__node__{$type}__{$nid}";
$variables['theme_hook_suggestions'][] = "print__{$format}";
$variables['theme_hook_suggestions'][] = "print__{$format}__node__{$type}";
$variables['theme_hook_suggestions'][] = "print__{$format}__node__{$type}__{$nid}";
}
/**
* Returns HTML for the published line of the print template.
*
* @param array $vars
* An empty associative array
*
* @return string
* HTML text with the published line
*
* @ingroup themeable
João Ventura
committed
*/
function theme_print_published($vars) {
global $base_url;
$published_site = variable_get('site_name', 0);
return $published_site ? t('Published on %site_name', array('%site_name' => $published_site)) . ' (' . l($base_url, $base_url) . ')' : '';
}
/**
* Returns HTML for the breadcrumb line of the print template.
*
* @param array $vars
* An associative array containing:
* - $node: the node object
*
* @return string
* HTML text with the breadcrumb
*
* @ingroup themeable
João Ventura
committed
*/
function theme_print_breadcrumb($vars) {
$node = $vars['node'];
João Ventura
committed
$old_path = $_GET['q'];
João Ventura
committed
$path = empty($node->nid) ? $node->path : "node/$node->nid";
menu_set_active_item($path);
$breadcrumb = drupal_get_breadcrumb();
if (!empty($breadcrumb)) {
$breadcrumb[] = menu_get_active_title();
João Ventura
committed
menu_set_active_item($old_path);
João Ventura
committed
return filter_xss(implode(' > ', $breadcrumb));
}
else {
João Ventura
committed
menu_set_active_item($old_path);
João Ventura
committed
return '';
}
}
/**
* Returns HTML for the footer of the print template.
*
* @param array $vars
* An empty associative array
*
* @return string
* HTML text with the footer
*
* @ingroup themeable
João Ventura
committed
*/
function theme_print_footer($vars) {
$footer = '';
switch (variable_get('print_footer_options', PRINT_FOOTER_OPTIONS_DEFAULT)) {
case 1: // theme's
$footer_blocks = block_get_blocks_by_region('footer');
$footer = variable_get('site_footer', FALSE) . "\n" . drupal_render($footer_blocks);
break;
case 2: // user-specifed
$footer = variable_get('print_footer_user', PRINT_FOOTER_USER_DEFAULT);
break;
}
// Delete the contextual links
$footer = preg_replace('!\s*<div class="contextual-links-wrapper">.*?</div>!sim', '', $footer);
João Ventura
committed
return filter_xss_admin($footer);
}
/**
* Returns HTML for the source URL line of the print template.
*
* @param array $vars
* An associative array containing:
* - $url: the URL to the full node view.
* - $node: the node object.
* - $cid; comment ID of the comment to display.
*
* @return string
* HTML text with the footer
*
* @ingroup themeable
João Ventura
committed
*/
function theme_print_sourceurl($vars) {
$sourceurl_date = variable_get('print_sourceurl_date', PRINT_SOURCEURL_DATE_DEFAULT);
$url = is_int($vars['cid']) ? $vars['url'] . '#comment-' . $vars['cid'] : $vars['url'];
$output = '<strong>' . t('Source URL');
if ($sourceurl_date && isset($vars['node'])) {
$output .= ' (';
$date = format_date($vars['node']->changed, 'short');
$output .= empty($vars['node']->nid) ? t('retrieved on !date', array('!date' => $date)) : t('modified on !date', array('!date' => $date));
$output .= ')';
}
$output .= ':</strong> ' . $url;
return $output;
}
/**
* Returns HTML for the URL list of the print template.
*
* @param array $vars
* An empty associative array
*
* @return string
* HTML text with the URL list
*
* @ingroup themeable
João Ventura
committed
*/
function theme_print_url_list($vars) {
global $_print_urls;
// Display the collected links at the bottom of the page. Code once taken from Kjartan Mannes' project.module
if (!empty($_print_urls)) {
$urls = _print_friendly_urls();
$max = count($urls);
$url_list = '';
foreach ($urls as $key => $url) {
João Ventura
committed
drupal_alter('print_url_list', $url);
João Ventura
committed
$url_list .= '[' . ($key + 1) . '] ' . check_plain($url) . "<br />\n";
}
if (!empty($url_list)) {
return "<p><strong>" . t('Links') . "</strong><br />$url_list</p>";
}
else {
return '';
}
}
* Generates a robots meta tag to tell them what they may index
*/
function _print_robots_meta_generator() {
$robots_meta = array();
João Ventura
committed
if (variable_get('print_robots_noindex', PRINT_ROBOTS_NOINDEX_DEFAULT)) {
João Ventura
committed
if (variable_get('print_robots_nofollow', PRINT_ROBOTS_NOFOLLOW_DEFAULT)) {
João Ventura
committed
if (variable_get('print_robots_noarchive', PRINT_ROBOTS_NOARCHIVE_DEFAULT)) {
João Ventura
committed
if (count($robots_meta) > 0) {
João Ventura
committed
return '<meta name="robots" content=' . implode(', ', $robots_meta) . ' />';
João Ventura
committed
return '';
João Ventura
committed
* Generates the CSS directive to include in the printer-friendly version
*
João Ventura
committed
* If TRUE, the provided CSS will be expanded, instead of given as a list
* of includes.
function _print_css_generator($expand = FALSE) {
$print_css = variable_get('print_css', PRINT_CSS_DEFAULT);
if (!empty($print_css)) {
drupal_add_css(strtr($print_css, array('%t' => drupal_get_path('theme', variable_get('theme_default')))));
drupal_add_css(drupal_get_path('module', 'print') . '/css/print.css');
João Ventura
committed
if (!variable_get('print_keep_theme_css', PRINT_KEEP_THEME_CSS_DEFAULT)) {
João Ventura
committed
foreach ($drupal_css as $key => $css_file) {
if ($css_file['group'] == CSS_THEME) {
// Unset the theme's CSS
unset($drupal_css[$key]);
}
João Ventura
committed
}
João Ventura
committed
// Expand the CSS if requested
if ($expand) {
$style = '';
$css_files = array_keys($drupal_css);
João Ventura
committed
if (file_exists($filename)) {
$style .= file_get_contents($filename, TRUE);
João Ventura
committed
}
João Ventura
committed
return "<style type='text/css' media='all'>$style</style>\n";
João Ventura
committed
return drupal_get_css($drupal_css);
* Callback function for the preg_replace_callback for URL-capable patterns
* Manipulate URLs to make them absolute in the URLs list, and add a [n]
* footnote marker.
* array with the matched tag patterns, usually <a...>+text+</a>
*
* @return string
* tag with re-written URL and, if applicable, the [n] index to the URL list
global $base_url, $base_root, $_print_urls;
$include_anchors = variable_get('print_urls_anchors', PRINT_URLS_ANCHORS_DEFAULT);
// first, split the html into the different tag attributes
$pattern = '!\s*(\w+\s*=\s*"(?:\\\"|[^"])*")\s*|\s*(\w+\s*=\s*\'(?:\\\\\'|[^\'])*\')\s*|\s*(\w+\s*=\s*\w+)\s*|\s+!';
$attribs = preg_split($pattern, $matches[1], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
foreach ($attribs as $key => $value) {
$attribs[$key] = preg_replace('!(\w)\s*=\s*(.*)!', '$1=$2', $value);
$size = count($attribs);
for ($i=1; $i < $size; $i++) {
// If the attribute is href or src, we may need to rewrite the URL in the value
if (preg_match('!^(?:href|src)\s*?=(.*)!i', $attribs[$i], $urls) > 0) {
$url = trim($urls[1], " \t\n\r\0\x0B\"'");
if (empty($url)) {
// If URL is empty, use current_url
$path = explode('/', $_GET['q']);
unset($path[0]);
$path = implode('/', $path);
if (ctype_digit($path)) {
$path = "node/$path";
}
// Printer-friendly URLs is on, so we need to make it absolute
$newurl = url($path, array('fragment' => drupal_substr($url, 1), 'absolute' => TRUE));
}
elseif (strpos(html_entity_decode($url), '://') || preg_match('!^mailto:.*?@.*?\..*?$!iu', html_entity_decode($url))) {
João Ventura
committed
$newurl = $url;
João Ventura
committed
elseif (strpos(html_entity_decode($url), '//') === 0) {
// URL is 'almost absolute', but it does not contain protocol; replace with base_path protocol
$newurl = (empty($_SERVER['HTTPS']) ? 'http' : 'https') . ":" . $url;
$matches[1] = str_replace($url, $newurl, $matches[1]);
}
if ($url[0] == '#') {
if ($include_anchors && (!empty($_print_urls))) {
$path = explode('/', $_GET['q']);
unset($path[0]);
$path = implode('/', $path);
if (ctype_digit($path)) {
$path = "node/$path";
}
// Printer-friendly URLs is on, so we need to make it absolute
$newurl = url($path, array('fragment' => drupal_substr($url, 1), 'absolute' => TRUE));
}
// Because base href is the original page, change the link to
// still be usable inside the print page
$matches[1] = str_replace($url, base_path() . $_GET['q'] . $url, $matches[1]);
}
else {
// URL is relative, convert it into absolute URL
if ($url[0] == '/') {
// If it starts with '/' just append it to the server name
João Ventura
committed
$newurl = $base_root . '/' . trim($url, '/');
elseif (preg_match('!^(?:index.php)?\?q=!i', $url)) {
// If it starts with ?q=, just prepend with the base URL
João Ventura
committed
$newurl = $base_url . '/' . trim($url, '/');
João Ventura
committed
$newurl = url(trim($url, '/'), array('absolute' => TRUE));
$matches[1] = str_replace($url, $newurl, $matches[1]);
if (count($matches) == 4) {
$ret .= $matches[2] . $matches[3];
if ((!empty($_print_urls)) && (isset($newurl))) {
João Ventura
committed
$ret .= ' <span class="print-footnote">[' . _print_friendly_urls(trim($newurl)) . ']</span>';
}
}
return $ret;
}
/**
* Auxiliary function to store the Printer-friendly URLs list as static.
*
* list of URLs previously stored if $url is 0, or the current count
*/
function _print_friendly_urls($url = 0) {
static $urls = array();
$url_idx = array_search($url, $urls);
if ($url_idx !== FALSE) {
return ($url_idx + 1);
}
else {
$urls[] = $url;
return count($urls);
}
}
/**
* Check URL list settings for this node
*
* TRUE if URL list should be displayed, FALSE otherwise
*/
$node_urllist = variable_get('print_' . $format . '_display_sys_urllist', PRINT_TYPE_SYS_URLLIST_DEFAULT);
else {
$node_urllist = isset($node->{'print_' . $format . '_display_urllist'}) ? $node->{'print_' . $format . '_display_urllist'} : variable_get('print_' . $format . '_display_urllist_' . $node->type, PRINT_TYPE_URLLIST_DEFAULT);
}
// Get value of Printer-friendly URLs setting
João Ventura
committed
return (variable_get('print_urls', PRINT_URLS_DEFAULT) && ($node_urllist));
* Prepare a Printer-friendly-ready node body for content nodes
* node ID of the node to be rendered into a printer-friendly page
* @param int $vid
* (optional) revision ID of the node to use
* (optional) comment ID of the individual comment to be rendered
João Ventura
committed
* @param string $view_mode
* (optional) view mode to be used when rendering the content
*
* @return object
* filled node-like object to be used in the print template
João Ventura
committed
function _print_generate_node($nid, $format, $vid = NULL, $cid = NULL, $view_mode = PRINT_VIEW_MODE) {
if (!isset($langcode)) {
$langcode = $GLOBALS['language_content']->language;
}
$node = node_load($nid, $vid);
João Ventura
committed
if (!$node) {
// Node not found
drupal_not_found();
João Ventura
committed
}
elseif (!node_access('view', $node)) {
João Ventura
committed
drupal_access_denied();
drupal_set_title($node->title);
João Ventura
committed
// Adapted (simplified) version of node_view
node_build_content($node, $view_mode);
// Disable the links area
unset($node->content['links']);
$build = $node->content;
unset($node->content);
João Ventura
committed
if (function_exists('comment_node_page_additions') &&
(($cid != NULL) || (variable_get('print_comments', PRINT_COMMENTS_DEFAULT)))) {
// Print only the requested comment (or if $cid is NULL, all of them)
$comments = comment_node_page_additions($node);
if (!empty($comments)) {
unset($comments['comment_form']);
foreach ($comments['comments'] as $key => &$comment) {
if (is_numeric($key)) {
if (($cid != NULL) && ($key != $cid)) {
unset($comments['comments'][$key]);
}
else {
unset($comment['links']);
}
}
}
$build['comments'] = $comments;
}
$build += array(
'#node' => $node,
'#view_mode' => $view_mode,
'#print_format' => $format,
);
João Ventura
committed
João Ventura
committed
$type = 'node';
drupal_alter(array('node_view', 'entity_view'), $build, $type);
$content = render($build);
$parts = explode('<div class="content', $content, 2);
if (count($parts) == 2) {
$pattern = '!(.*?)<a [^>]*?>(.*?)</a>(.*?)!mis';
$parts[0] = preg_replace($pattern, '$1$2$3', $parts[0]);
$content = implode('<div class="content', $parts);
}
$_print_urls = _print_url_list_enabled($node, $format);
$pattern = '!<(a\s[^>]*?)>(.*?)(</a>)!is';
$content = preg_replace_callback($pattern, '_print_rewrite_urls', $content);
João Ventura
committed
$node->content = $content;
* Prepare a Printer-friendly-ready node body for non-content pages
* path of the node to be rendered into a printer-friendly page
*
* @return object
* filled node-like object to be used in the print template
// Handle node tabs
$parts = explode('/', $path);
if (ctype_digit($parts[0]) && (count($parts) > 1)) {
$path = 'node/' . $path;
}
$path = drupal_get_normal_path($path);
menu_set_active_item($path);
// Adapted from index.php.
$node = new stdClass();
João Ventura
committed
$node->content = menu_execute_active_handler($path, FALSE);
if (is_array($node->content)) {
$node->content = drupal_render($node->content);
João Ventura
committed
if (is_int($node->content)) {
switch ($node->content) {
case MENU_NOT_FOUND:
João Ventura
committed
drupal_not_found();
break;
case MENU_ACCESS_DENIED:
João Ventura
committed
drupal_access_denied();
break;
}
João Ventura
committed
$node->title = drupal_get_title();
$node->path = $path;
João Ventura
committed
$node->changed = REQUEST_TIME;
João Ventura
committed
João Ventura
committed
$node->content = preg_replace('!\s*<div class="links">.*?</div>!sim', '', $node->content);
// Delete the contextual links also
$node->content = preg_replace('!\s*<div class="contextual-links-wrapper">.*?</div>!sim', '', $node->content);
$_print_urls = _print_url_list_enabled($node, $format);
$pattern = '!<(a\s[^>]*?)>(.*?)(</a>)!is';
João Ventura
committed
$node->content = preg_replace_callback($pattern, '_print_rewrite_urls', $node->content);
* Prepare a Printer-friendly-ready node body for book pages
* node ID of the node to be rendered into a printer-friendly page
*
* @return object
* filled node-like object to be used in the print template
João Ventura
committed
if (!$node) {
// Node not found
drupal_not_found();
João Ventura
committed
}
elseif (!node_access('view', $node) || (!user_access('access printer-friendly version'))) {
João Ventura
committed
drupal_access_denied();
João Ventura
committed
$node->content = book_export_traverse($tree, 'book_node_export');
$_print_urls = _print_url_list_enabled($node, $format);
$pattern = '!<(a\s[^>]*?)>(.*?)(</a>)!is';
João Ventura
committed
$node->content = preg_replace_callback($pattern, '_print_rewrite_urls', $node->content);