* @desc Gather and returns various infos and statistics about an animal */ if(!defined('DOKU_FARM_PLUGIN')) define('DOKU_FARM_PLUGIN', defined('DOKU_FARMPLUGINLOADED') ? DOKU_INC.'lib/plugins/farm/' : './'); require_once DOKU_FARM_PLUGIN.'animal.class.php'; class dokuwiki_farm_animal_infos { var $animal = null; /** * @param $animal animal object or animal name as string */ function __construct($animal = null) { if(is_a($animal, 'dokuwiki_farm_animal')) { if(dokuwiki_farm_animal::exists($animal->getName())) $this->animal = $animal; }elseif(is_string($animal)) { if(dokuwiki_farm_animal::exists($animal)) $this->animal = new dokuwiki_farm_animal($animal); } } /** * Returns the size of a directory contents * @param $p path to the directory * @return directory disk usage in bytes */ private function dirsize($p = '.') { $s = 0; foreach(scandir($p) as $i) { if($i == '.' || $i == '..') continue; if(@is_file($p.'/'.$i)) $s += filesize($p.'/'.$i); if(@is_dir($p.'/'.$i)) $s += $this->dirsize($p.'/'.$i); } return $s; } /** * Format data using a string template * @param $fmt string template * @param $opt array of replacers * @return formated string */ public function formatedprint($fmt, $opt) { $rawreplace = array('url', 'link', 'namespace', 'user'); foreach($rawreplace as $k) if(isset($opt[$k])) $fmt = str_replace('{'.$k.'}', $opt[$k], $fmt); if(isset($opt['date'])) { $date = (int)$opt['date']; $dfmt = isset($opt['dformat']) ? $opt['dformat'] : '%Y/%m/%d %H:%M'; $delta = abs(time() - $date); $date = strftime($dfmt, $date); $fmt = str_replace('{date}', $date, $fmt); if(isset($opt['dunits'])) { $triggers = array( 's' => array(1, 60), 'i' => array(60, 3600), 'h' => array(3600, 24*3600), 'd' => array(24*3600, 7*24*3600), 'w' => array(7*24*3600, 30.5*24*3600), 'm' => array(30.5*24*3600, 365.25*24*3600), 'y' => array(365.25*24*3600, 0) ); $prec = null; $str = '?'; foreach($triggers as $t => $v) { if($v[1] && $delta >= $v[1]) { $prec = $t; continue; } $hg = floor($delta / $v[0]); $lw = $prec ? floor(($delta % $v[0]) / $triggers[$prec][0]) : null; $str = $hg.' '.$opt['dunits'][$t][($hg > 1) ? 1 : 0].($lw ? ' '.$lw.' '.$opt['dunits'][$prec][($lw > 1) ? 1 : 0] : ''); break; } $fmt = str_replace('{delta}', $str, $fmt); } } if(isset($opt['size'])) $fmt = str_replace('{size}', $this->animal->manager->nicesize($opt['size']), $fmt); $fmt = preg_replace('`\{[^}]+\}`', '?', $fmt); return $fmt; } /** * Shorten a namespace identifier for rendering * @param $ns namespace identifier * @param $maxlen maximum length * @return shortened namespace identifier */ private function shortenNS($ns, $maxlen=32) { $ns = str_replace('/', ':', $ns); if(strlen($ns) <= $maxlen) return $ns; if(preg_match('`^([^:]+:)([^:]+(:[^:]+))(:.+)$`', $ns, $m)) { $st = $m[1]; $mid = $m[2]; $end = $m[4]; while(strlen($st.$end) + 3 <= $maxlen) { $st .= substr($mid, 0, 1); $end = substr($mid, -1, 1).$end; $mid = substr($mid, 1, -1); } $ns = $st.'...'.$end; } if(strlen($ns) > $maxlen + 5) $ns = substr($ns, 0, floor($maxlen / 2)).'...'.substr($ns, -1 * floor($maxlen / 2)); return $ns; } /** * Get the disk usage of the related animal * @param $mode specific item family pointer * @param $nice boolean telling whether to nicely format the size or not * @return size as string */ public function getSize($mode='', $nice=false) { if(!in_array($mode, array('pages', 'media', 'cache', 'index'))) $mode = ''; if(!$this->animal) return 0; $s = $this->dirsize($this->animal->getPath().(($mode != '') ? '/data/'.$mode : '')); if($nice) $s = $this->animal->manager->nicesize($s); return $s; } /** * Gather the list of files in a directory and 'stat' them * @param $p path * @param $regexp PCRE used to match only specific files * @param $stripinitialpath boolean telling whether striping base path from items paths or not * @param $stripend integer used to specify if the identifiers must be truncated * @param $initialpath base path * @return associative array of 'stat' results */ public function filesinfo($p='.', $regexp='', $stripinitialpath=false, $stripend=0, $initialpath='') { $r = array(); if($initialpath == '') $initialpath = $p; foreach(scandir($p) as $i) { if($i == '.' || $i == '..') continue; $w = $p.'/'.$i; if($stripinitialpath && $initialpath != '') { $w = substr($w, strlen($initialpath)); while(substr($w, 0, 1) == '/') $w = substr($w, 1); } if($stripend) $w = substr($w, 0, -1 * $stripend); if(@is_dir($p.'/'.$i)) $r = array_merge($r, $this->filesinfo($p.'/'.$i, $regexp, $stripinitialpath, $stripend, $initialpath)); if(@is_file($p.'/'.$i)) if($regexp == '' || preg_match('`'.$regexp.'`i', $i)) $r[$w] = stat($p.'/'.$i); } return $r; } /** * Returns the average size of the specified items * @param $f array of 'stat' results * @return average size */ private function avgsize($f) { return round(array_sum(array_map(create_function('$e', 'return $e[\'size\'];'), $f)) / count($f)); } /** * Returns the total size of the specified items * @param $f array of 'stat' results * @return total size */ private function totalsize($f) { return array_sum(array_map(create_function('$e', 'return $e[\'size\'];'), $f)); } /** * Returns the identifier of the biggest item from specified items * @param $f array of 'stat' results * @return file identifier as string */ private function biggest($f) { uasort($f, create_function('$a,$b', 'return $b[\'size\'] - $a[\'size\'];')); return key($f); } /** * Returns the identifier of the smallest item from specified items * @param $f array of 'stat' results * @return file identifier as string */ private function smallest($f) { uasort($f, create_function('$a,$b', 'return $a[\'size\'] - $b[\'size\'];')); return key($f); } /** * Returns a page metadata * @param $page page identifier * @return array of metadata (DokuWik style) */ private function getmeta($page) { if(!$this->animal) return 0; if($page == '') return null; if(!@file_exists($this->animal->getPath().'/data/meta/'.$page.'.meta')) return null; return unserialize(@file_get_contents($this->animal->getPath().'/data/meta/'.$page.'.meta')); } /** * Extract the last modification date from metadata * @param $m metadata array (DokuWik style) * @return unix timestamp */ private function getmdate($m) { if(isset($m['last_change']['date'])) return (int)$m['last_change']['date']; if(isset($m['current']['date']['modified'])) return (int)$m['current']['date']['modified']; if(isset($m['current']['date']['created'])) return (int)$m['current']['date']['created']; return 0; } /** * Returns the identifier of the oldest modified item from specified items * @param $f array of 'stat' results * @return file identifier as string */ private function oldest($f) { $fm = array(); foreach($f as $ns => $i) $fm[$ns] = $this->getmdate($this->getmeta($ns)); uasort($fm, create_function('$a,$b', 'return $a - $b;')); return key($fm); } /** * Returns the identifier of the latest modified item from specified items * @param $f array of 'stat' results * @return file identifier as string */ private function latest($f) { $fm = array(); foreach($f as $ns => $i) $fm[$ns] = $this->getmdate($this->getmeta($ns)); uasort($fm, create_function('$a,$b', 'return $b - $a;')); return key($fm); } /** * Build a namespace url * @param $ns namespace * @param $media boolean telling whether the pointed item is a media ressource or not * @return url string */ private function elurl($ns, $media=false) { if(!$this->animal) return 0; return $this->animal->getUrl($ns, $media); } /** * Build a namespace html link * @param $ns namespace * @param $media boolean telling whether the pointed item is a media ressource or not * @return string */ private function elhtmlurl($ns, $media=false) { return ''.$this->shortenNS($ns).''; } /** * Returns animal's status * @return string */ public function getStatus() { return $this->animal->getStatus(); } /** * Returns animal's lock state * @return string */ public function getLockState() { return $this->animal->getLockState(); } /** * Returns animal's creation date * @param $nice boolean telling whether output must be formated * @return unix timestamp or formated date as string */ public function getCreation($nice=false) { global $conf; $d = (int)$this->animal->getMetadata('creation_date'); if(!$nice) return $d; return $this->formatedprint( $this->animal->manager->getLang('animal_info_creationfmt'), array( 'date' => $d, 'dformat' => $conf['dformat'], 'dunits' => $this->animal->manager->getLang('timedeltaunits') ) ); } /** * Gather infos about animal's pages * @param $nice boolean telling whether output must be formated * @return array of infos descriptors */ public function pagesInfo($nice=false) { if(!$this->animal) return 0; global $conf; $fi = $this->filesinfo($this->animal->getPath().'/data/pages', '.*\.txt', true, 4); $info = array(); // general $info['count'] = count($fi); $info['average'] = $this->avgsize($fi); if($nice) $info['average'] = $this->animal->manager->nicesize($info['average']); $info['total'] = $this->totalsize($fi); if($nice) $info['total'] = $this->animal->manager->nicesize($info['total']); // biggest page $bg = $this->biggest($fi); if($nice) { $info['biggest'] = $this->elhtmlurl($bg).' ('.$this->animal->manager->nicesize($fi[$bg]['size']).')'; }else{ $info['biggest'] = array( 'size' => $fi[$bg]['size'], 'url' => $this->elurl($bg), 'namespace' => $bg ); } // smallest page $st = $this->smallest($fi); if($nice) { $info['smallest'] = $this->elhtmlurl($st).' ('.$this->animal->manager->nicesize($fi[$st]['size']).')'; }else{ $info['smallest'] = array( 'size' => $fi[$st]['size'], 'url' => $this->elurl($st), 'namespace' => $st ); } // oldest page (by edit date) $ot = $this->oldest($fi); $m = $this->getmeta($ot); if($nice) { $info['oldest'] = $this->formatedprint( $this->animal->manager->getLang('animal_info_pageschangedescriptor'), array( 'link' => $this->elhtmlurl($ot), 'user' => $m['current']['last_change']['user'], 'date' => $m['current']['last_change']['date'], 'dformat' => $conf['dformat'], 'dunits' => $this->animal->manager->getLang('timedeltaunits') ) ); }else{ $info['oldest'] = array( 'date' => $m['current']['last_change']['date'], 'user' => $m['current']['last_change']['user'], 'url' => $this->elurl($ot), 'namespace' => $ot ); } // latest edited page $lt = $this->latest($fi); $m = $this->getmeta($lt); if($nice) { $info['latest'] = $this->formatedprint( $this->animal->manager->getLang('animal_info_pageschangedescriptor'), array( 'link' => $this->elhtmlurl($lt), 'user' => $m['current']['last_change']['user'], 'date' => $m['current']['last_change']['date'], 'dformat' => $conf['dformat'], 'dunits' => $this->animal->manager->getLang('timedeltaunits') ) ); }else{ $info['latest'] = array( 'date' => $m['current']['last_change']['date'], 'user' => $m['current']['last_change']['user'], 'url' => $this->elurl($lt), 'namespace' => $lt ); } return $info; } /** * Gather infos about animal's medias * @param $nice boolean telling whether output must be formated * @return array of infos descriptors */ public function mediasInfo($nice=false) { if(!$this->animal) return 0; global $conf; $fi = $this->filesinfo($this->animal->getPath().'/data/media', '', true); $info = array(); // general $info['count'] = count($fi); $info['average'] = $this->avgsize($fi); if($nice) $info['average'] = $this->animal->manager->nicesize($info['average']); $info['total'] = $this->totalsize($fi); if($nice) $info['total'] = $this->animal->manager->nicesize($info['total']); // biggest media $bg = $this->biggest($fi); if($nice) { $info['biggest'] = $this->elhtmlurl($bg, true).' ('.$this->animal->manager->nicesize($fi[$bg]['size']).')'; }else{ $info['biggest'] = array( 'size' => $fi[$bg]['size'], 'url' => $this->elurl($bg, true), 'namespace' => $bg ); } // smallest media $st = $this->smallest($fi); if($nice) { $info['smallest'] = $this->elhtmlurl($st, true).' ('.$this->animal->manager->nicesize($fi[$st]['size']).')'; }else{ $info['smallest'] = array( 'size' => $fi[$st]['size'], 'url' => $this->elurl($st, true), 'namespace' => $st ); } return $info; } } ?>