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