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 65*a2549de1Sternite $thumb = new thumbnail($mediapath_file,$this); 66*a2549de1Sternite if ($thumb->create()) { 67*a2549de1Sternite 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 124*a2549de1Sterniteclass thumbnail { 125*a2549de1Sternite 126*a2549de1Sternite private $source_filepath; 127*a2549de1Sternite private $source_mediapath; 128*a2549de1Sternite private ?thumb_engine $thumb_engine = null; 129*a2549de1Sternite 130*a2549de1Sternite public function __construct(string $source_filepath, DokuWiki_Syntax_Plugin $plugin, bool $ismediapath = true) { 131*a2549de1Sternite 132*a2549de1Sternite if ($ismediapath) { 133*a2549de1Sternite $this->source_mediapath = $source_filepath; 134*a2549de1Sternite $this->source_filepath = mediaFN($source_filepath); 135*a2549de1Sternite } else { 136*a2549de1Sternite $this->source_mediapath = false; 137*a2549de1Sternite $this->source_filepath = $source_filepath; 138*a2549de1Sternite } 139*a2549de1Sternite 140*a2549de1Sternite // Now attach the correct thumb_engine for the file type of the source file 141*a2549de1Sternite //TODO: check for extension "fileinfo", then check for MIME type: if (mime_content_type($filepath_local_file) == "application/pdf") { 142*a2549de1Sternite if (substr($this->source_filepath,-4) == ".pdf") { 143*a2549de1Sternite $this->thumb_engine = new thumb_pdf_engine($this,$plugin->getConf('thumb_width')); 144*a2549de1Sternite } else { 145*a2549de1Sternite $this->thumb_engine = new thumb_zip_engine($this,$plugin->getConf('thumb_width'),$plugin->getConf('thumb_paths')); 146*a2549de1Sternite } 147*a2549de1Sternite } 148*a2549de1Sternite 149*a2549de1Sternite public function create() { 150*a2549de1Sternite if (!$this->thumb_engine) { 151*a2549de1Sternite return false; 152*a2549de1Sternite } 153*a2549de1Sternite 154*a2549de1Sternite return $this->thumb_engine->act(); 155*a2549de1Sternite } 156*a2549de1Sternite 157*a2549de1Sternite public function getSourceFilepath() { 158*a2549de1Sternite return $this->source_filepath; 159*a2549de1Sternite } 160*a2549de1Sternite 161*a2549de1Sternite protected function getFilename() { 162*a2549de1Sternite 163*a2549de1Sternite return basename($this->source_filepath) . ".thumb.".$this->thumb_engine->getFileSuffix(); 164*a2549de1Sternite } 165*a2549de1Sternite 166*a2549de1Sternite public function getFilepath() { 167*a2549de1Sternite return dirname($this->source_filepath) . DIRECTORY_SEPARATOR . $this->getFilename(); 168*a2549de1Sternite } 169*a2549de1Sternite 170*a2549de1Sternite public function getMediapath() { 171*a2549de1Sternite if ($this->source_mediapath !== false) { 172*a2549de1Sternite return substr($this->source_mediapath,0,strrpos($this->source_mediapath,':')) . ":" . $this->getFilename(); 173*a2549de1Sternite } else { 174*a2549de1Sternite return false; 175*a2549de1Sternite } 176*a2549de1Sternite } 177*a2549de1Sternite 178*a2549de1Sternite public function getTimestamp() { 179*a2549de1Sternite return file_exists($this->getFilepath()) ? filemtime($this->getFilepath()) : false; 180*a2549de1Sternite } 181*a2549de1Sternite} 182*a2549de1Sternite 183*a2549de1Sterniteabstract class thumb_engine { 184*a2549de1Sternite 185*a2549de1Sternite private ?thumbnail $thumbnail = null; 186*a2549de1Sternite private int $width; 187*a2549de1Sternite 188*a2549de1Sternite public function __construct(thumbnail $thumbnail, int $width) { 189*a2549de1Sternite $this->thumbnail = $thumbnail; 190*a2549de1Sternite $this->width = $width; 191*a2549de1Sternite } 192*a2549de1Sternite 193*a2549de1Sternite protected function getSourceFilepath() { 194*a2549de1Sternite return $this->thumbnail->getSourceFilepath(); 195*a2549de1Sternite } 196*a2549de1Sternite 197*a2549de1Sternite protected function getTargetFilepath() { 198*a2549de1Sternite return $this->thumbnail->getFilepath(); 199*a2549de1Sternite } 200*a2549de1Sternite 201*a2549de1Sternite protected function getTargetWidth() { 202*a2549de1Sternite return $this->width; 203*a2549de1Sternite } 204*a2549de1Sternite 205*a2549de1Sternite public function act() { 206*a2549de1Sternite if ($this->act_internal()) { 207*a2549de1Sternite // 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*a2549de1Sternite touch($this->getTargetFilepath(), filemtime($this->getSourceFilepath())); 209*a2549de1Sternite return true; 210*a2549de1Sternite } 211*a2549de1Sternite return false; 212*a2549de1Sternite } 213*a2549de1Sternite 214*a2549de1Sternite public abstract function act_internal(); 215*a2549de1Sternite 216*a2549de1Sternite public abstract function getFileSuffix(); 217*a2549de1Sternite} 218*a2549de1Sternite 219*a2549de1Sterniteclass thumb_pdf_engine extends thumb_engine { 220*a2549de1Sternite 221*a2549de1Sternite public function getFileSuffix() { 222*a2549de1Sternite return "jpg"; 223*a2549de1Sternite } 224*a2549de1Sternite 225*a2549de1Sternite public function act_internal() { 226*a2549de1Sternite $im = new imagick( $this->getSourceFilepath()."[0]" ); 227*a2549de1Sternite $im->setImageColorspace(255); 228*a2549de1Sternite $im->setResolution(300, 300); 229*a2549de1Sternite $im->setCompressionQuality(95); 230*a2549de1Sternite $im->setImageFormat('jpeg'); 231*a2549de1Sternite //$im->resizeImage(substr($this->getConf('thumb_width'),-2),0,imagick::FILTER_LANCZOS,0.9); 232*a2549de1Sternite $im->writeImage($this->getTargetFilepath()); 233*a2549de1Sternite $im->clear(); 234*a2549de1Sternite $im->destroy(); 235*a2549de1Sternite 236*a2549de1Sternite return true; 237*a2549de1Sternite } 238*a2549de1Sternite} 239*a2549de1Sternite 240*a2549de1Sterniteclass thumb_zip_engine extends thumb_engine { 241*a2549de1Sternite 242*a2549de1Sternite private array $thumb_paths; 243*a2549de1Sternite private $file_suffix = ""; 244*a2549de1Sternite 245*a2549de1Sternite public function __construct(thumbnail $thumbnail, int $width, array $thumb_paths) { 246*a2549de1Sternite parent::__construct($thumbnail,$width); 247*a2549de1Sternite $this->thumb_paths = $thumb_paths; 248*a2549de1Sternite } 249*a2549de1Sternite 250*a2549de1Sternite public function getFileSuffix() { 251*a2549de1Sternite return $this->file_suffix; 252*a2549de1Sternite } 253*a2549de1Sternite 254*a2549de1Sternite public function act_internal() { 255*a2549de1Sternite $zip = new ZipArchive; 256*a2549de1Sternite if ($zip->open($this->getSourceFilepath()) !== true) { 257*a2549de1Sternite // file is no zip or cannot be opened 258*a2549de1Sternite return false; 259*a2549de1Sternite } 260*a2549de1Sternite $timestamp_local_file = filemtime($this->getSourceFilepath()); 261*a2549de1Sternite 262*a2549de1Sternite // The media file exists and acts as a zip file! 263*a2549de1Sternite 264*a2549de1Sternite // Check all possible paths (configured in configuration key 'thumb_paths') if there is a file available 265*a2549de1Sternite foreach($this->thumb_paths as $thumbnail_path) { 266*a2549de1Sternite $this->file_suffix = substr(strrchr($thumbnail_path,'.'),1); 267*a2549de1Sternite 268*a2549de1Sternite if ($zip->locateName($thumbnail_path) !== false) { 269*a2549de1Sternite 270*a2549de1Sternite if (file_exists($this->getTargetFilepath()) && filemtime($this->getTargetFilepath()) == $timestamp_local_file) { 271*a2549de1Sternite // A thumbnail file for the current file version has already been created, just report that the file is in place by returning true: 272*a2549de1Sternite return true; 273*a2549de1Sternite } 274*a2549de1Sternite 275*a2549de1Sternite // Get the thumbnail file! 276*a2549de1Sternite $fp = $zip->getStream($thumbnail_path); 277*a2549de1Sternite if(!$fp) { 278*a2549de1Sternite return false; 279*a2549de1Sternite } 280*a2549de1Sternite 281*a2549de1Sternite $thumbnaildata = ''; 282*a2549de1Sternite while (!feof($fp)) { 283*a2549de1Sternite $thumbnaildata .= fread($fp, 8192); 284*a2549de1Sternite } 285*a2549de1Sternite 286*a2549de1Sternite fclose($fp); 287*a2549de1Sternite 288*a2549de1Sternite // Write thumbnail file to media folder 289*a2549de1Sternite file_put_contents($this->getTargetFilepath(), $thumbnaildata); 290*a2549de1Sternite 291*a2549de1Sternite return true; 292*a2549de1Sternite } 293*a2549de1Sternite } 294*a2549de1Sternite 295*a2549de1Sternite return true; 296*a2549de1Sternite } 297*a2549de1Sternite}