xref: /plugin/statistics/admin.php (revision 2812a7519d6c63c493fa4b6e912a9321cbe63a38)
1<?php
2/**
3 * statistics plugin
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Andreas Gohr <gohr@cosmocode.de>
7 */
8
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
13require_once(DOKU_PLUGIN.'admin.php');
14
15/**
16 * All DokuWiki plugins to extend the admin function
17 * need to inherit from this class
18 */
19class admin_plugin_statistics extends DokuWiki_Admin_Plugin {
20    var $dblink = null;
21    var $opt    = '';
22    var $from   = '';
23    var $to     = '';
24    var $start  = '';
25    var $tlimit = '';
26
27    /**
28     * return some info
29     */
30    function getInfo(){
31        return confToHash(dirname(__FILE__).'/info.txt');
32    }
33
34    /**
35     * Access for managers allowed
36     */
37    function forAdminOnly(){
38        return false;
39    }
40
41    /**
42     * return sort order for position in admin menu
43     */
44    function getMenuSort() {
45        return 150;
46    }
47
48    /**
49     * handle user request
50     */
51    function handle() {
52        $this->opt = preg_replace('/[^a-z]+/','',$_REQUEST['opt']);
53
54        $this->start = (int) $_REQUEST['s'];
55
56        // fixme add better sanity checking here:
57        $this->from = preg_replace('/[^\d\-]+/','',$_REQUEST['f']);
58        $this->to = preg_replace('/[^\d\-]+/','',$_REQUEST['t']);
59        if(!$this->from) $this->from = date('Y-m-d');
60        if(!$this->to) $this->to     = date('Y-m-d');
61
62        //setup limit clause
63        if($this->from != $this->to){
64            $this->tlimit = "DATE(A.dt) >= DATE('".$this->from."') AND DATE(A.dt) <= DATE('".$this->to."')";
65        }else{
66            $this->tlimit = "DATE(A.dt) = DATE('".$this->from."')";
67        }
68    }
69
70    /**
71     * fixme build statistics here
72     */
73    function html() {
74        $this->html_toc();
75        echo '<h1>Access Statistics</h1>';
76        $this->html_timeselect();
77
78        switch($this->opt){
79            case 'country':
80                $this->html_country();
81                break;
82            case 'page':
83                $this->html_page();
84                break;
85            case 'referer':
86                $this->html_referer();
87                break;
88            default:
89                $this->html_dashboard();
90        }
91    }
92
93    function html_toc(){
94        echo '<div class="toc">';
95        echo '<div class="tocheader toctoggle" id="toc__header">';
96        echo 'Detailed Statistics';
97        echo '</div>';
98        echo '<div id="toc__inside">';
99        echo '<ul class="toc">';
100
101        echo '<li><div class="li">';
102        echo '<a href="?do=admin&amp;page=statistics&amp;opt=&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;s='.$this->start.'">Dashboard</a>';
103        echo '</div></li>';
104
105        echo '<li><div class="li">';
106        echo '<a href="?do=admin&amp;page=statistics&amp;opt=page&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;s='.$this->start.'">Pages</a>';
107        echo '</div></li>';
108
109        echo '<li><div class="li">';
110        echo '<a href="?do=admin&amp;page=statistics&amp;opt=referer&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;s='.$this->start.'">Incoming Links</a>';
111        echo '</div></li>';
112
113        echo '<li><div class="li">';
114        echo '<a href="?do=admin&amp;page=statistics&amp;opt=country&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;s='.$this->start.'">Countries</a>';
115        echo '</div></li>';
116
117        echo '</ul>';
118        echo '</div>';
119        echo '</div>';
120    }
121
122    /**
123     * Print the time selection menu
124     */
125    function html_timeselect(){
126        $now   = date('Y-m-d');
127        $yday  = date('Y-m-d',time()-(60*60*24));
128        $week  = date('Y-m-d',time()-(60*60*24*7));
129        $month = date('Y-m-d',time()-(60*60*24*30));
130
131        echo '<div class="plg_stats_timeselect">';
132        echo '<span>Select the timeframe:</span>';
133        echo '<ul>';
134
135        echo '<li>';
136        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$now.'&amp;t='.$now.'&amp;s='.$this->start.'">';
137        echo 'today';
138        echo '</a>';
139        echo '</li>';
140
141        echo '<li>';
142        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$yday.'&amp;t='.$yday.'&amp;s='.$this->start.'">';
143        echo 'yesterday';
144        echo '</a>';
145        echo '</li>';
146
147        echo '<li>';
148        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$week.'&amp;t='.$now.'&amp;s='.$this->start.'">';
149        echo 'last 7 days';
150        echo '</a>';
151        echo '</li>';
152
153        echo '<li>';
154        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$month.'&amp;t='.$now.'&amp;s='.$this->start.'">';
155        echo 'last 30 days';
156        echo '</a>';
157        echo '</li>';
158
159        echo '</ul>';
160
161
162        echo '<form action="" method="get">';
163        echo '<input type="hidden" name="do" value="admin" />';
164        echo '<input type="hidden" name="page" value="statistics" />';
165        echo '<input type="hidden" name="opt" value="'.$this->opt.'" />';
166        echo '<input type="hidden" name="s" value="'.$this->start.'" />';
167        echo '<input type="text" name="f" value="'.$this->from.'" class="edit" />';
168        echo '<input type="text" name="t" value="'.$this->to.'" class="edit" />';
169        echo '<input type="submit" value="go" class="button" />';
170        echo '</form>';
171
172        echo '</div>';
173    }
174
175
176    /**
177     * Print an introductionary screen
178     */
179    function html_dashboard(){
180        echo '<p>This page gives you a quick overview on what is happening in your Wiki. For detailed lists
181              choose a topic from the list.</p>';
182
183
184        echo '<div class="plg_stats_dashboard">';
185
186        // general info
187        echo '<div class="plg_stats_top">';
188        $result = $this->sql_aggregate($this->tlimit);
189        echo '<ul>';
190        echo '<li><span>'.$result['pageviews'].'</span> page views</li>';
191        echo '<li><span>'.$result['sessions'].'</span> visitors (sessions)</li>';
192        echo '<li><span>'.$result['users'].'</span> logged in users</li>';
193
194        echo '</ul>';
195        echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/img.php?img=trend&amp;f='.$this->from.'&amp;t='.$this->to.'" />';
196        echo '</div>';
197
198
199        // top pages today
200        echo '<div>';
201        echo '<h2>Most popular pages</h2>';
202        $result = $this->sql_pages($this->tlimit,$this->start,15);
203        $this->html_resulttable($result);
204        echo '</div>';
205
206        // top referer today
207        echo '<div>';
208        echo '<h2>Top incoming links</h2>';
209        $result = $this->sql_referer($this->tlimit,$this->start,15);
210        $this->html_resulttable($result);
211        echo '</div>';
212
213        // top countries today
214        echo '<div>';
215        echo '<h2>Visitor\'s top countries</h2>';
216        echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/img.php?img=country&amp;f='.$this->from.'&amp;t='.$this->to.'" />';
217//        $result = $this->sql_countries($this->tlimit,$this->start,15);
218//        $this->html_resulttable($result,array('','Countries','Count'));
219        echo '</div>';
220
221        echo '</div>';
222    }
223
224    function html_country(){
225        echo '<div class="plg_stats_full">';
226        echo '<h2>Visitor\'s Countries</h2>';
227        $result = $this->sql_countries($this->tlimit,$this->start,150);
228        $this->html_resulttable($result);
229        echo '</div>';
230    }
231
232    function html_page(){
233        echo '<div class="plg_stats_full">';
234        echo '<h2>Popular Pages</h2>';
235        $result = $this->sql_pages($this->tlimit,$this->start,150);
236        $this->html_resulttable($result);
237        echo '</div>';
238    }
239
240    function html_referer(){
241        echo '<div class="plg_stats_full">';
242        echo '<h2>Incoming Links</h2>';
243        $result = $this->sql_aggregate($this->tlimit);
244
245        $all    = $result['search']+$result['external']+$result['direct'];
246
247        printf("<p>Of all %d external visits, %d (%.1f%%) were bookmarked (direct) accesses,
248                %d (%.1f%%) came from search engines and %d (%.1f%%) were referred through
249                links from other pages.</p>",$all,$result['direct'],(100*$result['direct']/$all),
250                $result['search'],(100*$result['search']/$all),$result['external'],
251                (100*$result['external']/$all));
252
253        $result = $this->sql_referer($this->tlimit,$this->start,150);
254        $this->html_resulttable($result);
255        echo '</div>';
256    }
257
258
259
260    /**
261     * Display a result in a HTML table
262     */
263    function html_resulttable($result,$header=''){
264        echo '<table>';
265        if(is_array($header)){
266            echo '<tr>';
267            foreach($header as $h){
268                echo '<th>'.hsc($h).'</th>';
269            }
270            echo '</tr>';
271        }
272
273        foreach($result as $row){
274            echo '<tr>';
275            foreach($row as $k => $v){
276                echo '<td class="plg_stats_X'.$k.'">';
277                if($k == 'page'){
278                    echo '<a href="'.wl($v).'" class="wikilink1">';
279                    echo hsc($v);
280                    echo '</a>';
281                }elseif($k == 'url'){
282                    $url = hsc($v);
283                    if(strlen($url) > 45){
284                        $url = substr($url,0,30).' &hellip; '.substr($url,-15);
285                    }
286                    echo '<a href="'.$v.'" class="urlextern">';
287                    echo $url;
288                    echo '</a>';
289                }elseif($k == 'html'){
290                    echo $v;
291                }elseif($k == 'cflag'){
292                    echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/flags/'.hsc($v).'.png" alt="'.hsc($v).'" width="18" height="12"/>';
293                }else{
294                    echo hsc($v);
295                }
296                echo '</td>';
297            }
298            echo '</tr>';
299        }
300        echo '</table>';
301    }
302
303    /**
304     * Create an image
305     */
306    function img_build($img){
307        include(dirname(__FILE__).'/inc/AGC.class.php');
308
309        switch($img){
310            case 'country':
311                // build top countries + other
312                $result = $this->sql_countries($this->tlimit,$this->start,0);
313                $data = array();
314                $top = 0;
315                foreach($result as $row){
316                    if($top < 7){
317                        $data[$row['country']] = $row['cnt'];
318                    }else{
319                        $data['other'] += $row['cnt'];
320                    }
321                    $top++;
322                }
323                $pie = new AGC(300, 200);
324                $pie->setProp("showkey",true);
325                $pie->setProp("showval",false);
326                $pie->setProp("showgrid",false);
327                $pie->setProp("type","pie");
328                $pie->setProp("keyinfo",1);
329                $pie->setProp("keysize",8);
330                $pie->setProp("keywidspc",-50);
331                $pie->setProp("key",array_keys($data));
332                $pie->addBulkPoints(array_values($data));
333                @$pie->graph();
334                $pie->showGraph();
335                break;
336            case 'trend':
337                $hours  = ($this->from == $this->to);
338                $result = $this->sql_trend($this->tlimit,$hours);
339                $data1   = array();
340                $data2   = array();
341
342                $graph = new AGC(400, 150);
343                $graph->setProp("type","bar");
344                $graph->setProp("showgrid",false);
345                $graph->setProp("barwidth",.8);
346                $graph->setColor('color',0,'blue');
347                $graph->setColor('color',1,'red');
348
349                if($hours){
350                    //preset $hours
351                    for($i=0;$i<24;$i++){
352                        $data1[$i] = 0;
353                        $data2[$i] = 0;
354                        $graph->setProp("scale",array(' 0h','   4h','   8h','    12h','    16h','    20h','    24h'));
355                    }
356                }else{
357                    $graph->setProp("scale",array(next(array_keys($data1)),$this->to));
358                }
359
360                foreach($result as $row){
361                    $data1[$row['time']] = $row['pageviews'];
362                    $data2[$row['time']] = $row['sessions'];
363                }
364
365                foreach($data1 as $key => $val){
366                    $graph->addPoint($val,$key,0);
367                }
368                foreach($data2 as $key => $val){
369                    $graph->addPoint($val,$key,1);
370                }
371
372                @$graph->graph();
373                $graph->showGraph();
374
375            default:
376                $this->sendGIF();
377        }
378    }
379
380
381    /**
382     * Return some aggregated statistics
383     */
384    function sql_aggregate($tlimit){
385        $data = array();
386
387        $sql = "SELECT ref_type, COUNT(*) as cnt
388                  FROM ".$this->getConf('db_prefix')."access as A
389                 WHERE $tlimit
390                   AND ua_type = 'browser'
391              GROUP BY ref_type";
392        $result = $this->runSQL($sql);
393
394        foreach($result as $row){
395            if($row['ref_type'] == 'search')   $data['search']   = $row['cnt'];
396            if($row['ref_type'] == 'external') $data['external'] = $row['cnt'];
397            if($row['ref_type'] == 'internal') $data['internal'] = $row['cnt'];
398            if($row['ref_type'] == '')         $data['direct']   = $row['cnt'];
399        }
400
401        $sql = "SELECT COUNT(DISTINCT session) as sessions,
402                       COUNT(session) as views,
403                       COUNT(DISTINCT user) as users
404                  FROM ".$this->getConf('db_prefix')."access as A
405                 WHERE $tlimit
406                   AND ua_type = 'browser'";
407        $result = $this->runSQL($sql);
408
409        $data['users']     = $result[0]['users'] - 1; // subtract empty user
410        $data['sessions']  = $result[0]['sessions'];
411        $data['pageviews'] = $result[0]['views'];
412
413        $sql = "SELECT COUNT(id) as robots
414                  FROM ".$this->getConf('db_prefix')."access as A
415                 WHERE $tlimit
416                   AND ua_type = 'robot'";
417        $result = $this->runSQL($sql);
418        $data['robots'] = $result[0]['robots'];
419
420        return $data;
421    }
422
423    function sql_trend($tlimit,$hours=false){
424        if($hours){
425            $sql = "SELECT HOUR(dt) as time,
426                           COUNT(DISTINCT session) as sessions,
427                           COUNT(session) as pageviews
428                      FROM ".$this->getConf('db_prefix')."access as A
429                     WHERE $tlimit
430                       AND ua_type = 'browser'
431                  GROUP BY HOUR(dt)
432                  ORDER BY time";
433        }else{
434            $sql = "SELECT DATE(dt) as time,
435                           COUNT(DISTINCT session) as sessions,
436                           COUNT(session) as pageviews
437                      FROM ".$this->getConf('db_prefix')."access as A
438                     WHERE $tlimit
439                       AND ua_type = 'browser'
440                  GROUP BY DATE(dt)
441                  ORDER BY time";
442        }
443        return $this->runSQL($sql);
444    }
445
446    function sql_pages($tlimit,$start=0,$limit=20){
447        $sql = "SELECT COUNT(*) as cnt, page
448                  FROM ".$this->getConf('db_prefix')."access as A
449                 WHERE $tlimit
450                   AND ua_type = 'browser'
451              GROUP BY page
452              ORDER BY cnt DESC, page".
453              $this->sql_limit($start,$limit);
454        return $this->runSQL($sql);
455    }
456
457    function sql_referer($tlimit,$start=0,$limit=20){
458        $sql = "SELECT COUNT(*) as cnt, ref as url
459                  FROM ".$this->getConf('db_prefix')."access as A
460                 WHERE $tlimit
461                   AND ua_type = 'browser'
462                   AND ref_type = 'external'
463              GROUP BY ref_md5
464              ORDER BY cnt DESC, url".
465              $this->sql_limit($start,$limit);
466        return $this->runSQL($sql);
467    }
468
469    function sql_countries($tlimit,$start=0,$limit=20){
470        $sql = "SELECT COUNT(*) as cnt, B.code AS cflag, B.country
471                  FROM ".$this->getConf('db_prefix')."access as A,
472                       ".$this->getConf('db_prefix')."iplocation as B
473                 WHERE $tlimit
474                   AND A.ip = B.ip
475              GROUP BY B.country
476              ORDER BY cnt DESC, B.country".
477              $this->sql_limit($start,$limit);
478        return $this->runSQL($sql);
479    }
480
481    /**
482     * Builds a limit clause
483     */
484    function sql_limit($start,$limit){
485        $start = (int) $start;
486        $limit = (int) $limit;
487        if($limit){
488            return " LIMIT $start,$limit";
489        }elseif($start){
490            return " OFFSET $start";
491        }
492        return '';
493    }
494
495    /**
496     * Return a link to the DB, opening the connection if needed
497     */
498    function dbLink(){
499        // connect to DB if needed
500        if(!$this->dblink){
501            $this->dblink = mysql_connect($this->getConf('db_server'),
502                                          $this->getConf('db_user'),
503                                          $this->getConf('db_password'));
504            if(!$this->dblink){
505                msg('DB Error: connection failed',-1);
506                return null;
507            }
508            // set utf-8
509            if(!mysql_db_query($this->getConf('db_database'),'set names utf8',$this->dblink)){
510                msg('DB Error: could not set UTF-8 ('.mysql_error($this->dblink).')',-1);
511                return null;
512            }
513        }
514        return $this->dblink;
515    }
516
517    /**
518     * Simple function to run a DB query
519     */
520    function runSQL($sql_string) {
521        $link = $this->dbLink();
522
523        $result = mysql_db_query($this->conf['db_database'],$sql_string,$link);
524        if(!$result){
525            msg('DB Error: '.mysql_error($link).' '.hsc($sql_string),-1);
526            return null;
527        }
528
529        $resultarray = array();
530
531        //mysql_db_query returns 1 on a insert statement -> no need to ask for results
532        if ($result != 1) {
533            for($i=0; $i< mysql_num_rows($result); $i++) {
534                $temparray = mysql_fetch_assoc($result);
535                $resultarray[]=$temparray;
536            }
537            mysql_free_result($result);
538        }
539
540        if (mysql_insert_id($link)) {
541            $resultarray = mysql_insert_id($link); //give back ID on insert
542        }
543
544        return $resultarray;
545    }
546
547    /**
548     * Returns a short name for a User Agent and sets type, version and os info
549     */
550    function ua_info($ua,&$type,&$ver,&$os){
551        $ua = strtr($ua,' +','__');
552        $ua = strtolower($ua);
553
554        // common browsers
555        $regvermsie     = '/msie([+_ ]|)([\d\.]*)/i';
556        $regvernetscape = '/netscape.?\/([\d\.]*)/i';
557        $regverfirefox  = '/firefox\/([\d\.]*)/i';
558        $regversvn      = '/svn\/([\d\.]*)/i';
559        $regvermozilla  = '/mozilla(\/|)([\d\.]*)/i';
560        $regnotie       = '/webtv|omniweb|opera/i';
561        $regnotnetscape = '/gecko|compatible|opera|galeon|safari/i';
562
563        $name = '';
564        # IE ?
565        if(preg_match($regvermsie,$ua,$m) && !preg_match($regnotie,$ua)){
566            $type = 'browser';
567            $ver  = $m[2];
568            $name = 'msie';
569        }
570        # Firefox ?
571        elseif (preg_match($regverfirefox,$ua,$m)){
572            $type = 'browser';
573            $ver  = $m[1];
574            $name = 'firefox';
575        }
576        # Subversion ?
577        elseif (preg_match($regversvn,$ua,$m)){
578            $type = 'rcs';
579            $ver  = $m[1];
580            $name = 'svn';
581        }
582        # Netscape 6.x, 7.x ... ?
583        elseif (preg_match($regvernetscape,$ua,$m)){
584            $type = 'browser';
585            $ver  = $m[1];
586            $name = 'netscape';
587        }
588        # Netscape 3.x, 4.x ... ?
589        elseif(preg_match($regvermozilla,$ua,$m) && !preg_match($regnotnetscape,$ua)){
590            $type = 'browser';
591            $ver  = $m[2];
592            $name = 'netscape';
593        }else{
594            include(dirname(__FILE__).'/inc/browsers.php');
595            foreach($BrowsersSearchIDOrder as $regex){
596                if(preg_match('/'.$regex.'/',$ua)){
597                    // it's a browser!
598                    $type = 'browser';
599                    $name = strtolower($regex);
600                    break;
601                }
602            }
603        }
604
605        // check OS for browsers
606        if($type == 'browser'){
607            include(dirname(__FILE__).'/inc/operating_systems.php');
608            foreach($OSSearchIDOrder as $regex){
609                if(preg_match('/'.$regex.'/',$ua)){
610                    $os = $OSHashID[$regex];
611                    break;
612                }
613            }
614
615        }
616
617        // are we done now?
618        if($name) return $name;
619
620        include(dirname(__FILE__).'/inc/robots.php');
621        foreach($RobotsSearchIDOrder as $regex){
622            if(preg_match('/'.$regex.'/',$ua)){
623                    // it's a robot!
624                    $type = 'robot';
625                    return strtolower($regex);
626            }
627        }
628
629        // dunno
630        return '';
631    }
632
633    /**
634     *
635     * @fixme: put search engine queries in seperate table here
636     */
637    function log_search($referer,&$type){
638        $referer = strtr($referer,' +','__');
639        $referer = strtolower($referer);
640
641        include(dirname(__FILE__).'/inc/search_engines.php');
642
643        foreach($SearchEnginesSearchIDOrder as $regex){
644            if(preg_match('/'.$regex.'/',$referer)){
645                if(!$NotSearchEnginesKeys[$regex] ||
646                   !preg_match('/'.$NotSearchEnginesKeys[$regex].'/',$referer)){
647                    // it's a search engine!
648                    $type = 'search';
649                    break;
650                }
651            }
652        }
653        if($type != 'search') return; // we're done here
654
655        #fixme now do the keyword magic!
656    }
657
658    /**
659     * Resolve IP to country/city
660     */
661    function log_ip($ip){
662        // check if IP already known and up-to-date
663        $sql = "SELECT ip
664                  FROM ".$this->getConf('db_prefix')."iplocation
665                 WHERE ip ='".addslashes($ip)."'
666                   AND lastupd > DATE_SUB(CURDATE(),INTERVAL 30 DAY)";
667        $result = $this->runSQL($sql);
668        if($result[0]['ip']) return;
669
670        $http = new DokuHTTPClient();
671        $http->timeout = 10;
672        $data = $http->get('http://api.hostip.info/get_html.php?ip='.$ip);
673
674        if(preg_match('/^Country: (.*?) \((.*?)\)\nCity: (.*?)$/s',$data,$match)){
675            $country = addslashes(trim($match[1]));
676            $code    = addslashes(strtolower(trim($match[2])));
677            $city    = addslashes(trim($match[3]));
678            $host    = addslashes(gethostbyaddr($ip));
679            $ip      = addslashes($ip);
680
681            $sql = "REPLACE INTO ".$this->getConf('db_prefix')."iplocation
682                        SET ip = '$ip',
683                            country = '$country',
684                            code    = '$code',
685                            city    = '$city',
686                            host    = '$host'";
687            $this->runSQL($sql);
688        }
689    }
690
691    /**
692     * log a page access
693     *
694     * called from log.php
695     */
696    function log_access(){
697        if(!$_REQUEST['p']) return;
698
699        # FIXME check referer against blacklist and drop logging for bad boys
700
701        // handle referer
702        $referer = trim($_REQUEST['r']);
703        if($referer){
704            $ref     = addslashes($referer);
705            $ref_md5 = ($ref) ? md5($referer) : '';
706            if(strpos($referer,DOKU_URL) === 0){
707                $ref_type = 'internal';
708            }else{
709                $ref_type = 'external';
710                $this->log_search($referer,$ref_type);
711            }
712        }else{
713            $ref      = '';
714            $ref_md5  = '';
715            $ref_type = '';
716        }
717
718        // handle user agent
719        $agent   = trim($_SERVER['HTTP_USER_AGENT']);
720
721        $ua      = addslashes($agent);
722        $ua_type = '';
723        $ua_ver  = '';
724        $os      = '';
725        $ua_info = addslashes($this->ua_info($agent,$ua_type,$ua_ver,$os));
726
727        $page    = addslashes($_REQUEST['p']);
728        $ip      = addslashes($_SERVER['REMOTE_ADDR']);
729        $sx      = (int) $_REQUEST['sx'];
730        $sy      = (int) $_REQUEST['sy'];
731        $vx      = (int) $_REQUEST['vx'];
732        $vy      = (int) $_REQUEST['vy'];
733        $user    = addslashes($_SERVER['REMOTE_USER']);
734        $session = addslashes(session_id());
735
736        $sql  = "INSERT DELAYED INTO ".$this->getConf('db_prefix')."access
737                    SET page     = '$page',
738                        ip       = '$ip',
739                        ua       = '$ua',
740                        ua_info  = '$ua_info',
741                        ua_type  = '$ua_type',
742                        ua_ver   = '$ua_ver',
743                        os       = '$os',
744                        ref      = '$ref',
745                        ref_md5  = '$ref_md5',
746                        ref_type = '$ref_type',
747                        screen_x = '$sx',
748                        screen_y = '$sy',
749                        view_x   = '$vx',
750                        view_y   = '$vy',
751                        user     = '$user',
752                        session  = '$session'";
753        $ok = $this->runSQL($sql);
754        if(is_null($ok)){
755            global $MSG;
756            print_r($MSG);
757        }
758
759        // resolve the IP
760        $this->log_ip($_SERVER['REMOTE_ADDR']);
761    }
762
763    /**
764     * Just send a 1x1 pixel blank gif to the browser
765     *
766     * @called from log.php
767     *
768     * @author Andreas Gohr <andi@splitbrain.org>
769     * @author Harry Fuecks <fuecks@gmail.com>
770     */
771    function sendGIF(){
772        $img = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7');
773        header('Content-Type: image/gif');
774        header('Content-Length: '.strlen($img));
775        header('Connection: Close');
776        print $img;
777        flush();
778        // Browser should drop connection after this
779        // Thinks it's got the whole image
780    }
781
782}
783