1<?php 2 3use dokuwiki\ErrorHandler; 4use dokuwiki\Extension\ActionPlugin; 5use dokuwiki\Extension\Event; 6use dokuwiki\Extension\EventHandler; 7use dokuwiki\plugin\dw2pdf\MenuItem; 8use dokuwiki\plugin\dw2pdf\src\Cache; 9use dokuwiki\plugin\dw2pdf\src\CollectorFactory; 10use dokuwiki\plugin\dw2pdf\src\Config; 11use dokuwiki\plugin\dw2pdf\src\ExportException; 12use dokuwiki\plugin\dw2pdf\src\PdfExportService; 13use Mpdf\MpdfException; 14 15/** 16 * dw2Pdf Plugin: Conversion from dokuwiki content to pdf. 17 * 18 * Export html content to pdf, for different url parameter configurations 19 * DokuPDF which extends mPDF is used for generating the pdf from html. 20 * 21 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 22 * @author Luigi Micco <l.micco@tiscali.it> 23 * @author Andreas Gohr <andi@splitbrain.org> 24 */ 25class action_plugin_dw2pdf extends ActionPlugin 26{ 27 /** 28 * Register the events 29 * 30 * @param EventHandler $controller 31 */ 32 public function register(EventHandler $controller) 33 { 34 $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'convert'); 35 $controller->register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'addsvgbutton'); 36 } 37 38 /** 39 * Do the HTML to PDF conversion work 40 * 41 * @param Event $event 42 * @throws MpdfException 43 */ 44 public function convert(Event $event) 45 { 46 global $REV, $DATE_AT, $INPUT; 47 48 // our event? 49 $allowedEvents = ['export_pdfbook', 'export_pdf', 'export_pdfns']; 50 if (!in_array($event->data, $allowedEvents)) { 51 return; 52 } 53 54 $this->loadConfig(); 55 56 try { 57 $config = new Config($this->conf); 58 $collector = CollectorFactory::create( 59 $event->data, 60 $config, 61 ((int) $REV) ?: null, 62 ((int) $DATE_AT) ?: null 63 ); 64 $cache = new Cache($config, $collector); 65 66 $pdfService = new PdfExportService( 67 $config, 68 $collector, 69 $cache, 70 $this->getLang('tocheader'), 71 $INPUT->server->str('REMOTE_USER', '', true) 72 ); 73 74 $cacheFile = $pdfService->getPdf(); // dumps HTML when in debug mode and exits 75 } catch (ExportException $e) { 76 // expected failure carrying a message meant for the user; escape dynamic parts as 77 // the message may end up in an HTML sink (see exportError()) 78 $args = array_map('hsc', $e->getArgs()); 79 $this->exportError($event, vsprintf($this->getLang($e->getMessage()), $args)); 80 return; 81 } catch (\Exception $e) { 82 // unexpected failure, keep the details out of the user's way but log them 83 ErrorHandler::logException($e); 84 $this->exportError($event, $this->getLang('exportfailed')); 85 return; 86 } 87 88 // take over the request and deliver the file 89 $event->preventDefault(); 90 $event->stopPropagation(); 91 92 $pdfService->sendPdf($cacheFile); // exits after sending 93 } 94 95 /** 96 * Surface an export failure to the user 97 * 98 * BookCreator triggers export_pdfbook as a background download via the jQuery.fileDownload 99 * plugin, which cannot be redirected. It reads the response body and injects it into an error 100 * dialog through jQuery.html(), so failures for that action are answered with an HTTP error and 101 * the message in the body. Any dynamic parts of the message must therefore be HTML escaped by 102 * the caller. All other actions are regular navigations and get a flash message plus a redirect 103 * back to the current page. 104 * 105 * @param Event $event The export event being handled 106 * @param string $message The localized message to show the user, safe for HTML output 107 * @return void 108 */ 109 protected function exportError(Event $event, string $message): void 110 { 111 if ($event->data === 'export_pdfbook') { 112 http_status(400); 113 header('Content-Type: text/html; charset=utf-8'); 114 echo $message; 115 exit(); 116 } 117 118 msg($message, -1); 119 $event->data = 'redirect'; 120 } 121 122 123 /** 124 * Add 'export pdf' button to page tools, new SVG based mechanism 125 * 126 * @param Event $event 127 */ 128 public function addsvgbutton(Event $event) 129 { 130 global $INFO; 131 if ($event->data['view'] != 'page' || !$this->getConf('showexportbutton')) { 132 return; 133 } 134 135 if (!$INFO['exists']) { 136 return; 137 } 138 139 array_splice($event->data['items'], -1, 0, [new MenuItem()]); 140 } 141} 142