1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Echoes an image tag of Google Charts map from sorted array of 'country_code' => 'number of visits' (sort by DESC) |
|
|
|
|
5
|
|
|
* |
6
|
|
|
*/ |
7
|
|
|
function yourls_stats_countries_map( $countries, $id = null ) { |
8
|
|
|
|
9
|
|
|
yourls_do_action( 'pre_stats_countries_map' ); |
10
|
|
|
|
11
|
|
|
// if $id is null then assign a random string |
12
|
|
|
if( $id === null ) |
13
|
|
|
$id = uniqid ( 'yourls_stats_map_' ); |
14
|
|
|
|
15
|
|
|
$data = array_merge( array( 'Country' => 'Hits' ), $countries ); |
16
|
|
|
$data = yourls_google_array_to_data_table( $data ); |
17
|
|
|
|
18
|
|
|
$options = array( |
19
|
|
|
'backgroundColor' => "white", |
|
|
|
|
20
|
|
|
'colorAxis' => "{colors:['A8D0ED','99C4E4','8AB8DB','7BACD2','6BA1C9','5C95C0','4D89B7','3E7DAE','2E72A5','1F669C']}", |
|
|
|
|
21
|
|
|
'width' => "550", |
|
|
|
|
22
|
|
|
'height' => "340", |
|
|
|
|
23
|
|
|
'theme' => 'maximized' |
|
|
|
|
24
|
|
|
); |
|
|
|
|
25
|
|
|
$options = yourls_apply_filter( 'stats_countries_map_options', $options ); |
26
|
|
|
|
27
|
|
|
$map = yourls_google_viz_code( 'GeoChart', $data, $options, $id ); |
28
|
|
|
|
29
|
|
|
echo yourls_apply_filter( 'stats_countries_map', $map, $countries, $options, $id ); |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Echoes an image tag of Google Charts pie from sorted array of 'data' => 'value' (sort by DESC). Optional $limit = (integer) limit list of X first countries, sorted by most visits |
|
|
|
|
34
|
|
|
* |
35
|
|
|
*/ |
36
|
|
|
function yourls_stats_pie( $data, $limit = 10, $size = '340x220', $id = null ) { |
37
|
|
|
|
38
|
|
|
yourls_do_action( 'pre_stats_pie' ); |
39
|
|
|
|
40
|
|
|
// if $id is null then assign a random string |
41
|
|
|
if( $id === null ) |
42
|
|
|
$id = uniqid ( 'yourls_stats_pie_' ); |
43
|
|
|
|
44
|
|
|
// Trim array: $limit first item + the sum of all others |
45
|
|
|
if ( count( $data ) > $limit ) { |
46
|
|
|
$i= 0; |
47
|
|
|
$trim_data = array( 'Others' => 0 ); |
48
|
|
|
foreach( $data as $item=>$value ) { |
49
|
|
|
$i++; |
50
|
|
|
if( $i <= $limit ) { |
51
|
|
|
$trim_data[$item] = $value; |
52
|
|
|
} else { |
53
|
|
|
$trim_data['Others'] += $value; |
54
|
|
|
} |
55
|
|
|
} |
56
|
|
|
$data = $trim_data; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
// Scale items |
60
|
|
|
$_data = yourls_scale_data( $data ); |
61
|
|
|
|
62
|
|
|
list($width, $height) = explode( 'x', $size ); |
63
|
|
|
|
64
|
|
|
$options = array( |
65
|
|
|
'theme' => 'maximized', |
|
|
|
|
66
|
|
|
'width' => $width, |
|
|
|
|
67
|
|
|
'height' => $height, |
|
|
|
|
68
|
|
|
'colors' => "['A8D0ED','99C4E4','8AB8DB','7BACD2','6BA1C9','5C95C0','4D89B7','3E7DAE','2E72A5','1F669C']", |
|
|
|
|
69
|
|
|
'legend' => 'none', |
|
|
|
|
70
|
|
|
'chartArea' => '{top: "5%", height: "90%"}', |
|
|
|
|
71
|
|
|
'pieSliceText' => 'label', |
72
|
|
|
); |
|
|
|
|
73
|
|
|
$options = yourls_apply_filter( 'stats_pie_options', $options ); |
74
|
|
|
|
75
|
|
|
$script_data = array_merge( array( 'Country' => 'Value' ), $_data ); |
76
|
|
|
$script_data = yourls_google_array_to_data_table( $script_data ); |
77
|
|
|
|
78
|
|
|
$pie = yourls_google_viz_code( 'PieChart', $script_data, $options, $id ); |
79
|
|
|
|
80
|
|
|
echo yourls_apply_filter( 'stats_pie', $pie, $data, $limit, $size, $options, $id ); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Build a list of all daily values between d1/m1/y1 to d2/m2/y2. |
85
|
|
|
* |
86
|
|
|
*/ |
87
|
|
|
function yourls_build_list_of_days( $dates ) { |
88
|
|
|
/* Say we have an array like: |
|
|
|
|
89
|
|
|
$dates = array ( |
90
|
|
|
2009 => array ( |
91
|
|
|
'08' => array ( |
92
|
|
|
29 => 15, |
93
|
|
|
30 => 5, |
94
|
|
|
), |
95
|
|
|
'09' => array ( |
96
|
|
|
'02' => 3, |
97
|
|
|
'03' => 5, |
98
|
|
|
'04' => 2, |
99
|
|
|
'05' => 99, |
100
|
|
|
), |
101
|
|
|
), |
102
|
|
|
) |
103
|
|
|
*/ |
104
|
|
|
|
105
|
|
|
if( !$dates ) |
106
|
|
|
return array(); |
107
|
|
|
|
108
|
|
|
// Get first & last years from our range. In our example: 2009 & 2009 |
109
|
|
|
$first_year = key( $dates ); |
110
|
|
|
$_keys = array_keys( $dates ); |
111
|
|
|
$last_year = end( $_keys ); |
112
|
|
|
reset( $dates ); |
113
|
|
|
|
114
|
|
|
// Get first & last months from our range. In our example: 08 & 09 |
115
|
|
|
$first_month = key( $dates[ $first_year ] ); |
116
|
|
|
$_keys = array_keys( $dates[ $last_year ] ); |
117
|
|
|
$last_month = end( $_keys ); |
118
|
|
|
reset( $dates ); |
119
|
|
|
|
120
|
|
|
// Get first & last days from our range. In our example: 29 & 05 |
121
|
|
|
$first_day = key( $dates[ $first_year ][ $first_month ] ); |
122
|
|
|
$_keys = array_keys( $dates[ $last_year ][ $last_month ] ); |
123
|
|
|
$last_day = end( $_keys ); |
124
|
|
|
|
125
|
|
|
unset( $_keys ); |
126
|
|
|
|
127
|
|
|
// Now build a list of all years (2009), month (08 & 09) and days (all from 2009-08-29 to 2009-09-05) |
|
|
|
|
128
|
|
|
$list_of_years = array(); |
129
|
|
|
$list_of_months = array(); |
130
|
|
|
$list_of_days = array(); |
131
|
|
|
for ( $year = $first_year; $year <= $last_year; $year++ ) { |
132
|
|
|
$_year = sprintf( '%04d', $year ); |
133
|
|
|
$list_of_years[ $_year ] = $_year; |
134
|
|
|
$current_first_month = ( $year == $first_year ? $first_month : '01' ); |
|
|
|
|
135
|
|
|
$current_last_month = ( $year == $last_year ? $last_month : '12' ); |
|
|
|
|
136
|
|
|
for ( $month = $current_first_month; $month <= $current_last_month; $month++ ) { |
137
|
|
|
$_month = sprintf( '%02d', $month ); |
138
|
|
|
$list_of_months[ $_month ] = $_month; |
139
|
|
|
$current_first_day = ( $year == $first_year && $month == $first_month ? $first_day : '01' ); |
|
|
|
|
140
|
|
|
$current_last_day = ( $year == $last_year && $month == $last_month ? $last_day : yourls_days_in_month( $month, $year) ); |
|
|
|
|
141
|
|
|
for ( $day = $current_first_day; $day <= $current_last_day; $day++ ) { |
142
|
|
|
$day = sprintf( '%02d', $day ); |
143
|
|
|
$key = date( 'M d, Y', mktime( 0, 0, 0, $_month, $day, $_year ) ); |
|
|
|
|
144
|
|
|
$list_of_days[ $key ] = isset( $dates[$_year][$_month][$day] ) ? $dates[$_year][$_month][$day] : 0; |
|
|
|
|
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
return array( |
150
|
|
|
'list_of_days' => $list_of_days, |
151
|
|
|
'list_of_months' => $list_of_months, |
152
|
|
|
'list_of_years' => $list_of_years, |
153
|
|
|
); |
|
|
|
|
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Echoes an image tag of Google Charts line graph from array of values (eg 'number of clicks'). |
158
|
|
|
* |
159
|
|
|
* $legend1_list & legend2_list are values used for the 2 x-axis labels. $id is an HTML/JS id |
160
|
|
|
* |
161
|
|
|
*/ |
162
|
|
|
function yourls_stats_line( $values, $id = null ) { |
163
|
|
|
|
164
|
|
|
yourls_do_action( 'pre_stats_line' ); |
165
|
|
|
|
166
|
|
|
// if $id is null then assign a random string |
167
|
|
|
if( $id === null ) |
168
|
|
|
$id = uniqid ( 'yourls_stats_line_' ); |
169
|
|
|
|
170
|
|
|
// If we have only 1 day of data, prepend a fake day with 0 hits for a prettier graph |
171
|
|
|
if ( count( $values ) == 1 ) |
|
|
|
|
172
|
|
|
array_unshift( $values, 0 ); |
173
|
|
|
|
174
|
|
|
// Keep only a subset of values to keep graph smooth |
175
|
|
|
$values = yourls_array_granularity( $values, 30 ); |
176
|
|
|
|
177
|
|
|
$data = array_merge( array( 'Time' => 'Hits' ), $values ); |
178
|
|
|
$data = yourls_google_array_to_data_table( $data ); |
179
|
|
|
|
180
|
|
|
$options = array( |
181
|
|
|
"legend" => "none", |
|
|
|
|
182
|
|
|
"pointSize" => "3", |
|
|
|
|
183
|
|
|
"theme" => "maximized", |
|
|
|
|
184
|
|
|
"curveType" => "function", |
|
|
|
|
185
|
|
|
"width" => 430, |
|
|
|
|
186
|
|
|
"height" => 220, |
|
|
|
|
187
|
|
|
"hAxis" => "{minTextSpacing: 80, maxTextLines: 1, maxAlternation: 1}", |
|
|
|
|
188
|
|
|
"vAxis" => "{minValue: 0, format: '#'}", |
|
|
|
|
189
|
|
|
"colors" => "['#2a85b3']", |
|
|
|
|
190
|
|
|
); |
|
|
|
|
191
|
|
|
$options = yourls_apply_filter( 'stats_line_options', $options ); |
192
|
|
|
|
193
|
|
|
$lineChart = yourls_google_viz_code( 'LineChart', $data, $options, $id ); |
194
|
|
|
|
195
|
|
|
echo yourls_apply_filter( 'stats_line', $lineChart, $values, $options, $id ); |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Return the number of days in a month. From php.net, used if PHP built without calendar functions |
200
|
|
|
* |
201
|
|
|
*/ |
202
|
|
|
function yourls_days_in_month( $month, $year ) { |
203
|
|
|
// calculate number of days in a month |
204
|
|
|
return $month == 2 ? ( $year % 4 ? 28 : ( $year % 100 ? 29 : ( $year % 400 ? 28 : 29 ) ) ) : ( ( $month - 1 ) % 7 % 2 ? 30 : 31 ); |
|
|
|
|
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* Get max value from date array of 'Aug 12, 2012' = '1337' |
209
|
|
|
* |
210
|
|
|
*/ |
211
|
|
|
function yourls_stats_get_best_day( $list_of_days ) { |
212
|
|
|
$max = max( $list_of_days ); |
213
|
|
|
foreach( $list_of_days as $k=>$v ) { |
214
|
|
|
if ( $v == $max ) |
|
|
|
|
215
|
|
|
return array( 'day' => $k, 'max' => $max ); |
|
|
|
|
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* Return domain of a URL |
221
|
|
|
* |
222
|
|
|
*/ |
223
|
|
|
function yourls_get_domain( $url, $include_scheme = false ) { |
224
|
|
|
$parse = @parse_url( $url ); // Hiding ugly stuff coming from malformed referrer URLs |
225
|
|
|
|
226
|
|
|
// Get host & scheme. Fall back to path if not found. |
227
|
|
|
$host = isset( $parse['host'] ) ? $parse['host'] : ''; |
228
|
|
|
$scheme = isset( $parse['scheme'] ) ? $parse['scheme'] : ''; |
229
|
|
|
$path = isset( $parse['path'] ) ? $parse['path'] : ''; |
230
|
|
|
if( !$host ) |
231
|
|
|
$host = $path; |
232
|
|
|
|
233
|
|
|
if ( $include_scheme && $scheme ) |
234
|
|
|
$host = $scheme.'://'.$host; |
235
|
|
|
|
236
|
|
|
return $host; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Return favicon URL |
241
|
|
|
* |
242
|
|
|
*/ |
243
|
|
|
function yourls_get_favicon_url( $url ) { |
244
|
|
|
return yourls_match_current_protocol( '//www.google.com/s2/favicons?domain=' . yourls_get_domain( $url, false ) ); |
|
|
|
|
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Scale array of data from 0 to 100 max |
249
|
|
|
* |
250
|
|
|
*/ |
251
|
|
|
function yourls_scale_data( $data ) { |
252
|
|
|
$max = max( $data ); |
253
|
|
|
if( $max > 100 ) { |
254
|
|
|
foreach( $data as $k=>$v ) { |
255
|
|
|
$data[$k] = intval( $v / $max * 100 ); |
256
|
|
|
} |
257
|
|
|
} |
258
|
|
|
return $data; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* Tweak granularity of array $array: keep only $grain values. This make less accurate but less messy graphs when too much values. See http://code.google.com/apis/chart/formats.html#granularity |
|
|
|
|
263
|
|
|
* |
264
|
|
|
*/ |
265
|
|
|
function yourls_array_granularity( $array, $grain = 100, $preserve_max = true ) { |
266
|
|
|
if ( count( $array ) > $grain ) { |
267
|
|
|
$max = max( $array ); |
268
|
|
|
$step = intval( count( $array ) / $grain ); |
269
|
|
|
$i = 0; |
270
|
|
|
// Loop through each item and unset except every $step (optional preserve the max value) |
271
|
|
|
foreach( $array as $k=>$v ) { |
272
|
|
|
$i++; |
273
|
|
|
if ( $i % $step != 0 ) { |
|
|
|
|
274
|
|
|
if ( $preserve_max == false ) { |
|
|
|
|
275
|
|
|
unset( $array[$k] ); |
276
|
|
|
} else { |
277
|
|
|
if ( $v < $max ) |
278
|
|
|
unset( $array[$k] ); |
279
|
|
|
} |
280
|
|
|
} |
281
|
|
|
} |
282
|
|
|
} |
283
|
|
|
return $array; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* Transform data array to data table for Google API |
288
|
|
|
* |
289
|
|
|
*/ |
290
|
|
|
function yourls_google_array_to_data_table( $data ){ |
|
|
|
|
291
|
|
|
$str = "var data = google.visualization.arrayToDataTable([\n"; |
292
|
|
|
foreach( $data as $label => $values ){ |
293
|
|
|
if( !is_array( $values ) ) { |
294
|
|
|
$values = array( $values ); |
295
|
|
|
} |
296
|
|
|
$str .= "\t['$label',"; |
|
|
|
|
297
|
|
|
foreach( $values as $value ){ |
298
|
|
|
if( !is_numeric( $value ) && strpos( $value, '[' ) !== 0 && strpos( $value, '{' ) !== 0 ) { |
299
|
|
|
$value = "'$value'"; |
|
|
|
|
300
|
|
|
} |
301
|
|
|
$str .= "$value"; |
|
|
|
|
302
|
|
|
} |
303
|
|
|
$str .= "],\n"; |
304
|
|
|
} |
305
|
|
|
$str = substr( $str, 0, -2 ) . "\n"; // remove the trailing comma/return, reappend the return |
306
|
|
|
$str .= "]);\n"; // wrap it up |
307
|
|
|
return $str; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Return javascript code that will display the Google Chart |
312
|
|
|
* |
313
|
|
|
*/ |
314
|
|
|
function yourls_google_viz_code( $graph_type, $data, $options, $id ) { |
315
|
|
|
$function_name = 'yourls_graph' . $id; |
316
|
|
|
$code = "\n<script id=\"$function_name\" type=\"text/javascript\">\n"; |
|
|
|
|
317
|
|
|
$code .= "function $function_name() { \n"; |
|
|
|
|
318
|
|
|
|
319
|
|
|
$code .= "$data\n"; |
|
|
|
|
320
|
|
|
|
321
|
|
|
$code .= "var options = {\n"; |
322
|
|
|
foreach( $options as $field => $value ) { |
323
|
|
|
if( !is_numeric( $value ) && strpos( $value, '[' ) !== 0 && strpos( $value, '{' ) !== 0 ) { |
324
|
|
|
$value = "\"$value\""; |
|
|
|
|
325
|
|
|
} |
326
|
|
|
$code .= "\t'$field': $value,\n"; |
|
|
|
|
327
|
|
|
} |
328
|
|
|
$code = substr( $code, 0, -2 ) . "\n"; // remove the trailing comma/return, reappend the return |
329
|
|
|
$code .= "\t}\n"; |
330
|
|
|
|
331
|
|
|
$code .= "new google.visualization.$graph_type( document.getElementById('visualization_$id') ).draw( data, options );"; |
|
|
|
|
332
|
|
|
$code .= "}\n"; |
333
|
|
|
$code .= "google.setOnLoadCallback( $function_name );\n"; |
|
|
|
|
334
|
|
|
$code .= "</script>\n"; |
335
|
|
|
$code .= "<div id=\"visualization_$id\"></div>\n"; |
|
|
|
|
336
|
|
|
|
337
|
|
|
return $code; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
|
Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.