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