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