1 <?php rcs_id('$Id: SystemInfo.php,v 1.3 2002-09-18 18:34:13 dairiki Exp $');
3 * Usage: <?plugin SystemInfo all ?>
4 * or <?plugin SystemInfo pagestats cachestats discspace hitstats ?>
5 * or <?plugin SystemInfo version ?>
6 * or <?plugin SystemInfo current_theme ?>
7 * or <?plugin SystemInfo PHPWIKI_DIR ?>
9 * Provide access to phpwiki's lower level system information.
10 * version, CHARSET, pagestats, SERVER_NAME, database, discspace,
11 * cachestats, userstats, linkstats, accessstats, hitstats, revisionstats,
12 * interwikilinks, imageextensions, wikiwordregexp, availableplugins, downloadurl, ...
14 * In spirit to http://www.ecyrd.com/JSPWiki/SystemInfo.jsp
16 * Todo: Some calculations are heavy (~5-8 secs), so we should cache the result.
17 * In the page or with WikiPluginCached?
19 //require_once "lib/WikiPluginCached.php";
21 class WikiPlugin_SystemInfo
22 //extends WikiPluginCached
25 function getPluginType() {
26 return PLUGIN_CACHED_HTML;
29 return _("SystemInfo");
32 function getDescription() {
33 return _("Provide access to PhpWiki's lower level system information.");
35 function getExpire($dbi, $argarray, $request) {
36 return '+1800'; // 30 minutes
38 function getHtml($dbi, $argarray, $request) {
39 $loader = new WikiPluginLoader;
40 return $loader->expandPI('<?plugin SystemInfo '
41 . WikiPluginCached::glueArgs($argarray) // all
45 function getDefaultArguments() {
47 'seperator' => ' ', // on multiple args
53 global $DBParams, $request;
54 $s = _("db type") . ": {$DBParams['dbtype']}, ";
55 switch ($DBParams['dbtype']) {
58 $dsn = $DBParams['dsn'];
59 $s .= _("db backend") . ": ";
60 $s .= ($DBParams['dbtype'] == 'SQL') ? 'PearDB' : 'ADODB';
61 if (preg_match('/^(\w+):/', $dsn, $m)) {
67 $s .= _("dba handler") . ": {$DBParams['dba_handler']}, ";
70 // $s .= "cvs stuff: , ";
73 // $s .= "flatfile stuff: , ";
76 $s .= _("timeout") . ": {$DBParams['timeout']}";
79 function cachestats() {
80 global $DBParams, $request;
81 if (! defined('USECACHE') or !USECACHE)
82 return _("no cache used");
83 $dbi = $request->getDbh();
84 $cache = $dbi->_cache;
85 $s = _("cached pagedata") . ": " . count($cache->_pagedata_cache);
86 $s .= ", " . _("cached versiondata") . ": " . count($cache->_versiondata_cache);
87 //$s .= ", glv size: " . count($cache->_glv_cache);
88 //$s .= ", cache hits: ?";
89 //$s .= ", cache misses: ?";
92 function ExpireParams() {
94 $s = sprintf(_("Keep up to %d major edits, but keep them no longer than %d days."),
95 $ExpireParams['major']['keep'], $ExpireParams['major']['max_age']);
96 $s .= sprintf(_(" Keep up to %d minor edits, but keep them no longer than %d days."),
97 $ExpireParams['minor']['keep'], $ExpireParams['minor']['max_age']);
98 $s .= sprintf(_(" Keep the latest contributions of the last %d authors up to %d days."),
99 $ExpireParams['author']['keep'], $ExpireParams['author']['max_age']);
100 $s .= sprintf(_(" Additionally, try to keep the latest contributions of all authors in the last %d days (even if there are more than %d of them,) but in no case keep more than %d unique author revisions."),
101 $ExpireParams['author']['min_age'], $ExpireParams['author']['keep'], $ExpireParams['author']['max_keep']);
104 function pagestats() {
107 $dbi = $request->getDbh();
108 $include_empty = true;
109 $iter = $dbi->getAllPages($include_empty);
110 while ($page = $iter->next()) $e++;
111 $s = sprintf(_("%d pages"), $e);
112 $include_empty = false;
113 $iter = $dbi->getAllPages($include_empty);
114 while ($page = $iter->next()) $a++;
115 $s .= ", " . sprintf(_("%d not-empty pages"), $a);
117 // $s .= ", " . sprintf(_("earliest page from %s"), $earliestdate);
118 // $s .= ", " . sprintf(_("latest page from %s"), $latestdate);
119 // $s .= ", " . sprintf(_("latest pagerevision from %s"), $latestrevdate);
122 //What kind of link statistics?
123 // total links in, total links out, mean links per page, ...
124 // Any useful numbers similar to a VisualWiki interestmap?
125 function linkstats() {
129 // number of homepages: easy
130 // number of anonymous users?
131 // calc this from accesslog info?
132 // number of anonymous edits?
133 // easy. related to the view/edit rate in accessstats.
134 function userstats() {
136 $dbi = $request->getDbh();
138 $page_iter = $dbi->getAllPages(true);
139 while ($page = $page_iter->next()) {
140 if ($page->isUserPage(true)) // check if the admin is there. if not add him to the authusers.
143 $s = sprintf(_("%d homepages"), $h);
144 // $s .= ", " . sprintf(_("%d anonymous users"), $au); // ??
145 // $s .= ", " . sprintf(_("%d anonymous edits"), $ae); // see recentchanges
146 // $s .= ", " . sprintf(_("%d authenticated users"), $auth); // users with password set
147 // $s .= ", " . sprintf(_("%d externally authenticated users"), $extauth); // query AuthDB?
150 //only from logging info possible. = hitstats per time.
151 // total hits per day/month/year
153 function accessstats() {
157 // only absolute numbers, not for any time interval. see accessstats
158 // some useful number derived from the curve of the hit stats.
159 // total, max, mean, median, stddev;
160 // %d pages less than 3 hits (<10%) <10% percent of the leastpopular
161 // %d pages more than 100 hits (>90%) >90% percent of the mostpopular
162 function hitstats() {
164 $dbi = $request->getDbh();
165 $total = 0; $max = 0;
167 $page_iter = $dbi->getAllPages(true);
168 while ($page = $page_iter->next()) {
169 if ($current = $page->getCurrentRevision() and (! $current->hasDefaultContents())) {
170 $h = $page->get('hits');
179 $median_i = (int) $n / 2;
181 $median = $hits[$median_i];
183 $median = $hits[$median_i];
184 $stddev = stddev(&$hits,$total);
186 $s = sprintf(_("total hits: %d"), $total);
187 $s .= ", " . sprintf(_("max: %d"), $max);
188 $s .= ", " . sprintf(_("mean: %2.3f"), $total / $n);
189 $s .= ", " . sprintf(_("median: %d"), $median);
190 $s .= ", " . sprintf(_("stddev: %2.3f"), $stddev);
192 $mintreshold = $max * $percentage / 100.0; // lower than 10% of the hits
193 reset($hits); $nmin = $hits[0] < $mintreshold ? 1 : 0;
194 while (next($hits) < $mintreshold)
196 $maxtreshold = $max - $mintreshold; // more than 90% of the hits
197 end($hits); $nmax = 1;
198 while (prev($hits) > $maxtreshold)
200 $s .= "; " . sprintf(_("%d pages with less than %d hits (<%d%%)."), $nmin, $mintreshold, $percentage);
201 $s .= " " . sprintf(_("%d page(s) with more than %d hits (>%d%%)."), $nmax, $maxtreshold, 100-$percentage);
204 function revisionstats() {
206 $dbi = $request->getDbh();
207 $total = 0; $max = 0;
209 $page_iter = $dbi->getAllPages(true);
210 while ($page = $page_iter->next()) {
211 if ($current = $page->getCurrentRevision() and (! $current->hasDefaultContents())) {
212 //$ma = $page->get('major');
213 //$mi = $page->get('minor');
219 // size of databases/files/cvs are possible plus the known size of the app.
220 // Todo: cache this costly operation!
221 function discspace() {
224 $appsize = `du -s $dir | cut -f1`;
226 if (in_array($DBParams['dbtype'],array('SQL','ADODB'))) {
228 } elseif ($DBParams['dbtype'] == 'dba') {
230 $dbdir = $DBParams['directory'];
231 if ($DBParams['dba_handler'] == 'db3')
232 $pagesize = filesize($DBParams['directory']."/wiki_pagedb.db3") / 1024;
233 // if issubdirof($dbdir, $dir) $appsize -= $pagesize;
234 } else { // flatfile, cvs
235 $dbdir = $DBParams['directory'];
236 $pagesize = `du -s $dbdir`;
237 // if issubdirof($dbdir, $dir) $appsize -= $pagesize;
239 $s = sprintf(_("Application size: %d Kb"), $appsize);
241 $s .= ", " . sprintf(_("Pagedata size: %d Kb", $pagesize));
245 function inlineimages () {
246 return implode(' ',explode('|',$GLOBALS['InlineImages']));
248 function wikinameregexp () {
249 return $GLOBALS['WikiNameRegexp'];
251 function allowedprotocols () {
252 return implode(' ',explode('|',$GLOBALS['AllowedProtocols']));
254 function available_plugins () {
255 $fileset = new FileSet(FindFile('lib/plugin'),'*.php');
256 $list = $fileset->getFiles();
259 return sprintf(_("Total %d plugins: "),count($list)) .
260 implode(', ',array_map(create_function('$f','return substr($f,0,-4);'),$list));
262 function supported_languages () {
263 $available_languages = array('en');
264 $dir_root = PHPWIKI_DIR . '/locale/';
265 $dir = dir($dir_root);
267 while($entry = $dir->read()) {
268 if (is_dir($dir_root.$entry) and (substr($entry,0,1) != '.') and
269 $entry != 'po' and $entry != 'CVS') {
270 array_push($available_languages,$entry);
275 natcasesort($available_languages);
277 return sprintf(_("Total %d languages: "),count($available_languages)) .
278 implode(', ',$available_languages) . ". " .
279 sprintf(_("Current language: '%s'"), $GLOBALS['LANG']) .
280 ((DEFAULT_LANGUAGE != $GLOBALS['LANG'])
281 ? ". " . sprintf(_("System default: '%s'"), DEFAULT_LANGUAGE)
285 function supported_themes () {
287 $available_themes = array();
288 $dir_root = PHPWIKI_DIR . '/themes/';
289 $dir = dir($dir_root);
291 while($entry = $dir->read()) {
292 if (is_dir($dir_root.$entry) and (substr($entry,0,1) != '.')
294 array_push($available_themes,$entry);
299 natcasesort($available_themes);
300 return sprintf(_("Total %d themes: "),count($available_themes)) .
301 implode(', ',$available_themes) . ". " .
302 sprintf(_("Current theme: '%s'"), $Theme->_name) .
303 ((THEME != $Theme->_name)
304 ? ". " . sprintf(_("System default: '%s'"), THEME)
309 function call ($arg, &$availableargs) {
310 if (!empty($availableargs[$arg]))
311 return $availableargs[$arg]();
312 elseif (method_exists($this,$arg)) // any defined SystemInfo->method()system
313 return call_user_func_array(array(&$this, $arg),'');
314 elseif (defined($arg) and $arg != 'ADMIN_PASSWD') // any defined constant
315 return constant($arg);
317 return $this->error(sprintf(_("unknown argument '%s' to SystemInfo"),$arg));
321 function run($dbi, $argstr, $request) {
322 // don't parse argstr for name=value pairs. instead we use just 'name'
323 //$args = $this->getArgs($argstr, $request);
324 $args['seperator'] = ' ';
325 $availableargs = // name => callback + 0 args
326 array ('appname' => create_function('',"return 'PhpWiki';"),
327 'version' => create_function('',"return sprintf('%s',PHPWIKI_VERSION);"),
328 'LANG' => create_function('','return $GLOBALS["LANG"];'),
329 'LC_ALL' => create_function('','return setlocale(LC_ALL, 0);'),
330 'current_language' => create_function('','return $GLOBALS["LANG"];'),
331 'system_language' => create_function('','return DEFAULT_LANGUAGE;'),
332 'current_theme' => create_function('','return $GLOBALS["Theme"]->_name;'),
333 'system_theme' => create_function('','return THEME;'),
334 // more here or as method.
335 '' => create_function('',"return 'dummy';")
337 // split the argument string by any number of commas or space characters,
338 // which include " ", \r, \t, \n and \f
339 $allargs = preg_split("/[\s,]+/",$argstr,-1,PREG_SPLIT_NO_EMPTY);
340 if (in_array('all',$allargs) or in_array('table',$allargs)) {
341 $allargs = array('appname' => _("Application name"),
342 'version' => _("PhpWiki engine version"),
343 'database' => _("Database"),
344 'cachestats' => _("Cache statistics"),
345 'pagestats' => _("Page statistics"),
346 //'revisionstats' => _("Page revision statistics"),
347 //'linkstats' => _("Link statistics"),
348 'userstats' => _("User statistics"),
349 //'accessstats' => _("Access statistics"),
350 'hitstats' => _("Hit statistics"),
351 // 'discspace' => _("Harddisc usage"),
352 'expireparams' => _("Expiry parameters"),
353 'wikinameregexp' => _("Wikiname regexp"),
354 'allowedprotocols' => _("Allowed protocols"),
355 'inlineimages' => _("Inline images"),
356 'available_plugins' => _("Available plugins"),
357 'supported_languages' => _("Supported languages"),
358 'supported_themes' => _("Supported themes"),
362 $table = HTML::table(array('border' => 1,'cellspacing' => 3,'cellpadding' => 3));
363 foreach ($allargs as $arg => $desc) {
365 if (!$desc) $desc = _($arg);
366 $table->pushContent(HTML::tr(HTML::td(HTML::strong($desc . ':')),
367 HTML::td(HTML($this->call($arg,&$availableargs)))));
372 foreach ($allargs as $arg) {
373 $o = $this->call($arg,&$availableargs);
374 if (is_object($o)) return $o;
375 else $output .= ($o . $args['seperator']);
377 // if more than one arg, remove the trailing seperator
378 if ($output) $output = substr($output,0,- strlen($args['seperator']));
379 return HTML($output);
384 /* // autolisp stdlib
385 ;;; Median of the sorted list of numbers. 50% is above and 50% below
386 ;;; "center of a distribution"
387 ;;; Ex: (std-median (std-make-list 100 std-%random)) => 0.5 +- epsilon
388 ;;; (std-median (std-make-list 100 (lambda () (std-random 10))))
389 ;;; => 4.0-5.0 [0..9]
390 ;;; (std-median (std-make-list 99 (lambda () (std-random 10))))
392 ;;; (std-median '(0 0 2 4 12)) => 2
393 ;;; (std-median '(0 0 4 12)) => 2.0
394 (defun STD-MEDIAN (numlst / l)
395 (setq numlst (std-sort numlst '<)) ; don't remove duplicates
396 (if (= 0 (rem (setq l (length numlst)) 2)) ; if even length
397 (* 0.5 (+ (nth (/ l 2) numlst) ; force float!
398 (nth (1- (/ l 2)) numlst))) ; fixed by Serge Pashkov
399 (nth (/ l 2) numlst)))
402 function median($hits) {
406 $median = (int) $n / 2;
407 if (! ($n % 2)) // proper rounding on even length
408 return ($hits[$median] + $hits[$median-1]) * 0.5;
410 return $hits[$median];
413 function rsum($a, $b) {
417 function mean(&$hits,$total=false) {
419 if (!$total) $total = array_reduce($hits,'rsum');
420 return (float) $total / ($n * 1.0);
422 function gensym($prefix = "_gensym") {
424 while (isset($GLOBALS[$prefix.$i])) $i++;
428 /* // autolisp stdlib
429 (defun STD-STANDARD-DEVIATION (numlst / n _dev_m r)
430 (setq n (length numlst)
431 _dev_m (std-mean numlst)
432 r (mapcar (function (lambda (x) (std-sqr (- x _dev_m)))) numlst))
433 (sqrt (* (std-mean r) (/ n (float (- n 1))))))
436 function stddev(&$hits,$total=false) {
438 if (!$total) $total = array_reduce($hits,'rsum');
439 $mean = gensym("_mean");
440 $GLOBALS[$mean] = $total / $n;
441 $cb = "global ${$mean}; return (\$i-${$mean})*(\$i-${$mean});";
442 $r = array_map(create_function('$i',"global ${$mean}; return (\$i-${$mean})*(\$i-${$mean});"),$hits);
443 unset($GLOBALS[$mean]);
444 return (float) sqrt(mean($r,$total) * ($n / (float)($n -1)));
447 function stddev(&$hits,$total=false) {
449 if (!$total) $total = array_reduce($hits,'rsum');
450 $GLOBALS['mean'] = $total / $n;
451 $r = array_map(create_function('$i','global $mean; return ($i-$mean)*($i-$mean);'),$hits);
452 unset($GLOBALS['mean']);
453 return (float) sqrt(mean($r,$total) * ($n / (float)($n -1)));
460 // c-hanging-comment-ender-p: nil
461 // indent-tabs-mode: nil