xref: /plugin/statistics/admin.php (revision 87d5e44b252985d375db6eb3c6ebf1b7df736a84)
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
22    /**
23     * return some info
24     */
25    function getInfo(){
26        return confToHash(dirname(__FILE__).'/info.txt');
27    }
28
29    /**
30     * Access for managers allowed
31     */
32    function forAdminOnly(){
33        return false;
34    }
35
36    /**
37     * return sort order for position in admin menu
38     */
39    function getMenuSort() {
40        return 150;
41    }
42
43    /**
44     * handle user request
45     */
46    function handle() {
47    }
48
49    /**
50     * fixme build statistics here
51     */
52    function html() {
53        // fixme build a navigation menu in a TOC here
54
55        switch($_REQUEST['opt']){
56
57            default:
58                echo $this->locale_xhtml('intro');
59                echo $this->html_dashboard();
60        }
61    }
62
63    function html_timeselect(){
64        echo '<form>';
65
66        echo '</form>';
67    }
68
69
70    function html_dashboard(){
71
72        // top pages today
73        $sql = "SELECT page, COUNT(*) as cnt
74                  FROM ".$this->getConf('db_prefix')."access
75                 WHERE DATE(dt) = CURDATE()
76                   AND ua_type = 'browser'
77              GROUP BY page
78              ORDER BY cnt DESC, page
79                 LIMIT 20";
80        $result = $this->runSQL($sql);
81        $this->html_resulttable($result,array('Pages','Count'));
82
83        // top referer today
84        $sql = "SELECT ref as url, COUNT(*) as cnt
85                  FROM ".$this->getConf('db_prefix')."access
86                 WHERE DATE(dt) = CURDATE()
87                   AND ua_type = 'browser'
88                   AND ref_type = 'external'
89              GROUP BY ref_md5
90              ORDER BY cnt DESC, url
91                 LIMIT 20";
92        $result = $this->runSQL($sql);
93        $this->html_resulttable($result,array('Incoming Links','Count'));
94    }
95
96    /**
97     * Display a result in a HTML table
98     */
99    function html_resulttable($result,$header){
100        echo '<table>';
101        echo '<tr>';
102        foreach($header as $h){
103            echo '<th>'.hsc($h).'</th>';
104        }
105        echo '</tr>';
106
107        foreach($result as $row){
108            echo '<tr>';
109            foreach($row as $k => $v){
110                echo '<td class="stats_'.$k.'">';
111                if($k == 'page'){
112                    echo '<a href="'.wl($v).'" class="wikilink1">';
113                    echo hsc($v);
114                    echo '</a>';
115                }elseif($k == 'url'){
116                    echo '<a href="'.$v.'" class="urlextern">';
117                    echo hsc($v);
118                    echo '</a>';
119                }elseif($k == 'html'){
120                    echo $v;
121                }else{
122                    echo hsc($v);
123                }
124                echo '</td>';
125            }
126            echo '</tr>';
127        }
128        echo '</table>';
129    }
130
131
132    /**
133     * Return a link to the DB, opening the connection if needed
134     */
135    function dbLink(){
136        // connect to DB if needed
137        if(!$this->dblink){
138            $this->dblink = mysql_connect($this->getConf('db_server'),
139                                          $this->getConf('db_user'),
140                                          $this->getConf('db_password'));
141            if(!$this->dblink){
142                msg('DB Error: connection failed',-1);
143                return null;
144            }
145            // set utf-8
146            if(!mysql_db_query($this->getConf('db_database'),'set names utf8',$this->dblink)){
147                msg('DB Error: could not set UTF-8 ('.mysql_error($this->dblink).')',-1);
148                return null;
149            }
150        }
151        return $this->dblink;
152    }
153
154    /**
155     * Simple function to run a DB query
156     */
157    function runSQL($sql_string) {
158        $link = $this->dbLink();
159
160        $result = mysql_db_query($this->conf['db_database'],$sql_string,$link);
161        if(!$result){
162            msg('DB Error: '.mysql_error($link),-1);
163            return null;
164        }
165
166        $resultarray = array();
167
168        //mysql_db_query returns 1 on a insert statement -> no need to ask for results
169        if ($result != 1) {
170            for($i=0; $i< mysql_num_rows($result); $i++) {
171                $temparray = mysql_fetch_assoc($result);
172                $resultarray[]=$temparray;
173            }
174            mysql_free_result($result);
175        }
176
177        if (mysql_insert_id($link)) {
178            $resultarray = mysql_insert_id($link); //give back ID on insert
179        }
180
181        return $resultarray;
182    }
183
184    /**
185     * Returns a short name for a User Agent and sets type, version and os info
186     */
187    function ua_info($ua,&$type,&$ver,&$os){
188        $ua = strtr($ua,' +','__');
189        $ua = strtolower($ua);
190
191        // common browsers
192        $regvermsie     = '/msie([+_ ]|)([\d\.]*)/i';
193        $regvernetscape = '/netscape.?\/([\d\.]*)/i';
194        $regverfirefox  = '/firefox\/([\d\.]*)/i';
195        $regversvn      = '/svn\/([\d\.]*)/i';
196        $regvermozilla  = '/mozilla(\/|)([\d\.]*)/i';
197        $regnotie       = '/webtv|omniweb|opera/i';
198        $regnotnetscape = '/gecko|compatible|opera|galeon|safari/i';
199
200        $name = '';
201        # IE ?
202        if(preg_match($regvermsie,$ua,$m) && !preg_match($regnotie,$ua)){
203            $type = 'browser';
204            $ver  = $m[2];
205            $name = 'msie';
206        }
207        # Firefox ?
208        elseif (preg_match($regverfirefox,$ua,$m)){
209            $type = 'browser';
210            $ver  = $m[1];
211            $name = 'firefox';
212        }
213        # Subversion ?
214        elseif (preg_match($regversvn,$ua,$m)){
215            $type = 'rcs';
216            $ver  = $m[1];
217            $name = 'svn';
218        }
219        # Netscape 6.x, 7.x ... ?
220        elseif (preg_match($regvernetscape,$ua,$m)){
221            $type = 'browser';
222            $ver  = $m[1];
223            $name = 'netscape';
224        }
225        # Netscape 3.x, 4.x ... ?
226        elseif(preg_match($regvermozilla,$ua,$m) && !preg_match($regnotnetscape,$ua)){
227            $type = 'browser';
228            $ver  = $m[2];
229            $name = 'netscape';
230        }else{
231            include(dirname(__FILE__).'/inc/browsers.php');
232            foreach($BrowsersSearchIDOrder as $regex){
233                if(preg_match('/'.$regex.'/',$ua)){
234                    // it's a browser!
235                    $type = 'browser';
236                    $name = strtolower($regex);
237                    break;
238                }
239            }
240        }
241
242        // check OS for browsers
243        if($type == 'browser'){
244            include(dirname(__FILE__).'/inc/operating_systems.php');
245            foreach($OSSearchIDOrder as $regex){
246                if(preg_match('/'.$regex.'/',$ua)){
247                    $os = $OSHashID[$regex];
248                    break;
249                }
250            }
251
252        }
253
254        // are we done now?
255        if($name) return $name;
256
257        include(dirname(__FILE__).'/inc/robots.php');
258        foreach($RobotsSearchIDOrder as $regex){
259            if(preg_match('/'.$regex.'/',$ua)){
260                    // it's a robot!
261                    $type = 'robot';
262                    return strtolower($regex);
263            }
264        }
265
266        // dunno
267        return '';
268    }
269
270    /**
271     *
272     * @fixme: put search engine queries in seperate table here
273     */
274    function log_search($referer,&$type){
275        $referer = strtr($referer,' +','__');
276        $referer = strtolower($referer);
277
278        include(dirname(__FILE__).'/inc/search_engines.php');
279
280        foreach($SearchEnginesSearchIDOrder as $regex){
281            if(preg_match('/'.$regex.'/',$referer)){
282                if(!$NotSearchEnginesKeys[$regex] ||
283                   !preg_match('/'.$NotSearchEnginesKeys[$regex].'/',$referer)){
284                    // it's a search engine!
285                    $type = 'search';
286                    break;
287                }
288            }
289        }
290        if($type != 'search') return; // we're done here
291
292        #fixme now do the keyword magic!
293    }
294
295    /**
296     * Resolve IP to country/city
297     */
298    function log_ip($ip){
299        // check if IP already known and up-to-date
300        $sql = "SELECT ip
301                  FROM ".$this->getConf('db_prefix')."iplocation
302                 WHERE ip ='".addslashes($ip)."'
303                   AND lastupd > DATE_SUB(CURDATE(),INTERVAL 30 DAY)";
304        $result = $this->runSQL($sql);
305        if($result[0]['ip']) return;
306
307        $http = new DokuHTTPClient();
308        $http->timeout = 10;
309        $data = $http->get('http://api.hostip.info/get_html.php?ip='.$ip);
310
311        if(preg_match('/^Country: (.*?) \((.*?)\)\nCity: (.*?)$/s',$data,$match)){
312            $country = addslashes(trim($match[1]));
313            $code    = addslashes(strtolower(trim($match[2])));
314            $city    = addslashes(trim($match[3]));
315            $host    = addslashes(gethostbyaddr($ip));
316            $ip      = addslashes($ip);
317
318            $sql = "REPLACE INTO ".$this->getConf('db_prefix')."iplocation
319                        SET ip = '$ip',
320                            country = '$country',
321                            code    = '$code',
322                            city    = '$city',
323                            host    = '$host'";
324            $this->runSQL($sql);
325        }
326    }
327
328    /**
329     * log a page access
330     *
331     * called from log.php
332     */
333    function log_access(){
334        if(!$_REQUEST['p']) return;
335
336        # FIXME check referer against blacklist and drop logging for bad boys
337
338        // handle referer
339        $referer = trim($_REQUEST['r']);
340        if($referer){
341            $ref     = addslashes($referer);
342            $ref_md5 = ($ref) ? md5($referer) : '';
343            if(strpos($referer,DOKU_URL) === 0){
344                $ref_type = 'internal';
345            }else{
346                $ref_type = 'external';
347                $this->log_search($referer,$ref_type);
348            }
349        }else{
350            $ref      = '';
351            $ref_md5  = '';
352            $ref_type = '';
353        }
354
355        // handle user agent
356        $agent   = trim($_SERVER['HTTP_USER_AGENT']);
357
358        $ua      = addslashes($agent);
359        $ua_type = '';
360        $ua_ver  = '';
361        $os      = '';
362        $ua_info = addslashes($this->ua_info($agent,$ua_type,$ua_ver,$os));
363
364        $page    = addslashes($_REQUEST['p']);
365        $ip      = addslashes($_SERVER['REMOTE_ADDR']);
366        $sx      = (int) $_REQUEST['sx'];
367        $sy      = (int) $_REQUEST['sy'];
368        $vx      = (int) $_REQUEST['vx'];
369        $vy      = (int) $_REQUEST['vy'];
370        $user    = addslashes($_SERVER['REMOTE_USER']);
371        $session = addslashes(session_id());
372
373        $sql  = "INSERT DELAYED INTO ".$this->getConf('db_prefix')."access
374                    SET page     = '$page',
375                        ip       = '$ip',
376                        ua       = '$ua',
377                        ua_info  = '$ua_info',
378                        ua_type  = '$ua_type',
379                        ua_ver   = '$ua_ver',
380                        os       = '$os',
381                        ref      = '$ref',
382                        ref_md5  = '$ref_md5',
383                        ref_type = '$ref_type',
384                        screen_x = '$sx',
385                        screen_y = '$sy',
386                        view_x   = '$vx',
387                        view_y   = '$vy',
388                        user     = '$user',
389                        session  = '$session'";
390        $ok = $this->runSQL($sql);
391        if(is_null($ok)){
392            global $MSG;
393            print_r($MSG);
394        }
395
396        // resolve the IP
397        $this->log_ip($_SERVER['REMOTE_ADDR']);
398    }
399
400    /**
401     * Just send a 1x1 pixel blank gif to the browser
402     *
403     * @called from log.php
404     *
405     * @author Andreas Gohr <andi@splitbrain.org>
406     * @author Harry Fuecks <fuecks@gmail.com>
407     */
408    function sendGIF(){
409        $img = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7');
410        header('Content-Type: image/gif');
411        header('Content-Length: '.strlen($img));
412        header('Connection: Close');
413        print $img;
414        flush();
415        // Browser should drop connection after this
416        // Thinks it's got the whole image
417    }
418
419}
420