1<?php
2// must be run within Dokuwiki
3if(!defined('DOKU_INC')) die();
4
5/**
6 * statdisplay plugin graph 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_graph extends DokuWiki_Plugin {
12    /** @var helper_plugin_statdisplay_log */
13    private $log = null;
14
15    /**
16     * Outputs a Graph image
17     *
18     * @param  string $command
19     * @param  string $from
20     * @param  string $to
21     * @return void
22     */
23    public function sendgraph($command, $from = '', $to = '') {
24        require dirname(__FILE__).'/../pchart/pData.php';
25        require dirname(__FILE__).'/../pchart/pChart.php';
26        require dirname(__FILE__).'/../pchart/GDCanvas.php';
27        require dirname(__FILE__).'/../pchart/PieChart.php';
28
29        $this->log = plugin_load('helper', 'statdisplay_log');
30
31        header('Content-Type: image/png');
32        switch($command) {
33            case 'all':
34                $this->summary();
35                break;
36            case 'month by day':
37                $this->monthby('day', $from);
38                break;
39            case 'month by hour':
40                $this->monthby('hour', $from);
41                break;
42            case 'traffic by day':
43                $this->trafficby('day', $from);
44                break;
45            case 'traffic by hour':
46                $this->trafficby('hour', $from);
47                break;
48            case 'traffic by user':
49                $this->userdownloads($from);
50                break;
51            default:
52                $this->nograph('No such graph: '.$command);
53                break;
54        }
55    }
56
57    /**
58     * Show all the access data
59     *
60     * @param string $from
61     * @param string $to
62     * @return void
63     */
64    private function summary($from = '', $to = '') {
65        $times    = array();
66        $hits     = array();
67        $pages    = array();
68        $media    = array();
69        $visitors = array();
70
71        foreach($this->log->logdata as $month => $data) {
72            if($month{0} == '_') continue;
73            if($from && $month < $from) continue;
74            if($to && $month > $to) break;
75
76            $times[]    = $month;
77            $pages[]    = $data['page']['all']['count'];
78            $media[]    = $data['media']['all']['count'];
79            $hits[]     = $data['hits']['all']['count'];
80            $visitors[] = $data['hits']['all']['visitor'];
81        }
82
83        $title = $this->getLang('t_summary');
84
85        $this->accessgraph(
86            $title,
87            $times,
88            array(
89                 $this->getLang('hits'),
90                 $this->getLang('pages'),
91                 $this->getLang('media'),
92                 $this->getLang('visitors'),
93            ),
94            array($hits, $pages, $media, $visitors)
95        );
96    }
97
98    /**
99     * Show month access by day or hour
100     *
101     * @param string $by  either day or hour
102     * @param string $date
103     */
104    private function monthby($by, $date = '') {
105        if(!$date) $date = date('Y-m');
106        $data = $this->log->logdata[$date];
107
108        $times    = array();
109        $hits     = array();
110        $pages    = array();
111        $media    = array();
112        $visitors = array();
113
114        foreach(array_keys((array) $data['hits'][$by]) as $idx) {
115            $times[]    = $idx;
116            $pages[]    = $data['page'][$by][$idx]['count'];
117            $media[]    = $data['media'][$by][$idx]['count'];
118            $hits[]     = $data['hits'][$by][$idx]['count'];
119            $visitors[] = $data['hits'][$by][$idx]['visitor'];
120        }
121
122        $title = sprintf($this->getLang('t_'.$by), $date);
123
124        $this->accessgraph(
125            $title,
126            $times,
127            array(
128                 $this->getLang('hits'),
129                 $this->getLang('pages'),
130                 $this->getLang('media'),
131                 $this->getLang('visitors'),
132            ),
133            array($hits, $pages, $media, $visitors)
134        );
135    }
136
137    /**
138     * Show month traffic by day or hour
139     *
140     * @param string $by  either day or hour
141     * @param string $date
142     */
143    private function trafficby($by, $date = '') {
144        if(!$date) $date = date('Y-m');
145        $data = $this->log->logdata[$date];
146
147        $times = array();
148        $hits  = array();
149        $pages = array();
150        $media = array();
151
152        foreach(array_keys((array) $data['hits'][$by]) as $idx) {
153            $times[] = $idx;
154            $pages[] = $data['page'][$by][$idx]['bytes'] / 1024;
155            $media[] = $data['media'][$by][$idx]['bytes'] / 1024;
156            $hits[]  = $data['hits'][$by][$idx]['bytes'] / 1024;
157        }
158
159        $title = 'Traffic';
160
161        $this->accessgraph(
162            $title,
163            $times,
164            array(
165                 $this->getLang('all'),
166                 $this->getLang('pages'),
167                 $this->getLang('media'),
168            ),
169            array($hits, $pages, $media)
170        );
171    }
172
173    /**
174     * @param string $date month to display
175     */
176    private function userdownloads($date) {
177        $usertraffic = $this->log->usertraffic($date);
178        if(!$usertraffic) $this->nograph($this->getLang('t_usertraffic').': no data');
179
180        $tomb = create_function('$in', 'return $in / 1024 /1024;');
181        $usertraffic = array_map($tomb, $usertraffic);
182
183        // get work day average
184        if(count($usertraffic)){
185            $avg = $this->log->avg($usertraffic);
186            // $avg = $avg / 7 *5; //work day average
187        }else{
188            $avg = 0;
189        }
190        arsort($usertraffic); // highest first
191
192        // limit number of users shown
193        $maxusers = 10;
194        if(count($usertraffic) > $maxusers+1){
195            $others = array_slice($usertraffic, $maxusers);
196            $usertraffic = array_slice($usertraffic, 0 ,$maxusers);
197
198            $other = 0;
199            foreach($others as $user => $traffic){
200                $other += $traffic;
201            }
202
203            $usertraffic[sprintf($this->getLang('others'), count($others))] = $other;
204        }
205
206        // prepare the graph datasets
207        $DataSet = new pData();
208        $DataSet->addPoints(array_values($usertraffic), "traffic");
209
210        // setup axis
211        $DataSet->AddPoints(array_keys($usertraffic), 'names');
212        $DataSet->AddAllSeries();
213        $DataSet->SetAbscissaLabelSeries('names');
214        $DataSet->removeSeries('names');
215        $DataSet->removeSeriesName('names');
216
217
218        // create the bar graph
219        $Canvas = new GDCanvas(600, 300, false);
220        $Chart  = new pChart(600, 300, $Canvas);
221
222        $Chart->setFontProperties(dirname(__FILE__).'/../pchart/Fonts/DroidSans.ttf', 8);
223        $Chart->setGraphArea(50, 40, 600, 200);
224        $Chart->drawScale(
225            $DataSet, new ScaleStyle(SCALE_NORMAL, new Color(127)),
226            45, 1, true
227        );
228
229        $Chart->drawBarGraph($DataSet->GetData(), $DataSet->GetDataDescription());
230        //$Chart->drawLegend(500, 40, $DataSet->GetDataDescription(), new Color(250));
231
232        $Chart->drawTreshold($avg, new Color(128,0,0));
233
234        $Chart->setFontProperties(dirname(__FILE__).'/../pchart/Fonts/DroidSans.ttf', 12);
235        $Chart->drawTitle(10, 10, $this->getLang('t_usertraffic').' (MB)', new Color(0), 590, 30);
236
237        $Chart->Render(null);
238
239    }
240
241    /**
242     * Draws a line or bargraph depending on the number of data points
243     *
244     * @param string $title the graph's title
245     * @param array  $axis the axis points
246     * @param array  $labels the labels for the datasets
247     * @param array  $datasets any number of data arrays
248     */
249    private function accessgraph($title, $axis, $labels, $datasets) {
250        if(!count($axis)) {
251            $this->nograph($title.': no data');
252            return;
253        }
254
255        // add the data and labels
256        $DataSet = new pData();
257        foreach($datasets as $num => $set) {
258            $DataSet->AddPoints($set, "series$num");
259            $DataSet->SetSeriesName($labels[$num], "series$num");
260        }
261
262        // setup axis
263        $DataSet->AddPoints($axis, 'times');
264        $DataSet->AddAllSeries();
265        $DataSet->SetAbscissaLabelSeries('times');
266        $DataSet->removeSeries('times');
267        $DataSet->removeSeriesName('times');
268
269        $Canvas      = new GDCanvas(600, 300, false);
270        $Chart       = new pChart(600, 300, $Canvas);
271        $usebargraph = (count($axis) < 10);
272
273        $Chart->setFontProperties(dirname(__FILE__).'/../pchart/Fonts/DroidSans.ttf', 8);
274        $Chart->setGraphArea(50, 40, 500, 200);
275        $Chart->drawScale(
276            $DataSet, new ScaleStyle(SCALE_NORMAL, new Color(127)),
277            45, 1, $usebargraph
278        );
279
280        if($usebargraph) {
281            $Chart->drawBarGraph($DataSet->GetData(), $DataSet->GetDataDescription());
282        } else {
283            $Chart->drawLineGraph($DataSet->GetData(), $DataSet->GetDataDescription());
284        }
285        $Chart->drawLegend(500, 40, $DataSet->GetDataDescription(), new Color(250));
286
287        $Chart->setFontProperties(dirname(__FILE__).'/../pchart/Fonts/DroidSans.ttf', 12);
288        $Chart->drawTitle(10, 10, $title, new Color(0), 590, 30);
289
290        $Chart->Render(null);
291    }
292
293    /**
294     * Just creates a message
295     *
296     * @param $title
297     */
298    private function nograph($title) {
299        $Canvas = new GDCanvas(300, 40, false);
300        $Chart  = new pChart(300, 40, $Canvas);
301        $Chart->setFontProperties(dirname(__FILE__).'/../pchart/Fonts/DroidSans.ttf', 10);
302        $Chart->drawTitle(0, 0, $title, new Color(128, 0, 0), 300, 40);
303        $Chart->Render(null);
304        exit;
305    }
306}
307