Newer
Older
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
* @category Piwik_Plugins
* @package Piwik_ImageGraph
*/
/**
* The ImageGraph.get API call lets you generate a beautiful static PNG Graph for any existing Piwik report (supported graphs types are: line plot, pie chart, vertical bar chart).
* A few notes about some of the parameters available:<br/>
* - $graphType defines the type of graph plotted, accepted values are: 'evolution', 'verticalBar', 'pie'<br/>
* - $colors accepts a comma delimited list of colors that will overwrite the default Piwik colors <br/>
* - You can also customize the width, height, font size, metric being plotted (in case the data contains multiple columns/metrics).
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
*
* @package Piwik_ImageGraph
*/
class Piwik_ImageGraph_API
{
static private $instance = null;
const GRAPH_TYPE_BASIC_LINE = "evolution";
const GRAPH_TYPE_BASIC_BAR = "verticalBar";
const GRAPH_TYPE_3D_PIE = "3dPie";
const GRAPH_TYPE_BASIC_PIE = "pie";
const GRAPH_OUTPUT_INLINE = 0;
const GRAPH_OUTPUT_FILE = 1;
private $GRAPH_COLOR_PIE_0 = "3C5A69";
private $GRAPH_COLOR_PIE_1 = "679BB5";
private $GRAPH_COLOR_PIE_2 = "695A3C";
private $GRAPH_COLOR_PIE_3 = "B58E67";
private $GRAPH_COLOR_PIE_4 = "8AA68A";
private $GRAPH_COLOR_PIE_5 = "A4D2A6";
private $GRAPH_COLOR_PIE_6 = "EEEEEE";
private $GRAPH_COLOR_BAR_0 = "3B5AA9";
private $GRAPH_COLOR_BAR_1 = "063E7E";
private $GRAPH_COLOR_LINE = "063E7E";
const GRAPH_COLOR_PIE_0 = "3C5A69";
const GRAPH_COLOR_PIE_1 = "679BB5";
const GRAPH_COLOR_PIE_2 = "695A3C";
const GRAPH_COLOR_PIE_3 = "B58E67";
const GRAPH_COLOR_PIE_4 = "8AA68A";
const GRAPH_COLOR_PIE_5 = "A4D2A6";
const GRAPH_COLOR_PIE_6 = "EEEEEE";
const GRAPH_COLOR_BAR_0 = "3B5AA9";
const GRAPH_COLOR_BAR_1 = "063E7E";
const GRAPH_COLOR_LINE = "063E7E";
const GRAPH_FONT_SIZE = 9;
const GRAPH_WIDTH = 1044;
const GRAPH_HEIGHT = 290;
/**
* @return Piwik_ImageGraph_API
*/
static public function getInstance()
{
if (self::$instance == null)
{
$c = __CLASS__;
self::$instance = new $c();
}
return self::$instance;
}
public function get( $idSite, $period, $date,
$apiModule, $apiAction,
$graphType = false, $outputType = false, $column = false, $showMetricTitle = true,
$width = false, $height = false, $fontSize = false, $aliasedGraph = true, $colors = false
)
{
// Health check - should we also test for GD2 only?
$extensions = @get_loaded_extensions();
if (!in_array('gd', $extensions))
{
throw new Exception("Error: To create graphs in Piwik, please enable GD php extension in php.ini, and restart your web server.");
}
// Parameters init
if(empty($width))
{
$width = self::GRAPH_WIDTH;
}
if(empty($height))
{
$height = self::GRAPH_HEIGHT;
}
if(empty($fontSize))
{
$fontSize = self::GRAPH_FONT_SIZE;
}
if(empty($graphType))
{
$graphType = self::GRAPH_TYPE_BASIC_LINE;
}
if(empty($outputType))
{
$outputType = self::GRAPH_OUTPUT_INLINE;
}
$colors = explode(",", $colors);
if(!is_array($colors))
{
$colors = array();
}
if(empty($showMetricTitle))
{
$showMetricTitle = false;
}
if(empty($aliasedGraph))
{
$aliasedGraph = false;
}
try
{
Piwik::checkUserHasViewAccess($idSite);
//Set default colors if needed
$count = -1;
$needCount = 0;
$constNameBase = "";
switch($graphType)
{
case self::GRAPH_TYPE_3D_PIE:
case self::GRAPH_TYPE_BASIC_PIE:
$needCount = 7;
$constNameBase = "GRAPH_COLOR_PIE_";
break;
case self::GRAPH_TYPE_BASIC_BAR:
$needCount = 2;
$constNameBase = "GRAPH_COLOR_BAR_";
break;
case self::GRAPH_TYPE_BASIC_LINE:
default:
$needCount = 1;
$constNameBase = "GRAPH_COLOR_LINE";
break;
}
while(++$count < $needCount)
{
if(!isset($colors[$count]))
{
if($needCount == 1)
{
$colors[$count] = $this->{$constNameBase};
}
else
{
$colors[$count] = $this->{$constNameBase.$count};
}
}
}
//Fetch the metadata for given api-action
$metadata = Piwik_API_API::getInstance()->getMetadata($idSite, $apiModule, $apiAction, $apiParameters = array(), $language = false, $period, $date);
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//If the metadata doesn´t provide any information about the dimension,
//the $abscissaColumn could only be the date-index
if(empty($metadata[0]["dimension"]))
{
$abscissaColumn = "date";
$abscissaVal = Piwik_Translate("Piwik_Date");
}
else
{
$abscissaColumn = "label";
$abscissaVal = $metadata[0]["dimension"];
}
$availableColumns = array_merge(array($abscissaColumn => $abscissaVal),
!empty($metadata[0]["metrics"]) ? $metadata[0]["metrics"] : array(),
!empty($metadata[0]["processedMetrics"]) ? $metadata[0]["processedMetrics"] : array(),
!empty($metadata[0]["metricsGoal"]) ? $metadata[0]["metricsGoal"] : array(),
!empty($metadata[0]["processedMetricsGoal"]) ? $metadata[0]["processedMetricsGoal"] : array()
);
if(empty($column))
{
$column = 'nb_visits';
if(empty($availableColumns[$column]))
{
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
}
}
//If the desired $ordinateColumn is not present, we have to throw an exception
if(empty($column) || empty($availableColumns[$column]))
{
throw new Exception(Piwik_Translate("ImageGraph_ColumnOrdinateMissing", $column));
}
$metricTitle = $showMetricTitle ? $availableColumns[$column] : false;
//Save original GET to reset after processing. Important for API-in-API-call
$origGET = $_GET;
//If a pie should be drawn the report should always be sorted and truncated,
if( $graphType == self::GRAPH_TYPE_3D_PIE ||
$graphType == self::GRAPH_TYPE_BASIC_PIE
)
{
$_GET["filter_sort_column"] = $column;
//if filter_truncate is less-equal than 5, we don´t have to set it
if(empty($_GET["filter_truncate"]) || $_GET["filter_truncate"] > 5)
$_GET["filter_truncate"] = 5;
}
//else if a bar- or line chart should be drawn and the report has a dimension,
//we should truncate if the dimension is unlimited
//We mustn´t sort the report, because this brakes the readability of the reports with limited dimension.
else if(($graphType == self::GRAPH_TYPE_BASIC_BAR ||
$graphType == self::GRAPH_TYPE_BASIC_LINE) &&
!empty($metadata[0]['dimension']) &&
empty($metadata[0]['constantRowsCount'])
)
{
$filterTruncateGET = Piwik_Common::getRequestVar('filter_truncate', false, 'int');
if($filterTruncateGET <= 0)
{
//if filter_truncate is less-equal than 24, we don´t have to set it
if(empty($_GET["filter_truncate"]) || $_GET["filter_truncate"] > 24)
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
}
}
//Fetch the report for given site, date, period and api-action
$report = Piwik_API_API::getInstance()->getProcessedReport( $idSite, $period, $date,
$apiModule, $apiAction
);
//Copy the required data into arrays
$abscissaSerie = $ordinateSerie = array();
$count = 0;
//If the report has no dimension we have one or many reports each with only one row within the reportdata.
if( empty($metadata[0]['dimension']) &&
($graphType == self::GRAPH_TYPE_BASIC_BAR ||
$graphType == self::GRAPH_TYPE_BASIC_LINE)
)
{
//We get one report with one row for any reports called with 'period=range',
//or all reports called with 'period!=range' and a non-dimensional date like 'date=yesterday'
if(get_class($report["reportData"]) == "Piwik_DataTable_Simple")
{
$row = $report["reportData"]->getRows();
$row = $row[0]->getColumns();
$this->setDataFromRowHelper($report, $column, $row, $count, $abscissaSerie, false, $ordinateSerie);
}
//We get many reports with one row if the request is called with 'period!=range' and a dimensional date like 'date=lastX'
else
{
$rowData = array();
$countTemp = 0;
foreach($report["reportData"]->metadata as $dateVal)
{
$rowData[$countTemp++] = array('rowId' => $dateVal['period']->getLocalizedShortString());
}
$countTemp = 0;
foreach($report["reportData"]->getArray() as $row)
{
$rowData[$countTemp++]['row'] = $row;
}
foreach($rowData as $rowD)
{
$row = $rowD['row']->getRows();
if(!array_key_exists(0, $row))
{
continue;
}
$row = $row[0]->getColumns();
$this->setDataFromRowHelper($report, $column, $row, $count, $abscissaSerie, array('rowId' => $rowD['rowId']), $ordinateSerie);
}
}
}
//Otherwise we have rows
else if(!empty($metadata[0]['dimension']))
{
if(!($report["reportData"] instanceof Piwik_DataTable))
{
throw new Exception("The graph cannot be drawn with the request 'date' and 'period' parameters.");
}
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
foreach($report["reportData"]->getRows() as $rowId => $row)
{
$row = $row->getColumns();
$this->setDataFromRowHelper($report, $column, $row, $count, $abscissaSerie, array('abscissaColumn' => $abscissaColumn), $ordinateSerie);
}
}
else
{
throw new Exception('Invalid $graphType for this API function.');
}
if($count == 0)
{
throw new Exception(Piwik_Translate("General_NoDataForGraph"));
}
//Reset GET to original values
$_GET = $origGET;
//Setup the graph
$graph = new Piwik_ImageGraph_ImageGraphObject($width, $height, $fontSize);
$graph->setData($abscissaSerie, $ordinateSerie, $availableColumns[$abscissaColumn], $availableColumns[$column], "", $metricTitle, $aliasedGraph);
switch($graphType)
{
case self::GRAPH_TYPE_BASIC_PIE:
$graph->printBasicPieGraph($colors[0], $colors[1], $colors[2], $colors[3], $colors[4], $colors[5], $colors[6]);
break;
case self::GRAPH_TYPE_3D_PIE:
$graph->print3dPieGraph($colors[0], $colors[1], $colors[2], $colors[3], $colors[4], $colors[5], $colors[6]);
break;
case self::GRAPH_TYPE_BASIC_BAR:
$graph->printBasicBarGraph($colors[0], $colors[1]);
break;
case self::GRAPH_TYPE_BASIC_LINE:
default:
$graph->printBasicLineGraph($colors[0]);
break;
}
} catch (Exception $e) {
$graph = new Piwik_ImageGraph_ImageGraphObject($width, $height, $fontSize);
$graph->printException($e);
}
//Decide, whether the img is saved temporarily or displayed in the browser
switch($outputType)
{
case self::GRAPH_OUTPUT_FILE:
switch($graphType)
{
case self::GRAPH_TYPE_BASIC_PIE:
$typeName = "BasicPie";
break;
case self::GRAPH_TYPE_3D_PIE:
$typeName = "3DPie";
break;
case self::GRAPH_TYPE_BASIC_BAR:
$typeName = "BasicBar";
break;
case self::GRAPH_TYPE_BASIC_LINE:
default:
$typeName = "BasicLine";
break;
}
$fileName = $typeName."_".$apiModule."_".$apiAction." $date $idSite.png";
$fileName = str_replace(array(" ","/"), "_", $fileName);
if(!Piwik_Common::isValidFilename($fileName))
{
throw new Exception("Error: Image graph filename '$fileName' is not valid.");
}
$path = PIWIK_INCLUDE_PATH."/tmp/".$fileName;
$graph->Render($path);
return $path;
case self::GRAPH_OUTPUT_INLINE:
default:
$graph->Stroke();
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
}
}
private function setDataFromRowHelper($report, $column, $rowData, &$count, &$abscissaSerie, $abscissaOptions, &$ordinateSerie)
{
if(!empty($abscissaOptions['rowId']))
{
$afterDay = stripos($abscissaOptions['rowId'], " ");
if(is_numeric($afterDay) && $afterDay > 0)
{
$afterDay = substr($abscissaOptions['rowId'], 0, $afterDay);
}
$abscissaSerie[$count] = Piwik_Common::unsanitizeInputValue($abscissaOptions['rowId']);
}
else if(!empty($abscissaOptions['abscissaColumn']))
{
$abscissaSerie[$count] = Piwik_Common::unsanitizeInputValue($rowData[$abscissaOptions['abscissaColumn']]);
}
else
{
$abscissaSerie[$count] = $report["prettyDate"];
}
$ordinateSerie[$count] = @str_replace("%", "", @str_replace(",", ".", $rowData[$column]));
if(@sscanf($ordinateSerie[$count], "%02d:%02d:%02d", $hour, $min, $sec) === 3)
{
$ordinateSerie[$count] = ((($hour*60) + $min) * 60) + $sec;
}
$count++;
}
}