Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
141 / 141 |
|
100.00% |
16 / 16 |
CRAP | |
100.00% |
1 / 1 |
Visitors | |
100.00% |
141 / 141 |
|
100.00% |
16 / 16 |
36 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
create | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
1 | |||
track | |
100.00% |
30 / 30 |
|
100.00% |
1 / 1 |
3 | |||
getDefaultFields | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
doUrl | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
doVisitorId | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
doReferrer | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
doDeviceDetect | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
doCustom | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
6 | |||
doCounter | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
5 | |||
doConfig | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
doPerformance | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
1 | |||
doLocalTime | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
doTime | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
doLanguage | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
doLocation | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
5 |
1 | <?php |
2 | |
3 | namespace Drupal\visitors\Controller; |
4 | |
5 | use Drupal\Core\Controller\ControllerBase; |
6 | use Drupal\visitors\VisitorsTrackerInterface; |
7 | use Symfony\Component\DependencyInjection\ContainerInterface; |
8 | use Symfony\Component\HttpFoundation\Response; |
9 | use Drupal\visitors_geoip\VisitorsGeoIpInterface; |
10 | use Drupal\visitors\VisitorsCounterInterface; |
11 | use Drupal\visitors\VisitorsCookieInterface; |
12 | use Drupal\visitors\VisitorsDeviceInterface; |
13 | use Drupal\visitors\VisitorsLocationInterface; |
14 | use Drupal\Component\Datetime\TimeInterface; |
15 | use Drupal\Core\Config\ConfigFactoryInterface; |
16 | use Psr\Log\LoggerInterface; |
17 | use Symfony\Component\HttpFoundation\Request; |
18 | use Symfony\Component\HttpFoundation\ServerBag; |
19 | |
20 | /** |
21 | * Visitors tracking controller. |
22 | */ |
23 | final class Visitors extends ControllerBase { |
24 | |
25 | /** |
26 | * The time service. |
27 | * |
28 | * @var \Drupal\Component\Datetime\TimeInterface |
29 | */ |
30 | protected $time; |
31 | |
32 | /** |
33 | * The visitors settings. |
34 | * |
35 | * @var \Drupal\Core\Config\Config |
36 | */ |
37 | protected $settings; |
38 | |
39 | /** |
40 | * The logger service. |
41 | * |
42 | * @var \Psr\Log\LoggerInterface |
43 | */ |
44 | protected $logger; |
45 | |
46 | /** |
47 | * The counter service. |
48 | * |
49 | * @var \Drupal\visitors\VisitorsCounterInterface |
50 | */ |
51 | protected $counter; |
52 | |
53 | /** |
54 | * The cookie service. |
55 | * |
56 | * @var \Drupal\visitors\VisitorsCookieInterface |
57 | */ |
58 | protected $cookie; |
59 | |
60 | /** |
61 | * The device service. |
62 | * |
63 | * @var \Drupal\visitors\VisitorsDeviceInterface |
64 | */ |
65 | protected $device; |
66 | |
67 | /** |
68 | * The location service. |
69 | * |
70 | * @var \Drupal\visitors\VisitorsLocationInterface |
71 | */ |
72 | protected $location; |
73 | |
74 | /** |
75 | * The tracker service. |
76 | * |
77 | * @var \Drupal\visitors\VisitorsTrackerInterface |
78 | */ |
79 | protected $tracker; |
80 | |
81 | /** |
82 | * The geoip service. |
83 | * |
84 | * @var \Drupal\visitors_geoip\VisitorsGeoIpInterface|null |
85 | */ |
86 | protected $geoip; |
87 | |
88 | /** |
89 | * Visitor tracker. |
90 | * |
91 | * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory |
92 | * The config factory service. |
93 | * @param \Drupal\Component\Datetime\TimeInterface $time |
94 | * The time service. |
95 | * @param \Psr\Log\LoggerInterface $logger |
96 | * The logger service. |
97 | * @param \Drupal\visitors\VisitorsCounterInterface $counter |
98 | * The counter service. |
99 | * @param \Drupal\visitors\VisitorsCookieInterface $cookie |
100 | * The cookie service. |
101 | * @param \Drupal\visitors\VisitorsDeviceInterface $device |
102 | * The device service. |
103 | * @param \Drupal\visitors\VisitorsLocationInterface $location |
104 | * The location service. |
105 | * @param \Drupal\visitors\VisitorsTrackerInterface $tracker |
106 | * The date service. |
107 | * @param \Drupal\visitors_geoip\VisitorsGeoIpInterface|null $geoip |
108 | * The geoip service. |
109 | */ |
110 | public function __construct( |
111 | ConfigFactoryInterface $config_factory, |
112 | TimeInterface $time, |
113 | LoggerInterface $logger, |
114 | VisitorsCounterInterface $counter, |
115 | VisitorsCookieInterface $cookie, |
116 | VisitorsDeviceInterface $device, |
117 | VisitorsLocationInterface $location, |
118 | VisitorsTrackerInterface $tracker, |
119 | ?VisitorsGeoIpInterface $geoip = NULL, |
120 | ) { |
121 | |
122 | $this->settings = $config_factory->get('visitors.config'); |
123 | |
124 | $this->time = $time; |
125 | $this->logger = $logger; |
126 | $this->counter = $counter; |
127 | $this->cookie = $cookie; |
128 | $this->device = $device; |
129 | $this->location = $location; |
130 | $this->tracker = $tracker; |
131 | $this->geoip = $geoip; |
132 | } |
133 | |
134 | /** |
135 | * {@inheritdoc} |
136 | */ |
137 | public static function create(ContainerInterface $container): Visitors { |
138 | return new self( |
139 | $container->get('config.factory'), |
140 | $container->get('datetime.time'), |
141 | $container->get('logger.channel.visitors'), |
142 | $container->get('visitors.counter'), |
143 | $container->get('visitors.cookie'), |
144 | $container->get('visitors.device'), |
145 | $container->get('visitors.location'), |
146 | $container->get('visitors.tracker'), |
147 | $container->get('visitors_geoip.lookup', ContainerInterface::NULL_ON_INVALID_REFERENCE), |
148 | ); |
149 | |
150 | } |
151 | |
152 | /** |
153 | * Tracks visits. |
154 | */ |
155 | public function track(Request $request): Response { |
156 | $response = new Response(); |
157 | $response->setStatusCode(Response::HTTP_NO_CONTENT); |
158 | |
159 | $server = $request->server; |
160 | $query = $request->query->all(); |
161 | |
162 | $fields = $this->getDefaultFields(); |
163 | |
164 | $ip = $request->getClientIp(); |
165 | $fields['visitors_ip'] = $ip; |
166 | $fields['visitors_uid'] = $query['uid'] ?? 0; |
167 | $fields['visitors_title'] = $query['action_name'] ?? ''; |
168 | $fields['visitors_user_agent'] = $server->get('HTTP_USER_AGENT', ''); |
169 | |
170 | $bot_retention_log = $this->settings->get('bot_retention_log'); |
171 | $discard_bot = ($bot_retention_log == -1); |
172 | |
173 | $this->doDeviceDetect($fields, $server); |
174 | if ($discard_bot && $fields['bot']) { |
175 | return $response; |
176 | } |
177 | |
178 | $this->doVisitorId($fields, $query); |
179 | $this->doUrl($fields, $query); |
180 | $this->doReferrer($fields, $query); |
181 | |
182 | $cvar = $query['_cvar'] ?? NULL; |
183 | $this->doCustom($fields, $cvar); |
184 | |
185 | $this->doCounter($fields, $cvar); |
186 | |
187 | $this->doConfig($fields, $query); |
188 | $this->doPerformance($fields, $query); |
189 | |
190 | $this->doLocalTime($fields, $query); |
191 | $this->doTime($fields); |
192 | |
193 | $languages = $request->getLanguages() ?? []; |
194 | $this->doLanguage($fields, $languages); |
195 | $this->doLocation($fields, $ip, $languages); |
196 | |
197 | // Write fields to database. |
198 | $this->tracker->writeLog($fields); |
199 | |
200 | return $response; |
201 | } |
202 | |
203 | /** |
204 | * Get the default fields. |
205 | * |
206 | * @return array |
207 | * The default fields. |
208 | */ |
209 | protected function getDefaultFields(): array { |
210 | $fields = [ |
211 | 'bot' => 0, |
212 | ]; |
213 | |
214 | return $fields; |
215 | } |
216 | |
217 | /** |
218 | * Detects the visitor url. |
219 | * |
220 | * @param string[] $fields |
221 | * The fields array. |
222 | * @param string[] $query |
223 | * The query array. |
224 | */ |
225 | protected function doUrl(array &$fields, array $query) { |
226 | $url = $query['url'] ?? ''; |
227 | $fields['visitors_url'] = $url; |
228 | } |
229 | |
230 | /** |
231 | * Detects the visitor id. |
232 | * |
233 | * @param string[] $fields |
234 | * The fields array. |
235 | * @param string[] $query |
236 | * The query array. |
237 | */ |
238 | protected function doVisitorId(array &$fields, array $query) { |
239 | $visitor_id = $query['_id'] ?? $this->cookie->getId(); |
240 | $fields['visitor_id'] = $visitor_id; |
241 | } |
242 | |
243 | /** |
244 | * Detects the referrer. |
245 | * |
246 | * @param string[] $fields |
247 | * The fields array. |
248 | * @param string[] $query |
249 | * The query array. |
250 | */ |
251 | protected function doReferrer(array &$fields, array $query) { |
252 | $referrer = $query['urlref'] ?? ''; |
253 | $fields['visitors_referer'] = $referrer; |
254 | } |
255 | |
256 | /** |
257 | * Detects the device. |
258 | * |
259 | * @param string[] $fields |
260 | * The fields array. |
261 | * @param \Symfony\Component\HttpFoundation\ServerBag $server |
262 | * The server array. |
263 | */ |
264 | protected function doDeviceDetect(array &$fields, ServerBag $server) { |
265 | if (!$this->device->hasLibrary()) { |
266 | return NULL; |
267 | } |
268 | |
269 | $user_agent = $server->get('HTTP_USER_AGENT', ''); |
270 | $this->device->doDeviceFields($fields, $user_agent, $server->all()); |
271 | |
272 | } |
273 | |
274 | /** |
275 | * Set the fields with data in the custom variable. |
276 | */ |
277 | protected function doCustom(array &$fields, $cvar = NULL) { |
278 | $path = ''; |
279 | $route = ''; |
280 | $server = NULL; |
281 | |
282 | if (!is_null($cvar)) { |
283 | $custom = json_decode($cvar); |
284 | foreach ($custom as $c) { |
285 | if ($c[0] == 'path') { |
286 | $path = $c[1]; |
287 | } |
288 | if ($c[0] == 'route') { |
289 | $route = $c[1]; |
290 | } |
291 | if ($c[0] == 'server') { |
292 | $server = $c[1]; |
293 | } |
294 | } |
295 | } |
296 | |
297 | $fields['visitors_path'] = $path; |
298 | $fields['route'] = $route; |
299 | $fields['server'] = $server; |
300 | |
301 | } |
302 | |
303 | /** |
304 | * Record the view of the entity. |
305 | */ |
306 | protected function doCounter(array &$fields, $cvar = NULL) { |
307 | |
308 | $viewed = NULL; |
309 | if (!is_null($cvar)) { |
310 | $custom = json_decode($cvar); |
311 | foreach ($custom as $c) { |
312 | if ($c[0] == 'viewed') { |
313 | $viewed = $c[1]; |
314 | } |
315 | } |
316 | } |
317 | |
318 | if (!is_null($viewed)) { |
319 | [$type, $id] = explode(':', $viewed); |
320 | $this->counter->recordView($type, $id); |
321 | } |
322 | } |
323 | |
324 | /** |
325 | * Set the configuration fields. |
326 | */ |
327 | protected function doConfig(array &$fields, array $query) { |
328 | |
329 | $fields['config_resolution'] = $query['res'] ?? NULL; |
330 | $fields['config_pdf'] = $query['pdf'] ?? NULL; |
331 | $fields['config_flash'] = $query['fla'] ?? NULL; |
332 | $fields['config_java'] = $query['java'] ?? NULL; |
333 | $fields['config_quicktime'] = $query['qt'] ?? NULL; |
334 | $fields['config_realplayer'] = $query['realp'] ?? NULL; |
335 | $fields['config_windowsmedia'] = $query['wma'] ?? NULL; |
336 | $fields['config_silverlight'] = $query['ag'] ?? NULL; |
337 | $fields['config_cookie'] = $query['cookie'] ?? NULL; |
338 | } |
339 | |
340 | /** |
341 | * Set the performance fields. |
342 | */ |
343 | protected function doPerformance(array &$fields, array $query) { |
344 | $fields['pf_network'] = $query['pf_net'] ?? NULL; |
345 | $fields['pf_server'] = $query['pf_srv'] ?? NULL; |
346 | $fields['pf_transfer'] = $query['pf_tfr'] ?? NULL; |
347 | $fields['pf_dom_processing'] = $query['pf_dm1'] ?? NULL; |
348 | $fields['pf_dom_complete'] = $query['pf_dm2'] ?? NULL; |
349 | $fields['pf_on_load'] = $query['pf_onl'] ?? NULL; |
350 | |
351 | $fields['pf_total'] = ($fields['pf_network'] ?? 0) |
352 | + ($fields['pf_server'] ?? 0) |
353 | + ($fields['pf_transfer'] ?? 0) |
354 | + ($fields['pf_dom_processing'] ?? 0) |
355 | + ($fields['pf_dom_complete'] ?? 0) |
356 | + ($fields['pf_on_load'] ?? 0); |
357 | } |
358 | |
359 | /** |
360 | * Set the visitor's local time field. |
361 | */ |
362 | protected function doLocalTime(array &$fields, array $query) { |
363 | $hours = $query['h'] ?? NULL; |
364 | $minutes = $query['m'] ?? NULL; |
365 | $seconds = $query['s'] ?? NULL; |
366 | |
367 | $has_null = is_null($hours) || is_null($minutes) || is_null($seconds); |
368 | if ($has_null) { |
369 | return NULL; |
370 | } |
371 | |
372 | $time = $hours * 3600 + $minutes * 60 + $seconds; |
373 | |
374 | $fields['visitor_localtime'] = $time; |
375 | } |
376 | |
377 | /** |
378 | * Set the server time field. |
379 | */ |
380 | protected function doTime(array &$fields) { |
381 | $fields['visitors_date_time'] = $this->time->getRequestTime(); |
382 | } |
383 | |
384 | /** |
385 | * Set the language fields. |
386 | */ |
387 | protected function doLanguage(array &$fields, array $languages) { |
388 | if (empty($languages)) { |
389 | return NULL; |
390 | } |
391 | |
392 | $language = $languages[0] ?? ''; |
393 | $lang = explode('_', $language); |
394 | $fields['language'] = $lang[0]; |
395 | } |
396 | |
397 | /** |
398 | * Set the location fields. |
399 | */ |
400 | protected function doLocation(array &$fields, $ip_address, $languages) { |
401 | if (!empty($languages)) { |
402 | $language = $languages[0] ?? ''; |
403 | $lang = explode('_', $language); |
404 | $country_code = strtoupper($lang[1] ?? ''); |
405 | if ($this->location->isValidCountryCode($country_code)) { |
406 | $fields['location_country'] = $country_code; |
407 | $fields['location_continent'] = $this->location->getContinent($country_code); |
408 | } |
409 | } |
410 | |
411 | if (!$this->geoip) { |
412 | return NULL; |
413 | } |
414 | |
415 | /** @var \GeoIp2\Model\City|null $location */ |
416 | $location = $this->geoip->city($ip_address); |
417 | if (!$location) { |
418 | return NULL; |
419 | } |
420 | |
421 | $fields['location_continent'] = $location->continent->code; |
422 | $fields['location_country'] = $location->country->isoCode; |
423 | $fields['location_region'] = $location->subdivisions[0]->isoCode; |
424 | $fields['location_city'] = $location->city->names['en']; |
425 | $fields['location_latitude'] = $location->location->latitude; |
426 | $fields['location_longitude'] = $location->location->longitude; |
427 | |
428 | } |
429 | |
430 | } |