Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
1155 / 1155
100.00% covered (success)
100.00%
13 / 13
CRAP
n/a
0 / 0
visitors_help
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
visitors_cron
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
visitors_page_attachments
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
visitors_form_user_form_alter
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
5
visitors_user_profile_form_submit
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
visitors_node_links_alter
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
4
visitors_entity_delete
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
visitors_ranking
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
3
visitors_views_data
100.00% covered (success)
100.00%
1012 / 1012
100.00% covered (success)
100.00%
1 / 1
7
visitors_token_info
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
2
visitors_tokens
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
12
visitors_form_alter
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
visitors_preprocess_html
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2
3/**
4 * @file
5 * Logs visitors for your site.
6 */
7
8use Drupal\Core\Entity\ContentEntityInterface;
9use Drupal\Core\Entity\EntityInterface;
10use Drupal\Core\Form\FormStateInterface;
11use Drupal\Core\Render\BubbleableMetadata;
12use Drupal\node\NodeInterface;
13use Drupal\visitors\VisitorsVisibilityInterface;
14use Drupal\visitors\Plugin\VisitorsEvent\Form;
15
16/**
17 * Implements hook_help().
18 */
19function visitors_help($route_name, $route_match) {
20  switch ($route_name) {
21    case 'help.page.visitors':
22      $help = '<p><a href="https://git.drupalcode.org/project/visitors/-/commits/3.0.x"><img alt="coverage report" src="https://git.drupalcode.org/project/visitors/badges/3.0.x/coverage.svg" /></a> &nbsp;';
23      $help .= '<a href="https://git.drupalcode.org/project/visitors/-/commits/3.0.x"><img alt="pipeline status" src="https://git.drupalcode.org/project/visitors/badges/3.0.x/pipeline.svg" /></a> &nbsp;';
24      $help .= '<a href="https://www.drupal.org/project/visitors">Homepage</a> &nbsp;';
25      $help .= '<a href="https://www.drupal.org/project/issues/visitors?version=any_3.">Issues</a></p>';
26      $help .= '<p>'
27        . t('The Visitors module logs all visitors to your site and provides various statistics about them.')
28        . '</p>';
29
30      return [
31        '#title' => t('Visitors'),
32        'description' => [
33          '#markup' => $help,
34        ],
35      ];
36  }
37}
38
39/**
40 * Implements hook_cron().
41 */
42function visitors_cron(): void {
43  \Drupal::service('visitors.cron')->execute();
44}
45
46/**
47 * Implements hook_page_attachments().
48 */
49function visitors_page_attachments(array &$page) {
50  \Drupal::service('visitors.page_attachments')->pageAttachments($page);
51}
52
53/**
54 * Implements hook_form_FORM_ID_alter().
55 *
56 * Allow users to decide if tracking code will be added to pages or not.
57 */
58function visitors_form_user_form_alter(&$form, FormStateInterface $form_state) {
59
60  $config = \Drupal::config('visitors.settings');
61  $visibility_users = $config->get('visibility.user_account_mode');
62  if ($visibility_users == VisitorsVisibilityInterface::USER_NO_PERSONALIZATION) {
63    return;
64  }
65  /** @var \Drupal\user\AccountForm $user_form */
66  $user_form = $form_state->getFormObject();
67  /** @var \Drupal\user\UserInterface $account */
68  $account = $user_form->getEntity();
69
70  if (!$account->hasPermission('opt-out of visitors tracking')) {
71    return;
72  }
73
74  $account_data_visitors = \Drupal::service('user.data')->get('visitors', $account->id());
75
76  $form['visitors'] = [
77    '#type' => 'details',
78    '#title' => t('Visitors settings'),
79    '#weight' => 3,
80    '#open' => TRUE,
81  ];
82  $description = '';
83  switch ($visibility_users) {
84    case VisitorsVisibilityInterface::USER_OPT_OUT:
85      $description = t('Users are tracked by default, but you are able to opt out.');
86      break;
87
88    case VisitorsVisibilityInterface::USER_OPT_IN:
89      $description = t('Users are <em>not</em> tracked by default, but you are able to opt in.');
90      break;
91  }
92
93  $form['visitors']['user_account_users'] = [
94    '#type' => 'checkbox',
95    '#title' => t('Enable user tracking'),
96    '#description' => $description,
97    '#default_value' => $account_data_visitors['user_account_users'] ?? $visibility_users,
98  ];
99
100  // Custom submit handler.
101  $form['actions']['submit']['#submit'][] = 'visitors_user_profile_form_submit';
102
103}
104
105/**
106 * Submit callback for user profile form to save the Visitor setting.
107 */
108function visitors_user_profile_form_submit($form, FormStateInterface $form_state) {
109  if (!$form_state->hasValue('user_account_users')) {
110    return;
111  }
112  /** @var \Drupal\user\AccountForm $user_form */
113  $user_form = $form_state->getFormObject();
114  /** @var \Drupal\user\UserInterface $account */
115  $account = $user_form->getEntity();
116
117  $value = (int) $form_state->getValue('user_account_users');
118  \Drupal::service('user.data')
119    ->set('visitors', $account->id(), 'user_account_users', $value);
120}
121
122/**
123 * Implements hook_node_links_alter().
124 */
125function visitors_node_links_alter(array &$links, NodeInterface $entity, array &$context) {
126  if ($context['view_mode'] == 'rss') {
127    return NULL;
128  }
129  $links['#cache']['contexts'][] = 'user.permissions';
130  if (!\Drupal::currentUser()->hasPermission('view visitors counter')) {
131    return NULL;
132  }
133  $settings = \Drupal::config('visitors.settings');
134
135  $statistics = \Drupal::service('visitors.counter')->fetchView('node', $entity->id());
136  if ($statistics) {
137    $statistics_links['visitors_counter']['title'] = \Drupal::translation()
138      ->formatPlural($statistics->getTotalCount(), '1 view', '@count views');
139    $links['visitors'] = [
140      '#theme' => 'links__node__visitors',
141      '#links' => $statistics_links,
142      '#attributes' => ['class' => ['links', 'inline']],
143    ];
144  }
145  $links['#cache']['max-age'] = $settings->get('counter.display_max_age');
146
147}
148
149/**
150 * Implements hook_entity_delete().
151 */
152function visitors_entity_delete(EntityInterface $entity) {
153
154  $entity_id = $entity->id();
155  if (!is_int($entity_id)) {
156    return;
157  }
158  $entity_type = $entity->getEntityTypeId();
159
160  \Drupal::service('visitors.counter')
161    ->deleteViews($entity_type, $entity_id);
162}
163
164/**
165 * Implements hook_ranking().
166 */
167function visitors_ranking() {
168  $settings = \Drupal::config('visitors.settings');
169  $is_enabled_and_has_node_entity_type = $settings->get('counter.enabled')
170    && in_array('node', $settings->get('counter.entity_types'));
171  if ($is_enabled_and_has_node_entity_type) {
172    return [
173      'views' => [
174        'title' => t('Number of views'),
175        'join' => [
176          'type' => 'LEFT',
177          'table' => 'visitors_counter',
178          'alias' => 'visitors_counter',
179          'on' => "visitors_counter.entity_id = i.sid AND visitors_counter.entity_type = 'node'",
180        ],
181        // Inverse law that maps the highest view count on the site to 1 and 0
182        // to 0. Note that the ROUND here is necessary for PostgreSQL and SQLite
183        // in order to ensure that the :statistics_scale argument is treated as
184        // a numeric type, because the PostgreSQL PDO driver sometimes puts
185        // values in as strings instead of numbers in complex expressions like
186        // this.
187        'score' => '2.0 - 2.0 / (1.0 + visitors_counter.total * (ROUND(:statistics_scale, 4)))',
188        'arguments' => [':statistics_scale' => \Drupal::state()->get('visitors.node_counter_scale', 0)],
189      ],
190    ];
191  }
192}
193
194/**
195 * Implements hook_views_data().
196 */
197function visitors_views_data() {
198  $data = [];
199  $data['visitors_counter']['table']['group'] = t('Visitors');
200  $data['visitors_counter']['table']['base'] = [
201    'title' => t('Visitors Entity Counter'),
202    'help' => t('Visitors data from visitors DB table.'),
203  ];
204  $settings = \Drupal::config('visitors.settings');
205  $supported_entity_types = $settings->get('counter.entity_types') ?? [];
206  foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type_id => $entity_type) {
207    $base_table = $entity_type->getBaseTable();
208    if (!in_array($entity_type_id, $supported_entity_types) || !$entity_type->entityClassImplements(ContentEntityInterface::class) || !$base_table) {
209      continue;
210    }
211
212    $base_table = $entity_type->getDataTable() ?: $base_table;
213    $args = ['@entity_type' => $entity_type_id];
214
215    // Multilingual properties are stored in data table.
216    if (!($table = $entity_type->getDataTable())) {
217      $table = $entity_type->getBaseTable();
218    }
219    $data[$base_table]['visitors_counter'] = [
220      'title' => t('Visitors @entity_type counter', $args),
221      'help' => t('Relate all visitor counts on the @entity_type.', $args),
222      'relationship' => [
223        'group' => t('Visitor Counters'),
224        'label' => t('Visitor counters'),
225        'base' => 'visitors_counter',
226        'base field' => 'entity_id',
227        'relationship field' => $entity_type->getKey('id'),
228        'id' => 'standard',
229        'extra' => [
230          [
231            'field' => 'entity_type',
232            'value' => $entity_type_id,
233          ],
234        ],
235      ],
236    ];
237
238    $data['visitors_counter']['table']['join'][$table] = [
239      'type' => 'LEFT',
240      'left_field' => $entity_type->getKey('id'),
241      'field' => 'entity_id',
242      'extra' => [
243        [
244          'field' => 'entity_type',
245          'value' => $entity_type_id,
246        ],
247      ],
248    ];
249
250  }
251
252  $data['visitors_counter']['total'] = [
253    'title' => t('Total views'),
254    'help' => t('The total number of times the node has been viewed.'),
255    'field' => [
256      'id' => 'visitors_numeric',
257      'click sortable' => TRUE,
258    ],
259    'filter' => [
260      'id' => 'numeric',
261    ],
262    'argument' => [
263      'id' => 'numeric',
264    ],
265    'sort' => [
266      'id' => 'standard',
267    ],
268  ];
269  $data['visitors_counter']['today'] = [
270    'title' => t('Views today'),
271    'help' => t('The total number of times the node has been viewed today.'),
272    'field' => [
273      'id' => 'visitors_numeric',
274      'click sortable' => TRUE,
275    ],
276    'filter' => [
277      'id' => 'numeric',
278    ],
279    'argument' => [
280      'id' => 'numeric',
281    ],
282    'sort' => [
283      'id' => 'standard',
284    ],
285  ];
286  $data['visitors_counter']['timestamp'] = [
287    'title' => t('Most recent visit'),
288    'help' => t('The most recent time the node has been viewed.'),
289    'field' => [
290      'id' => 'visitors_counter_timestamp',
291      'click sortable' => TRUE,
292    ],
293    'filter' => [
294      'id' => 'date',
295    ],
296    'argument' => [
297      'id' => 'date',
298    ],
299    'sort' => [
300      'id' => 'standard',
301    ],
302  ];
303
304  $data['visitors_visit']['table']['group'] = t('Visitors');
305  $data['visitors_visit']['table']['base'] = [
306    'title' => t('Visitors'),
307    'help' => t('Visitors data from visitors DB table.'),
308  ];
309
310  $data['visitors_visit']['id'] = [
311    'title' => t('Visit ID'),
312    'help' => t('Visitors visit ID.'),
313    'field' => [
314      'id' => 'numeric',
315    ],
316    'relationship' => [
317      'title' => t('Visitors Log'),
318      'help' => t('Log of the visitors entry.'),
319      'base' => 'visitors_event',
320      'base field' => 'visit_id',
321      'id' => 'standard',
322    ],
323    'sort' => [
324      'id' => 'standard',
325    ],
326    'filter' => [
327      'id' => 'numeric',
328    ],
329    'argument' => [
330      'id' => 'numeric',
331    ],
332  ];
333  $data['visitors_visit']['visitor_id'] = [
334    'title' => t('Unique visitor'),
335    'help' => t('A unique ID for the visitor.'),
336    'field' => [
337      'id' => 'standard',
338    ],
339    'filter' => [
340      'id' => 'string',
341    ],
342    'sort' => [
343      'id' => 'standard',
344    ],
345    'argument' => [
346      'id' => 'string',
347    ],
348  ];
349  $data['visitors_visit']['uid'] = [
350    'title' => t('Visit User'),
351    'help' => t('The user Id of the Visit.'),
352    'field' => [
353      'id' => 'standard',
354    ],
355    'relationship' => [
356      'title' => t('User'),
357      'help' => t('Relate visitor data to the user entity.'),
358      'base' => 'users_field_data',
359      'base field' => 'uid',
360      'id' => 'standard',
361    ],
362    'filter' => [
363      'id' => 'numeric',
364    ],
365    'argument' => [
366      'id' => 'numeric',
367    ],
368  ];
369  $data['visitors_visit']['localtime'] = [
370    'title' => t('Visitor Hour'),
371    'help' => t('The hour (client) of the visit.'),
372    'field' => [
373      'id' => 'visitors_local_hour',
374      'field' => 'localtime',
375    ],
376  ];
377  $data['visitors_visit']['returning'] = [
378    'title' => t('Returning Visitor'),
379    'help' => t('Indicates whether the visitor is a returning visitor.'),
380    'field' => [
381      'id' => 'boolean',
382    ],
383    'filter' => [
384      'id' => 'boolean',
385    // Optional: allows filtering on NULL values.
386      'allow empty' => TRUE,
387    ],
388    'argument' => [
389      'id' => 'boolean',
390    ],
391  ];
392  $data['visitors_visit']['visit_count'] = [
393    'title' => t('Visit Count'),
394    'help' => t('The number of visits.'),
395    'field' => [
396      'id' => 'numeric',
397    ],
398    'sort' => [
399      'id' => 'standard',
400    ],
401    'filter' => [
402      'id' => 'numeric',
403    ],
404    'argument' => [
405      'id' => 'numeric',
406    ],
407  ];
408  $data['visitors_visit']['page_count'] = [
409    'title' => t('Page Count'),
410    'help' => t('The number of pages viewed in the visit.'),
411    'field' => [
412      'id' => 'numeric',
413    ],
414    'sort' => [
415      'id' => 'standard',
416    ],
417    'filter' => [
418      'id' => 'numeric',
419    ],
420    'argument' => [
421      'id' => 'numeric',
422    ],
423  ];
424  $data['visitors_visit']['entry'] = [
425    'title' => t('Entry page'),
426    'help' => t('The first page viewed in the visit.'),
427    'field' => [
428      'id' => 'numeric',
429    ],
430    'relationship' => [
431      'title' => t('Entry page'),
432      'help' => t('Page view Log.'),
433      'base' => 'visitors_event',
434      'base field' => 'id',
435      'id' => 'standard',
436    ],
437    'sort' => [
438      'id' => 'standard',
439    ],
440    'filter' => [
441      'id' => 'numeric',
442    ],
443    'argument' => [
444      'id' => 'numeric',
445    ],
446  ];
447  $data['visitors_visit']['entry_time'] = [
448    'title' => t('Entry Time'),
449    'help' => t('The timestamp of the page view.'),
450    'field' => [
451      'id' => 'date',
452      'click sortable' => TRUE,
453    ],
454  ];
455  $data['visitors_visit']['exit'] = [
456    'title' => t('Exit page'),
457    'help' => t('The last page viewed in the visit.'),
458    'field' => [
459      'id' => 'numeric',
460    ],
461    'relationship' => [
462      'title' => t('Exit page'),
463      'help' => t('Exit Page view.'),
464      'base' => 'visitors_event',
465      'base field' => 'id',
466      'id' => 'standard',
467    ],
468    'sort' => [
469      'id' => 'standard',
470    ],
471    'filter' => [
472      'id' => 'numeric',
473    ],
474    'argument' => [
475      'id' => 'numeric',
476    ],
477  ];
478  $data['visitors_visit']['exit_time'] = [
479    'title' => t('Exit Time'),
480    'help' => t('The timestamp of the page view.'),
481    'field' => [
482      'id' => 'date',
483      'click sortable' => TRUE,
484    ],
485  ];
486  $data['visitors_visit']['visit_time'] = [
487    'title' => t('Visit Time'),
488    'help' => t('Visit time filter.'),
489    'filter' => [
490      'id' => 'visitors_visit_date',
491      'field' => 'exit_time',
492    ],
493  ];
494  $data['visitors_visit']['config_id'] = [
495    'title' => t('Config Id'),
496    'help' => t('Visitor config hash.'),
497    'field' => [
498      'id' => 'standard',
499    ],
500    'filter' => [
501      'id' => 'string',
502    ],
503    'sort' => [
504      'id' => 'standard',
505    ],
506    'argument' => [
507      'id' => 'string',
508    ],
509  ];
510  $data['visitors_visit']['config_resolution'] = [
511    'title' => t('Resolution'),
512    'help' => t("The visitor's screen resolution."),
513    'field' => [
514      'id' => 'standard',
515    ],
516    'filter' => [
517      'id' => 'string',
518    ],
519    'argument' => [
520      'id' => 'string',
521    ],
522  ];
523  $data['visitors_visit']['config_pdf'] = [
524    'title' => t('PDF Plugin'),
525    'help' => t("The visitor's browser supports PDFs."),
526    'field' => [
527      'id' => 'visitors_pdf',
528    ],
529    'filter' => [
530      'id' => 'boolean',
531    ],
532  ];
533  $data['visitors_visit']['config_flash'] = [
534    'title' => t('Flash Plugin'),
535    'help' => t("The visitor's browser supports Flash."),
536    'field' => [
537      'id' => 'visitors_flash',
538    ],
539    'filter' => [
540      'id' => 'boolean',
541    ],
542  ];
543  $data['visitors_visit']['config_java'] = [
544    'title' => t('Java Plugin'),
545    'help' => t("The visitor's browser supports Java."),
546    'field' => [
547      'id' => 'visitors_java',
548    ],
549    'filter' => [
550      'id' => 'boolean',
551    ],
552  ];
553  $data['visitors_visit']['config_quicktime'] = [
554    'title' => t('Quicktime Plugin'),
555    'help' => t("The visitor's browser supports Quicktime."),
556    'field' => [
557      'id' => 'visitors_quicktime',
558    ],
559    'filter' => [
560      'id' => 'boolean',
561    ],
562  ];
563  $data['visitors_visit']['config_realplayer'] = [
564    'title' => t('Realplayer Plugin'),
565    'help' => t("The visitor's browser supports Realplayer."),
566    'field' => [
567      'id' => 'visitors_realplayer',
568    ],
569    'filter' => [
570      'id' => 'boolean',
571    ],
572  ];
573  $data['visitors_visit']['config_windowsmedia'] = [
574    'title' => t('Windows Media Plugin'),
575    'help' => t("The visitor's browser supports Windows Media."),
576    'field' => [
577      'id' => 'visitors_windowsmedia',
578    ],
579    'filter' => [
580      'id' => 'boolean',
581    ],
582  ];
583  $data['visitors_visit']['config_silverlight'] = [
584    'title' => t('Silverlight Plugin'),
585    'help' => t("The visitor's browser supports Silverlight."),
586    'field' => [
587      'id' => 'visitors_silverlight',
588    ],
589    'filter' => [
590      'id' => 'boolean',
591    ],
592  ];
593  $data['visitors_visit']['config_cookie'] = [
594    'title' => t('Cookie Plugin'),
595    'help' => t("The visitor's browser supports cookies."),
596    'field' => [
597      'id' => 'visitors_cookie',
598    ],
599    'filter' => [
600      'id' => 'boolean',
601    ],
602  ];
603  $data['visitors_visit']['config_browser_engine'] = [
604    'title' => t('Browser Engine'),
605    'help' => t('The engine used by the browser.'),
606    'field' => [
607      'id' => 'standard',
608    ],
609    'filter' => [
610      'id' => 'string',
611    ],
612    'argument' => [
613      'id' => 'string',
614    ],
615  ];
616  $data['visitors_visit']['config_browser_name'] = [
617    'title' => t('Browser Name'),
618    'help' => t('The name of the browser.'),
619    'field' => [
620      'id' => 'visitors_browser',
621    ],
622    'filter' => [
623      'id' => 'string',
624    ],
625    'argument' => [
626      'id' => 'string',
627    ],
628  ];
629  $data['visitors_visit']['config_browser_version'] = [
630    'title' => t('Browser Version'),
631    'help' => t('The version of the browser.'),
632    'field' => [
633      'id' => 'standard',
634    ],
635    'filter' => [
636      'id' => 'string',
637    ],
638    'argument' => [
639      'id' => 'string',
640    ],
641  ];
642  $data['visitors_visit']['config_client_type'] = [
643    'title' => t('Client type'),
644    'help' => t('The type of the client.'),
645    'field' => [
646      'id' => 'standard',
647    ],
648    'filter' => [
649      'id' => 'string',
650    ],
651    'argument' => [
652      'id' => 'string',
653    ],
654  ];
655  $data['visitors_visit']['config_device_brand'] = [
656    'title' => t('Device brand'),
657    'help' => t('The brand of the device.'),
658    'field' => [
659      'id' => 'visitors_brand',
660    ],
661    'filter' => [
662      'id' => 'string',
663    ],
664    'argument' => [
665      'id' => 'string',
666    ],
667  ];
668  $data['visitors_visit']['config_device_model'] = [
669    'title' => t('Device model'),
670    'help' => t('The model of the device.'),
671    'field' => [
672      'id' => 'standard',
673    ],
674    'filter' => [
675      'id' => 'string',
676    ],
677    'argument' => [
678      'id' => 'string',
679    ],
680  ];
681  $data['visitors_visit']['config_device_type'] = [
682    'title' => t('Device type'),
683    'help' => t('The type of device.'),
684    'field' => [
685      'id' => 'visitors_device',
686    ],
687    'filter' => [
688      'id' => 'string',
689    ],
690    'argument' => [
691      'id' => 'string',
692    ],
693  ];
694  $data['visitors_visit']['config_os'] = [
695    'title' => t('Operating System'),
696    'help' => t('The operating system.'),
697    'field' => [
698      'id' => 'visitors_operating_system',
699    ],
700    'filter' => [
701      'id' => 'string',
702    ],
703    'argument' => [
704      'id' => 'string',
705    ],
706  ];
707  $data['visitors_visit']['config_os_version'] = [
708    'title' => t('OS version'),
709    'help' => t('The version of the Operating System.'),
710    'field' => [
711      'id' => 'standard',
712    ],
713    'filter' => [
714      'id' => 'string',
715    ],
716    'argument' => [
717      'id' => 'string',
718    ],
719  ];
720  $data['visitors_visit']['bot'] = [
721    'title' => t('Bot'),
722    'help' => t("The visit is from a bot."),
723    'field' => [
724      'id' => 'boolean',
725    ],
726    'filter' => [
727      'id' => 'boolean',
728    ],
729    'argument' => [
730      'id' => 'numeric',
731    ],
732  ];
733  $data['visitors_visit']['location_browser_lang'] = [
734    'title' => t('Language'),
735    'help' => t('The browser language.'),
736    'field' => [
737      'id' => 'visitors_language',
738    ],
739    'filter' => [
740      'id' => 'string',
741    ],
742    'argument' => [
743      'id' => 'string',
744    ],
745  ];
746  $data['visitors_visit']['location_ip'] = [
747    'title' => t('Visitors IP'),
748    'help' => t('The IP of the visitors entry.'),
749    'field' => [
750      'id' => 'standard',
751    ],
752    'filter' => [
753      'id' => 'string',
754    ],
755    'argument' => [
756      'id' => 'string',
757    ],
758  ];
759  $data['visitors_visit']['location_continent'] = [
760    'title' => t('Continent'),
761    'help' => t('The location continent.'),
762    'field' => [
763      'id' => 'visitors_continent',
764    ],
765    'filter' => [
766      'id' => 'string',
767    ],
768    'argument' => [
769      'id' => 'string',
770    ],
771  ];
772  $data['visitors_visit']['location_country'] = [
773    'title' => t('Country'),
774    'help' => t('The location country.'),
775    'field' => [
776      'id' => 'visitors_country',
777    ],
778    'filter' => [
779      'id' => 'string',
780    ],
781    'argument' => [
782      'id' => 'string',
783    ],
784  ];
785  $data['visitors_visit']['location_region'] = [
786    'title' => t('Region'),
787    'help' => t('The region of the visitor.'),
788    'field' => [
789      'id' => 'standard',
790    ],
791    'filter' => [
792      'id' => 'string',
793    ],
794    'argument' => [
795      'id' => 'string',
796    ],
797  ];
798  $data['visitors_visit']['location_city'] = [
799    'title' => t('City'),
800    'help' => t('The city of the visitor.'),
801    'field' => [
802      'id' => 'standard',
803    ],
804    'filter' => [
805      'id' => 'string',
806    ],
807    'argument' => [
808      'id' => 'string',
809    ],
810  ];
811  $data['visitors_visit']['location_latitude'] = [
812    'title' => t('Location Latitude'),
813    'help' => t('The latitude derived from the IP address.'),
814    'field' => [
815      'id' => 'numeric',
816    ],
817    'filter' => [
818      'id' => 'numeric',
819    ],
820    'sort' => [
821      'id' => 'numeric',
822    ],
823  ];
824  $data['visitors_visit']['location_longitude'] = [
825    'title' => t('Location Longitude'),
826    'help' => t('The longitude derived from the IP address.'),
827    'field' => [
828      'id' => 'numeric',
829    ],
830    'filter' => [
831      'id' => 'numeric',
832    ],
833    'sort' => [
834      'id' => 'numeric',
835    ],
836  ];
837  $data['visitors_visit']['referer_type'] = [
838    'title' => t('Referer Type'),
839    'help' => t('The type of the referer.'),
840    'field' => [
841      'id' => 'standard',
842    ],
843    'filter' => [
844      'id' => 'string',
845    ],
846  ];
847  $data['visitors_visit']['referer_name'] = [
848    'title' => t('Referer Name'),
849    'help' => t('The name of the referer.'),
850    'field' => [
851      'id' => 'standard',
852    ],
853  ];
854  $data['visitors_visit']['referer_url'] = [
855    'title' => t('Referer URL'),
856    'help' => t('The URL of the referer.'),
857    'field' => [
858      'id' => 'standard',
859    ],
860  ];
861  $data['visitors_visit']['referer_keyword'] = [
862    'title' => t('Referer Keyword'),
863    'help' => t('The keyword of the referer.'),
864    'field' => [
865      'id' => 'standard',
866    ],
867  ];
868
869  $data['visitors_event']['table']['group'] = t('Visitors');
870  $data['visitors_event']['table']['base'] = [
871    'title' => t('Visitors Log'),
872    'help' => t('Visitors data from visitors DB table.'),
873  ];
874
875  $data['visitors_event']['id'] = [
876    'title' => t('Log Id'),
877    'help' => t('Visitors Log Id.'),
878    'field' => [
879      'id' => 'numeric',
880    ],
881    'sort' => [
882      'id' => 'standard',
883    ],
884    'filter' => [
885      'id' => 'numeric',
886    ],
887    'argument' => [
888      'id' => 'numeric',
889    ],
890  ];
891  $data['visitors_event']['visit_id'] = [
892    'title' => t('Visit Id'),
893    'help' => t('Visitors visit Id.'),
894    'field' => [
895      'id' => 'numeric',
896    ],
897    'relationship' => [
898      'title' => t('Visitors Visit'),
899      'help' => t('Visit of the Log.'),
900      'base' => 'visitors_visit',
901      'base field' => 'id',
902      'id' => 'standard',
903    ],
904    'sort' => [
905      'id' => 'standard',
906    ],
907    'filter' => [
908      'id' => 'numeric',
909    ],
910    'argument' => [
911      'id' => 'numeric',
912    ],
913  ];
914  $data['visitors_event']['title'] = [
915    'title' => t('Visitors title'),
916    'help' => t('The title of the visitors entry.'),
917    'field' => [
918      'id' => 'standard',
919    ],
920    'filter' => [
921      'id' => 'string',
922    ],
923  ];
924  $data['visitors_event']['uid'] = [
925    'title' => t('Visitors UID'),
926    'help' => t('The user ID of the visitors entry.'),
927    'field' => [
928      'id' => 'standard',
929    ],
930    'relationship' => [
931      'title' => t('User'),
932      'help' => t('The user entity from the visitor entry.'),
933      'base' => 'users_field_data',
934      'base field' => 'uid',
935      'id' => 'standard',
936    ],
937    'filter' => [
938      'id' => 'numeric',
939    ],
940    'argument' => [
941      'id' => 'numeric',
942    ],
943  ];
944  $data['visitors_event']['url_prefix'] = [
945    'title' => t('URL Prefix'),
946    'help' => t('The URL Prefix.'),
947    'field' => [
948      'id' => 'visitors_url_prefix',
949    ],
950  ];
951  $data['visitors_event']['url'] = [
952    'title' => t('Visitors URL'),
953    'help' => t('The URL of the visitors entry.'),
954    'field' => [
955      'id' => 'standard',
956    ],
957    'filter' => [
958      'id' => 'string',
959    ],
960  ];
961
962  $data['visitors_event']['path'] = [
963    'title' => t('Visitors path'),
964    'help' => t('The path of the visitors entry.'),
965    'field' => [
966      'id' => 'standard',
967    ],
968    'filter' => [
969      'id' => 'string',
970    ],
971    'argument' => [
972      'id' => 'string',
973    ],
974  ];
975  $data['visitors_event']['route'] = [
976    'title' => t('Route'),
977    'help' => t('The route of the visitors entry.'),
978    'field' => [
979      'id' => 'standard',
980    ],
981    'filter' => [
982      'id' => 'string',
983    ],
984    'argument' => [
985      'id' => 'string',
986    ],
987  ];
988  $data['visitors_event']['referrer_url'] = [
989    'title' => t('Visitors referer'),
990    'help' => t('The referer of the visitors entry.'),
991    'field' => [
992      'id' => 'standard',
993    ],
994    'filter' => [
995      'id' => 'string',
996    ],
997  ];
998  $data['visitors_event']['server'] = [
999    'title' => t('Server'),
1000    'help' => t('The server that generated the response.'),
1001    'field' => [
1002      'id' => 'standard',
1003    ],
1004    'filter' => [
1005      'id' => 'string',
1006    ],
1007    'argument' => [
1008      'id' => 'string',
1009    ],
1010  ];
1011  $data['visitors_event']['pf_network'] = [
1012    'title' => t('Network'),
1013    'help' => t('Network performance.'),
1014    'field' => [
1015      'id' => 'numeric',
1016    ],
1017    'sort' => [
1018      'id' => 'standard',
1019    ],
1020    'filter' => [
1021      'id' => 'numeric',
1022    ],
1023    'argument' => [
1024      'id' => 'numeric',
1025    ],
1026  ];
1027  $data['visitors_event']['pf_server'] = [
1028    'title' => t('Server'),
1029    'help' => t('Server performance.'),
1030    'field' => [
1031      'id' => 'numeric',
1032    ],
1033    'sort' => [
1034      'id' => 'standard',
1035    ],
1036    'filter' => [
1037      'id' => 'numeric',
1038    ],
1039    'argument' => [
1040      'id' => 'numeric',
1041    ],
1042  ];
1043  $data['visitors_event']['pf_transfer'] = [
1044    'title' => t('Transfer'),
1045    'help' => t('Transfer performance.'),
1046    'field' => [
1047      'id' => 'numeric',
1048    ],
1049    'sort' => [
1050      'id' => 'standard',
1051    ],
1052    'filter' => [
1053      'id' => 'numeric',
1054    ],
1055    'argument' => [
1056      'id' => 'numeric',
1057    ],
1058  ];
1059  $data['visitors_event']['pf_dom_processing'] = [
1060    'title' => t('DOM Processing'),
1061    'help' => t('DOM Processing performance.'),
1062    'field' => [
1063      'id' => 'numeric',
1064    ],
1065    'sort' => [
1066      'id' => 'standard',
1067    ],
1068    'filter' => [
1069      'id' => 'numeric',
1070    ],
1071    'argument' => [
1072      'id' => 'numeric',
1073    ],
1074  ];
1075  $data['visitors_event']['pf_dom_complete'] = [
1076    'title' => t('DOM Complete'),
1077    'help' => t('DOM Complete performance.'),
1078    'field' => [
1079      'id' => 'numeric',
1080    ],
1081    'sort' => [
1082      'id' => 'standard',
1083    ],
1084    'filter' => [
1085      'id' => 'numeric',
1086    ],
1087    'argument' => [
1088      'id' => 'numeric',
1089    ],
1090  ];
1091  $data['visitors_event']['pf_on_load'] = [
1092    'title' => t('On Load'),
1093    'help' => t('On Load performance.'),
1094    'field' => [
1095      'id' => 'numeric',
1096    ],
1097    'sort' => [
1098      'id' => 'standard',
1099    ],
1100    'filter' => [
1101      'id' => 'numeric',
1102    ],
1103    'argument' => [
1104      'id' => 'numeric',
1105    ],
1106  ];
1107  $data['visitors_event']['pf_total'] = [
1108    'title' => t('Total'),
1109    'help' => t('Total performance.'),
1110    'field' => [
1111      'id' => 'numeric',
1112    ],
1113    'sort' => [
1114      'id' => 'standard',
1115    ],
1116    'filter' => [
1117      'id' => 'numeric',
1118    ],
1119    'argument' => [
1120      'id' => 'numeric',
1121    ],
1122  ];
1123  $data['visitors_event']['created'] = [
1124    'title' => t('Created'),
1125    'help' => t('The timestamp of the page view.'),
1126    'field' => [
1127      'id' => 'date',
1128      'click sortable' => TRUE,
1129    ],
1130    'filter' => [
1131      'id' => 'visitors_date',
1132    ],
1133  ];
1134  $data['visitors_event']['visitors_hour'] = [
1135    'title' => t('Hour'),
1136    'help' => t('The hour (server) of the visit.'),
1137    'field' => [
1138      'id' => 'visitors_hour',
1139      'field' => 'created',
1140    ],
1141    'sort' => [
1142      'id' => 'visitors_timestamp',
1143      'field' => 'created',
1144    ],
1145  ];
1146  $data['visitors_event']['visitors_month'] = [
1147    'title' => t('Month'),
1148    'help' => t('The month of the visit.'),
1149    'field' => [
1150      'id' => 'visitors_month',
1151      'field' => 'created',
1152    ],
1153    'sort' => [
1154      'id' => 'visitors_timestamp',
1155      'field' => 'created',
1156    ],
1157  ];
1158  $data['visitors_event']['visitors_local_hour'] = [
1159    'title' => t('Local hour'),
1160    'help' => t('Visitors local time.'),
1161    'field' => [
1162      'id' => 'visitors_local_hour',
1163      'field' => 'created',
1164    ],
1165    'sort' => [
1166      'id' => 'visitors_timestamp',
1167      'field' => 'created',
1168    ],
1169  ];
1170  $data['visitors_event']['visitors_day_of_week'] = [
1171    'title' => t('Day of Week'),
1172    'help' => t('The day of week of the visit.'),
1173    'field' => [
1174      'id' => 'visitors_day_of_week',
1175      'field' => 'created',
1176    ],
1177    'sort' => [
1178      'id' => 'visitors_timestamp',
1179      'field' => 'created',
1180    ],
1181  ];
1182  $data['visitors_event']['visitors_day_of_month'] = [
1183    'title' => t('Day of Month'),
1184    'help' => t('The day of month of the visit.'),
1185    'field' => [
1186      'id' => 'visitors_day_of_month',
1187      'field' => 'created',
1188    ],
1189    'sort' => [
1190      'id' => 'visitors_timestamp',
1191      'field' => 'created',
1192    ],
1193  ];
1194  $data['visitors_event']['visitors_day'] = [
1195    'title' => t('Day'),
1196    'help' => t('The day of the visit.'),
1197    'field' => [
1198      'id' => 'visitors_day',
1199      'field' => 'created',
1200    ],
1201    'sort' => [
1202      'id' => 'visitors_timestamp',
1203      'field' => 'created',
1204    ],
1205  ];
1206  $data['visitors_event']['visitors_week'] = [
1207    'title' => t('Week'),
1208    'help' => t('The week of the visit.'),
1209    'field' => [
1210      'id' => 'visitors_week',
1211      'field' => 'created',
1212    ],
1213    'sort' => [
1214      'id' => 'visitors_timestamp',
1215      'field' => 'created',
1216    ],
1217  ];
1218
1219  $data['visitors_visit']['visitors_display_link'] = [
1220    'title' => t('Link to Visitors display'),
1221    'help' => t('Displays a link to a non-path-based display of this view while keeping the filter criteria, sort criteria, pager settings and contextual filters.'),
1222    'area' => [
1223      'id' => 'visitors_display_link',
1224    ],
1225  ];
1226
1227  return $data;
1228}
1229
1230/**
1231 * Implements hook_token_info().
1232 */
1233function visitors_token_info() {
1234  $entity['total-count'] = [
1235    'name' => t("Number of views"),
1236    'description' => t("The number of visitors who have read the node."),
1237  ];
1238  $entity['day-count'] = [
1239    'name' => t("Views today"),
1240    'description' => t("The number of visitors who have read the node today."),
1241  ];
1242  $entity['last-view'] = [
1243    'name' => t("Last view"),
1244    'description' => t("The date on which a visitor last read the node."),
1245    'type' => 'date',
1246  ];
1247
1248  $token = [
1249    'tokens' => [],
1250  ];
1251  $entity_types = \Drupal::config('visitors.settings')
1252    ->get('counter.entity_types') ?? [];
1253  foreach ($entity_types as $entity_type) {
1254    $token['tokens'][$entity_type] = $entity;
1255  }
1256
1257  return $token;
1258}
1259
1260/**
1261 * Implements hook_tokens().
1262 */
1263function visitors_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
1264  $token_service = \Drupal::token();
1265  $entity_types = \Drupal::config('visitors.settings')
1266    ->get('counter.entity_types') ?? [];
1267  $replacements = [];
1268
1269  if (!in_array($type, $entity_types) || empty($data[$type])) {
1270    return $replacements;
1271  }
1272  $entity = $data[$type];
1273
1274  /** @var \Drupal\visitors\VisitorsCounterInterface $counter_storage */
1275  $counter_storage = \Drupal::service('visitors.counter');
1276
1277  $entity_id = $entity->id() ?? 0;
1278  $entity_view = $counter_storage->fetchView($type, $entity_id);
1279  foreach ($tokens as $name => $original) {
1280    if ($name == 'total-count') {
1281      $replacements[$original] = $entity_view ? $entity_view->getTotalCount() : 0;
1282    }
1283    elseif ($name == 'day-count') {
1284      $replacements[$original] = $entity_view ? $entity_view->getDayCount() : 0;
1285    }
1286    elseif ($name == 'last-view') {
1287      $replacements[$original] = $entity_view ? \Drupal::service('date.formatter')->format($entity_view->getTimestamp()) : t('never');
1288    }
1289  }
1290
1291  if ($created_tokens = $token_service->findWithPrefix($tokens, 'last-view')) {
1292    $replacements += $token_service->generate('date', $created_tokens, ['date' => $entity_view ? $entity_view->getTimestamp() : 0], $options, $bubbleable_metadata);
1293  }
1294
1295  return $replacements;
1296}
1297
1298/**
1299 * Implements hook_form_alter().
1300 */
1301function visitors_form_alter(&$form, FormStateInterface $form_state, $form_id) {
1302  $static = &drupal_static(__FUNCTION__);
1303
1304  $form_object = $form_state->getFormObject();
1305  if (method_exists($form_object, 'getBaseFormId')) {
1306    $base_form_id = $form_object->getBaseFormId();
1307  }
1308  else {
1309    $base_form_id = $form_id;
1310  }
1311
1312  $static[$form_id] = $base_form_id;
1313
1314  $form['#submit'][] = [Form::class, 'submit'];
1315  $form['#validate'][] = [Form::class, 'validate'];
1316}
1317
1318/**
1319 * Implements hook_preprocess_html().
1320 */
1321function visitors_preprocess_html(array &$variables) {
1322  $title = '';
1323  if (isset($variables['head_title']['title'])) {
1324    $title = strip_tags((string) $variables['head_title']['title']);
1325  }
1326  elseif (isset($variables['head_title']) && is_string($variables['head_title'])) {
1327    $title = strip_tags($variables['head_title']);
1328  }
1329
1330  if ($title) {
1331    $variables['#attached']['drupalSettings']['visitors']['title'] = $title;
1332  }
1333}