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