1
|
<?php
|
2
|
|
3
|
/**
|
4
|
* @file
|
5
|
* Displays Printer-friendly versions of Drupal pages.
|
6
|
*
|
7
|
* @ingroup print
|
8
|
*/
|
9
|
|
10
|
define('PRINT_PDF_PDF_TOOL_DEFAULT', FALSE);
|
11
|
define('PRINT_PDF_CONTENT_DISPOSITION_DEFAULT', 2);
|
12
|
define('PRINT_PDF_PAPER_SIZE_DEFAULT', 'A4');
|
13
|
define('PRINT_PDF_PAGE_ORIENTATION_DEFAULT', 'portrait');
|
14
|
define('PRINT_PDF_IMAGES_VIA_FILE_DEFAULT', 0);
|
15
|
define('PRINT_PDF_AUTOCONFIG_DEFAULT', 1);
|
16
|
define('PRINT_PDF_FILENAME_DEFAULT', '[site:name] - [node:title] - [node:changed:custom:Y-m-d]');
|
17
|
define('PRINT_PDF_CACHE_ENABLED_DEFAULT', 0);
|
18
|
// 1 day.
|
19
|
define('PRINT_PDF_CACHE_LIFETIME_DEFAULT', 86400);
|
20
|
|
21
|
/**
|
22
|
* Implements hook_print_link().
|
23
|
*/
|
24
|
function print_pdf_print_link() {
|
25
|
return array(
|
26
|
'format' => 'pdf',
|
27
|
'text' => t('PDF version'),
|
28
|
'description' => t('Display a PDF version of this page.'),
|
29
|
'path' => 'printpdf',
|
30
|
'class' => 'print-pdf',
|
31
|
'icon' => 'pdf_icon.png',
|
32
|
'module' => 'print_pdf',
|
33
|
);
|
34
|
}
|
35
|
|
36
|
/**
|
37
|
* Implements hook_print_new_window_alter().
|
38
|
*/
|
39
|
function print_pdf_print_new_window_alter(&$new_window, $format) {
|
40
|
$new_window = (variable_get('print_pdf_content_disposition', PRINT_PDF_CONTENT_DISPOSITION_DEFAULT) == 1);
|
41
|
}
|
42
|
|
43
|
/**
|
44
|
* Implements hook_permission().
|
45
|
*/
|
46
|
function print_pdf_permission() {
|
47
|
return array(
|
48
|
'access PDF version' => array(
|
49
|
'title' => t('Access the PDF version'),
|
50
|
'description' => t('View the PDF versions and the links to them in the original pages.'),
|
51
|
),
|
52
|
);
|
53
|
}
|
54
|
|
55
|
/**
|
56
|
* Implements hook_init().
|
57
|
*/
|
58
|
function print_pdf_init() {
|
59
|
if (variable_get('print_pdf_autoconfig', PRINT_PDF_AUTOCONFIG_DEFAULT)) {
|
60
|
$print_pdf_pdf_tool = variable_get('print_pdf_pdf_tool', PRINT_PDF_PDF_TOOL_DEFAULT);
|
61
|
$tool = explode('|', $print_pdf_pdf_tool);
|
62
|
$function = $tool[0] . '_pdf_tool_info';
|
63
|
$info = function_exists($function) ? $function() : array();
|
64
|
|
65
|
if (isset($info['public_dirs'])) {
|
66
|
foreach ($info['public_dirs'] as $dir) {
|
67
|
$directory = 'public://print_pdf/' . $tool[0] . '/' . $dir;
|
68
|
/** @var DrupalLocalStreamWrapper $wrapper */
|
69
|
$wrapper = file_stream_wrapper_get_instance_by_uri($directory);
|
70
|
$real_directory_path = $wrapper->getDirectoryPath() . "/" . file_uri_target($directory);
|
71
|
$result = file_prepare_directory($real_directory_path, FILE_CREATE_DIRECTORY);
|
72
|
if (!$result) {
|
73
|
watchdog('print_pdf', 'Failed to create directory "%dir" for %tool.', array('%dir' => $directory, '%tool' => $tool[0]), WATCHDOG_CRITICAL);
|
74
|
}
|
75
|
}
|
76
|
}
|
77
|
}
|
78
|
if (variable_get('print_pdf_cache_enabled', PRINT_PDF_CACHE_ENABLED_DEFAULT)) {
|
79
|
$directory = print_pdf_cache_dir();
|
80
|
$wrapper = file_stream_wrapper_get_instance_by_uri($directory);
|
81
|
$real_directory_path = $wrapper->getDirectoryPath() . "/" . file_uri_target($directory);
|
82
|
$result = file_prepare_directory($real_directory_path, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY);
|
83
|
if (!$result) {
|
84
|
watchdog('print_pdf', 'Failed to create directory "%dir" for print_pdf cache.', array('%dir' => $directory), WATCHDOG_CRITICAL);
|
85
|
}
|
86
|
}
|
87
|
}
|
88
|
|
89
|
/**
|
90
|
* Implements hook_flush_caches().
|
91
|
*/
|
92
|
function print_pdf_flush_caches() {
|
93
|
print_pdf_cache_delete();
|
94
|
|
95
|
return array();
|
96
|
}
|
97
|
|
98
|
/**
|
99
|
* Implements hook_menu().
|
100
|
*/
|
101
|
function print_pdf_menu() {
|
102
|
$link = print_pdf_print_link();
|
103
|
$items = array();
|
104
|
|
105
|
$items[$link['path']] = array(
|
106
|
'title' => 'Printer-friendly PDF',
|
107
|
'page callback' => 'print_pdf_controller',
|
108
|
'access arguments' => array('access PDF version'),
|
109
|
'type' => MENU_CALLBACK,
|
110
|
'file' => 'print_pdf.pages.inc',
|
111
|
);
|
112
|
$items[$link['path'] . '/' . $link['path']] = array(
|
113
|
'access callback' => FALSE,
|
114
|
);
|
115
|
$items['admin/config/user-interface/print/pdf'] = array(
|
116
|
'title' => 'PDF',
|
117
|
'description' => 'Configure the settings of the PDF generation functionality.',
|
118
|
'page callback' => 'drupal_get_form',
|
119
|
'page arguments' => array('print_pdf_settings'),
|
120
|
'access arguments' => array('administer print'),
|
121
|
'weight' => 3,
|
122
|
'type' => MENU_LOCAL_TASK,
|
123
|
'file' => 'print_pdf.admin.inc',
|
124
|
);
|
125
|
$items['admin/config/user-interface/print/pdf/options'] = array(
|
126
|
'title' => 'Options',
|
127
|
'weight' => -1,
|
128
|
'type' => MENU_DEFAULT_LOCAL_TASK,
|
129
|
);
|
130
|
|
131
|
return $items;
|
132
|
}
|
133
|
|
134
|
/**
|
135
|
* Implements hook_variable_info().
|
136
|
*/
|
137
|
function print_pdf_variable_info($options) {
|
138
|
$link = print_pdf_print_link();
|
139
|
|
140
|
$variable['print_pdf_link_text'] = array(
|
141
|
'title' => t('PDF version'),
|
142
|
'description' => t('Text used in the link to the PDF version.'),
|
143
|
'type' => 'string',
|
144
|
'default' => t($link['text']),
|
145
|
);
|
146
|
|
147
|
return $variable;
|
148
|
}
|
149
|
|
150
|
/**
|
151
|
* Implements hook_block_info().
|
152
|
*/
|
153
|
function print_pdf_block_info() {
|
154
|
$block['print_pdf-top']['info'] = t('Most PDFd');
|
155
|
$block['print_pdf-top']['cache'] = DRUPAL_CACHE_GLOBAL;
|
156
|
return $block;
|
157
|
}
|
158
|
|
159
|
/**
|
160
|
* Implements hook_block_view().
|
161
|
*/
|
162
|
function print_pdf_block_view($delta = 0) {
|
163
|
$block = array();
|
164
|
switch ($delta) {
|
165
|
case 'print_pdf-top':
|
166
|
$block['subject'] = t('Most PDFd');
|
167
|
$result = db_query_range("SELECT path FROM {print_pdf_page_counter} LEFT JOIN {node} n ON path = CONCAT('node/', n.nid) WHERE status <> 0 OR status IS NULL ORDER BY totalcount DESC", 0, 3)
|
168
|
->fetchAll();
|
169
|
if (count($result)) {
|
170
|
$items = array();
|
171
|
foreach ($result as $obj) {
|
172
|
$items[] = l(_print_get_title($obj->path), $obj->path);
|
173
|
}
|
174
|
$block['content'] = theme('item_list', array('items' => $items, 'type' => 'ul'));
|
175
|
}
|
176
|
break;
|
177
|
}
|
178
|
return $block;
|
179
|
}
|
180
|
|
181
|
/**
|
182
|
* Implements hook_node_load().
|
183
|
*/
|
184
|
function print_pdf_node_load($nodes, $types) {
|
185
|
$ids = array();
|
186
|
foreach ($nodes as $node) {
|
187
|
$ids[] = $node->nid;
|
188
|
}
|
189
|
|
190
|
$link = print_pdf_print_link();
|
191
|
|
192
|
$size = 'print_' . $link['format'] . '_size';
|
193
|
$orientation = 'print_' . $link['format'] . '_orientation';
|
194
|
|
195
|
$result = db_query('SELECT nid, size, orientation FROM {print_pdf_node_conf} WHERE nid IN (:nids)', array(':nids' => $ids))->fetchAllAssoc('nid');
|
196
|
|
197
|
foreach ($nodes as $node) {
|
198
|
$node->{$size} = (isset($result[$node->nid]) && !empty($result[$node->nid]->size)) ? $result[$node->nid]->size : variable_get($size . '_' . $node->type);
|
199
|
$node->{$orientation} = (isset($result[$node->nid]) && !empty($result[$node->nid]->orientation)) ? $result[$node->nid]->orientation : variable_get($orientation . '_' . $node->type);
|
200
|
}
|
201
|
}
|
202
|
|
203
|
/**
|
204
|
* Implements hook_node_insert().
|
205
|
*/
|
206
|
function print_pdf_node_insert($node) {
|
207
|
print_pdf_node_update($node);
|
208
|
}
|
209
|
|
210
|
/**
|
211
|
* Implements hook_node_update().
|
212
|
*/
|
213
|
function print_pdf_node_update($node) {
|
214
|
if (user_access('administer print') || user_access('node-specific print configuration')) {
|
215
|
$link = print_pdf_print_link();
|
216
|
|
217
|
$size = 'print_' . $link['format'] . '_size';
|
218
|
$orientation = 'print_' . $link['format'] . '_orientation';
|
219
|
|
220
|
if (!isset($node->{$size})) {
|
221
|
$node->{$size} = variable_get($size . '_' . $node->type);
|
222
|
}
|
223
|
if (!isset($node->{$orientation})) {
|
224
|
$node->{$orientation} = variable_get($orientation . '_' . $node->type);
|
225
|
}
|
226
|
|
227
|
db_merge('print_pdf_node_conf')
|
228
|
->key(array('nid' => $node->nid))
|
229
|
->fields(array(
|
230
|
'size' => $node->{$size},
|
231
|
'orientation' => $node->{$orientation},
|
232
|
))
|
233
|
->execute();
|
234
|
}
|
235
|
|
236
|
print_pdf_cache_delete($node->nid);
|
237
|
}
|
238
|
|
239
|
/**
|
240
|
* Implements hook_node_delete().
|
241
|
*/
|
242
|
function print_pdf_node_delete($node) {
|
243
|
db_delete('print_pdf_page_counter')
|
244
|
->condition('path', 'node/' . $node->nid)
|
245
|
->execute();
|
246
|
|
247
|
print_pdf_cache_delete($node->nid);
|
248
|
}
|
249
|
|
250
|
/**
|
251
|
* Implements hook_form_alter().
|
252
|
*/
|
253
|
function print_pdf_form_alter(&$form, &$form_state, $form_id) {
|
254
|
// Add the node-type settings to activate the printer-friendly version link.
|
255
|
if ((user_access('administer print') || user_access('node-specific print configuration')) &&
|
256
|
(($form_id == 'node_type_form') || !empty($form['#node_edit_form']))) {
|
257
|
$link = print_pdf_print_link();
|
258
|
|
259
|
$size = 'print_' . $link['format'] . '_size';
|
260
|
$orientation = 'print_' . $link['format'] . '_orientation';
|
261
|
|
262
|
$form['print']['print_' . $link['format']][$size] = array(
|
263
|
'#type' => 'select',
|
264
|
'#title' => t('Paper size'),
|
265
|
'#options' => _print_pdf_paper_sizes(TRUE),
|
266
|
'#description' => t('Choose the paper size of the generated PDF.'),
|
267
|
);
|
268
|
|
269
|
$form['print']['print_' . $link['format']][$orientation] = array(
|
270
|
'#type' => 'select',
|
271
|
'#title' => t('Page orientation'),
|
272
|
'#options' => array(
|
273
|
'' => 'Unchanged',
|
274
|
'portrait' => t('Portrait'),
|
275
|
'landscape' => t('Landscape'),
|
276
|
),
|
277
|
'#description' => t('Choose the page orientation of the generated PDF.'),
|
278
|
);
|
279
|
|
280
|
if ($form_id == 'node_type_form') {
|
281
|
$form['print']['print_' . $link['format']][$size]['#default_value'] = variable_get($size . '_' . $form['#node_type']->type);
|
282
|
$form['print']['print_' . $link['format']][$orientation]['#default_value'] = variable_get($orientation . '_' . $form['#node_type']->type);
|
283
|
}
|
284
|
else {
|
285
|
$node = $form['#node'];
|
286
|
$form['print']['print_' . $link['format']][$size]['#default_value'] = isset($node->{$size}) ? $node->{$size} : variable_get($size . '_' . $node->type);
|
287
|
$form['print']['print_' . $link['format']][$orientation]['#default_value'] = isset($node->{$orientation}) ? $node->{$orientation} : variable_get($orientation . '_' . $node->type);
|
288
|
}
|
289
|
}
|
290
|
}
|
291
|
|
292
|
/**
|
293
|
* Auxiliary function to display a formatted PDF version link.
|
294
|
*
|
295
|
* Function made available so that developers may call this function from
|
296
|
* their defined pages/blocks.
|
297
|
*
|
298
|
* @param string $path
|
299
|
* Path to be used in the link. If not specified, the current URL is used.
|
300
|
* @param object $node
|
301
|
* Node object, to be used in checking node access. If the path argument is
|
302
|
* not provided, the path used will be node/nid.
|
303
|
* @param string $location
|
304
|
* Where in the page where the link is being inserted ('link', 'corner',
|
305
|
* 'block', 'help').
|
306
|
*
|
307
|
* @return bool
|
308
|
* string with the HTML link to the printer-friendly page
|
309
|
*
|
310
|
* @ingroup print_api
|
311
|
*/
|
312
|
function print_pdf_insert_link($path = NULL, $node = NULL, $location = '') {
|
313
|
if (function_exists('print_ui_insert_link')) {
|
314
|
return print_ui_insert_link(print_pdf_print_link(), array(
|
315
|
'path' => $path,
|
316
|
'node' => $node,
|
317
|
'location' => $location,
|
318
|
));
|
319
|
}
|
320
|
else {
|
321
|
return FALSE;
|
322
|
}
|
323
|
}
|
324
|
|
325
|
/**
|
326
|
* Check if the link to the PDF version is allowed depending on the settings.
|
327
|
*
|
328
|
* @param array $args
|
329
|
* Array containing the possible parameters:
|
330
|
* view_mode, node, type, path.
|
331
|
*
|
332
|
* @return bool
|
333
|
* FALSE if not allowed, TRUE otherwise
|
334
|
*/
|
335
|
function print_pdf_link_allowed($args) {
|
336
|
$print_pdf_pdf_tool = variable_get('print_pdf_pdf_tool', PRINT_PDF_PDF_TOOL_DEFAULT);
|
337
|
|
338
|
return (user_access('access PDF version') && (!empty($print_pdf_pdf_tool)));
|
339
|
}
|
340
|
|
341
|
/**
|
342
|
* Implements hook_cron().
|
343
|
*/
|
344
|
function print_pdf_cron() {
|
345
|
print_pdf_cache_clean();
|
346
|
}
|
347
|
|
348
|
/**
|
349
|
* Removes pdf files for nodes/paths if they are older than the lifetime.
|
350
|
*/
|
351
|
function print_pdf_cache_clean() {
|
352
|
$lifetime = variable_get('print_pdf_cache_lifetime', PRINT_PDF_CACHE_LIFETIME_DEFAULT);
|
353
|
|
354
|
if ($lifetime > 0) {
|
355
|
$files = file_scan_directory(print_pdf_cache_dir(), '!\d+\.pdf$!');
|
356
|
foreach ($files as $file) {
|
357
|
// For all files in the cache directory, see when they were last accessed.
|
358
|
$result = db_query("SELECT timestamp FROM {print_pdf_page_counter} WHERE path = :path", array(':path' => 'node/' . $file->name))
|
359
|
->fetchField();
|
360
|
|
361
|
// Keep the file only if last access was within the cache max life value.
|
362
|
if (($result === FALSE) || ($result + $lifetime < REQUEST_TIME)) {
|
363
|
print_pdf_cache_delete($file->name);
|
364
|
}
|
365
|
}
|
366
|
}
|
367
|
}
|
368
|
|
369
|
/**
|
370
|
* Returns the cache directory.
|
371
|
*
|
372
|
* @return string
|
373
|
* The scheme://path of the cache directory
|
374
|
*/
|
375
|
function print_pdf_cache_dir() {
|
376
|
$scheme = 'private';
|
377
|
if (!file_stream_wrapper_valid_scheme($scheme)) {
|
378
|
$scheme = 'temporary';
|
379
|
}
|
380
|
return $scheme . '://print_pdf/cache';
|
381
|
}
|
382
|
|
383
|
/**
|
384
|
* Deletes one or more files from the PDF cache directory.
|
385
|
*
|
386
|
* @param int $nid
|
387
|
* The node ID of the page for which the cached PDF should be deleted.
|
388
|
* If not provided, the entire cache directory will be deleted.
|
389
|
*/
|
390
|
function print_pdf_cache_delete($nid = NULL) {
|
391
|
$directory = print_pdf_cache_dir();
|
392
|
|
393
|
if ($nid) {
|
394
|
$filename = $directory . '/' . $nid . '.pdf';
|
395
|
if (is_file($filename)) {
|
396
|
file_unmanaged_delete($filename);
|
397
|
}
|
398
|
}
|
399
|
else {
|
400
|
// If no nid is provided, flush the entire cache.
|
401
|
if (is_dir($directory)) {
|
402
|
file_unmanaged_delete_recursive($directory);
|
403
|
}
|
404
|
}
|
405
|
}
|
406
|
|
407
|
/**
|
408
|
* Displays the PDF as inline or a downloadable file.
|
409
|
*
|
410
|
* @param string $pdf
|
411
|
* PDF content string.
|
412
|
* @param string $filename
|
413
|
* Filename of the generated PDF.
|
414
|
*
|
415
|
* @return string
|
416
|
* The disposed PDF file
|
417
|
*/
|
418
|
function print_pdf_dispose_content($pdf, $filename) {
|
419
|
if (headers_sent()) {
|
420
|
exit('Unable to stream pdf: headers already sent');
|
421
|
}
|
422
|
header('Cache-Control: private');
|
423
|
header('Content-Type: application/pdf');
|
424
|
|
425
|
$content_disposition = variable_get('print_pdf_content_disposition', PRINT_PDF_CONTENT_DISPOSITION_DEFAULT);
|
426
|
$attachment = ($content_disposition == 2) ? 'attachment' : 'inline';
|
427
|
|
428
|
header("Content-Disposition: $attachment; filename=\"$filename\"");
|
429
|
|
430
|
echo $pdf;
|
431
|
flush();
|
432
|
|
433
|
return TRUE;
|
434
|
}
|
435
|
|
436
|
/**
|
437
|
* Generate a PDF version of the provided HTML.
|
438
|
*
|
439
|
* @param string $html
|
440
|
* HTML content of the PDF.
|
441
|
* @param array $meta
|
442
|
* Meta information to be used in the PDF
|
443
|
* - url: original URL
|
444
|
* - name: author's name
|
445
|
* - title: Page title
|
446
|
* - node: node object.
|
447
|
* @param string $filename
|
448
|
* (optional) Filename of the generated PDF.
|
449
|
* @param string $paper_size
|
450
|
* (optional) Paper size of the generated PDF.
|
451
|
* @param string $page_orientation
|
452
|
* (optional) Page orientation of the generated PDF.
|
453
|
*
|
454
|
* @return string|null
|
455
|
* generated PDF page, or NULL in case of error
|
456
|
*
|
457
|
* @see print_pdf_controller()
|
458
|
*
|
459
|
* @ingroup print_api
|
460
|
*/
|
461
|
function print_pdf_generate_html($html, $meta, $filename = NULL, $paper_size = NULL, $page_orientation = NULL) {
|
462
|
$pdf_tool = explode('|', variable_get('print_pdf_pdf_tool', PRINT_PDF_PDF_TOOL_DEFAULT));
|
463
|
|
464
|
module_load_include('inc', $pdf_tool[0], $pdf_tool[0] . '.pages');
|
465
|
|
466
|
$function = $pdf_tool[0] . '_print_pdf_generate';
|
467
|
$pdf = function_exists($function) ? $function($html, $meta, $paper_size, $page_orientation) : NULL;
|
468
|
if ($filename) {
|
469
|
return print_pdf_dispose_content($pdf, $filename);
|
470
|
}
|
471
|
|
472
|
return $pdf;
|
473
|
}
|
474
|
|
475
|
/**
|
476
|
* Implements hook_views_api().
|
477
|
*/
|
478
|
function print_pdf_views_api() {
|
479
|
return array(
|
480
|
'api' => 2.0,
|
481
|
'path' => drupal_get_path('module', 'print_pdf'),
|
482
|
);
|
483
|
}
|
484
|
|
485
|
/**
|
486
|
* Lists all possible paper sizes.
|
487
|
*
|
488
|
* @param bool $include_default
|
489
|
* Flag indicating whether to include the tool's default value.
|
490
|
*
|
491
|
* @return array
|
492
|
* array of strings with the available paper sizes
|
493
|
*/
|
494
|
function _print_pdf_paper_sizes($include_default = FALSE) {
|
495
|
$ret = ($include_default) ? array('' => 'Unchanged') : array();
|
496
|
|
497
|
$ret += array(
|
498
|
'4A0' => '4A0',
|
499
|
'2A0' => '2A0',
|
500
|
'A0' => 'A0',
|
501
|
'A1' => 'A1',
|
502
|
'A2' => 'A2',
|
503
|
'A3' => 'A3',
|
504
|
'A4' => 'A4',
|
505
|
'A5' => 'A5',
|
506
|
'A6' => 'A6',
|
507
|
'A7' => 'A7',
|
508
|
'A8' => 'A8',
|
509
|
'A9' => 'A9',
|
510
|
'A10' => 'A10',
|
511
|
'B0' => 'B0',
|
512
|
'B1' => 'B1',
|
513
|
'B2' => 'B2',
|
514
|
'B3' => 'B3',
|
515
|
'B4' => 'B4',
|
516
|
'B5' => 'B5',
|
517
|
'B6' => 'B6',
|
518
|
'B7' => 'B7',
|
519
|
'B8' => 'B8',
|
520
|
'B9' => 'B9',
|
521
|
'B10' => 'B10',
|
522
|
'C0' => 'C0',
|
523
|
'C1' => 'C1',
|
524
|
'C2' => 'C2',
|
525
|
'C3' => 'C3',
|
526
|
'C4' => 'C4',
|
527
|
'C5' => 'C5',
|
528
|
'C6' => 'C6',
|
529
|
'C7' => 'C7',
|
530
|
'C8' => 'C8',
|
531
|
'C9' => 'C9',
|
532
|
'C10' => 'C10',
|
533
|
'RA0' => 'RA0',
|
534
|
'RA1' => 'RA1',
|
535
|
'RA2' => 'RA2',
|
536
|
'RA3' => 'RA3',
|
537
|
'RA4' => 'RA4',
|
538
|
'SRA0' => 'SRA0',
|
539
|
'SRA1' => 'SRA1',
|
540
|
'SRA2' => 'SRA2',
|
541
|
'SRA3' => 'SRA3',
|
542
|
'SRA4' => 'SRA4',
|
543
|
'LETTER' => 'Letter',
|
544
|
'LEGAL' => 'Legal',
|
545
|
'EXECUTIVE' => 'Executive',
|
546
|
'FOLIO' => 'Folio',
|
547
|
);
|
548
|
|
549
|
return $ret;
|
550
|
}
|