Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
VisibilityService
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
6 / 6
22
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 isVisible
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 user
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
7
 page
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 getPathAlias
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 roles
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3namespace Drupal\visitors\Service;
4
5use Drupal\Core\Config\ConfigFactoryInterface;
6use Drupal\Core\Path\CurrentPathStack;
7use Drupal\Core\Path\PathMatcher;
8use Drupal\Core\Session\AccountInterface;
9use Drupal\Core\Session\AccountProxyInterface;
10use Drupal\path_alias\AliasManagerInterface;
11use Drupal\user\UserDataInterface;
12use Drupal\visitors\VisitorsVisibilityInterface;
13use Symfony\Component\HttpFoundation\RequestStack;
14
15/**
16 * Service for checking visitors visibility.
17 */
18class VisibilityService implements VisitorsVisibilityInterface {
19
20  /**
21   * The config object.
22   *
23   * @var \Drupal\Core\Config\ImmutableConfig
24   */
25  protected $config;
26
27  /**
28   * The current path.
29   *
30   * @var \Drupal\Core\Path\CurrentPathStack
31   */
32  protected $path;
33
34  /**
35   * The alias manager.
36   *
37   * @var \Drupal\path_alias\AliasManagerInterface
38   */
39  protected $aliasManager;
40
41  /**
42   * The path matcher.
43   *
44   * @var \Drupal\Core\Path\PathMatcher
45   */
46  protected $pathMatcher;
47
48  /**
49   * The user data service.
50   *
51   * @var \Drupal\user\UserDataInterface
52   */
53  protected $userData;
54
55  /**
56   * The request object.
57   *
58   * @var \Symfony\Component\HttpFoundation\Request
59   */
60  protected $request;
61
62  /**
63   * The current user.
64   *
65   * @var \Drupal\Core\Session\AccountProxyInterface
66   */
67  protected $currentUser;
68
69  /**
70   * The status codes.
71   *
72   * @var array
73   */
74  protected $statusCodes;
75
76  /**
77   * Constructs a new VisibilityService.
78   *
79   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
80   *   The config factory.
81   * @param \Drupal\Core\Path\CurrentPathStack $path_current
82   *   The current path.
83   * @param \Drupal\path_alias\AliasManagerInterface|null $alias_manager
84   *   The alias manager.
85   * @param \Drupal\Core\Path\PathMatcher $path_matcher
86   *   The path matcher.
87   * @param \Drupal\user\UserDataInterface $user_data
88   *   The user data service.
89   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
90   *   The request stack.
91   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
92   *   The current user.
93   */
94  public function __construct(ConfigFactoryInterface $config_factory, CurrentPathStack $path_current, ?AliasManagerInterface $alias_manager, PathMatcher $path_matcher, UserDataInterface $user_data, RequestStack $request_stack, AccountProxyInterface $current_user) {
95
96    $this->config = $config_factory->get('visitors.config');
97    $this->path = $path_current;
98    $this->aliasManager = $alias_manager;
99    $this->pathMatcher = $path_matcher;
100    $this->userData = $user_data;
101    $this->request = $request_stack->getCurrentRequest();
102    $this->currentUser = $current_user;
103  }
104
105  /**
106   * {@inheritdoc}
107   */
108  public function isVisible(): bool {
109    if ($this->config->get('disable_tracking')) {
110      return FALSE;
111    }
112
113    if (!$this->user($this->currentUser)) {
114      return FALSE;
115    }
116
117    return $this->page();
118  }
119
120  /**
121   * {@inheritdoc}
122   */
123  public function user(AccountInterface $account): bool {
124    $enabled = FALSE;
125    if ($this->config->get('visibility.exclude_user1') && $account->id() == 1) {
126      return FALSE;
127    }
128
129    // Is current user a member of a role that should be tracked?
130    if ($this->roles($account)) {
131
132      // Use the user's block visibility setting, if necessary.
133      $visibility_user_account_mode = $this->config->get('visibility.user_account_mode');
134      if ($visibility_user_account_mode != 0) {
135        $user_data_visitors = $this->userData->get('visitors', $account->id());
136        if ($account->id() && isset($user_data_visitors['user_account_users'])) {
137          $enabled = $user_data_visitors['user_account_users'];
138        }
139        else {
140          $enabled = ($visibility_user_account_mode == 1);
141        }
142      }
143      else {
144        $enabled = TRUE;
145      }
146
147    }
148
149    return $enabled;
150  }
151
152  /**
153   * {@inheritdoc}
154   */
155  public function page(): bool {
156    $page_match = NULL;
157
158    $visibility_request_path_mode = $this->config->get('visibility.request_path_mode');
159    $visibility_request_path_pages = $this->config->get('visibility.request_path_pages');
160
161    // Match path if necessary.
162    if (empty($visibility_request_path_pages)) {
163      $page_match = TRUE;
164
165      return $page_match;
166    }
167    // Convert path to lowercase. This allows comparison of the same path
168    // with different case. Ex: /Page, /page, /PAGE.
169    $pages = mb_strtolower($visibility_request_path_pages);
170
171    // Compare the lowercase path alias (if any) and internal path.
172    $path = $this->path->getPath();
173    $path_alias = $this->getPathAlias($path);
174
175    $alias_match = $this->pathMatcher->matchPath($path_alias, $pages);
176    $path_match = $this->pathMatcher->matchPath($path, $pages);
177    $page_match = $alias_match || ($path != $path_alias && $path_match);
178
179    // When $visibility_request_path_mode has a value of 0, the tracking
180    // code is displayed on all pages except those listed in $pages. When
181    // set to 1, it is displayed only on those pages listed in $pages.
182    $page_match = !($visibility_request_path_mode xor $page_match);
183
184    return $page_match;
185  }
186
187  /**
188   * Get the path alias.
189   */
190  protected function getPathAlias($path) {
191    $path_alias = mb_strtolower($path);
192
193    if ($this->aliasManager) {
194      $path_alias = $this->aliasManager->getAliasByPath($path);
195      if (!empty($path_alias)) {
196        $path_alias = mb_strtolower($path_alias);
197      }
198    }
199
200    return $path_alias;
201  }
202
203  /**
204   * {@inheritdoc}
205   */
206  public function roles(AccountInterface $account): bool {
207    $enabled = $visibility_user_role_mode = $this->config->get('visibility.user_role_mode') ?? 0;
208    $user_role_roles = $this->config->get('visibility.user_role_roles');
209
210    if (empty($user_role_roles)) {
211      return TRUE;
212    }
213    // One or more roles are selected.
214    foreach (array_values($account->getRoles()) as $user_role) {
215      // Is the current user a member of one of these roles?
216      if (in_array($user_role, $user_role_roles)) {
217        // Current user is a member of a role that should be tracked/excluded
218        // from tracking.
219        $enabled = !$visibility_user_role_mode;
220        break;
221      }
222    }
223
224    return $enabled;
225  }
226
227}