xref: /plugin/dw2pdf/action.php (revision 05d2b5074ba45a14d28da490e91649a26224c3b3)
1<?php
2/**
3 * dw2Pdf Plugin: Conversion from dokuwiki content to pdf.
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Luigi Micco <l.micco@tiscali.it>
7 * @author     Andreas Gohr <andi@splitbrain.org>
8 */
9
10// must be run within Dokuwiki
11if(!defined('DOKU_INC')) die();
12
13/**
14 * Class action_plugin_dw2pdf
15 *
16 * Export hmtl content to pdf, for different url parameter configurations
17 * DokuPDF which extends mPDF is used for generating the pdf from html.
18 */
19class action_plugin_dw2pdf extends DokuWiki_Action_Plugin {
20    /**
21     * Settings for current export, collected from url param, plugin config, global config
22     *
23     * @var array
24     */
25    protected $exportConfig = null;
26    protected $tpl;
27    protected $list = array();
28
29    /**
30     * Constructor. Sets the correct template
31     */
32    public function __construct() {
33        $this->tpl = $this->getExportConfig('template');
34    }
35
36    /**
37     * Register the events
38     */
39    public function register(Doku_Event_Handler $controller) {
40        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'convert', array());
41        $controller->register_hook('TEMPLATE_PAGETOOLS_DISPLAY', 'BEFORE', $this, 'addbutton', array());
42    }
43
44    /**
45     * Do the HTML to PDF conversion work
46     *
47     * @param Doku_Event $event
48     * @param array      $param
49     * @return bool
50     */
51    public function convert(Doku_Event $event, $param) {
52        global $ACT;
53        global $REV;
54        global $ID;
55
56        // our event?
57        if(($ACT != 'export_pdfbook') && ($ACT != 'export_pdf') && ($ACT != 'export_pdfns')) return false;
58
59        // check user's rights
60        if(auth_quickaclcheck($ID) < AUTH_READ) return false;
61
62        if($data = $this->collectExportPages($event)) {
63            list($title, $this->list) = $data;
64        } else {
65            return false;
66        }
67
68        // it's ours, no one else's
69        $event->preventDefault();
70
71        // prepare cache
72        $cachekey = join(',', $this->list)
73                    . $REV
74                    . $this->getExportConfig('template')
75                    . $this->getExportConfig('pagesize')
76                    . $this->getExportConfig('orientation')
77                    . $this->getExportConfig('doublesided')
78                    . ($this->getExportConfig('hasToC') ? join('-', $this->getExportConfig('levels')) : '0')
79                    . $title;
80        $cache = new cache($cachekey, '.dw2.pdf');
81
82        $depends['files']   = array_map('wikiFN', $this->list);
83        $depends['files'][] = __FILE__;
84        $depends['files'][] = dirname(__FILE__) . '/renderer.php';
85        $depends['files'][] = dirname(__FILE__) . '/mpdf/mpdf.php';
86        $depends['files']   = array_merge($depends['files'], getConfigFiles('main'));
87
88        // hard work only when no cache available
89        if(!$this->getConf('usecache') || !$cache->useCache($depends)) {
90            $this->generatePDF($cache->cache, $title);
91        }
92
93        // deliver the file
94        $this->sendPDFFile($cache->cache, $title);
95        return true;
96    }
97
98
99    /**
100     * Obtain list of pages and title, based on url parameters
101     *
102     * @param Doku_Event $event
103     * @return string|bool
104     */
105    protected function collectExportPages(Doku_Event $event) {
106        global $ACT;
107        global $ID;
108        global $INPUT;
109        global $conf;
110
111        // list of one or multiple pages
112        $list = array();
113
114        if($ACT == 'export_pdf') {
115            $list[0] = $ID;
116            $title = $INPUT->str('pdftitle');
117            if(!$title) {
118                $title = p_get_first_heading($ID);
119            }
120
121        } elseif($ACT == 'export_pdfns') {
122            //check input for title and ns
123            if(!$title = $INPUT->str('pdfns_title')) {
124                $this->showPageWithErrorMsg($event, 'needtitle');
125                return false;
126            }
127            $pdfnamespace = cleanID($INPUT->str('pdfns_ns'));
128            if(!@is_dir(dirname(wikiFN($pdfnamespace . ':dummy')))) {
129                $this->showPageWithErrorMsg($event, 'needns');
130                return false;
131            }
132
133            //sort order
134            $order = $INPUT->str('pdfns_order', 'natural', true);
135            $sortoptions = array('pagename', 'date', 'natural');
136            if(!in_array($order, $sortoptions)) {
137                $order = 'natural';
138            }
139
140            //search depth
141            $depth = $INPUT->int('pdfns_depth', 0);
142            if($depth < 0) {
143                $depth = 0;
144            }
145
146            //page search
147            $result = array();
148            $opts = array('depth' => $depth); //recursive all levels
149            $dir = utf8_encodeFN(str_replace(':', '/', $pdfnamespace));
150            search($result, $conf['datadir'], 'search_allpages', $opts, $dir);
151
152            //sorting
153            if(count($result) > 0) {
154                if($order == 'date') {
155                    usort($result, array($this, '_datesort'));
156                } elseif($order == 'pagename') {
157                    usort($result, array($this, '_pagenamesort'));
158                }
159            }
160
161            foreach($result as $item) {
162                $list[] = $item['id'];
163            }
164
165        } elseif(isset($_COOKIE['list-pagelist']) && !empty($_COOKIE['list-pagelist'])) {
166            //is in Bookmanager of bookcreator plugin a title given?
167            if(!$title = $INPUT->str('pdfbook_title')) {
168                $this->showPageWithErrorMsg($event, 'needtitle');
169                return false;
170            } else {
171                $list = explode("|", $_COOKIE['list-pagelist']);
172            }
173
174        } else {
175            //show empty bookcreator message
176            $this->showPageWithErrorMsg($event, 'empty');
177            return false;
178        }
179
180        return array($title, $list);
181    }
182
183
184    /**
185     * Set error notification and reload page again
186     *
187     * @param Doku_Event $event
188     * @param string     $msglangkey key of translation key
189     */
190    private function showPageWithErrorMsg(Doku_Event $event, $msglangkey) {
191        msg($this->getLang($msglangkey), -1);
192
193        $event->data = 'show';
194        $_SERVER['REQUEST_METHOD'] = 'POST'; //clears url
195    }
196
197    /**
198     * Build a pdf from the html
199     *
200     * @param string $cachefile
201     * @param string $title
202     */
203    protected function generatePDF($cachefile, $title) {
204        global $ID;
205        global $REV;
206        global $INPUT;
207
208        //some shortcuts to export settings
209        $hasToC = $this->getExportConfig('hasToC');
210        $levels = $this->getExportConfig('levels');
211        $isDebug = $this->getExportConfig('isDebug');
212
213        // initialize PDF library
214        require_once(dirname(__FILE__) . "/DokuPDF.class.php");
215
216        $mpdf = new DokuPDF($this->getExportConfig('pagesize'), $this->getExportConfig('orientation'));
217
218        // let mpdf fix local links
219        $self = parse_url(DOKU_URL);
220        $url = $self['scheme'] . '://' . $self['host'];
221        if($self['port']) {
222            $url .= ':' . $self['port'];
223        }
224        $mpdf->setBasePath($url);
225
226        // Set the title
227        $mpdf->SetTitle($title);
228
229        // some default document settings
230        //note: double-sided document, starts at an odd page (first page is a right-hand side page)
231        //      single-side document has only odd pages
232        $mpdf->mirrorMargins = $this->getExportConfig('doublesided');
233        $mpdf->useOddEven = $this->getExportConfig('doublesided'); //duplicate of mirrorMargins
234        $mpdf->setAutoTopMargin = 'stretch';
235        $mpdf->setAutoBottomMargin = 'stretch';
236//            $mpdf->pagenumSuffix = '/'; //prefix for {nbpg}
237        if($hasToC) {
238            $mpdf->PageNumSubstitutions[] = array('from' => 1, 'reset' => 0, 'type' => 'i', 'suppress' => 'off'); //use italic pageno until ToC
239            $mpdf->h2toc = $levels;
240        } else {
241            $mpdf->PageNumSubstitutions[] = array('from' => 1, 'reset' => 0, 'type' => '1', 'suppress' => 'off');
242        }
243
244        // load the template
245        $template = $this->load_template($title);
246
247        // prepare HTML header styles
248        $html = '';
249        if($isDebug) {
250            $html .= '<html><head>';
251            $html .= '<style type="text/css">';
252        }
253        $styles = $this->load_css();
254        $styles .= '@page { size:auto; ' . $template['page'] . '}';
255        $styles .= '@page :first {' . $template['first'] . '}';
256
257        $styles .= '@page landscape-page { size:landscape }';
258        $styles .= 'div.dw2pdf-landscape { page:landscape-page }';
259        $styles .= '@page portrait-page { size:portrait }';
260        $styles .= 'div.dw2pdf-portrait { page:portrait-page }';
261
262        $mpdf->WriteHTML($styles, 1);
263
264        if($isDebug) {
265            $html .= $styles;
266            $html .= '</style>';
267            $html .= '</head><body>';
268        }
269
270        $body_start = $template['html'];
271        $body_start .= '<div class="dokuwiki">';
272
273        // insert the cover page
274        $body_start .= $template['cover'];
275
276        $mpdf->WriteHTML($body_start, 2, true, false); //start body html
277        if($isDebug) {
278            $html .= $body_start;
279        }
280        if($hasToC) {
281            //Note: - for double-sided document the ToC is always on an even number of pages, so that the following content is on a correct odd/even page
282            //      - first page of ToC starts always at odd page (so eventually an additional blank page is included before)
283            //      - there is no page numbering at the pages of the ToC
284            $mpdf->TOCpagebreakByArray(
285                array(
286                    'toc-preHTML' => '<h2>Table of contents</h2>',
287                    'toc-bookmarkText' => 'Table of Content',
288                    'links' => true,
289                    'outdent' => '1em',
290                    'resetpagenum' => true, //start pagenumbering after ToC
291                    'pagenumstyle' => '1'
292                )
293            );
294            $html .= '<tocpagebreak>';
295        }
296
297        // store original pageid
298        $keep = $ID;
299
300        // loop over all pages
301        $cnt = count($this->list);
302        for($n = 0; $n < $cnt; $n++) {
303            $page = $this->list[$n];
304
305            // set global pageid to the rendered page
306            $ID = $page;
307
308            $pagehtml = p_cached_output(wikiFN($page, $REV), 'dw2pdf', $page);
309            $pagehtml .= $this->page_depend_replacements($template['cite'], cleanID($page));
310            if($n < ($cnt - 1)) {
311                $pagehtml .= '<pagebreak />';
312            }
313
314            $mpdf->WriteHTML($pagehtml, 2, false, false); //intermediate body html
315            if($isDebug) {
316                $html .= $pagehtml;
317            }
318        }
319        //restore ID
320        $ID = $keep;
321
322        // insert the back page
323        $body_end = $template['back'];
324
325        $body_end .= '</div>';
326
327        $mpdf->WriteHTML($body_end, 2, false, true); // finish body html
328        if($isDebug) {
329            $html .= $body_end;
330            $html .= '</body>';
331            $html .= '</html>';
332        }
333
334        //Return html for debugging
335        if($isDebug) {
336            if($INPUT->str('debughtml', 'text', true) == 'html') {
337                echo $html;
338            } else {
339                header('Content-Type: text/plain; charset=utf-8');
340                echo $html;
341            }
342            exit();
343        };
344
345        // write to cache file
346        $mpdf->Output($cachefile, 'F');
347    }
348
349    /**
350     * @param string $cachefile
351     * @param string $title
352     */
353    protected function sendPDFFile($cachefile, $title) {
354        header('Content-Type: application/pdf');
355        header('Cache-Control: must-revalidate, no-transform, post-check=0, pre-check=0');
356        header('Pragma: public');
357        http_conditionalRequest(filemtime($cachefile));
358
359        $filename = rawurlencode(cleanID(strtr($title, ':/;"', '    ')));
360        if($this->getConf('output') == 'file') {
361            header('Content-Disposition: attachment; filename="' . $filename . '.pdf";');
362        } else {
363            header('Content-Disposition: inline; filename="' . $filename . '.pdf";');
364        }
365
366        //try to send file, and exit if done
367        http_sendfile($cachefile);
368
369        $fp = @fopen($cachefile, "rb");
370        if($fp) {
371            http_rangeRequest($fp, filesize($cachefile), 'application/pdf');
372        } else {
373            header("HTTP/1.0 500 Internal Server Error");
374            print "Could not read file - bad permissions?";
375        }
376        exit();
377    }
378
379    /**
380     * Load the various template files and prepare the HTML/CSS for insertion
381     */
382    protected function load_template($title) {
383        global $ID;
384        global $conf;
385
386        // this is what we'll return
387        $output = array(
388            'cover' => '',
389            'html'  => '',
390            'page'  => '',
391            'first' => '',
392            'cite'  => '',
393        );
394
395        // prepare header/footer elements
396        $html = '';
397        foreach(array('header', 'footer') as $section) {
398            foreach(array('', '_odd', '_even', '_first') as $order) {
399                $file = DOKU_PLUGIN . 'dw2pdf/tpl/' . $this->tpl . '/' . $section . $order . '.html';
400                if(file_exists($file)) {
401                    $html .= '<htmlpage' . $section . ' name="' . $section . $order . '">' . DOKU_LF;
402                    $html .= file_get_contents($file) . DOKU_LF;
403                    $html .= '</htmlpage' . $section . '>' . DOKU_LF;
404
405                    // register the needed pseudo CSS
406                    if($order == '_first') {
407                        $output['first'] .= $section . ': html_' . $section . $order . ';' . DOKU_LF;
408                    } elseif($order == '_even') {
409                        $output['page'] .= 'even-' . $section . '-name: html_' . $section . $order . ';' . DOKU_LF;
410                    } elseif($order == '_odd') {
411                        $output['page'] .= 'odd-' . $section . '-name: html_' . $section . $order . ';' . DOKU_LF;
412                    } else {
413                        $output['page'] .= $section . ': html_' . $section . $order . ';' . DOKU_LF;
414                    }
415                }
416            }
417        }
418
419        // prepare replacements
420        $replace = array(
421            '@PAGE@'    => '{PAGENO}',
422            '@PAGES@'   => '{nbpg}', //see also $mpdf->pagenumSuffix = ' / '
423            '@TITLE@'   => hsc($title),
424            '@WIKI@'    => $conf['title'],
425            '@WIKIURL@' => DOKU_URL,
426            '@DATE@'    => dformat(time()),
427            '@BASE@'    => DOKU_BASE,
428            '@TPLBASE@' => DOKU_BASE . 'lib/plugins/dw2pdf/tpl/' . $this->tpl . '/'
429        );
430
431        // set HTML element
432        $html = str_replace(array_keys($replace), array_values($replace), $html);
433        //TODO For bookcreator $ID (= bookmanager page) makes no sense
434        $output['html'] = $this->page_depend_replacements($html, $ID);
435
436        // cover page
437        $coverfile = DOKU_PLUGIN . 'dw2pdf/tpl/' . $this->tpl . '/cover.html';
438        if(file_exists($coverfile)) {
439            $output['cover'] = file_get_contents($coverfile);
440            $output['cover'] = str_replace(array_keys($replace), array_values($replace), $output['cover']);
441            $output['cover'] .= '<pagebreak />';
442        }
443
444        // cover page
445        $backfile = DOKU_PLUGIN . 'dw2pdf/tpl/' . $this->tpl . '/back.html';
446        if(file_exists($backfile)) {
447            $output['back'] = '<pagebreak />';
448            $output['back'] .= file_get_contents($backfile);
449            $output['back'] = str_replace(array_keys($replace), array_values($replace), $output['back']);
450        }
451
452        // citation box
453        $citationfile = DOKU_PLUGIN . 'dw2pdf/tpl/' . $this->tpl . '/citation.html';
454        if(file_exists($citationfile)) {
455            $output['cite'] = file_get_contents($citationfile);
456            $output['cite'] = str_replace(array_keys($replace), array_values($replace), $output['cite']);
457        }
458
459        return $output;
460    }
461
462    /**
463     * @param string $raw code with placeholders
464     * @param string $id  pageid
465     * @return string
466     */
467    protected function page_depend_replacements($raw, $id) {
468        global $REV;
469
470        // generate qr code for this page using google infographics api
471        $qr_code = '';
472        if($this->getConf('qrcodesize')) {
473            $url = urlencode(wl($id, '', '&', true));
474            $qr_code = '<img src="https://chart.googleapis.com/chart?chs=' .
475                $this->getConf('qrcodesize') . '&cht=qr&chl=' . $url . '" />';
476        }
477        // prepare replacements
478        $replace['@ID@']      = $id;
479        $replace['@UPDATE@']  = dformat(filemtime(wikiFN($id, $REV)));
480        $replace['@PAGEURL@'] = wl($id, ($REV) ? array('rev' => $REV) : false, true, "&");
481        $replace['@QRCODE@']  = $qr_code;
482
483        $content = str_replace(array_keys($replace), array_values($replace), $raw);
484
485        // @DATE(<date>[, <format>])@
486        $content = preg_replace_callback(
487            '/@DATE\((.*?)(?:,\s*(.*?))?\)@/',
488            array($this, 'replacedate'),
489            $content
490        );
491
492        return $content;
493    }
494
495
496    /**
497     * (callback) Replace date by request datestring
498     * e.g. '%m(30-11-1975)' is replaced by '11'
499     *
500     * @param array $match with [0]=>whole match, [1]=> first subpattern, [2] => second subpattern
501     * @return string
502     */
503    function replacedate($match) {
504        global $conf;
505        //no 2nd argument for default date format
506        if($match[2] == null) {
507            $match[2] = $conf['dformat'];
508        }
509        return strftime($match[2], strtotime($match[1]));
510    }
511
512
513    /**
514     * Load all the style sheets and apply the needed replacements
515     */
516    protected function load_css() {
517        global $conf;
518        //reusue the CSS dispatcher functions without triggering the main function
519        define('SIMPLE_TEST', 1);
520        require_once(DOKU_INC . 'lib/exe/css.php');
521
522        // prepare CSS files
523        $files = array_merge(
524            array(
525                DOKU_INC . 'lib/styles/screen.css'
526                    => DOKU_BASE . 'lib/styles/',
527                DOKU_INC . 'lib/styles/print.css'
528                    => DOKU_BASE . 'lib/styles/',
529            ),
530            css_pluginstyles('all'),
531            $this->css_pluginPDFstyles(),
532            array(
533                DOKU_PLUGIN . 'dw2pdf/conf/style.css'
534                    => DOKU_BASE . 'lib/plugins/dw2pdf/conf/',
535                DOKU_PLUGIN . 'dw2pdf/tpl/' . $this->tpl . '/style.css'
536                    => DOKU_BASE . 'lib/plugins/dw2pdf/tpl/' . $this->tpl . '/',
537                DOKU_PLUGIN . 'dw2pdf/conf/style.local.css'
538                    => DOKU_BASE . 'lib/plugins/dw2pdf/conf/',
539            )
540        );
541        $css = '';
542        foreach($files as $file => $location) {
543            $display = str_replace(fullpath(DOKU_INC), '', fullpath($file));
544            $css .= "\n/* XXXXXXXXX $display XXXXXXXXX */\n";
545            $css .= css_loadfile($file, $location);
546        }
547
548        if(function_exists('css_parseless')) {
549            // apply pattern replacements
550            $styleini = css_styleini($conf['template']);
551            $css = css_applystyle($css, $styleini['replacements']);
552
553            // parse less
554            $css = css_parseless($css);
555        } else {
556            // @deprecated 2013-12-19: fix backward compatibility
557            $css = css_applystyle($css, DOKU_INC . 'lib/tpl/' . $conf['template'] . '/');
558        }
559
560        return $css;
561    }
562
563    /**
564     * Returns a list of possible Plugin PDF Styles
565     *
566     * Checks for a pdf.css, falls back to print.css
567     *
568     * @author Andreas Gohr <andi@splitbrain.org>
569     */
570    protected function css_pluginPDFstyles() {
571        $list = array();
572        $plugins = plugin_list();
573
574        $usestyle = explode(',', $this->getConf('usestyles'));
575        foreach($plugins as $p) {
576            if(in_array($p, $usestyle)) {
577                $list[DOKU_PLUGIN . "$p/screen.css"] = DOKU_BASE . "lib/plugins/$p/";
578                $list[DOKU_PLUGIN . "$p/style.css"] = DOKU_BASE . "lib/plugins/$p/";
579            }
580
581            if(file_exists(DOKU_PLUGIN . "$p/pdf.css")) {
582                $list[DOKU_PLUGIN . "$p/pdf.css"] = DOKU_BASE . "lib/plugins/$p/";
583            } else {
584                $list[DOKU_PLUGIN . "$p/print.css"] = DOKU_BASE . "lib/plugins/$p/";
585            }
586        }
587        return $list;
588    }
589
590    /**
591     * Returns array of pages which will be included in the exported pdf
592     *
593     * @return array
594     */
595    public function getExportedPages() {
596        return $this->list;
597    }
598
599    /**
600     * usort callback to sort by file lastmodified time
601     */
602    public function _datesort($a, $b) {
603        if($b['rev'] < $a['rev']) return -1;
604        if($b['rev'] > $a['rev']) return 1;
605        return strcmp($b['id'], $a['id']);
606    }
607
608    /**
609     * usort callback to sort by page id
610     */
611    public function _pagenamesort($a, $b) {
612        if($a['id'] <= $b['id']) return -1;
613        if($a['id'] > $b['id']) return 1;
614        return 0;
615    }
616
617    /**
618     * Return settings read from:
619     *   1. url parameters
620     *   2. plugin config
621     *   3. global config
622     *
623     * @return array
624     */
625    protected function loadExportConfig() {
626        global $INPUT;
627        global $conf;
628
629        $this->exportConfig = array();
630
631        // decide on the paper setup from param or config
632        $this->exportConfig['pagesize'] = $INPUT->str('pagesize', $this->getConf('pagesize'), true);
633        $this->exportConfig['orientation'] = $INPUT->str('orientation', $this->getConf('orientation'), true);
634
635        $doublesided = $INPUT->bool('doublesided', (bool) $this->getConf('doublesided'));
636        $this->exportConfig['doublesided'] = $doublesided ? '1' : '0';
637
638        $hasToC = $INPUT->bool('toc', (bool) $this->getConf('toc'));
639        $levels = array();
640        if($hasToC) {
641            $toclevels = $INPUT->str('toclevels', $this->getConf('toclevels'), true);
642            list($top_input, $max_input) = explode('-', $toclevels, 2);
643            list($top_conf, $max_conf) = explode('-', $this->getConf('toclevels'), 2);
644            $bounds_input = array(
645                'top' => array(
646                    (int) $top_input,
647                    (int) $top_conf
648                ),
649                'max' => array(
650                    (int) $max_input,
651                    (int) $max_conf
652                )
653            );
654            $bounds = array(
655                'top' => $conf['toptoclevel'],
656                'max' => $conf['maxtoclevel']
657
658            );
659            foreach($bounds_input as $bound => $values) {
660                foreach($values as $value) {
661                    if($value > 0 && $value <= 5) {
662                        //stop at valid value and store
663                        $bounds[$bound] = $value;
664                        break;
665                    }
666                }
667            }
668
669            if($bounds['max'] < $bounds['top']) {
670                $bounds['max'] = $bounds['top'];
671            }
672
673            for($level = $bounds['top']; $level <= $bounds['max']; $level++) {
674                $levels["H$level"] = $level - 1;
675            }
676        }
677        $this->exportConfig['hasToC'] = $hasToC;
678        $this->exportConfig['levels'] = $levels;
679
680        $this->exportConfig['maxbookmarks'] = $INPUT->int('maxbookmarks', $this->getConf('maxbookmarks'), true);
681
682        $tplconf = $this->getConf('template');
683        $tpl = $INPUT->str('tpl', $tplconf, true);
684        if(!is_dir(DOKU_PLUGIN . 'dw2pdf/tpl/' . $tpl)) {
685            $tpl = $tplconf;
686        }
687        if(!$tpl){
688            $tpl = 'default';
689        }
690        $this->exportConfig['template'] = $tpl;
691
692        $this->exportConfig['isDebug'] = $conf['allowdebug'] && $INPUT->has('debughtml');
693    }
694
695    /**
696     * Returns requested config
697     *
698     * @param string $name
699     * @param mixed  $notset
700     * @return mixed|bool
701     */
702    public function getExportConfig($name, $notset = false) {
703        if ($this->exportConfig === null){
704            $this->loadExportConfig();
705        }
706
707        if(isset($this->exportConfig[$name])){
708            return $this->exportConfig[$name];
709        }else{
710            return $notset;
711        }
712    }
713
714    /**
715     * Add 'export pdf'-button to pagetools
716     *
717     * @param Doku_Event $event
718     * @param mixed      $param not defined
719     */
720    public function addbutton(Doku_Event $event, $param) {
721        global $ID, $REV;
722
723        if($this->getConf('showexportbutton') && $event->data['view'] == 'main') {
724            $params = array('do' => 'export_pdf');
725            if($REV) {
726                $params['rev'] = $REV;
727            }
728
729            // insert button at position before last (up to top)
730            $event->data['items'] = array_slice($event->data['items'], 0, -1, true) +
731                array('export_pdf' =>
732                          '<li>'
733                          . '<a href=' . wl($ID, $params) . '  class="action export_pdf" rel="nofollow" title="' . $this->getLang('export_pdf_button') . '">'
734                          . '<span>' . $this->getLang('export_pdf_button') . '</span>'
735                          . '</a>'
736                          . '</li>'
737                ) +
738                array_slice($event->data['items'], -1, 1, true);
739        }
740    }
741}
742