1<?php 2/** 3 * Wiki farm animal infos and statistics class 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Etienne MELEARD <etienne.meleard@cru.fr> 7 * @desc Gather and returns various infos and statistics about an animal 8 */ 9 10if(!defined('DOKU_FARM_PLUGIN')) define('DOKU_FARM_PLUGIN', defined('DOKU_FARMPLUGINLOADED') ? DOKU_INC.'lib/plugins/farm/' : './'); 11 12require_once DOKU_FARM_PLUGIN.'animal.class.php'; 13 14class dokuwiki_farm_animal_infos { 15 var $animal = null; 16 17 /** 18 * @param $animal animal object or animal name as string 19 */ 20 function __construct($animal = null) { 21 if(is_a($animal, 'dokuwiki_farm_animal')) { 22 if(dokuwiki_farm_animal::exists($animal->getName())) $this->animal = $animal; 23 }elseif(is_string($animal)) { 24 if(dokuwiki_farm_animal::exists($animal)) $this->animal = new dokuwiki_farm_animal($animal); 25 } 26 } 27 28 /** 29 * Returns the size of a directory contents 30 * @param $p path to the directory 31 * @return directory disk usage in bytes 32 */ 33 private function dirsize($p = '.') { 34 $s = 0; 35 foreach(scandir($p) as $i) { 36 if($i == '.' || $i == '..') continue; 37 if(@is_file($p.'/'.$i)) $s += filesize($p.'/'.$i); 38 if(@is_dir($p.'/'.$i)) $s += $this->dirsize($p.'/'.$i); 39 } 40 return $s; 41 } 42 43 /** 44 * Format data using a string template 45 * @param $fmt string template 46 * @param $opt array of replacers 47 * @return formated string 48 */ 49 public function formatedprint($fmt, $opt) { 50 $rawreplace = array('url', 'link', 'namespace', 'user'); 51 foreach($rawreplace as $k) if(isset($opt[$k])) $fmt = str_replace('{'.$k.'}', $opt[$k], $fmt); 52 if(isset($opt['date'])) { 53 $date = (int)$opt['date']; 54 $dfmt = isset($opt['dformat']) ? $opt['dformat'] : '%Y/%m/%d %H:%M'; 55 $delta = abs(time() - $date); 56 $date = strftime($dfmt, $date); 57 $fmt = str_replace('{date}', $date, $fmt); 58 if(isset($opt['dunits'])) { 59 $triggers = array( 60 's' => array(1, 60), 61 'i' => array(60, 3600), 62 'h' => array(3600, 24*3600), 63 'd' => array(24*3600, 7*24*3600), 64 'w' => array(7*24*3600, 30.5*24*3600), 65 'm' => array(30.5*24*3600, 365.25*24*3600), 66 'y' => array(365.25*24*3600, 0) 67 ); 68 $prec = null; 69 $str = '?'; 70 foreach($triggers as $t => $v) { 71 if($v[1] && $delta >= $v[1]) { 72 $prec = $t; 73 continue; 74 } 75 $hg = floor($delta / $v[0]); 76 $lw = $prec ? floor(($delta % $v[0]) / $triggers[$prec][0]) : null; 77 $str = $hg.' '.$opt['dunits'][$t][($hg > 1) ? 1 : 0].($lw ? ' '.$lw.' '.$opt['dunits'][$prec][($lw > 1) ? 1 : 0] : ''); 78 break; 79 } 80 $fmt = str_replace('{delta}', $str, $fmt); 81 } 82 } 83 if(isset($opt['size'])) $fmt = str_replace('{size}', $this->animal->manager->nicesize($opt['size']), $fmt); 84 $fmt = preg_replace('`\{[^}]+\}`', '?', $fmt); 85 return $fmt; 86 } 87 88 /** 89 * Shorten a namespace identifier for rendering 90 * @param $ns namespace identifier 91 * @param $maxlen maximum length 92 * @return shortened namespace identifier 93 */ 94 private function shortenNS($ns, $maxlen=32) { 95 $ns = str_replace('/', ':', $ns); 96 if(strlen($ns) <= $maxlen) return $ns; 97 if(preg_match('`^([^:]+:)([^:]+(:[^:]+))(:.+)$`', $ns, $m)) { 98 $st = $m[1]; 99 $mid = $m[2]; 100 $end = $m[4]; 101 while(strlen($st.$end) + 3 <= $maxlen) { 102 $st .= substr($mid, 0, 1); 103 $end = substr($mid, -1, 1).$end; 104 $mid = substr($mid, 1, -1); 105 } 106 $ns = $st.'...'.$end; 107 } 108 if(strlen($ns) > $maxlen + 5) $ns = substr($ns, 0, floor($maxlen / 2)).'...'.substr($ns, -1 * floor($maxlen / 2)); 109 return $ns; 110 } 111 112 /** 113 * Get the disk usage of the related animal 114 * @param $mode specific item family pointer 115 * @param $nice boolean telling whether to nicely format the size or not 116 * @return size as string 117 */ 118 public function getSize($mode='', $nice=false) { 119 if(!in_array($mode, array('pages', 'media', 'cache', 'index'))) $mode = ''; 120 if(!$this->animal) return 0; 121 $s = $this->dirsize($this->animal->getPath().(($mode != '') ? '/data/'.$mode : '')); 122 if($nice) $s = $this->animal->manager->nicesize($s); 123 return $s; 124 } 125 126 /** 127 * Gather the list of files in a directory and 'stat' them 128 * @param $p path 129 * @param $regexp PCRE used to match only specific files 130 * @param $stripinitialpath boolean telling whether striping base path from items paths or not 131 * @param $stripend integer used to specify if the identifiers must be truncated 132 * @param $initialpath base path 133 * @return associative array of 'stat' results 134 */ 135 public function filesinfo($p='.', $regexp='', $stripinitialpath=false, $stripend=0, $initialpath='') { 136 $r = array(); 137 if($initialpath == '') $initialpath = $p; 138 foreach(scandir($p) as $i) { 139 if($i == '.' || $i == '..') continue; 140 $w = $p.'/'.$i; 141 if($stripinitialpath && $initialpath != '') { 142 $w = substr($w, strlen($initialpath)); 143 while(substr($w, 0, 1) == '/') $w = substr($w, 1); 144 } 145 if($stripend) $w = substr($w, 0, -1 * $stripend); 146 if(@is_dir($p.'/'.$i)) $r = array_merge($r, $this->filesinfo($p.'/'.$i, $regexp, $stripinitialpath, $stripend, $initialpath)); 147 if(@is_file($p.'/'.$i)) if($regexp == '' || preg_match('`'.$regexp.'`i', $i)) $r[$w] = stat($p.'/'.$i); 148 } 149 return $r; 150 } 151 152 /** 153 * Returns the average size of the specified items 154 * @param $f array of 'stat' results 155 * @return average size 156 */ 157 private function avgsize($f) { 158 return round(array_sum(array_map(create_function('$e', 'return $e[\'size\'];'), $f)) / count($f)); 159 } 160 161 /** 162 * Returns the total size of the specified items 163 * @param $f array of 'stat' results 164 * @return total size 165 */ 166 private function totalsize($f) { 167 return array_sum(array_map(create_function('$e', 'return $e[\'size\'];'), $f)); 168 } 169 170 /** 171 * Returns the identifier of the biggest item from specified items 172 * @param $f array of 'stat' results 173 * @return file identifier as string 174 */ 175 private function biggest($f) { 176 uasort($f, create_function('$a,$b', 'return $b[\'size\'] - $a[\'size\'];')); 177 return key($f); 178 } 179 180 /** 181 * Returns the identifier of the smallest item from specified items 182 * @param $f array of 'stat' results 183 * @return file identifier as string 184 */ 185 private function smallest($f) { 186 uasort($f, create_function('$a,$b', 'return $a[\'size\'] - $b[\'size\'];')); 187 return key($f); 188 } 189 190 /** 191 * Returns a page metadata 192 * @param $page page identifier 193 * @return array of metadata (DokuWik style) 194 */ 195 private function getmeta($page) { 196 if(!$this->animal) return 0; 197 if($page == '') return null; 198 if(!@file_exists($this->animal->getPath().'/data/meta/'.$page.'.meta')) return null; 199 return unserialize(@file_get_contents($this->animal->getPath().'/data/meta/'.$page.'.meta')); 200 } 201 202 /** 203 * Extract the last modification date from metadata 204 * @param $m metadata array (DokuWik style) 205 * @return unix timestamp 206 */ 207 private function getmdate($m) { 208 if(isset($m['last_change']['date'])) return (int)$m['last_change']['date']; 209 if(isset($m['current']['date']['modified'])) return (int)$m['current']['date']['modified']; 210 if(isset($m['current']['date']['created'])) return (int)$m['current']['date']['created']; 211 return 0; 212 } 213 214 /** 215 * Returns the identifier of the oldest modified item from specified items 216 * @param $f array of 'stat' results 217 * @return file identifier as string 218 */ 219 private function oldest($f) { 220 $fm = array(); 221 foreach($f as $ns => $i) $fm[$ns] = $this->getmdate($this->getmeta($ns)); 222 uasort($fm, create_function('$a,$b', 'return $a - $b;')); 223 return key($fm); 224 } 225 226 /** 227 * Returns the identifier of the latest modified item from specified items 228 * @param $f array of 'stat' results 229 * @return file identifier as string 230 */ 231 private function latest($f) { 232 $fm = array(); 233 foreach($f as $ns => $i) $fm[$ns] = $this->getmdate($this->getmeta($ns)); 234 uasort($fm, create_function('$a,$b', 'return $b - $a;')); 235 return key($fm); 236 } 237 238 /** 239 * Build a namespace url 240 * @param $ns namespace 241 * @param $media boolean telling whether the pointed item is a media ressource or not 242 * @return url string 243 */ 244 private function elurl($ns, $media=false) { 245 if(!$this->animal) return 0; 246 return $this->animal->getUrl($ns, $media); 247 } 248 249 /** 250 * Build a namespace html link 251 * @param $ns namespace 252 * @param $media boolean telling whether the pointed item is a media ressource or not 253 * @return string 254 */ 255 private function elhtmlurl($ns, $media=false) { 256 return '<a href="'.str_replace('&', '&', $this->elurl($ns, $media)).'" target="_blank">'.$this->shortenNS($ns).'</a>'; 257 } 258 259 /** 260 * Returns animal's status 261 * @return string 262 */ 263 public function getStatus() { 264 return $this->animal->getStatus(); 265 } 266 267 /** 268 * Returns animal's lock state 269 * @return string 270 */ 271 public function getLockState() { 272 return $this->animal->getLockState(); 273 } 274 275 /** 276 * Returns animal's creation date 277 * @param $nice boolean telling whether output must be formated 278 * @return unix timestamp or formated date as string 279 */ 280 public function getCreation($nice=false) { 281 global $conf; 282 $d = (int)$this->animal->getMetadata('creation_date'); 283 if(!$nice) return $d; 284 return $this->formatedprint( 285 $this->animal->manager->getLang('animal_info_creationfmt'), 286 array( 287 'date' => $d, 288 'dformat' => $conf['dformat'], 289 'dunits' => $this->animal->manager->getLang('timedeltaunits') 290 ) 291 ); 292 } 293 294 /** 295 * Gather infos about animal's pages 296 * @param $nice boolean telling whether output must be formated 297 * @return array of infos descriptors 298 */ 299 public function pagesInfo($nice=false) { 300 if(!$this->animal) return 0; 301 global $conf; 302 303 $fi = $this->filesinfo($this->animal->getPath().'/data/pages', '.*\.txt', true, 4); 304 $info = array(); 305 306 // general 307 $info['count'] = count($fi); 308 309 $info['average'] = $this->avgsize($fi); 310 if($nice) $info['average'] = $this->animal->manager->nicesize($info['average']); 311 312 $info['total'] = $this->totalsize($fi); 313 if($nice) $info['total'] = $this->animal->manager->nicesize($info['total']); 314 315 // biggest page 316 $bg = $this->biggest($fi); 317 if($nice) { 318 $info['biggest'] = $this->elhtmlurl($bg).' ('.$this->animal->manager->nicesize($fi[$bg]['size']).')'; 319 }else{ 320 $info['biggest'] = array( 321 'size' => $fi[$bg]['size'], 322 'url' => $this->elurl($bg), 323 'namespace' => $bg 324 ); 325 } 326 327 // smallest page 328 $st = $this->smallest($fi); 329 if($nice) { 330 $info['smallest'] = $this->elhtmlurl($st).' ('.$this->animal->manager->nicesize($fi[$st]['size']).')'; 331 }else{ 332 $info['smallest'] = array( 333 'size' => $fi[$st]['size'], 334 'url' => $this->elurl($st), 335 'namespace' => $st 336 ); 337 } 338 339 // oldest page (by edit date) 340 $ot = $this->oldest($fi); 341 $m = $this->getmeta($ot); 342 if($nice) { 343 $info['oldest'] = $this->formatedprint( 344 $this->animal->manager->getLang('animal_info_pageschangedescriptor'), 345 array( 346 'link' => $this->elhtmlurl($ot), 347 'user' => $m['current']['last_change']['user'], 348 'date' => $m['current']['last_change']['date'], 349 'dformat' => $conf['dformat'], 350 'dunits' => $this->animal->manager->getLang('timedeltaunits') 351 ) 352 ); 353 }else{ 354 $info['oldest'] = array( 355 'date' => $m['current']['last_change']['date'], 356 'user' => $m['current']['last_change']['user'], 357 'url' => $this->elurl($ot), 358 'namespace' => $ot 359 ); 360 } 361 362 // latest edited page 363 $lt = $this->latest($fi); 364 $m = $this->getmeta($lt); 365 if($nice) { 366 $info['latest'] = $this->formatedprint( 367 $this->animal->manager->getLang('animal_info_pageschangedescriptor'), 368 array( 369 'link' => $this->elhtmlurl($lt), 370 'user' => $m['current']['last_change']['user'], 371 'date' => $m['current']['last_change']['date'], 372 'dformat' => $conf['dformat'], 373 'dunits' => $this->animal->manager->getLang('timedeltaunits') 374 ) 375 ); 376 }else{ 377 $info['latest'] = array( 378 'date' => $m['current']['last_change']['date'], 379 'user' => $m['current']['last_change']['user'], 380 'url' => $this->elurl($lt), 381 'namespace' => $lt 382 ); 383 } 384 385 return $info; 386 } 387 388 /** 389 * Gather infos about animal's medias 390 * @param $nice boolean telling whether output must be formated 391 * @return array of infos descriptors 392 */ 393 public function mediasInfo($nice=false) { 394 if(!$this->animal) return 0; 395 global $conf; 396 397 $fi = $this->filesinfo($this->animal->getPath().'/data/media', '', true); 398 $info = array(); 399 400 // general 401 $info['count'] = count($fi); 402 403 $info['average'] = $this->avgsize($fi); 404 if($nice) $info['average'] = $this->animal->manager->nicesize($info['average']); 405 406 $info['total'] = $this->totalsize($fi); 407 if($nice) $info['total'] = $this->animal->manager->nicesize($info['total']); 408 409 // biggest media 410 $bg = $this->biggest($fi); 411 if($nice) { 412 $info['biggest'] = $this->elhtmlurl($bg, true).' ('.$this->animal->manager->nicesize($fi[$bg]['size']).')'; 413 }else{ 414 $info['biggest'] = array( 415 'size' => $fi[$bg]['size'], 416 'url' => $this->elurl($bg, true), 417 'namespace' => $bg 418 ); 419 } 420 421 // smallest media 422 $st = $this->smallest($fi); 423 if($nice) { 424 $info['smallest'] = $this->elhtmlurl($st, true).' ('.$this->animal->manager->nicesize($fi[$st]['size']).')'; 425 }else{ 426 $info['smallest'] = array( 427 'size' => $fi[$st]['size'], 428 'url' => $this->elurl($st, true), 429 'namespace' => $st 430 ); 431 } 432 433 return $info; 434 } 435} 436?> 437