1762f4807SAndreas Gohr<?php 2762f4807SAndreas Gohr 3762f4807SAndreas Gohrnamespace dokuwiki\plugin\statistics; 4762f4807SAndreas Gohr 5762f4807SAndreas Gohruse DeviceDetector\DeviceDetector; 6762f4807SAndreas Gohruse DeviceDetector\Parser\Client\Browser; 7762f4807SAndreas Gohruse DeviceDetector\Parser\Device\AbstractDeviceParser; 8762f4807SAndreas Gohruse DeviceDetector\Parser\OperatingSystem; 9762f4807SAndreas Gohruse dokuwiki\HTTP\DokuHTTPClient; 10762f4807SAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB; 11762f4807SAndreas Gohruse dokuwiki\Utf8\Clean; 12762f4807SAndreas Gohruse helper_plugin_popularity; 13762f4807SAndreas Gohruse helper_plugin_statistics; 14762f4807SAndreas Gohr 15762f4807SAndreas Gohr 16762f4807SAndreas Gohrclass Logger 17762f4807SAndreas Gohr{ 18762f4807SAndreas Gohr /** @var helper_plugin_statistics The statistics helper plugin instance */ 19762f4807SAndreas Gohr protected helper_plugin_statistics $hlp; 20762f4807SAndreas Gohr 21762f4807SAndreas Gohr /** @var SQLiteDB The SQLite database instance */ 22762f4807SAndreas Gohr protected SQLiteDB $db; 23762f4807SAndreas Gohr 24762f4807SAndreas Gohr /** @var string The full user agent string */ 25762f4807SAndreas Gohr protected string $uaAgent; 26762f4807SAndreas Gohr 27762f4807SAndreas Gohr /** @var string The type of user agent (browser, robot, feedreader) */ 28762f4807SAndreas Gohr protected string $uaType = 'browser'; 29762f4807SAndreas Gohr 30762f4807SAndreas Gohr /** @var string The browser/client name */ 31762f4807SAndreas Gohr protected string $uaName; 32762f4807SAndreas Gohr 33762f4807SAndreas Gohr /** @var string The browser/client version */ 34762f4807SAndreas Gohr protected string $uaVersion; 35762f4807SAndreas Gohr 36762f4807SAndreas Gohr /** @var string The operating system/platform */ 37762f4807SAndreas Gohr protected string $uaPlatform; 38762f4807SAndreas Gohr 39762f4807SAndreas Gohr /** @var string The unique user identifier */ 40762f4807SAndreas Gohr protected string $uid; 41762f4807SAndreas Gohr 42c7cad24dSAndreas Gohr /** @var DokuHTTPClient|null The HTTP client instance for testing */ 43c7cad24dSAndreas Gohr protected ?DokuHTTPClient $httpClient = null; 44c7cad24dSAndreas Gohr 45762f4807SAndreas Gohr 46762f4807SAndreas Gohr /** 47762f4807SAndreas Gohr * Constructor 48762f4807SAndreas Gohr * 49762f4807SAndreas Gohr * Parses browser info and set internal vars 50762f4807SAndreas Gohr */ 51c7cad24dSAndreas Gohr public function __construct(helper_plugin_statistics $hlp, ?DokuHTTPClient $httpClient = null) 52762f4807SAndreas Gohr { 53762f4807SAndreas Gohr global $INPUT; 54762f4807SAndreas Gohr 55762f4807SAndreas Gohr $this->hlp = $hlp; 56762f4807SAndreas Gohr $this->db = $this->hlp->getDB(); 57c7cad24dSAndreas Gohr $this->httpClient = $httpClient; 58762f4807SAndreas Gohr 59762f4807SAndreas Gohr $ua = trim($INPUT->server->str('HTTP_USER_AGENT')); 60762f4807SAndreas Gohr 61762f4807SAndreas Gohr AbstractDeviceParser::setVersionTruncation(AbstractDeviceParser::VERSION_TRUNCATION_MAJOR); 62762f4807SAndreas Gohr $dd = new DeviceDetector($ua); // FIXME we could use client hints, but need to add headers 63762f4807SAndreas Gohr $dd->discardBotInformation(); 64762f4807SAndreas Gohr $dd->parse(); 65762f4807SAndreas Gohr 6600f786d8SAndreas Gohr if ($dd->isFeedReader()) { 6700f786d8SAndreas Gohr $this->uaType = 'feedreader'; 6800f786d8SAndreas Gohr } else if ($dd->isBot()) { 69762f4807SAndreas Gohr $this->uaType = 'robot'; 70762f4807SAndreas Gohr 71762f4807SAndreas Gohr // for now ignore bots 72762f4807SAndreas Gohr throw new \RuntimeException('Bot detected, not logging'); 73762f4807SAndreas Gohr } 74762f4807SAndreas Gohr 75762f4807SAndreas Gohr $this->uaAgent = $ua; 7605786d83SAndreas Gohr $this->uaName = Browser::getBrowserFamily($dd->getClient('name')) ?: 'Unknown'; 7700f786d8SAndreas Gohr $this->uaVersion = $dd->getClient('version') ?: '0'; 7805786d83SAndreas Gohr $this->uaPlatform = OperatingSystem::getOsFamily($dd->getOs('name')) ?: 'Unknown'; 79762f4807SAndreas Gohr $this->uid = $this->getUID(); 80762f4807SAndreas Gohr 81762f4807SAndreas Gohr 82762f4807SAndreas Gohr $this->logLastseen(); 83762f4807SAndreas Gohr } 84762f4807SAndreas Gohr 85762f4807SAndreas Gohr /** 86762f4807SAndreas Gohr * Should be called before logging 87762f4807SAndreas Gohr * 88762f4807SAndreas Gohr * This starts a transaction, so all logging is done in one go 89762f4807SAndreas Gohr */ 90762f4807SAndreas Gohr public function begin(): void 91762f4807SAndreas Gohr { 92762f4807SAndreas Gohr $this->hlp->getDB()->getPdo()->beginTransaction(); 93762f4807SAndreas Gohr } 94762f4807SAndreas Gohr 95762f4807SAndreas Gohr /** 96762f4807SAndreas Gohr * Should be called after logging 97762f4807SAndreas Gohr * 98762f4807SAndreas Gohr * This commits the transaction started in begin() 99762f4807SAndreas Gohr */ 100762f4807SAndreas Gohr public function end(): void 101762f4807SAndreas Gohr { 102762f4807SAndreas Gohr $this->hlp->getDB()->getPdo()->commit(); 103762f4807SAndreas Gohr } 104762f4807SAndreas Gohr 105762f4807SAndreas Gohr /** 106762f4807SAndreas Gohr * Get the unique user ID 107762f4807SAndreas Gohr * 108762f4807SAndreas Gohr * @return string The unique user identifier 109762f4807SAndreas Gohr */ 110762f4807SAndreas Gohr protected function getUID(): string 111762f4807SAndreas Gohr { 112762f4807SAndreas Gohr global $INPUT; 113762f4807SAndreas Gohr 114762f4807SAndreas Gohr $uid = $INPUT->str('uid'); 115762f4807SAndreas Gohr if (!$uid) $uid = get_doku_pref('plgstats', false); 116762f4807SAndreas Gohr if (!$uid) $uid = session_id(); 117b188870fSAndreas Gohr set_doku_pref('plgstats', $uid); 118762f4807SAndreas Gohr return $uid; 119762f4807SAndreas Gohr } 120762f4807SAndreas Gohr 121762f4807SAndreas Gohr /** 122762f4807SAndreas Gohr * Return the user's session ID 123762f4807SAndreas Gohr * 124762f4807SAndreas Gohr * This is usually our own managed session, not a PHP session (only in fallback) 125762f4807SAndreas Gohr * 126762f4807SAndreas Gohr * @return string The session identifier 127762f4807SAndreas Gohr */ 128762f4807SAndreas Gohr protected function getSession(): string 129762f4807SAndreas Gohr { 130762f4807SAndreas Gohr global $INPUT; 131762f4807SAndreas Gohr 132762f4807SAndreas Gohr $ses = $INPUT->str('ses'); 133762f4807SAndreas Gohr if (!$ses) $ses = get_doku_pref('plgstatsses', false); 134762f4807SAndreas Gohr if (!$ses) $ses = session_id(); 135b188870fSAndreas Gohr set_doku_pref('plgstatsses', $ses); 136762f4807SAndreas Gohr return $ses; 137762f4807SAndreas Gohr } 138762f4807SAndreas Gohr 139762f4807SAndreas Gohr /** 140762f4807SAndreas Gohr * Log that we've seen the user (authenticated only) 141762f4807SAndreas Gohr */ 142762f4807SAndreas Gohr public function logLastseen(): void 143762f4807SAndreas Gohr { 144762f4807SAndreas Gohr global $INPUT; 145762f4807SAndreas Gohr 146762f4807SAndreas Gohr if (empty($INPUT->server->str('REMOTE_USER'))) return; 147762f4807SAndreas Gohr 148762f4807SAndreas Gohr $this->db->exec( 149762f4807SAndreas Gohr 'REPLACE INTO lastseen (user, dt) VALUES (?, CURRENT_TIMESTAMP)', 150762f4807SAndreas Gohr $INPUT->server->str('REMOTE_USER'), 151762f4807SAndreas Gohr ); 152762f4807SAndreas Gohr } 153762f4807SAndreas Gohr 154762f4807SAndreas Gohr /** 155762f4807SAndreas Gohr * Log actions by groups 156762f4807SAndreas Gohr * 157762f4807SAndreas Gohr * @param string $type The type of access to log ('view','edit') 158762f4807SAndreas Gohr * @param array $groups The groups to log 159762f4807SAndreas Gohr */ 160762f4807SAndreas Gohr public function logGroups(string $type, array $groups): void 161762f4807SAndreas Gohr { 162483101d3SAndreas Gohr if (!$groups) return; 163762f4807SAndreas Gohr 164483101d3SAndreas Gohr $toLog = (array)$this->hlp->getConf('loggroups'); 165*fced2f86SAnna Dabrowska 166*fced2f86SAnna Dabrowska // if specific groups are configured, limit logging to them only 167*fced2f86SAnna Dabrowska $groups = !empty(array_filter($toLog)) ? array_intersect($groups, $toLog) : $groups; 168483101d3SAndreas Gohr if (!$groups) return; 169762f4807SAndreas Gohr 170483101d3SAndreas Gohr $placeholders = join(',', array_fill(0, count($groups), '(?, ?)')); 171762f4807SAndreas Gohr $params = []; 172483101d3SAndreas Gohr $sql = "INSERT INTO groups (`type`, `group`) VALUES $placeholders"; 173762f4807SAndreas Gohr foreach ($groups as $group) { 174762f4807SAndreas Gohr $params[] = $type; 175762f4807SAndreas Gohr $params[] = $group; 176762f4807SAndreas Gohr } 177762f4807SAndreas Gohr $sql = rtrim($sql, ','); 178762f4807SAndreas Gohr $this->db->exec($sql, $params); 179762f4807SAndreas Gohr } 180762f4807SAndreas Gohr 181762f4807SAndreas Gohr /** 182762f4807SAndreas Gohr * Log external search queries 183762f4807SAndreas Gohr * 184762f4807SAndreas Gohr * Will not write anything if the referer isn't a search engine 185762f4807SAndreas Gohr * 186762f4807SAndreas Gohr * @param string $referer The HTTP referer URL 187762f4807SAndreas Gohr * @param string $type Reference to the type variable that will be modified 188762f4807SAndreas Gohr */ 189762f4807SAndreas Gohr public function logExternalSearch(string $referer, string &$type): void 190762f4807SAndreas Gohr { 191762f4807SAndreas Gohr global $INPUT; 192762f4807SAndreas Gohr 193762f4807SAndreas Gohr $searchEngine = new SearchEngines($referer); 194762f4807SAndreas Gohr 195762f4807SAndreas Gohr if (!$searchEngine->isSearchEngine()) { 196762f4807SAndreas Gohr return; // not a search engine 197762f4807SAndreas Gohr } 198762f4807SAndreas Gohr 199762f4807SAndreas Gohr $type = 'search'; 200762f4807SAndreas Gohr $query = $searchEngine->getQuery(); 201762f4807SAndreas Gohr 202762f4807SAndreas Gohr // log it! 203762f4807SAndreas Gohr $words = explode(' ', Clean::stripspecials($query, ' ', '\._\-:\*')); 204762f4807SAndreas Gohr $this->logSearch($INPUT->str('p'), $query, $words, $searchEngine->getEngine()); 205762f4807SAndreas Gohr } 206762f4807SAndreas Gohr 207762f4807SAndreas Gohr /** 208762f4807SAndreas Gohr * Log search data to the search related tables 209762f4807SAndreas Gohr * 210762f4807SAndreas Gohr * @param string $page The page being searched from 211762f4807SAndreas Gohr * @param string $query The search query 212762f4807SAndreas Gohr * @param array $words Array of search words 213762f4807SAndreas Gohr * @param string $engine The search engine name 214762f4807SAndreas Gohr */ 215762f4807SAndreas Gohr public function logSearch(string $page, string $query, array $words, string $engine): void 216762f4807SAndreas Gohr { 217762f4807SAndreas Gohr $sid = $this->db->exec( 218762f4807SAndreas Gohr 'INSERT INTO search (dt, page, query, engine) VALUES (CURRENT_TIMESTAMP, ?, ?, ?)', 219762f4807SAndreas Gohr $page, $query, $engine 220762f4807SAndreas Gohr ); 221762f4807SAndreas Gohr if (!$sid) return; 222762f4807SAndreas Gohr 223762f4807SAndreas Gohr foreach ($words as $word) { 224762f4807SAndreas Gohr if (!$word) continue; 225762f4807SAndreas Gohr $this->db->exec( 226762f4807SAndreas Gohr 'INSERT INTO searchwords (sid, word) VALUES (?, ?)', 227762f4807SAndreas Gohr $sid, $word 228762f4807SAndreas Gohr ); 229762f4807SAndreas Gohr } 230762f4807SAndreas Gohr } 231762f4807SAndreas Gohr 232762f4807SAndreas Gohr /** 233762f4807SAndreas Gohr * Log that the session was seen 234762f4807SAndreas Gohr * 235762f4807SAndreas Gohr * This is used to calculate the time people spend on the whole site 236762f4807SAndreas Gohr * during their session 237762f4807SAndreas Gohr * 238762f4807SAndreas Gohr * Viewcounts are used for bounce calculation 239762f4807SAndreas Gohr * 240762f4807SAndreas Gohr * @param int $addview set to 1 to count a view 241762f4807SAndreas Gohr */ 242762f4807SAndreas Gohr public function logSession(int $addview = 0): void 243762f4807SAndreas Gohr { 244762f4807SAndreas Gohr // only log browser sessions 245762f4807SAndreas Gohr if ($this->uaType != 'browser') return; 246762f4807SAndreas Gohr 247762f4807SAndreas Gohr $session = $this->getSession(); 248762f4807SAndreas Gohr $this->db->exec( 249762f4807SAndreas Gohr 'INSERT OR REPLACE INTO session ( 250762f4807SAndreas Gohr session, dt, end, views, uid 251762f4807SAndreas Gohr ) VALUES ( 252762f4807SAndreas Gohr ?, 253762f4807SAndreas Gohr CURRENT_TIMESTAMP, 254762f4807SAndreas Gohr CURRENT_TIMESTAMP, 255762f4807SAndreas Gohr COALESCE((SELECT views FROM session WHERE session = ?) + ?, ?), 256762f4807SAndreas Gohr ? 257762f4807SAndreas Gohr )', 258762f4807SAndreas Gohr $session, $session, $addview, $addview, $this->uid 259762f4807SAndreas Gohr ); 260762f4807SAndreas Gohr } 261762f4807SAndreas Gohr 262762f4807SAndreas Gohr /** 263762f4807SAndreas Gohr * Resolve IP to country/city and store in database 264762f4807SAndreas Gohr * 265762f4807SAndreas Gohr * @param string $ip The IP address to resolve 266762f4807SAndreas Gohr */ 267762f4807SAndreas Gohr public function logIp(string $ip): void 268762f4807SAndreas Gohr { 269762f4807SAndreas Gohr // check if IP already known and up-to-date 270762f4807SAndreas Gohr $result = $this->db->queryValue( 271762f4807SAndreas Gohr "SELECT ip 272762f4807SAndreas Gohr FROM iplocation 273762f4807SAndreas Gohr WHERE ip = ? 274762f4807SAndreas Gohr AND lastupd > date('now', '-30 days')", 275762f4807SAndreas Gohr $ip 276762f4807SAndreas Gohr ); 277762f4807SAndreas Gohr if ($result) return; 278762f4807SAndreas Gohr 279c7cad24dSAndreas Gohr $http = $this->httpClient ?: new DokuHTTPClient(); 280762f4807SAndreas Gohr $http->timeout = 10; 281762f4807SAndreas Gohr $json = $http->get('http://ip-api.com/json/' . $ip); // yes, it's HTTP only 282762f4807SAndreas Gohr 283762f4807SAndreas Gohr if (!$json) return; // FIXME log error 284762f4807SAndreas Gohr try { 285762f4807SAndreas Gohr $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR); 286762f4807SAndreas Gohr } catch (\JsonException $e) { 287762f4807SAndreas Gohr return; // FIXME log error 288762f4807SAndreas Gohr } 289a10aed88SAndreas Gohr if (!isset($data['status']) || $data['status'] !== 'success') { 290a10aed88SAndreas Gohr return; // FIXME log error 291a10aed88SAndreas Gohr } 292762f4807SAndreas Gohr 293762f4807SAndreas Gohr $host = gethostbyaddr($ip); 294762f4807SAndreas Gohr $this->db->exec( 295762f4807SAndreas Gohr 'INSERT OR REPLACE INTO iplocation ( 296762f4807SAndreas Gohr ip, country, code, city, host, lastupd 297762f4807SAndreas Gohr ) VALUES ( 298762f4807SAndreas Gohr ?, ?, ?, ?, ?, CURRENT_TIMESTAMP 299762f4807SAndreas Gohr )', 300762f4807SAndreas Gohr $ip, $data['country'], $data['countryCode'], $data['city'], $host 301762f4807SAndreas Gohr ); 302762f4807SAndreas Gohr } 303762f4807SAndreas Gohr 304762f4807SAndreas Gohr /** 305762f4807SAndreas Gohr * Log a click on an external link 306762f4807SAndreas Gohr * 307762f4807SAndreas Gohr * Called from log.php 308762f4807SAndreas Gohr */ 309762f4807SAndreas Gohr public function logOutgoing(): void 310762f4807SAndreas Gohr { 311762f4807SAndreas Gohr global $INPUT; 312762f4807SAndreas Gohr 313762f4807SAndreas Gohr if (!$INPUT->str('ol')) return; 314762f4807SAndreas Gohr 315762f4807SAndreas Gohr $link = $INPUT->str('ol'); 316762f4807SAndreas Gohr $link_md5 = md5($link); 317762f4807SAndreas Gohr $session = $this->getSession(); 318762f4807SAndreas Gohr $page = $INPUT->str('p'); 319762f4807SAndreas Gohr 320762f4807SAndreas Gohr $this->db->exec( 321762f4807SAndreas Gohr 'INSERT INTO outlinks ( 322762f4807SAndreas Gohr dt, session, page, link_md5, link 323762f4807SAndreas Gohr ) VALUES ( 324762f4807SAndreas Gohr CURRENT_TIMESTAMP, ?, ?, ?, ? 325762f4807SAndreas Gohr )', 326762f4807SAndreas Gohr $session, $page, $link_md5, $link 327762f4807SAndreas Gohr ); 328762f4807SAndreas Gohr } 329762f4807SAndreas Gohr 330762f4807SAndreas Gohr /** 331762f4807SAndreas Gohr * Log a page access 332762f4807SAndreas Gohr * 333762f4807SAndreas Gohr * Called from log.php 334762f4807SAndreas Gohr */ 335762f4807SAndreas Gohr public function logAccess(): void 336762f4807SAndreas Gohr { 337762f4807SAndreas Gohr global $INPUT, $USERINFO; 338762f4807SAndreas Gohr 339762f4807SAndreas Gohr if (!$INPUT->str('p')) return; 340762f4807SAndreas Gohr 341762f4807SAndreas Gohr # FIXME check referer against blacklist and drop logging for bad boys 342762f4807SAndreas Gohr 343762f4807SAndreas Gohr // handle referer 344762f4807SAndreas Gohr $referer = trim($INPUT->str('r')); 345762f4807SAndreas Gohr if ($referer) { 346762f4807SAndreas Gohr $ref = $referer; 347762f4807SAndreas Gohr $ref_md5 = md5($referer); 348762f4807SAndreas Gohr if (str_starts_with($referer, DOKU_URL)) { 349762f4807SAndreas Gohr $ref_type = 'internal'; 350762f4807SAndreas Gohr } else { 351762f4807SAndreas Gohr $ref_type = 'external'; 352762f4807SAndreas Gohr $this->logExternalSearch($referer, $ref_type); 353762f4807SAndreas Gohr } 354762f4807SAndreas Gohr } else { 355762f4807SAndreas Gohr $ref = ''; 356762f4807SAndreas Gohr $ref_md5 = ''; 357762f4807SAndreas Gohr $ref_type = ''; 358762f4807SAndreas Gohr } 359762f4807SAndreas Gohr 360762f4807SAndreas Gohr $page = $INPUT->str('p'); 361762f4807SAndreas Gohr $ip = clientIP(true); 362762f4807SAndreas Gohr $sx = $INPUT->int('sx'); 363762f4807SAndreas Gohr $sy = $INPUT->int('sy'); 364762f4807SAndreas Gohr $vx = $INPUT->int('vx'); 365762f4807SAndreas Gohr $vy = $INPUT->int('vy'); 366762f4807SAndreas Gohr $js = $INPUT->int('js'); 367762f4807SAndreas Gohr $user = $INPUT->server->str('REMOTE_USER'); 368762f4807SAndreas Gohr $session = $this->getSession(); 369762f4807SAndreas Gohr 370762f4807SAndreas Gohr $this->db->exec( 371762f4807SAndreas Gohr 'INSERT INTO access ( 372762f4807SAndreas Gohr dt, page, ip, ua, ua_info, ua_type, ua_ver, os, ref, ref_md5, ref_type, 373762f4807SAndreas Gohr screen_x, screen_y, view_x, view_y, js, user, session, uid 374762f4807SAndreas Gohr ) VALUES ( 375762f4807SAndreas Gohr CURRENT_TIMESTAMP, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 376762f4807SAndreas Gohr ?, ?, ?, ?, ?, ?, ?, ? 377762f4807SAndreas Gohr )', 378762f4807SAndreas Gohr $page, $ip, $this->uaAgent, $this->uaName, $this->uaType, $this->uaVersion, $this->uaPlatform, 379762f4807SAndreas Gohr $ref, $ref_md5, $ref_type, $sx, $sy, $vx, $vy, $js, $user, $session, $this->uid 380762f4807SAndreas Gohr ); 381762f4807SAndreas Gohr 382762f4807SAndreas Gohr if ($ref_md5) { 383762f4807SAndreas Gohr $this->db->exec( 384762f4807SAndreas Gohr 'INSERT OR IGNORE INTO refseen ( 385762f4807SAndreas Gohr ref_md5, dt 386762f4807SAndreas Gohr ) VALUES ( 387762f4807SAndreas Gohr ?, CURRENT_TIMESTAMP 388762f4807SAndreas Gohr )', 389762f4807SAndreas Gohr $ref_md5 390762f4807SAndreas Gohr ); 391762f4807SAndreas Gohr } 392762f4807SAndreas Gohr 393762f4807SAndreas Gohr // log group access 394762f4807SAndreas Gohr if (isset($USERINFO['grps'])) { 395762f4807SAndreas Gohr $this->logGroups('view', $USERINFO['grps']); 396762f4807SAndreas Gohr } 397762f4807SAndreas Gohr 398762f4807SAndreas Gohr // resolve the IP 399762f4807SAndreas Gohr $this->logIp(clientIP(true)); 400762f4807SAndreas Gohr } 401762f4807SAndreas Gohr 402762f4807SAndreas Gohr /** 403762f4807SAndreas Gohr * Log access to a media file 404762f4807SAndreas Gohr * 405762f4807SAndreas Gohr * Called from action.php 406762f4807SAndreas Gohr * 407762f4807SAndreas Gohr * @param string $media The media ID 408762f4807SAndreas Gohr * @param string $mime The media's mime type 409762f4807SAndreas Gohr * @param bool $inline Is this displayed inline? 410762f4807SAndreas Gohr * @param int $size Size of the media file 411762f4807SAndreas Gohr */ 412762f4807SAndreas Gohr public function logMedia(string $media, string $mime, bool $inline, int $size): void 413762f4807SAndreas Gohr { 414762f4807SAndreas Gohr global $INPUT; 415762f4807SAndreas Gohr 416762f4807SAndreas Gohr [$mime1, $mime2] = explode('/', strtolower($mime)); 417762f4807SAndreas Gohr $inline = $inline ? 1 : 0; 418762f4807SAndreas Gohr $size = (int)$size; 419762f4807SAndreas Gohr 420762f4807SAndreas Gohr $ip = clientIP(true); 421762f4807SAndreas Gohr $user = $INPUT->server->str('REMOTE_USER'); 422762f4807SAndreas Gohr $session = $this->getSession(); 423762f4807SAndreas Gohr 424762f4807SAndreas Gohr $this->db->exec( 425762f4807SAndreas Gohr 'INSERT INTO media ( 426762f4807SAndreas Gohr dt, media, ip, ua, ua_info, ua_type, ua_ver, os, user, session, uid, 427762f4807SAndreas Gohr size, mime1, mime2, inline 428762f4807SAndreas Gohr ) VALUES ( 429762f4807SAndreas Gohr CURRENT_TIMESTAMP, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 430762f4807SAndreas Gohr ?, ?, ?, ? 431762f4807SAndreas Gohr )', 432762f4807SAndreas Gohr $media, $ip, $this->uaAgent, $this->uaName, $this->uaType, $this->uaVersion, $this->uaPlatform, 433762f4807SAndreas Gohr $user, $session, $this->uid, $size, $mime1, $mime2, $inline 434762f4807SAndreas Gohr ); 435762f4807SAndreas Gohr } 436762f4807SAndreas Gohr 437762f4807SAndreas Gohr /** 438762f4807SAndreas Gohr * Log page edits 439762f4807SAndreas Gohr * 440762f4807SAndreas Gohr * @param string $page The page that was edited 441762f4807SAndreas Gohr * @param string $type The type of edit (create, edit, etc.) 442762f4807SAndreas Gohr */ 443762f4807SAndreas Gohr public function logEdit(string $page, string $type): void 444762f4807SAndreas Gohr { 445762f4807SAndreas Gohr global $INPUT, $USERINFO; 446762f4807SAndreas Gohr 447762f4807SAndreas Gohr $ip = clientIP(true); 448762f4807SAndreas Gohr $user = $INPUT->server->str('REMOTE_USER'); 449762f4807SAndreas Gohr $session = $this->getSession(); 450762f4807SAndreas Gohr 451762f4807SAndreas Gohr $this->db->exec( 452762f4807SAndreas Gohr 'INSERT INTO edits ( 453762f4807SAndreas Gohr dt, page, type, ip, user, session, uid 454762f4807SAndreas Gohr ) VALUES ( 455762f4807SAndreas Gohr CURRENT_TIMESTAMP, ?, ?, ?, ?, ?, ? 456762f4807SAndreas Gohr )', 457762f4807SAndreas Gohr $page, $type, $ip, $user, $session, $this->uid 458762f4807SAndreas Gohr ); 459762f4807SAndreas Gohr 460762f4807SAndreas Gohr // log group access 461762f4807SAndreas Gohr if (isset($USERINFO['grps'])) { 462762f4807SAndreas Gohr $this->logGroups('edit', $USERINFO['grps']); 463762f4807SAndreas Gohr } 464762f4807SAndreas Gohr } 465762f4807SAndreas Gohr 466762f4807SAndreas Gohr /** 467762f4807SAndreas Gohr * Log login/logoffs and user creations 468762f4807SAndreas Gohr * 469762f4807SAndreas Gohr * @param string $type The type of login event (login, logout, create) 470762f4807SAndreas Gohr * @param string $user The username (optional, will use current user if empty) 471762f4807SAndreas Gohr */ 472762f4807SAndreas Gohr public function logLogin(string $type, string $user = ''): void 473762f4807SAndreas Gohr { 474762f4807SAndreas Gohr global $INPUT; 475762f4807SAndreas Gohr 476762f4807SAndreas Gohr if (!$user) $user = $INPUT->server->str('REMOTE_USER'); 477762f4807SAndreas Gohr 478762f4807SAndreas Gohr $ip = clientIP(true); 479762f4807SAndreas Gohr $session = $this->getSession(); 480762f4807SAndreas Gohr 481762f4807SAndreas Gohr $this->db->exec( 482762f4807SAndreas Gohr 'INSERT INTO logins ( 483762f4807SAndreas Gohr dt, type, ip, user, session, uid 484762f4807SAndreas Gohr ) VALUES ( 485762f4807SAndreas Gohr CURRENT_TIMESTAMP, ?, ?, ?, ?, ? 486762f4807SAndreas Gohr )', 487762f4807SAndreas Gohr $type, $ip, $user, $session, $this->uid 488762f4807SAndreas Gohr ); 489762f4807SAndreas Gohr } 490762f4807SAndreas Gohr 491762f4807SAndreas Gohr /** 492762f4807SAndreas Gohr * Log the current page count and size as today's history entry 493762f4807SAndreas Gohr */ 494762f4807SAndreas Gohr public function logHistoryPages(): void 495762f4807SAndreas Gohr { 496762f4807SAndreas Gohr global $conf; 497762f4807SAndreas Gohr 498762f4807SAndreas Gohr // use the popularity plugin's search method to find the wanted data 499762f4807SAndreas Gohr /** @var helper_plugin_popularity $pop */ 500762f4807SAndreas Gohr $pop = plugin_load('helper', 'popularity'); 501b188870fSAndreas Gohr $list = $this->initEmptySearchList(); 502762f4807SAndreas Gohr search($list, $conf['datadir'], [$pop, 'searchCountCallback'], ['all' => false], ''); 503762f4807SAndreas Gohr $page_count = $list['file_count']; 504762f4807SAndreas Gohr $page_size = $list['file_size']; 505762f4807SAndreas Gohr 506762f4807SAndreas Gohr $this->db->exec( 507762f4807SAndreas Gohr 'INSERT OR REPLACE INTO history ( 508762f4807SAndreas Gohr info, value, dt 509762f4807SAndreas Gohr ) VALUES ( 510483101d3SAndreas Gohr ?, ?, CURRENT_TIMESTAMP 511762f4807SAndreas Gohr )', 512762f4807SAndreas Gohr 'page_count', $page_count 513762f4807SAndreas Gohr ); 514762f4807SAndreas Gohr $this->db->exec( 515762f4807SAndreas Gohr 'INSERT OR REPLACE INTO history ( 516762f4807SAndreas Gohr info, value, dt 517762f4807SAndreas Gohr ) VALUES ( 518483101d3SAndreas Gohr ?, ?, CURRENT_TIMESTAMP 519762f4807SAndreas Gohr )', 520762f4807SAndreas Gohr 'page_size', $page_size 521762f4807SAndreas Gohr ); 522762f4807SAndreas Gohr } 523762f4807SAndreas Gohr 524762f4807SAndreas Gohr /** 525762f4807SAndreas Gohr * Log the current media count and size as today's history entry 526762f4807SAndreas Gohr */ 527762f4807SAndreas Gohr public function logHistoryMedia(): void 528762f4807SAndreas Gohr { 529762f4807SAndreas Gohr global $conf; 530762f4807SAndreas Gohr 531762f4807SAndreas Gohr // use the popularity plugin's search method to find the wanted data 532762f4807SAndreas Gohr /** @var helper_plugin_popularity $pop */ 533762f4807SAndreas Gohr $pop = plugin_load('helper', 'popularity'); 534b188870fSAndreas Gohr $list = $this->initEmptySearchList(); 535762f4807SAndreas Gohr search($list, $conf['mediadir'], [$pop, 'searchCountCallback'], ['all' => true], ''); 536762f4807SAndreas Gohr $media_count = $list['file_count']; 537762f4807SAndreas Gohr $media_size = $list['file_size']; 538762f4807SAndreas Gohr 539762f4807SAndreas Gohr $this->db->exec( 540762f4807SAndreas Gohr 'INSERT OR REPLACE INTO history ( 541762f4807SAndreas Gohr info, value, dt 542762f4807SAndreas Gohr ) VALUES ( 543483101d3SAndreas Gohr ?, ?, CURRENT_TIMESTAMP 544762f4807SAndreas Gohr )', 545762f4807SAndreas Gohr 'media_count', $media_count 546762f4807SAndreas Gohr ); 547762f4807SAndreas Gohr $this->db->exec( 548762f4807SAndreas Gohr 'INSERT OR REPLACE INTO history ( 549762f4807SAndreas Gohr info, value, dt 550762f4807SAndreas Gohr ) VALUES ( 551483101d3SAndreas Gohr ?, ?, CURRENT_TIMESTAMP 552762f4807SAndreas Gohr )', 553762f4807SAndreas Gohr 'media_size', $media_size 554762f4807SAndreas Gohr ); 555762f4807SAndreas Gohr } 556b188870fSAndreas Gohr 557b188870fSAndreas Gohr /** 558b188870fSAndreas Gohr * @todo can be dropped in favor of helper_plugin_popularity::initEmptySearchList() once it's public 559b188870fSAndreas Gohr * @return array 560b188870fSAndreas Gohr */ 561b188870fSAndreas Gohr protected function initEmptySearchList() 562b188870fSAndreas Gohr { 563b188870fSAndreas Gohr return array_fill_keys([ 564b188870fSAndreas Gohr 'file_count', 565b188870fSAndreas Gohr 'file_size', 566b188870fSAndreas Gohr 'file_max', 567b188870fSAndreas Gohr 'file_min', 568b188870fSAndreas Gohr 'dir_count', 569b188870fSAndreas Gohr 'dir_nest', 570b188870fSAndreas Gohr 'file_oldest' 571b188870fSAndreas Gohr ], 0); 572b188870fSAndreas Gohr } 573762f4807SAndreas Gohr} 574