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