1 <?php rcs_id('$Id: SystemInfo.php,v 1.4 2002-12-14 23:20:10 carstenklapp 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 _("Provides 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 // hack: suppress error when using sql, so no timeout
77 @$s .= _("timeout:") . " {$DBParams['timeout']}";
80 function cachestats() {
81 global $DBParams, $request;
82 if (! defined('USECACHE') or !USECACHE)
83 return _("no cache used");
84 $dbi = $request->getDbh();
85 $cache = $dbi->_cache;
86 $s = _("cached pagedata:") . " " . count($cache->_pagedata_cache);
87 $s .= ", " . _("cached versiondata:") . " " . count($cache->_versiondata_cache);
88 //$s .= ", glv size: " . count($cache->_glv_cache);
89 //$s .= ", cache hits: ?";
90 //$s .= ", cache misses: ?";
93 function ExpireParams() {
95 $s = sprintf(_("Keep up to %d major edits, but keep them no longer than %d days."),
96 $ExpireParams['major']['keep'], $ExpireParams['major']['max_age']);
97 $s .= sprintf(_(" Keep up to %d minor edits, but keep them no longer than %d days."),
98 $ExpireParams['minor']['keep'], $ExpireParams['minor']['max_age']);
99 $s .= sprintf(_(" Keep the latest contributions of the last %d authors up to %d days."),
100 $ExpireParams['author']['keep'], $ExpireParams['author']['max_age']);
101 $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."),
102 $ExpireParams['author']['min_age'], $ExpireParams['author']['keep'], $ExpireParams['author']['max_keep']);
105 function pagestats() {
108 $dbi = $request->getDbh();
109 $include_empty = true;
110 $iter = $dbi->getAllPages($include_empty);
111 while ($page = $iter->next()) $e++;
112 $s = sprintf(_("%d pages"), $e);
113 $include_empty = false;
114 $iter = $dbi->getAllPages($include_empty);
115 while ($page = $iter->next()) $a++;
116 $s .= ", " . sprintf(_("%d not-empty pages"), $a);
118 // $s .= ", " . sprintf(_("earliest page from %s"), $earliestdate);
119 // $s .= ", " . sprintf(_("latest page from %s"), $latestdate);
120 // $s .= ", " . sprintf(_("latest pagerevision from %s"), $latestrevdate);
123 //What kind of link statistics?
124 // total links in, total links out, mean links per page, ...
125 // Any useful numbers similar to a VisualWiki interestmap?
126 function linkstats() {
130 // number of homepages: easy
131 // number of anonymous users?
132 // calc this from accesslog info?
133 // number of anonymous edits?
134 // easy. related to the view/edit rate in accessstats.
135 function userstats() {
137 $dbi = $request->getDbh();
139 $page_iter = $dbi->getAllPages(true);
140 while ($page = $page_iter->next()) {
141 if ($page->isUserPage(true)) // check if the admin is there. if not add him to the authusers.
144 $s = sprintf(_("%d homepages"), $h);
145 // $s .= ", " . sprintf(_("%d anonymous users"), $au); // ??
146 // $s .= ", " . sprintf(_("%d anonymous edits"), $ae); // see recentchanges
147 // $s .= ", " . sprintf(_("%d authenticated users"), $auth); // users with password set
148 // $s .= ", " . sprintf(_("%d externally authenticated users"), $extauth); // query AuthDB?
151 //only from logging info possible. = hitstats per time.
152 // total hits per day/month/year
154 function accessstats() {
158 // only absolute numbers, not for any time interval. see accessstats
159 // some useful number derived from the curve of the hit stats.
160 // total, max, mean, median, stddev;
161 // %d pages less than 3 hits (<10%) <10% percent of the leastpopular
162 // %d pages more than 100 hits (>90%) >90% percent of the mostpopular
163 function hitstats() {
165 $dbi = $request->getDbh();
166 $total = 0; $max = 0;
168 $page_iter = $dbi->getAllPages(true);
169 while ($page = $page_iter->next()) {
170 if ($current = $page->getCurrentRevision() and (! $current->hasDefaultContents())) {
171 $h = $page->get('hits');
180 $median_i = (int) $n / 2;
182 $median = $hits[$median_i];
184 $median = $hits[$median_i];
185 $stddev = stddev(&$hits,$total);
187 $s = sprintf(_("total hits: %d"), $total);
188 $s .= ", " . sprintf(_("max: %d"), $max);
189 $s .= ", " . sprintf(_("mean: %2.3f"), $total / $n);
190 $s .= ", " . sprintf(_("median: %d"), $median);
191 $s .= ", " . sprintf(_("stddev: %2.3f"), $stddev);
193 $mintreshold = $max * $percentage / 100.0; // lower than 10% of the hits
194 reset($hits); $nmin = $hits[0] < $mintreshold ? 1 : 0;
195 while (next($hits) < $mintreshold)
197 $maxtreshold = $max - $mintreshold; // more than 90% of the hits
198 end($hits); $nmax = 1;
199 while (prev($hits) > $maxtreshold)
201 $s .= "; " . sprintf(_("%d pages with less than %d hits (<%d%%)."), $nmin, $mintreshold, $percentage);
202 $s .= " " . sprintf(_("%d page(s) with more than %d hits (>%d%%)."), $nmax, $maxtreshold, 100-$percentage);
205 function revisionstats() {
207 $dbi = $request->getDbh();
208 $total = 0; $max = 0;
210 $page_iter = $dbi->getAllPages(true);
211 while ($page = $page_iter->next()) {
212 if ($current = $page->getCurrentRevision() and (! $current->hasDefaultContents())) {
213 //$ma = $page->get('major');
214 //$mi = $page->get('minor');
220 // size of databases/files/cvs are possible plus the known size of the app.
221 // Todo: cache this costly operation!
222 function discspace() {
225 $appsize = `du -s $dir | cut -f1`;
227 if (in_array($DBParams['dbtype'],array('SQL','ADODB'))) {
229 } elseif ($DBParams['dbtype'] == 'dba') {
231 $dbdir = $DBParams['directory'];
232 if ($DBParams['dba_handler'] == 'db3')
233 $pagesize = filesize($DBParams['directory']."/wiki_pagedb.db3") / 1024;
234 // if issubdirof($dbdir, $dir) $appsize -= $pagesize;
235 } else { // flatfile, cvs
236 $dbdir = $DBParams['directory'];
237 $pagesize = `du -s $dbdir`;
238 // if issubdirof($dbdir, $dir) $appsize -= $pagesize;
240 $s = sprintf(_("Application size: %d Kb"), $appsize);
242 $s .= ", " . sprintf(_("Pagedata size: %d Kb", $pagesize));
246 function inlineimages () {
247 return implode(' ',explode('|',$GLOBALS['InlineImages']));
249 function wikinameregexp () {
250 return $GLOBALS['WikiNameRegexp'];
252 function allowedprotocols () {
253 return implode(' ',explode('|',$GLOBALS['AllowedProtocols']));
255 function available_plugins () {
256 $fileset = new FileSet(FindFile('lib/plugin'),'*.php');
257 $list = $fileset->getFiles();
260 return sprintf(_("Total %d plugins: "),count($list)) .
261 implode(', ',array_map(create_function('$f','return substr($f,0,-4);'),$list));
263 function supported_languages () {
264 $available_languages = array('en');
265 $dir_root = PHPWIKI_DIR . '/locale/';
266 $dir = dir($dir_root);
268 while($entry = $dir->read()) {
269 if (is_dir($dir_root.$entry) and (substr($entry,0,1) != '.') and
270 $entry != 'po' and $entry != 'CVS') {
271 array_push($available_languages,$entry);
276 natcasesort($available_languages);
278 return sprintf(_("Total of %d languages: "),count($available_languages)) .
279 implode(', ',$available_languages) . ". " .
280 sprintf(_("Current language: '%s'"), $GLOBALS['LANG']) .
281 ((DEFAULT_LANGUAGE != $GLOBALS['LANG'])
282 ? ". " . sprintf(_("Default language: '%s'"), DEFAULT_LANGUAGE)
286 function supported_themes () {
288 $available_themes = array();
289 $dir_root = PHPWIKI_DIR . '/themes/';
290 $dir = dir($dir_root);
292 while($entry = $dir->read()) {
293 if (is_dir($dir_root.$entry) and (substr($entry,0,1) != '.')
295 array_push($available_themes,$entry);
300 natcasesort($available_themes);
301 return sprintf(_("Total of %d themes: "),count($available_themes)) .
302 implode(', ',$available_themes) . ". " .
303 sprintf(_("Current theme: '%s'"), $Theme->_name) .
304 ((THEME != $Theme->_name)
305 ? ". " . sprintf(_("Default theme: '%s'"), THEME)
310 function call ($arg, &$availableargs) {
311 if (!empty($availableargs[$arg]))
312 return $availableargs[$arg]();
313 elseif (method_exists($this,$arg)) // any defined SystemInfo->method()system
314 return call_user_func_array(array(&$this, $arg),'');
315 elseif (defined($arg) and $arg != 'ADMIN_PASSWD') // any defined constant
316 return constant($arg);
318 return $this->error(sprintf(_("unknown argument '%s' to SystemInfo"),$arg));
322 function run($dbi, $argstr, $request) {
323 // don't parse argstr for name=value pairs. instead we use just 'name'
324 //$args = $this->getArgs($argstr, $request);
325 $args['seperator'] = ' ';
326 $availableargs = // name => callback + 0 args
327 array ('appname' => create_function('',"return 'PhpWiki';"),
328 'version' => create_function('',"return sprintf('%s',PHPWIKI_VERSION);"),
329 'LANG' => create_function('','return $GLOBALS["LANG"];'),
330 'LC_ALL' => create_function('','return setlocale(LC_ALL, 0);'),
331 'current_language' => create_function('','return $GLOBALS["LANG"];'),
332 'system_language' => create_function('','return DEFAULT_LANGUAGE;'),
333 'current_theme' => create_function('','return $GLOBALS["Theme"]->_name;'),
334 'system_theme' => create_function('','return THEME;'),
335 // more here or as method.
336 '' => create_function('',"return 'dummy';")
338 // split the argument string by any number of commas or space characters,
339 // which include " ", \r, \t, \n and \f
340 $allargs = preg_split("/[\s,]+/",$argstr,-1,PREG_SPLIT_NO_EMPTY);
341 if (in_array('all',$allargs) or in_array('table',$allargs)) {
342 $allargs = array('appname' => _("Application name"),
343 'version' => _("PhpWiki engine version"),
344 'database' => _("Database"),
345 'cachestats' => _("Cache statistics"),
346 'pagestats' => _("Page statistics"),
347 //'revisionstats' => _("Page revision statistics"),
348 //'linkstats' => _("Link statistics"),
349 'userstats' => _("User statistics"),
350 //'accessstats' => _("Access statistics"),
351 'hitstats' => _("Hit statistics"),
352 // 'discspace' => _("Harddisc usage"),
353 'expireparams' => _("Expiry parameters"),
354 'wikinameregexp' => _("Wikiname regexp"),
355 'allowedprotocols' => _("Allowed protocols"),
356 'inlineimages' => _("Inline images"),
357 'available_plugins' => _("Available plugins"),
358 'supported_languages' => _("Supported languages"),
359 'supported_themes' => _("Supported themes"),
363 $table = HTML::table(array('border' => 1,'cellspacing' => 3,'cellpadding' => 3));
364 foreach ($allargs as $arg => $desc) {
366 if (!$desc) $desc = _($arg);
367 $table->pushContent(HTML::tr(HTML::td(HTML::strong($desc . ':')),
368 HTML::td(HTML($this->call($arg,&$availableargs)))));
373 foreach ($allargs as $arg) {
374 $o = $this->call($arg,&$availableargs);
375 if (is_object($o)) return $o;
376 else $output .= ($o . $args['seperator']);
378 // if more than one arg, remove the trailing seperator
379 if ($output) $output = substr($output,0,- strlen($args['seperator']));
380 return HTML($output);
385 /* // autolisp stdlib
386 ;;; Median of the sorted list of numbers. 50% is above and 50% below
387 ;;; "center of a distribution"
388 ;;; Ex: (std-median (std-make-list 100 std-%random)) => 0.5 +- epsilon
389 ;;; (std-median (std-make-list 100 (lambda () (std-random 10))))
390 ;;; => 4.0-5.0 [0..9]
391 ;;; (std-median (std-make-list 99 (lambda () (std-random 10))))
393 ;;; (std-median '(0 0 2 4 12)) => 2
394 ;;; (std-median '(0 0 4 12)) => 2.0
395 (defun STD-MEDIAN (numlst / l)
396 (setq numlst (std-sort numlst '<)) ; don't remove duplicates
397 (if (= 0 (rem (setq l (length numlst)) 2)) ; if even length
398 (* 0.5 (+ (nth (/ l 2) numlst) ; force float!
399 (nth (1- (/ l 2)) numlst))) ; fixed by Serge Pashkov
400 (nth (/ l 2) numlst)))
403 function median($hits) {
407 $median = (int) $n / 2;
408 if (! ($n % 2)) // proper rounding on even length
409 return ($hits[$median] + $hits[$median-1]) * 0.5;
411 return $hits[$median];
414 function rsum($a, $b) {
418 function mean(&$hits,$total=false) {
420 if (!$total) $total = array_reduce($hits,'rsum');
421 return (float) $total / ($n * 1.0);
423 function gensym($prefix = "_gensym") {
425 while (isset($GLOBALS[$prefix.$i])) $i++;
429 /* // autolisp stdlib
430 (defun STD-STANDARD-DEVIATION (numlst / n _dev_m r)
431 (setq n (length numlst)
432 _dev_m (std-mean numlst)
433 r (mapcar (function (lambda (x) (std-sqr (- x _dev_m)))) numlst))
434 (sqrt (* (std-mean r) (/ n (float (- n 1))))))
437 function stddev(&$hits,$total=false) {
439 if (!$total) $total = array_reduce($hits,'rsum');
440 $mean = gensym("_mean");
441 $GLOBALS[$mean] = $total / $n;
442 $cb = "global ${$mean}; return (\$i-${$mean})*(\$i-${$mean});";
443 $r = array_map(create_function('$i',"global ${$mean}; return (\$i-${$mean})*(\$i-${$mean});"),$hits);
444 unset($GLOBALS[$mean]);
445 return (float) sqrt(mean($r,$total) * ($n / (float)($n -1)));
448 function stddev(&$hits,$total=false) {
450 if (!$total) $total = array_reduce($hits,'rsum');
451 $GLOBALS['mean'] = $total / $n;
452 $r = array_map(create_function('$i','global $mean; return ($i-$mean)*($i-$mean);'),$hits);
453 unset($GLOBALS['mean']);
454 return (float) sqrt(mean($r,$total) * ($n / (float)($n -1)));
461 // c-hanging-comment-ender-p: nil
462 // indent-tabs-mode: nil