1<?php 2/** 3 * Imports filetrees into DokuWikis media directory 4 * and creating a wiki pagetree with filelists linking to the 5 * imported documents 6 * 7 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 8 * @author Frank Schiebel <frank@linuxmuster.net> 9 */ 10 11if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 12if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 13if(!defined('DOKU_INCLUDE')) define('DOKU_INCLUDE',DOKU_INC.'inc/'); 14require_once(DOKU_PLUGIN . 'admin.php'); 15require_once(DOKU_INCLUDE . 'io.php'); 16 17/** 18 * All DokuWiki plugins to extend the admin function 19 * need to inherit from this class 20 */ 21class admin_plugin_doctree2filelist extends DokuWiki_Admin_Plugin 22{ 23 /** 24 * Constructor 25 */ 26 function admin_plugin_doctree2filelist() 27 { 28 $this->setupLocale(); 29 } 30 31 /** 32 * return some info 33 */ 34 function getInfo() 35 { 36 return array( 37 'author' => 'Frank Schiebel', 38 'email' => 'frank@linuxmuster.net', 39 'date' => '2010-11-27', 40 'name' => 'doctree2filelist: Imports document tree into dokuwiki', 41 'desc' => 'This plugin is for importing a whole tree with (office-)documents to a wiki page-structure. It has been written for openschulportfolio, a dokuwiki based portfolio-system for schools.', 42 'url' => 'http://www.openschulportfolio.de/', 43 ); 44 } 45 46 /** 47 * return sort order for position in admin menu 48 */ 49 function getMenuSort() 50 { 51 return 999; 52 } 53 54 /** 55 * return a menu prompt for the admin menu 56 * NOT REQUIRED - its better to place $lang['menu'] string in localised string file 57 * only use this function when you need to vary the string returned 58 */ 59 function getMenuText() 60 { 61 $menu_base = $this->getLang('plugname'); 62 return $menu_base; 63 } 64 65 /** 66 * handle user request 67 */ 68 function handle() { 69 if (!isset($_REQUEST['ospcmd'])) return; 70 71 if ($_REQUEST['ospcmd'] == "create_upload_dir") { 72 $this->_create_upload_dir(); 73 } 74 if ($_REQUEST['ospcmd'] == "docsuploaded") { 75 $this->_save_status("DOCSUPLOADED"); 76 } 77 if ($_REQUEST['ospcmd'] == "delete_upload_dir") { 78 $this->_delete_upload_dir(); 79 } 80 if ($_REQUEST['ospcmd'] == "start_over") { 81 $this->_reset_wizard(); 82 } 83 if ($_REQUEST['ospcmd'] == "importit") { 84 $this->_import_docs(); 85 } 86 } 87 88 /** 89 * output appropriate html 90 */ 91 function html() { 92 global $conf; 93 94 # check for filelist plugin 95 if (!file_exists(DOKU_PLUGIN . "filelist/syntax.php")) { 96 print $this->_div_warning("start"); 97 print $this->getLang('filelist_plugin_required'); 98 print '<a href="http://www.dokuwiki.org/plugin:filelist">The filelist-plugin can be found here.</a>'; 99 return; 100 } 101 102 # print out explanation and warning 103 print "<h1>" . $this->getLang('headline') ."</h1>\n"; 104 print "<p>" . $this->getLang('description') ."</p>\n"; 105 print $this->getLang('detaildesc') ."\n"; 106 print $this->_div_warning("start"); 107 print $this->getLang('warning_osp') ."\n"; 108 print $this->_div_warning("end"); 109 110 # determine upload dir from 111 $file_upload = $this->_strip_doubleslashes($conf['savedir'] . '/media/' . $this->getConf('sourcetree') . '/'); 112 print "<h2>" . $this->getLang('wizard') . "</h2>\n"; 113 print '<div class="settingsbox"><strong>' . $this->getLang('settings') . "</strong><br />"; 114 print $this->getLang('importdir') .": <tt>". $file_upload . "</tt><br />\n"; 115 print $this->getLang('targetns') .": <tt>". $this->getConf('destination_namespace') . "</tt>\n"; 116 print "</div>\n"; 117 118 $status = $this->_read_status(); 119 120 if ($status == "IMPORTED") { 121 $statusline = $this->_read_status("all"); 122 print "<p><span class=\"ospok\">OK </span>" . $this->getLang('lastimport') . " " . $statusline . "</p>\n"; 123 print $this->_create_reset_form(); 124 } 125 if ($status == "DOCSUPLOADED") { 126 print "<p><span class=\"ospok\">OK </span>" . $this->getLang('docsuploaded') . "</p>\n"; 127 print "<div class=\"ospnext\">" . $this->getLang('importnow') . "\n"; 128 ptln('<form action="'.wl($ID).'" method="post" /> '); 129 ptln(' <input type="hidden" name="do" value="admin" />'); 130 ptln(' <input type="hidden" name="ospcmd" value="importit" />'); 131 ptln(' <input type="hidden" name="page" value="'.$this->getPluginName().'" />'); 132 print ' <input type="submit" value="'. $this->getLang('btn_import') . '"> ' . "\n"; 133 ptln('</form></div>'); 134 } 135 if (is_dir($file_upload) && ($status != "IMPORTED") && ($status != "DOCSUPLOADED") ) { 136 print "<span class=\"ospok\">OK </span>" . $this->getLang('sourcedir_exists') . " <tt> " . $file_upload . " </tt></span>\n"; 137 print "<div class=\"ospnext\">" . $this->getLang('docuploadnow') . "\n"; 138 ptln('<form action="'.wl($ID).'" method="post" /> '); 139 ptln(' <input type="hidden" name="do" value="admin" />'); 140 ptln(' <input type="hidden" name="ospcmd" value="docsuploaded" />'); 141 ptln(' <input type="hidden" name="page" value="'.$this->getPluginName().'" />'); 142 print ' <input type="submit" value="'. $this->getLang('btn_confirm_upload') . '"> ' . "\n"; 143 ptln('</form></div>'); 144 } 145 if (!is_dir($file_upload) && $status == "START") { 146 ptln("<div class=\"ospnext\">"); 147 print $this->getLang('sourcedir_does_not_exist') . "\n "; 148 ptln(' <form action="'.wl($ID).'" method="post" /> '); 149 ptln(' <input type="hidden" name="do" value="admin" />'); 150 ptln(' <input type="hidden" name="ospcmd" value="create_upload_dir" />'); 151 ptln(' <input type="hidden" name="page" value="'.$this->getPluginName().'" />'); 152 print ' <input type="submit" value="' . $this->getLang('btn_create_upload_dir') . '"> ' . "\n"; 153 ptln(' </form>'); 154 ptln('</div>'); 155 } 156 157 } 158 159 /** 160 * Creates creates reset form 161 * 162 * @author Frank Schiebel <frank@linuxmuster.net> 163 * @param none 164 * @return none 165 * 166 **/ 167 function _create_reset_form() { 168 global $conf; 169 # determine upload dir from conf 170 $file_upload = $this->_strip_doubleslashes($conf['savedir'] . '/media/' . $this->getConf('sourcetree') . '/'); 171 if (@is_dir($file_upload) && @is_writable($file_upload)) { 172 $html = '<form action="'.wl($ID).'" method="post" /> '."\n"; 173 $html .= ' <input type="hidden" name="do" value="admin" />'."\n"; 174 $html .= ' <input type="hidden" name="ospcmd" value="delete_upload_dir" />'."\n"; 175 $html .= ' <input type="hidden" name="page" value="'.$this->getPluginName().'" />'."\n"; 176 $html .= ' <input type="submit" value="' . $this->getLang('btn_delete_upload_dir') . '"> ' . "\n"; 177 $html .= '</form>'."\n"; 178 $html .= '<form action="'.wl($ID).'" method="post" /> '."\n"; 179 $html .= ' <input type="hidden" name="do" value="admin" />'."\n"; 180 $html .= ' <input type="hidden" name="ospcmd" value="importit" />'."\n"; 181 $html .= ' <input type="hidden" name="page" value="'.$this->getPluginName().'" />'."\n"; 182 $html .= ' <input type="submit" value="' . $this->getLang('btn_reimport') . '"> ' . "\n"; 183 $html .= '</form>'."\n"; 184 } 185 $html .= '<form action="'.wl($ID).'" method="post" /> '."\n"; 186 $html .= ' <input type="hidden" name="do" value="admin" />'."\n"; 187 $html .= ' <input type="hidden" name="ospcmd" value="start_over" />'."\n"; 188 $html .= ' <input type="hidden" name="page" value="'.$this->getPluginName().'" />'."\n"; 189 $html .= ' <input type="submit" value="' . $this->getLang('btn_start_over') . '"> ' . "\n"; 190 $html .= '</form>'."\n"; 191 return $html; 192 } 193 194 /** 195 * Creates upload dir according to config 196 * 197 * @author Frank Schiebel <frank@linuxmuster.net> 198 * @param none 199 * @return none 200 * 201 **/ 202 function _create_upload_dir() { 203 global $conf; 204 # determine upload dir from conf 205 $file_upload = $this->_strip_doubleslashes($conf['savedir'] . '/media/' . $this->getConf('sourcetree') . '/'); 206 mkdir($file_upload); 207 $this->_save_status("UPLOADDIRCREATED"); 208 } 209 210 /** 211 * Resets wizard 212 * 213 * @author Frank Schiebel <frank@linuxmuster.net> 214 * @param none 215 * @return none 216 * 217 **/ 218 function _reset_wizard() { 219 global $conf; 220 # determine upload dir from conf 221 $file_upload = $this->_strip_doubleslashes($conf['savedir'] . '/media/' . $this->getConf('sourcetree') . '/'); 222 if (@is_dir($file_upload) && @is_writable($file_upload)) { 223 $this->_deltree($file_upload); 224 } 225 $this->_save_status("START"); 226 } 227 228 /** 229 * Deletes upload dir and all containing docs 230 * 231 * @author Frank Schiebel <frank@linuxmuster.net> 232 * @param none 233 * @return none 234 * 235 **/ 236 function _delete_upload_dir() { 237 global $conf; 238 # determine upload dir from conf 239 $file_upload = $this->_strip_doubleslashes($conf['savedir'] . '/media/' . $this->getConf('sourcetree') . '/'); 240 if (@is_dir($file_upload) && @is_writable($file_upload)) { 241 $this->_deltree($file_upload); 242 } 243 } 244 245 /** 246 * Import document tree to media dir 247 * 248 * @author Frank Schiebel <frank@linuxmuster.net> 249 * @param none 250 * @returns none 251 * 252 **/ 253 function _import_docs() { 254 global $conf; 255 $mediapath = $this->_strip_doubleslashes($conf['savedir'] . '/media/'); 256 $pagespath = $this->_strip_doubleslashes($conf['savedir'] . '/pages/'); 257 # determine upload dir from conf 258 $file_upload = $this->_strip_doubleslashes($conf['savedir'] . '/media/' . $this->getConf('sourcetree') . '/'); 259 $subpath = str_replace(":", "/", $this->getConf('destination_namespace')); 260 $media_dest = $this->_strip_doubleslashes($mediapath."/".$subpath."/"); 261 $pagesdir = $this->_strip_doubleslashes($pagespath."/".$subpath."/"); 262 263 # delete old media and pages dir 264 if (is_dir($media_dest)) { 265 $this->_deltree($media_dest); 266 } 267 if (is_dir($pagesdir)) { 268 $this->_deltree($pagesdir); 269 } 270 271 # create fresh namespacedirs for media an pages 272 io_createNamespace($this->getConf('destination_namespace').":xx"); 273 io_createNamespace($this->getConf('destination_namespace').":xx", 'media'); 274 275 // copy files recursively 276 $this->_copytree($file_upload, $media_dest); 277 // create startpages 278 $this->create_startpages($media_dest); 279 280 # determine if we are running under openschulportfolio 281 if (file_exists(DOKU_INC . "/lib/tpl/portfolio/ospversion.php")) { 282 $pfstartfile_in = realpath(dirname(__FILE__))."/start.txt"; 283 $pfstartfile_out = $pagespath."portfolio/start.txt"; 284 copy($pfstartfile_in, $pfstartfile_out); 285 } 286 $this->_save_status("IMPORTED"); 287 288 } 289 290 /** 291 * deletes directory tree recursively 292 * 293 * @author Frank Schiebel <frank@linuxmuster.net> 294 * @param string directory to delete 295 * @returns boolean status of rmdir operation 296 * 297 **/ 298 function _deltree($dest) { 299 $list = array_diff(scandir($dest), array('.', '..')); 300 foreach ($list as $value) { 301 $file = $dest.'/'.$value; 302 if (is_dir($file)) { $this->_deltree($file); } else { unlink($file); } 303 } 304 return rmdir($dest); 305 } 306 307 308 /** 309 * Copy a file, or recursively copy a folder and its contents 310 * 311 * @author Aidan Lister <aidan@php.net> 312 * @author Frank Schiebel <frank@linuxmuster.net> 313 * @version 1.0.1 314 * @link http://aidanlister.com/2004/04/recursively-copying-directories-in-php/ 315 * @param string $source Source path 316 * @param string $dest Destination path 317 * @return bool Returns TRUE on success, FALSE on failure 318 * 319 **/ 320 function _copytree($source, $dest) { 321 global $conf; 322 323 $source = $this->_strip_doubleslashes($source); 324 $dest = $this->_get_clean_filename($dest); 325 // Simple copy for a file 326 if (is_file($source)) { 327 return @copy($source, $dest); 328 } 329 $dest = $this->_get_clean_filename($dest); 330 331 // Make destination directory 332 if (!is_dir($dest)) { 333 334 $mediapath = $this->_strip_doubleslashes($conf['savedir'] . '/media/'); 335 $pagespath = $this->_strip_doubleslashes($conf['savedir'] . '/pages/'); 336 337 338 $pages_dir = str_replace("$mediapath", "$pagespath", $dest); 339 mkdir($dest); 340 if (!is_dir($pages_dir)) { 341 @mkdir($pages_dir); 342 } 343 } 344 345 // Loop through the folder 346 if (!is_dir($source)) { 347 return false; 348 } 349 350 $dir = dir($source); 351 while (false !== $entry = $dir->read()) { 352 // Skip pointers 353 if ($entry == '.' || $entry == '..') { 354 continue; 355 } 356 357 // Deep copy directories 358 $this->_copytree("$source/$entry", "$dest/$entry"); 359 } 360 361 // Clean up 362 $dir->close(); 363 return true; 364 } 365 366 /** Gets utf8 encoded wiki filename (experimantal, has to be tested!) 367 * 368 **/ 369 function _get_clean_filename($dest) { 370 global $conf; 371 372 $fixpath = $this->_strip_doubleslashes($conf['savedir'] . "/media/"); 373 $dest = str_replace("$fixpath", "", $dest); 374 # Fix windows encoding: this is really bad, but i could not figure out how to 375 # change the filename to utf8 from wathever encoding comes in... 376 $dest = urlencode($dest); 377 $dest = str_replace('%2F','/', $dest); 378 $dest = str_replace('%25','%', $dest); 379 $dest = str_replace('%C2','', $dest); 380 $dest = str_replace('%C3','', $dest); 381 $dest = str_replace('%81','ue', $dest); 382 $dest = str_replace('%84','ae', $dest); 383 $dest = str_replace('%94','oe', $dest); 384 $dest = str_replace('%A1','ss', $dest); 385 $dest = str_replace('+','_', $dest); 386 $dest = str_replace('-','_', $dest); 387 388 $dest = str_replace('__','_', $dest); 389 $dest = str_replace("//", "/", $dest); 390 $dest = str_replace("/", ":", $dest); 391 $dest = preg_replace("/:$/", "", $dest); 392 393 $dest = mediaFN($dest); 394 $dest = $this->_strip_doubleslashes($fixpath . str_replace($conf['mediadir'], '', $dest)); 395 396 return $dest; 397 } 398 399 /** recursively creates startpages for imported document structure 400 * 401 **/ 402 function create_startpages($dest) { 403 global $conf; 404 $subdirs = array(); 405 406 // namespace for filelist 407 $media = $this->_strip_doubleslashes($conf['savedir'] . "/media/"); 408 $pages = $this->_strip_doubleslashes($conf['savedir'] . "/pages/"); 409 410 // change dest to pages 411 $dest = str_replace($media, $pages, $dest); 412 $startpage = $dest ."start.txt"; 413 414 // get filelist namespace 415 $ns_dir = str_replace($pages, "", $dest); 416 $filelist_namespace = str_replace("/",":", $ns_dir); 417 $header_namespace = preg_replace("|.*/(.*)/$|","$1", $ns_dir); 418 419 // get all subdirs of actual ns 420 $dir = dir($dest); 421 while (false !== $entry = $dir->read()) { 422 // Skip pointers 423 if ($entry == '.' || $entry == '..') { 424 continue; 425 } 426 if (is_dir($dest.$entry)) { 427 $subdirs[] = $entry; 428 // recursively call function 429 $this->create_startpages($dest.$entry."/"); 430 } 431 } 432 433 // sort subdirs and create wiki-markup 434 $subdir_out = ""; 435 if (count($subdirs) > 0 ) { 436 sort($subdirs); 437 $subdir_out = "===== " . $this->getLang('subnamespaces') ." ===== \n\n"; 438 foreach ($subdirs as $subdir) { 439 $subdir_out .= " * [[.$subdir:start|$subdir]]\n"; 440 } 441 } 442 443 // write start.txt 444 $handle = fopen ("$startpage", "w"); 445 fwrite($handle, "[[".DOKU_URL."/lib/exe/mediamanager.php?ns=$filelist_namespace|" . $this->getLang('edit_files') . "]] | [[..start|" . $this->getLang('ns_up') . "]] \n\n"); 446 fwrite($handle, "====== " . $this->getLang('documents_for') . $header_namespace . " ======\n\n"); 447 fwrite($handle, $this->getLang('docslisted') . "\\\\ ''$filelist_namespace'' \n\n"); 448 fwrite($handle, "{{filelist>:$filelist_namespace*&style=table&tableheader=1&tableshowdate=1&tableshowsize=1}}\n\n"); 449 fwrite($handle, "$subdir_out\n"); 450 fclose($handle); 451 } 452 453 /** 454 * Strip double slashes from path names 455 * 456 * @author Frank Schiebel <frank@linuxmuster.net> 457 * @param string path in 458 * @returns string path out 459 * 460 **/ 461 function _strip_doubleslashes($path) { 462 return preg_replace('/\/\//','/', $path); 463 } 464 465 /** 466 * Save status of import procedure to status file 467 * $conf['cachedir'].'/doctree2filelist.status' 468 * creates the file if it does not exist. 469 * 470 * @author Frank Schiebel <frank@linuxmuster.net> 471 * @param string statusstring 472 * @return true on success 473 * 474 **/ 475 function _save_status($status) { 476 global $conf; 477 // build status line 478 $t = time(); 479 $statusline = $t."\t".strftime($conf['dformat'],$t)."\t".$_SERVER['REMOTE_ADDR']."\t".$_SERVER['REMOTE_USER']."\t".$status."\n"; 480 // write status to file 481 io_saveFile($conf['cachedir'].'/doctree2filelist.status',$statusline, false); 482 return true; 483 } 484 485 /** 486 * reads status of import procedure from status file 487 * $conf['cachedir'].'/doctree2filelist.status' 488 * 489 * @author Frank Schiebel <frank@linuxmuster.net> 490 * @param none 491 * @return string statusstring 492 * 493 **/ 494 function _read_status($mode = "statonly") { 495 global $conf; 496 $status = "START"; 497 // read status from file 498 $statusfile = $conf['cachedir'].'/doctree2filelist.status'; 499 if(@file_exists($statusfile)) { 500 $statusline = file($statusfile); 501 $statusline = $statusline[0]; 502 $status = explode("\t", $statusline); 503 if ($mode == "statonly" ) { 504 $status = rtrim($status[4]); 505 } else { 506 $status = $status[1] . " " . $this->getLang('fromuser') . " " . $status[3]; 507 } 508 } 509 510 return $status; 511 } 512 513 /** 514 * prints a warning div. Uses the note plugin if available 515 * 516 * @author Frank Schiebel <frank@linuxmuster.net> 517 * @param string (start|end) 518 * @return string html 519 * 520 **/ 521 function _div_warning($mode) { 522 if($mode == "start") { 523 if (file_exists(DOKU_PLUGIN . "note/syntax.php")) { 524 return '<div class="notewarning">'; 525 } else { 526 return '<div class="docimpwarning">'; 527 } 528 } else { 529 return '</div>'; 530 } 531 } 532 533 534} 535