1<?php 2 3use dokuwiki\Extension\ActionPlugin; 4use dokuwiki\Extension\Event; 5use dokuwiki\Extension\EventHandler; 6 7/** 8 * 9 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 10 * @author Andreas Gohr <gohr@cosmocode.de> 11 */ 12class action_plugin_statistics extends ActionPlugin 13{ 14 /** 15 * register the eventhandlers and initialize some options 16 */ 17 public function register(EventHandler $controller) 18 { 19 global $JSINFO; 20 global $ACT; 21 $JSINFO['act'] = $ACT; 22 23 $controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'initSession', []); 24 // FIXME new save event might be better: 25 $controller->register_hook('IO_WIKIPAGE_WRITE', 'BEFORE', $this, 'logedits', []); 26 $controller->register_hook('SEARCH_QUERY_FULLPAGE', 'AFTER', $this, 'logsearch', []); 27 $controller->register_hook('FETCH_MEDIA_STATUS', 'BEFORE', $this, 'logmedia', []); 28 $controller->register_hook('INDEXER_TASKS_RUN', 'AFTER', $this, 'loghistory', []); 29 30 // log registration and login/logout actionsonly when user tracking is enabled 31 if (!$this->getConf('nousers')) { 32 $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'loglogins', []); 33 $controller->register_hook('AUTH_USER_CHANGE', 'AFTER', $this, 'logregistration', []); 34 } 35 } 36 37 /** 38 * This ensures we have a session for the statistics plugin 39 * 40 * We reset this when the user agent changes or the session is too old 41 * (15 minutes). 42 */ 43 public function initSession() 44 { 45 global $INPUT; 46 47 // load session data 48 if (isset($_SESSION[DOKU_COOKIE]['statistics'])) { 49 $session = $_SESSION[DOKU_COOKIE]['statistics']; 50 } else { 51 $session = []; 52 } 53 // reset if session is too old 54 if (time() - ($session['time'] ?? 0) > 60 * 15) { 55 $session = []; 56 } 57 // reset if user agent changed 58 if ($INPUT->server->str('HTTP_USER_AGENT') != ($session['user_agent'] ?? '')) { 59 $session = []; 60 } 61 62 // update session data 63 $session['time'] = time(); 64 $session['user_agent'] = $INPUT->server->str('HTTP_USER_AGENT'); 65 $session['uid'] = get_doku_pref('plgstats', bin2hex(random_bytes(16))); 66 if (!isset($session['id'])) { 67 // generate a new session id if not set 68 $session['id'] = bin2hex(random_bytes(16)); 69 } 70 71 // store session and cookie data 72 $_SESSION[DOKU_COOKIE]['statistics'] = $session; 73 set_doku_pref('plgstats', $session['uid']); 74 } 75 76 /** 77 * @fixme call this in the webbug call 78 */ 79 public function putpixel() 80 { 81 global $ID, $INPUT; 82 $url = DOKU_BASE . 'lib/plugins/statistics/log.php?p=' . rawurlencode($ID) . 83 '&r=' . rawurlencode($INPUT->server->str('HTTP_REFERER')) . '&rnd=' . time(); 84 85 echo '<noscript><img alt="" src="' . $url . '" width="1" height="1" /></noscript>'; 86 } 87 88 /** 89 * Log page edits actions 90 */ 91 public function logedits(Event $event, $param) 92 { 93 if ($event->data[3]) return; // no revision 94 95 if (file_exists($event->data[0][0])) { 96 if ($event->data[0][1] == '') { 97 $type = 'D'; 98 } else { 99 $type = 'E'; 100 } 101 } else { 102 $type = 'C'; 103 } 104 /** @var helper_plugin_statistics $hlp */ 105 $hlp = plugin_load('helper', 'statistics'); 106 $hlp->getLogger()->logEdit($event->data[1] . ':' . $event->data[2], $type); 107 } 108 109 /** 110 * Log internal search 111 */ 112 public function logsearch(Event $event, $param) 113 { 114 /** @var helper_plugin_statistics $hlp */ 115 $hlp = plugin_load('helper', 'statistics'); 116 $hlp->getLogger()->logSearch($event->data['query'], $event->data['highlight']); 117 } 118 119 /** 120 * Log login/logouts 121 */ 122 public function loglogins(Event $event, $param) 123 { 124 global $INPUT; 125 126 $type = ''; 127 $act = $this->actClean($event->data); 128 $user = $INPUT->server->str('REMOTE_USER'); 129 if ($act == 'logout') { 130 // logout 131 $type = 'o'; 132 } elseif ($INPUT->server->str('REMOTE_USER') && $act == 'login') { 133 if ($INPUT->str('r')) { 134 // permanent login 135 $type = 'p'; 136 } else { 137 // normal login 138 $type = 'l'; 139 } 140 } elseif ($INPUT->str('u') && !$INPUT->str('http_credentials') && !$INPUT->server->str('REMOTE_USER')) { 141 // failed attempt 142 $user = $INPUT->str('u'); 143 $type = 'f'; 144 } 145 if (!$type) return; 146 147 /** @var helper_plugin_statistics $hlp */ 148 $hlp = plugin_load('helper', 'statistics'); 149 $hlp->getLogger()->logLogin($type, $user); 150 } 151 152 /** 153 * Log user creations 154 */ 155 public function logregistration(Event $event, $param) 156 { 157 if ($event->data['type'] == 'create') { 158 /** @var helper_plugin_statistics $hlp */ 159 $hlp = plugin_load('helper', 'statistics'); 160 $hlp->getLogger()->logLogin('C', $event->data['params'][0]); 161 } 162 } 163 164 /** 165 * Log media access 166 */ 167 public function logmedia(Event $event, $param) 168 { 169 if ($event->data['status'] < 200) return; 170 if ($event->data['status'] >= 400) return; 171 if (preg_match('/^\w+:\/\//', $event->data['media'])) return; 172 173 // no size for redirect/not modified 174 if ($event->data['status'] >= 300) { 175 $size = 0; 176 } else { 177 $size = @filesize($event->data['file']); 178 } 179 180 /** @var helper_plugin_statistics $hlp */ 181 $hlp = plugin_load('helper', 'statistics'); 182 $hlp->getLogger()->logMedia( 183 $event->data['media'], 184 $event->data['mime'], 185 !$event->data['download'], 186 $size 187 ); 188 } 189 190 /** 191 * Log the daily page and media counts for the history 192 */ 193 public function loghistory(Event $event, $param) 194 { 195 echo 'Plugin Statistics: started' . DOKU_LF; 196 197 /** @var helper_plugin_statistics $hlp */ 198 $hlp = plugin_load('helper', 'statistics'); 199 $db = $hlp->getDB(); 200 201 // check if a history was gathered already today 202 $result = $db->queryAll( 203 "SELECT info FROM history WHERE date(dt) = date('now')" 204 ); 205 206 $page_ran = false; 207 $media_ran = false; 208 foreach ($result as $row) { 209 if ($row['info'] == 'page_count') $page_ran = true; 210 if ($row['info'] == 'media_count') $media_ran = true; 211 } 212 213 if ($page_ran && $media_ran) { 214 echo 'Plugin Statistics: nothing to do - finished' . DOKU_LF; 215 return; 216 } 217 218 $event->stopPropagation(); 219 $event->preventDefault(); 220 221 if ($page_ran) { 222 echo 'Plugin Statistics: logging media' . DOKU_LF; 223 $hlp->getLogger()->logHistoryMedia(); 224 } else { 225 echo 'Plugin Statistics: logging pages' . DOKU_LF; 226 $hlp->getLogger()->logHistoryPages(); 227 } 228 echo 'Plugin Statistics: finished' . DOKU_LF; 229 } 230 231 /** 232 * Pre-Sanitize the action command 233 * 234 * Similar to act_clean in action.php but simplified and without 235 * error messages 236 */ 237 protected function actClean($act) 238 { 239 // check if the action was given as array key 240 if (is_array($act)) { 241 [$act] = array_keys($act); 242 } 243 244 //remove all bad chars 245 $act = strtolower($act); 246 $act = preg_replace('/[^a-z_]+/', '', $act); 247 248 return $act; 249 } 250} 251