1<?php
2// must be run within Dokuwiki
3if (!defined('DOKU_INC')) die();
4
5/**
6 * statdisplay plugin table helper component
7 *
8 * @author Andreas Gohr <gohr@cosmocode.de>
9 * @license  GPL 2 (http://www.gnu.org/licenses/gpl.html)
10 */
11class helper_plugin_statdisplay_table extends DokuWiki_Plugin
12{
13    /** @var helper_plugin_statdisplay_log */
14    private $log = null;
15
16    /** @var Doku_Renderer */
17    private $R = null;
18
19    /**
20     * @param Doku_Renderer $R
21     * @param string $command type of statistic
22     * @param string $from restrict to this month
23     * @param string $to end interval
24     * @return void
25     */
26    public function table($R, $command, $from = '', $to = '')
27    {
28        $this->R = $R;
29        $this->log = plugin_load('helper', 'statdisplay_log');
30
31        switch ($command) {
32            case 'all':
33                $this->summary($from, $to);
34                break;
35            case 'one month':
36                $this->month($from);
37                break;
38            case 'month by day':
39                $this->monthby('day', $from);
40                break;
41            case 'month by hour':
42                $this->monthby('hour', $from);
43                break;
44            case 'top referers':
45                $this->referer($from);
46                break;
47            case 'top entries':
48                $this->entry($from);
49                break;
50            case 'top urls':
51                $this->url($from);
52                break;
53            case 'top bytes':
54                break;
55            case 'user agents':
56                $this->ua($from);
57                break;
58            case 'progress bar':
59                $this->progress();
60                break;
61            case 'traffic by user':
62                $this->userdownloads($from);
63                break;
64            default:
65                $R->cdata('No such table: ' . $command);
66
67        }
68    }
69
70    private function progress()
71    {
72        $pct = sprintf('%.2f', $this->log->progress());
73        $this->R->doc .= '<div class="statdisplay-progress" title="' . $pct . '%"><span style="width: ' . $pct . '%"></span></div>';
74    }
75
76    /**
77     * Print referers for a given month
78     *
79     * @param string $date
80     */
81    private function referer($date = '')
82    {
83        if (!$date) $date = date('Y-m');
84        $this->listtable(
85            $this->log->logdata[$date]['referer_url'],
86            $this->log->logdata[$date]['referer']['count'],
87            sprintf($this->getLang('t_topReferrer'), $date)
88        );
89    }
90
91    /**
92     * Print top entry pages for a given month
93     *
94     * @param string $date
95     */
96    private function entry($date = '')
97    {
98        if (!$date) $date = date('Y-m');
99        $this->listtable(
100            $this->log->logdata[$date]['entry'],
101            $this->log->logdata[$date]['page']['all']['count'],
102            sprintf($this->getLang('t_topEntry'), $date)
103        );
104    }
105
106    /**
107     * Print top user agents for a given month
108     *
109     * @param string $date
110     */
111    private function ua($date = '')
112    {
113        if (!$date) $date = date('Y-m');
114        $this->listtable(
115            $this->log->logdata[$date]['useragent'],
116            $this->log->logdata[$date]['page']['all']['count'],
117            sprintf($this->getLang('t_topUserAgents'), $date)
118        );
119    }
120
121    /**
122     * Print top pages for a given month
123     *
124     * @param string $date
125     */
126    private function url($date = '')
127    {
128        if (!$date) $date = date('Y-m');
129        $this->listtable(
130            $this->log->logdata[$date]['page_url'],
131            $this->log->logdata[$date]['page']['all']['count'],
132            sprintf($this->getLang('t_topPages'), $date)
133        );
134    }
135
136    /**
137     * Print daily or hourly statistics
138     *
139     * @param string $by either 'day' or 'hour'
140     * @param string $date
141     */
142    private function monthby($by, $date = '')
143    {
144        if (!$date) $date = date('Y-m');
145        $data = $this->log->logdata[$date];
146
147        $title = sprintf($this->getLang('t_' . $by), $date);
148
149        $this->R->table_open();
150
151        $this->R->tablerow_open();
152        $this->head($title, 11);
153        $this->R->tablerow_close();
154
155        $this->R->tablerow_open();
156        $this->head($this->getLang($by));
157        $this->head($this->getLang('hits'), 2);
158        $this->head($this->getLang('media'), 2);
159        $this->head($this->getLang('pages'), 2);
160        $this->head($this->getLang('visitors'), 2);
161        $this->head($this->getLang('traffic'), 2);
162        $this->R->tablerow_close();
163
164        $keys = array_keys((array)$data['hits'][$by]);
165        sort($keys);
166        foreach ($keys as $idx) {
167            $this->R->tablerow_open();
168            $this->hcell($idx);
169
170            $this->cell($data['hits'][$by][$idx]['count']);
171            $this->cell($this->pct($data['hits'][$by][$idx]['count'], $data['hits']['all']['count']));
172
173            $this->cell($data['media'][$by][$idx]['count']);
174            $this->cell($this->pct($data['media'][$by][$idx]['count'], $data['media']['all']['count']));
175
176            $this->cell($data['page'][$by][$idx]['count']);
177            $this->cell($this->pct($data['page'][$by][$idx]['count'], $data['page']['all']['count']));
178
179            $this->cell($data['hits'][$by][$idx]['visitor']);
180            $this->cell($this->pct($data['hits'][$by][$idx]['visitor'], $data['hits']['all']['visitor']));
181
182            $this->cell(filesize_h($data['hits'][$by][$idx]['bytes']));
183            $this->cell($this->pct($data['hits'][$by][$idx]['bytes'], $data['hits']['all']['bytes']));
184
185            $this->R->tablerow_close();
186        }
187
188        $this->R->table_close();
189    }
190
191    /**
192     * print a single month
193     *
194     * @param $date
195     */
196    private function month($date = '')
197    {
198        if (!$date) $date = date('Y-m');
199        $data = $this->log->logdata[$date];
200
201        $this->R->table_open();
202
203        $this->R->tablerow_open();
204        $this->head(sprintf($this->getLang('t_statisticMonth'), $date), 3);
205        $this->R->tablerow_close();
206
207        $this->R->tablerow_open();
208        $this->hcell($this->getLang('totalHits'));
209        $this->cell($data['page']['all']['count'] + $data['media']['all']['count'], 2);
210        $this->R->tablerow_close();
211
212        $this->R->tablerow_open();
213        $this->hcell($this->getLang('totalFiles'));
214        $this->cell($data['media']['all']['count'], 2);
215        $this->R->tablerow_close();
216
217        $this->R->tablerow_open();
218        $this->hcell($this->getLang('totalPages'));
219        $this->cell($data['page']['all']['count'], 2);
220        $this->R->tablerow_close();
221
222        $this->R->tablerow_open();
223        $this->hcell($this->getLang('totalVisitors'));
224        $this->cell($data['page']['all']['visitor'], 2);
225        $this->R->tablerow_close();
226
227        $this->R->tablerow_open();
228        $this->hcell($this->getLang('totalBytes'));
229        $this->cell(filesize_h($data['page']['all']['bytes']), 2);
230        $this->R->tablerow_close();
231
232        $this->R->tablerow_open();
233        $this->head('');
234        $this->head($this->getLang('avg'));
235        $this->head($this->getLang('max'));
236        $this->R->tablerow_close();
237
238        $this->R->tablerow_open();
239        $this->hcell($this->getLang('hitsHour'));
240        $this->cell($this->log->avg($data['hits']['hour'], 'count'));
241        $this->cell($this->log->max($data['hits']['hour'], 'count'));
242        $this->R->tablerow_close();
243
244        $this->R->tablerow_open();
245        $this->hcell($this->getLang('hitsDay'));
246        $this->cell($this->log->avg($data['hits']['day'], 'count'));
247        $this->cell($this->log->max($data['hits']['day'], 'count'));
248        $this->R->tablerow_close();
249
250        $this->R->tablerow_open();
251        $this->hcell($this->getLang('filesDay'));
252        $this->cell($this->log->avg($data['media']['day'], 'count'));
253        $this->cell($this->log->max($data['media']['day'], 'count'));
254        $this->R->tablerow_close();
255
256        $this->R->tablerow_open();
257        $this->hcell($this->getLang('pagesDay'));
258        $this->cell($this->log->avg($data['page']['day'], 'count'));
259        $this->cell($this->log->max($data['page']['day'], 'count'));
260        $this->R->tablerow_close();
261
262        $this->R->tablerow_open();
263        $this->hcell($this->getLang('bytesDay'));
264        $this->cell(filesize_h($this->log->avg($data['hits']['day'], 'bytes')));
265        $this->cell(filesize_h($this->log->max($data['hits']['day'], 'bytes')));
266        $this->R->tablerow_close();
267
268        $this->R->tablerow_open();
269        $this->head($this->getLang('hitsStatusCode'), 3);
270        $this->R->tablerow_close();
271
272        foreach ((array)$this->log->logdata[$date]['status']['all'] as $code => $count) {
273            $this->R->tablerow_open();
274            $this->hcell('Status ' . $code . ' - ' . $this->getLang('status_' . $code));
275            $this->cell($count, 2);
276            $this->R->tablerow_close();
277        }
278
279        $this->R->table_close();
280    }
281
282    /**
283     * print the whole summary table
284     *
285     * @param string $from unused
286     * @param string $to unsused
287     */
288    private function summary($from = '', $to = '')
289    {
290        $this->R->table_open();
291
292        $this->R->tablerow_open();
293        $this->head($this->getLang('summaryMonth'), 10);
294        $this->R->tablerow_close();
295
296        $this->R->tablerow_open();
297        $this->head($this->getLang('month'), 1, 2);
298        $this->head($this->getLang('dailyavg'), 4);
299        $this->head($this->getLang('totals'), 5);
300        $this->R->tablerow_close();
301
302        $this->R->tablerow_open();
303        $this->head($this->getLang('hits'));
304        $this->head($this->getLang('files'));
305        $this->head($this->getLang('pages'));
306        $this->head($this->getLang('visitors'));
307        $this->head($this->getLang('hits'));
308        $this->head($this->getLang('files'));
309        $this->head($this->getLang('pages'));
310        $this->head($this->getLang('visitors'));
311        $this->head($this->getLang('bytes'));
312
313        $this->R->tablerow_close();
314
315        foreach ((array)$this->log->logdata as $month => $data) {
316            if ($month[0] == '_') continue;
317            if (!empty($from) && $month < $from) continue;
318            if (!empty($to) && $month > $to) break;
319
320
321
322
323            $this->R->tablerow_open();
324
325            $this->cell($month, 1, false); // Month
326            // ---- averages ----
327            $this->cell(round($this->log->avg($data['hits']['day'] ?? [], 'count'))); // Hits
328            $this->cell(round($this->log->avg($data['media']['day'] ?? [], 'count'))); // Files
329            $this->cell(round($this->log->avg($data['page']['day'] ?? [], 'count'))); // Pages
330            $this->cell(round($this->log->avg($data['hits']['day'] ?? [], 'visitor'))); // Visits
331            // ---- totals ----
332            $this->cell($data['hits']['all']['count']); // Hits
333            $this->cell($data['media']['all']['count']); // Files
334            $this->cell($data['page']['all']['count']); // Pages
335            $this->cell($data['hits']['all']['visitor']); // Visitors
336            $this->cell(filesize_h($data['hits']['all']['bytes'])); // kBytes
337
338            $this->R->tablerow_close();
339        }
340
341        $this->R->table_close();
342    }
343
344    /**
345     * @param string $date month to display
346     */
347    private function userdownloads($date)
348    {
349        $usertraffic = $this->log->usertraffic($date);
350
351        $this->listtable($usertraffic, $this->log->sum($usertraffic), $this->getLang('t_usertraffic'), true);
352    }
353
354    /**
355     * Print a simple listing table
356     *
357     * @param array $data
358     * @param float $max
359     * @param string $title
360     * @param bool $istraffic
361     * @return void
362     */
363    private function listtable(&$data, $max, $title, $istraffic = false)
364    {
365        if (!$data) $data = array();
366
367        arsort($data);
368        $row = 1;
369
370        $this->R->table_open();
371
372        $this->R->tablerow_open();
373        $this->head($title, 4);
374        $this->R->tablerow_close();
375
376        $this->R->tablerow_open();
377        $this->head('#');
378        $this->head($this->getLang('name'));
379        if ($istraffic) {
380            $this->head($this->getLang('traffic'), 2);
381        } else {
382            $this->head($this->getLang('hits'), 2);
383        }
384        $this->R->tablerow_close();
385
386        foreach ($data as $key => $count) {
387            if ($istraffic) {
388                $val = filesize_h($count);
389            } else {
390                $val = $count;
391            }
392
393            $this->R->tablerow_open();
394            $this->cell($row);
395            $this->hcell($key);
396            $this->cell($val);
397            $this->cell($this->pct($count, $max));
398            $this->R->tablerow_close();
399            $row++;
400            if ($row > $this->log->top_limit) break;
401        }
402
403        $this->R->table_close();
404    }
405
406    /**
407     * Calculate and format a percent value
408     *
409     * @param $val
410     * @param $max
411     * @return string
412     */
413    private function pct($val, $max)
414    {
415        if (!$max) return '0.00%';
416
417        return sprintf("%.2f%%", $val * 100 / $max);
418    }
419
420    /**
421     * print a table header cell
422     *
423     * @param string $data
424     * @param int $col
425     * @param int $row
426     */
427    private function head($data = '', $col = 1, $row = 1)
428    {
429        $this->R->tableheader_open($col, 'center', $row);
430        $this->R->cdata($data);
431        $this->R->tableheader_close();
432    }
433
434    /**
435     * print a non numeric data cell
436     *
437     * @param string $data
438     * @param int $span
439     */
440    private function hcell($data = '', $span = 1)
441    {
442        $this->cell($data, $span, false);
443    }
444
445    /**
446     * print a numeric data cell
447     *
448     * @param string $data
449     * @param int $span
450     * @param bool $number
451     */
452    private function cell($data = '', $span = 1, $number = true)
453    {
454        if ($number) {
455            if (!$data) $data = 0;
456            $align = 'right';
457        } else {
458            $align = null;
459        }
460
461        $this->R->tablecell_open($span, $align);
462        $this->R->cdata($data);
463        $this->R->tablecell_close();
464    }
465}
466