Support latest hook_perm() implementation
[project/print.git] / print.module
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Displays Printer-friendly versions of Drupal pages.
7 *
8 * This is the core module of the PF package, with the Drupal hooks
9 * and other administrative functions.
10 */
11
12 define('PRINT_PATH', 'print');
13
14 define('PRINT_LOGO_OPTIONS_DEFAULT', 1);
15 define('PRINT_LOGO_URL_DEFAULT', '');
16 define('PRINT_FOOTER_OPTIONS_DEFAULT', 1);
17 define('PRINT_FOOTER_USER_DEFAULT', '');
18 define('PRINT_CSS_DEFAULT', '');
19 define('PRINT_URLS_DEFAULT', 1);
20 define('PRINT_COMMENTS_DEFAULT', 0);
21 define('PRINT_NEWWINDOW_DEFAULT', 1);
22
23 define('PRINT_HTML_LINK_POS_DEFAULT', 'link');
24 define('PRINT_HTML_SHOW_LINK_DEFAULT', 1);
25 define('PRINT_HTML_NODE_LINK_VISIBILITY_DEFAULT', 0);
26 define('PRINT_HTML_NODE_LINK_PAGES_DEFAULT', '');
27 define('PRINT_HTML_LINK_CLASS_DEFAULT', 'print-page');
28 define('PRINT_HTML_SYS_LINK_VISIBILITY_DEFAULT', 1);
29 define('PRINT_HTML_SYS_LINK_PAGES_DEFAULT', '');
30 define('PRINT_HTML_LINK_USE_ALIAS_DEFAULT', 0);
31 define('PRINT_HTML_BOOK_LINK_DEFAULT', 1);
32 define('PRINT_HTML_NEW_WINDOW_DEFAULT', 0);
33 define('PRINT_HTML_SENDTOPRINTER_DEFAULT', 0);
34
35 define('PRINT_SOURCEURL_ENABLED_DEFAULT', 1);
36 define('PRINT_SOURCEURL_DATE_DEFAULT', 0);
37 define('PRINT_SOURCEURL_FORCENODE_DEFAULT', 0);
38
39 define('PRINT_ROBOTS_NOINDEX_DEFAULT', 1);
40 define('PRINT_ROBOTS_NOFOLLOW_DEFAULT', 1);
41 define('PRINT_ROBOTS_NOARCHIVE_DEFAULT', 0);
42
43 define('PRINT_TYPE_SHOW_LINK_DEFAULT', 1);
44 define('PRINT_TYPE_COMMENT_LINK_DEFAULT', 0);
45
46 define('PRINT_ALLOW_NORMAL_LINK', 1);
47 define('PRINT_ALLOW_BOOK_LINK', 2);
48
49 /**
50 * Implementation of hook_perm().
51 */
52 function print_perm() {
53 return array(
54 'access print' => array(
55 'title' => t('Access the printer-friendly page'),
56 'description' => t('View the printer-friendly pages and the links to them in the original pages.'),
57 ),
58 'administer print' => array(
59 'title' => t('Administer the module'),
60 'description' => t('Perform maintenance tasks for the print module.'),
61 ),
62 );
63 }
64
65 /**
66 * Implementation of hook_theme().
67 */
68 function print_theme() {
69 return array(
70 'print_format_link' => array(
71 'arguments' => array(),
72 ),
73 'print_text' => array(
74 'arguments' => array(),
75 ),
76 );
77 }
78
79 /**
80 * Implementation of hook_menu().
81 */
82 function print_menu() {
83 $items = array();
84
85 $items[PRINT_PATH] = array(
86 'title' => 'Printer-friendly',
87 'page callback' => 'print_controller_html',
88 'access arguments' => array('access print'),
89 'type' => MENU_CALLBACK,
90 );
91 $items['admin/settings/print'] = array(
92 'title' => 'Printer-friendly pages',
93 'description' => 'Adds a printer-friendly version link to content and administrative pages.',
94 'page callback' => 'drupal_get_form',
95 'page arguments' => array('print_html_settings'),
96 'access arguments' => array('administer print'),
97 );
98 $items['admin/settings/print/html'] = array(
99 'title' => 'Web page',
100 'weight' => 1,
101 'type' => MENU_DEFAULT_LOCAL_TASK,
102 );
103 $items['admin/settings/print/common'] = array(
104 'title' => 'Settings',
105 'page callback' => 'drupal_get_form',
106 'page arguments' => array('print_main_settings'),
107 'access arguments' => array('administer print'),
108 'weight' => 10,
109 'type' => MENU_LOCAL_TASK,
110 );
111
112 return $items;
113 }
114
115 /**
116 * Implementation of hook_block().
117 */
118 function print_block($op = 'list', $delta = 0, $edit = array()) {
119 switch ($op) {
120 case 'list':
121 $block[$delta]['info'] = t('Printer, e-mail and PDF versions');
122 return $block;
123 break;
124 case 'configure':
125 return '';
126 case 'save':
127 return;
128 case 'view':
129 $nid = preg_replace('!^node/!', '', $_GET['q']);
130 if (is_numeric($nid)) {
131 $node = node_load(array('nid' => $nid));
132 }
133 else {
134 $node = NULL;
135 }
136 $funcs = get_defined_functions();
137 $block['content'] = '';
138 foreach ($funcs['user'] as $func) {
139 if (preg_match('!^print.*?_insert_link$!', $func)) {
140 $link = $func(NULL, $node);
141 if (!empty($link)) {
142 $block['content'] .= $link .'<br />';
143 }
144 }
145 }
146 return $block;
147 break;
148 }
149 }
150
151 /**
152 * Implementation of hook_link().
153 */
154 function print_link($type, $node = NULL, $teaser = FALSE) {
155 $print_html_link_pos = variable_get('print_html_link_pos', array(PRINT_HTML_LINK_POS_DEFAULT => PRINT_HTML_LINK_POS_DEFAULT));
156 $print_html_link_use_alias = variable_get('print_html_link_use_alias', PRINT_HTML_LINK_USE_ALIAS_DEFAULT);
157 $allowed_type = print_link_allowed(array('type' => $type, 'node' => $node, 'teaser' => $teaser));
158 if (($allowed_type === PRINT_ALLOW_NORMAL_LINK) && !empty($print_html_link_pos['link'])) {
159 drupal_add_css(drupal_get_path('module', 'print') .'/css/printlinks.css');
160 $links = array();
161 $format = theme('print_format_link');
162
163 $query_arr = $_GET;
164 if ($type == 'comment') {
165 $query_arr['comment'] = $node->cid;
166 }
167 $query = print_query_string_encode($query_arr, array('q'));
168 if (empty($query)) $query = NULL;
169
170 if ($print_html_link_use_alias) {
171 $path = drupal_get_path_alias('node/'. $node->nid);
172 }
173 else {
174 $path = $node->nid;
175 }
176
177 $links['print_html'] = array(
178 'href' => PRINT_PATH .'/'. $path,
179 'title' => $format['text'],
180 'attributes' => $format['attributes'],
181 'html' => $format['html'],
182 'query' => $query,
183 );
184
185 return $links;
186 }
187 else {
188 return;
189 }
190 }
191
192 /**
193 * Implementation of hook_link_alter().
194 */
195 function print_link_alter(&$links, $node) {
196 foreach ($links as $module => $link) {
197 if (strpos($module, 'book_printer') !== FALSE) {
198 $print_html_book_link = variable_get('print_html_book_link', PRINT_HTML_BOOK_LINK_DEFAULT);
199
200 if ($print_html_book_link) {
201 $format = theme('print_format_link');
202 $format['attributes']['title'] = $link['attributes']['title'];
203
204 $links[$module]['href'] = PRINT_PATH .'/'. $link['href'];
205 $links[$module]['attributes'] = $format['attributes'];
206 }
207 }
208 }
209 }
210
211 /**
212 * Implementation of hook_help().
213 */
214 function print_help($path, $arg) {
215 switch ($path) {
216 case 'admin/help#print':
217 // Return a line-break version of the module README
218 return filter_filter('process', 1, NULL, file_get_contents(drupal_get_path('module', 'print') .'/README.txt') );
219 }
220
221 $print_html_link_pos = variable_get('print_html_link_pos', array(PRINT_HTML_LINK_POS_DEFAULT => PRINT_HTML_LINK_POS_DEFAULT));
222 if ((preg_match('!^node/!i', $path) == 0) &&
223 !(empty($print_html_link_pos['link']) && empty($print_html_link_pos['corner']))) {
224 static $output = FALSE;
225
226 if ($output === FALSE) {
227 $output = TRUE;
228
229 $link = print_insert_link();
230 if ($link) {
231 return "<span class='print-syslink'>$link</span>";
232 }
233 }
234 }
235 }
236
237 /**
238 * Implementation of hook_nodeapi_view().
239 */
240 function print_nodeapi_view(&$node, $teaser, $page) {
241 $print_html_link_pos = variable_get('print_html_link_pos', array(PRINT_HTML_LINK_POS_DEFAULT => PRINT_HTML_LINK_POS_DEFAULT));
242 if (($teaser === FALSE) && !empty($print_html_link_pos['corner']) &&
243 (preg_match('!^print!i', $_GET['q']) == 0)) {
244 $link = print_insert_link(NULL, $node);
245 if ($link) {
246 $node->content['print_link'] = array(
247 '#markup' => "<span class='print-link'>$link</span>",
248 '#weight' => -1,
249 );
250 }
251 }
252 }
253
254 /**
255 * Implementation of hook_form_alter().
256 */
257 function print_form_alter(&$form, $form_state, $form_id) {
258 // Add the node-type settings option to activate the printer-friendly version link
259 if ($form_id == 'node_type_form') {
260 $form['print'] = array(
261 '#type' => 'fieldset',
262 '#title' => t('Printer, e-mail and PDF versions'),
263 '#collapsible' => TRUE,
264 '#collapsed' => TRUE,
265 );
266
267 $form['print']['print_display'] = array(
268 '#type' => 'checkbox',
269 '#title' => t('Show printer-friendly version link'),
270 '#default_value' => variable_get('print_display_'. $form['#node_type']->type, PRINT_TYPE_SHOW_LINK_DEFAULT),
271 '#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' ))),
272 );
273 $form['print']['print_display_comment'] = array(
274 '#type' => 'checkbox',
275 '#title' => t('Show printer-friendly version link in individual comments'),
276 '#default_value' => variable_get('print_display_comment_'. $form['#node_type']->type, PRINT_TYPE_COMMENT_LINK_DEFAULT),
277 '#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' ))),
278 );
279 }
280 }
281
282 /**
283 * Auxiliary function to fill the Printer-friendly link attributes
284 *
285 * @param $title
286 * text to displayed by the link when hovering over it with the mouse
287 * @param $class
288 * class attribute to be used in the link
289 * @param $new_window
290 * if TRUE opens the target page in a new window
291 * @return
292 * array of formatted attributes
293 */
294 function print_fill_attributes($title = '', $class = '', $new_window = FALSE) {
295 $print_newwindow = variable_get('print_newwindow', PRINT_NEWWINDOW_DEFAULT);
296 $print_robots_noindex = variable_get('print_robots_noindex', PRINT_ROBOTS_NOINDEX_DEFAULT);
297
298 $attributes = array();
299 $attributes['title'] = $title;
300 if (!empty($class)) {
301 $attributes['class'] = $class;
302 }
303
304 if ($new_window) {
305 switch ($print_newwindow) {
306 case 0:
307 $attributes['target'] = '_blank';
308 break;
309 case 1:
310 $attributes['onclick'] = 'window.open(this.href); return false';
311 break;
312 }
313 }
314 if (!empty($print_robots_noindex)) {
315 $attributes['rel'] = 'nofollow';
316 }
317 return $attributes;
318 }
319
320 /**
321 * Auxiliary function to set the link text and html flag
322 *
323 * @param $type
324 * type of link: 0 or 1 for a text-only link, 2 for icon-only and 3 for
325 * both text and icon
326 * @param $text
327 * text to be displayed on the link to the printer-friendly page
328 * @param $img
329 * path to the icon file
330 * @return
331 * array with the link text and html flag
332 */
333 function _print_format_link_aux($type = 0, $text = '', $img = '') {
334 if ($type >= 2) {
335 $html = TRUE;
336 switch ($type) {
337 case 2:
338 $text = theme('image', $img, $text, $text, array('class' => 'print-icon'));
339 break;
340 case 3:
341 $text = theme('image', $img, $text, $text, array('class' => 'print-icon print-icon-margin')) . $text;
342 break;
343 }
344 }
345 else {
346 $html = FALSE;
347 }
348
349 return array('text' => $text,
350 'html' => $html,
351 );
352 }
353
354 /**
355 * Format the Printer-friendly link
356 *
357 * @return
358 * array of formatted attributes
359 * @ingroup themeable
360 */
361 function theme_print_format_link() {
362 $print_html_link_class = variable_get('print_html_link_class', PRINT_HTML_LINK_CLASS_DEFAULT);
363 $print_html_new_window = variable_get('print_html_new_window', PRINT_HTML_NEW_WINDOW_DEFAULT);
364 $print_html_show_link = variable_get('print_html_show_link', PRINT_HTML_SHOW_LINK_DEFAULT);
365
366 $text = t('Printer-friendly version');
367 $img = drupal_get_path('module', 'print') .'/icons/print_icon.gif';
368 $title = t('Display a printer-friendly version of this page.');
369 $class = strip_tags($print_html_link_class);
370 $new_window = $print_html_new_window;
371 $format = _print_format_link_aux($print_html_show_link, $text, $img);
372
373 return array('text' => $format['text'],
374 'html' => $format['html'],
375 'attributes' => print_fill_attributes($title, $class, $new_window),
376 );
377 }
378
379 /**
380 * Define the strings displayed by the module in the printer-friendly template
381 *
382 * @return
383 * array with the used text strings
384 * @ingroup themeable
385 */
386 function theme_print_text() {
387 return array('retrieved' => '',
388 'sourceURL' => '',
389 'published' => '',
390 'by' => '',
391 'created' => '',
392 'links' => '',
393 );
394 }
395
396 /**
397 * Auxiliary function to display a formatted Printer-friendly link
398 *
399 * Function made available so that developers may call this function from
400 * their defined pages/blocks.
401 *
402 * @param $path
403 * path of the original page (optional). If not specified, the current URL
404 * is used
405 * @param $node
406 * an optional node object, to be used in defining the path, if used, the
407 * path argument is irrelevant
408 * @return
409 * string with the HTML link to the printer-friendly page
410 */
411 function print_insert_link($path = NULL, $node = NULL) {
412 if ($node !== NULL) {
413 $nid = $node->nid;
414 $path = 'node/'. $nid;
415 $allowed_type = print_link_allowed(array('node' => $node));
416 }
417 else {
418 if ($path === NULL) {
419 $nid = preg_replace('!^node/!', '', $_GET['q']);
420 $path = $_GET['q'];
421 }
422 else {
423 $nid = NULL;
424 }
425 $allowed_type = print_link_allowed(array('path' => $path));
426 }
427
428 if ($allowed_type) {
429 if ($nid !== NULL) {
430 if ($allowed_type === PRINT_ALLOW_BOOK_LINK) {
431 $path = 'book/export/html/'. $nid;
432 }
433 else {
434 if (variable_get('print_html_link_use_alias', PRINT_HTML_LINK_USE_ALIAS_DEFAULT)) {
435 $path = drupal_get_path_alias($path);
436 }
437 else {
438 $path = $nid;
439 }
440 }
441 $path = PRINT_PATH .'/'. $path;
442 $query = print_query_string_encode($_GET, array('q'));
443 if (empty($query)) {
444 $query = NULL;
445 }
446 }
447 else {
448 $query = NULL;
449 }
450 drupal_add_css(drupal_get_path('module', 'print') .'/css/printlinks.css');
451 $format = theme('print_format_link');
452 return '<span class="print_html">'. l($format['text'], $path, array('attributes' => $format['attributes'], 'query' => $query, 'absolute' => TRUE, 'html' => $format['html'])) .'</span>';
453 }
454 else {
455 return FALSE;
456 }
457 }
458
459 /**
460 * Determine if the current page is enabled according to the visibility settings
461 *
462 * @param $visibility
463 * current visibility settings:
464 * 0 for show on every page except the listed pages
465 * 1 for show on only the listed pages
466 * @param $pages
467 * list of pages
468 * @return
469 * TRUE if it is enabled, FALSE otherwise
470 */
471 function _print_page_match($visibility, $pages) {
472 if ($pages) {
473 $path = drupal_get_path_alias($_GET['q']);
474 $page_match = drupal_match_path($path, $pages);
475 if ($path != $_GET['q']) {
476 $page_match = $page_match || drupal_match_path($_GET['q'], $pages);
477 }
478
479 return !($visibility xor $page_match);
480 }
481 else {
482 return !$visibility;
483 }
484 }
485
486 /**
487 * Determine a the link to the PF version is allowed depending on all possible settings
488 *
489 * @param $args
490 * array containing the possible parameters:
491 * teaser, node, type, path
492 * @return
493 * FALSE if not allowed
494 * PRINT_ALLOW_NORMAL_LINK if a normal link is allowed
495 * PRINT_ALLOW_BOOK_LINK if a link is allowed in a book node
496 */
497 function print_link_allowed($args) {
498 if (!empty($args['teaser']) || !user_access('access print')) {
499 // If showing only the teaser or the user is not allowed or link is disabled
500 return FALSE;
501 }
502 if (!empty($args['path'])) {
503 $nid = preg_replace('!^node/!', '', drupal_get_normal_path($args['path']));
504 if (is_numeric($nid)) {
505 $args['node'] = node_load(array('nid' => $nid));
506 }
507 }
508 if (!empty($args['node'])) {
509 static $node_type = FALSE;
510
511 $node = $args['node'];
512 if ($node_type === FALSE) {
513 if (isset($node->type)) {
514 $node_type = $node->type;
515 }
516 else {
517 $node_type = '';
518 }
519 }
520 // Node
521 $print_html_node_link_visibility = variable_get('print_html_node_link_visibility', PRINT_HTML_NODE_LINK_VISIBILITY_DEFAULT);
522 $print_html_node_link_pages = variable_get('print_html_node_link_pages', PRINT_HTML_NODE_LINK_PAGES_DEFAULT);
523
524 if (!empty($node->printing) ||
525 !_print_page_match($print_html_node_link_visibility, $print_html_node_link_pages)) {
526 // Page not in visibility list or we are working!
527 return FALSE;
528 }
529 elseif (isset($args['type']) && ($args['type'] == 'comment') && isset($node_type)) {
530 // Link is for a comment, return the configured setting
531 return variable_get('print_display_comment_'. $node_type, PRINT_TYPE_COMMENT_LINK_DEFAULT);
532 }
533 else {
534 // Node link
535 if (isset($node_type) &&
536 !variable_get('print_display_'. $node_type, PRINT_TYPE_SHOW_LINK_DEFAULT)) {
537 // Link for this node type is disabled
538 return FALSE;
539 }
540 elseif (isset($node->book)) {
541 // Node is a book;
542 $print_html_book_link = variable_get('print_html_book_link', PRINT_HTML_BOOK_LINK_DEFAULT);
543 if (!$print_html_book_link || !user_access('access printer-friendly version')) {
544 // Book link is disabled
545 return FALSE;
546 }
547 else {
548 return PRINT_ALLOW_BOOK_LINK;
549 }
550 }
551 else {
552 return PRINT_ALLOW_NORMAL_LINK;
553 }
554 }
555 }
556 else {
557 // 'System' page
558 $print_html_sys_link_visibility = variable_get('print_html_sys_link_visibility', PRINT_HTML_SYS_LINK_VISIBILITY_DEFAULT);
559 $print_html_sys_link_pages = variable_get('print_html_sys_link_pages', PRINT_HTML_SYS_LINK_PAGES_DEFAULT);
560
561 return _print_page_match($print_html_sys_link_visibility, $print_html_sys_link_pages);
562 }
563 }
564
565 /**
566 * Parse an array into a valid urlencoded query string.
567 * Modified from drupal_query_string_encode to prevent re-encoding of
568 * encoded original.
569 *
570 * @param $query
571 * The array to be processed e.g. $_GET
572 * @param $exclude
573 * The array filled with keys to be excluded.
574 * @return
575 * urlencoded string which can be appended to/as the URL query string
576 */
577 function print_query_string_encode($query, $exclude = array(), $parent = '') {
578 $params = array();
579 foreach ($query as $key => $value) {
580 if ($parent) {
581 $key = $parent .'['. $key .']';
582 }
583
584 if (in_array($key, $exclude)) {
585 continue;
586 }
587
588 if (is_array($value)) {
589 $params[] = print_query_string_encode($value, $exclude, $key);
590 }
591 else {
592 $params[] = $key .'='. rawurlencode($value);
593 }
594 }
595
596 return implode('&', $params);
597 }