1<?php 2/** 3 * statistics plugin 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <gohr@splitbrain.org> 7 */ 8 9// must be run within Dokuwiki 10if(!defined('DOKU_INC')) die(); 11 12/** 13 * All DokuWiki plugins to extend the admin function 14 * need to inherit from this class 15 */ 16class admin_plugin_statistics extends DokuWiki_Admin_Plugin { 17 /** @var string the currently selected page */ 18 protected $opt = ''; 19 20 /** @var string from date in YYYY-MM-DD */ 21 protected $from = ''; 22 /** @var string to date in YYYY-MM-DD */ 23 protected $to = ''; 24 /** @var int Offset to use when displaying paged data */ 25 protected $start = 0; 26 27 /** @var string MySQL timelimit statement */ 28 protected $tlimit = ''; 29 30 /** @var helper_plugin_statistics */ 31 protected $hlp; 32 33 /** 34 * Available statistic pages 35 */ 36 protected $pages = array( 37 'dashboard' => 1, 38 39 'content' => array( 40 'page', 41 'edits', 42 'images', 43 'downloads', 44 'history', 45 ), 46 'users' => array( 47 'topuser', 48 'topeditor', 49 'topgroup', 50 'topgroupedit', 51 'seenusers', 52 ), 53 'links' => array( 54 'referer', 55 'newreferer', 56 'outlinks', 57 ), 58 'search' => array( 59 'searchengines', 60 'searchphrases', 61 'searchwords', 62 'internalsearchphrases', 63 'internalsearchwords', 64 ), 65 'technology' => array( 66 'browsers', 67 'os', 68 'countries', 69 'resolution', 70 'viewport', 71 ), 72 ); 73 74 /** @var array keeps a list of all real content pages, generated from above array */ 75 protected $allowedpages = array(); 76 77 /** 78 * Initialize the helper 79 */ 80 public function __construct() { 81 $this->hlp = plugin_load('helper', 'statistics'); 82 83 // build a list of pages 84 foreach($this->pages as $key => $val) { 85 if(is_array($val)) { 86 $this->allowedpages = array_merge($this->allowedpages, $val); 87 }else { 88 $this->allowedpages[] = $key; 89 } 90 } 91 } 92 93 /** 94 * Access for managers allowed 95 */ 96 public function forAdminOnly() { 97 return false; 98 } 99 100 /** 101 * return sort order for position in admin menu 102 */ 103 public function getMenuSort() { 104 return 350; 105 } 106 107 /** 108 * handle user request 109 */ 110 public function handle() { 111 $this->opt = preg_replace('/[^a-z]+/', '', $_REQUEST['opt']); 112 if(!in_array($this->opt, $this->allowedpages)) $this->opt = 'dashboard'; 113 114 $this->start = (int) $_REQUEST['s']; 115 $this->setTimeframe($_REQUEST['f'], $_REQUEST['t']); 116 } 117 118 /** 119 * set limit clause 120 */ 121 public function setTimeframe($from, $to) { 122 // swap if wrong order 123 if($from > $to) list($from, $to) = array($to, $from); 124 125 $this->tlimit = $this->hlp->Query()->mktlimit($from, $to); 126 $this->from = $from; 127 $this->to = $to; 128 } 129 130 /** 131 * Output the Statistics 132 */ 133 function html() { 134 echo '<div id="plugin__statistics">'; 135 echo '<h1>' . $this->getLang('menu') . '</h1>'; 136 $this->html_timeselect(); 137 tpl_flush(); 138 139 $method = 'html_' . $this->opt; 140 if(method_exists($this, $method)) { 141 echo '<div class="plg_stats_' . $this->opt . '">'; 142 echo '<h2>' . $this->getLang($this->opt) . '</h2>'; 143 $this->$method(); 144 echo '</div>'; 145 } 146 echo '</div>'; 147 } 148 149 /** 150 * Return the TOC 151 * 152 * @return array 153 */ 154 function getTOC() { 155 $toc = array(); 156 foreach($this->pages as $key => $info) { 157 if (is_array($info)) { 158 159 $toc[] = html_mktocitem( 160 '', 161 $this->getLang($key), 162 1, 163 '' 164 ); 165 166 foreach($info as $page) { 167 $toc[] = html_mktocitem( 168 '?do=admin&page=statistics&opt=' . $page . '&f=' . $this->from . '&t=' . $this->to, 169 $this->getLang($page), 170 2, 171 '' 172 ); 173 } 174 } else { 175 $toc[] = html_mktocitem( 176 '?do=admin&page=statistics&opt=' . $key . '&f=' . $this->from . '&t=' . $this->to, 177 $this->getLang($key), 178 1, 179 '' 180 ); 181 } 182 } 183 return $toc; 184 } 185 186 function html_graph($name, $width, $height) { 187 $url = DOKU_BASE . 'lib/plugins/statistics/img.php?img=' . $name . 188 '&f=' . $this->from . '&t=' . $this->to; 189 echo '<img src="' . $url . '" class="graph" width="' . $width . '" height="' . $height . '"/>'; 190 } 191 192 /** 193 * Outputs pagination links 194 * 195 * @param int $limit 196 * @param int $next 197 */ 198 function html_pager($limit, $next) { 199 echo '<div class="plg_stats_pager">'; 200 201 if($this->start > 0) { 202 $go = max($this->start - $limit, 0); 203 echo '<a href="?do=admin&page=statistics&opt=' . $this->opt . '&f=' . $this->from . '&t=' . $this->to . '&s=' . $go . '" class="prev button">' . $this->getLang('prev') . '</a>'; 204 } 205 206 if($next) { 207 $go = $this->start + $limit; 208 echo '<a href="?do=admin&page=statistics&opt=' . $this->opt . '&f=' . $this->from . '&t=' . $this->to . '&s=' . $go . '" class="next button">' . $this->getLang('next') . '</a>'; 209 } 210 echo '</div>'; 211 } 212 213 /** 214 * Print the time selection menu 215 */ 216 function html_timeselect() { 217 $today = date('Y-m-d'); 218 $last1 = date('Y-m-d', time() - (60 * 60 * 24)); 219 $last7 = date('Y-m-d', time() - (60 * 60 * 24 * 7)); 220 $last30 = date('Y-m-d', time() - (60 * 60 * 24 * 30)); 221 222 echo '<div class="plg_stats_timeselect">'; 223 echo '<span>' . $this->getLang('time_select') . '</span> '; 224 225 echo '<form action="'.DOKU_SCRIPT.'" method="get">'; 226 echo '<input type="hidden" name="do" value="admin" />'; 227 echo '<input type="hidden" name="page" value="statistics" />'; 228 echo '<input type="hidden" name="opt" value="' . $this->opt . '" />'; 229 echo '<input type="text" name="f" value="' . $this->from . '" class="edit datepicker" />'; 230 echo '<input type="text" name="t" value="' . $this->to . '" class="edit datepicker" />'; 231 echo '<input type="submit" value="go" class="button" />'; 232 echo '</form>'; 233 234 echo '<ul>'; 235 foreach(array('today', 'last1', 'last7', 'last30') as $time) { 236 echo '<li>'; 237 echo '<a href="?do=admin&page=statistics&opt=' . $this->opt . '&f=' . $$time . '&t=' . $today . '">'; 238 echo $this->getLang('time_' . $time); 239 echo '</a>'; 240 echo '</li>'; 241 } 242 echo '</ul>'; 243 244 echo '</div>'; 245 } 246 247 /** 248 * Print an introductionary screen 249 */ 250 function html_dashboard() { 251 echo '<p>' . $this->getLang('intro_dashboard') . '</p>'; 252 253 // general info 254 echo '<div class="plg_stats_top">'; 255 $result = $this->hlp->Query()->aggregate($this->tlimit); 256 257 echo '<ul class="left">'; 258 foreach(array('pageviews', 'sessions', 'visitors', 'users', 'logins', 'current') as $name) { 259 echo '<li><div class="li">' . sprintf($this->getLang('dash_' . $name), $result[$name]) . '</div></li>'; 260 } 261 echo '</ul>'; 262 263 echo '<ul class="left">'; 264 foreach(array('bouncerate', 'timespent', 'avgpages', 'newvisitors', 'registrations') as $name) { 265 echo '<li><div class="li">' . sprintf($this->getLang('dash_' . $name), $result[$name]) . '</div></li>'; 266 } 267 echo '</ul>'; 268 269 echo '<br style="clear: left" />'; 270 271 $this->html_graph('dashboardviews', 700, 280); 272 $this->html_graph('dashboardwiki', 700, 280); 273 echo '</div>'; 274 275 // top pages today 276 echo '<div>'; 277 echo '<h2>' . $this->getLang('dash_mostpopular') . '</h2>'; 278 $result = $this->hlp->Query()->pages($this->tlimit, $this->start, 15); 279 $this->html_resulttable($result); 280 echo '<a href="?do=admin&page=statistics&opt=page&f=' . $this->from . '&t=' . $this->to . '" class="more button">' . $this->getLang('more') . '</a>'; 281 echo '</div>'; 282 283 // top referer today 284 echo '<div>'; 285 echo '<h2>' . $this->getLang('dash_newincoming') . '</h2>'; 286 $result = $this->hlp->Query()->newreferer($this->tlimit, $this->start, 15); 287 $this->html_resulttable($result); 288 echo '<a href="?do=admin&page=statistics&opt=newreferer&f=' . $this->from . '&t=' . $this->to . '" class="more button">' . $this->getLang('more') . '</a>'; 289 echo '</div>'; 290 291 // top searches today 292 echo '<div>'; 293 echo '<h2>' . $this->getLang('dash_topsearch') . '</h2>'; 294 $result = $this->hlp->Query()->searchphrases(true, $this->tlimit, $this->start, 15); 295 $this->html_resulttable($result); 296 echo '<a href="?do=admin&page=statistics&opt=searchphrases&f=' . $this->from . '&t=' . $this->to . '" class="more button">' . $this->getLang('more') . '</a>'; 297 echo '</div>'; 298 } 299 300 function html_history() { 301 echo '<p>' . $this->getLang('intro_history') . '</p>'; 302 $this->html_graph('history_page_count', 600, 200); 303 $this->html_graph('history_page_size', 600, 200); 304 $this->html_graph('history_media_count', 600, 200); 305 $this->html_graph('history_media_size', 600, 200); 306 } 307 308 function html_countries() { 309 echo '<p>' . $this->getLang('intro_countries') . '</p>'; 310 $this->html_graph('countries', 400, 200); 311 $result = $this->hlp->Query()->countries($this->tlimit, $this->start, 150); 312 $this->html_resulttable($result, '', 150); 313 } 314 315 function html_page() { 316 echo '<p>' . $this->getLang('intro_page') . '</p>'; 317 $result = $this->hlp->Query()->pages($this->tlimit, $this->start, 150); 318 $this->html_resulttable($result, '', 150); 319 } 320 321 function html_edits() { 322 echo '<p>' . $this->getLang('intro_edits') . '</p>'; 323 $result = $this->hlp->Query()->edits($this->tlimit, $this->start, 150); 324 $this->html_resulttable($result, '', 150); 325 } 326 327 function html_images() { 328 echo '<p>' . $this->getLang('intro_images') . '</p>'; 329 330 $result = $this->hlp->Query()->imagessum($this->tlimit); 331 echo '<p>'; 332 echo sprintf($this->getLang('trafficsum'), $result[0]['cnt'], filesize_h($result[0]['filesize'])); 333 echo '</p>'; 334 335 $result = $this->hlp->Query()->images($this->tlimit, $this->start, 150); 336 $this->html_resulttable($result, '', 150); 337 } 338 339 function html_downloads() { 340 echo '<p>' . $this->getLang('intro_downloads') . '</p>'; 341 342 $result = $this->hlp->Query()->downloadssum($this->tlimit); 343 echo '<p>'; 344 echo sprintf($this->getLang('trafficsum'), $result[0]['cnt'], filesize_h($result[0]['filesize'])); 345 echo '</p>'; 346 347 $result = $this->hlp->Query()->downloads($this->tlimit, $this->start, 150); 348 $this->html_resulttable($result, '', 150); 349 } 350 351 function html_browsers() { 352 echo '<p>' . $this->getLang('intro_browsers') . '</p>'; 353 $this->html_graph('browsers', 400, 200); 354 $result = $this->hlp->Query()->browsers($this->tlimit, $this->start, 150, true); 355 $this->html_resulttable($result, '', 150); 356 } 357 358 function html_topuser(){ 359 echo '<p>' . $this->getLang('intro_topuser') . '</p>'; 360 $this->html_graph('topuser', 400, 200); 361 $result = $this->hlp->Query()->topuser($this->tlimit, $this->start, 150, true); 362 $this->html_resulttable($result, '', 150); 363 } 364 365 function html_topeditor(){ 366 echo '<p>' . $this->getLang('intro_topeditor') . '</p>'; 367 $this->html_graph('topeditor', 400, 200); 368 $result = $this->hlp->Query()->topeditor($this->tlimit, $this->start, 150, true); 369 $this->html_resulttable($result, '', 150); 370 } 371 372 function html_topgroup(){ 373 echo '<p>' . $this->getLang('intro_topgroup') . '</p>'; 374 $this->html_graph('topgroup', 400, 200); 375 $result = $this->hlp->Query()->topgroup($this->tlimit, $this->start, 150, true); 376 $this->html_resulttable($result, '', 150); 377 } 378 379 function html_topgroupedit(){ 380 echo '<p>' . $this->getLang('intro_topgroupedit') . '</p>'; 381 $this->html_graph('topgroupedit', 400, 200); 382 $result = $this->hlp->Query()->topgroupedit($this->tlimit, $this->start, 150, true); 383 $this->html_resulttable($result, '', 150); 384 } 385 386 function html_os() { 387 echo '<p>' . $this->getLang('intro_os') . '</p>'; 388 $this->html_graph('os', 400, 200); 389 $result = $this->hlp->Query()->os($this->tlimit, $this->start, 150, true); 390 $this->html_resulttable($result, '', 150); 391 } 392 393 function html_referer() { 394 $result = $this->hlp->Query()->aggregate($this->tlimit); 395 396 $all = $result['search'] + $result['external'] + $result['direct']; 397 398 if($all) { 399 printf( 400 '<p>' . $this->getLang('intro_referer') . '</p>', 401 $all, $result['direct'], (100 * $result['direct'] / $all), 402 $result['search'], (100 * $result['search'] / $all), $result['external'], 403 (100 * $result['external'] / $all) 404 ); 405 } 406 407 $result = $this->hlp->Query()->referer($this->tlimit, $this->start, 150); 408 $this->html_resulttable($result, '', 150); 409 } 410 411 function html_newreferer() { 412 echo '<p>' . $this->getLang('intro_newreferer') . '</p>'; 413 414 $result = $this->hlp->Query()->newreferer($this->tlimit, $this->start, 150); 415 $this->html_resulttable($result, '', 150); 416 } 417 418 function html_outlinks() { 419 echo '<p>' . $this->getLang('intro_outlinks') . '</p>'; 420 $result = $this->hlp->Query()->outlinks($this->tlimit, $this->start, 150); 421 $this->html_resulttable($result, '', 150); 422 } 423 424 function html_searchphrases() { 425 echo '<p>' . $this->getLang('intro_searchphrases') . '</p>'; 426 $result = $this->hlp->Query()->searchphrases(true, $this->tlimit, $this->start, 150); 427 $this->html_resulttable($result, '', 150); 428 } 429 430 function html_searchwords() { 431 echo '<p>' . $this->getLang('intro_searchwords') . '</p>'; 432 $result = $this->hlp->Query()->searchwords(true, $this->tlimit, $this->start, 150); 433 $this->html_resulttable($result, '', 150); 434 } 435 436 function html_internalsearchphrases() { 437 echo '<p>' . $this->getLang('intro_internalsearchphrases') . '</p>'; 438 $result = $this->hlp->Query()->searchphrases(false, $this->tlimit, $this->start, 150); 439 $this->html_resulttable($result, '', 150); 440 } 441 442 function html_internalsearchwords() { 443 echo '<p>' . $this->getLang('intro_internalsearchwords') . '</p>'; 444 $result = $this->hlp->Query()->searchwords(false, $this->tlimit, $this->start, 150); 445 $this->html_resulttable($result, '', 150); 446 } 447 448 function html_searchengines() { 449 echo '<p>' . $this->getLang('intro_searchengines') . '</p>'; 450 $this->html_graph('searchengines', 400, 200); 451 $result = $this->hlp->Query()->searchengines($this->tlimit, $this->start, 150); 452 $this->html_resulttable($result, '', 150); 453 } 454 455 function html_resolution() { 456 echo '<p>' . $this->getLang('intro_resolution') . '</p>'; 457 $this->html_graph('resolution', 650, 490); 458 $result = $this->hlp->Query()->resolution($this->tlimit, $this->start, 150); 459 $this->html_resulttable($result, '', 150); 460 } 461 462 function html_viewport() { 463 echo '<p>' . $this->getLang('intro_viewport') . '</p>'; 464 $this->html_graph('viewport', 650, 490); 465 $result = $this->hlp->Query()->viewport($this->tlimit, $this->start, 150); 466 $this->html_resulttable($result, '', 150); 467 } 468 469 function html_seenusers() { 470 echo '<p>' . $this->getLang('intro_seenusers') . '</p>'; 471 $result = $this->hlp->Query()->seenusers($this->tlimit, $this->start, 150); 472 $this->html_resulttable($result, '', 150); 473 } 474 475 /** 476 * Display a result in a HTML table 477 */ 478 function html_resulttable($result, $header = '', $pager = 0) { 479 echo '<table class="inline">'; 480 if(is_array($header)) { 481 echo '<tr>'; 482 foreach($header as $h) { 483 echo '<th>' . hsc($h) . '</th>'; 484 } 485 echo '</tr>'; 486 } 487 488 $count = 0; 489 if(is_array($result)) foreach($result as $row) { 490 echo '<tr>'; 491 foreach($row as $k => $v) { 492 if($k == 'res_x') continue; 493 if($k == 'res_y') continue; 494 495 echo '<td class="plg_stats_X' . $k . '">'; 496 if($k == 'page') { 497 echo '<a href="' . wl($v) . '" class="wikilink1">'; 498 echo hsc($v); 499 echo '</a>'; 500 } elseif($k == 'media') { 501 echo '<a href="' . ml($v) . '" class="wikilink1">'; 502 echo hsc($v); 503 echo '</a>'; 504 } elseif($k == 'filesize') { 505 echo filesize_h($v); 506 } elseif($k == 'url') { 507 $url = hsc($v); 508 $url = preg_replace('/^https?:\/\/(www\.)?/', '', $url); 509 if(strlen($url) > 45) { 510 $url = substr($url, 0, 30) . ' … ' . substr($url, -15); 511 } 512 echo '<a href="' . $v . '" class="urlextern">'; 513 echo $url; 514 echo '</a>'; 515 } elseif($k == 'ilookup') { 516 echo '<a href="' . wl('', array('id' => $v, 'do' => 'search')) . '">Search</a>'; 517 } elseif($k == 'lookup') { 518 echo '<a href="http://www.google.com/search?q=' . rawurlencode($v) . '">'; 519 echo '<img src="' . DOKU_BASE . 'lib/plugins/statistics/ico/search/google.png" alt="Google" border="0" />'; 520 echo '</a> '; 521 522 echo '<a href="http://search.yahoo.com/search?p=' . rawurlencode($v) . '">'; 523 echo '<img src="' . DOKU_BASE . 'lib/plugins/statistics/ico/search/yahoo.png" alt="Yahoo!" border="0" />'; 524 echo '</a> '; 525 526 echo '<a href="http://www.bing.com/search?q=' . rawurlencode($v) . '">'; 527 echo '<img src="' . DOKU_BASE . 'lib/plugins/statistics/ico/search/bing.png" alt="Bing" border="0" />'; 528 echo '</a> '; 529 530 } elseif($k == 'engine') { 531 include_once(dirname(__FILE__) . '/inc/searchengines.php'); 532 if(isset($SEARCHENGINEINFO[$v])) { 533 echo '<a href="' . $SEARCHENGINEINFO[$v][1] . '">' . $SEARCHENGINEINFO[$v][0] . '</a>'; 534 } else { 535 echo hsc(ucwords($v)); 536 } 537 } elseif($k == 'eflag') { 538 $this->html_icon('search', $v); 539 } elseif($k == 'bflag') { 540 $this->html_icon('browser', $v); 541 } elseif($k == 'osflag') { 542 $this->html_icon('os', $v); 543 } elseif($k == 'cflag') { 544 $this->html_icon('flags', $v); 545 } elseif($k == 'html') { 546 echo $v; 547 } else { 548 echo hsc($v); 549 } 550 echo '</td>'; 551 } 552 echo '</tr>'; 553 554 if($pager && ($count == $pager)) break; 555 $count++; 556 } 557 echo '</table>'; 558 559 if($pager) $this->html_pager($pager, count($result) > $pager); 560 } 561 562 function html_icon($type, $value) { 563 $value = strtolower(preg_replace('/[^\w]+/', '', $value)); 564 $value = str_replace(' ', '_', $value); 565 $file = 'lib/plugins/statistics/ico/' . $type . '/' . $value . '.png'; 566 if($type == 'flags') { 567 $w = 18; 568 $h = 12; 569 } else { 570 $w = 16; 571 $h = 16; 572 } 573 if(file_exists(DOKU_INC . $file)) { 574 echo '<img src="' . DOKU_BASE . $file . '" alt="' . hsc($value) . '" width="' . $w . '" height="' . $h . '" />'; 575 } 576 } 577} 578