1*762f4807SAndreas Gohr<?php 2*762f4807SAndreas Gohr 3*762f4807SAndreas Gohrnamespace dokuwiki\plugin\statistics; 4*762f4807SAndreas Gohr 5*762f4807SAndreas Gohruse DeviceDetector\DeviceDetector; 6*762f4807SAndreas Gohruse DeviceDetector\Parser\Client\Browser; 7*762f4807SAndreas Gohruse DeviceDetector\Parser\Device\AbstractDeviceParser; 8*762f4807SAndreas Gohruse DeviceDetector\Parser\OperatingSystem; 9*762f4807SAndreas Gohruse dokuwiki\HTTP\DokuHTTPClient; 10*762f4807SAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB; 11*762f4807SAndreas Gohruse dokuwiki\Utf8\Clean; 12*762f4807SAndreas Gohruse dokuwiki\Utf8\PhpString; 13*762f4807SAndreas Gohruse helper_plugin_popularity; 14*762f4807SAndreas Gohruse helper_plugin_statistics; 15*762f4807SAndreas Gohr 16*762f4807SAndreas Gohr 17*762f4807SAndreas Gohrclass Logger 18*762f4807SAndreas Gohr{ 19*762f4807SAndreas Gohr /** @var helper_plugin_statistics The statistics helper plugin instance */ 20*762f4807SAndreas Gohr protected helper_plugin_statistics $hlp; 21*762f4807SAndreas Gohr 22*762f4807SAndreas Gohr /** @var SQLiteDB The SQLite database instance */ 23*762f4807SAndreas Gohr protected SQLiteDB $db; 24*762f4807SAndreas Gohr 25*762f4807SAndreas Gohr /** @var string The full user agent string */ 26*762f4807SAndreas Gohr protected string $uaAgent; 27*762f4807SAndreas Gohr 28*762f4807SAndreas Gohr /** @var string The type of user agent (browser, robot, feedreader) */ 29*762f4807SAndreas Gohr protected string $uaType = 'browser'; 30*762f4807SAndreas Gohr 31*762f4807SAndreas Gohr /** @var string The browser/client name */ 32*762f4807SAndreas Gohr protected string $uaName; 33*762f4807SAndreas Gohr 34*762f4807SAndreas Gohr /** @var string The browser/client version */ 35*762f4807SAndreas Gohr protected string $uaVersion; 36*762f4807SAndreas Gohr 37*762f4807SAndreas Gohr /** @var string The operating system/platform */ 38*762f4807SAndreas Gohr protected string $uaPlatform; 39*762f4807SAndreas Gohr 40*762f4807SAndreas Gohr /** @var string The unique user identifier */ 41*762f4807SAndreas Gohr protected string $uid; 42*762f4807SAndreas Gohr 43*762f4807SAndreas Gohr 44*762f4807SAndreas Gohr /** 45*762f4807SAndreas Gohr * Constructor 46*762f4807SAndreas Gohr * 47*762f4807SAndreas Gohr * Parses browser info and set internal vars 48*762f4807SAndreas Gohr */ 49*762f4807SAndreas Gohr public function __construct(helper_plugin_statistics $hlp) 50*762f4807SAndreas Gohr { 51*762f4807SAndreas Gohr global $INPUT; 52*762f4807SAndreas Gohr 53*762f4807SAndreas Gohr $this->hlp = $hlp; 54*762f4807SAndreas Gohr $this->db = $this->hlp->getDB(); 55*762f4807SAndreas Gohr 56*762f4807SAndreas Gohr $ua = trim($INPUT->server->str('HTTP_USER_AGENT')); 57*762f4807SAndreas Gohr 58*762f4807SAndreas Gohr AbstractDeviceParser::setVersionTruncation(AbstractDeviceParser::VERSION_TRUNCATION_MAJOR); 59*762f4807SAndreas Gohr $dd = new DeviceDetector($ua); // FIXME we could use client hints, but need to add headers 60*762f4807SAndreas Gohr $dd->discardBotInformation(); 61*762f4807SAndreas Gohr $dd->parse(); 62*762f4807SAndreas Gohr 63*762f4807SAndreas Gohr if ($dd->isBot()) { 64*762f4807SAndreas Gohr $this->uaType = 'robot'; 65*762f4807SAndreas Gohr 66*762f4807SAndreas Gohr // for now ignore bots 67*762f4807SAndreas Gohr throw new \RuntimeException('Bot detected, not logging'); 68*762f4807SAndreas Gohr } 69*762f4807SAndreas Gohr 70*762f4807SAndreas Gohr $this->uaAgent = $ua; 71*762f4807SAndreas Gohr $this->uaName = Browser::getBrowserFamily($dd->getClient('name')); 72*762f4807SAndreas Gohr $this->uaVersion = $dd->getClient('version'); 73*762f4807SAndreas Gohr $this->uaPlatform = OperatingSystem::getOsFamily($dd->getOs('name')); 74*762f4807SAndreas Gohr $this->uid = $this->getUID(); 75*762f4807SAndreas Gohr 76*762f4807SAndreas Gohr if ($dd->isFeedReader()) { 77*762f4807SAndreas Gohr $this->uaType = 'feedreader'; 78*762f4807SAndreas Gohr } 79*762f4807SAndreas Gohr 80*762f4807SAndreas Gohr $this->logLastseen(); 81*762f4807SAndreas Gohr } 82*762f4807SAndreas Gohr 83*762f4807SAndreas Gohr /** 84*762f4807SAndreas Gohr * Should be called before logging 85*762f4807SAndreas Gohr * 86*762f4807SAndreas Gohr * This starts a transaction, so all logging is done in one go 87*762f4807SAndreas Gohr */ 88*762f4807SAndreas Gohr public function begin(): void 89*762f4807SAndreas Gohr { 90*762f4807SAndreas Gohr $this->hlp->getDB()->getPdo()->beginTransaction(); 91*762f4807SAndreas Gohr } 92*762f4807SAndreas Gohr 93*762f4807SAndreas Gohr /** 94*762f4807SAndreas Gohr * Should be called after logging 95*762f4807SAndreas Gohr * 96*762f4807SAndreas Gohr * This commits the transaction started in begin() 97*762f4807SAndreas Gohr */ 98*762f4807SAndreas Gohr public function end(): void 99*762f4807SAndreas Gohr { 100*762f4807SAndreas Gohr $this->hlp->getDB()->getPdo()->commit(); 101*762f4807SAndreas Gohr } 102*762f4807SAndreas Gohr 103*762f4807SAndreas Gohr /** 104*762f4807SAndreas Gohr * Get the unique user ID 105*762f4807SAndreas Gohr * 106*762f4807SAndreas Gohr * @return string The unique user identifier 107*762f4807SAndreas Gohr */ 108*762f4807SAndreas Gohr protected function getUID(): string 109*762f4807SAndreas Gohr { 110*762f4807SAndreas Gohr global $INPUT; 111*762f4807SAndreas Gohr 112*762f4807SAndreas Gohr $uid = $INPUT->str('uid'); 113*762f4807SAndreas Gohr if (!$uid) $uid = get_doku_pref('plgstats', false); 114*762f4807SAndreas Gohr if (!$uid) $uid = session_id(); 115*762f4807SAndreas Gohr return $uid; 116*762f4807SAndreas Gohr } 117*762f4807SAndreas Gohr 118*762f4807SAndreas Gohr /** 119*762f4807SAndreas Gohr * Return the user's session ID 120*762f4807SAndreas Gohr * 121*762f4807SAndreas Gohr * This is usually our own managed session, not a PHP session (only in fallback) 122*762f4807SAndreas Gohr * 123*762f4807SAndreas Gohr * @return string The session identifier 124*762f4807SAndreas Gohr */ 125*762f4807SAndreas Gohr protected function getSession(): string 126*762f4807SAndreas Gohr { 127*762f4807SAndreas Gohr global $INPUT; 128*762f4807SAndreas Gohr 129*762f4807SAndreas Gohr $ses = $INPUT->str('ses'); 130*762f4807SAndreas Gohr if (!$ses) $ses = get_doku_pref('plgstatsses', false); 131*762f4807SAndreas Gohr if (!$ses) $ses = session_id(); 132*762f4807SAndreas Gohr return $ses; 133*762f4807SAndreas Gohr } 134*762f4807SAndreas Gohr 135*762f4807SAndreas Gohr /** 136*762f4807SAndreas Gohr * Log that we've seen the user (authenticated only) 137*762f4807SAndreas Gohr */ 138*762f4807SAndreas Gohr public function logLastseen(): void 139*762f4807SAndreas Gohr { 140*762f4807SAndreas Gohr global $INPUT; 141*762f4807SAndreas Gohr 142*762f4807SAndreas Gohr if (empty($INPUT->server->str('REMOTE_USER'))) return; 143*762f4807SAndreas Gohr 144*762f4807SAndreas Gohr $this->db->exec( 145*762f4807SAndreas Gohr 'REPLACE INTO lastseen (user, dt) VALUES (?, CURRENT_TIMESTAMP)', 146*762f4807SAndreas Gohr $INPUT->server->str('REMOTE_USER'), 147*762f4807SAndreas Gohr ); 148*762f4807SAndreas Gohr } 149*762f4807SAndreas Gohr 150*762f4807SAndreas Gohr /** 151*762f4807SAndreas Gohr * Log actions by groups 152*762f4807SAndreas Gohr * 153*762f4807SAndreas Gohr * @param string $type The type of access to log ('view','edit') 154*762f4807SAndreas Gohr * @param array $groups The groups to log 155*762f4807SAndreas Gohr */ 156*762f4807SAndreas Gohr public function logGroups(string $type, array $groups): void 157*762f4807SAndreas Gohr { 158*762f4807SAndreas Gohr if (!is_array($groups)) { 159*762f4807SAndreas Gohr return; 160*762f4807SAndreas Gohr } 161*762f4807SAndreas Gohr 162*762f4807SAndreas Gohr $tolog = (array)$this->hlp->getConf('loggroups'); 163*762f4807SAndreas Gohr $groups = array_intersect($groups, $tolog); 164*762f4807SAndreas Gohr if ($groups === []) { 165*762f4807SAndreas Gohr return; 166*762f4807SAndreas Gohr } 167*762f4807SAndreas Gohr 168*762f4807SAndreas Gohr 169*762f4807SAndreas Gohr $params = []; 170*762f4807SAndreas Gohr $sql = "INSERT INTO groups (`type`, `group`) VALUES "; 171*762f4807SAndreas Gohr foreach ($groups as $group) { 172*762f4807SAndreas Gohr $sql .= '(?, ?),'; 173*762f4807SAndreas Gohr $params[] = $type; 174*762f4807SAndreas Gohr $params[] = $group; 175*762f4807SAndreas Gohr } 176*762f4807SAndreas Gohr $sql = rtrim($sql, ','); 177*762f4807SAndreas Gohr $this->db->exec($sql, $params); 178*762f4807SAndreas Gohr } 179*762f4807SAndreas Gohr 180*762f4807SAndreas Gohr /** 181*762f4807SAndreas Gohr * Log external search queries 182*762f4807SAndreas Gohr * 183*762f4807SAndreas Gohr * Will not write anything if the referer isn't a search engine 184*762f4807SAndreas Gohr * 185*762f4807SAndreas Gohr * @param string $referer The HTTP referer URL 186*762f4807SAndreas Gohr * @param string $type Reference to the type variable that will be modified 187*762f4807SAndreas Gohr */ 188*762f4807SAndreas Gohr public function logExternalSearch(string $referer, string &$type): void 189*762f4807SAndreas Gohr { 190*762f4807SAndreas Gohr global $INPUT; 191*762f4807SAndreas Gohr 192*762f4807SAndreas Gohr $searchEngine = new SearchEngines($referer); 193*762f4807SAndreas Gohr 194*762f4807SAndreas Gohr if (!$searchEngine->isSearchEngine()) { 195*762f4807SAndreas Gohr return; // not a search engine 196*762f4807SAndreas Gohr } 197*762f4807SAndreas Gohr 198*762f4807SAndreas Gohr $type = 'search'; 199*762f4807SAndreas Gohr $query = $searchEngine->getQuery(); 200*762f4807SAndreas Gohr 201*762f4807SAndreas Gohr // log it! 202*762f4807SAndreas Gohr $words = explode(' ', Clean::stripspecials($query, ' ', '\._\-:\*')); 203*762f4807SAndreas Gohr $this->logSearch($INPUT->str('p'), $query, $words, $searchEngine->getEngine()); 204*762f4807SAndreas Gohr } 205*762f4807SAndreas Gohr 206*762f4807SAndreas Gohr /** 207*762f4807SAndreas Gohr * Log search data to the search related tables 208*762f4807SAndreas Gohr * 209*762f4807SAndreas Gohr * @param string $page The page being searched from 210*762f4807SAndreas Gohr * @param string $query The search query 211*762f4807SAndreas Gohr * @param array $words Array of search words 212*762f4807SAndreas Gohr * @param string $engine The search engine name 213*762f4807SAndreas Gohr */ 214*762f4807SAndreas Gohr public function logSearch(string $page, string $query, array $words, string $engine): void 215*762f4807SAndreas Gohr { 216*762f4807SAndreas Gohr $sid = $this->db->exec( 217*762f4807SAndreas Gohr 'INSERT INTO search (dt, page, query, engine) VALUES (CURRENT_TIMESTAMP, ?, ?, ?)', 218*762f4807SAndreas Gohr $page, $query, $engine 219*762f4807SAndreas Gohr ); 220*762f4807SAndreas Gohr if (!$sid) return; 221*762f4807SAndreas Gohr 222*762f4807SAndreas Gohr foreach ($words as $word) { 223*762f4807SAndreas Gohr if (!$word) continue; 224*762f4807SAndreas Gohr $this->db->exec( 225*762f4807SAndreas Gohr 'INSERT INTO searchwords (sid, word) VALUES (?, ?)', 226*762f4807SAndreas Gohr $sid, $word 227*762f4807SAndreas Gohr ); 228*762f4807SAndreas Gohr } 229*762f4807SAndreas Gohr } 230*762f4807SAndreas Gohr 231*762f4807SAndreas Gohr /** 232*762f4807SAndreas Gohr * Log that the session was seen 233*762f4807SAndreas Gohr * 234*762f4807SAndreas Gohr * This is used to calculate the time people spend on the whole site 235*762f4807SAndreas Gohr * during their session 236*762f4807SAndreas Gohr * 237*762f4807SAndreas Gohr * Viewcounts are used for bounce calculation 238*762f4807SAndreas Gohr * 239*762f4807SAndreas Gohr * @param int $addview set to 1 to count a view 240*762f4807SAndreas Gohr */ 241*762f4807SAndreas Gohr public function logSession(int $addview = 0): void 242*762f4807SAndreas Gohr { 243*762f4807SAndreas Gohr // only log browser sessions 244*762f4807SAndreas Gohr if ($this->uaType != 'browser') return; 245*762f4807SAndreas Gohr 246*762f4807SAndreas Gohr $session = $this->getSession(); 247*762f4807SAndreas Gohr $this->db->exec( 248*762f4807SAndreas Gohr 'INSERT OR REPLACE INTO session ( 249*762f4807SAndreas Gohr session, dt, end, views, uid 250*762f4807SAndreas Gohr ) VALUES ( 251*762f4807SAndreas Gohr ?, 252*762f4807SAndreas Gohr CURRENT_TIMESTAMP, 253*762f4807SAndreas Gohr CURRENT_TIMESTAMP, 254*762f4807SAndreas Gohr COALESCE((SELECT views FROM session WHERE session = ?) + ?, ?), 255*762f4807SAndreas Gohr ? 256*762f4807SAndreas Gohr )', 257*762f4807SAndreas Gohr $session, $session, $addview, $addview, $this->uid 258*762f4807SAndreas Gohr ); 259*762f4807SAndreas Gohr } 260*762f4807SAndreas Gohr 261*762f4807SAndreas Gohr /** 262*762f4807SAndreas Gohr * Resolve IP to country/city and store in database 263*762f4807SAndreas Gohr * 264*762f4807SAndreas Gohr * @param string $ip The IP address to resolve 265*762f4807SAndreas Gohr */ 266*762f4807SAndreas Gohr public function logIp(string $ip): void 267*762f4807SAndreas Gohr { 268*762f4807SAndreas Gohr // check if IP already known and up-to-date 269*762f4807SAndreas Gohr $result = $this->db->queryValue( 270*762f4807SAndreas Gohr "SELECT ip 271*762f4807SAndreas Gohr FROM iplocation 272*762f4807SAndreas Gohr WHERE ip = ? 273*762f4807SAndreas Gohr AND lastupd > date('now', '-30 days')", 274*762f4807SAndreas Gohr $ip 275*762f4807SAndreas Gohr ); 276*762f4807SAndreas Gohr if ($result) return; 277*762f4807SAndreas Gohr 278*762f4807SAndreas Gohr $http = new DokuHTTPClient(); 279*762f4807SAndreas Gohr $http->timeout = 10; 280*762f4807SAndreas Gohr $json = $http->get('http://ip-api.com/json/' . $ip); // yes, it's HTTP only 281*762f4807SAndreas Gohr 282*762f4807SAndreas Gohr if (!$json) return; // FIXME log error 283*762f4807SAndreas Gohr try { 284*762f4807SAndreas Gohr $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR); 285*762f4807SAndreas Gohr } catch (\JsonException $e) { 286*762f4807SAndreas Gohr return; // FIXME log error 287*762f4807SAndreas Gohr } 288*762f4807SAndreas Gohr 289*762f4807SAndreas Gohr $host = gethostbyaddr($ip); 290*762f4807SAndreas Gohr $this->db->exec( 291*762f4807SAndreas Gohr 'INSERT OR REPLACE INTO iplocation ( 292*762f4807SAndreas Gohr ip, country, code, city, host, lastupd 293*762f4807SAndreas Gohr ) VALUES ( 294*762f4807SAndreas Gohr ?, ?, ?, ?, ?, CURRENT_TIMESTAMP 295*762f4807SAndreas Gohr )', 296*762f4807SAndreas Gohr $ip, $data['country'], $data['countryCode'], $data['city'], $host 297*762f4807SAndreas Gohr ); 298*762f4807SAndreas Gohr } 299*762f4807SAndreas Gohr 300*762f4807SAndreas Gohr /** 301*762f4807SAndreas Gohr * Log a click on an external link 302*762f4807SAndreas Gohr * 303*762f4807SAndreas Gohr * Called from log.php 304*762f4807SAndreas Gohr */ 305*762f4807SAndreas Gohr public function logOutgoing(): void 306*762f4807SAndreas Gohr { 307*762f4807SAndreas Gohr global $INPUT; 308*762f4807SAndreas Gohr 309*762f4807SAndreas Gohr if (!$INPUT->str('ol')) return; 310*762f4807SAndreas Gohr 311*762f4807SAndreas Gohr $link = $INPUT->str('ol'); 312*762f4807SAndreas Gohr $link_md5 = md5($link); 313*762f4807SAndreas Gohr $session = $this->getSession(); 314*762f4807SAndreas Gohr $page = $INPUT->str('p'); 315*762f4807SAndreas Gohr 316*762f4807SAndreas Gohr $this->db->exec( 317*762f4807SAndreas Gohr 'INSERT INTO outlinks ( 318*762f4807SAndreas Gohr dt, session, page, link_md5, link 319*762f4807SAndreas Gohr ) VALUES ( 320*762f4807SAndreas Gohr CURRENT_TIMESTAMP, ?, ?, ?, ? 321*762f4807SAndreas Gohr )', 322*762f4807SAndreas Gohr $session, $page, $link_md5, $link 323*762f4807SAndreas Gohr ); 324*762f4807SAndreas Gohr } 325*762f4807SAndreas Gohr 326*762f4807SAndreas Gohr /** 327*762f4807SAndreas Gohr * Log a page access 328*762f4807SAndreas Gohr * 329*762f4807SAndreas Gohr * Called from log.php 330*762f4807SAndreas Gohr */ 331*762f4807SAndreas Gohr public function logAccess(): void 332*762f4807SAndreas Gohr { 333*762f4807SAndreas Gohr global $INPUT, $USERINFO; 334*762f4807SAndreas Gohr 335*762f4807SAndreas Gohr if (!$INPUT->str('p')) return; 336*762f4807SAndreas Gohr 337*762f4807SAndreas Gohr # FIXME check referer against blacklist and drop logging for bad boys 338*762f4807SAndreas Gohr 339*762f4807SAndreas Gohr // handle referer 340*762f4807SAndreas Gohr $referer = trim($INPUT->str('r')); 341*762f4807SAndreas Gohr if ($referer) { 342*762f4807SAndreas Gohr $ref = $referer; 343*762f4807SAndreas Gohr $ref_md5 = md5($referer); 344*762f4807SAndreas Gohr if (str_starts_with($referer, DOKU_URL)) { 345*762f4807SAndreas Gohr $ref_type = 'internal'; 346*762f4807SAndreas Gohr } else { 347*762f4807SAndreas Gohr $ref_type = 'external'; 348*762f4807SAndreas Gohr $this->logExternalSearch($referer, $ref_type); 349*762f4807SAndreas Gohr } 350*762f4807SAndreas Gohr } else { 351*762f4807SAndreas Gohr $ref = ''; 352*762f4807SAndreas Gohr $ref_md5 = ''; 353*762f4807SAndreas Gohr $ref_type = ''; 354*762f4807SAndreas Gohr } 355*762f4807SAndreas Gohr 356*762f4807SAndreas Gohr $page = $INPUT->str('p'); 357*762f4807SAndreas Gohr $ip = clientIP(true); 358*762f4807SAndreas Gohr $sx = $INPUT->int('sx'); 359*762f4807SAndreas Gohr $sy = $INPUT->int('sy'); 360*762f4807SAndreas Gohr $vx = $INPUT->int('vx'); 361*762f4807SAndreas Gohr $vy = $INPUT->int('vy'); 362*762f4807SAndreas Gohr $js = $INPUT->int('js'); 363*762f4807SAndreas Gohr $user = $INPUT->server->str('REMOTE_USER'); 364*762f4807SAndreas Gohr $session = $this->getSession(); 365*762f4807SAndreas Gohr 366*762f4807SAndreas Gohr $this->db->exec( 367*762f4807SAndreas Gohr 'INSERT INTO access ( 368*762f4807SAndreas Gohr dt, page, ip, ua, ua_info, ua_type, ua_ver, os, ref, ref_md5, ref_type, 369*762f4807SAndreas Gohr screen_x, screen_y, view_x, view_y, js, user, session, uid 370*762f4807SAndreas Gohr ) VALUES ( 371*762f4807SAndreas Gohr CURRENT_TIMESTAMP, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 372*762f4807SAndreas Gohr ?, ?, ?, ?, ?, ?, ?, ? 373*762f4807SAndreas Gohr )', 374*762f4807SAndreas Gohr $page, $ip, $this->uaAgent, $this->uaName, $this->uaType, $this->uaVersion, $this->uaPlatform, 375*762f4807SAndreas Gohr $ref, $ref_md5, $ref_type, $sx, $sy, $vx, $vy, $js, $user, $session, $this->uid 376*762f4807SAndreas Gohr ); 377*762f4807SAndreas Gohr 378*762f4807SAndreas Gohr if ($ref_md5) { 379*762f4807SAndreas Gohr $this->db->exec( 380*762f4807SAndreas Gohr 'INSERT OR IGNORE INTO refseen ( 381*762f4807SAndreas Gohr ref_md5, dt 382*762f4807SAndreas Gohr ) VALUES ( 383*762f4807SAndreas Gohr ?, CURRENT_TIMESTAMP 384*762f4807SAndreas Gohr )', 385*762f4807SAndreas Gohr $ref_md5 386*762f4807SAndreas Gohr ); 387*762f4807SAndreas Gohr } 388*762f4807SAndreas Gohr 389*762f4807SAndreas Gohr // log group access 390*762f4807SAndreas Gohr if (isset($USERINFO['grps'])) { 391*762f4807SAndreas Gohr $this->logGroups('view', $USERINFO['grps']); 392*762f4807SAndreas Gohr } 393*762f4807SAndreas Gohr 394*762f4807SAndreas Gohr // resolve the IP 395*762f4807SAndreas Gohr $this->logIp(clientIP(true)); 396*762f4807SAndreas Gohr } 397*762f4807SAndreas Gohr 398*762f4807SAndreas Gohr /** 399*762f4807SAndreas Gohr * Log access to a media file 400*762f4807SAndreas Gohr * 401*762f4807SAndreas Gohr * Called from action.php 402*762f4807SAndreas Gohr * 403*762f4807SAndreas Gohr * @param string $media The media ID 404*762f4807SAndreas Gohr * @param string $mime The media's mime type 405*762f4807SAndreas Gohr * @param bool $inline Is this displayed inline? 406*762f4807SAndreas Gohr * @param int $size Size of the media file 407*762f4807SAndreas Gohr */ 408*762f4807SAndreas Gohr public function logMedia(string $media, string $mime, bool $inline, int $size): void 409*762f4807SAndreas Gohr { 410*762f4807SAndreas Gohr global $INPUT; 411*762f4807SAndreas Gohr 412*762f4807SAndreas Gohr [$mime1, $mime2] = explode('/', strtolower($mime)); 413*762f4807SAndreas Gohr $inline = $inline ? 1 : 0; 414*762f4807SAndreas Gohr $size = (int)$size; 415*762f4807SAndreas Gohr 416*762f4807SAndreas Gohr $ip = clientIP(true); 417*762f4807SAndreas Gohr $user = $INPUT->server->str('REMOTE_USER'); 418*762f4807SAndreas Gohr $session = $this->getSession(); 419*762f4807SAndreas Gohr 420*762f4807SAndreas Gohr $this->db->exec( 421*762f4807SAndreas Gohr 'INSERT INTO media ( 422*762f4807SAndreas Gohr dt, media, ip, ua, ua_info, ua_type, ua_ver, os, user, session, uid, 423*762f4807SAndreas Gohr size, mime1, mime2, inline 424*762f4807SAndreas Gohr ) VALUES ( 425*762f4807SAndreas Gohr CURRENT_TIMESTAMP, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 426*762f4807SAndreas Gohr ?, ?, ?, ? 427*762f4807SAndreas Gohr )', 428*762f4807SAndreas Gohr $media, $ip, $this->uaAgent, $this->uaName, $this->uaType, $this->uaVersion, $this->uaPlatform, 429*762f4807SAndreas Gohr $user, $session, $this->uid, $size, $mime1, $mime2, $inline 430*762f4807SAndreas Gohr ); 431*762f4807SAndreas Gohr } 432*762f4807SAndreas Gohr 433*762f4807SAndreas Gohr /** 434*762f4807SAndreas Gohr * Log page edits 435*762f4807SAndreas Gohr * 436*762f4807SAndreas Gohr * @param string $page The page that was edited 437*762f4807SAndreas Gohr * @param string $type The type of edit (create, edit, etc.) 438*762f4807SAndreas Gohr */ 439*762f4807SAndreas Gohr public function logEdit(string $page, string $type): void 440*762f4807SAndreas Gohr { 441*762f4807SAndreas Gohr global $INPUT, $USERINFO; 442*762f4807SAndreas Gohr 443*762f4807SAndreas Gohr $ip = clientIP(true); 444*762f4807SAndreas Gohr $user = $INPUT->server->str('REMOTE_USER'); 445*762f4807SAndreas Gohr $session = $this->getSession(); 446*762f4807SAndreas Gohr 447*762f4807SAndreas Gohr $this->db->exec( 448*762f4807SAndreas Gohr 'INSERT INTO edits ( 449*762f4807SAndreas Gohr dt, page, type, ip, user, session, uid 450*762f4807SAndreas Gohr ) VALUES ( 451*762f4807SAndreas Gohr CURRENT_TIMESTAMP, ?, ?, ?, ?, ?, ? 452*762f4807SAndreas Gohr )', 453*762f4807SAndreas Gohr $page, $type, $ip, $user, $session, $this->uid 454*762f4807SAndreas Gohr ); 455*762f4807SAndreas Gohr 456*762f4807SAndreas Gohr // log group access 457*762f4807SAndreas Gohr if (isset($USERINFO['grps'])) { 458*762f4807SAndreas Gohr $this->logGroups('edit', $USERINFO['grps']); 459*762f4807SAndreas Gohr } 460*762f4807SAndreas Gohr } 461*762f4807SAndreas Gohr 462*762f4807SAndreas Gohr /** 463*762f4807SAndreas Gohr * Log login/logoffs and user creations 464*762f4807SAndreas Gohr * 465*762f4807SAndreas Gohr * @param string $type The type of login event (login, logout, create) 466*762f4807SAndreas Gohr * @param string $user The username (optional, will use current user if empty) 467*762f4807SAndreas Gohr */ 468*762f4807SAndreas Gohr public function logLogin(string $type, string $user = ''): void 469*762f4807SAndreas Gohr { 470*762f4807SAndreas Gohr global $INPUT; 471*762f4807SAndreas Gohr 472*762f4807SAndreas Gohr if (!$user) $user = $INPUT->server->str('REMOTE_USER'); 473*762f4807SAndreas Gohr 474*762f4807SAndreas Gohr $ip = clientIP(true); 475*762f4807SAndreas Gohr $session = $this->getSession(); 476*762f4807SAndreas Gohr 477*762f4807SAndreas Gohr $this->db->exec( 478*762f4807SAndreas Gohr 'INSERT INTO logins ( 479*762f4807SAndreas Gohr dt, type, ip, user, session, uid 480*762f4807SAndreas Gohr ) VALUES ( 481*762f4807SAndreas Gohr CURRENT_TIMESTAMP, ?, ?, ?, ?, ? 482*762f4807SAndreas Gohr )', 483*762f4807SAndreas Gohr $type, $ip, $user, $session, $this->uid 484*762f4807SAndreas Gohr ); 485*762f4807SAndreas Gohr } 486*762f4807SAndreas Gohr 487*762f4807SAndreas Gohr /** 488*762f4807SAndreas Gohr * Log the current page count and size as today's history entry 489*762f4807SAndreas Gohr */ 490*762f4807SAndreas Gohr public function logHistoryPages(): void 491*762f4807SAndreas Gohr { 492*762f4807SAndreas Gohr global $conf; 493*762f4807SAndreas Gohr 494*762f4807SAndreas Gohr // use the popularity plugin's search method to find the wanted data 495*762f4807SAndreas Gohr /** @var helper_plugin_popularity $pop */ 496*762f4807SAndreas Gohr $pop = plugin_load('helper', 'popularity'); 497*762f4807SAndreas Gohr $list = []; 498*762f4807SAndreas Gohr search($list, $conf['datadir'], [$pop, 'searchCountCallback'], ['all' => false], ''); 499*762f4807SAndreas Gohr $page_count = $list['file_count']; 500*762f4807SAndreas Gohr $page_size = $list['file_size']; 501*762f4807SAndreas Gohr 502*762f4807SAndreas Gohr $this->db->exec( 503*762f4807SAndreas Gohr 'INSERT OR REPLACE INTO history ( 504*762f4807SAndreas Gohr info, value, dt 505*762f4807SAndreas Gohr ) VALUES ( 506*762f4807SAndreas Gohr ?, ?, date("now") 507*762f4807SAndreas Gohr )', 508*762f4807SAndreas Gohr 'page_count', $page_count 509*762f4807SAndreas Gohr ); 510*762f4807SAndreas Gohr $this->db->exec( 511*762f4807SAndreas Gohr 'INSERT OR REPLACE INTO history ( 512*762f4807SAndreas Gohr info, value, dt 513*762f4807SAndreas Gohr ) VALUES ( 514*762f4807SAndreas Gohr ?, ?, date("now") 515*762f4807SAndreas Gohr )', 516*762f4807SAndreas Gohr 'page_size', $page_size 517*762f4807SAndreas Gohr ); 518*762f4807SAndreas Gohr } 519*762f4807SAndreas Gohr 520*762f4807SAndreas Gohr /** 521*762f4807SAndreas Gohr * Log the current media count and size as today's history entry 522*762f4807SAndreas Gohr */ 523*762f4807SAndreas Gohr public function logHistoryMedia(): void 524*762f4807SAndreas Gohr { 525*762f4807SAndreas Gohr global $conf; 526*762f4807SAndreas Gohr 527*762f4807SAndreas Gohr // use the popularity plugin's search method to find the wanted data 528*762f4807SAndreas Gohr /** @var helper_plugin_popularity $pop */ 529*762f4807SAndreas Gohr $pop = plugin_load('helper', 'popularity'); 530*762f4807SAndreas Gohr $list = []; 531*762f4807SAndreas Gohr search($list, $conf['mediadir'], [$pop, 'searchCountCallback'], ['all' => true], ''); 532*762f4807SAndreas Gohr $media_count = $list['file_count']; 533*762f4807SAndreas Gohr $media_size = $list['file_size']; 534*762f4807SAndreas Gohr 535*762f4807SAndreas Gohr $this->db->exec( 536*762f4807SAndreas Gohr 'INSERT OR REPLACE INTO history ( 537*762f4807SAndreas Gohr info, value, dt 538*762f4807SAndreas Gohr ) VALUES ( 539*762f4807SAndreas Gohr ?, ?, date("now") 540*762f4807SAndreas Gohr )', 541*762f4807SAndreas Gohr 'media_count', $media_count 542*762f4807SAndreas Gohr ); 543*762f4807SAndreas Gohr $this->db->exec( 544*762f4807SAndreas Gohr 'INSERT OR REPLACE INTO history ( 545*762f4807SAndreas Gohr info, value, dt 546*762f4807SAndreas Gohr ) VALUES ( 547*762f4807SAndreas Gohr ?, ?, date("now") 548*762f4807SAndreas Gohr )', 549*762f4807SAndreas Gohr 'media_size', $media_size 550*762f4807SAndreas Gohr ); 551*762f4807SAndreas Gohr } 552*762f4807SAndreas Gohr} 553