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