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