1<?php
2/**
3 * ODT Plugin: Exports to ODT
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Andreas Gohr <andi@splitbrain.org>
7 * @author     Aurelien Bompard <aurelien@bompard.org>
8 */
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12/**
13 * Class syntax_plugin_odt
14 *
15 * @package DokuWiki\Syntax
16 */
17class syntax_plugin_odt extends DokuWiki_Syntax_Plugin {
18    protected $config = NULL;
19
20    /**
21     * What kind of syntax are we?
22     */
23    public function getType() {
24        return 'substition';
25    }
26
27    /**
28     * What about paragraphs?
29     */
30    public function getPType() {
31        return 'normal';
32    }
33
34    /**
35     * Where to sort in?
36     */
37    public function getSort() {
38        return 319; // Before image detection, which uses {{...}} and is 320
39    }
40
41    /**
42     * Connect pattern to lexer
43     *
44     * @param string $mode
45     */
46    public function connectTo($mode) {
47        $this->Lexer->addSpecialPattern('~~ODT~~', $mode, 'plugin_odt');
48        $this->Lexer->addSpecialPattern('{{odt>.+?}}', $mode, 'plugin_odt');
49    }
50
51    /**
52     * Handler to prepare matched data for the rendering process
53     *
54     * @param   string $match The text matched by the patterns
55     * @param   int $state The lexer state for the match
56     * @param   int $pos The character position of the matched text
57     * @param   Doku_Handler $handler The Doku_Handler object
58     * @return  bool|array Return an array with all data you want to use in render, false don't add an instruction
59     */
60    public function handle($match, $state, $pos, Doku_Handler $handler) {
61        // Export button
62        if($match == '~~ODT~~') {
63            return array();
64        }
65
66        // Extended info
67        $match = substr($match, 6, -2); //strip markup
68        $extinfo = explode(':', $match);
69
70        $info_type = $extinfo[0];
71
72        if(count($extinfo) < 2) { // no value
73            $info_value = '';
74        } elseif(count($extinfo) == 2) {
75            $info_value = $extinfo[1];
76        } else { // value may contain colons
77            $info_value = implode(array_slice($extinfo, 1), ':');
78        }
79        return array($info_type, $info_value, $pos);
80    }
81
82    /**
83     * Handles the actual output creation.
84     *
85     * @param string $format output format being rendered
86     * @param Doku_Renderer $renderer the current renderer object
87     * @param array $data data created by handler()
88     * @return  boolean                 rendered correctly? (however, returned value is not used at the moment)
89     */
90    public function render($format, Doku_Renderer $renderer, $data) {
91        global $ID, $REV;
92
93        if(!$data) { // Export button
94            if($format != 'xhtml') return false;
95
96            $renderer->doc .= '<a href="' . exportlink($ID, 'odt', ($REV != '' ? 'rev=' . $REV : '')) . '" title="' . $this->getLang('view') . '">';
97            $renderer->doc .= '<img src="' . DOKU_BASE . 'lib/plugins/odt/odt.png" align="right" alt="' . $this->getLang('view') . '" width="48" height="48" />';
98            $renderer->doc .= '</a>';
99            return true;
100
101        } else { // Extended info
102            // Load config helper if not done yet
103            if ( $this->config == NULL ) {
104                $this->config = plugin_load('helper', 'odt_config');
105            }
106
107            list($info_type, $info_value, $pos) = $data;
108
109            // If it is a config option store it in the meta data
110            // and set the config parameter in the renderer.
111            if ( $this->config->isParam($info_type) ) {
112                if($format == 'odt') {
113                    /** @var renderer_plugin_odt_page $renderer */
114                    $renderer->setConfigParam($info_type, $info_value);
115                } elseif($format == 'metadata') {
116                    if ($this->config->addingToMetaIsAllowed($info_type, $pos)) {
117                        /** @var Doku_Renderer_metadata $renderer */
118                        $renderer->meta['relation']['odt'][$info_type] = $info_value;
119                    }
120                }
121            }
122
123            // Do some more work for the tags which are not just a config parameter setter
124            switch($info_type)
125            {
126                case 'toc': // Insert TOC in exported ODT file
127                    if($format == 'odt') {
128                        /** @var renderer_plugin_odt_page $renderer */
129                        $renderer->render_index('toc', $info_value);
130                    } elseif($format == 'metadata') {
131                        /** @var Doku_Renderer_metadata $renderer */
132                        $renderer->meta['relation']['odt']['toc'] = $info_value;
133                    } elseif($format == 'xhtml') {
134                        $this->insert_index_preview ($renderer, 'toc');
135                    }
136                break;
137                case 'chapter-index': // Insert chapter index in exported ODT file
138                    if($format == 'odt') {
139                        /** @var renderer_plugin_odt_page $renderer */
140                        $renderer->render_index('chapter', $info_value);
141                    } elseif($format == 'xhtml') {
142                        $this->insert_index_preview ($renderer, 'chapter');
143                    }
144                break;
145                case 'disablelinks': // Disable creating links and only show the text instead
146                    if($format == 'odt') {
147                        $renderer->disable_links();
148                    }
149                break;
150                case 'enablelinks': // Re-enable creating links
151                    if($format == 'odt') {
152                        $renderer->enable_links();
153                    }
154                break;
155                case 'page':
156                    if($format == 'odt') {
157                        /** @var renderer_plugin_odt_page $renderer */
158                        $params = explode(',', $info_value);
159                        $format = trim ($params [0]);
160                        $orientation = trim ($params [1]);
161                        for ( $index = 2 ; $index < 6 ; $index++ ) {
162                            if ( empty($params [$index]) ) {
163                                $params [$index] = 2;
164                            }
165                        }
166                        $renderer->setPageFormat($format, $orientation, $params [2], $params [3], $params [4], $params [5]);
167                    }
168                break;
169                case 'format':
170                    if($format == 'odt') {
171                        /** @var renderer_plugin_odt_page $renderer */
172                        $format = trim ($info_value);
173                        $renderer->setPageFormat($format);
174                    }
175                break;
176                case 'orientation':
177                    if($format == 'odt') {
178                        /** @var renderer_plugin_odt_page $renderer */
179                        $orientation = trim ($info_value);
180                        $renderer->setPageFormat(NULL,$orientation);
181                    }
182                break;
183                case 'margin_top':
184                    if($format == 'odt') {
185                        /** @var renderer_plugin_odt_page $renderer */
186                        $margin = trim ($info_value);
187                        $renderer->setPageFormat(NULL,NULL,$margin);
188                    }
189                break;
190                case 'margin_right':
191                    if($format == 'odt') {
192                        /** @var renderer_plugin_odt_page $renderer */
193                        $margin = trim ($info_value);
194                        $renderer->setPageFormat(NULL,NULL,NULL,$margin);
195                    }
196                break;
197                case 'margin_bottom':
198                    if($format == 'odt') {
199                        /** @var renderer_plugin_odt_page $renderer */
200                        $margin = trim ($info_value);
201                        $renderer->setPageFormat(NULL,NULL,NULL,NULL,$margin);
202                    }
203                break;
204                case 'margin_left':
205                    if($format == 'odt') {
206                        /** @var renderer_plugin_odt_page $renderer */
207                        $margin = trim ($info_value);
208                        $renderer->setPageFormat(NULL,NULL,NULL,NULL,NULL,$margin);
209                    }
210                break;
211                case 'templatepage': // Take wiki page content as additional CSS input
212                    if($format == 'odt' || $format == 'xhtml' ) {
213                        if ($this->check_templatepage ($info_value, $format) == true &&
214                            $format == 'odt' ) {
215                            /** @var renderer_plugin_odt_page $renderer */
216                            $renderer->read_templatepage($info_value);
217                        }
218                    }
219                break;
220                case 'frame-open': // Insert/Open ODT frame
221                    if($format == 'odt' ) {
222                        /** @var renderer_plugin_odt_page $renderer */
223                        $this->frame_open($renderer, $info_value);
224                    }
225                break;
226                case 'frame-close': // Close ODT frame
227                    if($format == 'odt' ) {
228                        /** @var renderer_plugin_odt_page $renderer */
229                        $this->frame_close($renderer);
230                    }
231                break;
232            }
233        }
234        return false;
235    }
236
237    /**
238     * Insert a browser preview for an index.
239     *
240     * @param  Doku_Renderer $renderer The current renderer
241     * @param  string        $type     The index type ('toc' or 'chapter)'
242     */
243    function insert_index_preview ($renderer, $type='toc') {
244        if ($this->config->getParam ('index_in_browser') == 'hide') {
245            return;
246        }
247        switch ($type) {
248            case 'toc':
249                $msg = $this->getLang('toc_msg');
250                $reminder = $this->getLang('update_toc_msg');
251            break;
252            case 'chapter':
253                $msg = $this->getLang('chapter_msg');
254                $reminder = $this->getLang('update_chapter_msg');
255            break;
256        }
257        $renderer->doc .= '<p class="index_preview_odt">';
258        $renderer->doc .= '<span id="text" class="index_preview_odt">'.$msg.'</span><br>';
259        $renderer->doc .= '<span id="reminder" class="index_preview_odt">'.$reminder.'</span>';
260        $renderer->doc .= '</p>';
261    }
262
263    /**
264     * Checl existance of the template page and display error
265     * message in case of xhtml rendering.
266     *
267     * @param  string $pagename The page to check
268     * @param  string $format   The render format ('xhtml' or 'odt')
269     */
270    protected function check_templatepage ($pagename, $format) {
271        $exists = false;
272        if (empty($pagename)) {
273            if ($format == 'xhtml') {
274                msg(sprintf("No page specified!", html_wikilink($pagename)), -1);
275            }
276            return (false);
277        }
278        resolve_pageid($INFO['namespace'], $pagename, $exists);
279        if(!$exists) {
280            if ($format == 'xhtml') {
281                msg(sprintf("Page not found!", html_wikilink($pagename)), -1);
282            }
283            return (false);
284        }
285        return (true);
286    }
287
288    /**
289     * Open a frame with a text box.
290     *
291     * @param  Doku_Renderer $renderer The current renderer object
292     * @param  string        $params   Parameters for the frame
293     */
294    protected function frame_open ($renderer, $params) {
295        // Get inline CSS for ODT frame
296        $odt_css = '';
297        if ( preg_match('/odt-css="[^"]+";/', $params, $matches) === 1 ) {
298            $quote = strpos ($matches [0], '"');
299            $temp = substr ($matches [0], $quote+1);
300            $temp = trim ($temp, '";');
301            $odt_css = $temp.';';
302        }
303        $odt_css_id = '';
304        if ( preg_match('/odt-css-id="[^"]+";/', $params, $matches) === 1 ) {
305            $quote = strpos ($matches [0], '"');
306            $temp = substr ($matches [0], $quote+1);
307            $temp = trim ($temp, '";');
308            $odt_css_id = $temp;
309        }
310
311        $properties = array();
312
313        $renderer->getODTPropertiesNew ($properties, NULL, 'id="'.$odt_css_id.'" style="'.$odt_css.'"');
314
315        if (empty($properties ['page'])) {
316            $properties ['anchor-type'] = 'page';
317        }
318        if (empty($properties ['wrap'])) {
319            $properties ['wrap'] = 'run-through';
320        }
321        if (empty($properties ['number-wrapped-paragraphs'])) {
322            $properties ['number-wrapped-paragraphs'] = 'no-limit';
323        }
324        if (empty($properties ['vertical-pos'])) {
325            $properties ['vertical-pos'] = 'from-top';
326        }
327        if (empty($properties ['vertical-rel'])) {
328            $properties ['vertical-rel'] = 'page';
329        }
330        if (empty($properties ['horizontal-pos'])) {
331            $properties ['horizontal-pos'] = 'from-left';
332        }
333        if (empty($properties ['horizontal-rel'])) {
334            $properties ['horizontal-rel'] = 'page';
335        }
336        if (empty($properties ['wrap-influence-on-position'])) {
337            $properties ['wrap-influence-on-position'] = 'once-concurrent';
338        }
339        if (empty($properties ['flow-with-text'])) {
340            $properties ['flow-with-text'] = 'false';
341        }
342        if (empty($properties ['margin-top'])) {
343            $properties ['margin-top'] = '0cm';
344        }
345        if (empty($properties ['margin-right'])) {
346            $properties ['margin-right'] = '0cm';
347        }
348        if (empty($properties ['margin-bottom'])) {
349            $properties ['margin-bottom'] = '0cm';
350        }
351        if (empty($properties ['margin-left'])) {
352            $properties ['margin-left'] = '0cm';
353        }
354        if (empty($properties ['padding-top'])) {
355            $properties ['padding-top'] = '0cm';
356        }
357        if (empty($properties ['padding-right'])) {
358            $properties ['padding-right'] = '0cm';
359        }
360        if (empty($properties ['padding-bottom'])) {
361            $properties ['padding-bottom'] = '0cm';
362        }
363        if (empty($properties ['padding-left'])) {
364            $properties ['padding-left'] = '0cm';
365        }
366        if (empty($properties ['border-top'])) {
367            $properties ['border-top'] = 'none';
368        }
369        if (empty($properties ['border-right'])) {
370            $properties ['border-right'] = 'none';
371        }
372        if (empty($properties ['border-bottom'])) {
373            $properties ['border-bottom'] = 'none';
374        }
375        if (empty($properties ['border-left'])) {
376            $properties ['border-left'] = 'none';
377        }
378        if (empty($properties ['horizontal-align'])) {
379            $properties ['horizontal-align'] = 'left';
380        }
381
382        $renderer->_odtOpenTextBoxUseProperties ($properties);
383        $renderer->p_open();
384    }
385
386    /**
387     * Close a frame with a text box.
388     *
389     * @param  Doku_Renderer $renderer The current renderer object
390     */
391    protected function frame_close ($renderer) {
392        $renderer->p_close();
393        $renderer->_odtCloseTextBox ();
394    }
395}
396