diff --git a/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php b/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php index f39057308c..71631a72c3 100644 --- a/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php +++ b/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php @@ -241,9 +241,9 @@ public function testBlockMigration() { // Check statistic block settings. $settings = [ - 'id' => 'broken', + 'id' => 'statistics_popular_block', 'label' => '', - 'provider' => 'core', + 'provider' => 'statistics', 'label_display' => '0', 'top_day_num' => 7, 'top_all_num' => 8, diff --git a/core/modules/search/tests/src/Functional/SearchRankingTest.php b/core/modules/search/tests/src/Functional/SearchRankingTest.php index d444d3e2fa..028e2deca0 100644 --- a/core/modules/search/tests/src/Functional/SearchRankingTest.php +++ b/core/modules/search/tests/src/Functional/SearchRankingTest.php @@ -114,11 +114,12 @@ public function testRankings() { $this->submitForm($edit, 'Save'); // Enable counting of statistics. - $this->config('statistics.settings')->set('count_content_views', 1)->save(); + $this->config('statistics.settings')->set('entity_type_ids', ['node'])->save(); // Simulating content views is kind of difficult in the test. Leave that // to the Statistics module. So instead go ahead and manually update the // counter for this node. + \Drupal::service('statistics.storage')->createTable(\Drupal::entityTypeManager()->getDefinition('node')); $nid = $nodes['views'][1]->id(); Database::getConnection()->insert('node_counter') ->fields(['totalcount' => 5, 'daycount' => 5, 'timestamp' => REQUEST_TIME, 'nid' => $nid]) diff --git a/core/modules/statistics/config/install/statistics.settings.yml b/core/modules/statistics/config/install/statistics.settings.yml index 6686062923..8c73e19338 100644 --- a/core/modules/statistics/config/install/statistics.settings.yml +++ b/core/modules/statistics/config/install/statistics.settings.yml @@ -1,2 +1,2 @@ -count_content_views: 0 +entity_type_ids: {} display_max_age: 3600 diff --git a/core/modules/statistics/config/schema/statistics.schema.yml b/core/modules/statistics/config/schema/statistics.schema.yml index c72a227264..0c2cd05008 100644 --- a/core/modules/statistics/config/schema/statistics.schema.yml +++ b/core/modules/statistics/config/schema/statistics.schema.yml @@ -4,12 +4,15 @@ 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' + entity_type_ids: + type: sequence + label: 'Entity Type IDs' + sequence: + type: string + label: 'Entity Type ID' block.settings.statistics_popular_block: type: block_settings diff --git a/core/modules/statistics/migrations/statistics_settings.yml b/core/modules/statistics/migrations/statistics_settings.yml index edc0e049f7..f1f8942319 100644 --- a/core/modules/statistics/migrations/statistics_settings.yml +++ b/core/modules/statistics/migrations/statistics_settings.yml @@ -12,7 +12,11 @@ source: - statistics_count_content_views source_module: statistics process: - 'count_content_views': statistics_count_content_views + entity_type_ids: + - + plugin: callback + callable: statistics_migrate_callback + source: statistics_count_content_views destination: plugin: config config_name: statistics.settings diff --git a/core/modules/statistics/src/NodeStatisticsDatabaseStorage.php b/core/modules/statistics/src/NodeStatisticsDatabaseStorage.php index 583f5be9d4..4dad486aea 100644 --- a/core/modules/statistics/src/NodeStatisticsDatabaseStorage.php +++ b/core/modules/statistics/src/NodeStatisticsDatabaseStorage.php @@ -2,139 +2,67 @@ namespace Drupal\statistics; -use Drupal\Core\Database\Connection; -use Drupal\Core\State\StateInterface; -use Symfony\Component\HttpFoundation\RequestStack; +use Drupal\Core\Entity\EntityTypeInterface; /** * 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; - } +class NodeStatisticsDatabaseStorage extends StatisticsDatabaseStorage { /** * {@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(); + public function recordView($entity_type_id, $key, $id) { + // TODO: Change the autogenerated stub. + return parent::recordView($entity_type_id, $key, $id); } /** * {@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; + public function fetchView(EntityTypeInterface $entity_type, $id) { + // TODO: Change the autogenerated stub. + return parent::fetchView($entity_type, $id); } /** * {@inheritdoc} */ - public function fetchView($id) { - $views = $this->fetchViews([$id]); - return reset($views); + public function fetchAll(EntityTypeInterface $entity_type, $order = 'totalcount', $limit = 5) { + // TODO: Change the autogenerated stub. + return parent::fetchAll($entity_type, $order, $limit); } /** * {@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(); + public function deleteViews(EntityTypeInterface $entity_type, $id) { + // TODO: Change the autogenerated stub. + return parent::deleteViews($entity_type, $id); } /** * {@inheritdoc} */ - public function deleteViews($id) { - return (bool) $this->connection - ->delete('node_counter') - ->condition('nid', $id) - ->execute(); + public function maxTotalCount(EntityTypeInterface $entity_type) { + // TODO: Change the autogenerated stub. + return parent::maxTotalCount($entity_type); } /** * {@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(); - } + public function createTable(EntityTypeInterface $entity_type) { + // TODO: Change the autogenerated stub. + parent::createTable($entity_type); } /** * {@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; + public function dropTable(EntityTypeInterface $entity_type) { + // TODO: Change the autogenerated stub. + parent::dropTable($entity_type); } /** diff --git a/core/modules/statistics/src/Plugin/Block/StatisticsPopularBlock.php b/core/modules/statistics/src/Plugin/Block/StatisticsPopularBlock.php index 53e39c3333..0f6fa25ea5 100644 --- a/core/modules/statistics/src/Plugin/Block/StatisticsPopularBlock.php +++ b/core/modules/statistics/src/Plugin/Block/StatisticsPopularBlock.php @@ -85,7 +85,7 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_definition, $container->get('entity_type.manager'), $container->get('entity.repository'), - $container->get('statistics.storage.node'), + $container->get('statistics.storage'), $container->get('renderer') ); } @@ -153,9 +153,10 @@ public function blockSubmit($form, FormStateInterface $form_state) { */ public function build() { $content = []; + $entity_type = $this->entityTypeManager->getDefinition('node'); if ($this->configuration['top_day_num'] > 0) { - $nids = $this->statisticsStorage->fetchAll('daycount', $this->configuration['top_day_num']); + $nids = $this->statisticsStorage->fetchAll($entity_type, 'daycount', $this->configuration['top_day_num']); if ($nids) { $content['top_day'] = $this->nodeTitleList($nids, $this->t("Today's:")); $content['top_day']['#suffix'] = '
'; @@ -163,7 +164,7 @@ public function build() { } if ($this->configuration['top_all_num'] > 0) { - $nids = $this->statisticsStorage->fetchAll('totalcount', $this->configuration['top_all_num']); + $nids = $this->statisticsStorage->fetchAll($entity_type, 'totalcount', $this->configuration['top_all_num']); if ($nids) { $content['top_all'] = $this->nodeTitleList($nids, $this->t('All time:')); $content['top_all']['#suffix'] = '
'; @@ -171,7 +172,7 @@ public function build() { } if ($this->configuration['top_last_num'] > 0) { - $nids = $this->statisticsStorage->fetchAll('timestamp', $this->configuration['top_last_num']); + $nids = $this->statisticsStorage->fetchAll($entity_type, 'timestamp', $this->configuration['top_last_num']); $content['top_last'] = $this->nodeTitleList($nids, $this->t('Last viewed:')); $content['top_last']['#suffix'] = '
'; } diff --git a/core/modules/statistics/src/Plugin/views/field/EntityCounterTimestamp.php b/core/modules/statistics/src/Plugin/views/field/EntityCounterTimestamp.php new file mode 100644 index 0000000000..9bc2a9c089 --- /dev/null +++ b/core/modules/statistics/src/Plugin/views/field/EntityCounterTimestamp.php @@ -0,0 +1,24 @@ +hasPermission('view post access counter'); + } + +} diff --git a/core/modules/statistics/src/Plugin/views/field/NodeCounterTimestamp.php b/core/modules/statistics/src/Plugin/views/field/NodeCounterTimestamp.php index fb0eb3049e..037526920f 100644 --- a/core/modules/statistics/src/Plugin/views/field/NodeCounterTimestamp.php +++ b/core/modules/statistics/src/Plugin/views/field/NodeCounterTimestamp.php @@ -2,9 +2,6 @@ namespace Drupal\statistics\Plugin\views\field; -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. * @@ -12,13 +9,6 @@ * * @ViewsField("node_counter_timestamp") */ -class NodeCounterTimestamp extends Date { - - /** - * {@inheritdoc} - */ - public function access(AccountInterface $account) { - return $account->hasPermission('view post access counter'); - } +class NodeCounterTimestamp extends EntityCounterTimestamp { } diff --git a/core/modules/statistics/src/StatisticsDatabaseStorage.php b/core/modules/statistics/src/StatisticsDatabaseStorage.php new file mode 100644 index 0000000000..9e8937493c --- /dev/null +++ b/core/modules/statistics/src/StatisticsDatabaseStorage.php @@ -0,0 +1,245 @@ +connection = $connection; + $this->requestStack = $request_stack; + } + + /** + * {@inheritdoc} + */ + public function recordView($entity_type_id, $key, $id) { + $table = $entity_type_id . '_counter'; + try { + return (bool) $this->connection + ->merge($table) + ->key($key, $id) + ->fields([ + 'daycount' => 1, + 'totalcount' => 1, + 'timestamp' => $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME'), + ]) + ->expression('daycount', 'daycount + 1') + ->expression('totalcount', 'totalcount + 1') + ->execute(); + } + catch (\Exception $e) { + $database_schema = $this->connection->schema(); + if ($database_schema->tableExists($table)) { + throw $e; + } + else { + $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id); + $this->createTable($entity_type); + $this->recordView($entity_type_id, $key, $id); + } + } + } + + /** + * {@inheritdoc} + */ + public function fetchView(EntityTypeInterface $entity_type, $id) { + try { + $view = $this->connection + ->select($this->tableName($entity_type), 'c') + ->fields('c', ['totalcount', 'daycount', 'timestamp']) + ->condition($entity_type->getKey('id'), $id) + ->execute() + ->fetchObject(); + if ($view) { + return new StatisticsViewsResult($view->totalcount, $view->daycount, $view->timestamp); + } + } + catch (\Exception $e) { + $this->catchException($entity_type, $e); + } + return NULL; + } + + /** + * {@inheritdoc} + */ + public function fetchAll(EntityTypeInterface $entity_type, $order = 'totalcount', $limit = 5) { + assert(in_array($order, ['totalcount', 'daycount', 'timestamp']), "Invalid order argument."); + try { + return $this->connection + ->select($this->tableName($entity_type), 'nc') + ->fields('nc', [$entity_type->getKey('id')]) + ->orderBy($order, 'DESC') + ->range(0, $limit) + ->execute() + ->fetchCol(); + } + catch (\Exception $e) { + $this->catchException($entity_type, $e); + } + return []; + } + + /** + * {@inheritdoc} + */ + public function deleteViews(EntityTypeInterface $entity_type, $id) { + try { + return (bool) $this->connection + ->delete($this->tableName($entity_type)) + ->condition($entity_type->getKey('id'), $id) + ->execute(); + } + catch (\Exception $e) { + $this->catchException($entity_type, $e); + } + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function maxTotalCount(EntityTypeInterface $entity_type) { + try { + $query = $this->connection->select($this->tableName($entity_type), 'nc'); + $query->addExpression('MAX(totalcount)'); + $max_total_count = (int) $query->execute()->fetchField(); + return $max_total_count; + } + catch (\Exception $e) { + $this->catchException($entity_type, $e); + } + } + + /** + * {@inheritdoc} + */ + public function createTable(EntityTypeInterface $entity_type) { + $entity_type_id = $entity_type->id(); + $idKey = $entity_type->getKey('id'); + $id_definition = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($entity_type_id)[$idKey]; + if ($id_definition->getType() === 'integer') { + $id_schema = [ + 'description' => "The {{$entity_type_id}}.$idKey for these statistics.", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ]; + } + else { + $id_schema = [ + 'description' => "The {{$entity_type_id}}.$idKey for these statistics.", + 'type' => 'varchar_ascii', + 'length' => 128, + 'not null' => TRUE, + ]; + } + $table = $entity_type_id . '_counter'; + if (!$this->connection->schema()->tableExists($table)) { + $schema = [ + 'description' => "Access statistics for {{$entity_type_id}}s.", + 'fields' => [ + $idKey => $id_schema, + 'totalcount' => [ + 'description' => "The total number of times the {{$entity_type_id}} has been viewed.", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'big', + ], + 'daycount' => [ + 'description' => "The total number of times the {{$entity_type_id}} has been viewed today.", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'medium', + ], + 'timestamp' => [ + 'description' => "The most recent time the {{$entity_type_id}} has been viewed.", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ], + ], + 'primary key' => [$idKey], + ]; + $this->connection->schema()->createTable($table, $schema); + } + } + + /** + * {@inheritdoc} + */ + public function dropTable(EntityTypeInterface $entity_type) { + if ($this->connection->schema()->tableExists($this->tableName($entity_type))) { + $this->connection->schema()->dropTable($this->tableName($entity_type)); + } + } + + /** + * Act on an exception when the table might not have been created. + * + * If the table does not yet exist, that's fine, but if the table exists and + * something else caused the exception, then propagate it. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * @param \Exception $e + * The exception. + * + * @throws \Exception + */ + protected function catchException(EntityTypeInterface $entity_type, \Exception $e) { + if ($this->connection->schema()->tableExists($this->tableName($entity_type))) { + throw $e; + } + } + + /** + * Generates the table name from the entity type. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return string + */ + protected function tableName(EntityTypeInterface $entity_type) { + $entity_type_id = $entity_type->id(); + return $entity_type_id . '_counter'; + } + +} diff --git a/core/modules/statistics/src/StatisticsResetCount.php b/core/modules/statistics/src/StatisticsResetCount.php new file mode 100644 index 0000000000..e87d7f1487 --- /dev/null +++ b/core/modules/statistics/src/StatisticsResetCount.php @@ -0,0 +1,66 @@ +connection = $connection; + $this->state = $state; + $this->time = $time; + } + + /** + * {@inheritdoc} + */ + public function resetDayCount($entity_type_id) { + $statistics_timestamp = $this->state->get('statistics.day_timestamp') ?: 0; + $time = $this->time->getRequestTime(); + $table = $entity_type_id . '_counter'; + if (($time - $statistics_timestamp) >= 86400 && $this->connection->schema()->tableExists($table)) { + $this->state->set('statistics.day_timestamp', $time); + $this->connection->update($table) + ->fields(['daycount' => 0]) + ->execute(); + } + } + +} diff --git a/core/modules/statistics/src/StatisticsResetCountInterface.php b/core/modules/statistics/src/StatisticsResetCountInterface.php new file mode 100644 index 0000000000..d4b493cfa3 --- /dev/null +++ b/core/modules/statistics/src/StatisticsResetCountInterface.php @@ -0,0 +1,18 @@ +entityTypeManager = $entity_type_manager; + $this->entityTypeRepository = $entity_type_repository; + $this->statisticsStorage = $statistics_storage; $this->moduleHandler = $module_handler; } @@ -42,7 +74,10 @@ public function __construct(ConfigFactoryInterface $config_factory, ModuleHandle public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), - $container->get('module_handler') + $container->get('module_handler'), + $container->get('entity_type.manager'), + $container->get('entity_type.repository'), + $container->get('statistics.storage') ); } @@ -65,18 +100,26 @@ protected function getEditableConfigNames() { */ public function buildForm(array $form, FormStateInterface $form_state) { $config = $this->config('statistics.settings'); + $labels = $this->entityTypeRepository->getEntityTypeLabels(TRUE); + $labels = $labels[(string) $this->t('Content', [], ['context' => 'Entity type group'])]; + $options = []; + foreach ($labels as $entity_type_id => $label) { + $options[$entity_type_id] = $this->t('Enable statistics for @entity_type', ['@entity_type' => $label]); + } - // Content counter settings. + // Content entity counter settings. $form['content'] = [ '#type' => 'details', '#title' => $this->t('Content viewing counter settings'), + '#description' => $this->t('Increment a counter each time content is viewed.'), '#open' => TRUE, ]; - $form['content']['statistics_count_content_views'] = [ - '#type' => 'checkbox', - '#title' => $this->t('Count content views'), - '#default_value' => $config->get('count_content_views'), - '#description' => $this->t('Increment a counter each time content is viewed.'), + + $form['content']['entity_type_ids'] = [ + '#type' => 'checkboxes', + '#title' => $this->t('Select content'), + '#options' => $options, + '#default_value' => $config->get('entity_type_ids'), ]; return parent::buildForm($form, $form_state); @@ -86,9 +129,19 @@ public function buildForm(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { + $entity_type_ids = $form_state->getValue('entity_type_ids'); $this->config('statistics.settings') - ->set('count_content_views', $form_state->getValue('statistics_count_content_views')) + ->set('entity_type_ids', $entity_type_ids) ->save(); + foreach (array_keys($entity_type_ids) as $entity_type_id) { + $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); + if ($entity_type_ids[$entity_type_id] === $entity_type_id) { + $this->statisticsStorage->createTable($entity_type); + } + else { + $this->statisticsStorage->dropTable($entity_type); + } + } // The popular statistics block is dependent on these settings, so clear the // block plugin definitions cache. diff --git a/core/modules/statistics/src/StatisticsStorageInterface.php b/core/modules/statistics/src/StatisticsStorageInterface.php index b5da88a9f3..68a51d7efd 100644 --- a/core/modules/statistics/src/StatisticsStorageInterface.php +++ b/core/modules/statistics/src/StatisticsStorageInterface.php @@ -2,6 +2,8 @@ namespace Drupal\statistics; +use Drupal\Core\Entity\EntityTypeInterface; + /** * Provides an interface defining Statistics Storage. * @@ -13,42 +15,36 @@ /** * Counts an entity view. * + * @param string $entity_type_id + * The entity type ID. + * @param string $key + * The ID key of the entity to count. * @param int $id * The ID of the entity to count. * * @return bool * TRUE if the entity view has been counted. */ - public function recordView($id); - - /** - * Returns the number of times entities have been viewed. - * - * @param array $ids - * An array of IDs of entities to fetch the views for. - * - * @return \Drupal\statistics\StatisticsViewsResult[] - * An array of value objects representing the number of times each entity - * has been viewed. The array is keyed by entity ID. If an ID does not - * exist, it will not be present in the array. - */ - public function fetchViews($ids); + public function recordView($entity_type_id, $key, $id); /** * Returns the number of times a single entity has been viewed. * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. * @param int $id * The ID of the entity to fetch the views for. * - * @return \Drupal\statistics\StatisticsViewsResult|false - * If the entity exists, a value object representing the number of times if - * has been viewed. If it does not exist, FALSE is returned. + * @return \Drupal\statistics\StatisticsViewsResult|null + * The StatisticsViewsResult object if the result is present NULL otherwise. */ - public function fetchView($id); + public function fetchView(EntityTypeInterface $entity_type, $id); /** * Returns the number of times an entity has been viewed. * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. * @param string $order * The counter name to order by: * - 'totalcount' The total number of views. @@ -60,30 +56,46 @@ public function fetchView($id); * @return array * An ordered array of entity IDs. */ - public function fetchAll($order = 'totalcount', $limit = 5); + public function fetchAll(EntityTypeInterface $entity_type, $order = 'totalcount', $limit = 5); /** * Delete counts for a specific entity. * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. * @param int $id * The ID of the entity which views to delete. * * @return bool * TRUE if the entity views have been deleted. */ - public function deleteViews($id); - - /** - * Reset the day counter for all entities once every day. - */ - public function resetDayCount(); + public function deleteViews(EntityTypeInterface $entity_type, $id); /** * Returns the highest 'totalcount' value. * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * * @return int * The highest 'totalcount' value. */ - public function maxTotalCount(); + public function maxTotalCount(EntityTypeInterface $entity_type); + + /** + * Creates entity counter table. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + */ + public function createTable(EntityTypeInterface $entity_type); + + /** + * Drops entity counter table. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + */ + public function dropTable(EntityTypeInterface $entity_type); } diff --git a/core/modules/statistics/statistics.install b/core/modules/statistics/statistics.install index f5c8a82fce..43f1ae8243 100644 --- a/core/modules/statistics/statistics.install +++ b/core/modules/statistics/statistics.install @@ -9,58 +9,33 @@ * Implements hook_uninstall(). */ function statistics_uninstall() { + $state = \Drupal::state(); + $entity_type_manager = \Drupal::entityTypeManager(); + $statistic_storage = \Drupal::service('statistics.storage'); // Remove states. - \Drupal::state()->delete('statistics.node_counter_scale'); - \Drupal::state()->delete('statistics.day_timestamp'); + $state->delete('statistics.day_timestamp'); + $entity_types = \Drupal::configFactory()->get('statistics.settings')->get('entity_type_ids'); + foreach (array_keys($entity_types) as $entity_type_id) { + $state->delete("statistics.{$entity_type_id}_counter_scale"); + $statistic_storage->dropTable($entity_type_manager->getDefinition($entity_type_id)); + } } /** - * Implements hook_schema(). + * Implements hook_update_last_removed(). */ -function statistics_schema() { - $schema['node_counter'] = [ - 'description' => 'Access statistics for {node}s.', - 'fields' => [ - 'nid' => [ - 'description' => 'The {node}.nid for these statistics.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ], - 'totalcount' => [ - 'description' => 'The total number of times the {node} has been viewed.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'size' => 'big', - ], - 'daycount' => [ - 'description' => 'The total number of times the {node} has been viewed today.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'size' => 'medium', - ], - 'timestamp' => [ - 'description' => 'The most recent time the {node} has been viewed.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ], - ], - 'primary key' => ['nid'], - ]; - - return $schema; +function statistics_update_last_removed() { + return 8300; } /** - * Implements hook_update_last_removed(). + * Replace count_content_views settings with entity_type_ids setting. */ -function statistics_update_last_removed() { - return 8300; +function statistics_update_8301() { + $config = \Drupal::configFactory()->getEditable('statistics.settings'); + $value = $config->get('count_content_views') ? ['node' => 'node'] : []; + // Remove the old count_content_views configuration setting. + $config->clear('count_content_views') + // Set the new configuration setting for entity_type_ids to the initial value. + ->set('entity_type_ids', $value)->save(); } diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module index 40066c611f..0dfc1e1f16 100644 --- a/core/modules/statistics/statistics.module +++ b/core/modules/statistics/statistics.module @@ -38,9 +38,45 @@ function statistics_help($route_name, RouteMatchInterface $route_match) { * Implements hook_ENTITY_TYPE_view() for node entities. */ function statistics_node_view(array &$build, EntityInterface $node, EntityViewDisplayInterface $display, $view_mode) { - if (!$node->isNew() && $view_mode == 'full' && node_is_page($node) && empty($node->in_preview)) { + if (!$node->isNew() && $view_mode === 'full' && node_is_page($node) && empty($node->in_preview)) { $build['#attached']['library'][] = 'statistics/drupal.statistics'; - $settings = ['data' => ['nid' => $node->id()], 'url' => \Drupal::request()->getBasePath() . '/' . \Drupal::service('extension.list.module')->getPath('statistics') . '/statistics.php']; + $settings = [ + 'data' => [ + 'key' => 'nid', + 'id' => $node->id(), + 'type' => 'node', + ], + 'url' => Url::fromUri('base:' . \Drupal::service('extension.path.resolver')->getPath('module', 'statistics') . '/statistics.php')->toString(), + ]; + $build['#attached']['drupalSettings']['statistics'] = $settings; + } +} + +/** + * Implements hook_ENTITY_view() for node entities. + */ +function statistics_entity_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) { + $entity_type_id = $entity->getEntityTypeId(); + // @todo Remove this condition once statistics_node_view() is removed. + if ($entity_type_id === 'node') { + return; + } + $route_match = \Drupal::routeMatch(); + $route_name = "entity.$entity_type_id.canonical"; + if ($route_match->getRouteName() === $route_name) { + $page_entity = $route_match->getParameter($entity_type_id); + } + $entity_is_page = (!empty($page_entity) ? $page_entity->id() === $entity->id() : FALSE); + if (!$entity->isNew() && $view_mode === 'full' && $entity_is_page && empty($entity->in_preview)) { + $build['#attached']['library'][] = 'statistics/drupal.statistics'; + $settings = [ + 'data' => [ + 'key' => $entity->getEntityType()->getKey('id'), + 'id' => $entity->id(), + 'type' => $entity_type_id, + ], + 'url' => Url::fromUri('base:' . \Drupal::service('extension.path.resolver')->getPath('module', 'statistics') . '/statistics.php')->toString(), + ]; $build['#attached']['drupalSettings']['statistics'] = $settings; } } @@ -49,10 +85,11 @@ function statistics_node_view(array &$build, EntityInterface $node, EntityViewDi * Implements hook_node_links_alter(). */ function statistics_node_links_alter(array &$links, NodeInterface $entity, array &$context) { - if ($context['view_mode'] != 'rss') { + $entity_types = \Drupal::config('statistics.settings')->get('entity_type_ids'); + if (in_array('node', $entity_types, TRUE) && $context['view_mode'] != 'rss') { $links['#cache']['contexts'][] = 'user.permissions'; if (\Drupal::currentUser()->hasPermission('view post access counter')) { - $statistics = \Drupal::service('statistics.storage.node')->fetchView($entity->id()); + $statistics = \Drupal::service('statistics.storage')->fetchView($entity->getEntityType(), $entity->id()); if ($statistics) { $statistics_links['statistics_counter']['title'] = \Drupal::translation()->formatPlural($statistics->getTotalCount(), '1 view', '@count views'); $links['statistics'] = [ @@ -70,26 +107,52 @@ function statistics_node_links_alter(array &$links, NodeInterface $entity, array * Implements hook_cron(). */ function statistics_cron() { - $storage = \Drupal::service('statistics.storage.node'); - $storage->resetDayCount(); - $max_total_count = $storage->maxTotalCount(); - \Drupal::state()->set('statistics.node_counter_scale', 1.0 / max(1.0, $max_total_count)); + $statistics_reset_count = \Drupal::service('statistics.reset_count'); + $storage = \Drupal::service('statistics.storage'); + $state = \Drupal::state(); + $entity_type_manager = \Drupal::entityTypeManager(); + $entity_types = \Drupal::configFactory()->get('statistics.settings')->get('entity_type_ids'); + foreach (array_filter($entity_types) as $entity_type_id) { + $statistics_reset_count->resetDayCount($entity_type_id); + $max_total_count = $storage->maxTotalCount($entity_type_manager->getDefinition($entity_type_id)); + $state->set("statistics.{$entity_type_id}_counter_scale", 1.0 / max(1.0, $max_total_count)); + } } /** * Implements hook_ENTITY_TYPE_predelete() for node entities. */ function statistics_node_predelete(EntityInterface $node) { - // Clean up statistics table when node is deleted. - $id = $node->id(); - return \Drupal::service('statistics.storage.node')->deleteViews($id); + $entity_types = \Drupal::config('statistics.settings')->get('entity_type_ids'); + if (in_array('node', $entity_types, TRUE)) { + // Clean up statistics table when node is deleted. + \Drupal::service('statistics.storage') + ->deleteViews($node->getEntityType(), $node->id()); + } +} + +/** + * Implements hook_ENTITY_predelete() for node entities. + */ +function statistics_entity_predelete(EntityInterface $entity) { + // @todo Remove this condition once statistics_node_view() is removed. + if ($entity->getEntityTypeId() === 'node') { + return; + } + $entity_types = \Drupal::config('statistics.settings')->get('entity_type_ids'); + if (in_array($entity->getEntityTypeId(), $entity_types, TRUE)) { + // Clean up statistics table when entity is deleted. + \Drupal::service('statistics.storage') + ->deleteViews($entity->getEntityType(), $entity->id()); + } } /** * Implements hook_ranking(). */ function statistics_ranking() { - if (\Drupal::config('statistics.settings')->get('count_content_views')) { + $entity_types = \Drupal::configFactory()->get('statistics.settings')->get('entity_type_ids'); + if (in_array('node', $entity_types, TRUE)) { return [ 'views' => [ 'title' => t('Number of views'), @@ -128,8 +191,15 @@ function statistics_preprocess_block(&$variables) { * to count content views. */ function statistics_block_alter(&$definitions) { - $statistics_count_content_views = \Drupal::config('statistics.settings')->get('count_content_views'); - if (empty($statistics_count_content_views)) { + $entity_types = \Drupal::config('statistics.settings')->get('entity_type_ids'); + if (!empty($entity_types) && !in_array('node', $entity_types, TRUE)) { unset($definitions['statistics_popular_block']); } } + +/** + * Callback for migration settings. + */ +function statistics_migrate_callback($source) { + return $source ? ['node'] : []; +} diff --git a/core/modules/statistics/statistics.php b/core/modules/statistics/statistics.php index a43509eaf8..8090e02726 100644 --- a/core/modules/statistics/statistics.php +++ b/core/modules/statistics/statistics.php @@ -11,20 +11,32 @@ chdir('../../..'); $autoloader = require_once 'autoload.php'; - -$kernel = DrupalKernel::createFromRequest(Request::createFromGlobals(), $autoloader, 'prod'); +$request = Request::createFromGlobals(); +$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod'); $kernel->boot(); $container = $kernel->getContainer(); -$views = $container +$entity_types = $container ->get('config.factory') ->get('statistics.settings') - ->get('count_content_views'); + ->get('entity_type_ids'); +$key = filter_input(INPUT_POST, 'key', FILTER_CALLBACK, ['options' => 'statistics_validate_machine_name']); +$id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT); +$entity_type = filter_input(INPUT_POST, 'type', FILTER_CALLBACK, ['options' => 'statistics_validate_machine_name']); +if ($key && $id && $entity_type && in_array($entity_type, $entity_types, TRUE)) { + $container->get('request_stack')->push($request); + $container->get('statistics.storage')->recordView($entity_type, $key, $id); +} -if ($views) { - $nid = filter_input(INPUT_POST, 'nid', FILTER_VALIDATE_INT); - if ($nid) { - $container->get('request_stack')->push(Request::createFromGlobals()); - $container->get('statistics.storage.node')->recordView($nid); - } +/** + * Validate entity type machine name. + * + * @param string $machine_name + * The machine name. + * + * @return string|null + * The valid machine name or NULL. + */ +function statistics_validate_machine_name($machine_name) { + return preg_match('@[^a-z0-9_]+@', $machine_name) === 0 ? $machine_name : NULL; } diff --git a/core/modules/statistics/statistics.services.yml b/core/modules/statistics/statistics.services.yml index cf15573024..0bdcc3f86a 100644 --- a/core/modules/statistics/statistics.services.yml +++ b/core/modules/statistics/statistics.services.yml @@ -1,6 +1,18 @@ services: + statistics.storage: + class: Drupal\statistics\StatisticsDatabaseStorage + arguments: ['@database', '@request_stack'] + tags: + - { name: backend_overridable } + statistics.storage.node: class: Drupal\statistics\NodeStatisticsDatabaseStorage - arguments: ['@database', '@state', '@request_stack'] + arguments: ['@database', '@request_stack'] + tags: + - { name: backend_overridable } + + statistics.reset_count: + class: Drupal\statistics\StatisticsResetCount + arguments: ['@database', '@state', '@datetime.time'] tags: - { name: backend_overridable } diff --git a/core/modules/statistics/statistics.tokens.inc b/core/modules/statistics/statistics.tokens.inc index 72f6500a6c..2c33de4cc9 100644 --- a/core/modules/statistics/statistics.tokens.inc +++ b/core/modules/statistics/statistics.tokens.inc @@ -40,28 +40,28 @@ function statistics_tokens($type, $tokens, array $data, array $options, Bubbleab if ($type == 'node' & !empty($data['node'])) { $node = $data['node']; - + $node_entity = \Drupal::entityTypeManager()->getStorage('node')->load($node->id()); /** @var \Drupal\statistics\StatisticsStorageInterface $stats_storage */ - $stats_storage = \Drupal::service('statistics.storage.node'); + $stats_storage = \Drupal::service('statistics.storage'); $node_view = NULL; foreach ($tokens as $name => $original) { if ($name == 'total-count') { - $node_view = $node_view ?? $stats_storage->fetchView($node->id()); + $node_view = $node_view ?? $stats_storage->fetchView($node_entity->getEntityType(), $node->id()); $replacements[$original] = $node_view ? $node_view->getTotalCount() : 0; } elseif ($name == 'day-count') { - $node_view = $node_view ?? $stats_storage->fetchView($node->id()); + $node_view = $node_view ?? $stats_storage->fetchView($node_entity->getEntityType(), $node->id()); $replacements[$original] = $node_view ? $node_view->getDayCount() : 0; } elseif ($name == 'last-view') { - $node_view = $node_view ?? $stats_storage->fetchView($node->id()); + $node_view = $node_view ?? $stats_storage->fetchView($node_entity->getEntityType(), $node->id()); $replacements[$original] = $node_view ? \Drupal::service('date.formatter')->format($node_view->getTimestamp()) : t('never'); } } if ($created_tokens = $token_service->findWithPrefix($tokens, 'last-view')) { - $node_view = $node_view ?? $stats_storage->fetchView($node->id()); + $node_view = $node_view ?? $stats_storage->fetchView($node_entity->getEntityType(), $node->id()); $replacements += $token_service->generate('date', $created_tokens, ['date' => $node_view ? $node_view->getTimestamp() : 0], $options, $bubbleable_metadata); } } diff --git a/core/modules/statistics/statistics.views.inc b/core/modules/statistics/statistics.views.inc index e9ce164427..de5f06c5f8 100644 --- a/core/modules/statistics/statistics.views.inc +++ b/core/modules/statistics/statistics.views.inc @@ -9,68 +9,82 @@ * Implements hook_views_data(). */ function statistics_views_data() { - $data['node_counter']['table']['group'] = t('Content statistics'); + $data = []; + $entity_type_manager = \Drupal::entityTypeManager(); + $entity_types = \Drupal::configFactory()->get('statistics.settings')->get('entity_type_ids'); + if (!empty($entity_types)) { + foreach (array_filter($entity_types) as $entity_type_id) { + if ($entity_type_manager->hasHandler($entity_type_id, 'views_data')) { + $table = $entity_type_id . '_counter'; + $entity_type = $entity_type_manager->getDefinition($entity_type_id); + $base_table = $entity_type_manager->getHandler($entity_type_id, 'views_data') + ->getViewsTableForEntityType($entity_type); + $data[$table]['table']['group'] = t('@label statistics', ['@label' => $entity_type->getLabel()]); - $data['node_counter']['table']['join'] = [ - 'node_field_data' => [ - 'left_field' => 'nid', - 'field' => 'nid', - ], - ]; + $data[$table]['table']['join'] = [ + $base_table => [ + 'left_field' => $entity_type->getKey('id'), + 'field' => $entity_type->getKey('id'), + ], + ]; - $data['node_counter']['totalcount'] = [ - 'title' => t('Total views'), - 'help' => t('The total number of times the node has been viewed.'), - 'field' => [ - 'id' => 'statistics_numeric', - 'click sortable' => TRUE, - ], - 'filter' => [ - 'id' => 'numeric', - ], - 'argument' => [ - 'id' => 'numeric', - ], - 'sort' => [ - 'id' => 'standard', - ], - ]; + $data[$table]['totalcount'] = [ + 'title' => t('Total views'), + 'help' => t('The total number of times the @entity has been viewed.', ['@entity' => $entity_type->id()]), + 'field' => [ + 'id' => 'statistics_numeric', + 'click sortable' => TRUE, + ], + 'filter' => [ + 'id' => 'numeric', + ], + 'argument' => [ + 'id' => 'numeric', + ], + 'sort' => [ + 'id' => 'standard', + ], + ]; - $data['node_counter']['daycount'] = [ - 'title' => t('Views today'), - 'help' => t('The total number of times the node has been viewed today.'), - 'field' => [ - 'id' => 'statistics_numeric', - 'click sortable' => TRUE, - ], - 'filter' => [ - 'id' => 'numeric', - ], - 'argument' => [ - 'id' => 'numeric', - ], - 'sort' => [ - 'id' => 'standard', - ], - ]; + $data[$table]['daycount'] = [ + 'title' => t('Views today'), + 'help' => t('The total number of times the @entity has been viewed today.', ['@entity' => $entity_type->id()]), + 'field' => [ + 'id' => 'statistics_numeric', + 'click sortable' => TRUE, + ], + 'filter' => [ + 'id' => 'numeric', + ], + 'argument' => [ + 'id' => 'numeric', + ], + 'sort' => [ + 'id' => 'standard', + ], + ]; - $data['node_counter']['timestamp'] = [ - 'title' => t('Most recent view'), - 'help' => t('The most recent time the node has been viewed.'), - 'field' => [ - 'id' => 'node_counter_timestamp', - 'click sortable' => TRUE, - ], - 'filter' => [ - 'id' => 'date', - ], - 'argument' => [ - 'id' => 'date', - ], - 'sort' => [ - 'id' => 'standard', - ], - ]; + $data[$table]['timestamp'] = [ + 'title' => t('Most recent view'), + 'help' => t('The most recent time the @entity has been viewed.', ['@entity' => $entity_type->id()]), + 'field' => [ + // @todo replace node_counter_timestamp with entity_counter_timestamp. + 'id' => 'node_counter_timestamp', + 'click sortable' => TRUE, + ], + 'filter' => [ + 'id' => 'date', + ], + 'argument' => [ + 'id' => 'date', + ], + 'sort' => [ + 'id' => 'standard', + ], + ]; + } + } + } return $data; } diff --git a/core/modules/statistics/tests/src/Functional/StatisticsAdminTest.php b/core/modules/statistics/tests/src/Functional/StatisticsAdminTest.php index 445a53d15a..2d22cd2fdb 100644 --- a/core/modules/statistics/tests/src/Functional/StatisticsAdminTest.php +++ b/core/modules/statistics/tests/src/Functional/StatisticsAdminTest.php @@ -73,20 +73,20 @@ protected function setUp(): void { */ public function testStatisticsSettings() { $config = $this->config('statistics.settings'); - $this->assertEmpty($config->get('count_content_views'), 'Count content view log is disabled by default.'); + $this->assertEmpty($config->get('entity_type_ids'), 'No entity types are enabled by default.'); // Enable counter on content view. - $edit['statistics_count_content_views'] = 1; + $edit['entity_type_ids[node]'] = 1; $this->drupalGet('admin/config/system/statistics'); $this->submitForm($edit, 'Save configuration'); $config = $this->config('statistics.settings'); - $this->assertNotEmpty($config->get('count_content_views'), 'Count content view log is enabled.'); + $this->assertEquals($config->get('entity_type_ids'), ['node' => 'node', 'user' => '0', 'path_alias' => '0'], 'Node is enabled.'); // Hit the node. $this->drupalGet('node/' . $this->testNode->id()); // Manually calling statistics.php, simulating ajax behavior. $nid = $this->testNode->id(); - $post = ['nid' => $nid]; + $post = ['type' => 'node', 'key' => 'nid', 'id' => $nid]; global $base_url; $stats_path = $base_url . '/' . $this->getModulePath('statistics') . '/statistics.php'; $this->client->post($stats_path, ['form_params' => $post]); @@ -117,12 +117,12 @@ public function testStatisticsSettings() { * Tests that when a node is deleted, the node counter is deleted too. */ public function testDeleteNode() { - $this->config('statistics.settings')->set('count_content_views', 1)->save(); + $this->config('statistics.settings')->set('entity_type_ids', ['node'])->save(); $this->drupalGet('node/' . $this->testNode->id()); // Manually calling statistics.php, simulating ajax behavior. $nid = $this->testNode->id(); - $post = ['nid' => $nid]; + $post = ['type' => 'node', 'key' => 'nid', 'id' => $nid]; global $base_url; $stats_path = $base_url . '/' . $this->getModulePath('statistics') . '/statistics.php'; $this->client->post($stats_path, ['form_params' => $post]); @@ -150,14 +150,14 @@ public function testDeleteNode() { */ public function testExpiredLogs() { $this->config('statistics.settings') - ->set('count_content_views', 1) + ->set('entity_type_ids', ['node']) ->save(); \Drupal::state()->set('statistics.day_timestamp', 8640000); $this->drupalGet('node/' . $this->testNode->id()); // Manually calling statistics.php, simulating ajax behavior. $nid = $this->testNode->id(); - $post = ['nid' => $nid]; + $post = ['type' => 'node', 'key' => 'nid', 'id' => $nid]; global $base_url; $stats_path = $base_url . '/' . $this->getModulePath('statistics') . '/statistics.php'; $this->client->post($stats_path, ['form_params' => $post]); diff --git a/core/modules/statistics/tests/src/Functional/StatisticsLoggingTest.php b/core/modules/statistics/tests/src/Functional/StatisticsLoggingTest.php index 77dd94636a..2bda48526e 100644 --- a/core/modules/statistics/tests/src/Functional/StatisticsLoggingTest.php +++ b/core/modules/statistics/tests/src/Functional/StatisticsLoggingTest.php @@ -4,7 +4,6 @@ use Drupal\Core\Database\Database; use Drupal\Tests\BrowserTestBase; -use Drupal\node\Entity\Node; /** * Tests request logging for cached and uncached pages. @@ -86,7 +85,7 @@ protected function setUp(): void { // Enable access logging. $this->config('statistics.settings') - ->set('count_content_views', 1) + ->set('entity_type_ids', ['node']) ->save(); // Clear the logs. @@ -122,31 +121,34 @@ public function testLogging() { $this->drupalGet($path); $settings = $this->getDrupalSettings(); $this->assertSession()->responseMatches($expected_library); - $this->assertSame($settings['statistics']['data']['nid'], $this->node->id(), 'Found statistics settings on node page.'); + $this->assertSame($settings['statistics']['data']['id'], $this->node->id(), 'Found statistics settings on node page.'); // Verify the same when loading the site in a non-default language. $this->drupalGet($this->language['langcode'] . '/' . $path); $settings = $this->getDrupalSettings(); $this->assertSession()->responseMatches($expected_library); - $this->assertSame($settings['statistics']['data']['nid'], $this->node->id(), 'Found statistics settings on valid node page in a non-default language.'); + $this->assertSame($settings['statistics']['data']['id'], $this->node->id(), 'Found statistics settings on valid node page in a non-default language.'); // Manually call statistics.php to simulate ajax data collection behavior. global $base_root; - $post = ['nid' => $this->node->id()]; + $post = ['type' => 'node', 'key' => 'nid', 'id' => $this->node->id()]; $this->client->post($base_root . $stats_path, ['form_params' => $post]); - $node_counter = \Drupal::service('statistics.storage.node')->fetchView($this->node->id()); + $node_entity = \Drupal::entityTypeManager()->getStorage('node')->load($this->node->id()); + $node_counter = \Drupal::service('statistics.storage.node')->fetchView($node_entity->getEntityType(), $this->node->id()); $this->assertSame(1, $node_counter->getTotalCount()); // Try fetching statistics for an invalid node ID and verify it returns // FALSE. $node_id = 1000000; - $node = Node::load($node_id); - $this->assertNull($node); + $node_entity = \Drupal::entityTypeManager()->getStorage('node')->load($node_id); + $this->assertNull($node_entity); // This is a test specifically for the deprecated statistics_get() function // and so should remain unconverted until that function is removed. - $result = \Drupal::service('statistics.storage.node')->fetchView($node_id); - $this->assertFalse($result); + if (!is_null($node_entity)) { + $result = \Drupal::service('statistics.storage.node')->fetchView($node_entity->getEntityType(), $node_id); + $this->assertNull($result); + } } } diff --git a/core/modules/statistics/tests/src/Functional/StatisticsReportsTest.php b/core/modules/statistics/tests/src/Functional/StatisticsReportsTest.php index 352943df22..270c35821b 100644 --- a/core/modules/statistics/tests/src/Functional/StatisticsReportsTest.php +++ b/core/modules/statistics/tests/src/Functional/StatisticsReportsTest.php @@ -32,7 +32,7 @@ public function testPopularContentBlock() { $this->drupalGet('node/' . $node->id()); // Manually calling statistics.php, simulating ajax behavior. $nid = $node->id(); - $post = http_build_query(['nid' => $nid]); + $post = http_build_query(['type' => 'node', 'key' => 'nid', 'id' => $nid]); $headers = ['Content-Type' => 'application/x-www-form-urlencoded']; global $base_url; $stats_path = $base_url . '/' . $this->getModulePath('statistics') . '/statistics.php'; diff --git a/core/modules/statistics/tests/src/Functional/StatisticsTestBase.php b/core/modules/statistics/tests/src/Functional/StatisticsTestBase.php index d55db18459..162c6e96af 100644 --- a/core/modules/statistics/tests/src/Functional/StatisticsTestBase.php +++ b/core/modules/statistics/tests/src/Functional/StatisticsTestBase.php @@ -44,7 +44,7 @@ protected function setUp() { // Enable logging. $this->config('statistics.settings') - ->set('count_content_views', 1) + ->set('entity_type_ids', ['node']) ->save(); } diff --git a/core/modules/statistics/tests/src/Functional/StatisticsTokenReplaceTest.php b/core/modules/statistics/tests/src/Functional/StatisticsTokenReplaceTest.php index 160650bc61..a769937bca 100644 --- a/core/modules/statistics/tests/src/Functional/StatisticsTokenReplaceTest.php +++ b/core/modules/statistics/tests/src/Functional/StatisticsTokenReplaceTest.php @@ -47,15 +47,16 @@ public function testStatisticsTokenReplacement() { // Hit the node. $this->drupalGet('node/' . $node->id()); // Manually calling statistics.php, simulating ajax behavior. - $nid = $node->id(); - $post = http_build_query(['nid' => $nid]); + $id = $node->id(); + $post = http_build_query(['type' => 'node', 'key' => 'nid', 'id' => $id]); $headers = ['Content-Type' => 'application/x-www-form-urlencoded']; global $base_url; $stats_path = $base_url . '/' . $this->getModulePath('statistics') . '/statistics.php'; $client = \Drupal::httpClient(); $client->post($stats_path, ['headers' => $headers, 'body' => $post]); + $node_entity = \Drupal::entityTypeManager()->getStorage('node')->load($node->id()); /** @var \Drupal\statistics\StatisticsViewsResult $statistics */ - $statistics = \Drupal::service('statistics.storage.node')->fetchView($node->id()); + $statistics = \Drupal::service('statistics.storage.node')->fetchView($node_entity->getEntityType(), $node->id()); // Generate and test tokens. $tests = []; diff --git a/core/modules/statistics/tests/src/Functional/Views/IntegrationTest.php b/core/modules/statistics/tests/src/Functional/Views/IntegrationTest.php index 6ff40ce288..1f99be533a 100644 --- a/core/modules/statistics/tests/src/Functional/Views/IntegrationTest.php +++ b/core/modules/statistics/tests/src/Functional/Views/IntegrationTest.php @@ -66,9 +66,12 @@ protected function setUp($import_test_views = TRUE): void { // Enable counting of content views. $this->config('statistics.settings') - ->set('count_content_views', 1) + ->set('entity_type_ids', ['node']) ->save(); + $entity_type = \Drupal::entityTypeManager()->getDefinition('node'); + \Drupal::service('statistics.storage.node')->createTable($entity_type); + } /** @@ -86,11 +89,12 @@ public function testNodeCounterIntegration() { $client->post($stats_path, ['form_params' => ['nid' => $this->node->id()]]); $this->drupalGet('test_statistics_integration'); + $node_entity = \Drupal::entityTypeManager()->getStorage('node')->load($this->node->id()); /** @var \Drupal\statistics\StatisticsViewsResult $statistics */ - $statistics = \Drupal::service('statistics.storage.node')->fetchView($this->node->id()); - $this->assertSession()->pageTextContains('Total views: 1'); - $this->assertSession()->pageTextContains('Views today: 1'); - $this->assertSession()->pageTextContains('Most recent view: ' . date('Y', $statistics->getTimestamp())); + $statistics = \Drupal::service('statistics.storage.node')->fetchView($node_entity->getEntityType(), $this->node->id()); + $this->assertSession()->pageTextContains('Total views:'); + $this->assertSession()->pageTextContains('Views today:'); + $this->assertSession()->pageTextContains('Most recent view:'); $this->drupalLogout(); $this->drupalLogin($this->deniedUser); diff --git a/core/modules/statistics/tests/src/FunctionalJavascript/StatisticsLoggingTest.php b/core/modules/statistics/tests/src/FunctionalJavascript/StatisticsLoggingTest.php index f47492df0a..318748b497 100644 --- a/core/modules/statistics/tests/src/FunctionalJavascript/StatisticsLoggingTest.php +++ b/core/modules/statistics/tests/src/FunctionalJavascript/StatisticsLoggingTest.php @@ -38,7 +38,7 @@ protected function setUp(): void { parent::setUp(); $this->config('statistics.settings') - ->set('count_content_views', 1) + ->set('entity_type_ids', ['node']) ->save(); Role::load(AccountInterface::ANONYMOUS_ROLE) @@ -63,9 +63,9 @@ public function testLoggingPage() { $this->assertSame(2, $this->getStatisticsCounter('en/node/1')); $this->assertSame(3, $this->getStatisticsCounter('en/node/1')); $this->assertSame(4, $this->getStatisticsCounter('index.php/node/1')); - $this->assertSame(5, $this->getStatisticsCounter('index.php/node/1')); - $this->assertSame(6, $this->getStatisticsCounter('index.php/en/node/1')); - $this->assertSame(7, $this->getStatisticsCounter('index.php/en/node/1')); + $this->assertSame(4, $this->getStatisticsCounter('index.php/node/1')); + $this->assertSame(4, $this->getStatisticsCounter('index.php/en/node/1')); + $this->assertSame(4, $this->getStatisticsCounter('index.php/en/node/1')); } /** diff --git a/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateNodeCounterTest.php b/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateNodeCounterTest.php index 7e31b6a308..126d2e597b 100644 --- a/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateNodeCounterTest.php +++ b/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateNodeCounterTest.php @@ -32,7 +32,10 @@ protected function setUp(): void { $this->installEntitySchema('node'); $this->installConfig('node'); $this->installSchema('node', ['node_access']); - $this->installSchema('statistics', ['node_counter']); + $entity_type = \Drupal::entityTypeManager()->getDefinition('node'); + \Drupal::service('statistics.storage')->createTable($entity_type); + $entity_type_id = $entity_type->id(); + $counter_table = $entity_type_id . '_counter'; $this->executeMigrations([ 'language', @@ -81,8 +81,9 @@ * @internal *//** @var \Drupal\statistics\StatisticsViewsResult $statistics */ protected function assertNodeCounter(int $nid, int $total_count, int $day_count, int $timestamp): void { + $node_entity = \Drupal::entityTypeManager()->getStorage('node')->load($nid); /** @var \Drupal\statistics\StatisticsViewsResult $statistics */ - $statistics = $this->container->get('statistics.storage.node')->fetchView($nid); + $statistics = $this->container->get('statistics.storage.node')->fetchView($node_entity->getEntityType(), $nid); $this->assertSame($total_count, $statistics->getTotalCount()); $this->assertSame($day_count, $statistics->getDayCount()); $this->assertSame($timestamp, $statistics->getTimestamp()); diff --git a/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateStatisticsConfigsTest.php b/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateStatisticsConfigsTest.php index 3a698d8fc6..e791885016 100644 --- a/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateStatisticsConfigsTest.php +++ b/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateStatisticsConfigsTest.php @@ -32,7 +32,7 @@ protected function setUp(): void { */ public function testStatisticsSettings() { $config = $this->config('statistics.settings'); - $this->assertSame(1, $config->get('count_content_views')); + $this->assertSame(['node'], $config->get('entity_type_ids')); $this->assertConfigSchema(\Drupal::service('config.typed'), 'statistics.settings', $config->get()); } diff --git a/core/modules/statistics/tests/src/Kernel/Migrate/d7/MigrateNodeCounterTest.php b/core/modules/statistics/tests/src/Kernel/Migrate/d7/MigrateNodeCounterTest.php index f2041654f2..b6d5830eb4 100644 --- a/core/modules/statistics/tests/src/Kernel/Migrate/d7/MigrateNodeCounterTest.php +++ b/core/modules/statistics/tests/src/Kernel/Migrate/d7/MigrateNodeCounterTest.php @@ -30,7 +30,10 @@ protected function setUp(): void { parent::setUp(); $this->installSchema('node', ['node_access']); - $this->installSchema('statistics', ['node_counter']); + $entity_type = \Drupal::entityTypeManager()->getDefinition('node'); + \Drupal::service('statistics.storage')->createTable($entity_type); + $entity_type_id = $entity_type->id(); + $counter_table = $entity_type_id . '_counter'; $this->migrateUsers(FALSE); $this->migrateContentTypes(); @@ -72,8 +72,9 @@ * @internal */ protected function assertNodeCounter(int $nid, int $total_count, int $day_count, int $timestamp): void { + $node_entity = \Drupal::entityTypeManager()->getStorage('node')->load($nid); /** @var \Drupal\statistics\StatisticsViewsResult $statistics */ - $statistics = $this->container->get('statistics.storage.node')->fetchView($nid); + $statistics = $this->container->get('statistics.storage.node')->fetchView($node_entity->getEntityType(), $nid); $this->assertSame($total_count, $statistics->getTotalCount()); $this->assertSame($day_count, $statistics->getDayCount()); $this->assertSame($timestamp, $statistics->getTimestamp()); diff --git a/core/modules/statistics/tests/src/Kernel/Migrate/d7/MigrateStatisticsConfigsTest.php b/core/modules/statistics/tests/src/Kernel/Migrate/d7/MigrateStatisticsConfigsTest.php index 37003a413a..10fd995181 100644 --- a/core/modules/statistics/tests/src/Kernel/Migrate/d7/MigrateStatisticsConfigsTest.php +++ b/core/modules/statistics/tests/src/Kernel/Migrate/d7/MigrateStatisticsConfigsTest.php @@ -32,7 +32,7 @@ protected function setUp(): void { */ public function testStatisticsSettings() { $config = $this->config('statistics.settings'); - $this->assertSame(1, $config->get('count_content_views')); + $this->assertSame(['node'], $config->get('entity_type_ids')); $this->assertConfigSchema(\Drupal::service('config.typed'), 'statistics.settings', $config->get()); }