xref: /plugin/statistics/admin.php (revision 3c0acc14a7443f3b1e8e9d865c403b7c2162ffda)
11878f16fSAndreas Gohr<?php
21878f16fSAndreas Gohr/**
31878f16fSAndreas Gohr * statistics plugin
41878f16fSAndreas Gohr *
51878f16fSAndreas Gohr * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
61878f16fSAndreas Gohr * @author     Andreas Gohr <gohr@cosmocode.de>
71878f16fSAndreas Gohr */
81878f16fSAndreas Gohr
91878f16fSAndreas Gohr// must be run within Dokuwiki
101878f16fSAndreas Gohrif(!defined('DOKU_INC')) die();
111878f16fSAndreas Gohr
121878f16fSAndreas Gohrif(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
131878f16fSAndreas Gohrrequire_once(DOKU_PLUGIN.'admin.php');
141878f16fSAndreas Gohr
151878f16fSAndreas Gohr/**
161878f16fSAndreas Gohr * All DokuWiki plugins to extend the admin function
171878f16fSAndreas Gohr * need to inherit from this class
181878f16fSAndreas Gohr */
191878f16fSAndreas Gohrclass admin_plugin_statistics extends DokuWiki_Admin_Plugin {
201878f16fSAndreas Gohr    var $dblink = null;
21264f1744SAndreas Gohr    var $opt    = '';
22264f1744SAndreas Gohr    var $from   = '';
23264f1744SAndreas Gohr    var $to     = '';
2495eb68e6SAndreas Gohr    var $start  = '';
25264f1744SAndreas Gohr    var $tlimit = '';
261878f16fSAndreas Gohr
271878f16fSAndreas Gohr    /**
281878f16fSAndreas Gohr     * return some info
291878f16fSAndreas Gohr     */
301878f16fSAndreas Gohr    function getInfo(){
311878f16fSAndreas Gohr        return confToHash(dirname(__FILE__).'/info.txt');
321878f16fSAndreas Gohr    }
331878f16fSAndreas Gohr
341878f16fSAndreas Gohr    /**
351878f16fSAndreas Gohr     * Access for managers allowed
361878f16fSAndreas Gohr     */
371878f16fSAndreas Gohr    function forAdminOnly(){
381878f16fSAndreas Gohr        return false;
391878f16fSAndreas Gohr    }
401878f16fSAndreas Gohr
411878f16fSAndreas Gohr    /**
421878f16fSAndreas Gohr     * return sort order for position in admin menu
431878f16fSAndreas Gohr     */
441878f16fSAndreas Gohr    function getMenuSort() {
4514d99ec0SAndreas Gohr        return 150;
461878f16fSAndreas Gohr    }
471878f16fSAndreas Gohr
481878f16fSAndreas Gohr    /**
491878f16fSAndreas Gohr     * handle user request
501878f16fSAndreas Gohr     */
511878f16fSAndreas Gohr    function handle() {
52264f1744SAndreas Gohr        $this->opt = preg_replace('/[^a-z]+/','',$_REQUEST['opt']);
5395eb68e6SAndreas Gohr
5495eb68e6SAndreas Gohr        $this->start = (int) $_REQUEST['s'];
5595eb68e6SAndreas Gohr
56264f1744SAndreas Gohr        // fixme add better sanity checking here:
57264f1744SAndreas Gohr        $this->from = preg_replace('/[^\d\-]+/','',$_REQUEST['f']);
58264f1744SAndreas Gohr        $this->to = preg_replace('/[^\d\-]+/','',$_REQUEST['t']);
59264f1744SAndreas Gohr        if(!$this->from) $this->from = date('Y-m-d');
60264f1744SAndreas Gohr        if(!$this->to) $this->to     = date('Y-m-d');
61264f1744SAndreas Gohr
62264f1744SAndreas Gohr        //setup limit clause
63264f1744SAndreas Gohr        if($this->from != $this->to){
64264f1744SAndreas Gohr            $this->tlimit = "DATE(A.dt) >= DATE('".$this->from."') AND DATE(A.dt) <= DATE('".$this->to."')";
65264f1744SAndreas Gohr        }else{
66264f1744SAndreas Gohr            $this->tlimit = "DATE(A.dt) = DATE('".$this->from."')";
67264f1744SAndreas Gohr        }
681878f16fSAndreas Gohr    }
691878f16fSAndreas Gohr
701878f16fSAndreas Gohr    /**
7194171ff3SAndreas Gohr     * fixme build statistics here
721878f16fSAndreas Gohr     */
731878f16fSAndreas Gohr    function html() {
749da6395dSAndreas Gohr        $this->html_toc();
75264f1744SAndreas Gohr        echo '<h1>Access Statistics</h1>';
76264f1744SAndreas Gohr        $this->html_timeselect();
77264f1744SAndreas Gohr
78264f1744SAndreas Gohr        switch($this->opt){
799da6395dSAndreas Gohr            case 'country':
809da6395dSAndreas Gohr                $this->html_country();
819da6395dSAndreas Gohr                break;
829da6395dSAndreas Gohr            case 'page':
839da6395dSAndreas Gohr                $this->html_page();
849da6395dSAndreas Gohr                break;
8575fa767dSAndreas Gohr            case 'browser':
8675fa767dSAndreas Gohr                $this->html_browser();
8775fa767dSAndreas Gohr                break;
88bd4217d3SAndreas Gohr            case 'os':
89bd4217d3SAndreas Gohr                $this->html_os();
90bd4217d3SAndreas Gohr                break;
919da6395dSAndreas Gohr            case 'referer':
929da6395dSAndreas Gohr                $this->html_referer();
939da6395dSAndreas Gohr                break;
94e7a2f1e0SAndreas Gohr            case 'newreferer':
95e7a2f1e0SAndreas Gohr                $this->html_newreferer();
96e7a2f1e0SAndreas Gohr                break;
97c73e16f1SAndreas Gohr            case 'resolution':
98c73e16f1SAndreas Gohr                $this->html_resolution();
99c73e16f1SAndreas Gohr                break;
10014d99ec0SAndreas Gohr            default:
1019da6395dSAndreas Gohr                $this->html_dashboard();
10214d99ec0SAndreas Gohr        }
10314d99ec0SAndreas Gohr    }
10414d99ec0SAndreas Gohr
1059da6395dSAndreas Gohr    function html_toc(){
1069da6395dSAndreas Gohr        echo '<div class="toc">';
1079da6395dSAndreas Gohr        echo '<div class="tocheader toctoggle" id="toc__header">';
1089da6395dSAndreas Gohr        echo 'Detailed Statistics';
1099da6395dSAndreas Gohr        echo '</div>';
1109da6395dSAndreas Gohr        echo '<div id="toc__inside">';
1119da6395dSAndreas Gohr        echo '<ul class="toc">';
1129da6395dSAndreas Gohr
1139da6395dSAndreas Gohr        echo '<li><div class="li">';
1142507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt=&amp;f='.$this->from.'&amp;t='.$this->to.'">Dashboard</a>';
1159da6395dSAndreas Gohr        echo '</div></li>';
1169da6395dSAndreas Gohr
1179da6395dSAndreas Gohr        echo '<li><div class="li">';
1182507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt=page&amp;f='.$this->from.'&amp;t='.$this->to.'">Pages</a>';
1199da6395dSAndreas Gohr        echo '</div></li>';
1209da6395dSAndreas Gohr
1219da6395dSAndreas Gohr        echo '<li><div class="li">';
1222507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt=referer&amp;f='.$this->from.'&amp;t='.$this->to.'">Incoming Links</a>';
1239da6395dSAndreas Gohr        echo '</div></li>';
1249da6395dSAndreas Gohr
1259da6395dSAndreas Gohr        echo '<li><div class="li">';
1262507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt=newreferer&amp;f='.$this->from.'&amp;t='.$this->to.'">New Incoming Links</a>';
127e7a2f1e0SAndreas Gohr        echo '</div></li>';
128e7a2f1e0SAndreas Gohr
129e7a2f1e0SAndreas Gohr        echo '<li><div class="li">';
1302507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt=browser&amp;f='.$this->from.'&amp;t='.$this->to.'">Browsers</a>';
13175fa767dSAndreas Gohr        echo '</div></li>';
13275fa767dSAndreas Gohr
13375fa767dSAndreas Gohr        echo '<li><div class="li">';
1342507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt=os&amp;f='.$this->from.'&amp;t='.$this->to.'">Operating Systems</a>';
135bd4217d3SAndreas Gohr        echo '</div></li>';
136bd4217d3SAndreas Gohr
137bd4217d3SAndreas Gohr        echo '<li><div class="li">';
1382507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt=country&amp;f='.$this->from.'&amp;t='.$this->to.'">Countries</a>';
1399da6395dSAndreas Gohr        echo '</div></li>';
1409da6395dSAndreas Gohr
141c73e16f1SAndreas Gohr        echo '<li><div class="li">';
142c73e16f1SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt=resolution&amp;f='.$this->from.'&amp;t='.$this->to.'">Resolution</a>';
143c73e16f1SAndreas Gohr        echo '</div></li>';
144c73e16f1SAndreas Gohr
1459da6395dSAndreas Gohr        echo '</ul>';
1469da6395dSAndreas Gohr        echo '</div>';
1479da6395dSAndreas Gohr        echo '</div>';
1489da6395dSAndreas Gohr    }
1499da6395dSAndreas Gohr
1502507f8e0SAndreas Gohr    function html_pager($limit,$next){
1512507f8e0SAndreas Gohr        echo '<div class="plg_stats_pager">';
1522507f8e0SAndreas Gohr
1532507f8e0SAndreas Gohr        if($this->start > 0){
1542507f8e0SAndreas Gohr            $go = max($this->start - $limit, 0);
1552507f8e0SAndreas Gohr            echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;s='.$go.'" class="prev">previous page</a>';
1562507f8e0SAndreas Gohr        }
1572507f8e0SAndreas Gohr
1582507f8e0SAndreas Gohr        if($next){
1592507f8e0SAndreas Gohr            $go = $this->start + $limit;
1602507f8e0SAndreas Gohr            echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$this->from.'&amp;t='.$this->to.'&amp;s='.$go.'" class="next">next page</a>';
1612507f8e0SAndreas Gohr        }
1622507f8e0SAndreas Gohr        echo '</div>';
1632507f8e0SAndreas Gohr    }
1642507f8e0SAndreas Gohr
165264f1744SAndreas Gohr    /**
166264f1744SAndreas Gohr     * Print the time selection menu
167264f1744SAndreas Gohr     */
16814d99ec0SAndreas Gohr    function html_timeselect(){
169264f1744SAndreas Gohr        $now   = date('Y-m-d');
170264f1744SAndreas Gohr        $yday  = date('Y-m-d',time()-(60*60*24));
171264f1744SAndreas Gohr        $week  = date('Y-m-d',time()-(60*60*24*7));
172264f1744SAndreas Gohr        $month = date('Y-m-d',time()-(60*60*24*30));
17314d99ec0SAndreas Gohr
174264f1744SAndreas Gohr        echo '<div class="plg_stats_timeselect">';
175264f1744SAndreas Gohr        echo '<span>Select the timeframe:</span>';
176264f1744SAndreas Gohr        echo '<ul>';
177264f1744SAndreas Gohr
178264f1744SAndreas Gohr        echo '<li>';
1792507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$now.'&amp;t='.$now.'">';
180264f1744SAndreas Gohr        echo 'today';
181264f1744SAndreas Gohr        echo '</a>';
182264f1744SAndreas Gohr        echo '</li>';
183264f1744SAndreas Gohr
184264f1744SAndreas Gohr        echo '<li>';
1852507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$yday.'&amp;t='.$yday.'">';
186264f1744SAndreas Gohr        echo 'yesterday';
187264f1744SAndreas Gohr        echo '</a>';
188264f1744SAndreas Gohr        echo '</li>';
189264f1744SAndreas Gohr
190264f1744SAndreas Gohr        echo '<li>';
1912507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$week.'&amp;t='.$now.'">';
192264f1744SAndreas Gohr        echo 'last 7 days';
193264f1744SAndreas Gohr        echo '</a>';
194264f1744SAndreas Gohr        echo '</li>';
195264f1744SAndreas Gohr
196264f1744SAndreas Gohr        echo '<li>';
1972507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt='.$this->opt.'&amp;f='.$month.'&amp;t='.$now.'">';
198264f1744SAndreas Gohr        echo 'last 30 days';
199264f1744SAndreas Gohr        echo '</a>';
200264f1744SAndreas Gohr        echo '</li>';
201264f1744SAndreas Gohr
202264f1744SAndreas Gohr        echo '</ul>';
203264f1744SAndreas Gohr
204264f1744SAndreas Gohr
205264f1744SAndreas Gohr        echo '<form action="" method="get">';
206264f1744SAndreas Gohr        echo '<input type="hidden" name="do" value="admin" />';
207264f1744SAndreas Gohr        echo '<input type="hidden" name="page" value="statistics" />';
208264f1744SAndreas Gohr        echo '<input type="hidden" name="opt" value="'.$this->opt.'" />';
209264f1744SAndreas Gohr        echo '<input type="text" name="f" value="'.$this->from.'" class="edit" />';
210264f1744SAndreas Gohr        echo '<input type="text" name="t" value="'.$this->to.'" class="edit" />';
211264f1744SAndreas Gohr        echo '<input type="submit" value="go" class="button" />';
21214d99ec0SAndreas Gohr        echo '</form>';
213264f1744SAndreas Gohr
214264f1744SAndreas Gohr        echo '</div>';
21514d99ec0SAndreas Gohr    }
21614d99ec0SAndreas Gohr
21714d99ec0SAndreas Gohr
218f5f32cbfSAndreas Gohr    /**
219f5f32cbfSAndreas Gohr     * Print an introductionary screen
220f5f32cbfSAndreas Gohr     */
22114d99ec0SAndreas Gohr    function html_dashboard(){
2222812a751SAndreas Gohr        echo '<p>This page gives you a quick overview on what is happening in your Wiki. For detailed lists
2232812a751SAndreas Gohr              choose a topic from the list.</p>';
2242812a751SAndreas Gohr
2252812a751SAndreas Gohr
226264f1744SAndreas Gohr        echo '<div class="plg_stats_dashboard">';
227264f1744SAndreas Gohr
2282812a751SAndreas Gohr        // general info
2292812a751SAndreas Gohr        echo '<div class="plg_stats_top">';
2302812a751SAndreas Gohr        $result = $this->sql_aggregate($this->tlimit);
2312812a751SAndreas Gohr        echo '<ul>';
2322812a751SAndreas Gohr        echo '<li><span>'.$result['pageviews'].'</span> page views</li>';
233*3c0acc14SAndreas Gohr        echo '<li><span>'.$result['sessions'].'</span> visits (sessions)</li>';
234*3c0acc14SAndreas Gohr        echo '<li><span>'.$result['visitors'].'</span> unique visitors</li>';
2352812a751SAndreas Gohr        echo '<li><span>'.$result['users'].'</span> logged in users</li>';
2362812a751SAndreas Gohr
2372812a751SAndreas Gohr        echo '</ul>';
2382812a751SAndreas Gohr        echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/img.php?img=trend&amp;f='.$this->from.'&amp;t='.$this->to.'" />';
2392812a751SAndreas Gohr        echo '</div>';
2402812a751SAndreas Gohr
24114d99ec0SAndreas Gohr
24287d5e44bSAndreas Gohr        // top pages today
243264f1744SAndreas Gohr        echo '<div>';
244264f1744SAndreas Gohr        echo '<h2>Most popular pages</h2>';
24595eb68e6SAndreas Gohr        $result = $this->sql_pages($this->tlimit,$this->start,15);
2462812a751SAndreas Gohr        $this->html_resulttable($result);
2472507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt=page&amp;f='.$this->from.'&amp;t='.$this->to.'" class="more">more</a>';
248264f1744SAndreas Gohr        echo '</div>';
24987d5e44bSAndreas Gohr
25087d5e44bSAndreas Gohr        // top referer today
251264f1744SAndreas Gohr        echo '<div>';
252e7a2f1e0SAndreas Gohr        echo '<h2>Newest incoming links</h2>';
253e7a2f1e0SAndreas Gohr        $result = $this->sql_newreferer($this->tlimit,$this->start,15);
2542812a751SAndreas Gohr        $this->html_resulttable($result);
2552507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt=newreferer&amp;f='.$this->from.'&amp;t='.$this->to.'" class="more">more</a>';
256264f1744SAndreas Gohr        echo '</div>';
25754f6c432SAndreas Gohr
25854f6c432SAndreas Gohr        // top countries today
259264f1744SAndreas Gohr        echo '<div>';
260264f1744SAndreas Gohr        echo '<h2>Visitor\'s top countries</h2>';
26195eb68e6SAndreas Gohr        echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/img.php?img=country&amp;f='.$this->from.'&amp;t='.$this->to.'" />';
2622507f8e0SAndreas Gohr        echo '<a href="?do=admin&amp;page=statistics&amp;opt=country&amp;f='.$this->from.'&amp;t='.$this->to.'" class="more">more</a>';
263264f1744SAndreas Gohr        echo '</div>';
264264f1744SAndreas Gohr
265264f1744SAndreas Gohr        echo '</div>';
26614d99ec0SAndreas Gohr    }
26714d99ec0SAndreas Gohr
2689da6395dSAndreas Gohr    function html_country(){
2699da6395dSAndreas Gohr        echo '<div class="plg_stats_full">';
2709da6395dSAndreas Gohr        echo '<h2>Visitor\'s Countries</h2>';
271bd4217d3SAndreas Gohr        echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/img.php?img=country&amp;f='.$this->from.'&amp;t='.$this->to.'" />';
2729da6395dSAndreas Gohr        $result = $this->sql_countries($this->tlimit,$this->start,150);
2732507f8e0SAndreas Gohr        $this->html_resulttable($result,'',150);
2749da6395dSAndreas Gohr        echo '</div>';
2759da6395dSAndreas Gohr    }
2769da6395dSAndreas Gohr
2779da6395dSAndreas Gohr    function html_page(){
2789da6395dSAndreas Gohr        echo '<div class="plg_stats_full">';
2799da6395dSAndreas Gohr        echo '<h2>Popular Pages</h2>';
2809da6395dSAndreas Gohr        $result = $this->sql_pages($this->tlimit,$this->start,150);
2812507f8e0SAndreas Gohr        $this->html_resulttable($result,'',150);
2829da6395dSAndreas Gohr        echo '</div>';
2839da6395dSAndreas Gohr    }
2849da6395dSAndreas Gohr
28575fa767dSAndreas Gohr    function html_browser(){
28675fa767dSAndreas Gohr        echo '<div class="plg_stats_full">';
28775fa767dSAndreas Gohr        echo '<h2>Browser Shootout</h2>';
28875fa767dSAndreas Gohr        echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/img.php?img=browser&amp;f='.$this->from.'&amp;t='.$this->to.'" />';
28975fa767dSAndreas Gohr        $result = $this->sql_browsers($this->tlimit,$this->start,150,true);
2902507f8e0SAndreas Gohr        $this->html_resulttable($result,'',150);
29175fa767dSAndreas Gohr        echo '</div>';
29275fa767dSAndreas Gohr    }
29375fa767dSAndreas Gohr
294bd4217d3SAndreas Gohr    function html_os(){
295bd4217d3SAndreas Gohr        echo '<div class="plg_stats_full">';
296bd4217d3SAndreas Gohr        echo '<h2>Operating Systems</h2>';
297bd4217d3SAndreas Gohr        $result = $this->sql_os($this->tlimit,$this->start,150,true);
2982507f8e0SAndreas Gohr        $this->html_resulttable($result,'',150);
299bd4217d3SAndreas Gohr        echo '</div>';
300bd4217d3SAndreas Gohr    }
301bd4217d3SAndreas Gohr
3029da6395dSAndreas Gohr    function html_referer(){
3039da6395dSAndreas Gohr        echo '<div class="plg_stats_full">';
3049da6395dSAndreas Gohr        echo '<h2>Incoming Links</h2>';
3052812a751SAndreas Gohr        $result = $this->sql_aggregate($this->tlimit);
3062812a751SAndreas Gohr
3072812a751SAndreas Gohr        $all    = $result['search']+$result['external']+$result['direct'];
3082812a751SAndreas Gohr
30994023548SAndreas Gohr        if($all){
3102812a751SAndreas Gohr            printf("<p>Of all %d external visits, %d (%.1f%%) were bookmarked (direct) accesses,
3112812a751SAndreas Gohr                    %d (%.1f%%) came from search engines and %d (%.1f%%) were referred through
3122812a751SAndreas Gohr                    links from other pages.</p>",$all,$result['direct'],(100*$result['direct']/$all),
3132812a751SAndreas Gohr                    $result['search'],(100*$result['search']/$all),$result['external'],
3142812a751SAndreas Gohr                    (100*$result['external']/$all));
31594023548SAndreas Gohr        }
3162812a751SAndreas Gohr
3179da6395dSAndreas Gohr        $result = $this->sql_referer($this->tlimit,$this->start,150);
3182507f8e0SAndreas Gohr        $this->html_resulttable($result,'',150);
3199da6395dSAndreas Gohr        echo '</div>';
3209da6395dSAndreas Gohr    }
3219da6395dSAndreas Gohr
322e7a2f1e0SAndreas Gohr    function html_newreferer(){
323e7a2f1e0SAndreas Gohr        echo '<div class="plg_stats_full">';
324e7a2f1e0SAndreas Gohr        echo '<h2>New Incoming Links</h2>';
325e7a2f1e0SAndreas Gohr        echo '<p>The following incoming links where first logged in the selected time frame,
326e7a2f1e0SAndreas Gohr              and have never been seen before.</p>';
327e7a2f1e0SAndreas Gohr
328e7a2f1e0SAndreas Gohr        $result = $this->sql_newreferer($this->tlimit,$this->start,150);
3292507f8e0SAndreas Gohr        $this->html_resulttable($result,'',150);
330e7a2f1e0SAndreas Gohr        echo '</div>';
331e7a2f1e0SAndreas Gohr    }
332e7a2f1e0SAndreas Gohr
333c73e16f1SAndreas Gohr    function html_resolution(){
334c73e16f1SAndreas Gohr        echo '<div class="plg_stats_full">';
335c73e16f1SAndreas Gohr        echo '<h2>Resolution</h2>';
336c73e16f1SAndreas Gohr        $result = $this->sql_resolution($this->tlimit,$this->start,150);
337c73e16f1SAndreas Gohr        $this->html_resulttable($result,'',150);
338c73e16f1SAndreas Gohr
339c73e16f1SAndreas Gohr        echo '<p>While the data above gives you some info about the resolution your visitors use, it does not tell you
340c73e16f1SAndreas Gohr              much about about the real size of their browser windows. The graphic below shows the size distribution of
341c73e16f1SAndreas Gohr              the view port (document area) of your visitor\'s browsers. Please note that this data can not be logged
342c73e16f1SAndreas Gohr              in all browsers. Because users may resize their browser window while browsing your site the statistics may
343c73e16f1SAndreas Gohr              be flawed. Take it with a grain of salt.</p>';
344c73e16f1SAndreas Gohr
345c73e16f1SAndreas Gohr        echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/img.php?img=view&amp;f='.$this->from.'&amp;t='.$this->to.'" />';
346c73e16f1SAndreas Gohr        echo '</div>';
347c73e16f1SAndreas Gohr    }
3489da6395dSAndreas Gohr
3499da6395dSAndreas Gohr
35014d99ec0SAndreas Gohr    /**
35114d99ec0SAndreas Gohr     * Display a result in a HTML table
35214d99ec0SAndreas Gohr     */
3532507f8e0SAndreas Gohr    function html_resulttable($result,$header='',$pager=0){
35414d99ec0SAndreas Gohr        echo '<table>';
3552812a751SAndreas Gohr        if(is_array($header)){
35614d99ec0SAndreas Gohr            echo '<tr>';
35714d99ec0SAndreas Gohr            foreach($header as $h){
35814d99ec0SAndreas Gohr                echo '<th>'.hsc($h).'</th>';
35914d99ec0SAndreas Gohr            }
36014d99ec0SAndreas Gohr            echo '</tr>';
3612812a751SAndreas Gohr        }
36214d99ec0SAndreas Gohr
3632507f8e0SAndreas Gohr        $count = 0;
36414d99ec0SAndreas Gohr        foreach($result as $row){
36514d99ec0SAndreas Gohr            echo '<tr>';
36614d99ec0SAndreas Gohr            foreach($row as $k => $v){
3672812a751SAndreas Gohr                echo '<td class="plg_stats_X'.$k.'">';
36814d99ec0SAndreas Gohr                if($k == 'page'){
36914d99ec0SAndreas Gohr                    echo '<a href="'.wl($v).'" class="wikilink1">';
37014d99ec0SAndreas Gohr                    echo hsc($v);
37114d99ec0SAndreas Gohr                    echo '</a>';
37214d99ec0SAndreas Gohr                }elseif($k == 'url'){
37354f6c432SAndreas Gohr                    $url = hsc($v);
37483b63546SAndreas Gohr                    $url = preg_replace('/^https?:\/\/(www\.)?/','',$url);
3752812a751SAndreas Gohr                    if(strlen($url) > 45){
3762812a751SAndreas Gohr                        $url = substr($url,0,30).' &hellip; '.substr($url,-15);
37754f6c432SAndreas Gohr                    }
37814d99ec0SAndreas Gohr                    echo '<a href="'.$v.'" class="urlextern">';
37954f6c432SAndreas Gohr                    echo $url;
38014d99ec0SAndreas Gohr                    echo '</a>';
38175fa767dSAndreas Gohr                }elseif($k == 'browser'){
38275fa767dSAndreas Gohr                    include_once(dirname(__FILE__).'/inc/browsers.php');
38375fa767dSAndreas Gohr                    echo $BrowsersHashIDLib[$v];
38475fa767dSAndreas Gohr                }elseif($k == 'bflag'){
38575fa767dSAndreas Gohr                    include_once(dirname(__FILE__).'/inc/browsers.php');
38675fa767dSAndreas Gohr                    echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/ico/browser/'.$BrowsersHashIcon[$v].'.png" alt="'.hsc($v).'" />';
387bd4217d3SAndreas Gohr                }elseif($k == 'os'){
388bd4217d3SAndreas Gohr                    if(empty($v)){
389bd4217d3SAndreas Gohr                        echo 'unknown';
390bd4217d3SAndreas Gohr                    }else{
391bd4217d3SAndreas Gohr                        include_once(dirname(__FILE__).'/inc/operating_systems.php');
392bd4217d3SAndreas Gohr                        echo $OSHashLib[$v];
393bd4217d3SAndreas Gohr                    }
394bd4217d3SAndreas Gohr                }elseif($k == 'osflag'){
395bd4217d3SAndreas Gohr                    echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/ico/os/'.hsc($v).'.png" alt="'.hsc($v).'" />';
39675fa767dSAndreas Gohr                }elseif($k == 'cflag'){
39775fa767dSAndreas Gohr                    echo '<img src="'.DOKU_BASE.'lib/plugins/statistics/ico/flags/'.hsc($v).'.png" alt="'.hsc($v).'" width="18" height="12" />';
39814d99ec0SAndreas Gohr                }elseif($k == 'html'){
39914d99ec0SAndreas Gohr                    echo $v;
40014d99ec0SAndreas Gohr                }else{
40114d99ec0SAndreas Gohr                    echo hsc($v);
40214d99ec0SAndreas Gohr                }
40314d99ec0SAndreas Gohr                echo '</td>';
40414d99ec0SAndreas Gohr            }
40514d99ec0SAndreas Gohr            echo '</tr>';
4062507f8e0SAndreas Gohr
4072507f8e0SAndreas Gohr            if($pager && ($count == $pager)) break;
4082507f8e0SAndreas Gohr            $count++;
40914d99ec0SAndreas Gohr        }
41014d99ec0SAndreas Gohr        echo '</table>';
4112507f8e0SAndreas Gohr
4122507f8e0SAndreas Gohr        if($pager) $this->html_pager($pager,count($result) > $pager);
4131878f16fSAndreas Gohr    }
4141878f16fSAndreas Gohr
41595eb68e6SAndreas Gohr    /**
41695eb68e6SAndreas Gohr     * Create an image
41795eb68e6SAndreas Gohr     */
41895eb68e6SAndreas Gohr    function img_build($img){
41995eb68e6SAndreas Gohr        include(dirname(__FILE__).'/inc/AGC.class.php');
42095eb68e6SAndreas Gohr
42195eb68e6SAndreas Gohr        switch($img){
42295eb68e6SAndreas Gohr            case 'country':
42395eb68e6SAndreas Gohr                // build top countries + other
42495eb68e6SAndreas Gohr                $result = $this->sql_countries($this->tlimit,$this->start,0);
42595eb68e6SAndreas Gohr                $data = array();
42695eb68e6SAndreas Gohr                $top = 0;
42795eb68e6SAndreas Gohr                foreach($result as $row){
42895eb68e6SAndreas Gohr                    if($top < 7){
42995eb68e6SAndreas Gohr                        $data[$row['country']] = $row['cnt'];
43095eb68e6SAndreas Gohr                    }else{
43195eb68e6SAndreas Gohr                        $data['other'] += $row['cnt'];
43295eb68e6SAndreas Gohr                    }
43395eb68e6SAndreas Gohr                    $top++;
43495eb68e6SAndreas Gohr                }
43595eb68e6SAndreas Gohr                $pie = new AGC(300, 200);
43695eb68e6SAndreas Gohr                $pie->setProp("showkey",true);
43795eb68e6SAndreas Gohr                $pie->setProp("showval",false);
43895eb68e6SAndreas Gohr                $pie->setProp("showgrid",false);
43995eb68e6SAndreas Gohr                $pie->setProp("type","pie");
44095eb68e6SAndreas Gohr                $pie->setProp("keyinfo",1);
44195eb68e6SAndreas Gohr                $pie->setProp("keysize",8);
44295eb68e6SAndreas Gohr                $pie->setProp("keywidspc",-50);
44395eb68e6SAndreas Gohr                $pie->setProp("key",array_keys($data));
44495eb68e6SAndreas Gohr                $pie->addBulkPoints(array_values($data));
44595eb68e6SAndreas Gohr                @$pie->graph();
44695eb68e6SAndreas Gohr                $pie->showGraph();
44795eb68e6SAndreas Gohr                break;
44875fa767dSAndreas Gohr            case 'browser':
44975fa767dSAndreas Gohr                // build top browsers + other
45075fa767dSAndreas Gohr                include_once(dirname(__FILE__).'/inc/browsers.php');
45175fa767dSAndreas Gohr
45275fa767dSAndreas Gohr                $result = $this->sql_browsers($this->tlimit,$this->start,0,false);
45375fa767dSAndreas Gohr                $data = array();
45475fa767dSAndreas Gohr                $top = 0;
45575fa767dSAndreas Gohr                foreach($result as $row){
45675fa767dSAndreas Gohr                    if($top < 5){
45775fa767dSAndreas Gohr                        $data[strip_tags($BrowsersHashIDLib[$row['ua_info']])] = $row['cnt'];
45875fa767dSAndreas Gohr                    }else{
45975fa767dSAndreas Gohr                        $data['other'] += $row['cnt'];
46075fa767dSAndreas Gohr                    }
46175fa767dSAndreas Gohr                    $top++;
46275fa767dSAndreas Gohr                }
46375fa767dSAndreas Gohr                $pie = new AGC(300, 200);
46475fa767dSAndreas Gohr                $pie->setProp("showkey",true);
46575fa767dSAndreas Gohr                $pie->setProp("showval",false);
46675fa767dSAndreas Gohr                $pie->setProp("showgrid",false);
46775fa767dSAndreas Gohr                $pie->setProp("type","pie");
46875fa767dSAndreas Gohr                $pie->setProp("keyinfo",1);
46975fa767dSAndreas Gohr                $pie->setProp("keysize",8);
47075fa767dSAndreas Gohr                $pie->setProp("keywidspc",-50);
47175fa767dSAndreas Gohr                $pie->setProp("key",array_keys($data));
47275fa767dSAndreas Gohr                $pie->addBulkPoints(array_values($data));
47375fa767dSAndreas Gohr                @$pie->graph();
47475fa767dSAndreas Gohr                $pie->showGraph();
47575fa767dSAndreas Gohr                break;
476c73e16f1SAndreas Gohr            case 'view':
477c73e16f1SAndreas Gohr
478c73e16f1SAndreas Gohr                $graph = new AGC(400, 200);
479c73e16f1SAndreas Gohr                $graph->setColor('color',0,'blue');
480c73e16f1SAndreas Gohr                $graph->setColor('color',1,'red');
481c73e16f1SAndreas Gohr                $graph->setProp("showkey",true);
482c73e16f1SAndreas Gohr                $graph->setProp("key",'view port width',0);
483c73e16f1SAndreas Gohr                $graph->setProp("key",'view port height',1);
484c73e16f1SAndreas Gohr
485c73e16f1SAndreas Gohr                $result = $this->sql_viewport($this->tlimit,0,0,true);
486c73e16f1SAndreas Gohr                foreach($result as $row){
487c73e16f1SAndreas Gohr                    $graph->addPoint($row['cnt'],$row['res_x'],0);
488c73e16f1SAndreas Gohr                }
489c73e16f1SAndreas Gohr
490c73e16f1SAndreas Gohr                $result = $this->sql_viewport($this->tlimit,0,0,false);
491c73e16f1SAndreas Gohr                foreach($result as $row){
492c73e16f1SAndreas Gohr                    $graph->addPoint($row['cnt'],$row['res_y'],1);
493c73e16f1SAndreas Gohr                }
494c73e16f1SAndreas Gohr
495c73e16f1SAndreas Gohr                @$graph->graph();
496c73e16f1SAndreas Gohr                $graph->showGraph();
497c73e16f1SAndreas Gohr
498c73e16f1SAndreas Gohr                break;
4992812a751SAndreas Gohr            case 'trend':
5002812a751SAndreas Gohr                $hours  = ($this->from == $this->to);
5012812a751SAndreas Gohr                $result = $this->sql_trend($this->tlimit,$hours);
5022812a751SAndreas Gohr                $data1   = array();
5032812a751SAndreas Gohr                $data2   = array();
5042812a751SAndreas Gohr
5052812a751SAndreas Gohr                $graph = new AGC(400, 150);
5062812a751SAndreas Gohr                $graph->setProp("type","bar");
5072812a751SAndreas Gohr                $graph->setProp("showgrid",false);
5082812a751SAndreas Gohr                $graph->setProp("barwidth",.8);
50975fa767dSAndreas Gohr
5102812a751SAndreas Gohr                $graph->setColor('color',0,'blue');
5112812a751SAndreas Gohr                $graph->setColor('color',1,'red');
512*3c0acc14SAndreas Gohr                $graph->setColor('color',2,'yellow');
5132812a751SAndreas Gohr
5142812a751SAndreas Gohr                if($hours){
5152812a751SAndreas Gohr                    //preset $hours
5162812a751SAndreas Gohr                    for($i=0;$i<24;$i++){
5172812a751SAndreas Gohr                        $data1[$i] = 0;
5182812a751SAndreas Gohr                        $data2[$i] = 0;
519*3c0acc14SAndreas Gohr                        $data3[$i] = 0;
5202812a751SAndreas Gohr                        $graph->setProp("scale",array(' 0h','   4h','   8h','    12h','    16h','    20h','    24h'));
5212812a751SAndreas Gohr                    }
5222812a751SAndreas Gohr                }else{
5232812a751SAndreas Gohr                    $graph->setProp("scale",array(next(array_keys($data1)),$this->to));
5242812a751SAndreas Gohr                }
5252812a751SAndreas Gohr
5262812a751SAndreas Gohr                foreach($result as $row){
5272812a751SAndreas Gohr                    $data1[$row['time']] = $row['pageviews'];
5282812a751SAndreas Gohr                    $data2[$row['time']] = $row['sessions'];
529*3c0acc14SAndreas Gohr                    $data3[$row['time']] = $row['visitors'];
5302812a751SAndreas Gohr                }
5312812a751SAndreas Gohr
5322812a751SAndreas Gohr                foreach($data1 as $key => $val){
5332812a751SAndreas Gohr                    $graph->addPoint($val,$key,0);
5342812a751SAndreas Gohr                }
5352812a751SAndreas Gohr                foreach($data2 as $key => $val){
5362812a751SAndreas Gohr                    $graph->addPoint($val,$key,1);
5372812a751SAndreas Gohr                }
538*3c0acc14SAndreas Gohr                foreach($data3 as $key => $val){
539*3c0acc14SAndreas Gohr                    $graph->addPoint($val,$key,2);
540*3c0acc14SAndreas Gohr                }
5412812a751SAndreas Gohr
5422812a751SAndreas Gohr                @$graph->graph();
5432812a751SAndreas Gohr                $graph->showGraph();
5442812a751SAndreas Gohr
54595eb68e6SAndreas Gohr            default:
54695eb68e6SAndreas Gohr                $this->sendGIF();
54795eb68e6SAndreas Gohr        }
54895eb68e6SAndreas Gohr    }
54995eb68e6SAndreas Gohr
55095eb68e6SAndreas Gohr
5512812a751SAndreas Gohr    /**
5522812a751SAndreas Gohr     * Return some aggregated statistics
5532812a751SAndreas Gohr     */
5542812a751SAndreas Gohr    function sql_aggregate($tlimit){
5552812a751SAndreas Gohr        $data = array();
5562812a751SAndreas Gohr
5572812a751SAndreas Gohr        $sql = "SELECT ref_type, COUNT(*) as cnt
5582812a751SAndreas Gohr                  FROM ".$this->getConf('db_prefix')."access as A
5592812a751SAndreas Gohr                 WHERE $tlimit
5602812a751SAndreas Gohr                   AND ua_type = 'browser'
5612812a751SAndreas Gohr              GROUP BY ref_type";
5622812a751SAndreas Gohr        $result = $this->runSQL($sql);
5632812a751SAndreas Gohr
5642812a751SAndreas Gohr        foreach($result as $row){
5652812a751SAndreas Gohr            if($row['ref_type'] == 'search')   $data['search']   = $row['cnt'];
5662812a751SAndreas Gohr            if($row['ref_type'] == 'external') $data['external'] = $row['cnt'];
5672812a751SAndreas Gohr            if($row['ref_type'] == 'internal') $data['internal'] = $row['cnt'];
5682812a751SAndreas Gohr            if($row['ref_type'] == '')         $data['direct']   = $row['cnt'];
5692812a751SAndreas Gohr        }
5702812a751SAndreas Gohr
5712812a751SAndreas Gohr        $sql = "SELECT COUNT(DISTINCT session) as sessions,
5722812a751SAndreas Gohr                       COUNT(session) as views,
573*3c0acc14SAndreas Gohr                       COUNT(DISTINCT user) as users,
574*3c0acc14SAndreas Gohr                       COUNT(DISTINCT uid) as visitors
5752812a751SAndreas Gohr                  FROM ".$this->getConf('db_prefix')."access as A
5762812a751SAndreas Gohr                 WHERE $tlimit
5772812a751SAndreas Gohr                   AND ua_type = 'browser'";
5782812a751SAndreas Gohr        $result = $this->runSQL($sql);
5792812a751SAndreas Gohr
58075fa767dSAndreas Gohr        $data['users']     = max($result[0]['users'] - 1,0); // subtract empty user
5812812a751SAndreas Gohr        $data['sessions']  = $result[0]['sessions'];
5822812a751SAndreas Gohr        $data['pageviews'] = $result[0]['views'];
583*3c0acc14SAndreas Gohr        $data['visitors']  = $result[0]['visitors'];
5842812a751SAndreas Gohr
5852812a751SAndreas Gohr        $sql = "SELECT COUNT(id) as robots
5862812a751SAndreas Gohr                  FROM ".$this->getConf('db_prefix')."access as A
5872812a751SAndreas Gohr                 WHERE $tlimit
5882812a751SAndreas Gohr                   AND ua_type = 'robot'";
5892812a751SAndreas Gohr        $result = $this->runSQL($sql);
5902812a751SAndreas Gohr        $data['robots'] = $result[0]['robots'];
5912812a751SAndreas Gohr
5922812a751SAndreas Gohr        return $data;
5932812a751SAndreas Gohr    }
5942812a751SAndreas Gohr
595bd4217d3SAndreas Gohr    /**
596bd4217d3SAndreas Gohr     * standard statistics follow, only accesses made by browsers are counted
597bd4217d3SAndreas Gohr     * for general stats like browser or OS only visitors not pageviews are counted
598bd4217d3SAndreas Gohr     */
5992812a751SAndreas Gohr    function sql_trend($tlimit,$hours=false){
6002812a751SAndreas Gohr        if($hours){
6012812a751SAndreas Gohr            $sql = "SELECT HOUR(dt) as time,
6022812a751SAndreas Gohr                           COUNT(DISTINCT session) as sessions,
603*3c0acc14SAndreas Gohr                           COUNT(session) as pageviews,
604*3c0acc14SAndreas Gohr                           COUNT(DISTINCT uid) as visitors
6052812a751SAndreas Gohr                      FROM ".$this->getConf('db_prefix')."access as A
6062812a751SAndreas Gohr                     WHERE $tlimit
6072812a751SAndreas Gohr                       AND ua_type = 'browser'
6082812a751SAndreas Gohr                  GROUP BY HOUR(dt)
6092812a751SAndreas Gohr                  ORDER BY time";
6102812a751SAndreas Gohr        }else{
6112812a751SAndreas Gohr            $sql = "SELECT DATE(dt) as time,
6122812a751SAndreas Gohr                           COUNT(DISTINCT session) as sessions,
613*3c0acc14SAndreas Gohr                           COUNT(session) as pageviews,
614*3c0acc14SAndreas Gohr                            COUNT(DISTINCT uid) as visitors
6152812a751SAndreas Gohr                      FROM ".$this->getConf('db_prefix')."access as A
6162812a751SAndreas Gohr                     WHERE $tlimit
6172812a751SAndreas Gohr                       AND ua_type = 'browser'
6182812a751SAndreas Gohr                  GROUP BY DATE(dt)
6192812a751SAndreas Gohr                  ORDER BY time";
6202812a751SAndreas Gohr        }
6212812a751SAndreas Gohr        return $this->runSQL($sql);
6222812a751SAndreas Gohr    }
6232812a751SAndreas Gohr
62495eb68e6SAndreas Gohr    function sql_pages($tlimit,$start=0,$limit=20){
6252812a751SAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, page
62695eb68e6SAndreas Gohr                  FROM ".$this->getConf('db_prefix')."access as A
62795eb68e6SAndreas Gohr                 WHERE $tlimit
62895eb68e6SAndreas Gohr                   AND ua_type = 'browser'
62995eb68e6SAndreas Gohr              GROUP BY page
63095eb68e6SAndreas Gohr              ORDER BY cnt DESC, page".
63195eb68e6SAndreas Gohr              $this->sql_limit($start,$limit);
63295eb68e6SAndreas Gohr        return $this->runSQL($sql);
63395eb68e6SAndreas Gohr    }
63495eb68e6SAndreas Gohr
63595eb68e6SAndreas Gohr    function sql_referer($tlimit,$start=0,$limit=20){
6362812a751SAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, ref as url
63795eb68e6SAndreas Gohr                  FROM ".$this->getConf('db_prefix')."access as A
63895eb68e6SAndreas Gohr                 WHERE $tlimit
63995eb68e6SAndreas Gohr                   AND ua_type = 'browser'
64095eb68e6SAndreas Gohr                   AND ref_type = 'external'
64195eb68e6SAndreas Gohr              GROUP BY ref_md5
64295eb68e6SAndreas Gohr              ORDER BY cnt DESC, url".
64395eb68e6SAndreas Gohr              $this->sql_limit($start,$limit);
64495eb68e6SAndreas Gohr        return $this->runSQL($sql);
64595eb68e6SAndreas Gohr    }
64695eb68e6SAndreas Gohr
647e7a2f1e0SAndreas Gohr    function sql_newreferer($tlimit,$start=0,$limit=20){
648e7a2f1e0SAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, ref as url
649e7a2f1e0SAndreas Gohr                  FROM ".$this->getConf('db_prefix')."access as A
650e7a2f1e0SAndreas Gohr                 WHERE ua_type = 'browser'
651e7a2f1e0SAndreas Gohr                   AND ref_type = 'external'
652e7a2f1e0SAndreas Gohr              GROUP BY ref_md5
653e7a2f1e0SAndreas Gohr                HAVING DATE(MIN(dt)) >= DATE('".$this->from."')
654e7a2f1e0SAndreas Gohr                   AND DATE(MIN(dt)) <= DATE('".$this->to."')
655e7a2f1e0SAndreas Gohr              ORDER BY cnt DESC, url".
656e7a2f1e0SAndreas Gohr              $this->sql_limit($start,$limit);
657e7a2f1e0SAndreas Gohr        return $this->runSQL($sql);
658e7a2f1e0SAndreas Gohr    }
659e7a2f1e0SAndreas Gohr
66095eb68e6SAndreas Gohr    function sql_countries($tlimit,$start=0,$limit=20){
661bd4217d3SAndreas Gohr        $sql = "SELECT COUNT(DISTINCT session) as cnt, B.code AS cflag, B.country
66295eb68e6SAndreas Gohr                  FROM ".$this->getConf('db_prefix')."access as A,
66395eb68e6SAndreas Gohr                       ".$this->getConf('db_prefix')."iplocation as B
66495eb68e6SAndreas Gohr                 WHERE $tlimit
66595eb68e6SAndreas Gohr                   AND A.ip = B.ip
66695eb68e6SAndreas Gohr              GROUP BY B.country
66795eb68e6SAndreas Gohr              ORDER BY cnt DESC, B.country".
66895eb68e6SAndreas Gohr              $this->sql_limit($start,$limit);
66995eb68e6SAndreas Gohr        return $this->runSQL($sql);
67095eb68e6SAndreas Gohr    }
67195eb68e6SAndreas Gohr
67275fa767dSAndreas Gohr    function sql_browsers($tlimit,$start=0,$limit=20,$ext=true){
67375fa767dSAndreas Gohr        if($ext){
67475fa767dSAndreas Gohr            $sel = 'ua_info as bflag, ua_info as browser, ua_ver';
67575fa767dSAndreas Gohr            $grp = 'ua_info, ua_ver';
67675fa767dSAndreas Gohr        }else{
67775fa767dSAndreas Gohr            $grp = 'ua_info';
67875fa767dSAndreas Gohr            $sel = 'ua_info';
67975fa767dSAndreas Gohr        }
68075fa767dSAndreas Gohr
681bd4217d3SAndreas Gohr        $sql = "SELECT COUNT(DISTINCT session) as cnt, $sel
68275fa767dSAndreas Gohr                  FROM ".$this->getConf('db_prefix')."access as A
68375fa767dSAndreas Gohr                 WHERE $tlimit
68475fa767dSAndreas Gohr                   AND ua_type = 'browser'
68575fa767dSAndreas Gohr              GROUP BY $grp
68675fa767dSAndreas Gohr              ORDER BY cnt DESC, ua_info".
68775fa767dSAndreas Gohr              $this->sql_limit($start,$limit);
68875fa767dSAndreas Gohr        return $this->runSQL($sql);
68975fa767dSAndreas Gohr    }
69075fa767dSAndreas Gohr
691bd4217d3SAndreas Gohr    function sql_os($tlimit,$start=0,$limit=20){
692bd4217d3SAndreas Gohr        $sql = "SELECT COUNT(DISTINCT session) as cnt, os as osflag, os
693bd4217d3SAndreas Gohr                  FROM ".$this->getConf('db_prefix')."access as A
694bd4217d3SAndreas Gohr                 WHERE $tlimit
695bd4217d3SAndreas Gohr                   AND ua_type = 'browser'
696bd4217d3SAndreas Gohr              GROUP BY os
697bd4217d3SAndreas Gohr              ORDER BY cnt DESC, os".
698bd4217d3SAndreas Gohr              $this->sql_limit($start,$limit);
699bd4217d3SAndreas Gohr        return $this->runSQL($sql);
700bd4217d3SAndreas Gohr    }
701bd4217d3SAndreas Gohr
702c73e16f1SAndreas Gohr    function sql_resolution($tlimit,$start=0,$limit=20){
703c73e16f1SAndreas Gohr        $sql = "SELECT COUNT(DISTINCT session) as cnt, CONCAT(screen_x,'x',screen_y) as res
704c73e16f1SAndreas Gohr                  FROM ".$this->getConf('db_prefix')."access as A
705c73e16f1SAndreas Gohr                 WHERE $tlimit
706c73e16f1SAndreas Gohr                   AND ua_type  = 'browser'
707c73e16f1SAndreas Gohr                   AND screen_x != 0
708c73e16f1SAndreas Gohr              GROUP BY screen_x, screen_y
709c73e16f1SAndreas Gohr              ORDER BY cnt DESC, screen_x".
710c73e16f1SAndreas Gohr              $this->sql_limit($start,$limit);
711c73e16f1SAndreas Gohr        return $this->runSQL($sql);
712c73e16f1SAndreas Gohr    }
713c73e16f1SAndreas Gohr
714c73e16f1SAndreas Gohr    function sql_viewport($tlimit,$start=0,$limit=20,$x=true){
715c73e16f1SAndreas Gohr        if($x){
716c73e16f1SAndreas Gohr            $col = 'view_x';
717c73e16f1SAndreas Gohr            $res = 'res_x';
718c73e16f1SAndreas Gohr        }else{
719c73e16f1SAndreas Gohr            $col = 'view_y';
720c73e16f1SAndreas Gohr            $res = 'res_y';
721c73e16f1SAndreas Gohr        }
722c73e16f1SAndreas Gohr
723c73e16f1SAndreas Gohr        $sql = "SELECT COUNT(*) as cnt,
724c73e16f1SAndreas Gohr                       ROUND($col/10)*10 as $res
725c73e16f1SAndreas Gohr                  FROM ".$this->getConf('db_prefix')."access as A
726c73e16f1SAndreas Gohr                 WHERE $tlimit
727c73e16f1SAndreas Gohr                   AND ua_type  = 'browser'
728c73e16f1SAndreas Gohr                   AND $col != 0
729c73e16f1SAndreas Gohr              GROUP BY $res
730c73e16f1SAndreas Gohr              ORDER BY cnt DESC, $res".
731c73e16f1SAndreas Gohr              $this->sql_limit($start,$limit);
732c73e16f1SAndreas Gohr        return $this->runSQL($sql);
733c73e16f1SAndreas Gohr    }
734c73e16f1SAndreas Gohr
73575fa767dSAndreas Gohr
73695eb68e6SAndreas Gohr    /**
73795eb68e6SAndreas Gohr     * Builds a limit clause
73895eb68e6SAndreas Gohr     */
73995eb68e6SAndreas Gohr    function sql_limit($start,$limit){
74095eb68e6SAndreas Gohr        $start = (int) $start;
74195eb68e6SAndreas Gohr        $limit = (int) $limit;
74295eb68e6SAndreas Gohr        if($limit){
7432507f8e0SAndreas Gohr            $limit += 1;
74495eb68e6SAndreas Gohr            return " LIMIT $start,$limit";
74595eb68e6SAndreas Gohr        }elseif($start){
74695eb68e6SAndreas Gohr            return " OFFSET $start";
74795eb68e6SAndreas Gohr        }
74895eb68e6SAndreas Gohr        return '';
74995eb68e6SAndreas Gohr    }
7501878f16fSAndreas Gohr
7511878f16fSAndreas Gohr    /**
75214d99ec0SAndreas Gohr     * Return a link to the DB, opening the connection if needed
7531878f16fSAndreas Gohr     */
75414d99ec0SAndreas Gohr    function dbLink(){
7551878f16fSAndreas Gohr        // connect to DB if needed
7561878f16fSAndreas Gohr        if(!$this->dblink){
7571878f16fSAndreas Gohr            $this->dblink = mysql_connect($this->getConf('db_server'),
7581878f16fSAndreas Gohr                                          $this->getConf('db_user'),
7591878f16fSAndreas Gohr                                          $this->getConf('db_password'));
7601878f16fSAndreas Gohr            if(!$this->dblink){
7611878f16fSAndreas Gohr                msg('DB Error: connection failed',-1);
7621878f16fSAndreas Gohr                return null;
7631878f16fSAndreas Gohr            }
7641878f16fSAndreas Gohr            // set utf-8
7651878f16fSAndreas Gohr            if(!mysql_db_query($this->getConf('db_database'),'set names utf8',$this->dblink)){
7661878f16fSAndreas Gohr                msg('DB Error: could not set UTF-8 ('.mysql_error($this->dblink).')',-1);
7671878f16fSAndreas Gohr                return null;
7681878f16fSAndreas Gohr            }
7691878f16fSAndreas Gohr        }
77014d99ec0SAndreas Gohr        return $this->dblink;
77114d99ec0SAndreas Gohr    }
7721878f16fSAndreas Gohr
77314d99ec0SAndreas Gohr    /**
77414d99ec0SAndreas Gohr     * Simple function to run a DB query
77514d99ec0SAndreas Gohr     */
77614d99ec0SAndreas Gohr    function runSQL($sql_string) {
77714d99ec0SAndreas Gohr        $link = $this->dbLink();
77814d99ec0SAndreas Gohr
77914d99ec0SAndreas Gohr        $result = mysql_db_query($this->conf['db_database'],$sql_string,$link);
78094171ff3SAndreas Gohr        if(!$result){
7812812a751SAndreas Gohr            msg('DB Error: '.mysql_error($link).' '.hsc($sql_string),-1);
7821878f16fSAndreas Gohr            return null;
7831878f16fSAndreas Gohr        }
7841878f16fSAndreas Gohr
7851878f16fSAndreas Gohr        $resultarray = array();
7861878f16fSAndreas Gohr
7871878f16fSAndreas Gohr        //mysql_db_query returns 1 on a insert statement -> no need to ask for results
7881878f16fSAndreas Gohr        if ($result != 1) {
7891878f16fSAndreas Gohr            for($i=0; $i< mysql_num_rows($result); $i++) {
7901878f16fSAndreas Gohr                $temparray = mysql_fetch_assoc($result);
7911878f16fSAndreas Gohr                $resultarray[]=$temparray;
7921878f16fSAndreas Gohr            }
7931878f16fSAndreas Gohr            mysql_free_result($result);
7941878f16fSAndreas Gohr        }
7951878f16fSAndreas Gohr
79614d99ec0SAndreas Gohr        if (mysql_insert_id($link)) {
79714d99ec0SAndreas Gohr            $resultarray = mysql_insert_id($link); //give back ID on insert
7981878f16fSAndreas Gohr        }
7991878f16fSAndreas Gohr
8001878f16fSAndreas Gohr        return $resultarray;
8011878f16fSAndreas Gohr    }
8021878f16fSAndreas Gohr
8031878f16fSAndreas Gohr    /**
80414d99ec0SAndreas Gohr     * Returns a short name for a User Agent and sets type, version and os info
8051878f16fSAndreas Gohr     */
80614d99ec0SAndreas Gohr    function ua_info($ua,&$type,&$ver,&$os){
80714d99ec0SAndreas Gohr        $ua = strtr($ua,' +','__');
80814d99ec0SAndreas Gohr        $ua = strtolower($ua);
80914d99ec0SAndreas Gohr
81014d99ec0SAndreas Gohr        // common browsers
81114d99ec0SAndreas Gohr        $regvermsie     = '/msie([+_ ]|)([\d\.]*)/i';
81214d99ec0SAndreas Gohr        $regvernetscape = '/netscape.?\/([\d\.]*)/i';
81314d99ec0SAndreas Gohr        $regverfirefox  = '/firefox\/([\d\.]*)/i';
81414d99ec0SAndreas Gohr        $regversvn      = '/svn\/([\d\.]*)/i';
81514d99ec0SAndreas Gohr        $regvermozilla  = '/mozilla(\/|)([\d\.]*)/i';
81614d99ec0SAndreas Gohr        $regnotie       = '/webtv|omniweb|opera/i';
81714d99ec0SAndreas Gohr        $regnotnetscape = '/gecko|compatible|opera|galeon|safari/i';
81814d99ec0SAndreas Gohr
81914d99ec0SAndreas Gohr        $name = '';
82014d99ec0SAndreas Gohr        # IE ?
82114d99ec0SAndreas Gohr        if(preg_match($regvermsie,$ua,$m) && !preg_match($regnotie,$ua)){
82214d99ec0SAndreas Gohr            $type = 'browser';
82314d99ec0SAndreas Gohr            $ver  = $m[2];
82414d99ec0SAndreas Gohr            $name = 'msie';
82514d99ec0SAndreas Gohr        }
82614d99ec0SAndreas Gohr        # Firefox ?
82714d99ec0SAndreas Gohr        elseif (preg_match($regverfirefox,$ua,$m)){
82814d99ec0SAndreas Gohr            $type = 'browser';
82914d99ec0SAndreas Gohr            $ver  = $m[1];
83014d99ec0SAndreas Gohr            $name = 'firefox';
83114d99ec0SAndreas Gohr        }
83214d99ec0SAndreas Gohr        # Subversion ?
83314d99ec0SAndreas Gohr        elseif (preg_match($regversvn,$ua,$m)){
83414d99ec0SAndreas Gohr            $type = 'rcs';
83514d99ec0SAndreas Gohr            $ver  = $m[1];
83614d99ec0SAndreas Gohr            $name = 'svn';
83714d99ec0SAndreas Gohr        }
83814d99ec0SAndreas Gohr        # Netscape 6.x, 7.x ... ?
83914d99ec0SAndreas Gohr        elseif (preg_match($regvernetscape,$ua,$m)){
84014d99ec0SAndreas Gohr            $type = 'browser';
84114d99ec0SAndreas Gohr            $ver  = $m[1];
84214d99ec0SAndreas Gohr            $name = 'netscape';
84314d99ec0SAndreas Gohr        }
84414d99ec0SAndreas Gohr        # Netscape 3.x, 4.x ... ?
84514d99ec0SAndreas Gohr        elseif(preg_match($regvermozilla,$ua,$m) && !preg_match($regnotnetscape,$ua)){
84614d99ec0SAndreas Gohr            $type = 'browser';
84714d99ec0SAndreas Gohr            $ver  = $m[2];
84814d99ec0SAndreas Gohr            $name = 'netscape';
84914d99ec0SAndreas Gohr        }else{
85014d99ec0SAndreas Gohr            include(dirname(__FILE__).'/inc/browsers.php');
85114d99ec0SAndreas Gohr            foreach($BrowsersSearchIDOrder as $regex){
85214d99ec0SAndreas Gohr                if(preg_match('/'.$regex.'/',$ua)){
85314d99ec0SAndreas Gohr                    // it's a browser!
85414d99ec0SAndreas Gohr                    $type = 'browser';
85514d99ec0SAndreas Gohr                    $name = strtolower($regex);
85614d99ec0SAndreas Gohr                    break;
85714d99ec0SAndreas Gohr                }
85814d99ec0SAndreas Gohr            }
85914d99ec0SAndreas Gohr        }
86014d99ec0SAndreas Gohr
86175fa767dSAndreas Gohr        // check versions for Safari and Opera
86275fa767dSAndreas Gohr        if($name == 'safari'){
86375fa767dSAndreas Gohr            if(preg_match('/safari\/([\d\.]*)/i',$ua,$match)){
86475fa767dSAndreas Gohr                $ver = $BrowsersSafariBuildToVersionHash[$match[1]];
86575fa767dSAndreas Gohr            }
86675fa767dSAndreas Gohr        }elseif($name == 'opera'){
86775fa767dSAndreas Gohr            if(preg_match('/opera[\/ ]([\d\.]*)/i',$ua,$match)){
86875fa767dSAndreas Gohr                $ver = $match[1];
86975fa767dSAndreas Gohr            }
87075fa767dSAndreas Gohr        }
87175fa767dSAndreas Gohr
87275fa767dSAndreas Gohr
87314d99ec0SAndreas Gohr        // check OS for browsers
87414d99ec0SAndreas Gohr        if($type == 'browser'){
87514d99ec0SAndreas Gohr            include(dirname(__FILE__).'/inc/operating_systems.php');
87614d99ec0SAndreas Gohr            foreach($OSSearchIDOrder as $regex){
87714d99ec0SAndreas Gohr                if(preg_match('/'.$regex.'/',$ua)){
87814d99ec0SAndreas Gohr                    $os = $OSHashID[$regex];
87914d99ec0SAndreas Gohr                    break;
88014d99ec0SAndreas Gohr                }
88114d99ec0SAndreas Gohr            }
88214d99ec0SAndreas Gohr
88314d99ec0SAndreas Gohr        }
88414d99ec0SAndreas Gohr
88514d99ec0SAndreas Gohr        // are we done now?
88614d99ec0SAndreas Gohr        if($name) return $name;
88714d99ec0SAndreas Gohr
88814d99ec0SAndreas Gohr        include(dirname(__FILE__).'/inc/robots.php');
88914d99ec0SAndreas Gohr        foreach($RobotsSearchIDOrder as $regex){
89014d99ec0SAndreas Gohr            if(preg_match('/'.$regex.'/',$ua)){
89114d99ec0SAndreas Gohr                    // it's a robot!
89214d99ec0SAndreas Gohr                    $type = 'robot';
89314d99ec0SAndreas Gohr                    return strtolower($regex);
89414d99ec0SAndreas Gohr            }
89514d99ec0SAndreas Gohr        }
89614d99ec0SAndreas Gohr
89714d99ec0SAndreas Gohr        // dunno
8981878f16fSAndreas Gohr        return '';
8991878f16fSAndreas Gohr    }
9001878f16fSAndreas Gohr
9011878f16fSAndreas Gohr    /**
90214d99ec0SAndreas Gohr     *
90314d99ec0SAndreas Gohr     * @fixme: put search engine queries in seperate table here
90414d99ec0SAndreas Gohr     */
90514d99ec0SAndreas Gohr    function log_search($referer,&$type){
90614d99ec0SAndreas Gohr        $referer = strtr($referer,' +','__');
90714d99ec0SAndreas Gohr        $referer = strtolower($referer);
90814d99ec0SAndreas Gohr
90914d99ec0SAndreas Gohr        include(dirname(__FILE__).'/inc/search_engines.php');
91014d99ec0SAndreas Gohr
91114d99ec0SAndreas Gohr        foreach($SearchEnginesSearchIDOrder as $regex){
91214d99ec0SAndreas Gohr            if(preg_match('/'.$regex.'/',$referer)){
91314d99ec0SAndreas Gohr                if(!$NotSearchEnginesKeys[$regex] ||
91414d99ec0SAndreas Gohr                   !preg_match('/'.$NotSearchEnginesKeys[$regex].'/',$referer)){
91514d99ec0SAndreas Gohr                    // it's a search engine!
91614d99ec0SAndreas Gohr                    $type = 'search';
91714d99ec0SAndreas Gohr                    break;
91814d99ec0SAndreas Gohr                }
91914d99ec0SAndreas Gohr            }
92014d99ec0SAndreas Gohr        }
92114d99ec0SAndreas Gohr        if($type != 'search') return; // we're done here
92214d99ec0SAndreas Gohr
92314d99ec0SAndreas Gohr        #fixme now do the keyword magic!
92414d99ec0SAndreas Gohr    }
92514d99ec0SAndreas Gohr
92614d99ec0SAndreas Gohr    /**
92714d99ec0SAndreas Gohr     * Resolve IP to country/city
92814d99ec0SAndreas Gohr     */
92914d99ec0SAndreas Gohr    function log_ip($ip){
93014d99ec0SAndreas Gohr        // check if IP already known and up-to-date
93114d99ec0SAndreas Gohr        $sql = "SELECT ip
93214d99ec0SAndreas Gohr                  FROM ".$this->getConf('db_prefix')."iplocation
93314d99ec0SAndreas Gohr                 WHERE ip ='".addslashes($ip)."'
93414d99ec0SAndreas Gohr                   AND lastupd > DATE_SUB(CURDATE(),INTERVAL 30 DAY)";
93514d99ec0SAndreas Gohr        $result = $this->runSQL($sql);
93614d99ec0SAndreas Gohr        if($result[0]['ip']) return;
93714d99ec0SAndreas Gohr
93814d99ec0SAndreas Gohr        $http = new DokuHTTPClient();
93914d99ec0SAndreas Gohr        $http->timeout = 10;
94014d99ec0SAndreas Gohr        $data = $http->get('http://api.hostip.info/get_html.php?ip='.$ip);
94114d99ec0SAndreas Gohr
94214d99ec0SAndreas Gohr        if(preg_match('/^Country: (.*?) \((.*?)\)\nCity: (.*?)$/s',$data,$match)){
94314d99ec0SAndreas Gohr            $country = addslashes(trim($match[1]));
94414d99ec0SAndreas Gohr            $code    = addslashes(strtolower(trim($match[2])));
94514d99ec0SAndreas Gohr            $city    = addslashes(trim($match[3]));
94614d99ec0SAndreas Gohr            $host    = addslashes(gethostbyaddr($ip));
94714d99ec0SAndreas Gohr            $ip      = addslashes($ip);
94814d99ec0SAndreas Gohr
94914d99ec0SAndreas Gohr            $sql = "REPLACE INTO ".$this->getConf('db_prefix')."iplocation
95014d99ec0SAndreas Gohr                        SET ip = '$ip',
95114d99ec0SAndreas Gohr                            country = '$country',
95214d99ec0SAndreas Gohr                            code    = '$code',
95314d99ec0SAndreas Gohr                            city    = '$city',
95414d99ec0SAndreas Gohr                            host    = '$host'";
95514d99ec0SAndreas Gohr            $this->runSQL($sql);
95614d99ec0SAndreas Gohr        }
95714d99ec0SAndreas Gohr    }
95814d99ec0SAndreas Gohr
95914d99ec0SAndreas Gohr    /**
9601878f16fSAndreas Gohr     * log a page access
9611878f16fSAndreas Gohr     *
9621878f16fSAndreas Gohr     * called from log.php
9631878f16fSAndreas Gohr     */
9641878f16fSAndreas Gohr    function log_access(){
96594171ff3SAndreas Gohr        if(!$_REQUEST['p']) return;
96694171ff3SAndreas Gohr
96714d99ec0SAndreas Gohr        # FIXME check referer against blacklist and drop logging for bad boys
96814d99ec0SAndreas Gohr
96914d99ec0SAndreas Gohr        // handle referer
97014d99ec0SAndreas Gohr        $referer = trim($_REQUEST['r']);
97114d99ec0SAndreas Gohr        if($referer){
97214d99ec0SAndreas Gohr            $ref     = addslashes($referer);
97314d99ec0SAndreas Gohr            $ref_md5 = ($ref) ? md5($referer) : '';
97414d99ec0SAndreas Gohr            if(strpos($referer,DOKU_URL) === 0){
97514d99ec0SAndreas Gohr                $ref_type = 'internal';
97614d99ec0SAndreas Gohr            }else{
97714d99ec0SAndreas Gohr                $ref_type = 'external';
97814d99ec0SAndreas Gohr                $this->log_search($referer,$ref_type);
97914d99ec0SAndreas Gohr            }
98014d99ec0SAndreas Gohr        }else{
98114d99ec0SAndreas Gohr            $ref      = '';
98214d99ec0SAndreas Gohr            $ref_md5  = '';
98314d99ec0SAndreas Gohr            $ref_type = '';
98414d99ec0SAndreas Gohr        }
98514d99ec0SAndreas Gohr
98614d99ec0SAndreas Gohr        // handle user agent
98714d99ec0SAndreas Gohr        $agent   = trim($_SERVER['HTTP_USER_AGENT']);
98814d99ec0SAndreas Gohr
98914d99ec0SAndreas Gohr        $ua      = addslashes($agent);
99014d99ec0SAndreas Gohr        $ua_type = '';
99114d99ec0SAndreas Gohr        $ua_ver  = '';
99214d99ec0SAndreas Gohr        $os      = '';
99314d99ec0SAndreas Gohr        $ua_info = addslashes($this->ua_info($agent,$ua_type,$ua_ver,$os));
99414d99ec0SAndreas Gohr
9951878f16fSAndreas Gohr        $page    = addslashes($_REQUEST['p']);
9961878f16fSAndreas Gohr        $ip      = addslashes($_SERVER['REMOTE_ADDR']);
9971878f16fSAndreas Gohr        $sx      = (int) $_REQUEST['sx'];
9981878f16fSAndreas Gohr        $sy      = (int) $_REQUEST['sy'];
9991878f16fSAndreas Gohr        $vx      = (int) $_REQUEST['vx'];
10001878f16fSAndreas Gohr        $vy      = (int) $_REQUEST['vy'];
100175fa767dSAndreas Gohr        $js      = (int) $_REQUEST['js'];
1002*3c0acc14SAndreas Gohr        $uid     = addslashes($_REQUEST['uid']);
10031878f16fSAndreas Gohr        $user    = addslashes($_SERVER['REMOTE_USER']);
10041878f16fSAndreas Gohr        $session = addslashes(session_id());
1005*3c0acc14SAndreas Gohr        if(!$uid) $uid = $session;
10061878f16fSAndreas Gohr
100794171ff3SAndreas Gohr        $sql  = "INSERT DELAYED INTO ".$this->getConf('db_prefix')."access
100875fa767dSAndreas Gohr                    SET dt       = NOW(),
100975fa767dSAndreas Gohr                        page     = '$page',
10101878f16fSAndreas Gohr                        ip       = '$ip',
10111878f16fSAndreas Gohr                        ua       = '$ua',
10121878f16fSAndreas Gohr                        ua_info  = '$ua_info',
101314d99ec0SAndreas Gohr                        ua_type  = '$ua_type',
101414d99ec0SAndreas Gohr                        ua_ver   = '$ua_ver',
101514d99ec0SAndreas Gohr                        os       = '$os',
10161878f16fSAndreas Gohr                        ref      = '$ref',
101794171ff3SAndreas Gohr                        ref_md5  = '$ref_md5',
101814d99ec0SAndreas Gohr                        ref_type = '$ref_type',
10191878f16fSAndreas Gohr                        screen_x = '$sx',
10201878f16fSAndreas Gohr                        screen_y = '$sy',
10211878f16fSAndreas Gohr                        view_x   = '$vx',
10221878f16fSAndreas Gohr                        view_y   = '$vy',
102375fa767dSAndreas Gohr                        js       = '$js',
10241878f16fSAndreas Gohr                        user     = '$user',
1025*3c0acc14SAndreas Gohr                        session  = '$session',
1026*3c0acc14SAndreas Gohr                        uid      = '$uid'";
10271878f16fSAndreas Gohr        $ok = $this->runSQL($sql);
10281878f16fSAndreas Gohr        if(is_null($ok)){
10291878f16fSAndreas Gohr            global $MSG;
10301878f16fSAndreas Gohr            print_r($MSG);
10311878f16fSAndreas Gohr        }
103214d99ec0SAndreas Gohr
103314d99ec0SAndreas Gohr        // resolve the IP
103414d99ec0SAndreas Gohr        $this->log_ip($_SERVER['REMOTE_ADDR']);
10351878f16fSAndreas Gohr    }
10361878f16fSAndreas Gohr
10371878f16fSAndreas Gohr    /**
10381878f16fSAndreas Gohr     * Just send a 1x1 pixel blank gif to the browser
10391878f16fSAndreas Gohr     *
10401878f16fSAndreas Gohr     * @called from log.php
10411878f16fSAndreas Gohr     *
10421878f16fSAndreas Gohr     * @author Andreas Gohr <andi@splitbrain.org>
10431878f16fSAndreas Gohr     * @author Harry Fuecks <fuecks@gmail.com>
10441878f16fSAndreas Gohr     */
10451878f16fSAndreas Gohr    function sendGIF(){
10461878f16fSAndreas Gohr        $img = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7');
10471878f16fSAndreas Gohr        header('Content-Type: image/gif');
10481878f16fSAndreas Gohr        header('Content-Length: '.strlen($img));
10491878f16fSAndreas Gohr        header('Connection: Close');
10501878f16fSAndreas Gohr        print $img;
10511878f16fSAndreas Gohr        flush();
10521878f16fSAndreas Gohr        // Browser should drop connection after this
10531878f16fSAndreas Gohr        // Thinks it's got the whole image
10541878f16fSAndreas Gohr    }
10551878f16fSAndreas Gohr
10561878f16fSAndreas Gohr}
1057