114d99ec0SAndreas Gohr<?php 2a8acb244SAndreas Gohr 3a8acb244SAndreas Gohruse dokuwiki\Extension\ActionPlugin; 4a8acb244SAndreas Gohruse dokuwiki\Extension\Event; 5*04928db4SAndreas Gohruse dokuwiki\Extension\EventHandler; 6a8acb244SAndreas Gohr 714d99ec0SAndreas Gohr/** 814d99ec0SAndreas Gohr * 914d99ec0SAndreas Gohr * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 1014d99ec0SAndreas Gohr * @author Andreas Gohr <gohr@cosmocode.de> 1114d99ec0SAndreas Gohr */ 12a8acb244SAndreas Gohrclass action_plugin_statistics extends ActionPlugin 13a8acb244SAndreas Gohr{ 1414d99ec0SAndreas Gohr /** 1514d99ec0SAndreas Gohr * register the eventhandlers and initialize some options 1614d99ec0SAndreas Gohr */ 17a8acb244SAndreas Gohr public function register(EventHandler $controller) 18a8acb244SAndreas Gohr { 19eabe0d07SAndreas Gohr global $JSINFO; 20eabe0d07SAndreas Gohr global $ACT; 21eabe0d07SAndreas Gohr $JSINFO['act'] = $ACT; 2214d99ec0SAndreas Gohr 23*04928db4SAndreas Gohr $controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'initSession', []); 2402aa9b73SAndreas Gohr // FIXME new save event might be better: 252257e39bSAndreas Gohr $controller->register_hook('IO_WIKIPAGE_WRITE', 'BEFORE', $this, 'logedits', []); 262257e39bSAndreas Gohr $controller->register_hook('SEARCH_QUERY_FULLPAGE', 'AFTER', $this, 'logsearch', []); 272257e39bSAndreas Gohr $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'loglogins', []); 282257e39bSAndreas Gohr $controller->register_hook('AUTH_USER_CHANGE', 'AFTER', $this, 'logregistration', []); 292257e39bSAndreas Gohr $controller->register_hook('FETCH_MEDIA_STATUS', 'BEFORE', $this, 'logmedia', []); 302257e39bSAndreas Gohr $controller->register_hook('INDEXER_TASKS_RUN', 'AFTER', $this, 'loghistory', []); 3114d99ec0SAndreas Gohr } 3214d99ec0SAndreas Gohr 3314d99ec0SAndreas Gohr /** 34*04928db4SAndreas Gohr * This ensures we have a session for the statistics plugin 35*04928db4SAndreas Gohr * 36*04928db4SAndreas Gohr * We reset this when the user agent changes or the session is too old 37*04928db4SAndreas Gohr * (15 minutes). 38*04928db4SAndreas Gohr */ 39*04928db4SAndreas Gohr public function initSession() 40*04928db4SAndreas Gohr { 41*04928db4SAndreas Gohr global $INPUT; 42*04928db4SAndreas Gohr 43*04928db4SAndreas Gohr // load session data 44*04928db4SAndreas Gohr if (isset($_SESSION[DOKU_COOKIE]['statistics'])) { 45*04928db4SAndreas Gohr $session = $_SESSION[DOKU_COOKIE]['statistics']; 46*04928db4SAndreas Gohr } else { 47*04928db4SAndreas Gohr $session = []; 48*04928db4SAndreas Gohr } 49*04928db4SAndreas Gohr // reset if session is too old 50*04928db4SAndreas Gohr if (time() - ($session['time'] ?? 0) > 60 * 15) { 51*04928db4SAndreas Gohr $session = []; 52*04928db4SAndreas Gohr } 53*04928db4SAndreas Gohr // reset if user agent changed 54*04928db4SAndreas Gohr if ($INPUT->server->str('HTTP_USER_AGENT') != ($session['user_agent'] ?? '')) { 55*04928db4SAndreas Gohr $session = []; 56*04928db4SAndreas Gohr } 57*04928db4SAndreas Gohr 58*04928db4SAndreas Gohr // update session data 59*04928db4SAndreas Gohr $session['time'] = time(); 60*04928db4SAndreas Gohr $session['user_agent'] = $INPUT->server->str('HTTP_USER_AGENT'); 61*04928db4SAndreas Gohr $session['uid'] = get_doku_pref('plgstats', bin2hex(random_bytes(16))); 62*04928db4SAndreas Gohr if (!isset($session['id'])) { 63*04928db4SAndreas Gohr // generate a new session id if not set 64*04928db4SAndreas Gohr $session['id'] = bin2hex(random_bytes(16)); 65*04928db4SAndreas Gohr } 66*04928db4SAndreas Gohr 67*04928db4SAndreas Gohr // store session and cookie data 68*04928db4SAndreas Gohr $_SESSION[DOKU_COOKIE]['statistics'] = $session; 69*04928db4SAndreas Gohr set_doku_pref('plgstats', $session['uid']); 70*04928db4SAndreas Gohr } 71*04928db4SAndreas Gohr 72*04928db4SAndreas Gohr /** 7314d99ec0SAndreas Gohr * @fixme call this in the webbug call 7414d99ec0SAndreas Gohr */ 75a8acb244SAndreas Gohr public function putpixel() 76a8acb244SAndreas Gohr { 77ed6e7cc1SAndreas Gohr (aider) global $ID, $INPUT; 7814d99ec0SAndreas Gohr $url = DOKU_BASE . 'lib/plugins/statistics/log.php?p=' . rawurlencode($ID) . 79ed6e7cc1SAndreas Gohr (aider) '&r=' . rawurlencode($INPUT->server->str('HTTP_REFERER')) . '&rnd=' . time(); 8014d99ec0SAndreas Gohr 812257e39bSAndreas Gohr echo '<noscript><img alt="" src="' . $url . '" width="1" height="1" /></noscript>'; 8214d99ec0SAndreas Gohr } 8358511ae8SAndreas Gohr 8458511ae8SAndreas Gohr /** 855bccfe87SAndreas Gohr * Log page edits actions 8658511ae8SAndreas Gohr */ 87a8acb244SAndreas Gohr public function logedits(Event $event, $param) 88a8acb244SAndreas Gohr { 8958511ae8SAndreas Gohr if ($event->data[3]) return; // no revision 9058511ae8SAndreas Gohr 9158511ae8SAndreas Gohr if (file_exists($event->data[0][0])) { 9258511ae8SAndreas Gohr if ($event->data[0][1] == '') { 9358511ae8SAndreas Gohr $type = 'D'; 9458511ae8SAndreas Gohr } else { 9558511ae8SAndreas Gohr $type = 'E'; 9658511ae8SAndreas Gohr } 9758511ae8SAndreas Gohr } else { 9858511ae8SAndreas Gohr $type = 'C'; 9958511ae8SAndreas Gohr } 1001664ba1dSAndreas Gohr /** @var helper_plugin_statistics $hlp */ 10158511ae8SAndreas Gohr $hlp = plugin_load('helper', 'statistics'); 10202aa9b73SAndreas Gohr $hlp->getLogger()->logEdit($event->data[1] . ':' . $event->data[2], $type); 10358511ae8SAndreas Gohr } 1045bccfe87SAndreas Gohr 1055bccfe87SAndreas Gohr /** 1065bccfe87SAndreas Gohr * Log internal search 1075bccfe87SAndreas Gohr */ 108a8acb244SAndreas Gohr public function logsearch(Event $event, $param) 109a8acb244SAndreas Gohr { 1101664ba1dSAndreas Gohr /** @var helper_plugin_statistics $hlp */ 1115bccfe87SAndreas Gohr $hlp = plugin_load('helper', 'statistics'); 11202aa9b73SAndreas Gohr $hlp->getLogger()->logSearch($event->data['query'], $event->data['highlight']); 1135bccfe87SAndreas Gohr } 114b5e880bdSAndreas Gohr 115b5e880bdSAndreas Gohr /** 116b5e880bdSAndreas Gohr * Log login/logouts 117b5e880bdSAndreas Gohr */ 118a8acb244SAndreas Gohr public function loglogins(Event $event, $param) 119a8acb244SAndreas Gohr { 120ed6e7cc1SAndreas Gohr (aider) global $INPUT; 121ed6e7cc1SAndreas Gohr (aider) 122b5e880bdSAndreas Gohr $type = ''; 123a5dadbc1SAndreas Gohr $act = $this->actClean($event->data); 124b5e880bdSAndreas Gohr if ($act == 'logout') { 125b5e880bdSAndreas Gohr $type = 'o'; 126ed6e7cc1SAndreas Gohr (aider) } elseif ($INPUT->server->str('REMOTE_USER') && $act == 'login') { 127ed6e7cc1SAndreas Gohr (aider) if ($INPUT->str('r')) { 128b5e880bdSAndreas Gohr $type = 'p'; 129b5e880bdSAndreas Gohr } else { 130b5e880bdSAndreas Gohr $type = 'l'; 131b5e880bdSAndreas Gohr } 132ed6e7cc1SAndreas Gohr (aider) } elseif ($INPUT->str('u') && !$INPUT->str('http_credentials') && !$INPUT->server->str('REMOTE_USER')) { 133b5e880bdSAndreas Gohr $type = 'f'; 134b5e880bdSAndreas Gohr } 135b5e880bdSAndreas Gohr if (!$type) return; 136b5e880bdSAndreas Gohr 1371664ba1dSAndreas Gohr /** @var helper_plugin_statistics $hlp */ 138b5e880bdSAndreas Gohr $hlp = plugin_load('helper', 'statistics'); 139211caa5dSAndreas Gohr $hlp->getLogger()->logLogin($type); 14014d99ec0SAndreas Gohr } 14114d99ec0SAndreas Gohr 142535aeea1SAndreas Gohr /** 143535aeea1SAndreas Gohr * Log user creations 144535aeea1SAndreas Gohr */ 145a8acb244SAndreas Gohr public function logregistration(Event $event, $param) 146a8acb244SAndreas Gohr { 147535aeea1SAndreas Gohr if ($event->data['type'] == 'create') { 1481664ba1dSAndreas Gohr /** @var helper_plugin_statistics $hlp */ 149535aeea1SAndreas Gohr $hlp = plugin_load('helper', 'statistics'); 150211caa5dSAndreas Gohr $hlp->getLogger()->logLogin('C', $event->data['params'][0]); 151535aeea1SAndreas Gohr } 152535aeea1SAndreas Gohr } 153b5e880bdSAndreas Gohr 154b5e880bdSAndreas Gohr /** 1551664ba1dSAndreas Gohr * Log media access 1561664ba1dSAndreas Gohr */ 157a8acb244SAndreas Gohr public function logmedia(Event $event, $param) 158a8acb244SAndreas Gohr { 1591664ba1dSAndreas Gohr if ($event->data['status'] < 200) return; 1601664ba1dSAndreas Gohr if ($event->data['status'] >= 400) return; 1611664ba1dSAndreas Gohr if (preg_match('/^\w+:\/\//', $event->data['media'])) return; 1621664ba1dSAndreas Gohr 1631664ba1dSAndreas Gohr // no size for redirect/not modified 1641664ba1dSAndreas Gohr if ($event->data['status'] >= 300) { 1651664ba1dSAndreas Gohr $size = 0; 1661664ba1dSAndreas Gohr } else { 16717978b38SAndreas Gohr $size = @filesize($event->data['file']); 1681664ba1dSAndreas Gohr } 1691664ba1dSAndreas Gohr 1701664ba1dSAndreas Gohr /** @var helper_plugin_statistics $hlp */ 1711664ba1dSAndreas Gohr $hlp = plugin_load('helper', 'statistics'); 172211caa5dSAndreas Gohr $hlp->getLogger()->logMedia( 1731664ba1dSAndreas Gohr $event->data['media'], 1741664ba1dSAndreas Gohr $event->data['mime'], 1751664ba1dSAndreas Gohr !$event->data['download'], 1761664ba1dSAndreas Gohr $size 1771664ba1dSAndreas Gohr ); 1781664ba1dSAndreas Gohr } 1791664ba1dSAndreas Gohr 1801664ba1dSAndreas Gohr /** 181cae4a1c5SAndreas Gohr * Log the daily page and media counts for the history 182cae4a1c5SAndreas Gohr */ 183a8acb244SAndreas Gohr public function loghistory(Event $event, $param) 184a8acb244SAndreas Gohr { 185cae4a1c5SAndreas Gohr echo 'Plugin Statistics: started' . DOKU_LF; 186cae4a1c5SAndreas Gohr 187cae4a1c5SAndreas Gohr /** @var helper_plugin_statistics $hlp */ 188cae4a1c5SAndreas Gohr $hlp = plugin_load('helper', 'statistics'); 189a9509e05SAndreas Gohr (aider) $db = $hlp->getDB(); 190cae4a1c5SAndreas Gohr 191cae4a1c5SAndreas Gohr // check if a history was gathered already today 192a9509e05SAndreas Gohr (aider) $result = $db->queryAll( 19302aa9b73SAndreas Gohr "SELECT info FROM history WHERE date(dt) = date('now')" 194a9509e05SAndreas Gohr (aider) ); 195cae4a1c5SAndreas Gohr 196cae4a1c5SAndreas Gohr $page_ran = false; 197cae4a1c5SAndreas Gohr $media_ran = false; 198cae4a1c5SAndreas Gohr foreach ($result as $row) { 199cae4a1c5SAndreas Gohr if ($row['info'] == 'page_count') $page_ran = true; 200cae4a1c5SAndreas Gohr if ($row['info'] == 'media_count') $media_ran = true; 201cae4a1c5SAndreas Gohr } 202cae4a1c5SAndreas Gohr 203cae4a1c5SAndreas Gohr if ($page_ran && $media_ran) { 204cae4a1c5SAndreas Gohr echo 'Plugin Statistics: nothing to do - finished' . DOKU_LF; 205cae4a1c5SAndreas Gohr return; 206cae4a1c5SAndreas Gohr } 207cae4a1c5SAndreas Gohr 208cae4a1c5SAndreas Gohr $event->stopPropagation(); 209cae4a1c5SAndreas Gohr $event->preventDefault(); 210cae4a1c5SAndreas Gohr 211cae4a1c5SAndreas Gohr if ($page_ran) { 212cae4a1c5SAndreas Gohr echo 'Plugin Statistics: logging media' . DOKU_LF; 213211caa5dSAndreas Gohr $hlp->getLogger()->logHistoryMedia(); 214cae4a1c5SAndreas Gohr } else { 215cae4a1c5SAndreas Gohr echo 'Plugin Statistics: logging pages' . DOKU_LF; 216211caa5dSAndreas Gohr $hlp->getLogger()->logHistoryPages(); 217cae4a1c5SAndreas Gohr } 218cae4a1c5SAndreas Gohr echo 'Plugin Statistics: finished' . DOKU_LF; 219cae4a1c5SAndreas Gohr } 220cae4a1c5SAndreas Gohr 221cae4a1c5SAndreas Gohr /** 222b5e880bdSAndreas Gohr * Pre-Sanitize the action command 223b5e880bdSAndreas Gohr * 224b5e880bdSAndreas Gohr * Similar to act_clean in action.php but simplified and without 225b5e880bdSAndreas Gohr * error messages 226b5e880bdSAndreas Gohr */ 227a5dadbc1SAndreas Gohr protected function actClean($act) 228a8acb244SAndreas Gohr { 229b5e880bdSAndreas Gohr // check if the action was given as array key 230b5e880bdSAndreas Gohr if (is_array($act)) { 231a8acb244SAndreas Gohr [$act] = array_keys($act); 232b5e880bdSAndreas Gohr } 233b5e880bdSAndreas Gohr 234b5e880bdSAndreas Gohr //remove all bad chars 235b5e880bdSAndreas Gohr $act = strtolower($act); 236b5e880bdSAndreas Gohr $act = preg_replace('/[^a-z_]+/', '', $act); 237b5e880bdSAndreas Gohr 238b5e880bdSAndreas Gohr return $act; 239b5e880bdSAndreas Gohr } 240b5e880bdSAndreas Gohr} 241