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