Fix #257900: Add capability to use different templates for PDF, mail, etc.
[project/print.git] / print.pages.inc
CommitLineData
b09f08f7 1<?php
1e156b5a 2// $Id$
b09f08f7 3
1e156b5a
JV
4/**
5 * @file
6 * Contains the functions to generate Printer-friendly pages.
7 *
8 * This file is included by the core PF module, and includes all the
9 * functions necessary to generate a PF version of the original page
10 * in HTML format.
11 */
b09f08f7 12
1e156b5a
JV
13/**
14 * Generate an HTML version of the printer-friendly page
15 *
16 * @see print_controller()
17 * @see _print_get_template()
18 */
b09f08f7 19function print_controller_html() {
0706a465
JV
20 // Remove the print/ prefix
21 $path = substr($_GET['q'], strpos($_GET['q'], '/')+1);
22 $cid = isset($_GET['comment']) ? $_GET['comment'] : NULL;
23
24 $print = print_controller($path, $cid);
0d7894a0 25 include_once(_print_get_template("html", $print["type"]));
b09f08f7
JV
26}
27
28/**
0706a465 29 * Select the print generator function based on the page type
1e156b5a
JV
30 *
31 * Depending on the type of node, this functions chooses the appropriate
32 * generator function.
33 *
0706a465
JV
34 * @param $path
35 * path of the original page
36 * @param $cid
37 * comment ID of the individual comment to be rendered
38 * @param $teaser
39 * if set to TRUE, outputs only the node's teaser
1e156b5a
JV
40 * @return
41 * array with the fields to be used in the template
42 * @see _print_generate_node()
43 * @see _print_generate_path()
44 * @see _print_generate_book()
b09f08f7 45 */
27dc0ea1 46function print_controller($path, $cid, $teaser = FALSE, $message = NULL) {
0706a465 47 if (!is_numeric($path)) {
b09f08f7
JV
48 // Indirect call with print/alias
49 // If there is a path alias with these arguments, generate a printer-friendly version for it
0706a465 50 $path = drupal_get_normal_path($path);
b09f08f7
JV
51 $ret = preg_match("/^node\/(.*)/i", $path, $matches);
52 if ($ret == 1) {
0706a465 53 $path = $matches[1];
b09f08f7
JV
54 }
55 }
0706a465 56 if (is_numeric($path)) {
27dc0ea1 57 $print = _print_generate_node($path, $cid, $teaser, $message);
b09f08f7
JV
58 }
59 else {
0706a465 60 $ret = preg_match("/^book\/export\/html\/(.*)/i", $path, $matches);
b09f08f7
JV
61 if ($ret == 1) {
62 // This is a book PF page link, handle trough the book handling functions
27dc0ea1 63 $print = _print_generate_book($matches[1], $teaser, $message);
b09f08f7
JV
64 }
65 else {
66 // If no content node was found, handle the page printing with the 'printable' engine
27dc0ea1 67 $print = _print_generate_path($path, $teaser, $message);
b09f08f7
JV
68 }
69 }
70
71 return $print;
72}
73
b09f08f7 74/**
1e156b5a 75 * Generates a robots meta tag to tell them what they may index
b09f08f7 76 *
1e156b5a
JV
77 * @return
78 * string with the meta robots tag
b09f08f7
JV
79 */
80function _print_robots_meta_generator() {
81 $robots_settings = variable_get('print_robot_settings', print_robot_settings_default());
82 $robots_meta = array();
83
84 if (!empty($robots_settings['noindex'])) {
85 $robots_meta[] = 'noindex';
86 }
87 if (!empty($robots_settings['nofollow'])) {
88 $robots_meta[] = 'nofollow';
89 }
90 if (!empty($robots_settings['noarchive'])) {
91 $robots_meta[] = 'noarchive';
92 }
93 if (!empty($robots_settings['nocache'])) {
94 $robots_meta[] = 'nocache';
95 }
96
7e748d74
JV
97 if (count($robots_meta) > 0) {
98 $robots_meta = implode(', ', $robots_meta);
99 $robots_meta = "<meta name=\"robots\" content=\"". $robots_meta ."\" />\n";
b09f08f7
JV
100 }
101 else {
102 $robots_meta = '';
103 }
104
105 return $robots_meta;
106}
107
108/**
1e156b5a 109 * Post-processor that fills the array for the template with common details
b09f08f7 110 *
1e156b5a
JV
111 * @param $node
112 * generated node with a printer-friendly node body
113 * @param $cid
114 * id of current comment being generated (NULL when not generating
115 * an individual comment)
116 * @return
117 * array with the fields to be used in the template
b09f08f7 118 */
27dc0ea1 119function _print_var_generator($node, $message = NULL, $cid = NULL) {
b09f08f7
JV
120 global $base_url, $language;
121
122 $path = empty($node->nid) ? $node->path : "node/$node->nid";
123
124 $themed = theme('print_text');
125
126 // print module settings
127 $print_settings = variable_get('print_settings', print_settings_default());
128 $print_html_settings = variable_get('print_html_settings', print_html_settings_default());
129 $print_sourceurl_settings = variable_get('print_sourceurl_settings', print_sourceurl_settings_default());
130
131 $print["language"] = $language->language;
132 $print["title"] = $node->title;
b09f08f7
JV
133 $print["head"] = drupal_get_html_head();
134 $print["scripts"] = drupal_get_js();
dad7232c 135 $print["robots_meta"] = _print_robots_meta_generator();
eaac64a9
JV
136 $print["url"] = url($path, array('absolute' => TRUE));
137 $print["base_href"] = "<base href=\"". $print["url"] ."\" />\n";
27dc0ea1 138 $print["favicon"] = theme_get_setting("toggle_favicon") ? "<link rel=\"shortcut icon\" href=\"". theme_get_setting("favicon") ."\" type=\"image/x-icon\" />\n" : "";
b09f08f7 139
0d7894a0 140 $css_files = array();
b09f08f7 141 if (!empty($print_settings['css'])) {
0d7894a0 142 $css_files[] = strip_tags($print_settings['css']);
b09f08f7
JV
143 }
144 else {
0d7894a0 145 $css_files[] = drupal_get_path('module', 'print') ."/print.css";
b09f08f7 146 }
5d9fa914
JV
147 // Add CCK's CSS file to properly display the fields
148 if (module_exists('content')) {
0d7894a0 149 $css_files[] = drupal_get_path('module', 'content') ."/content.css";
27dc0ea1
JV
150 }
151
152 // If we are sending a message via e-mail, the CSS must be embedded
153 if (!empty($message)) {
154 ob_start();
0d7894a0
JV
155 foreach ($css_files as $value) {
156 include($value);
27dc0ea1
JV
157 }
158 $style = ob_get_contents();
159 ob_end_clean();
160 $print["css"] = "<style type=\"text/css\" media=\"all\">". $style ."</style>\n";
161 }
162 else {
0d7894a0
JV
163 $print["css"] = "";
164 foreach ($css_files as $value) {
165 $print["css"] .= "<link type=\"text/css\" rel=\"stylesheet\" media=\"all\" href=\"". base_path() . $value ."\" />\n";
27dc0ea1 166 }
5d9fa914 167 }
b09f08f7
JV
168
169 $print["sendtoprinter"] = $print_html_settings['sendtoprinter'] ? " onload=\"window.print();\"" : "";
170
f6972c51 171 $logo_url = !empty($print_settings['logo_url']) ? strip_tags($print_settings['logo_url']) : theme_get_setting('logo');
1424b51c
JV
172 $print["logo"] = $logo_url ? "<img class=\"print-logo\" src=\"". $logo_url ."\" alt=\"\" />\n" : "";
173
174 $published_site = variable_get('site_name', 0);
eaac64a9 175 if ($published_site) {
1424b51c
JV
176 $published = (empty($themed["published"])) ? t('Published on %site_name', array('%site_name' => $published_site)) : ($themed['published'] ." ". $published_site);
177 $print["site_name"] = $published ." (". l($base_url, $base_url) .")";
178 }
179 else {
180 $print["site_name"] = "";
181 }
b09f08f7 182
b09f08f7 183 if ($print_sourceurl_settings['enabled'] == 1) {
dad7232c
JV
184 /* Grab and format the src URL */
185 if (empty($print_sourceurl_settings['forcenode'])) {
eaac64a9 186 $url = $print["url"];
dad7232c
JV
187 }
188 else {
eaac64a9 189 $url = $base_url .'/'. (((bool)variable_get('clean_url', '0')) ? '' : '?q=') . $path;
dad7232c
JV
190 }
191 if ($cid) {
eaac64a9 192 $url .= "#comment-$cid";
dad7232c 193 }
1424b51c
JV
194 $retrieved_date = format_date(time(), 'small');
195 $retrieved = (empty($themed["retrieved"])) ? t('retrieved on %date', array('%date' => $retrieved_date)) : ($themed["retrieved"] ." ". $retrieved_date);
dad7232c 196 $print["printdate"] = $print_sourceurl_settings['date'] ? " ($retrieved)" : "";
1424b51c
JV
197
198 $source_url = (empty($themed["sourceURL"])) ? t('Source URL') : $themed["sourceURL"];
eaac64a9 199 $print["source_url"] = "<strong>". $source_url . $print["printdate"] .":</strong> ". l($url, $url);
b09f08f7
JV
200 }
201 else {
202 $print["source_url"] = "";
203 }
204
7e748d74
JV
205 if (isset($node->type)) {
206 $node_type = $node->type;
1424b51c 207
eaac64a9
JV
208 if (theme_get_setting("toggle_node_info_$node_type")) {
209 $by_author = ($node->name ? $node->name : variable_get('anonymous', t('Anonymous')));
210 $by = (empty($themed["by"])) ? t('By %author', array('%author' => $by_author)) : ($themed["by"] ." ". $by_author);
211 $print["submitted"] = $by;
1424b51c 212
eaac64a9
JV
213 $created_datetime = format_date($node->created, 'small');
214 $created = (empty($themed["created"])) ? t('Created %date', array('%date' => $created_datetime)) : ($themed["created"] ." ". $created_datetime);
215 $print["created"] = $created;
216 }
217 else {
218 $print["submitted"] = "";
219 $print["created"] = "";
220 }
1424b51c 221
7e748d74
JV
222 $print["type"] = $node->type;
223 }
224 else {
225 $print["submitted"] = "";
226 $print["created"] = "";
227 $print["type"] = '';
228 }
ca01bdd2 229
b09f08f7
JV
230 menu_set_active_item($path);
231 $breadcrumb = drupal_get_breadcrumb();
232 if (!empty($breadcrumb)) {
233 $breadcrumb[] = menu_get_active_title();
234 $print["breadcrumb"] = implode(" > ", $breadcrumb);
235 }
236 else {
237 $print["breadcrumb"] = "";
238 }
239
240 // Display the collected links at the bottom of the page. Code once taken from Kjartan Mannes' project.module
241 if (!empty($print_settings['urls'])) {
242 $urls = _print_friendly_urls();
243 $max = count($urls);
1424b51c 244 $pfp_links = '';
b09f08f7 245 if ($max) {
b09f08f7 246 for ($i = 0; $i < $max; $i++) {
1424b51c 247 $pfp_links .= '['. ($i + 1) .'] '. $urls[$i] ."<br />\n";
b09f08f7 248 }
1424b51c
JV
249 $links = (empty($themed["links"])) ? t('Links') : $themed["links"];
250 $print["pfp_links"] = "<p><strong>". $links .":</strong><br />". $pfp_links ."</p>";
b09f08f7
JV
251 }
252 }
253
b09f08f7
JV
254 if (module_exists('taxonomy')) {
255 $terms = taxonomy_link('taxonomy terms', $node);
256 $print["taxonomy"] = theme('links', $terms);
257 }
258
b09f08f7 259 $print["content"] = $node->body;
eaac64a9 260 $print["node"] = $node;
27dc0ea1 261 $print["message"] = $message;
dad7232c 262 $print["footer_message"] = filter_xss_admin(variable_get('site_footer', FALSE)) ."\n". theme('blocks', 'footer') ;
b09f08f7
JV
263
264 return $print;
265}
266
267/**
1e156b5a 268 * Callback function for the preg_replace_callback for URL-capable patterns
b09f08f7 269 *
1e156b5a
JV
270 * Manipulate URLs to make them absolute in the URLs list, and to add a
271 * [n] footnote marker.
272 *
273 * @param $matches
274 * array with the matched tag patterns, usually <a...>+text+</a>
275 * @return
276 * tag with re-written URL and when appropriate the [n] index to the
277 * URL list
b09f08f7
JV
278 */
279function _print_rewrite_urls($matches) {
280 global $base_url, $base_root;
281
282 // Get value of Printer-friendly URLs setting
283 $print_settings = variable_get('print_settings', print_settings_default());
284 $pfurls = (!empty($print_settings['urls']));
285
286 //Temporarily convert spaces to %20 so that it isn't split below
7e748d74 287 $in_string = FALSE;
b09f08f7
JV
288 for ($i=0; $i < strlen($matches[1]); $i++) {
289 if ($matches[1][$i] == '"') {
290 $in_string = !$in_string;
291 }
292 if (($matches[1][$i] == ' ') && ($in_string)) {
293 $matches[1]=substr_replace($matches[1], "%20", $i, 1);
294 }
295 }
296 // remove whitespace immediately before and after the '=' sign
297 $matches[1]=preg_replace("/\s*=\s*/", "=", $matches[1]);
298
299 // first, split the html into the different tag attributes
300 $attribs = preg_split("/\s+/m", $matches[1]);
301
302 for ($i=1; $i < count($attribs); $i++) {
303 // If the attribute is href or src, we may need to rewrite the URL in the value
304 if (preg_match("/^(href|src)\s*?=/i", $attribs[$i]) > 0) {
305 // We may need to rewrite the URL, so let's isolate it
306 preg_match("/.*?=(.*)/is", $attribs[$i], $urls);
307 $url = trim($urls[1], " \t\n\r\0\x0B\"\'");
308
309 if (strpos($url, '://') || preg_match("/^mailto:.*?@.*?\..*?$/iu", $url)) {
310 // URL is absolute, do nothing
311 $newurl = urldecode($url);
312 }
313 else {
314 if (substr($url, 0, 1) == "#") {
315 // URL is an anchor tag
316 if ($pfurls) {
611da12d 317 $path = substr($_GET['q'], strpos($_GET['q'], '/')+1);
b09f08f7
JV
318 if (is_numeric($path)) {
319 $path = "node/$path";
320 }
321 // Printer-friendly URLs is on, so we need to make it absolute
322 $newurl = url($path, array('fragment' => substr(urldecode($url), 1), 'absolute' => TRUE));
323 }
324 // Because base href is the original page, change the link to
325 // still be usable inside the print page
326 $matches[1] = str_replace($url, $_GET['q'] . $url, $matches[1]);
327 }
328 else {
329 // URL is relative, convert it into absolute URL
330 $clean_url = (bool)variable_get('clean_url', '0');
331 if (substr($url, 0, 1) == "/") {
332 // If it starts with '/' just append it to the server name
333 $newurl = $base_root .'/'. trim(urldecode($url), "/");
334 }
335 elseif ((!$clean_url) && (preg_match("/^[index.php]?\?q=.*/i", $url))) {
336 // If Clean URLs is disabled, and it starts with q=?, just prepend with the base URL
337 $newurl = $base_url .'/'. trim(urldecode($url), "/");
338 }
339 else {
340 $newurl = url(trim(urldecode($url), "/"), array('absolute' => TRUE));
341 }
342 $matches[1] = str_replace($url, $newurl, $matches[1]);
343 }
344 }
345 }
346 }
347
348 //Revert all %20 in strings back to spaces
349 $matches[1] = str_replace("%20", " ", $matches[1]);
350
351 $ret = '<'. $matches[1] .'>';
352 if (count($matches) == 4) {
353 $ret .= $matches[2] . $matches[3];
ca01bdd2 354 if (($pfurls) && (isset($newurl))) {
b09f08f7
JV
355 $ret .= ' <span class="print-footnote">['. _print_friendly_urls(trim(stripslashes($newurl))) .']</span>';
356 }
357 }
358
359 return $ret;
360}
361
362/**
363 * Auxiliary function to store the Printer-friendly URLs list as static.
364 *
1e156b5a
JV
365 * @param $url
366 * absolute URL to be inserted in the list
367 * @return
368 * list of URLs previously stored if $url is 0, or the current count
369 * otherwise.
b09f08f7
JV
370 */
371function _print_friendly_urls($url = 0) {
372 static $urls = array();
373 if ($url) {
374 $url_idx = array_search($url, $urls);
375 if ($url_idx !== FALSE) {
376 return ($url_idx + 1);
377 }
378 else {
379 $urls[] = $url;
380 return count($urls);
381 }
382 }
383 return $urls;
384}
385
386/**
1e156b5a
JV
387 * Choose most appropriate template
388 *
b09f08f7
JV
389 * Auxiliary function to resolve the most appropriate template trying to find
390 * a content specific template in the theme or module dir before falling back
391 * on a generic template also in those dirs.
392 *
0d7894a0
JV
393 * @param format
394 * format of the PF page being rendered (html, pdf, etc.)
1e156b5a
JV
395 * @param $type
396 * name of the node type being rendered in a PF page
397 * @return
398 * filename of the most suitable template
b09f08f7 399 */
0d7894a0
JV
400function _print_get_template($format = NULL, $type = NULL) {
401 $filenames = array();
402 // First try to find a template defined both for the format and then the type
403 if (!empty($format) && !empty($type)) {
404 $filenames[] = "print_$format.node-$type.tpl.php";
405 }
406 // Then only for the format
407 if (!empty($format)) {
408 $filenames[] = "print_$format.tpl.php";
409 }
410 // If the node type is known, then try to find that type's template file
411 if (!empty($type)) {
412 $filenames[] = "print.node-$type.tpl.php";
413 }
414 // Finally search for a generic template file
415 $filenames[] = "print.tpl.php";
416
417 foreach ($filenames as $value) {
b09f08f7 418 // First in the theme directory
0d7894a0
JV
419 $file = drupal_get_path('theme', $GLOBALS['theme_key']) ."/". $value;
420 var_dump($file);
421 if (file_exists($file)) {
422 return $file;
b09f08f7
JV
423 }
424 // Then in the module directory
0d7894a0
JV
425 $file = drupal_get_path('module', 'print') ."/". $value;
426 var_dump($file);
427 if (file_exists($file)) {
428 return $file;
b09f08f7
JV
429 }
430 }
b09f08f7
JV
431}
432
b09f08f7 433/**
1e156b5a
JV
434 * Prepare a Printer-friendly-ready node body for content nodes
435 *
436 * @param $nid
437 * node ID of the node to be rendered into a printer-friendly page
438 * @param $cid
439 * comment ID of the individual comment to be rendered
0706a465
JV
440 * @param $teaser
441 * if set to TRUE, outputs only the node's teaser
1e156b5a
JV
442 * @return
443 * filled array ready to be used in the template
b09f08f7 444 */
27dc0ea1 445function _print_generate_node($nid, $cid = NULL, $teaser = FALSE, $message = NULL) {
b09f08f7
JV
446 // We can take a node id
447 $node = node_load(array('nid' => $nid));
448 if (!node_access('view', $node)) {
449 // Access is denied
450 return drupal_access_denied();
451 }
b0a02151 452 drupal_set_title($node->title);
b09f08f7
JV
453
454 //alert other modules that we are generating a printer-friendly page, so they can choose to show/hide info
7e748d74 455 $node->printing = TRUE;
b09f08f7
JV
456 // Turn off Pagination by the Paging module
457 unset($node->pages);
458 unset($node->page_count);
459
0706a465
JV
460 if ($teaser) {
461 unset($node->body);
462 }
463 else {
464 unset($node->teaser);
465 }
b09f08f7
JV
466 $node = (object)$node;
467 if ($cid === NULL) {
468 // Adapted (simplified) version of node_view for Drupal 5.x
469 //Render the node content
0706a465 470 $node = node_build_content($node, $teaser, TRUE);
b09f08f7
JV
471 // Disable fivestar widget output
472 unset($node->content["fivestar_widget"]);
473 // Disable service links module output
474 unset($node->content["service_links"]);
475
476 $node->body = drupal_render($node->content);
477 }
478
479 $print_settings = variable_get('print_settings', print_settings_default());
480
e458482f 481 if (function_exists('comment_render') && (($cid != NULL) || ($print_settings['comments']))) {
b09f08f7
JV
482 //Print only the requested comment (or if $cid is NULL, all of them)
483 $comments = comment_render($node, $cid);
484
485 //Remove the comment forms
486 $comments = preg_replace("/<form.*?id=\"comment-.*?\">.*?<\/form>/sim", "", $comments);
487 //Remove the 'Post new comment' title
488 $comments = preg_replace("/<h2.*?>Post new comment<\/h2>/", "", $comments);
489 //Remove the comment title hyperlink
490 $comments = preg_replace("/(<h3.*?>)(<a.*?>)(.*?)<\/a>(<\/h3>)/", "$1$3$4", $comments);
e458482f 491 //Remove the comment author link
c42f715c
JV
492 $pattern = "/(<span class=\"submitted\">)(.*?)<a.*?>(.*?)<\/a>(<\/span>)/sim";
493 if (preg_match($pattern, $comments)) {
494 $comments = preg_replace($pattern , "$1$2$3$4", $comments);
495 }
b09f08f7 496 //Remove the comment links
ca01bdd2 497 $comments = preg_replace("/\s*<ul class=\"links\">.*?<\/ul>/sim", "", $comments);
b09f08f7
JV
498 if ($cid != NULL) {
499 // Single comment requested, output only the comment
500 unset($node->body);
501 }
502 $node->body .= $comments;
503 }
504
7e748d74 505 node_invoke_nodeapi($node, 'alter', FALSE, TRUE);
b09f08f7
JV
506
507 // Convert the a href elements
508 $pattern = "@<(a\s[^>]*?)>(.*?)(</a>)@is";
509 $node->body = preg_replace_callback($pattern, "_print_rewrite_urls", $node->body);
510
511 init_theme();
512
27dc0ea1 513 $print = _print_var_generator($node, $message, $cid);
b09f08f7
JV
514
515 return $print;
516}
517
518/**
1e156b5a
JV
519 * Prepare a Printer-friendly-ready node body for non-content pages
520 *
521 * @param $path
522 * path of the node to be rendered into a printer-friendly page
523 * @return
524 * filled array ready to be used in the template
b09f08f7 525 */
27dc0ea1 526function _print_generate_path($path, $teaser = FALSE, $message = NULL) {
b09f08f7
JV
527 $path = drupal_get_normal_path($path);
528
529 menu_set_active_item($path);
530 // Adapted from index.php.
531 $node = new stdClass();
532 $node->body = menu_execute_active_handler($path);
00590736 533 $node->title = drupal_get_title();
b09f08f7
JV
534 $node->path = $path;
535
536 // It may happen that a drupal_not_found is called in the above call
537 if (preg_match('/404 Not Found/', drupal_get_headers()) == 1) {
538 return;
539 }
540
541 switch ($node->body) {
542 case MENU_NOT_FOUND:
543 return drupal_not_found();
544 break;
545 case MENU_ACCESS_DENIED:
546 return drupal_access_denied();
547 break;
548 }
549
550 // Delete any links area
551 $node->body = preg_replace("/\s*<div class=\"links\">.*?<\/div>/sim", "", $node->body);
552
553 // Convert the a href elements
554 $pattern = "@<(a\s[^>]*?)>(.*?)(</a>)@is";
555 $node->body = preg_replace_callback($pattern, "_print_rewrite_urls", $node->body);
556
557 init_theme();
558
27dc0ea1 559 $print = _print_var_generator($node, $message);
b09f08f7
JV
560
561 return $print;
562}
563
564
565/**
1e156b5a
JV
566 * Prepare a Printer-friendly-ready node body for book pages
567 *
568 * @param $nid
569 * node ID of the node to be rendered into a printer-friendly page
570 * @return
571 * filled array ready to be used in the template
b09f08f7 572 */
27dc0ea1 573function _print_generate_book($nid, $teaser = FALSE, $message = NULL) {
b09f08f7
JV
574 $node = node_load(array('nid' => $nid));
575 if (!node_access('view', $node) || (!user_access('access printer-friendly version'))) {
576 // Access is denied
577 return drupal_access_denied();
578 }
579
580 $tree = book_menu_subtree_data($node->book);
581 $node->body = book_export_traverse($tree, 'book_node_export');
582
583 // Convert the a href elements
584 $pattern = "@<(a\s[^>]*?)>(.*?)(</a>)@is";
585 $node->body = preg_replace_callback($pattern, "_print_rewrite_urls", $node->body);
586
587 init_theme();
588
27dc0ea1 589 $print = _print_var_generator($node, $message);
b09f08f7
JV
590 // The title is already displayed by the book_recurse, so avoid duplication
591 $print["title"] = "";
592
593 return $print;
594}