Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
278 / 278
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
Settings
100.00% covered (success)
100.00%
278 / 278
100.00% covered (success)
100.00%
8 / 8
17
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getFormId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getEditableConfigNames
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 buildForm
100.00% covered (success)
100.00%
236 / 236
100.00% covered (success)
100.00%
1 / 1
7
 submitForm
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
1
 entityTypes
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 roleOptions
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace Drupal\visitors\Form;
4
5use Drupal\Core\Config\Entity\ConfigEntityType;
6use Drupal\Core\Entity\EntityTypeManagerInterface;
7use Drupal\Core\Extension\ThemeExtensionList;
8use Drupal\Core\Form\ConfigFormBase;
9use Drupal\Core\Form\FormStateInterface;
10use Drupal\Core\Url;
11use Drupal\visitors\VisitorsVisibilityInterface;
12use Symfony\Component\DependencyInjection\ContainerInterface;
13
14/**
15 * Visitors Settings Form.
16 */
17class Settings extends ConfigFormBase {
18
19  /**
20   * Config settings.
21   *
22   * @var string
23   */
24  const SETTINGS = 'visitors.config';
25
26  /**
27   * An extension discovery instance.
28   *
29   * @var \Drupal\Core\Extension\ThemeExtensionList
30   */
31  protected $themeList;
32
33  /**
34   * An extension discovery instance.
35   *
36   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
37   */
38  protected $entityTypeManager;
39
40  /**
41   * Constructs a Settings form.
42   *
43   * @param \Drupal\Core\Extension\ThemeExtensionList $theme_list
44   *   The theme list.
45   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
46   *   The entity type manager.
47   */
48  public function __construct(ThemeExtensionList $theme_list, EntityTypeManagerInterface $entity_type_manager) {
49    $this->themeList = $theme_list;
50    $this->entityTypeManager = $entity_type_manager;
51  }
52
53  /**
54   * {@inheritdoc}
55   */
56  public static function create(ContainerInterface $container) {
57    return new self(
58      $container->get('extension.list.theme'),
59      $container->get('entity_type.manager')
60    );
61  }
62
63  /**
64   * {@inheritdoc}
65   */
66  public function getFormId() {
67    return 'visitors_admin_settings';
68  }
69
70  /**
71   * {@inheritdoc}
72   */
73  protected function getEditableConfigNames() {
74    return [
75      static::SETTINGS,
76    ];
77  }
78
79  /**
80   * {@inheritdoc}
81   */
82  public function buildForm(array $form, FormStateInterface $form_state) {
83    $config = $this->config('visitors.config');
84    $system_config = $this->config('system.theme');
85    $form = parent::buildForm($form, $form_state);
86
87    $form['#attached']['library'][] = 'visitors/visitors.admin';
88
89    $roles = [];
90    foreach ($this->entityTypeManager->getStorage('user_role')->loadMultiple() as $name => $role) {
91      $roles[$name] = $role->label();
92    }
93
94    $all_themes = $this->themeList->getList();
95    $default_theme = $system_config->get('default');
96    $admin_theme = $system_config->get('admin');
97
98    $default_name = $all_themes[$default_theme]->info['name'];
99    $themes_installed = [
100      'default' => $this->t('Default (@default)', ['@default' => $default_name]),
101    ];
102    if ($admin_theme) {
103      $admin_name = $all_themes[$admin_theme]->info['name'];
104      $themes_installed['admin'] = $this->t('Admin (@admin)', ['@admin' => $admin_name]);
105    }
106
107    $list_themes = array_filter($all_themes, function ($obj) {
108      $a = get_object_vars($obj);
109      return $a['status'] ?? FALSE;
110    });
111    $themes_installed += array_map(function ($value) {
112      return $value->info['name'];
113    }, $list_themes);
114
115    $form['visitors_disable_tracking'] = [
116      '#type' => 'radios',
117      '#title' => $this->t('Track visitors'),
118      '#options' => [
119        $this->t('Enabled'),
120        $this->t('Disabled'),
121      ],
122      '#description' => $this->t('Enable or disable tracking of visitors.'),
123      '#default_value' => (int) $config->get('disable_tracking'),
124    ];
125
126    $form['theme'] = [
127      '#type' => 'select',
128      '#title' => $this->t('Set a theme for reports'),
129      '#options' => $themes_installed,
130      '#default_value' => $config->get('theme') ?: 'admin',
131      '#description' => $this->t('Select a theme for the Visitors reports.'),
132    ];
133
134    // Visibility settings.
135    $form['tracking_scope'] = [
136      '#type' => 'vertical_tabs',
137      '#title' => $this->t('Tracking scope'),
138      '#title_display' => 'invisible',
139      '#default_tab' => 'edit-tracking',
140    ];
141
142    // Page specific visibility configurations.
143    $visibility_request_path_pages = $config->get('visibility.request_path_pages');
144
145    $form['page_visibility_settings'] = [
146      '#type' => 'details',
147      '#title' => $this->t('Pages'),
148      '#group' => 'tracking_scope',
149    ];
150
151    $description = $this->t(
152        "Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.",
153        [
154          '%blog' => '/blog',
155          '%blog-wildcard' => '/blog/*',
156          '%front' => '<front>',
157        ]
158      );
159
160    $form['page_visibility_settings']['visitors_visibility_request_path_pages'] = [
161      '#type' => 'textarea',
162      '#title' => $this->t('Pages'),
163      '#title_display' => 'invisible',
164      '#default_value' => !empty($visibility_request_path_pages) ? $visibility_request_path_pages : '',
165      '#description' => $description,
166      '#rows' => (int) 10,
167    ];
168    $form['page_visibility_settings']['visitors_visibility_request_path_mode'] = [
169      '#type' => 'radios',
170      '#title' => $this->t('Add tracking to specific pages'),
171      '#title_display' => 'invisible',
172      '#options' => [
173        VisitorsVisibilityInterface::PATH_EXCLUDE => $this->t('All pages except those listed'),
174        VisitorsVisibilityInterface::PATH_INCLUDE => $this->t('Only the listed pages'),
175      ],
176      '#default_value' => $config->get('visibility.request_path_mode'),
177    ];
178
179    // Render the role overview.
180    $visibility_user_role_roles = $config->get('visibility.user_role_roles');
181
182    $form['role_visibility_settings'] = [
183      '#type' => 'details',
184      '#title' => $this->t('Roles'),
185      '#group' => 'tracking_scope',
186    ];
187
188    $form['role_visibility_settings']['visitors_visibility_user_role_mode'] = [
189      '#type' => 'radios',
190      '#title' => $this->t('Add tracking for specific roles'),
191      '#options' => [
192        $this->t('Add to the selected roles only'),
193        $this->t('Add to every role except the selected ones'),
194      ],
195      '#default_value' => $config->get('visibility.user_role_mode'),
196    ];
197    $form['role_visibility_settings']['visitors_visibility_user_role_roles'] = [
198      '#type' => 'checkboxes',
199      '#title' => $this->t('Roles'),
200      '#default_value' => !empty($visibility_user_role_roles) ? $visibility_user_role_roles : [],
201      '#options' => $this->roleOptions(),
202      '#description' => $this->t('If none of the roles are selected, all users will be tracked. If a user has any of the roles checked, that user will be tracked (or excluded, depending on the setting above).'),
203    ];
204
205    $form['user_visibility_settings'] = [
206      '#type' => 'details',
207      '#title' => $this->t('Users'),
208      '#group' => 'tracking_scope',
209    ];
210    $t_permission = ['%permission' => $this->t('opt-out of visitors tracking')];
211    $form['user_visibility_settings']['visitors_visibility_user_account_mode'] = [
212      '#type' => 'radios',
213      '#title' => $this->t('Allow users to customize tracking on their account page'),
214      '#options' => [
215        VisitorsVisibilityInterface::USER_NO_PERSONALIZATION => $this->t('No customization allowed'),
216        VisitorsVisibilityInterface::USER_OPT_OUT => $this->t('Tracking on by default, users with %permission permission can opt out', $t_permission),
217        VisitorsVisibilityInterface::USER_OPT_IN => $this->t('Tracking off by default, users with %permission permission can opt in', $t_permission),
218      ],
219      '#default_value' => $config->get('visibility.user_account_mode') ?? 0,
220    ];
221    $form['user_visibility_settings']['visitors_trackuserid'] = [
222      '#type' => 'checkbox',
223      '#title' => $this->t('Track User ID'),
224      '#default_value' => $config->get('track.userid'),
225      '#description' => $this->t('User ID enables the analysis of groups of sessions, across devices, using a unique, persistent, and representing a user. <a href=":url">Learn more about the benefits of using User ID</a>.', [':url' => 'https://matomo.org/docs/user-id/']),
226    ];
227
228    $form['user_visibility_settings']['visibility_exclude_user1'] = [
229      '#type' => 'checkbox',
230      '#title' => $this->t('Exclude user1 from statistics'),
231      '#default_value' => $config->get('visibility.exclude_user1'),
232      '#description' => $this->t('Exclude hits of user1 from statistics.'),
233    ];
234
235    $form['entity'] = [
236      '#type' => 'details',
237      '#title' => $this->t('Entity counter'),
238      '#group' => 'tracking_scope',
239    ];
240    $form['entity']['counter_enabled'] = [
241      '#type' => 'checkbox',
242      '#title' => $this->t('Enabled'),
243      '#default_value' => $config->get('counter.enabled'),
244      '#description' => $this->t('Count the number of times entities are viewed.'),
245    ];
246    $form['entity']['entity_types'] = [
247      '#type' => 'checkboxes',
248      '#title' => $this->t('Entity Types'),
249      '#options' => $this->entityTypes(),
250      '#default_value' => $config->get('counter.entity_types') ?? [],
251      '#description' => $this->t('Which entity types should be tracked.'),
252    ];
253
254    $form['retention'] = [
255      '#type' => 'details',
256      '#title' => $this->t('Retention'),
257      '#group' => 'tracking_scope',
258    ];
259    $form['retention']['flush_log_timer'] = [
260      '#type' => 'select',
261      '#title' => $this->t('Discard visitors logs older than'),
262      '#default_value'   => $config->get('flush_log_timer'),
263      '#options' => [
264        0 => $this->t('Never'),
265        3600 => $this->t('1 hour'),
266        10800 => $this->t('3 hours'),
267        21600 => $this->t('6 hours'),
268        32400 => $this->t('9 hours'),
269        43200 => $this->t('12 hours'),
270        86400 => $this->t('1 day'),
271        172800 => $this->t('2 days'),
272        259200 => $this->t('3 days'),
273        604800 => $this->t('1 week'),
274        1209600 => $this->t('2 weeks'),
275        4838400 => $this->t('1 month 3 weeks'),
276        9676800 => $this->t('3 months 3 weeks'),
277        31536000 => $this->t('1 year'),
278      ],
279      '#description' =>
280      $this->t('Older visitors log entries (including referrer statistics) will be automatically discarded. (Requires a correctly configured <a href="@cron">cron maintenance task</a>.)',
281          ['@cron' => Url::fromRoute('system.status')->toString()]
282      ),
283    ];
284
285    $form['retention']['bot_retention_log'] = [
286      '#type' => 'select',
287      '#title' => $this->t('Discard bot logs older than'),
288      '#default_value'   => $config->get('bot_retention_log'),
289      '#options' => [
290        -1 => $this->t('Do not log'),
291        0 => $this->t('Never'),
292        3600 => $this->t('1 hour'),
293        10800 => $this->t('3 hours'),
294        21600 => $this->t('6 hours'),
295        32400 => $this->t('9 hours'),
296        43200 => $this->t('12 hours'),
297        86400 => $this->t('1 day'),
298        172800 => $this->t('2 days'),
299        259200 => $this->t('3 days'),
300        604800 => $this->t('1 week'),
301        1209600 => $this->t('2 weeks'),
302        4838400 => $this->t('1 month 3 weeks'),
303        9676800 => $this->t('3 months 3 weeks'),
304        31536000 => $this->t('1 year'),
305      ],
306      '#description' =>
307      $this->t('Control how long or if visits by bots are logged.'),
308    ];
309
310    $form['miscellaneous'] = [
311      '#type' => 'details',
312      '#title' => $this->t('Miscellaneous'),
313      '#group' => 'tracking_scope',
314    ];
315
316    $script_type = $config->get('script_type');
317    $form['miscellaneous']['script_type'] = [
318      '#type' => 'radios',
319      '#title' => $this->t('Script type'),
320      '#options' => [
321        'minified' => $this->t('Minified'),
322        'full' => $this->t('Full'),
323      ],
324      '#default_value' => $script_type == 'full' ? 'full' : 'minified',
325      '#description' => $this->t('Full script is for debugging purposes. Minified script is for production.'),
326    ];
327    $form['miscellaneous']['items_per_page'] = [
328      '#type' => 'select',
329      '#title' => 'Items per page',
330      '#default_value' => $config->get('items_per_page'),
331      '#options' => [
332        5 => 5,
333        10 => 10,
334        25 => 25,
335        50 => 50,
336        100 => 100,
337        200 => 200,
338        250 => 250,
339        500 => 500,
340        1000 => 1000,
341      ],
342      '#description' =>
343      $this->t('This is only used for the referrer report.'),
344    ];
345
346    return $form;
347  }
348
349  /**
350   * {@inheritdoc}
351   */
352  public function submitForm(array &$form, FormStateInterface $form_state) {
353    $config = $this->config(self::SETTINGS);
354    $values = $form_state->getValues();
355
356    $config
357      ->set('theme', $values['theme'])
358      ->set('items_per_page', $values['items_per_page'])
359      ->set('flush_log_timer', $values['flush_log_timer'])
360      ->set('bot_retention_log', $values['bot_retention_log'])
361      ->set('track.userid', $values['visitors_trackuserid'])
362      ->set('counter.enabled', $values['counter_enabled'])
363      ->set('counter.entity_types', array_filter($values['entity_types'] ?? []))
364      ->set('disable_tracking', $values['visitors_disable_tracking'])
365      ->set('visibility.request_path_mode', $values['visitors_visibility_request_path_mode'])
366      ->set('visibility.request_path_pages', $values['visitors_visibility_request_path_pages'])
367      ->set('visibility.user_account_mode', $values['visitors_visibility_user_account_mode'])
368      ->set('visibility.user_role_mode', $values['visitors_visibility_user_role_mode'])
369      ->set('visibility.user_role_roles', array_filter($values['visitors_visibility_user_role_roles']))
370      ->set('visibility.exclude_user1', $values['visibility_exclude_user1'])
371      ->set('script_type', $values['script_type'] ?? 'minified')
372      ->save();
373
374    parent::submitForm($form, $form_state);
375  }
376
377  /**
378   * Returns a list of entity types.
379   */
380  protected function entityTypes() {
381    $entity_types_list = [];
382    $entity_definitions = $this->entityTypeManager->getDefinitions();
383    foreach ($entity_definitions as $entity_name => $entity_definition) {
384      if ($entity_definition instanceof ConfigEntityType) {
385        continue;
386      }
387      $entity_types_list[$entity_name] = (string) $entity_definition->getLabel();
388    }
389    asort($entity_types_list);
390
391    return $entity_types_list;
392  }
393
394  /**
395   * Returns a list of roles.
396   */
397  protected function roleOptions() {
398    $user_roles = $this->entityTypeManager->getStorage('user_role')->loadMultiple();
399    $options = [];
400    foreach ($user_roles as $role) {
401      $options[$role->id()] = $role->label();
402    }
403
404    return \array_map('\Drupal\Component\Utility\Html::escape', $options);
405  }
406
407}