1<?php 2/** 3 * DokuWiki Plugin PreserveFilenames / action_angua.php 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Kazutaka Miyasaka <kazmiya@gmail.com> 7 */ 8 9// must be run within DokuWiki 10if (!defined('DOKU_INC')) { 11 die(); 12} 13 14if (!defined('DOKU_PLUGIN')) { 15 define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 16} 17 18require_once(DOKU_PLUGIN . 'preservefilenames/common.php'); 19require_once(DOKU_PLUGIN . 'preservefilenames/action_anteater.php'); 20 21class action_plugin_preservefilenames_angua extends action_plugin_preservefilenames_anteater 22{ 23 /** 24 * Registers event handlers 25 */ 26 function register(&$controller) 27 { 28 $this->common = new PreserveFilenames_Common(); 29 30 $controller->register_hook('MEDIA_UPLOAD_FINISH', 'AFTER', $this, '_saveMeta'); 31 $controller->register_hook('MEDIA_DELETE_FILE', 'AFTER', $this, '_deleteMeta'); 32 $controller->register_hook('MEDIA_SENDFILE', 'BEFORE', $this, '_sendFile'); 33 $controller->register_hook('PARSER_HANDLER_DONE', 'BEFORE', $this, '_replaceLinkTitle'); 34 $controller->register_hook('RENDERER_CONTENT_POSTPROCESS', 'AFTER', $this, '_replaceLinkURL'); 35 $controller->register_hook('MEDIAMANAGER_CONTENT_OUTPUT', 'BEFORE', $this, '_handleMediaContent'); 36 $controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, '_handleMediaFullscreen'); 37 $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, '_handleAjaxMediaList'); 38 $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, '_replaceSnippetDownload'); 39 } 40 41 /** 42 * Saves the name of the uploaded media file to a meta file 43 */ 44 function _saveMeta(&$event) 45 { 46 global $conf; 47 48 $id = $event->data[2]; 49 $filename_tidy = noNS($id); 50 51 // retrieve original filename 52 if (isset($_GET['qqfile'])) { 53 // via ajax uploader 54 $filename_orig = (string) $_GET['qqfile']; 55 } elseif (isset($_POST['mediaid'])) { 56 if (isset($_FILES['qqfile']['name'])) { 57 // via ajax uploader 58 $filename_orig = (string) $_FILES['qqfile']['name']; 59 } elseif (isset($_FILES['upload']['name'])) { 60 // via old-fashioned upload form 61 $filename_orig = (string) $_FILES['upload']['name']; 62 } else { 63 return; 64 } 65 66 // check if filename is specified 67 $specified_name = (string) $_POST['mediaid']; 68 69 if ($specified_name !== '') { 70 $filename_orig = $specified_name; 71 } 72 } else { 73 return; 74 } 75 76 $filename_safe = $this->common->_sanitizeFileName($filename_orig); 77 78 // no need to save original filename 79 if ($filename_tidy === $filename_safe) { 80 return; 81 } 82 83 // fallback if suspicious characters found 84 if ($filename_orig !== $filename_safe) { 85 return; 86 } 87 88 // save original filename to meta file 89 io_saveFile( 90 mediaMetaFN($id, '.filename'), 91 serialize(array( 92 'filename' => $filename_safe, 93 )) 94 ); 95 } 96 97 /** 98 * Deletes a meta file associated with the deleted media file 99 */ 100 function _deleteMeta(&$event) 101 { 102 $id = $event->data['id']; 103 $metaFilePath = mediaMetaFN($id, '.filename'); 104 105 if (@unlink($metaFilePath)) { 106 io_sweepNS($id, 'mediametadir'); 107 } else { 108 parent::_deleteMeta($event); 109 } 110 } 111 112 /** 113 * Returns original filename if exists 114 */ 115 function _getOriginalFileName($id) 116 { 117 $metaFilePath = mediaMetaFN($id, '.filename'); 118 $meta = unserialize(io_readFile($metaFilePath, false)); 119 120 if (empty($meta['filename'])) { 121 // check old meta file (for backward compatibility) 122 $filename = parent::_getOriginalFileName($id); 123 124 // move old meta file to media_meta directory 125 if ($filename !== false) { 126 $oldMetaFilePath = metaFN($id, '.filename'); 127 io_rename($oldMetaFilePath, $metaFilePath); 128 } 129 130 return $filename; 131 } else { 132 return $this->common->_sanitizeFileName($meta['filename']); 133 } 134 } 135 136 /** 137 * Handles media manager content output 138 * 139 * @see tpl_mediaContent 140 */ 141 function _handleMediaContent(&$event) 142 { 143 global $NS; 144 global $AUTH; 145 global $JUMPTO; 146 147 if ($event->data['do'] !== 'filelist') { 148 return; 149 } 150 151 $event->preventDefault(); 152 $this->_mod_media_filelist($NS, $AUTH, $JUMPTO); 153 } 154 155 /** 156 * Handles an action that calls full-screen media manager 157 * 158 * @see tpl_content_core() 159 */ 160 function _handleMediaFullscreen(&$event) 161 { 162 if ($event->data !== 'media') { 163 return; 164 } 165 166 $event->preventDefault(); 167 $this->_mod_tpl_media(); 168 } 169 170 /** 171 * Handles a 'medialist' ajax call 172 * 173 * @see ajax_medialist() 174 */ 175 function _handleAjaxMediaList(&$event) 176 { 177 global $NS; 178 179 if ($event->data !== 'preservefilenames_medialist') { 180 return; 181 } 182 183 $event->preventDefault(); 184 $NS = cleanID($_POST['ns']); 185 186 if ($_POST['do'] === 'media') { 187 $this->_mod_tpl_mediaFileList(); 188 } else { 189 tpl_mediaContent('fromAjax'); 190 } 191 } 192 193 // ------------------------------------------------------- 194 // The following methods whose name starts with '_mod' are 195 // slightly modified versions of existing functions. 196 // ------------------------------------------------------- 197 198 /** 199 * Prints full-screen media manager 200 * 201 * @see tpl_media() 202 */ 203 function _mod_tpl_media() 204 { 205 global $DEL, $NS, $IMG, $AUTH, $JUMPTO, $REV, $lang, $fullscreen, $conf; 206 $fullscreen = true; 207 require_once DOKU_INC.'lib/exe/mediamanager.php'; 208 209 if ($_REQUEST['image']) $image = cleanID($_REQUEST['image']); 210 if (isset($IMG)) $image = $IMG; 211 if (isset($JUMPTO)) $image = $JUMPTO; 212 if (isset($REV) && !$JUMPTO) $rev = $REV; 213 214 echo '<div id="mediamanager__page">'.NL; 215 echo '<h1>'.$lang['btn_media'].'</h1>'.NL; 216 html_msgarea(); 217 218 echo '<div class="panel namespaces">'.NL; 219 echo '<h2>'.$lang['namespaces'].'</h2>'.NL; 220 echo '<div class="panelHeader">'; 221 echo $lang['media_namespaces']; 222 echo '</div>'.NL; 223 224 echo '<div class="panelContent" id="media__tree">'.NL; 225 media_nstree($NS); 226 echo '</div>'.NL; 227 echo '</div>'.NL; 228 229 echo '<div class="panel filelist">'.NL; 230 $this->_mod_tpl_mediaFileList(); 231 echo '</div>'.NL; 232 233 echo '<div class="panel file">'.NL; 234 echo '<h2 class="a11y">'.$lang['media_file'].'</h2>'.NL; 235 tpl_mediaFileDetails($image, $rev); 236 echo '</div>'.NL; 237 238 echo '</div>'.NL; 239 } 240 241 /** 242 * Prints the central column in full-screen media manager 243 * 244 * @see tpl_mediaFileList() 245 */ 246 function _mod_tpl_mediaFileList() 247 { 248 global $AUTH; 249 global $NS; 250 global $JUMPTO; 251 global $lang; 252 253 $opened_tab = $_REQUEST['tab_files']; 254 if (!$opened_tab || !in_array($opened_tab, array('files', 'upload', 'search'))) $opened_tab = 'files'; 255 if ($_REQUEST['mediado'] == 'update') $opened_tab = 'upload'; 256 257 echo '<h2 class="a11y">' . $lang['mediaselect'] . '</h2>'.NL; 258 259 media_tabs_files($opened_tab); 260 261 echo '<div class="panelHeader">'.NL; 262 echo '<h3>'; 263 $tabTitle = ($NS) ? $NS : '['.$lang['mediaroot'].']'; 264 printf($lang['media_' . $opened_tab], '<strong>'.hsc($tabTitle).'</strong>'); 265 echo '</h3>'.NL; 266 if ($opened_tab === 'search' || $opened_tab === 'files') { 267 media_tab_files_options(); 268 } 269 echo '</div>'.NL; 270 271 echo '<div class="panelContent">'.NL; 272 if ($opened_tab == 'files') { 273 $this->_mod_media_tab_files($NS,$AUTH,$JUMPTO); 274 } elseif ($opened_tab == 'upload') { 275 media_tab_upload($NS,$AUTH,$JUMPTO); 276 } elseif ($opened_tab == 'search') { 277 media_tab_search($NS,$AUTH); 278 } 279 echo '</div>'.NL; 280 } 281 282 /** 283 * Prints tab that displays a list of all files 284 * 285 * @see media_tab_files() 286 */ 287 function _mod_media_tab_files($ns,$auth=null,$jump='') { 288 global $lang; 289 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 290 291 if($auth < AUTH_READ){ 292 echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL; 293 }else{ 294 $this->_mod_media_filelist($ns,$auth,$jump,true,_media_get_sort_type()); 295 } 296 } 297 298 /** 299 * List all files in a given Media namespace 300 * 301 * @see media_filelist() 302 */ 303 function _mod_media_filelist($ns,$auth=null,$jump='',$fullscreenview=false,$sort=false){ 304 global $conf; 305 global $lang; 306 $ns = cleanID($ns); 307 308 // check auth our self if not given (needed for ajax calls) 309 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 310 311 if (!$fullscreenview) echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL; 312 313 if($auth < AUTH_READ){ 314 // FIXME: print permission warning here instead? 315 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL; 316 }else{ 317 if (!$fullscreenview) media_uploadform($ns, $auth); 318 319 $dir = utf8_encodeFN(str_replace(':','/',$ns)); 320 $data = array(); 321 search($data,$conf['mediadir'],'search_media', 322 array('showmsg'=>true,'depth'=>1),$dir,1,$sort); 323 324 if(!count($data)){ 325 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL; 326 }else { 327 if ($fullscreenview) { 328 echo '<ul class="' . _media_get_list_type() . '">'; 329 } 330 foreach($data as $item){ 331 if (!$fullscreenview) { 332 $this->_mod_media_printfile($item,$auth,$jump); 333 } else { 334 $this->_mod_media_printfile_thumbs($item,$auth,$jump); 335 } 336 } 337 if ($fullscreenview) echo '</ul>'.NL; 338 } 339 } 340 if (!$fullscreenview) media_searchform($ns); 341 } 342 343 /** 344 * Formats and prints one file in the list 345 * 346 * @see media_printfile() 347 */ 348 function _mod_media_printfile($item,$auth,$jump,$display_namespace=false){ 349 global $lang; 350 global $conf; 351 352 // Prepare zebra coloring 353 // I always wanted to use this variable name :-D 354 static $twibble = 1; 355 $twibble *= -1; 356 $zebra = ($twibble == -1) ? 'odd' : 'even'; 357 358 // Automatically jump to recent action 359 if($jump == $item['id']) { 360 $jump = ' id="scroll__here" '; 361 }else{ 362 $jump = ''; 363 } 364 365 // Prepare fileicons 366 list($ext,$mime,$dl) = mimetype($item['file'],false); 367 $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext); 368 $class = 'select mediafile mf_'.$class; 369 370 // Prepare filename 371 $file = $this->_getOriginalFileName($item['id']); 372 373 if ($file === false) { 374 $file = utf8_decodeFN($item['file']); 375 } 376 377 // build fake media id 378 $ns = getNS($item['id']); 379 $fakeId = $ns === false ? $file : "$ns:$file"; 380 $fakeId_escaped = hsc($fakeId); 381 382 // Prepare info 383 $info = ''; 384 if($item['isimg']){ 385 $info .= (int) $item['meta']->getField('File.Width'); 386 $info .= '×'; 387 $info .= (int) $item['meta']->getField('File.Height'); 388 $info .= ' '; 389 } 390 $info .= '<i>'.dformat($item['mtime']).'</i>'; 391 $info .= ' '; 392 $info .= filesize_h($item['size']); 393 394 // output 395 echo '<div class="'.$zebra.'"'.$jump.' title="'.$fakeId_escaped.'">'.NL; 396 if (!$display_namespace) { 397 echo '<a name="h_:'.$item['id'].'" class="'.$class.'">'.hsc($file).'</a> '; 398 } else { 399 echo '<a name="h_:'.$item['id'].'" class="'.$class.'">'.$fakeId_escaped.'</a><br/>'; 400 } 401 echo '<span class="info">('.$info.')</span>'.NL; 402 403 // view button 404 $link = ml($fakeId,'',true); 405 echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/magnifier.png" '. 406 'alt="'.$lang['mediaview'].'" title="'.$lang['mediaview'].'" class="btn" /></a>'; 407 408 // mediamanager button 409 $link = wl('',array('do'=>'media','image'=>$fakeId,'ns'=>$ns)); 410 echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/mediamanager.png" '. 411 'alt="'.$lang['btn_media'].'" title="'.$lang['btn_media'].'" class="btn" /></a>'; 412 413 // delete button 414 if($item['writable'] && $auth >= AUTH_DELETE){ 415 $link = DOKU_BASE.'lib/exe/mediamanager.php?delete='.rawurlencode($fakeId). 416 '&sectok='.getSecurityToken(); 417 echo ' <a href="'.$link.'" class="btn_media_delete" title="'.$fakeId_escaped.'">'. 418 '<img src="'.DOKU_BASE.'lib/images/trash.png" alt="'.$lang['btn_delete'].'" '. 419 'title="'.$lang['btn_delete'].'" class="btn" /></a>'; 420 } 421 422 echo '<div class="example" id="ex_'.str_replace(':','_',$item['id']).'">'; 423 echo $lang['mediausage'].' <code>{{:'.str_replace(array('{','}'),array('(',')'),$fakeId_escaped).'}}</code>'; 424 echo '</div>'; 425 if($item['isimg']) media_printimgdetail($item); 426 echo '<div class="clearer"></div>'.NL; 427 echo '</div>'.NL; 428 } 429 430 /** 431 * Formats and prints one file in the list in the thumbnails view 432 * 433 * @see media_printfile_thumbs() 434 */ 435 function _mod_media_printfile_thumbs($item,$auth,$jump=false,$display_namespace=false){ 436 global $lang; 437 global $conf; 438 439 // Prepare filename 440 $file = $this->_getOriginalFileName($item['id']); 441 442 if ($file === false) { 443 $file = utf8_decodeFN($item['file']); 444 } 445 446 // build fake media id 447 $ns = getNS($item['id']); 448 $fakeId = $ns === false ? $file : "$ns:$file"; 449 $fakeId_escaped = hsc($fakeId); 450 451 // output 452 echo '<li><dl title="'.$fakeId_escaped.'">'.NL; 453 454 echo '<dt>'; 455 if($item['isimg']) { 456 media_printimgdetail($item, true); 457 458 } else { 459 echo '<a name="d_:'.$item['id'].'" class="image" title="'.$fakeId_escaped.'" href="'. 460 media_managerURL(array('image' => $fakeId, 'ns' => $ns, 461 'tab_details' => 'view')).'">'; 462 echo media_printicon($fakeId_escaped); 463 echo '</a>'; 464 } 465 echo '</dt>'.NL; 466 if (!$display_namespace) { 467 $name = hsc($file); 468 } else { 469 $name = $fakeId_escaped; 470 } 471 echo '<dd class="name"><a href="'.media_managerURL(array('image' => $fakeId, 'ns' => $ns, 472 'tab_details' => 'view')).'" name="h_:'.$item['id'].'">'.$name.'</a></dd>'.NL; 473 474 if($item['isimg']){ 475 $size = ''; 476 $size .= (int) $item['meta']->getField('File.Width'); 477 $size .= '×'; 478 $size .= (int) $item['meta']->getField('File.Height'); 479 echo '<dd class="size">'.$size.'</dd>'.NL; 480 } else { 481 echo '<dd class="size"> </dd>'.NL; 482 } 483 $date = dformat($item['mtime']); 484 echo '<dd class="date">'.$date.'</dd>'.NL; 485 $filesize = filesize_h($item['size']); 486 echo '<dd class="filesize">'.$filesize.'</dd>'.NL; 487 echo '</dl></li>'.NL; 488 } 489} 490