1<?php 2/** 3 * Wiki farm animal class 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Etienne MELEARD <etienne.meleard@cru.fr> 7 * @desc Describes a farm animal, providing ways to interract with its config / users / files 8 * also provides static methods to create animals / get global infos about the farm. 9 */ 10 11// Set the plugin include path depending if we are under DokuWiki or SOAPServer context 12if(!defined('DOKU_FARM_PLUGIN')) define('DOKU_FARM_PLUGIN', defined('DOKU_FARMPLUGINLOADED') ? DOKU_INC.'lib/plugins/farm/' : './'); 13 14require_once(DOKU_FARM_PLUGIN.'infos.animal.class.php'); 15 16class dokuwiki_farm_animal { 17 private $name = ''; 18 private $path = ''; 19 20 var $manager = null; 21 private $infos = null; 22 23 private $wikiconf = array(); 24 25 private static $farmconf = array(); 26 27 /** 28 * @param $name animal name 29 * @param $manager object that must handle error(), success(), nicesize(), getLang() ... calls 30 */ 31 function __construct($name = '', $manager = null) { 32 self::getConf(); 33 if(self::exists($name)) { 34 $this->name = $name; 35 $this->manager = & $manager; 36 $this->path = self::$farmconf['farmfsroot'].self::$farmconf['barn'].$this->name; 37 } 38 } 39 40 /** 41 * Return animal name 42 * @return string 43 */ 44 public function getName() { 45 return $this->name; 46 } 47 48 /** 49 * Return animal path in filesystem, based on farmfsroot farmconfig parameter 50 * @return string 51 */ 52 public function getPath() { 53 return $this->path; 54 } 55 56 /** 57 * Return url of animal ressource, with respect of animal config (useslash/userewrite) and farm config (userewrite/farmrewrite) 58 * @param $id ressource id (eg. namespace ...) 59 * @param $media boolean indicating whether the ressource is a media or a page 60 * @param $xmlencode boolean indicating whether output xml entities must be encoded 61 * @return url string 62 */ 63 public function getUrl($id = '', $media = false, $xmlencode = true) { 64 self::getConf(); 65 66 $wc = $this->getWikiConf(); 67 if($id) { 68 $id = str_replace('/', ':', $id); 69 if(isset($wc['editable']['useslash'])) if($wc['editable']['useslash']) $id = str_replace(':', '/', $id); 70 if(isset($wc['protected']['useslash'])) if($wc['protected']['useslash']) $id = str_replace(':', '/', $id); 71 } 72 73 $aur = false; 74 if(isset($wc['editable']['userewrite'])) if($wc['editable']['userewrite']) $aur = true; 75 if(isset($wc['protected']['userewrite'])) if($wc['protected']['userewrite']) $aur = true; 76 77 $url = self::$farmconf['farmwebroot']; 78 79 if(!self::$farmconf['userewrite']) { 80 $url .= self::$farmconf['farmer']; 81 if(!$aur) { 82 if(!$media) { 83 $url .= 'doku.php?animal='.$this->getName(); 84 if($id) $url .= ($xmlencode ? '&' : '&').'id='.$id; 85 }else $url .= 'lib/exe/fetch.php?w=&l=&cache=cache&media='.$id; 86 }else{ 87 if(!$media) { 88 $url .= $id.'?animal='.$this->getName(); 89 }else $url .= '_media/'.$id.'?w=&h=&cache=cache'; 90 } 91 }else{ 92 if(!self::$farmconf['farmrewrite']) $url .= self::$farmconf['barn']; 93 $url .= $this->getName(); 94 if(!$aur) { 95 if(!$media) { 96 if($id) $url .= '/doku.php?id='.$id; 97 }else $url .= 'lib/exe/fetch.php?w=&l=&cache=cache&media='.$id; 98 }else{ 99 if(!$media) { 100 $url .= '/'.$id; 101 }else $url .= '_media/'.$id.'?w=&h=&cache=cache'; 102 } 103 } 104 return $url; 105 } 106 107 /** 108 * Return the value of an animal's metadata 109 * @param $name metadata identifier 110 * @param $default value to return if "name" metadata not found 111 * @return string 112 */ 113 public function getMetadata($name = '', $default = null) { 114 global $conf; 115 if(!self::exists($this->name)) return null; 116 self::getConf(); 117 $metadata = array(); 118 foreach(explode("\n", @file_get_contents($this->path.'/animal.meta')) as $l) { 119 $l = preg_split('`\s+:\s+`', $l); 120 if(count($l) == 2) $metadata[$l[0]] = $l[1]; 121 } 122 if($name == '') return $metadata; 123 return isset($metadata[$name]) ? $metadata[$name] : $default; 124 } 125 126 /** 127 * Set animal's metadata 128 * @param $name metadata identifier 129 * @param $value metadata value 130 * @return success as boolean 131 */ 132 public function setMetadata($name = '', $value = '') { 133 global $conf; 134 if($name == '') return false; 135 if(!self::exists($this->name)) return null; 136 self::getConf(); 137 $metadata = array(); 138 foreach(explode("\n", @file_get_contents($this->path.'/animal.meta')) as $l) { 139 $l = preg_split('`\s+:\s+`', $l); 140 if(count($l) == 2) $metadata[$l[0]] = $l[1]; 141 } 142 if(is_null($value)) { 143 if(isset($metadata[$name])) unset($metadata[$name]); 144 }else $metadata[$name] = $value; 145 $out = array(); 146 foreach($metadata as $k => $v) $out[] = $k.' : '.$v; 147 if($fp = fopen($this->path.'/animal.meta', 'w')) { 148 fwrite($fp, implode("\n", $out)); 149 fclose($fp); 150 return true; 151 } 152 return false; 153 } 154 155 /** 156 * Returns animal's status 157 * @return string 158 */ 159 public function getStatus() { 160 return $this->getMetadata('status', 'open'); 161 } 162 163 /** 164 * Returns animal's lock state 165 * @return string 166 */ 167 public function getLockState() { 168 return $this->getMetadata('lockstate'); 169 } 170 171 /** 172 * Returns an animal_infos object loaded from the animal 173 * @return dokuwiki_farm_animal_infos object 174 */ 175 public function getInfos() { 176 if(!$this->infos) $this->infos = new dokuwiki_farm_animal_infos($this); 177 return $this->infos; 178 } 179 180 /** 181 * Gather and returns the animal wiki configuration as a multidimensionnal array 182 * @return multidimensionnal associative array 183 */ 184 public function getWikiConf() { 185 if(!self::exists($this->name)) return null; 186 $getfromas = array( 187 'local.protected.php' => 'protected', 188 'local.php' => 'editable' 189 ); 190 foreach($getfromas as $from => $as) { 191 if(@file_exists($this->path.'/conf/'.$from)) { 192 $conf = array(); 193 foreach(explode("\n", @file_get_contents($this->path.'/conf/'.$from)) as $line) { 194 if(preg_match('`^\$conf\[([^]]+(\]\[[^]]+)?)\]\s*=\s*([^;]+);`', $line, $m)) { 195 $path = array_map(create_function('$e', 'return preg_replace(\'`^(\\\'|")?([a-zA-Z0-9_-]+)(\\\'|")?$`i\', \'$2\', trim($e));'), explode('][', $m[1])); 196 $value = $m[3]; 197 eval('$conf['.implode('][', $path).'] = $value;'); 198 } 199 } 200 $this->wikiconf[$as] = $conf; 201 } 202 } 203 krsort($this->wikiconf); 204 return $this->wikiconf; 205 } 206 207 /** 208 * Set one of the animal wiki configuration parameters 209 * @param $mode string pointing the target configuration 210 * @param $path array containing the successive path elents used to access the target configuration parameter 211 * @param $value value to be set 212 * @return success as boolean 213 */ 214 public function setWikiConf($mode = 'editable', $path = array(), $value = null) { 215 if(!in_array($mode, array('editable', 'protected'))) return false; 216 if(!count($path)) return false; 217 if(!count($this->wikiconf)) $this->getWikiConf(); 218 $c = & $this->wikiconf[$mode]; 219 foreach($path as $p) { 220 if(!isset($c[$p])) $c[$p] = array(); 221 $c = & $c[$p]; 222 } 223 $c = $value; 224 return true; 225 } 226 227 /** 228 * Turns a multidimensionnal animal wiki configuration array into an array of php code strings 229 * @param $head path above element 230 * @param $el array element 231 * @return configuration php strings 232 */ 233 private function buildconfrecursive($head, $el) { 234 if(is_array($el)) { 235 $r = array(); 236 foreach($el as $k => $v) $r = array_merge($r, $this->buildconfrecursive(array_merge($head, array($k)), $v)); 237 return $r; 238 } 239 if(is_bool($el)) $el = $el ? 'true' : 'false'; 240 elseif(is_numeric($el)) $el = "$el"; 241 elseif( 242 !(substr($el, 0, 1) == "'" && substr($el, -1) == "'") && 243 !(substr($el, 0, 1) == '"' && substr($el, -1) == '"') && 244 !preg_match('`^DOKU_[A-Z_]+`', $el) && 245 !preg_match('`\.DOKU_[A-Z_]+$`', $el) 246 ) $el = "'".$el."'"; 247 return array('$conf[\''.implode("']['", $head).'\'] = '.$el.';'); 248 } 249 250 /** 251 * Save the animal's wiki configuration 252 * @return success as boolean 253 */ 254 public function saveWikiConf() { 255 if(!self::exists($this->name)) return false; 256 $savefromto = array( 257 'local.protected.php' => 'protected', 258 'local.php' => 'editable' 259 ); 260 $r = true; 261 foreach($savefromto as $to => $from) { 262 $conf = implode("\n", $this->buildconfrecursive(array(), $this->wikiconf[$from])); 263 if($fp = @fopen($this->path.'/conf/'.$to, 'w')) { 264 fwrite($fp, "<?php\n// Generated by farm plugin (".date('Y-m-d H:i:s').")\n\n".$conf."\n"); 265 fclose($fp); 266 }else $r = false; 267 } 268 $this->wikiconf = array(); 269 return $r; 270 } 271 272 /** 273 * Returns the animal's raw wiki configuration as a single string 274 * @param $mode string pointing the target configuration 275 * @return configuration as string 276 */ 277 public function getRawWikiConf($mode = 'editable') { 278 if(!self::exists($this->name)) return null; 279 $rel = array( 280 'protected' => 'local.protected.php', 281 'editable' => 'local.php' 282 ); 283 if(!in_array($mode, array_keys($rel))) return null; 284 if(!@file_exists($this->path.'/conf/'.$rel[$mode])) return null; 285 $r = array(); 286 foreach(explode("\n", @file_get_contents($this->path.'/conf/'.$rel[$mode])) as $line) { 287 if(preg_match('`^(\$conf\[[^]]+(\]\[[^]]+)?\]\s*=\s*[^;]+);`', $line, $m)) $r[] = $m[1]; 288 } 289 return implode("\n", $r); 290 } 291 292 /** 293 * Save animal wiki configuration from string 294 * @param $mode string pointing the target configuration 295 * @param $conf string 296 * @return success as boolean 297 */ 298 public function saveRawWikiConf($mode = 'editable', $conf = '') { 299 if(!self::exists($this->name)) return false; 300 $rel = array( 301 'protected' => 'local.protected.php', 302 'editable' => 'local.php' 303 ); 304 if(!in_array($mode, array_keys($rel))) return false; 305 if(!@file_exists($this->path.'/conf/'.$rel[$mode])) return false; 306 $this->wikiconf = array(); 307 $conf = implode("\n", array_map(create_function('$e', '$e = trim($e); if(substr($e, -1) != \';\') $e .= \';\'; return $e;'), explode("\n", $conf))); 308 if($fp = @fopen($this->path.'/conf/'.$rel[$mode], 'w')) { 309 fwrite($fp, "<?php\n// Generated by farm plugin (".date('Y-m-d H:i:s').")\n\n".$conf."\n"); 310 fclose($fp); 311 return true; 312 } 313 return false; 314 } 315 316 /** 317 * EXPERIMENTAL 318 * Returns the animal users as an array 319 * @return array of arrays containing 'pass', 'name', 'mail' and 'groups' (array) entries, may be empty if backend not compatible 320 */ 321 public function getUsers() { 322 $users = array(); 323 324 global $conf; 325 $fconf = $conf; 326 327 $conf = array(); 328 329 // Load animal conf 330 $wc = $this->getWikiConf(); 331 $conf = array_merge($wc['editable'], $wc['protected']); 332 if(!isset($conf['authtype']) || empty($conf['authtype'])) $conf['authtype'] = 'plain'; 333 334 if($conf['authtype'] == 'plain') { 335 foreach(explode("\n", @file_get_contents($this->path.'/conf/users.auth.php')) as $line) { 336 $line = preg_replace('/#.*$/','',$line); //ignore comments 337 $line = trim($line); 338 if(empty($line)) continue; 339 $row = split(':', $line, 5); 340 $groups = split(',', $row[4]); 341 $users[$row[0]] = array( 342 'pass' => $row[1], 343 'name' => urldecode($row[2]), 344 'mail' => $row[3], 345 'grps' => $groups 346 ); 347 } 348 }else{ 349 $auth_class = 'auth_'.$conf['authtype']; 350 if(!class_exists($auth_class)) if(@file_exists(DOKU_INC.'inc/auth/'.$conf['authtype'].'.class.php')) include DOKU_INC.'auth/'.$conf['authtype'].'.class.php'; 351 352 if(class_exists($auth_class)) { 353 $localauth = new $auth_class(); 354 $localauth->users = null; 355 $users = $localauth->retrieveUsers(); 356 } 357 } 358 359 $conf = $fconf; 360 361 return $users; 362 } 363 364 /** 365 * EXPERIMENTAL 366 * Add a user to the animal 367 * @param $user user login 368 * @param $pass user clear password 369 * @param $name user name 370 * @param $mail user email 371 * @param $grps array of groups the user is in 372 * @return success as boolean, also false if backend not compatible 373 */ 374 public function addUser($user, $pass, $name, $mail, $grps=null) { 375 if($name == '') $name = $user; 376 377 $users = $this->getUsers(); 378 if(isset($users[$user])) return false; 379 380 global $conf; 381 $fconf = $conf; 382 $conf = array(); 383 384 // Load animal conf 385 $wc = $this->getWikiConf(); 386 $conf = array_merge($wc['editable'], $wc['protected']); 387 if(!isset($conf['authtype']) || empty($conf['authtype'])) $conf['authtype'] = 'plain'; 388 389 if($conf['authtype'] == 'plain') { 390 $authfile = @file_get_contents($this->path.'/conf/users.auth.php'); 391 $pass = auth_cryptPassword($pass, isset($conf['passcrypt']) ? $conf['passcrypt'] : 'smd5'); 392 if(!is_array($grps)) $grps = array($conf['defaultgroup']); 393 $groups = join(',', $grps); 394 $userline = join(':', array($user, $pass, $name, $mail, $groups))."\n"; 395 if($authfile) $authfile .= $userline; 396 if($authfile) { 397 if($fp = fopen($this->path.'/conf/users.auth.php', 'w')) { 398 fwrite($fp, $authfile); 399 fclose($fp); 400 $r = true; 401 }else $r = false; 402 }else $r = false; 403 }else{ 404 $auth_class = 'auth_'.$conf['authtype']; 405 if(!class_exists($auth_class)) if(@file_exists(DOKU_INC.'inc/auth/'.$conf['authtype'].'.class.php')) include DOKU_INC.'auth/'.$conf['authtype'].'.class.php'; 406 407 if(class_exists($auth_class)) { 408 $localauth = new $auth_class(); 409 $localauth->users = null; 410 $r = ($localauth->createUser($user, $pass, $name, $mail, $grps) != null); 411 }else $r = false; 412 } 413 414 $conf = $fconf; 415 416 return $r; 417 } 418 419 /** 420 * EXPERIMENTAL 421 * Delete a user from the animal 422 * @param $user user login 423 * @return success as boolean, also false if backend not compatible 424 */ 425 public function deleteUser($user) { 426 if($user == '') return false; 427 428 $users = $this->getUsers(); 429 if(!isset($users[$user])) return false; 430 431 global $conf; 432 $fconf = $conf; 433 $conf = array(); 434 435 // Load animal conf 436 $wc = $this->getWikiConf(); 437 $conf = array_merge($wc['editable'], $wc['protected']); 438 if(!isset($conf['authtype']) || empty($conf['authtype'])) $conf['authtype'] = 'plain'; 439 440 if($conf['authtype'] == 'plain') { 441 $out = array(); 442 foreach(explode("\n", @file_get_contents($this->path.'/conf/users.auth.php')) as $line) { 443 if(substr($line, 0, strlen($user) + 1) != $user.':') $out[] = $line; 444 } 445 446 if($fp = fopen($this->path.'/conf/users.auth.php', 'w')) { 447 fwrite($fp, implode("\n", $out)); 448 fclose($fp); 449 $r = true; 450 }else $r = false; 451 }else{ 452 $auth_class = 'auth_'.$conf['authtype']; 453 if(!class_exists($auth_class)) if(@file_exists(DOKU_INC.'inc/auth/'.$conf['authtype'].'.class.php')) include DOKU_INC.'auth/'.$conf['authtype'].'.class.php'; 454 455 if(class_exists($auth_class)) { 456 $localauth = new $auth_class(); 457 $localauth->users = null; 458 $r = ($localauth->deleteUsers(array($user)) > 0); 459 }else $r = false; 460 } 461 462 $conf = $fconf; 463 464 return $r; 465 } 466 467 /** 468 * Scans the filesystem to gather informations about files / directories 469 * @param $p path 470 * @param $l level of depth 471 * @param $bp start path to strip 472 * @param $pref base64 encoded path reference 473 * @return array of fs items descriptors 474 */ 475 private function scanfs($p='', $l=0, $bp='', $pref='') { 476 if($p == '') return array(); 477 if($bp == '') $bp = $p; 478 if(substr($bp, -1) != '/') $bp .= '/'; 479 $fs = array(); 480 foreach(scandir($p) as $i) { 481 if($i == '.' || $i == '..') continue; 482 if(@is_file($p.'/'.$i)) { 483 $fi = stat($p.'/'.$i); 484 $fs[base64_encode(substr($p.'/'.$i, strlen($bp)))] = array( 485 'type' => 'file', 486 'name' => $i, 487 'size' => $fi['size'], 488 'mtime' => $fi['mtime'], 489 'lvl' => $l, 490 'pref' => $pref 491 ); 492 } 493 if(@is_dir($p.'/'.$i)) { 494 $fs[base64_encode(substr($p.'/'.$i, strlen($bp)))] = array( 495 'type' => 'dir', 496 'name' => $i, 497 'lvl' => $l, 498 'pref' => $pref 499 ); 500 $fs = array_merge($fs, $this->scanfs($p.'/'.$i, $l+1, $bp, base64_encode(substr($p.'/'.$i, strlen($bp))))); 501 } 502 } 503 return $fs; 504 } 505 506 /** 507 * Gather informations about the animal's filesystem 508 * @return array of fs items descriptors 509 */ 510 public function getFs() { 511 if(!self::exists($this->name)) return false; 512 return $this->scanfs($this->getPath()); 513 } 514 515 /** 516 * Returns the content of a file in the animal's filesystem 517 * @return file content as a string 518 */ 519 public function getFileContent($file) { 520 if(!self::exists($this->name)) return ''; 521 if(!@file_exists($this->path.'/'.$file)) return ''; 522 return @file_get_contents($this->path.'/'.$file); 523 } 524 525 /** 526 * Set the content of a file in the animal's filesystem 527 * @param $file file content as a string 528 * @param $ctn file content to be put as a string 529 * @param $delete boolean telling whether to delete the pointed file 530 * @return success as boolean 531 */ 532 public function setFileContent($file, $ctn='', $delete=false) { 533 if(!self::exists($this->name)) return false; 534 if($delete && !@file_exists($this->path.'/'.$file)) return false; 535 if($delete) return @unlink($this->path.'/'.$file); 536 if($fp = fopen($this->path.'/'.$file, 'w')) { 537 fwrite($fp, $ctn); 538 fclose($fp); 539 return true; 540 }else return false; 541 } 542 543 /** 544 * Deletes the animal, calling animal hooks 545 * @return success as boolean 546 */ 547 public function delete() { 548 self::getConf(); 549 550 if(@file_exists(DOKU_FARM_PLUGIN.'hooks.animal.class.php')) { 551 @include DOKU_FARM_PLUGIN.'hooks.animal.class.php'; 552 } 553 554 $hookhandler = class_exists('dokuwiki_farm_animal_hooks') ? new dokuwiki_farm_animal_hooks($handler) : null; 555 556 if($hookhandler) { 557 if(!$hookhandler->trigger('delete', 'before', array($this))) { 558 if($handler) $handler->errors[] = array('code' => 'animal_delete_eventhandlercancel_failure'); 559 return false; 560 } 561 } 562 563 if($this->name == self::$farmconf['animaltemplate']) return false; 564 $r = self::rmdir($this->path); 565 if(!$r) return false; 566 567 if($hookhandler) $hookhandler->trigger('delete', 'after', array($this->name)); 568 569 return true; 570 } 571 572 /** 573 * Creates a new animal, calling animal hooks 574 * @param $name name of the new animal 575 * @param $template template to use for clonning as string 576 * @param $host host to bind if virtual mode in use 577 * @param $handler handler object featuring a errors array property 578 * @return success as boolean 579 */ 580 public static function createNew($name = '', $template = null, $host = null, $handler = null) { 581 self::getConf(); 582 583 if(@file_exists(DOKU_FARM_PLUGIN.'hooks.animal.class.php')) { 584 @include DOKU_FARM_PLUGIN.'hooks.animal.class.php'; 585 } 586 587 $hookhandler = class_exists('dokuwiki_farm_animal_hooks') ? new dokuwiki_farm_animal_hooks($handler) : null; 588 589 if($hookhandler) { 590 if(!$hookhandler->trigger('create', 'before', array($name, $template))) { 591 if($handler) $handler->errors[] = array('code' => 'animal_new_eventhandlercancel_failure'); 592 return false; 593 } 594 } 595 596 if($farmconf['farmmaxanimals'] > 0) { 597 if(count(self::listAnimals()) >= $farmconf['farmmaxanimals']) { 598 if($handler) $handler->errors[] = array('code' => 'animal_new_farmfull_failure'); 599 return false; 600 } 601 } 602 603 if($farmconf['farmmaxsize'] > 0) { 604 if(count(self::farmSize()) >= $farmconf['farmmaxsize']) { 605 if($handler) $handler->errors[] = array('code' => 'animal_new_farmtoobig_failure'); 606 return false; 607 } 608 } 609 610 if(!$template) $template = self::$farmconf['animaltemplate']; 611 612 $name = strtr($name, "àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ", "aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY"); 613 $name = preg_replace('`\s+`', '_', $name); 614 if(!preg_match('`^[a-z0-9._-]+`i', $name)) { 615 if($handler) $handler->errors[] = array('code' => 'animal_new_badname_failure'); 616 return false; 617 } 618 if(self::exists($name) || ($name == trim(self::$farmconf['farmer'], '/')) || @is_dir(self::$farmconf['farmfsroot'].self::$farmconf['barn'].$name)) { 619 if($handler) $handler->errors[] = array('code' => 'animal_new_namealreadyinuse_failure', 'data' => array($name)); 620 return false; 621 } 622 623 if(!self::exists($template)) { 624 if($handler) $handler->errors[] = array('code' => 'animal_new_templatenotfound_failure', 'data' => array($template)); 625 return false; 626 } 627 628 $cph = create_function('$path,$isfile', ' 629if(!$isfile) return; 630$ctn = @file_get_contents($path); 631if(!preg_match(\'\\{ANIMAL\\}\', $ctn)) return; 632$ctn = str_replace(\'{ANIMAL}\', \''.$name.'\', $ctn); 633if($fp = fopen($path, \'w\')) { 634 fwrite($fp, $ctn); 635 fclose($fp); 636} 637'); 638 639 if(!self::cp_r(self::$farmconf['farmfsroot'].self::$farmconf['barn'].$template, self::$farmconf['farmfsroot'].self::$farmconf['barn'].$name, false, array(), $cph)) { 640 if($handler) $handler->errors[] = array('code' => 'animal_new_templatecopy_failure'); 641 return false; 642 } 643 644 if($fp = fopen(self::$farmconf['farmfsroot'].self::$farmconf['barn'].$name.'/animal.meta', 'w')) { 645 fwrite($fp, 'creation_date : '.time()); 646 fclose($fp); 647 }else{ 648 if($handler) $handler->errors[] = array('code' => 'animal_new_metacreate_failure'); 649 return false; 650 } 651 652 if(!self::exists($name)) { 653 if($handler) $handler->errors[] = array('code' => 'animal_new_createdespite_failure'); 654 return false; 655 } 656 657 $a = new dokuwiki_farm_animal($name, $handler->manager); 658 if(!$a->setWikiConf('protected', array('basedir'), 'DOKU_ANIMAL_BASEDIR.\''.$name.'\'') || !$a->setWikiConf('editable', array('title'), $name)) { 659 if($handler) $handler->errors[] = array('code' => 'animal_new_setconfig_failure'); 660 return false; 661 } 662 if(!$a->saveWikiConf()) { 663 if($handler) $handler->errors[] = array('code' => 'animal_new_saveconfig_failure'); 664 return false; 665 } 666 667 if($host && self::$farmconf['virtual']) { 668 $vh = @file_get_contents(DOKU_FARM_PLUGIN.'virtual_hosts.php'); 669 $e = array_filter(array_merge(array(self::$farmconf['farmerhost']), array_map(create_function('$l', 'return preg_replace(\'`^([^\s]+)\s`\', \'$1\', $l);'), explode("\n", $vh))), create_function('$v', 'return ($v == \''.$host.'\');')); 670 if(!count($e)) { 671 $fp = fopen(DOKU_FARM_PLUGIN.'virtual_hosts.php', 'w'); 672 if($fp) { 673 fwrite($fp, $vh."\n".$host.' '.$name); 674 fclose($fp); 675 }else{ 676 if($handler) $handler->errors[] = array('code' => 'animal_new_savevirtualhost_failure'); 677 return false; 678 } 679 }else{ 680 if($handler) $handler->errors[] = array('code' => 'animal_new_virtualhostexists_failure'); 681 return false; 682 } 683 } 684 685 if($hookhandler) $hookhandler->trigger('create', 'after', array($a)); 686 687 return $name; 688 } 689 690 /** 691 * Gather the list of animals 692 * @return array of dokuwiki_farm_animal objects 693 */ 694 public static function listAnimals($manager = null) { 695 self::getConf(); 696 $a = array(); 697 foreach(scandir(self::$farmconf['farmfsroot'].self::$farmconf['barn']) as $i) { 698 if($i == '.' && $i == '..') continue; 699 if(self::exists($i)) $a[] = new dokuwiki_farm_animal($i, $manager); 700 } 701 return $a; 702 } 703 704 /** 705 * Get the farm disk usage 706 * @return size in bytes 707 */ 708 public static function farmSize() { 709 return array_sum(array_map(create_function('$a', 'return $a->getInfos()->getSize();'), self::listAnimals())); 710 } 711 712 /** 713 * Copy / move a directory contents 714 * @param $src source path 715 * @param $dest destination path 716 * @param $mv boolean telling whether copying or moving contents 717 * @param $avoid path of elemnts that musn't be copied / moved 718 * @param $copyhook function to handle cpoy operations 719 * @return success as boolean 720 */ 721 private static function cp_r($src, $dest, $mv=false, $avoid=array(), $copyhook = null) { 722 if(in_array($src, $avoid)) return true; 723 if(@is_file($src)) { 724 $r = copy($src, $dest); 725 chmod($dest, fileperms($src)); 726 if($mv) $r &= unlink($src); 727 if($copyhook) $copyhook($dest, true); 728 return $r; 729 } 730 731 if(!@is_dir($dest)) mkdir($dest, fileperms($src)); 732 733 $r = true; 734 foreach(scandir($src) as $i) { 735 if($i == '.' || $i == '..') continue; 736 $r &= self::cp_r($src.'/'.$i, $dest.'/'.$i, $mv, $avoid); 737 } 738 739 if($mv) $r &= rmdir($src); 740 if($copyhook) $copyhook($dest, false); 741 742 return $r; 743 } 744 745 /** 746 * Removes a directory 747 * @param $p path to the directory 748 * @return success as boolean 749 */ 750 private static function rmdir($p) { 751 $r = true; 752 foreach(scandir($p) as $i) { 753 if($i == '.' || $i == '..') continue; 754 if(@is_dir($p.'/'.$i)) $r &= self::rmdir($p.'/'.$i); 755 if(@is_file($p.'/'.$i)) $r &= unlink($p.'/'.$i); 756 if(!$r) break; 757 } 758 if($r) rmdir($p); 759 return $r; 760 } 761 762 /** 763 * Gather and return the farm configuration 764 * @return associative array of farm configuration parameters 765 */ 766 public static function getConf() { 767 if(!self::$farmconf) { 768 $farmconf = array(); 769 include(DOKU_FARM_PLUGIN.'config.php'); 770 self::$farmconf = $farmconf; 771 } 772 return self::$farmconf; 773 } 774 775 /** 776 * Check if an animal exists 777 * @param $name name to test 778 * @return boolean 779 */ 780 public static function exists($name = '') { 781 if($name == '') return false; 782 $name = strtr($name, "àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ", "aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY"); 783 $name = preg_replace('`\s+`', '_', $name); 784 if(!preg_match('`^[a-z0-9._-]+`i', $name)) return false; 785 self::getConf(); 786 return @is_dir(self::$farmconf['farmfsroot'].self::$farmconf['barn'].$name) && @file_exists(self::$farmconf['farmfsroot'].self::$farmconf['barn'].$name.'/animal.meta'); 787 } 788} 789 790?> 791