1bbf77476Sternite<?php 2bbf77476Sternite/** 3bbf77476Sternite * DokuWiki Plugin mediathumbnails (Syntax Component) 4bbf77476Sternite * 5bbf77476Sternite * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6bbf77476Sternite * @author Thomas Schäfer <thomas.schaefer@itschert.net> 7bbf77476Sternite */ 8bbf77476Sternite 9bbf77476Sternite// must be run within Dokuwiki 10bbf77476Sterniteif (!defined('DOKU_INC')) { 11bbf77476Sternite die(); 12bbf77476Sternite} 13bbf77476Sternite 14bbf77476Sterniteclass syntax_plugin_mediathumbnails extends DokuWiki_Syntax_Plugin { 15bbf77476Sternite 16bbf77476Sternite /** 17bbf77476Sternite * @return string Syntax mode type 18bbf77476Sternite */ 19bbf77476Sternite public function getType() 20bbf77476Sternite { 21bbf77476Sternite return 'substition'; 22bbf77476Sternite } 23bbf77476Sternite 24bbf77476Sternite /** 25bbf77476Sternite * @return string Paragraph type 26bbf77476Sternite */ 27bbf77476Sternite public function getPType() 28bbf77476Sternite { 29bbf77476Sternite return 'normal'; 30bbf77476Sternite } 31bbf77476Sternite 32bbf77476Sternite /** 33bbf77476Sternite * @return int Sort order - Low numbers go before high numbers 34bbf77476Sternite */ 35bbf77476Sternite public function getSort() 36bbf77476Sternite { 37bbf77476Sternite return 1; 38bbf77476Sternite } 39bbf77476Sternite 40bbf77476Sternite /** 41bbf77476Sternite * Connect lookup pattern to lexer. 42bbf77476Sternite * 43bbf77476Sternite * @param string $mode Parser mode 44bbf77476Sternite */ 45bbf77476Sternite public function connectTo($mode) 46bbf77476Sternite { 47bbf77476Sternite $this->Lexer->addSpecialPattern("{{thumbnail>.+?}}", $mode, substr(get_class($this), 7)); 48bbf77476Sternite } 49bbf77476Sternite 50bbf77476Sternite /** 51bbf77476Sternite * Handle matches of the mediathumbnails syntax 52bbf77476Sternite * 53bbf77476Sternite * @param string $match The match of the syntax 54bbf77476Sternite * @param int $state The state of the handler 55bbf77476Sternite * @param int $pos The position in the document 56bbf77476Sternite * @param Doku_Handler $handler The handler 57bbf77476Sternite * 58bbf77476Sternite * @return array Data for the renderer 59bbf77476Sternite */ 60bbf77476Sternite public function handle($match, $state, $pos, Doku_Handler $handler) 61bbf77476Sternite { 62da77d72dSternite // Locate the given media file and check if it can be opened as zip 6332095c04Sternite $mediapath_file = substr($match, 12, -2); //strip markup 64bbf77476Sternite 65a2549de1Sternite $thumb = new thumbnail($mediapath_file,$this); 66a2549de1Sternite if ($thumb->create()) { 67a2549de1Sternite return array($mediapath_file,$thumb->getMediapath()); 68da77d72dSternite } 69bbf77476Sternite 70da77d72dSternite return array($mediapath_file); 71bbf77476Sternite } 72bbf77476Sternite 73bbf77476Sternite /** 74bbf77476Sternite * Render xhtml output or metadata 75bbf77476Sternite * 76bbf77476Sternite * @param string $mode Renderer mode (supported modes: xhtml) 77bbf77476Sternite * @param Doku_Renderer $renderer The renderer 78bbf77476Sternite * @param array $data The data from the handler() function 79bbf77476Sternite * 80bbf77476Sternite * @return bool If rendering was successful. 81bbf77476Sternite */ 82bbf77476Sternite public function render($mode, Doku_Renderer $renderer, $data) 83bbf77476Sternite { 84967904f1Sternite list ($mediapath_file, $mediapath_thumbnail) = $data; 85bbf77476Sternite 86bbf77476Sternite if ($mode == 'xhtml') { 87bbf77476Sternite 88da77d72dSternite // check if a thumbnail file was found 89da77d72dSternite if (!$mediapath_thumbnail) { 902f64379eSternite if ($this->getConf('show_missing_thumb_error')) { 91da77d72dSternite $renderer->doc .= trim($this->getConf('no_thumb_error_message')) . " " . $mediapath_file; 92da77d72dSternite return true; 93da77d72dSternite } else { 94da77d72dSternite return false; 95da77d72dSternite } 96da77d72dSternite } 97da77d72dSternite 98bbf77476Sternite $src = ml($mediapath_thumbnail,array()); 99bbf77476Sternite 100bbf77476Sternite $i = array(); 101b8b45234Sternite $i['width'] = $this->getConf('thumb_width'); 1023ac8d5b9Sternite //$i['height'] = ''; 1033ac8d5b9Sternite $i['title'] = $mediapath_file; 104bbf77476Sternite $i['class'] = 'tn'; 105bbf77476Sternite $iatt = buildAttributes($i); 106bbf77476Sternite 1076bc8b42dSternite $renderer->doc .= ($this->getConf('link_to_media_file') ? '<a href="/lib/exe/fetch.php?media=' . $mediapath_file . '">' : '') . 10832095c04Sternite '<img src="'.$src.'" '.$iatt.' />' . 1096bc8b42dSternite ($this->getConf('link_to_media_file') ? '</a>' : ''); 110bbf77476Sternite return true; 111bbf77476Sternite 112bbf77476Sternite } elseif ($mode == 'odt') { 113bbf77476Sternite 114bbf77476Sternite // TODO: yet to implement 115bbf77476Sternite $renderer->cdata(""); 116bbf77476Sternite return true; 117bbf77476Sternite 118bbf77476Sternite } 119bbf77476Sternite 120bbf77476Sternite return false; 121bbf77476Sternite } 122bbf77476Sternite} 123bbf77476Sternite 124a2549de1Sterniteclass thumbnail { 125a2549de1Sternite 126a2549de1Sternite private $source_filepath; 127a2549de1Sternite private $source_mediapath; 128a2549de1Sternite private ?thumb_engine $thumb_engine = null; 129a2549de1Sternite 130a2549de1Sternite public function __construct(string $source_filepath, DokuWiki_Syntax_Plugin $plugin, bool $ismediapath = true) { 131a2549de1Sternite 132a2549de1Sternite if ($ismediapath) { 133a2549de1Sternite $this->source_mediapath = $source_filepath; 134a2549de1Sternite $this->source_filepath = mediaFN($source_filepath); 135a2549de1Sternite } else { 136a2549de1Sternite $this->source_mediapath = false; 137a2549de1Sternite $this->source_filepath = $source_filepath; 138a2549de1Sternite } 139a2549de1Sternite 140a2549de1Sternite // Now attach the correct thumb_engine for the file type of the source file 141a2549de1Sternite //TODO: check for extension "fileinfo", then check for MIME type: if (mime_content_type($filepath_local_file) == "application/pdf") { 142a2549de1Sternite if (substr($this->source_filepath,-4) == ".pdf") { 143a2549de1Sternite $this->thumb_engine = new thumb_pdf_engine($this,$plugin->getConf('thumb_width')); 144a2549de1Sternite } else { 145a2549de1Sternite $this->thumb_engine = new thumb_zip_engine($this,$plugin->getConf('thumb_width'),$plugin->getConf('thumb_paths')); 146a2549de1Sternite } 147a2549de1Sternite } 148a2549de1Sternite 149a2549de1Sternite public function create() { 150a2549de1Sternite if (!$this->thumb_engine) { 151a2549de1Sternite return false; 152a2549de1Sternite } 153a2549de1Sternite 154a2549de1Sternite return $this->thumb_engine->act(); 155a2549de1Sternite } 156a2549de1Sternite 157a2549de1Sternite public function getSourceFilepath() { 158a2549de1Sternite return $this->source_filepath; 159a2549de1Sternite } 160a2549de1Sternite 161a2549de1Sternite protected function getFilename() { 162a2549de1Sternite 163a2549de1Sternite return basename($this->source_filepath) . ".thumb.".$this->thumb_engine->getFileSuffix(); 164a2549de1Sternite } 165a2549de1Sternite 166a2549de1Sternite public function getFilepath() { 167a2549de1Sternite return dirname($this->source_filepath) . DIRECTORY_SEPARATOR . $this->getFilename(); 168a2549de1Sternite } 169a2549de1Sternite 170a2549de1Sternite public function getMediapath() { 171a2549de1Sternite if ($this->source_mediapath !== false) { 172a2549de1Sternite return substr($this->source_mediapath,0,strrpos($this->source_mediapath,':')) . ":" . $this->getFilename(); 173a2549de1Sternite } else { 174a2549de1Sternite return false; 175a2549de1Sternite } 176a2549de1Sternite } 177a2549de1Sternite 178a2549de1Sternite public function getTimestamp() { 179a2549de1Sternite return file_exists($this->getFilepath()) ? filemtime($this->getFilepath()) : false; 180a2549de1Sternite } 181a2549de1Sternite} 182a2549de1Sternite 183a2549de1Sterniteabstract class thumb_engine { 184a2549de1Sternite 185a2549de1Sternite private ?thumbnail $thumbnail = null; 186a2549de1Sternite private int $width; 187a2549de1Sternite 188a2549de1Sternite public function __construct(thumbnail $thumbnail, int $width) { 189a2549de1Sternite $this->thumbnail = $thumbnail; 190a2549de1Sternite $this->width = $width; 191a2549de1Sternite } 192a2549de1Sternite 193a2549de1Sternite protected function getSourceFilepath() { 194a2549de1Sternite return $this->thumbnail->getSourceFilepath(); 195a2549de1Sternite } 196a2549de1Sternite 197a2549de1Sternite protected function getTargetFilepath() { 198a2549de1Sternite return $this->thumbnail->getFilepath(); 199a2549de1Sternite } 200a2549de1Sternite 201a2549de1Sternite protected function getTargetWidth() { 202a2549de1Sternite return $this->width; 203a2549de1Sternite } 204a2549de1Sternite 205a2549de1Sternite public function act() { 206a2549de1Sternite if ($this->act_internal()) { 207a2549de1Sternite // Set timestamp to the source file's timestamp (this is used to check in later passes if the file already exists in the correct version). 208*e80c5674Sternite if (filemtime($this->getSourceFilepath()) !== filemtime($this->getTargetFilepath())) { 209a2549de1Sternite touch($this->getTargetFilepath(), filemtime($this->getSourceFilepath())); 210*e80c5674Sternite } 211a2549de1Sternite return true; 212a2549de1Sternite } 213a2549de1Sternite return false; 214a2549de1Sternite } 215a2549de1Sternite 216*e80c5674Sternite // Checks if a thumbnail file for the current file version has already been created 217*e80c5674Sternite protected function thumb_needs_update() { 218*e80c5674Sternite return file_exists($this->getTargetFilepath()) && filemtime($this->getTargetFilepath()) !== filemtime($this->getSourceFilepath()); 219*e80c5674Sternite } 220*e80c5674Sternite 221a2549de1Sternite public abstract function act_internal(); 222a2549de1Sternite 223a2549de1Sternite public abstract function getFileSuffix(); 224a2549de1Sternite} 225a2549de1Sternite 226a2549de1Sterniteclass thumb_pdf_engine extends thumb_engine { 227a2549de1Sternite 228a2549de1Sternite public function getFileSuffix() { 229a2549de1Sternite return "jpg"; 230a2549de1Sternite } 231a2549de1Sternite 232a2549de1Sternite public function act_internal() { 233*e80c5674Sternite if ($this->thumb_needs_update()) { 234a2549de1Sternite $im = new imagick( $this->getSourceFilepath()."[0]" ); 235a2549de1Sternite $im->setImageColorspace(255); 236a2549de1Sternite $im->setResolution(300, 300); 237a2549de1Sternite $im->setCompressionQuality(95); 238a2549de1Sternite $im->setImageFormat('jpeg'); 239a2549de1Sternite //$im->resizeImage(substr($this->getConf('thumb_width'),-2),0,imagick::FILTER_LANCZOS,0.9); 240a2549de1Sternite $im->writeImage($this->getTargetFilepath()); 241a2549de1Sternite $im->clear(); 242a2549de1Sternite $im->destroy(); 243a2549de1Sternite 244a2549de1Sternite return true; 245*e80c5674Sternite } else { 246*e80c5674Sternite return true; 247*e80c5674Sternite } 248a2549de1Sternite } 249a2549de1Sternite} 250a2549de1Sternite 251a2549de1Sterniteclass thumb_zip_engine extends thumb_engine { 252a2549de1Sternite 253a2549de1Sternite private array $thumb_paths; 254a2549de1Sternite private $file_suffix = ""; 255a2549de1Sternite 256a2549de1Sternite public function __construct(thumbnail $thumbnail, int $width, array $thumb_paths) { 257a2549de1Sternite parent::__construct($thumbnail,$width); 258a2549de1Sternite $this->thumb_paths = $thumb_paths; 259a2549de1Sternite } 260a2549de1Sternite 261a2549de1Sternite public function getFileSuffix() { 262a2549de1Sternite return $this->file_suffix; 263a2549de1Sternite } 264a2549de1Sternite 265a2549de1Sternite public function act_internal() { 266*e80c5674Sternite 267a2549de1Sternite $zip = new ZipArchive; 268a2549de1Sternite if ($zip->open($this->getSourceFilepath()) !== true) { 269a2549de1Sternite // file is no zip or cannot be opened 270a2549de1Sternite return false; 271a2549de1Sternite } 272a2549de1Sternite 273a2549de1Sternite // The media file exists and acts as a zip file! 274a2549de1Sternite 275a2549de1Sternite // Check all possible paths (configured in configuration key 'thumb_paths') if there is a file available 276a2549de1Sternite foreach($this->thumb_paths as $thumbnail_path) { 277a2549de1Sternite $this->file_suffix = substr(strrchr($thumbnail_path,'.'),1); 278a2549de1Sternite 279a2549de1Sternite if ($zip->locateName($thumbnail_path) !== false) { 280a2549de1Sternite 281*e80c5674Sternite if (!$this->thumb_needs_update()) { 282a2549de1Sternite return true; 283a2549de1Sternite } 284a2549de1Sternite 285a2549de1Sternite // Get the thumbnail file! 286a2549de1Sternite $fp = $zip->getStream($thumbnail_path); 287a2549de1Sternite if(!$fp) { 288a2549de1Sternite return false; 289a2549de1Sternite } 290a2549de1Sternite 291a2549de1Sternite $thumbnaildata = ''; 292a2549de1Sternite while (!feof($fp)) { 293a2549de1Sternite $thumbnaildata .= fread($fp, 8192); 294a2549de1Sternite } 295a2549de1Sternite 296a2549de1Sternite fclose($fp); 297a2549de1Sternite 298a2549de1Sternite // Write thumbnail file to media folder 299a2549de1Sternite file_put_contents($this->getTargetFilepath(), $thumbnaildata); 300a2549de1Sternite 301a2549de1Sternite return true; 302a2549de1Sternite } 303a2549de1Sternite } 304a2549de1Sternite 305a2549de1Sternite return true; 306a2549de1Sternite } 307a2549de1Sternite}