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', 'page', 'edits', 'images', 'downloads', 38 'history', 'referer', 'newreferer', 39 'outlinks', 'searchengines', 'searchphrases', 40 'searchwords', 'internalsearchphrases', 41 'internalsearchwords', 'browsers', 'os', 42 'countries', 'resolution', 'viewport', 43 'seenusers' 44 ); 45 46 /** 47 * Initialize the helper 48 */ 49 public function __construct() { 50 $this->hlp = plugin_load('helper', 'statistics'); 51 } 52 53 /** 54 * Access for managers allowed 55 */ 56 public function forAdminOnly() { 57 return false; 58 } 59 60 /** 61 * return sort order for position in admin menu 62 */ 63 public function getMenuSort() { 64 return 350; 65 } 66 67 /** 68 * handle user request 69 */ 70 public function handle() { 71 $this->opt = preg_replace('/[^a-z]+/', '', $_REQUEST['opt']); 72 if(!in_array($this->opt, $this->pages)) $this->opt = 'dashboard'; 73 74 $this->start = (int) $_REQUEST['s']; 75 $this->setTimeframe($_REQUEST['f'], $_REQUEST['t']); 76 } 77 78 /** 79 * set limit clause 80 */ 81 public function setTimeframe($from, $to) { 82 // swap if wrong order 83 if($from > $to) list($from, $to) = array($to, $from); 84 85 $this->tlimit = $this->hlp->Query()->mktlimit($from, $to); 86 $this->from = $from; 87 $this->to = $to; 88 } 89 90 /** 91 * Output the Statistics 92 */ 93 function html() { 94 echo '<div id="plugin__statistics">'; 95 echo '<h1>' . $this->getLang('menu') . '</h1>'; 96 $this->html_timeselect(); 97 tpl_flush(); 98 99 $method = 'html_' . $this->opt; 100 if(method_exists($this, $method)) { 101 echo '<div class="plg_stats_' . $this->opt . '">'; 102 echo '<h2>' . $this->getLang($this->opt) . '</h2>'; 103 $this->$method(); 104 echo '</div>'; 105 } 106 echo '</div>'; 107 } 108 109 /** 110 * Return the TOC 111 * 112 * @return array 113 */ 114 function getTOC() { 115 $toc = array(); 116 foreach($this->pages as $page) { 117 $toc[] = array( 118 'link' => '?do=admin&page=statistics&opt=' . $page . '&f=' . $this->from . '&t=' . $this->to, 119 'title' => $this->getLang($page), 120 'level' => 1, 121 'type' => 'ul' 122 ); 123 } 124 return $toc; 125 } 126 127 function html_graph($name, $width, $height) { 128 $url = DOKU_BASE . 'lib/plugins/statistics/img.php?img=' . $name . 129 '&f=' . $this->from . '&t=' . $this->to; 130 echo '<img src="' . $url . '" class="graph" width="' . $width . '" height="' . $height . '"/>'; 131 } 132 133 /** 134 * Outputs pagination links 135 * 136 * @param int $limit 137 * @param int $next 138 */ 139 function html_pager($limit, $next) { 140 echo '<div class="plg_stats_pager">'; 141 142 if($this->start > 0) { 143 $go = max($this->start - $limit, 0); 144 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>'; 145 } 146 147 if($next) { 148 $go = $this->start + $limit; 149 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>'; 150 } 151 echo '</div>'; 152 } 153 154 /** 155 * Print the time selection menu 156 */ 157 function html_timeselect() { 158 $today = date('Y-m-d'); 159 $last1 = date('Y-m-d', time() - (60 * 60 * 24)); 160 $last7 = date('Y-m-d', time() - (60 * 60 * 24 * 7)); 161 $last30 = date('Y-m-d', time() - (60 * 60 * 24 * 30)); 162 163 echo '<div class="plg_stats_timeselect">'; 164 echo '<span>' . $this->getLang('time_select') . '</span> '; 165 166 echo '<form action="'.DOKU_SCRIPT.'" method="get">'; 167 echo '<input type="hidden" name="do" value="admin" />'; 168 echo '<input type="hidden" name="page" value="statistics" />'; 169 echo '<input type="hidden" name="opt" value="' . $this->opt . '" />'; 170 echo '<input type="text" name="f" value="' . $this->from . '" class="edit datepicker" />'; 171 echo '<input type="text" name="t" value="' . $this->to . '" class="edit datepicker" />'; 172 echo '<input type="submit" value="go" class="button" />'; 173 echo '</form>'; 174 175 echo '<ul>'; 176 foreach(array('today', 'last1', 'last7', 'last30') as $time) { 177 echo '<li>'; 178 echo '<a href="?do=admin&page=statistics&opt=' . $this->opt . '&f=' . $$time . '&t=' . $today . '">'; 179 echo $this->getLang('time_' . $time); 180 echo '</a>'; 181 echo '</li>'; 182 } 183 echo '</ul>'; 184 185 echo '</div>'; 186 } 187 188 /** 189 * Print an introductionary screen 190 */ 191 function html_dashboard() { 192 echo '<p>' . $this->getLang('intro_dashboard') . '</p>'; 193 194 // general info 195 echo '<div class="plg_stats_top">'; 196 $result = $this->hlp->Query()->aggregate($this->tlimit); 197 198 echo '<ul class="left">'; 199 foreach(array('pageviews', 'sessions', 'visitors', 'users', 'logins', 'current') as $name) { 200 echo '<li><div class="li">' . sprintf($this->getLang('dash_' . $name), $result[$name]) . '</div></li>'; 201 } 202 echo '</ul>'; 203 204 echo '<ul class="left">'; 205 foreach(array('bouncerate', 'timespent', 'avgpages', 'newvisitors', 'registrations') as $name) { 206 echo '<li><div class="li">' . sprintf($this->getLang('dash_' . $name), $result[$name]) . '</div></li>'; 207 } 208 echo '</ul>'; 209 210 $this->html_graph('dashboardviews', 700, 280); 211 $this->html_graph('dashboardwiki', 700, 280); 212 echo '</div>'; 213 214 // top pages today 215 echo '<div>'; 216 echo '<h2>' . $this->getLang('dash_mostpopular') . '</h2>'; 217 $result = $this->hlp->Query()->pages($this->tlimit, $this->start, 15); 218 $this->html_resulttable($result); 219 echo '<a href="?do=admin&page=statistics&opt=page&f=' . $this->from . '&t=' . $this->to . '" class="more button">' . $this->getLang('more') . '</a>'; 220 echo '</div>'; 221 222 // top referer today 223 echo '<div>'; 224 echo '<h2>' . $this->getLang('dash_newincoming') . '</h2>'; 225 $result = $this->hlp->Query()->newreferer($this->tlimit, $this->start, 15); 226 $this->html_resulttable($result); 227 echo '<a href="?do=admin&page=statistics&opt=newreferer&f=' . $this->from . '&t=' . $this->to . '" class="more button">' . $this->getLang('more') . '</a>'; 228 echo '</div>'; 229 230 // top searches today 231 echo '<div>'; 232 echo '<h2>' . $this->getLang('dash_topsearch') . '</h2>'; 233 $result = $this->hlp->Query()->searchphrases(true, $this->tlimit, $this->start, 15); 234 $this->html_resulttable($result); 235 echo '<a href="?do=admin&page=statistics&opt=searchphrases&f=' . $this->from . '&t=' . $this->to . '" class="more button">' . $this->getLang('more') . '</a>'; 236 echo '</div>'; 237 } 238 239 function html_history() { 240 echo '<p>' . $this->getLang('intro_history') . '</p>'; 241 $this->html_graph('history_page_count', 600, 200); 242 $this->html_graph('history_page_size', 600, 200); 243 $this->html_graph('history_media_count', 600, 200); 244 $this->html_graph('history_media_size', 600, 200); 245 } 246 247 function html_countries() { 248 echo '<p>' . $this->getLang('intro_countries') . '</p>'; 249 $this->html_graph('countries', 400, 200); 250 $result = $this->hlp->Query()->countries($this->tlimit, $this->start, 150); 251 $this->html_resulttable($result, '', 150); 252 } 253 254 function html_page() { 255 echo '<p>' . $this->getLang('intro_page') . '</p>'; 256 $result = $this->hlp->Query()->pages($this->tlimit, $this->start, 150); 257 $this->html_resulttable($result, '', 150); 258 } 259 260 function html_edits() { 261 echo '<p>' . $this->getLang('intro_edits') . '</p>'; 262 $result = $this->hlp->Query()->edits($this->tlimit, $this->start, 150); 263 $this->html_resulttable($result, '', 150); 264 } 265 266 function html_images() { 267 echo '<p>' . $this->getLang('intro_images') . '</p>'; 268 $result = $this->hlp->Query()->images($this->tlimit, $this->start, 150); 269 $this->html_resulttable($result, '', 150); 270 } 271 272 function html_downloads() { 273 echo '<p>' . $this->getLang('intro_downloads') . '</p>'; 274 $result = $this->hlp->Query()->downloads($this->tlimit, $this->start, 150); 275 $this->html_resulttable($result, '', 150); 276 } 277 278 function html_browsers() { 279 echo '<p>' . $this->getLang('intro_browsers') . '</p>'; 280 $this->html_graph('browsers', 400, 200); 281 $result = $this->hlp->Query()->browsers($this->tlimit, $this->start, 150, true); 282 $this->html_resulttable($result, '', 150); 283 } 284 285 function html_os() { 286 echo '<p>' . $this->getLang('intro_os') . '</p>'; 287 $this->html_graph('os', 400, 200); 288 $result = $this->hlp->Query()->os($this->tlimit, $this->start, 150, true); 289 $this->html_resulttable($result, '', 150); 290 } 291 292 function html_referer() { 293 $result = $this->hlp->Query()->aggregate($this->tlimit); 294 295 $all = $result['search'] + $result['external'] + $result['direct']; 296 297 if($all) { 298 printf( 299 '<p>' . $this->getLang('intro_referer') . '</p>', 300 $all, $result['direct'], (100 * $result['direct'] / $all), 301 $result['search'], (100 * $result['search'] / $all), $result['external'], 302 (100 * $result['external'] / $all) 303 ); 304 } 305 306 $result = $this->hlp->Query()->referer($this->tlimit, $this->start, 150); 307 $this->html_resulttable($result, '', 150); 308 } 309 310 function html_newreferer() { 311 echo '<p>' . $this->getLang('intro_newreferer') . '</p>'; 312 313 $result = $this->hlp->Query()->newreferer($this->tlimit, $this->start, 150); 314 $this->html_resulttable($result, '', 150); 315 } 316 317 function html_outlinks() { 318 echo '<p>' . $this->getLang('intro_outlinks') . '</p>'; 319 $result = $this->hlp->Query()->outlinks($this->tlimit, $this->start, 150); 320 $this->html_resulttable($result, '', 150); 321 } 322 323 function html_searchphrases() { 324 echo '<p>' . $this->getLang('intro_searchphrases') . '</p>'; 325 $result = $this->hlp->Query()->searchphrases(true, $this->tlimit, $this->start, 150); 326 $this->html_resulttable($result, '', 150); 327 } 328 329 function html_searchwords() { 330 echo '<p>' . $this->getLang('intro_searchwords') . '</p>'; 331 $result = $this->hlp->Query()->searchwords(true, $this->tlimit, $this->start, 150); 332 $this->html_resulttable($result, '', 150); 333 } 334 335 function html_internalsearchphrases() { 336 echo '<p>' . $this->getLang('intro_internalsearchphrases') . '</p>'; 337 $result = $this->hlp->Query()->searchphrases(false, $this->tlimit, $this->start, 150); 338 $this->html_resulttable($result, '', 150); 339 } 340 341 function html_internalsearchwords() { 342 echo '<p>' . $this->getLang('intro_internalsearchwords') . '</p>'; 343 $result = $this->hlp->Query()->searchwords(false, $this->tlimit, $this->start, 150); 344 $this->html_resulttable($result, '', 150); 345 } 346 347 function html_searchengines() { 348 echo '<p>' . $this->getLang('intro_searchengines') . '</p>'; 349 $this->html_graph('searchengines', 400, 200); 350 $result = $this->hlp->Query()->searchengines($this->tlimit, $this->start, 150); 351 $this->html_resulttable($result, '', 150); 352 } 353 354 function html_resolution() { 355 echo '<p>' . $this->getLang('intro_resolution') . '</p>'; 356 $this->html_graph('resolution', 650, 490); 357 $result = $this->hlp->Query()->resolution($this->tlimit, $this->start, 150); 358 $this->html_resulttable($result, '', 150); 359 } 360 361 function html_viewport() { 362 echo '<p>' . $this->getLang('intro_viewport') . '</p>'; 363 $this->html_graph('viewport', 650, 490); 364 $result = $this->hlp->Query()->viewport($this->tlimit, $this->start, 150); 365 $this->html_resulttable($result, '', 150); 366 } 367 368 function html_seenusers() { 369 echo '<p>' . $this->getLang('intro_seenusers') . '</p>'; 370 $result = $this->hlp->Query()->seenusers($this->tlimit, $this->start, 150); 371 $this->html_resulttable($result, '', 150); 372 } 373 374 /** 375 * Display a result in a HTML table 376 */ 377 function html_resulttable($result, $header = '', $pager = 0) { 378 echo '<table>'; 379 if(is_array($header)) { 380 echo '<tr>'; 381 foreach($header as $h) { 382 echo '<th>' . hsc($h) . '</th>'; 383 } 384 echo '</tr>'; 385 } 386 387 $count = 0; 388 if(is_array($result)) foreach($result as $row) { 389 echo '<tr>'; 390 foreach($row as $k => $v) { 391 if($k == 'res_x') continue; 392 if($k == 'res_y') continue; 393 394 echo '<td class="plg_stats_X' . $k . '">'; 395 if($k == 'page') { 396 echo '<a href="' . wl($v) . '" class="wikilink1">'; 397 echo hsc($v); 398 echo '</a>'; 399 } elseif($k == 'media') { 400 echo '<a href="' . ml($v) . '" class="wikilink1">'; 401 echo hsc($v); 402 echo '</a>'; 403 } elseif($k == 'filesize') { 404 echo filesize_h($v); 405 } elseif($k == 'url') { 406 $url = hsc($v); 407 $url = preg_replace('/^https?:\/\/(www\.)?/', '', $url); 408 if(strlen($url) > 45) { 409 $url = substr($url, 0, 30) . ' … ' . substr($url, -15); 410 } 411 echo '<a href="' . $v . '" class="urlextern">'; 412 echo $url; 413 echo '</a>'; 414 } elseif($k == 'ilookup') { 415 echo '<a href="' . wl('', array('id' => $v, 'do' => 'search')) . '">Search</a>'; 416 } elseif($k == 'lookup') { 417 echo '<a href="http://www.google.com/search?q=' . rawurlencode($v) . '">'; 418 echo '<img src="' . DOKU_BASE . 'lib/plugins/statistics/ico/search/google.png" alt="Google" border="0" />'; 419 echo '</a> '; 420 421 echo '<a href="http://search.yahoo.com/search?p=' . rawurlencode($v) . '">'; 422 echo '<img src="' . DOKU_BASE . 'lib/plugins/statistics/ico/search/yahoo.png" alt="Yahoo!" border="0" />'; 423 echo '</a> '; 424 425 echo '<a href="http://www.bing.com/search?q=' . rawurlencode($v) . '">'; 426 echo '<img src="' . DOKU_BASE . 'lib/plugins/statistics/ico/search/bing.png" alt="Bing" border="0" />'; 427 echo '</a> '; 428 429 } elseif($k == 'engine') { 430 include_once(dirname(__FILE__) . '/inc/searchengines.php'); 431 if(isset($SEARCHENGINEINFO[$v])) { 432 echo '<a href="' . $SEARCHENGINEINFO[$v][1] . '">' . $SEARCHENGINEINFO[$v][0] . '</a>'; 433 } else { 434 echo hsc(ucwords($v)); 435 } 436 } elseif($k == 'eflag') { 437 $this->html_icon('search', $v); 438 } elseif($k == 'bflag') { 439 $this->html_icon('browser', $v); 440 } elseif($k == 'osflag') { 441 $this->html_icon('os', $v); 442 } elseif($k == 'cflag') { 443 $this->html_icon('flags', $v); 444 } elseif($k == 'html') { 445 echo $v; 446 } else { 447 echo hsc($v); 448 } 449 echo '</td>'; 450 } 451 echo '</tr>'; 452 453 if($pager && ($count == $pager)) break; 454 $count++; 455 } 456 echo '</table>'; 457 458 if($pager) $this->html_pager($pager, count($result) > $pager); 459 } 460 461 function html_icon($type, $value) { 462 $value = strtolower(preg_replace('/[^\w]+/', '', $value)); 463 $value = str_replace(' ', '_', $value); 464 $file = 'lib/plugins/statistics/ico/' . $type . '/' . $value . '.png'; 465 if($type == 'flags') { 466 $w = 18; 467 $h = 12; 468 } else { 469 $w = 16; 470 $h = 16; 471 } 472 if(file_exists(DOKU_INC . $file)) { 473 echo '<img src="' . DOKU_BASE . $file . '" alt="' . hsc($value) . '" width="' . $w . '" height="' . $h . '" />'; 474 } 475 } 476} 477