Skip to content
Snippets Groups Projects
Verified Commit aa1e7921 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3341092 by quietone, catch: Remove Statistics from core

parent 39d743b8
No related branches found
No related tags found
No related merge requests found
Showing
with 0 additions and 749 deletions
......@@ -137,10 +137,6 @@ AddEncoding gzip svgz
RewriteCond %{REQUEST_URI} !/core/[^/]*\.php$
# Allow access to test-specific PHP files:
RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php
# Allow access to Statistics module's custom front controller.
# Copy and adapt this rule to directly execute PHP files in contributed or
# custom modules or to run another PHP application in the same directory.
RewriteCond %{REQUEST_URI} !/core/modules/statistics/statistics\.php$
# Deny access to any other PHP files that do not match the rules above.
# Specifically, disallow autoload.php from being served directly.
RewriteRule "^(.+/.*|autoload)\.php($|/)" - [F]
......
......@@ -367,9 +367,6 @@ Shortcut
Stark
- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
Statistics
- ?
Syslog
- Mariano D'Agostino 'dagmar' https://www.drupal.org/u/dagmar
......
......@@ -137,10 +137,6 @@ AddEncoding gzip svgz
RewriteCond %{REQUEST_URI} !/core/[^/]*\.php$
# Allow access to test-specific PHP files:
RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php
# Allow access to Statistics module's custom front controller.
# Copy and adapt this rule to directly execute PHP files in contributed or
# custom modules or to run another PHP application in the same directory.
RewriteCond %{REQUEST_URI} !/core/modules/statistics/statistics\.php$
# Deny access to any other PHP files that do not match the rules above.
# Specifically, disallow autoload.php from being served directly.
RewriteRule "^(.+/.*|autoload)\.php($|/)" - [F]
......
......@@ -49,8 +49,6 @@ class MigrationPluginListTest extends KernelTestBase {
'path',
'search',
'shortcut',
// @todo Remove statistics in https://www.drupal.org/project/drupal/issues/3341092
'statistics',
'syslog',
'system',
'taxonomy',
......
......@@ -11,7 +11,6 @@
use Drupal\migrate\Plugin\migrate\destination\EntityConfigBase;
use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
use Drupal\shortcut\Plugin\migrate\destination\ShortcutSetUsers;
use Drupal\statistics\Plugin\migrate\destination\NodeCounter;
use Drupal\system\Plugin\migrate\destination\d7\ThemeSettings;
use Drupal\Tests\migrate_drupal\Kernel\MigrateDrupalTestBase;
use Drupal\Tests\migrate_drupal\Traits\CreateMigrationsTrait;
......@@ -122,7 +121,6 @@ protected function getContentClasses() {
return [
EntityContentBase::class,
BlockedIp::class,
NodeCounter::class,
UserData::class,
];
}
......
......@@ -67,8 +67,6 @@ class StateFileExistsTest extends MigrateDrupalTestBase {
'responsive_image',
'search',
'shortcut',
// @todo Remove statistics in https://www.drupal.org/project/drupal/issues/3341092
'statistics',
'syslog',
'system',
'taxonomy',
......
count_content_views: 0
display_max_age: 3600
# Schema for the configuration files of the statistics module.
statistics.settings:
type: config_object
label: 'Statistics settings'
mapping:
count_content_views:
type: integer
label: 'Count content views'
display_max_age:
type: integer
label: 'How long any statistics may be cached, i.e. the refresh interval'
block.settings.statistics_popular_block:
type: block_settings
label: 'Popular content block settings'
mapping:
top_day_num:
type: integer
label: 'Number of day\s top views to display'
top_all_num:
type: integer
label: 'Number of all time views to display'
top_last_num:
type: integer
label: 'Number of most recent views to display'
# Schema for the views plugins of the Statistics module.
views.field.statistics_numeric:
type: views.field.numeric
label: 'Numeric values from the statistics module'
views.field.node_counter_timestamp:
type: views.field.date
label: 'The most recent time the node has been viewed'
---
label: 'Tracking and displaying popular content'
related:
- core.tracking_content
- history.tracking_user_content
- block.place
---
{% set statistics_settings_link_text %}{% trans %}Statistics{% endtrans %}{% endset %}
{% set permissions_link_text %}{% trans %}Permissions{% endtrans %}{% endset %}
{% set statistics_settings_link = render_var(help_route_link(statistics_settings_link_text, 'statistics.settings')) %}
{% set permissions_link = render_var(help_route_link(permissions_link_text, 'user.admin_permissions')) %}
<h2>{% trans %}Goal{% endtrans %}</h2>
<p>{% trans %}Configure and display tracking of how many times content has been viewed on your site, assuming that the core Statistics module is currently installed.{% endtrans %}</p>
<h2>{% trans %}What are the options for displaying popularity tracking?{% endtrans %}</h2>
<p>{% trans %}You can display a <em>content hits</em> counter of how many times a content item has been viewed, at the bottom of content item pages. You can also place a <em>Popular content</em> block in a region of your theme, which shows a list of the most popular and most recently-viewed content.{% endtrans %}</p>
<h2>{% trans %}Steps{% endtrans %}</h2>
<ol>
<li>{% trans %}In the <em>Manage</em> administrative menu, navigate to <em>Configuration</em> &gt; <em>System</em> &gt; <em>{{ statistics_settings_link }}</em>.{% endtrans %}</li>
<li>{% trans %}Check <em>Count content views</em> and click <em>Save configuration</em>.{% endtrans %}</li>
<li>{% trans %}In the <em>Manage</em> administrative menu, navigate to <em>People</em> &gt; <em>{{ permissions_link }}</em>.{% endtrans %}</li>
<li>{% trans %}In the <em>Statistics</em> section, check or uncheck the <em>View content hits</em> permission for each role. Click <em>Save permissions</em>.{% endtrans %}</li>
<li>{% trans %}Optionally, in the <em>Manage</em> administrative menu, navigate to <em>Structure</em> &gt; <em>Block layout</em>. Place the <em>Popular content</em> block in a region in your theme (you will need to have the core Block module installed; see related topic for more details on block placement).{% endtrans %}</li>
</ol>
finished:
6:
statistics: statistics
7:
statistics: statistics
# cspell:ignore daycount totalcount
id: statistics_node_counter
label: Node counter
migration_tags:
- Drupal 6
- Drupal 7
- Content
source:
plugin: node_counter
process:
nid:
-
plugin: migration_lookup
migration:
- d6_node_complete
- d7_node_complete
- d6_node
- d7_node
source: nid
-
plugin: node_complete_node_lookup
-
plugin: skip_on_empty
method: row
totalcount: totalcount
daycount: daycount
timestamp: timestamp
destination:
plugin: node_counter
migration_dependencies:
optional:
- d6_node
- d7_node
# cspell:ignore daycount totalcount
id: statistics_node_translation_counter
label: Node translation counter
migration_tags:
- Drupal 6
- Drupal 7
- Content
- Multilingual
source:
plugin: node_counter
process:
nid:
-
plugin: migration_lookup
migration:
- d6_node_translation
- d7_node_translation
source: nid
-
plugin: skip_on_empty
method: row
-
plugin: extract
index:
- 0
totalcount: totalcount
daycount: daycount
timestamp: timestamp
destination:
plugin: node_counter
migration_dependencies:
required:
- language
- statistics_node_counter
optional:
- d6_node_translation
- d7_node_translation
# cspell:ignore accesslog
id: statistics_settings
label: Statistics configuration
migration_tags:
- Drupal 6
- Drupal 7
- Configuration
source:
plugin: variable
variables:
- statistics_enable_access_log
- statistics_flush_accesslog_timer
- statistics_count_content_views
source_module: statistics
process:
'count_content_views': statistics_count_content_views
destination:
plugin: config
config_name: statistics.settings
<?php
namespace Drupal\statistics;
use Drupal\Core\Database\Connection;
use Drupal\Core\State\StateInterface;
use Symfony\Component\HttpFoundation\RequestStack;
// cspell:ignore daycount totalcount
/**
* Provides the default database storage backend for statistics.
*/
class NodeStatisticsDatabaseStorage implements StatisticsStorageInterface {
/**
* The database connection used.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* Constructs the statistics storage.
*
* @param \Drupal\Core\Database\Connection $connection
* The database connection for the node view storage.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* The request stack.
*/
public function __construct(Connection $connection, StateInterface $state, RequestStack $request_stack) {
$this->connection = $connection;
$this->state = $state;
$this->requestStack = $request_stack;
}
/**
* {@inheritdoc}
*/
public function recordView($id) {
return (bool) $this->connection
->merge('node_counter')
->key('nid', $id)
->fields([
'daycount' => 1,
'totalcount' => 1,
'timestamp' => $this->getRequestTime(),
])
->expression('daycount', '[daycount] + 1')
->expression('totalcount', '[totalcount] + 1')
->execute();
}
/**
* {@inheritdoc}
*/
public function fetchViews($ids) {
$views = $this->connection
->select('node_counter', 'nc')
->fields('nc', ['totalcount', 'daycount', 'timestamp'])
->condition('nid', $ids, 'IN')
->execute()
->fetchAll();
foreach ($views as $id => $view) {
$views[$id] = new StatisticsViewsResult($view->totalcount, $view->daycount, $view->timestamp);
}
return $views;
}
/**
* {@inheritdoc}
*/
public function fetchView($id) {
$views = $this->fetchViews([$id]);
return reset($views);
}
/**
* {@inheritdoc}
*/
public function fetchAll($order = 'totalcount', $limit = 5) {
assert(in_array($order, ['totalcount', 'daycount', 'timestamp']), "Invalid order argument.");
return $this->connection
->select('node_counter', 'nc')
->fields('nc', ['nid'])
->orderBy($order, 'DESC')
->range(0, $limit)
->execute()
->fetchCol();
}
/**
* {@inheritdoc}
*/
public function deleteViews($id) {
return (bool) $this->connection
->delete('node_counter')
->condition('nid', $id)
->execute();
}
/**
* {@inheritdoc}
*/
public function resetDayCount() {
$statistics_timestamp = $this->state->get('statistics.day_timestamp', 0);
if (($this->getRequestTime() - $statistics_timestamp) >= 86400) {
$this->state->set('statistics.day_timestamp', $this->getRequestTime());
$this->connection->update('node_counter')
->fields(['daycount' => 0])
->execute();
}
}
/**
* {@inheritdoc}
*/
public function maxTotalCount() {
$query = $this->connection->select('node_counter', 'nc');
$query->addExpression('MAX([totalcount])');
$max_total_count = (int) $query->execute()->fetchField();
return $max_total_count;
}
/**
* Get current request time.
*
* @return int
* Unix timestamp for current server request time.
*/
protected function getRequestTime() {
return $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME');
}
}
<?php
namespace Drupal\statistics\Plugin\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\Attribute\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\statistics\StatisticsStorageInterface;
// cspell:ignore daycount totalcount
/**
* Provides a 'Popular content' block.
*/
#[Block(
id: "statistics_popular_block",
admin_label: new TranslatableMarkup("Popular content"),
)]
class StatisticsPopularBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity repository service.
*
* @var \Drupal\Core\Entity\EntityRepositoryInterface
*/
protected $entityRepository;
/**
* The storage for statistics.
*
* @var \Drupal\statistics\StatisticsStorageInterface
*/
protected $statisticsStorage;
/**
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* Constructs a StatisticsPopularBlock object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
* The entity repository service
* @param \Drupal\statistics\StatisticsStorageInterface $statistics_storage
* The storage for statistics.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer configuration array.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityRepositoryInterface $entity_repository, StatisticsStorageInterface $statistics_storage, RendererInterface $renderer) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entity_type_manager;
$this->entityRepository = $entity_repository;
$this->statisticsStorage = $statistics_storage;
$this->renderer = $renderer;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('entity.repository'),
$container->get('statistics.storage.node'),
$container->get('renderer')
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'top_day_num' => 0,
'top_all_num' => 0,
'top_last_num' => 0,
];
}
/**
* {@inheritdoc}
*/
protected function blockAccess(AccountInterface $account) {
return AccessResult::allowedIfHasPermission($account, 'access content');
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
// Popular content block settings.
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 40];
$numbers = ['0' => $this->t('Disabled')] + array_combine($numbers, $numbers);
$form['statistics_block_top_day_num'] = [
'#type' => 'select',
'#title' => $this->t("Number of day's top views to display"),
'#default_value' => $this->configuration['top_day_num'],
'#options' => $numbers,
'#description' => $this->t('How many content items to display in "day" list.'),
];
$form['statistics_block_top_all_num'] = [
'#type' => 'select',
'#title' => $this->t('Number of all time views to display'),
'#default_value' => $this->configuration['top_all_num'],
'#options' => $numbers,
'#description' => $this->t('How many content items to display in "all time" list.'),
];
$form['statistics_block_top_last_num'] = [
'#type' => 'select',
'#title' => $this->t('Number of most recent views to display'),
'#default_value' => $this->configuration['top_last_num'],
'#options' => $numbers,
'#description' => $this->t('How many content items to display in "recently viewed" list.'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
$this->configuration['top_day_num'] = $form_state->getValue('statistics_block_top_day_num');
$this->configuration['top_all_num'] = $form_state->getValue('statistics_block_top_all_num');
$this->configuration['top_last_num'] = $form_state->getValue('statistics_block_top_last_num');
}
/**
* {@inheritdoc}
*/
public function build() {
$content = [];
if ($this->configuration['top_day_num'] > 0) {
$nids = $this->statisticsStorage->fetchAll('daycount', $this->configuration['top_day_num']);
if ($nids) {
$content['top_day'] = $this->nodeTitleList($nids, $this->t("Today's:"));
$content['top_day']['#suffix'] = '<br />';
}
}
if ($this->configuration['top_all_num'] > 0) {
$nids = $this->statisticsStorage->fetchAll('totalcount', $this->configuration['top_all_num']);
if ($nids) {
$content['top_all'] = $this->nodeTitleList($nids, $this->t('All time:'));
$content['top_all']['#suffix'] = '<br />';
}
}
if ($this->configuration['top_last_num'] > 0) {
$nids = $this->statisticsStorage->fetchAll('timestamp', $this->configuration['top_last_num']);
$content['top_last'] = $this->nodeTitleList($nids, $this->t('Last viewed:'));
$content['top_last']['#suffix'] = '<br />';
}
return $content;
}
/**
* Generates the ordered array of node links for build().
*
* @param int[] $nids
* An ordered array of node ids.
* @param string $title
* The title for the list.
*
* @return array
* A render array for the list.
*/
protected function nodeTitleList(array $nids, $title) {
$nodes = $this->entityTypeManager->getStorage('node')->loadMultiple($nids);
$items = [];
foreach ($nids as $nid) {
$node = $this->entityRepository->getTranslationFromContext($nodes[$nid]);
$item = $node->toLink()->toRenderable();
$this->renderer->addCacheableDependency($item, $node);
$items[] = $item;
}
return [
'#theme' => 'item_list__node',
'#items' => $items,
'#title' => $title,
'#cache' => [
'tags' => $this->entityTypeManager->getDefinition('node')->getListCacheTags(),
],
];
}
}
<?php
namespace Drupal\statistics\Plugin\migrate\destination;
use Drupal\Core\Database\Connection;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\Attribute\MigrateDestination;
use Drupal\migrate\Plugin\migrate\destination\DestinationBase;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
// cspell:ignore daycount totalcount
/**
* Destination for node counter.
*/
#[MigrateDestination(
id: 'node_counter',
destination_module: 'statistics'
)]
class NodeCounter extends DestinationBase implements ContainerFactoryPluginInterface {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* Constructs a node counter plugin.
*
* @param array $configuration
* Plugin configuration.
* @param string $plugin_id
* The plugin ID.
* @param mixed $plugin_definition
* The plugin definition.
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
* The current migration.
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, Connection $connection) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration);
$this->connection = $connection;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$migration,
$container->get('database')
);
}
/**
* {@inheritdoc}
*/
public function getIds() {
return ['nid' => ['type' => 'integer']];
}
/**
* {@inheritdoc}
*/
public function fields() {
return [
'nid' => $this->t('The ID of the node to which these statistics apply.'),
'totalcount' => $this->t('The total number of times the node has been viewed.'),
'daycount' => $this->t('The total number of times the node has been viewed today.'),
'timestamp' => $this->t('The most recent time the node has been viewed.'),
];
}
/**
* {@inheritdoc}
*/
public function import(Row $row, array $old_destination_id_values = []) {
$nid = $row->getDestinationProperty('nid');
$daycount = $row->getDestinationProperty('daycount');
$totalcount = $row->getDestinationProperty('totalcount');
$timestamp = $row->getDestinationProperty('timestamp');
$this->connection
->merge('node_counter')
->key('nid', $nid)
->fields([
'daycount' => $daycount,
'totalcount' => $totalcount,
'timestamp' => $timestamp,
])
->expression('daycount', '[daycount] + :daycount', [':daycount' => $daycount])
->expression('totalcount', '[totalcount] + :totalcount', [':totalcount' => $totalcount])
// Per Drupal policy: "A query may have any number of placeholders, but
// all must have unique names even if they have the same value."
// https://www.drupal.org/docs/drupal-apis/database-api/static-queries#placeholders
->expression('timestamp', 'CASE WHEN [timestamp] > :timestamp1 THEN [timestamp] ELSE :timestamp2 END', [':timestamp1' => $timestamp, ':timestamp2' => $timestamp])
->execute();
return [$row->getDestinationProperty('nid')];
}
}
<?php
namespace Drupal\statistics\Plugin\migrate\source;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
// cspell:ignore daycount totalcount
/**
* Drupal 6/7 node counter source from database.
*
* For available configuration keys, refer to the parent classes.
*
* @see \Drupal\migrate\Plugin\migrate\source\SqlBase
* @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
*
* @MigrateSource(
* id = "node_counter",
* source_module = "statistics"
* )
*/
class NodeCounter extends DrupalSqlBase {
/**
* {@inheritdoc}
*/
public function query() {
return $this->select('node_counter', 'nc')->fields('nc');
}
/**
* {@inheritdoc}
*/
public function fields() {
return [
'nid' => $this->t('The node ID.'),
'totalcount' => $this->t('The total number of times the node has been viewed.'),
'daycount' => $this->t('The total number of times the node has been viewed today.'),
'timestamp' => $this->t('The most recent time the node has been viewed.'),
];
}
/**
* {@inheritdoc}
*/
public function getIds() {
$ids['nid']['type'] = 'integer';
return $ids;
}
}
<?php
namespace Drupal\statistics\Plugin\views\field;
use Drupal\views\Attribute\ViewsField;
use Drupal\views\Plugin\views\field\Date;
use Drupal\Core\Session\AccountInterface;
/**
* Field handler to display the most recent time the node has been viewed.
*
* @ingroup views_field_handlers
*/
#[ViewsField("node_counter_timestamp")]
class NodeCounterTimestamp extends Date {
/**
* {@inheritdoc}
*/
public function access(AccountInterface $account) {
return $account->hasPermission('view post access counter');
}
}
<?php
namespace Drupal\statistics\Plugin\views\field;
use Drupal\views\Attribute\ViewsField;
use Drupal\views\Plugin\views\field\NumericField;
use Drupal\Core\Session\AccountInterface;
/**
* Field handler to display numeric values from the statistics module.
*
* @ingroup views_field_handlers
*/
#[ViewsField("statistics_numeric")]
class StatisticsNumeric extends NumericField {
/**
* {@inheritdoc}
*/
public function access(AccountInterface $account) {
return $account->hasPermission('view post access counter');
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment