Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
106 / 106
100.00% covered (success)
100.00%
10 / 10
CRAP
100.00% covered (success)
100.00%
1 / 1
VisitorsPopularBlock
100.00% covered (success)
100.00%
106 / 106
100.00% covered (success)
100.00%
10 / 10
20
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 defaultConfiguration
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 blockAccess
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 blockForm
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
1
 entityTypes
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 blockSubmit
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 build
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
6
 entityLabelList
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
2
 getCacheTags
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Drupal\visitors\Plugin\Block;
4
5use Drupal\Core\Access\AccessResult;
6use Drupal\Core\Block\BlockBase;
7use Drupal\Core\Config\ConfigFactoryInterface;
8use Drupal\Core\Entity\EntityRepositoryInterface;
9use Drupal\Core\Entity\EntityTypeManagerInterface;
10use Drupal\Core\Form\FormStateInterface;
11use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
12use Drupal\Core\Render\RendererInterface;
13use Drupal\Core\Session\AccountInterface;
14use Drupal\visitors\VisitorsCounterInterface;
15use Symfony\Component\DependencyInjection\ContainerInterface;
16
17/**
18 * Provides a 'Popular content' block.
19 *
20 * @Block(
21 *   id = "visitors_popular_block",
22 *   admin_label = @Translation("Popular content")
23 * )
24 */
25final class VisitorsPopularBlock extends BlockBase implements ContainerFactoryPluginInterface {
26
27  /**
28   * The entity type manager.
29   *
30   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
31   */
32  protected $entityTypeManager;
33
34  /**
35   * The entity repository service.
36   *
37   * @var \Drupal\Core\Entity\EntityRepositoryInterface
38   */
39  protected $entityRepository;
40
41  /**
42   * The storage for visitor counter.
43   *
44   * @var \Drupal\visitors\VisitorsCounterInterface
45   */
46  protected $statisticsStorage;
47
48  /**
49   * The renderer interface.
50   *
51   * @var \Drupal\Core\Render\RendererInterface
52   */
53  protected $renderer;
54
55  /**
56   * The config factory.
57   *
58   * @var \Drupal\Core\Config\ConfigFactoryInterface
59   */
60  protected $configFactory;
61
62  /**
63   * Constructs a StatisticsPopularBlock object.
64   *
65   * @param array $configuration
66   *   A configuration array containing information about the plugin instance.
67   * @param string $plugin_id
68   *   The plugin_id for the plugin instance.
69   * @param mixed $plugin_definition
70   *   The plugin implementation definition.
71   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
72   *   The entity type manager.
73   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
74   *   The entity repository service.
75   * @param \Drupal\visitors\VisitorsCounterInterface $statistics_storage
76   *   The storage for statistics.
77   * @param \Drupal\Core\Render\RendererInterface $renderer
78   *   The renderer configuration array.
79   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
80   *   The config factory.
81   */
82  public function __construct(
83    array $configuration,
84    $plugin_id,
85    $plugin_definition,
86    EntityTypeManagerInterface $entity_type_manager,
87    EntityRepositoryInterface $entity_repository,
88    VisitorsCounterInterface $statistics_storage,
89    RendererInterface $renderer,
90    ConfigFactoryInterface $config_factory,
91  ) {
92    parent::__construct($configuration, $plugin_id, $plugin_definition);
93    $this->entityTypeManager = $entity_type_manager;
94    $this->entityRepository = $entity_repository;
95    $this->statisticsStorage = $statistics_storage;
96    $this->renderer = $renderer;
97    $this->configFactory = $config_factory;
98  }
99
100  /**
101   * {@inheritdoc}
102   */
103  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
104    return new self(
105      $configuration,
106      $plugin_id,
107      $plugin_definition,
108      $container->get('entity_type.manager'),
109      $container->get('entity.repository'),
110      $container->get('visitors.counter'),
111      $container->get('renderer'),
112      $container->get('config.factory'),
113    );
114  }
115
116  /**
117   * {@inheritdoc}
118   */
119  public function defaultConfiguration() {
120    return [
121      'top_day_num' => 0,
122      'top_all_num' => 0,
123      'top_last_num' => 0,
124      'entity_type' => 'node',
125    ];
126  }
127
128  /**
129   * {@inheritdoc}
130   */
131  protected function blockAccess(AccountInterface $account) {
132    $settings = $this->configFactory->get('visitors.config');
133    $entity_type = $this->configuration['entity_type'] ?? '';
134    $allowed_entity_types = $settings->get('counter.entity_types');
135    $disabled_or_entity_type_not_allowed = !$settings->get('counter.enabled') || !in_array($entity_type, $allowed_entity_types);
136    if ($disabled_or_entity_type_not_allowed) {
137      return AccessResult::forbidden();
138    }
139
140    return AccessResult::allowedIfHasPermission($account, 'access content');
141  }
142
143  /**
144   * {@inheritdoc}
145   */
146  public function blockForm($form, FormStateInterface $form_state) {
147    // Popular content block settings.
148    $numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 40];
149    $numbers = ['0' => $this->t('Disabled')] + array_combine($numbers, $numbers);
150    $form['statistics_block_top_day_num'] = [
151      '#type' => 'select',
152      '#title' => $this->t("Number of day's top views to display"),
153      '#default_value' => $this->configuration['top_day_num'],
154      '#options' => $numbers,
155      '#description' => $this->t('How many content items to display in "day" list.'),
156    ];
157    $form['statistics_block_top_all_num'] = [
158      '#type' => 'select',
159      '#title' => $this->t('Number of all time views to display'),
160      '#default_value' => $this->configuration['top_all_num'],
161      '#options' => $numbers,
162      '#description' => $this->t('How many content items to display in "all time" list.'),
163    ];
164    $form['statistics_block_top_last_num'] = [
165      '#type' => 'select',
166      '#title' => $this->t('Number of most recent views to display'),
167      '#default_value' => $this->configuration['top_last_num'],
168      '#options' => $numbers,
169      '#description' => $this->t('How many content items to display in "recently viewed" list.'),
170    ];
171    $form['entity_type'] = [
172      '#type' => 'radios',
173      '#title' => $this->t('Entity types'),
174      '#options' => $this->entityTypes(),
175      '#default_value' => $this->configuration['entity_type'] ?? 'node',
176      '#description' => $this->t('Select entity types to display popular content.'),
177    ];
178
179    return $form;
180  }
181
182  /**
183   * Returns a list of entity types.
184   */
185  protected function entityTypes() {
186    $allowed_entity_types = $this->configFactory->get('visitors.config')->get('counter.entity_types');
187    $entity_types_list = [];
188    $entity_definitions = $this->entityTypeManager->getDefinitions();
189    foreach ($entity_definitions as $entity_name => $entity_definition) {
190      if (!in_array($entity_name, $allowed_entity_types)) {
191        continue;
192      }
193      $entity_types_list[$entity_name] = (string) $entity_definition->getLabel();
194    }
195    asort($entity_types_list);
196
197    return $entity_types_list;
198  }
199
200  /**
201   * {@inheritdoc}
202   */
203  public function blockSubmit($form, FormStateInterface $form_state) {
204    $this->configuration['top_day_num'] = $form_state->getValue('statistics_block_top_day_num');
205    $this->configuration['top_all_num'] = $form_state->getValue('statistics_block_top_all_num');
206    $this->configuration['top_last_num'] = $form_state->getValue('statistics_block_top_last_num');
207    $this->configuration['entity_type'] = $form_state->getValue('entity_type');
208  }
209
210  /**
211   * {@inheritdoc}
212   */
213  public function build() {
214    $content = [];
215    $entity_type = 'node';
216    if ($this->configuration['top_day_num'] > 0) {
217      $ids = $this->statisticsStorage->fetchAll($entity_type, 'today', $this->configuration['top_day_num']);
218      if ($ids) {
219        $content['top_day'] = $this->entityLabelList($ids, $this->t("Today's:"));
220        $content['top_day']['#suffix'] = '<br />';
221      }
222    }
223
224    if ($this->configuration['top_all_num'] > 0) {
225      $ids = $this->statisticsStorage->fetchAll($entity_type, 'total', $this->configuration['top_all_num']);
226      if ($ids) {
227        $content['top_all'] = $this->entityLabelList($ids, $this->t('All time:'));
228        $content['top_all']['#suffix'] = '<br />';
229      }
230    }
231
232    if ($this->configuration['top_last_num'] > 0) {
233      $ids = $this->statisticsStorage->fetchAll($entity_type, 'timestamp', $this->configuration['top_last_num']);
234      $content['top_last'] = $this->entityLabelList($ids, $this->t('Last viewed:'));
235      $content['top_last']['#suffix'] = '<br />';
236    }
237
238    return $content;
239  }
240
241  /**
242   * Generates the ordered array of entity links for build().
243   *
244   * @param int[] $ids
245   *   An ordered array of entity ids.
246   * @param string $title
247   *   The title for the list.
248   *
249   * @return array
250   *   A render array for the list.
251   */
252  protected function entityLabelList(array $ids, $title) {
253    $entity_type = $this->configuration['entity_type'] ?? 'node';
254    $entities = $this->entityTypeManager->getStorage($entity_type)->loadMultiple($ids);
255
256    $items = [];
257    foreach ($ids as $id) {
258      $entity = $this->entityRepository->getTranslationFromContext($entities[$id]);
259      $item = $entity->toLink()->toRenderable();
260      $this->renderer->addCacheableDependency($item, $entity);
261      $items[] = $item;
262    }
263
264    return [
265      '#theme' => 'item_list',
266      '#items' => $items,
267      '#title' => $title,
268      '#cache' => [
269        'tags' => $this->entityTypeManager->getDefinition($entity_type)->getListCacheTags(),
270      ],
271    ];
272  }
273
274  /**
275   * {@inheritdoc}
276   */
277  public function getCacheTags() {
278    return ['config:visitors.config'];
279  }
280
281}