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