xref: /plugin/statistics/admin.php (revision 616c1e8bfa342cfa9dcabbb51d8eed57ac7e29f7)
1<?php
2/**
3 * statistics plugin
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Andreas Gohr <gohr@splitbrain.org>
7 */
8
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12/**
13 * All DokuWiki plugins to extend the admin function
14 * need to inherit from this class
15 */
16class admin_plugin_statistics extends DokuWiki_Admin_Plugin {
17    /** @var string the currently selected page */
18    protected $opt = '';
19
20    /** @var string from date in YYYY-MM-DD */
21    protected $from = '';
22    /** @var string to date in YYYY-MM-DD */
23    protected $to = '';
24    /** @var int Offset to use when displaying paged data */
25    protected $start = 0;
26
27    /** @var string MySQL timelimit statement */
28    protected $tlimit = '';
29
30    /** @var helper_plugin_statistics  */
31    protected $hlp;
32
33    /**
34     * Available statistic pages
35     */
36    protected $pages = array(
37        'dashboard' => 1,
38
39        'content' => array(
40            'page',
41            'edits',
42            'images',
43            'downloads',
44            'history',
45        ),
46        'users' => array(
47            'topuser',
48            'topeditor',
49            'topgroup',
50            'topgroupedit',
51            'seenusers',
52        ),
53        'links' => array(
54            'referer',
55            'newreferer',
56            'outlinks',
57        ),
58        'search' => array(
59            'searchengines',
60            'searchphrases',
61            'searchwords',
62            'internalsearchphrases',
63            'internalsearchwords',
64        ),
65        'technology' => array(
66            'browsers',
67            'os',
68            'countries',
69            'resolution',
70            'viewport',
71        ),
72    );
73
74    /** @var array keeps a list of all real content pages, generated from above array */
75    protected $allowedpages = array();
76
77    /**
78     * Initialize the helper
79     */
80    public function __construct() {
81        $this->hlp = plugin_load('helper', 'statistics');
82
83        // build a list of pages
84        foreach($this->pages as $key => $val) {
85            if(is_array($val)) {
86                $this->allowedpages = array_merge($this->allowedpages, $val);
87            }else {
88                $this->allowedpages[] = $key;
89            }
90        }
91    }
92
93    /**
94     * Access for managers allowed
95     */
96    public function forAdminOnly() {
97        return false;
98    }
99
100    /**
101     * return sort order for position in admin menu
102     */
103    public function getMenuSort() {
104        return 350;
105    }
106
107    /**
108     * handle user request
109     */
110    public function handle() {
111        $this->opt = preg_replace('/[^a-z]+/', '', $_REQUEST['opt']);
112        if(!in_array($this->opt, $this->allowedpages)) $this->opt = 'dashboard';
113
114        $this->start = (int) $_REQUEST['s'];
115        $this->setTimeframe($_REQUEST['f'], $_REQUEST['t']);
116    }
117
118    /**
119     * set limit clause
120     */
121    public function setTimeframe($from, $to) {
122        // swap if wrong order
123        if($from > $to) list($from, $to) = array($to, $from);
124
125        $this->tlimit = $this->hlp->Query()->mktlimit($from, $to);
126        $this->from   = $from;
127        $this->to     = $to;
128    }
129
130    /**
131     * Output the Statistics
132     */
133    function html() {
134        echo '<div id="plugin__statistics">';
135        echo '<h1>' . $this->getLang('menu') . '</h1>';
136        $this->html_timeselect();
137        tpl_flush();
138
139        $method = 'html_' . $this->opt;
140        if(method_exists($this, $method)) {
141            echo '<div class="plg_stats_' . $this->opt . '">';
142            echo '<h2>' . $this->getLang($this->opt) . '</h2>';
143            $this->$method();
144            echo '</div>';
145        }
146        echo '</div>';
147    }
148
149    /**
150     * Return the TOC
151     *
152     * @return array
153     */
154    function getTOC() {
155        $toc = array();
156        foreach($this->pages as $key => $info) {
157            if (is_array($info)) {
158
159                $toc[] = html_mktocitem(
160                    '',
161                    $this->getLang($key),
162                    1,
163                    ''
164                );
165
166                foreach($info as $page) {
167                    $toc[] = html_mktocitem(
168                        '?do=admin&amp;page=statistics&amp;opt=' . $page . '&amp;f=' . $this->from . '&amp;t=' . $this->to,
169                        $this->getLang($page),
170                        2,
171                        ''
172                    );
173                }
174            } else {
175                $toc[] = html_mktocitem(
176                    '?do=admin&amp;page=statistics&amp;opt=' . $key . '&amp;f=' . $this->from . '&amp;t=' . $this->to,
177                    $this->getLang($key),
178                    1,
179                    ''
180                );
181            }
182        }
183        return $toc;
184    }
185
186    function html_graph($name, $width, $height) {
187        $url = DOKU_BASE . 'lib/plugins/statistics/img.php?img=' . $name .
188            '&amp;f=' . $this->from . '&amp;t=' . $this->to;
189        echo '<img src="' . $url . '" class="graph" width="' . $width . '" height="' . $height . '"/>';
190    }
191
192    /**
193     * Outputs pagination links
194     *
195     * @param int $limit
196     * @param int $next
197     */
198    function html_pager($limit, $next) {
199        echo '<div class="plg_stats_pager">';
200
201        if($this->start > 0) {
202            $go = max($this->start - $limit, 0);
203            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 button">' . $this->getLang('prev') . '</a>';
204        }
205
206        if($next) {
207            $go = $this->start + $limit;
208            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 button">' . $this->getLang('next') . '</a>';
209        }
210        echo '</div>';
211    }
212
213    /**
214     * Print the time selection menu
215     */
216    function html_timeselect() {
217        $today  = date('Y-m-d');
218        $last1  = date('Y-m-d', time() - (60 * 60 * 24));
219        $last7  = date('Y-m-d', time() - (60 * 60 * 24 * 7));
220        $last30 = date('Y-m-d', time() - (60 * 60 * 24 * 30));
221
222        echo '<div class="plg_stats_timeselect">';
223        echo '<span>' . $this->getLang('time_select') . '</span> ';
224
225        echo '<form action="'.DOKU_SCRIPT.'" method="get">';
226        echo '<input type="hidden" name="do" value="admin" />';
227        echo '<input type="hidden" name="page" value="statistics" />';
228        echo '<input type="hidden" name="opt" value="' . $this->opt . '" />';
229        echo '<input type="text" name="f" value="' . $this->from . '" class="edit datepicker" />';
230        echo '<input type="text" name="t" value="' . $this->to . '" class="edit datepicker" />';
231        echo '<input type="submit" value="go" class="button" />';
232        echo '</form>';
233
234        echo '<ul>';
235        foreach(array('today', 'last1', 'last7', 'last30') as $time) {
236            echo '<li>';
237            echo '<a href="?do=admin&amp;page=statistics&amp;opt=' . $this->opt . '&amp;f=' . $$time . '&amp;t=' . $today . '">';
238            echo $this->getLang('time_' . $time);
239            echo '</a>';
240            echo '</li>';
241        }
242        echo '</ul>';
243
244        echo '</div>';
245    }
246
247    /**
248     * Print an introductionary screen
249     */
250    function html_dashboard() {
251        echo '<p>' . $this->getLang('intro_dashboard') . '</p>';
252
253        // general info
254        echo '<div class="plg_stats_top">';
255        $result = $this->hlp->Query()->aggregate($this->tlimit);
256
257        echo '<ul class="left">';
258        foreach(array('pageviews', 'sessions', 'visitors', 'users', 'logins', 'current') as $name) {
259            echo '<li><div class="li">' . sprintf($this->getLang('dash_' . $name), $result[$name]) . '</div></li>';
260        }
261        echo '</ul>';
262
263        echo '<ul class="left">';
264        foreach(array('bouncerate', 'timespent', 'avgpages', 'newvisitors', 'registrations') as $name) {
265            echo '<li><div class="li">' . sprintf($this->getLang('dash_' . $name), $result[$name]) . '</div></li>';
266        }
267        echo '</ul>';
268
269        $this->html_graph('dashboardviews', 700, 280);
270        $this->html_graph('dashboardwiki', 700, 280);
271        echo '</div>';
272
273        // top pages today
274        echo '<div>';
275        echo '<h2>' . $this->getLang('dash_mostpopular') . '</h2>';
276        $result = $this->hlp->Query()->pages($this->tlimit, $this->start, 15);
277        $this->html_resulttable($result);
278        echo '<a href="?do=admin&amp;page=statistics&amp;opt=page&amp;f=' . $this->from . '&amp;t=' . $this->to . '" class="more button">' . $this->getLang('more') . '</a>';
279        echo '</div>';
280
281        // top referer today
282        echo '<div>';
283        echo '<h2>' . $this->getLang('dash_newincoming') . '</h2>';
284        $result = $this->hlp->Query()->newreferer($this->tlimit, $this->start, 15);
285        $this->html_resulttable($result);
286        echo '<a href="?do=admin&amp;page=statistics&amp;opt=newreferer&amp;f=' . $this->from . '&amp;t=' . $this->to . '" class="more button">' . $this->getLang('more') . '</a>';
287        echo '</div>';
288
289        // top searches today
290        echo '<div>';
291        echo '<h2>' . $this->getLang('dash_topsearch') . '</h2>';
292        $result = $this->hlp->Query()->searchphrases(true, $this->tlimit, $this->start, 15);
293        $this->html_resulttable($result);
294        echo '<a href="?do=admin&amp;page=statistics&amp;opt=searchphrases&amp;f=' . $this->from . '&amp;t=' . $this->to . '" class="more button">' . $this->getLang('more') . '</a>';
295        echo '</div>';
296    }
297
298    function html_history() {
299        echo '<p>' . $this->getLang('intro_history') . '</p>';
300        $this->html_graph('history_page_count', 600, 200);
301        $this->html_graph('history_page_size', 600, 200);
302        $this->html_graph('history_media_count', 600, 200);
303        $this->html_graph('history_media_size', 600, 200);
304    }
305
306    function html_countries() {
307        echo '<p>' . $this->getLang('intro_countries') . '</p>';
308        $this->html_graph('countries', 400, 200);
309        $result = $this->hlp->Query()->countries($this->tlimit, $this->start, 150);
310        $this->html_resulttable($result, '', 150);
311    }
312
313    function html_page() {
314        echo '<p>' . $this->getLang('intro_page') . '</p>';
315        $result = $this->hlp->Query()->pages($this->tlimit, $this->start, 150);
316        $this->html_resulttable($result, '', 150);
317    }
318
319    function html_edits() {
320        echo '<p>' . $this->getLang('intro_edits') . '</p>';
321        $result = $this->hlp->Query()->edits($this->tlimit, $this->start, 150);
322        $this->html_resulttable($result, '', 150);
323    }
324
325    function html_images() {
326        echo '<p>' . $this->getLang('intro_images') . '</p>';
327
328        $result = $this->hlp->Query()->imagessum($this->tlimit);
329        echo '<p>';
330        echo sprintf($this->getLang('trafficsum'), $result[0]['cnt'], filesize_h($result[0]['filesize']));
331        echo '</p>';
332
333        $result = $this->hlp->Query()->images($this->tlimit, $this->start, 150);
334        $this->html_resulttable($result, '', 150);
335    }
336
337    function html_downloads() {
338        echo '<p>' . $this->getLang('intro_downloads') . '</p>';
339
340        $result = $this->hlp->Query()->downloadssum($this->tlimit);
341        echo '<p>';
342        echo sprintf($this->getLang('trafficsum'), $result[0]['cnt'], filesize_h($result[0]['filesize']));
343        echo '</p>';
344
345        $result = $this->hlp->Query()->downloads($this->tlimit, $this->start, 150);
346        $this->html_resulttable($result, '', 150);
347    }
348
349    function html_browsers() {
350        echo '<p>' . $this->getLang('intro_browsers') . '</p>';
351        $this->html_graph('browsers', 400, 200);
352        $result = $this->hlp->Query()->browsers($this->tlimit, $this->start, 150, true);
353        $this->html_resulttable($result, '', 150);
354    }
355
356    function html_topuser(){
357        echo '<p>' . $this->getLang('intro_topuser') . '</p>';
358        $this->html_graph('topuser', 400, 200);
359        $result = $this->hlp->Query()->topuser($this->tlimit, $this->start, 150, true);
360        $this->html_resulttable($result, '', 150);
361    }
362
363    function html_topeditor(){
364        echo '<p>' . $this->getLang('intro_topeditor') . '</p>';
365        $this->html_graph('topeditor', 400, 200);
366        $result = $this->hlp->Query()->topeditor($this->tlimit, $this->start, 150, true);
367        $this->html_resulttable($result, '', 150);
368    }
369
370    function html_topgroup(){
371        echo '<p>' . $this->getLang('intro_topgroup') . '</p>';
372        $this->html_graph('topgroup', 400, 200);
373        $result = $this->hlp->Query()->topgroup($this->tlimit, $this->start, 150, true);
374        $this->html_resulttable($result, '', 150);
375    }
376
377    function html_topgroupedit(){
378        echo '<p>' . $this->getLang('intro_topgroupedit') . '</p>';
379        $this->html_graph('topgroupedit', 400, 200);
380        $result = $this->hlp->Query()->topgroupedit($this->tlimit, $this->start, 150, true);
381        $this->html_resulttable($result, '', 150);
382    }
383
384    function html_os() {
385        echo '<p>' . $this->getLang('intro_os') . '</p>';
386        $this->html_graph('os', 400, 200);
387        $result = $this->hlp->Query()->os($this->tlimit, $this->start, 150, true);
388        $this->html_resulttable($result, '', 150);
389    }
390
391    function html_referer() {
392        $result = $this->hlp->Query()->aggregate($this->tlimit);
393
394        $all = $result['search'] + $result['external'] + $result['direct'];
395
396        if($all) {
397            printf(
398                '<p>' . $this->getLang('intro_referer') . '</p>',
399                $all, $result['direct'], (100 * $result['direct'] / $all),
400                $result['search'], (100 * $result['search'] / $all), $result['external'],
401                (100 * $result['external'] / $all)
402            );
403        }
404
405        $result = $this->hlp->Query()->referer($this->tlimit, $this->start, 150);
406        $this->html_resulttable($result, '', 150);
407    }
408
409    function html_newreferer() {
410        echo '<p>' . $this->getLang('intro_newreferer') . '</p>';
411
412        $result = $this->hlp->Query()->newreferer($this->tlimit, $this->start, 150);
413        $this->html_resulttable($result, '', 150);
414    }
415
416    function html_outlinks() {
417        echo '<p>' . $this->getLang('intro_outlinks') . '</p>';
418        $result = $this->hlp->Query()->outlinks($this->tlimit, $this->start, 150);
419        $this->html_resulttable($result, '', 150);
420    }
421
422    function html_searchphrases() {
423        echo '<p>' . $this->getLang('intro_searchphrases') . '</p>';
424        $result = $this->hlp->Query()->searchphrases(true, $this->tlimit, $this->start, 150);
425        $this->html_resulttable($result, '', 150);
426    }
427
428    function html_searchwords() {
429        echo '<p>' . $this->getLang('intro_searchwords') . '</p>';
430        $result = $this->hlp->Query()->searchwords(true, $this->tlimit, $this->start, 150);
431        $this->html_resulttable($result, '', 150);
432    }
433
434    function html_internalsearchphrases() {
435        echo '<p>' . $this->getLang('intro_internalsearchphrases') . '</p>';
436        $result = $this->hlp->Query()->searchphrases(false, $this->tlimit, $this->start, 150);
437        $this->html_resulttable($result, '', 150);
438    }
439
440    function html_internalsearchwords() {
441        echo '<p>' . $this->getLang('intro_internalsearchwords') . '</p>';
442        $result = $this->hlp->Query()->searchwords(false, $this->tlimit, $this->start, 150);
443        $this->html_resulttable($result, '', 150);
444    }
445
446    function html_searchengines() {
447        echo '<p>' . $this->getLang('intro_searchengines') . '</p>';
448        $this->html_graph('searchengines', 400, 200);
449        $result = $this->hlp->Query()->searchengines($this->tlimit, $this->start, 150);
450        $this->html_resulttable($result, '', 150);
451    }
452
453    function html_resolution() {
454        echo '<p>' . $this->getLang('intro_resolution') . '</p>';
455        $this->html_graph('resolution', 650, 490);
456        $result = $this->hlp->Query()->resolution($this->tlimit, $this->start, 150);
457        $this->html_resulttable($result, '', 150);
458    }
459
460    function html_viewport() {
461        echo '<p>' . $this->getLang('intro_viewport') . '</p>';
462        $this->html_graph('viewport', 650, 490);
463        $result = $this->hlp->Query()->viewport($this->tlimit, $this->start, 150);
464        $this->html_resulttable($result, '', 150);
465    }
466
467    function html_seenusers() {
468        echo '<p>' . $this->getLang('intro_seenusers') . '</p>';
469        $result = $this->hlp->Query()->seenusers($this->tlimit, $this->start, 150);
470        $this->html_resulttable($result, '', 150);
471    }
472
473    /**
474     * Display a result in a HTML table
475     */
476    function html_resulttable($result, $header = '', $pager = 0) {
477        echo '<table>';
478        if(is_array($header)) {
479            echo '<tr>';
480            foreach($header as $h) {
481                echo '<th>' . hsc($h) . '</th>';
482            }
483            echo '</tr>';
484        }
485
486        $count = 0;
487        if(is_array($result)) foreach($result as $row) {
488            echo '<tr>';
489            foreach($row as $k => $v) {
490                if($k == 'res_x') continue;
491                if($k == 'res_y') continue;
492
493                echo '<td class="plg_stats_X' . $k . '">';
494                if($k == 'page') {
495                    echo '<a href="' . wl($v) . '" class="wikilink1">';
496                    echo hsc($v);
497                    echo '</a>';
498                } elseif($k == 'media') {
499                        echo '<a href="' . ml($v) . '" class="wikilink1">';
500                        echo hsc($v);
501                        echo '</a>';
502                } elseif($k == 'filesize') {
503                    echo filesize_h($v);
504                } elseif($k == 'url') {
505                    $url = hsc($v);
506                    $url = preg_replace('/^https?:\/\/(www\.)?/', '', $url);
507                    if(strlen($url) > 45) {
508                        $url = substr($url, 0, 30) . ' &hellip; ' . substr($url, -15);
509                    }
510                    echo '<a href="' . $v . '" class="urlextern">';
511                    echo $url;
512                    echo '</a>';
513                } elseif($k == 'ilookup') {
514                    echo '<a href="' . wl('', array('id' => $v, 'do' => 'search')) . '">Search</a>';
515                } elseif($k == 'lookup') {
516                    echo '<a href="http://www.google.com/search?q=' . rawurlencode($v) . '">';
517                    echo '<img src="' . DOKU_BASE . 'lib/plugins/statistics/ico/search/google.png" alt="Google" border="0" />';
518                    echo '</a> ';
519
520                    echo '<a href="http://search.yahoo.com/search?p=' . rawurlencode($v) . '">';
521                    echo '<img src="' . DOKU_BASE . 'lib/plugins/statistics/ico/search/yahoo.png" alt="Yahoo!" border="0" />';
522                    echo '</a> ';
523
524                    echo '<a href="http://www.bing.com/search?q=' . rawurlencode($v) . '">';
525                    echo '<img src="' . DOKU_BASE . 'lib/plugins/statistics/ico/search/bing.png" alt="Bing" border="0" />';
526                    echo '</a> ';
527
528                } elseif($k == 'engine') {
529                    include_once(dirname(__FILE__) . '/inc/searchengines.php');
530                    if(isset($SEARCHENGINEINFO[$v])) {
531                        echo '<a href="' . $SEARCHENGINEINFO[$v][1] . '">' . $SEARCHENGINEINFO[$v][0] . '</a>';
532                    } else {
533                        echo hsc(ucwords($v));
534                    }
535                } elseif($k == 'eflag') {
536                    $this->html_icon('search', $v);
537                } elseif($k == 'bflag') {
538                    $this->html_icon('browser', $v);
539                } elseif($k == 'osflag') {
540                    $this->html_icon('os', $v);
541                } elseif($k == 'cflag') {
542                    $this->html_icon('flags', $v);
543                } elseif($k == 'html') {
544                    echo $v;
545                } else {
546                    echo hsc($v);
547                }
548                echo '</td>';
549            }
550            echo '</tr>';
551
552            if($pager && ($count == $pager)) break;
553            $count++;
554        }
555        echo '</table>';
556
557        if($pager) $this->html_pager($pager, count($result) > $pager);
558    }
559
560    function html_icon($type, $value) {
561        $value = strtolower(preg_replace('/[^\w]+/', '', $value));
562        $value = str_replace(' ', '_', $value);
563        $file  = 'lib/plugins/statistics/ico/' . $type . '/' . $value . '.png';
564        if($type == 'flags') {
565            $w = 18;
566            $h = 12;
567        } else {
568            $w = 16;
569            $h = 16;
570        }
571        if(file_exists(DOKU_INC . $file)) {
572            echo '<img src="' . DOKU_BASE . $file . '" alt="' . hsc($value) . '" width="' . $w . '" height="' . $h . '" />';
573        }
574    }
575}
576