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')); 144*4ae20bfcSternite } else if (substr($this->source_filepath,-4) == ".jpg") { 145*4ae20bfcSternite $this->thumb_engine = new thumb_img_engine($this,$plugin->getConf('thumb_width')); 146a2549de1Sternite } else { 147a2549de1Sternite $this->thumb_engine = new thumb_zip_engine($this,$plugin->getConf('thumb_width'),$plugin->getConf('thumb_paths')); 148a2549de1Sternite } 149a2549de1Sternite } 150a2549de1Sternite 151a2549de1Sternite public function create() { 152a2549de1Sternite if (!$this->thumb_engine) { 153a2549de1Sternite return false; 154a2549de1Sternite } 155a2549de1Sternite 156a2549de1Sternite return $this->thumb_engine->act(); 157a2549de1Sternite } 158a2549de1Sternite 159a2549de1Sternite public function getSourceFilepath() { 160a2549de1Sternite return $this->source_filepath; 161a2549de1Sternite } 162a2549de1Sternite 163a2549de1Sternite protected function getFilename() { 164a2549de1Sternite 165a2549de1Sternite return basename($this->source_filepath) . ".thumb.".$this->thumb_engine->getFileSuffix(); 166a2549de1Sternite } 167a2549de1Sternite 168a2549de1Sternite public function getFilepath() { 169a2549de1Sternite return dirname($this->source_filepath) . DIRECTORY_SEPARATOR . $this->getFilename(); 170a2549de1Sternite } 171a2549de1Sternite 172a2549de1Sternite public function getMediapath() { 173a2549de1Sternite if ($this->source_mediapath !== false) { 174a2549de1Sternite return substr($this->source_mediapath,0,strrpos($this->source_mediapath,':')) . ":" . $this->getFilename(); 175a2549de1Sternite } else { 176a2549de1Sternite return false; 177a2549de1Sternite } 178a2549de1Sternite } 179a2549de1Sternite 180a2549de1Sternite public function getTimestamp() { 181a2549de1Sternite return file_exists($this->getFilepath()) ? filemtime($this->getFilepath()) : false; 182a2549de1Sternite } 183a2549de1Sternite} 184a2549de1Sternite 185a2549de1Sterniteabstract class thumb_engine { 186a2549de1Sternite 187a2549de1Sternite private ?thumbnail $thumbnail = null; 188a2549de1Sternite private int $width; 189a2549de1Sternite 190a2549de1Sternite public function __construct(thumbnail $thumbnail, int $width) { 191a2549de1Sternite $this->thumbnail = $thumbnail; 192a2549de1Sternite $this->width = $width; 193a2549de1Sternite } 194a2549de1Sternite 195a2549de1Sternite protected function getSourceFilepath() { 196a2549de1Sternite return $this->thumbnail->getSourceFilepath(); 197a2549de1Sternite } 198a2549de1Sternite 199a2549de1Sternite protected function getTargetFilepath() { 200a2549de1Sternite return $this->thumbnail->getFilepath(); 201a2549de1Sternite } 202a2549de1Sternite 203a2549de1Sternite protected function getTargetWidth() { 204a2549de1Sternite return $this->width; 205a2549de1Sternite } 206a2549de1Sternite 207a2549de1Sternite public function act() { 208a2549de1Sternite if ($this->act_internal()) { 209a2549de1Sternite // 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). 210e80c5674Sternite if (filemtime($this->getSourceFilepath()) !== filemtime($this->getTargetFilepath())) { 211a2549de1Sternite touch($this->getTargetFilepath(), filemtime($this->getSourceFilepath())); 212e80c5674Sternite } 213a2549de1Sternite return true; 214a2549de1Sternite } 215a2549de1Sternite return false; 216a2549de1Sternite } 217a2549de1Sternite 218e80c5674Sternite // Checks if a thumbnail file for the current file version has already been created 219e80c5674Sternite protected function thumb_needs_update() { 220*4ae20bfcSternite return !file_exists($this->getTargetFilepath()) || filemtime($this->getTargetFilepath()) !== filemtime($this->getSourceFilepath()); 221e80c5674Sternite } 222e80c5674Sternite 223a2549de1Sternite public abstract function act_internal(); 224a2549de1Sternite 225a2549de1Sternite public abstract function getFileSuffix(); 226a2549de1Sternite} 227a2549de1Sternite 228a2549de1Sterniteclass thumb_pdf_engine extends thumb_engine { 229a2549de1Sternite 230a2549de1Sternite public function getFileSuffix() { 231a2549de1Sternite return "jpg"; 232a2549de1Sternite } 233a2549de1Sternite 234a2549de1Sternite public function act_internal() { 235e80c5674Sternite if ($this->thumb_needs_update()) { 236a2549de1Sternite $im = new imagick( $this->getSourceFilepath()."[0]" ); 237a2549de1Sternite $im->setImageColorspace(255); 238a2549de1Sternite $im->setResolution(300, 300); 239a2549de1Sternite $im->setCompressionQuality(95); 240a2549de1Sternite $im->setImageFormat('jpeg'); 241a2549de1Sternite //$im->resizeImage(substr($this->getConf('thumb_width'),-2),0,imagick::FILTER_LANCZOS,0.9); 242a2549de1Sternite $im->writeImage($this->getTargetFilepath()); 243a2549de1Sternite $im->clear(); 244a2549de1Sternite $im->destroy(); 245a2549de1Sternite 246a2549de1Sternite return true; 247e80c5674Sternite } else { 248e80c5674Sternite return true; 249e80c5674Sternite } 250a2549de1Sternite } 251a2549de1Sternite} 252a2549de1Sternite 253*4ae20bfcSterniteclass thumb_img_engine extends thumb_engine { 254*4ae20bfcSternite 255*4ae20bfcSternite public function getFileSuffix() { 256*4ae20bfcSternite $this->file_suffix = substr(strrchr($this->getSourceFilepath(),'.'),1); 257*4ae20bfcSternite return $this->file_suffix; 258*4ae20bfcSternite } 259*4ae20bfcSternite 260*4ae20bfcSternite public function act_internal() { 261*4ae20bfcSternite if ($this->thumb_needs_update()) { 262*4ae20bfcSternite $im = new imagick( $this->getSourceFilepath() ); 263*4ae20bfcSternite //TODO: consider height? 264*4ae20bfcSternite $im->thumbnailImage($this->getTargetWidth(),$this->getTargetWidth(),true,false); 265*4ae20bfcSternite $im->writeImage($this->getTargetFilepath()); 266*4ae20bfcSternite $im->clear(); 267*4ae20bfcSternite $im->destroy(); 268*4ae20bfcSternite 269*4ae20bfcSternite return true; 270*4ae20bfcSternite } else { 271*4ae20bfcSternite return true; 272*4ae20bfcSternite } 273*4ae20bfcSternite } 274*4ae20bfcSternite} 275*4ae20bfcSternite 276a2549de1Sterniteclass thumb_zip_engine extends thumb_engine { 277a2549de1Sternite 278a2549de1Sternite private array $thumb_paths; 279a2549de1Sternite private $file_suffix = ""; 280a2549de1Sternite 281a2549de1Sternite public function __construct(thumbnail $thumbnail, int $width, array $thumb_paths) { 282a2549de1Sternite parent::__construct($thumbnail,$width); 283a2549de1Sternite $this->thumb_paths = $thumb_paths; 284a2549de1Sternite } 285a2549de1Sternite 286a2549de1Sternite public function getFileSuffix() { 287a2549de1Sternite return $this->file_suffix; 288a2549de1Sternite } 289a2549de1Sternite 290a2549de1Sternite public function act_internal() { 291e80c5674Sternite 292a2549de1Sternite $zip = new ZipArchive; 293a2549de1Sternite if ($zip->open($this->getSourceFilepath()) !== true) { 294a2549de1Sternite // file is no zip or cannot be opened 295a2549de1Sternite return false; 296a2549de1Sternite } 297a2549de1Sternite 298a2549de1Sternite // The media file exists and acts as a zip file! 299a2549de1Sternite 300a2549de1Sternite // Check all possible paths (configured in configuration key 'thumb_paths') if there is a file available 301a2549de1Sternite foreach($this->thumb_paths as $thumbnail_path) { 302a2549de1Sternite $this->file_suffix = substr(strrchr($thumbnail_path,'.'),1); 303a2549de1Sternite 304a2549de1Sternite if ($zip->locateName($thumbnail_path) !== false) { 305a2549de1Sternite 306e80c5674Sternite if (!$this->thumb_needs_update()) { 307a2549de1Sternite return true; 308a2549de1Sternite } 309a2549de1Sternite 310a2549de1Sternite // Get the thumbnail file! 311a2549de1Sternite $fp = $zip->getStream($thumbnail_path); 312a2549de1Sternite if(!$fp) { 313a2549de1Sternite return false; 314a2549de1Sternite } 315a2549de1Sternite 316a2549de1Sternite $thumbnaildata = ''; 317a2549de1Sternite while (!feof($fp)) { 318a2549de1Sternite $thumbnaildata .= fread($fp, 8192); 319a2549de1Sternite } 320a2549de1Sternite 321a2549de1Sternite fclose($fp); 322a2549de1Sternite 323a2549de1Sternite // Write thumbnail file to media folder 324a2549de1Sternite file_put_contents($this->getTargetFilepath(), $thumbnaildata); 325a2549de1Sternite 326a2549de1Sternite return true; 327a2549de1Sternite } 328a2549de1Sternite } 329a2549de1Sternite 330a2549de1Sternite return true; 331a2549de1Sternite } 332a2549de1Sternite}