1<?php
2/**
3 * DokuWiki Plugin structodt (Action Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Szymon Olewniczak <it@rid.pl>
7 */
8
9use dokuwiki\File\MediaResolver;
10use dokuwiki\plugin\struct\meta\Schema;
11use dokuwiki\plugin\struct\meta\Value;
12use splitbrain\PHPArchive\Zip;
13
14// must be run within Dokuwiki
15if(!defined('DOKU_INC')) die();
16
17class action_plugin_structodt extends DokuWiki_Action_Plugin {
18    /**
19     * Registers a callback function for a given event
20     *
21     * @param Doku_Event_Handler $controller DokuWiki's event controller object
22     * @return void
23     */
24    public function register(Doku_Event_Handler $controller) {
25        $controller->register_hook('PLUGIN_STRUCT_CONFIGPARSER_UNKNOWNKEY', 'BEFORE', $this, 'handle_struct_configparser');
26        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_action_act_prerpocess');
27    }
28
29    /**
30     * Add "template" config key
31     *
32     * @param Doku_Event $event  event object by reference
33     * @param mixed      $param  [the parameters passed as fifth argument to register_hook() when this
34     *                           handler was registered]
35     * @return void
36     */
37
38    public function handle_struct_configparser(Doku_Event &$event, $param) {
39        $keys = ['template', 'pdf', 'hideform', 'filename'];
40
41        $key = $event->data['key'];
42        $val = trim($event->data['val']);
43
44        if (!in_array($key, $keys)) return;
45
46        $event->preventDefault();
47        $event->stopPropagation();
48
49        switch ($key) {
50            case 'template':
51                $event->data['config'][$key] = array_map('trim', explode(',', $val));
52                break;
53            case 'pdf':
54                $event->data['config'][$key] = (bool) $val;
55                if ($event->data['config']) {
56                    //check for "unoconv"
57                    $val = shell_exec('command -v unoconv');
58                    if (empty($val)) {
59                        msg('Cannot locate "unoconv". Falling back to ODT mode.', 0);
60                        $event->data['config'][$key] = false;
61                        break;
62                    }
63                    //check for "ghostscript"
64                    $val = shell_exec('command -v ghostscript');
65                    if (empty($val)) {
66                        msg('Cannot locate "ghostscript". Falling back to ODT mode.', 0);
67                        $event->data['config'][$key] = false;
68                        break;
69                    }
70                }
71                break;
72            case 'hideform':
73                $event->data['config'][$key] = (bool) $val;
74                break;
75            case 'filename':
76                $event->data['config'][$key] = trim($val);
77                break;
78        }
79    }
80
81    /**
82     * Handle odt export
83     *
84     * @param Doku_Event $event event object by reference
85     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
86     *                           handler was registered]
87     * @return void
88     * @throws \splitbrain\PHPArchive\ArchiveIOException
89     * @throws \splitbrain\PHPArchive\FileInfoException
90     */
91
92    public function handle_action_act_prerpocess(Doku_Event &$event, $param) {
93        global $INPUT;
94        if ($event->data != 'structodt') return;
95
96        $method = 'action_' . $INPUT->str('action');
97        if (method_exists($this, $method)) {
98            call_user_func([$this, $method]);
99        }
100    }
101
102    protected function render_single($row, $templates, $ext='pdf') {
103        global $ID;
104        /**
105         * @var \helper_plugin_structodt
106         */
107        $helper = plugin_load('helper', 'structodt');
108
109        $rendered_pages = [];
110        try {
111            foreach ($templates as $template) {
112                $template = $helper->rowTemplate($row, $template);
113                $resolver = new MediaResolver($ID);
114                $template = $resolver->resolveId($template);
115                if ($template != '' && media_exists($template, '', false)) {
116                    $mimetype = mimetype($template);
117                    // pdf templates are available only in pdf output format
118                    if ($mimetype[0] == 'pdf' && $ext == 'pdf') {
119                        $rendered_page = $helper->tmpFileName('pdf');
120                        copy(mediaFN($template), $rendered_page);
121                        $rendered_pages[] = $rendered_page;
122                    } elseif ($mimetype[0] == 'odt') {
123                        $method = 'render' . strtoupper($ext);
124                        $rendered_pages[] = $helper->$method($template, $row);
125                    } else {
126                        throw new \Exception('unknown template file format');
127                    }
128                }
129            }
130            if (count($rendered_pages) > 1) {
131                $tmp_file = $helper->concatenate($rendered_pages);
132                foreach ($rendered_pages as $page) {
133                    @unlink($page);
134                }
135            } else {
136                $tmp_file = $rendered_pages[0];
137            }
138        } catch (\Exception $e) {
139            foreach ($rendered_pages as $page) {
140                @unlink($page);
141            }
142            msg($e->getMessage(), -1);
143        }
144        return $tmp_file;
145    }
146
147    /**
148     * Render file
149     */
150    public function action_render() {
151        global $INPUT;
152        $extensions = ['pdf', 'odt'];
153
154        /**
155         * @var \helper_plugin_structodt
156         */
157        $helper = plugin_load('helper', 'structodt');
158
159        $templates = json_decode($INPUT->str('template'));
160        $ext = $INPUT->str('filetype');
161        if (!in_array($ext, $extensions)) {
162            msg("Unknown file extension: $ext. Avaliable extensions: " . implode(', ', $extensions), -1);
163            return false;
164        }
165        if (count($templates) > 1 && $ext != 'pdf') {
166            msg("Multiple templates are available only for pdf format.", -1);
167            return false;
168        }
169
170        $schema = $INPUT->str('schema');
171        $pid = $INPUT->str('pid');
172        $rev = $INPUT->str('rev');
173        $rid = $INPUT->str('rid');
174        $filename = $INPUT->str('filename');
175
176        try {
177            $row = $helper->getRow($schema, $pid, $rev, $rid);
178            if (is_null($row)) {
179                throw new \Exception("Row with id: $pid doesn't exists");
180            }
181            $tmp_file = $this->render_single($row, $templates, $ext);
182            if (empty($filename)) {
183                $filename = empty($pid) ? $rid : noNS($pid);
184            } else {
185                $filename = $helper->rowTemplate($row, $filename);
186            }
187        } catch (\Exception $e) {
188            msg($e->getMessage(), -1);
189            return false;
190        }
191        $helper->sendFile($tmp_file, $filename, $ext);
192        @unlink($tmp_file);
193        exit();
194    }
195
196    /**
197     * Render all files as single PDF or ZIP archive
198     */
199    public function action_renderAll() {
200        global $INPUT;
201
202        /**
203         * @var \helper_plugin_structodt
204         */
205        $helper = plugin_load('helper', 'structodt');
206
207        $template_string = htmlspecialchars_decode($INPUT->str('template_string'));
208        $templates = json_decode($template_string);
209        $schemas = $INPUT->arr('schema');
210        $filter = $INPUT->arr('filter');
211        $format = $INPUT->str('format', 'zip');
212        $filename = $INPUT->str('filename');
213
214        /** @var Schema $first_schema */
215        $rows = $helper->getRows($schemas, $first_schema, $filter);
216        $files = [];
217        /** @var Value $row */
218        $i = 1;
219        foreach ($rows as $row) {
220            $tmp_file = $this->render_single($row, $templates);
221            $tmp_filename = empty($filename) ? $i++ : $helper->rowTemplate($row, $filename);
222            $files[$tmp_filename] = $tmp_file;
223        }
224
225        //join files
226        try {
227            if ($format == 'pdf') {
228                $tmp_file = $helper->concatenate($files);
229            } elseif ($format == 'zip') {
230                $tmp_file = $helper->tmpFileName('zip');
231                $archive = new Zip();
232                $archive->create($tmp_file);
233                foreach ($files as $filename => $file) {
234                    $archive->addFile($file, $filename . '.pdf');
235                }
236                $archive->close();
237            }
238        } catch (\Exception $e) {
239            msg($e->getMessage(), -1);
240            return false;
241        } finally {
242            foreach ($files as $file) {
243                @unlink($file);
244            }
245        }
246        $filename = $first_schema->getTranslatedLabel();
247        $helper->sendFile($tmp_file, $filename, $format);
248        @unlink($tmp_file);
249        exit();
250    }
251}
252
253// vim:ts=4:sw=4:et:
254