Move all files to 2017/
This commit is contained in:
parent
ac7370f67f
commit
2875863330
15717 changed files with 0 additions and 0 deletions
|
|
@ -0,0 +1,2 @@
|
|||
count_content_views: 0
|
||||
display_max_age: 3600
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# 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'
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# 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'
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
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, d7_node]
|
||||
source: nid
|
||||
-
|
||||
plugin: skip_on_empty
|
||||
method: row
|
||||
totalcount: totalcount
|
||||
daycount: daycount
|
||||
timestamp: timestamp
|
||||
destination:
|
||||
plugin: node_counter
|
||||
migration_dependencies:
|
||||
optional:
|
||||
- d6_node
|
||||
- d7_node
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\statistics;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\statistics\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
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 Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\statistics\StatisticsStorageInterface;
|
||||
|
||||
/**
|
||||
* Provides a 'Popular content' block.
|
||||
*
|
||||
* @Block(
|
||||
* id = "statistics_popular_block",
|
||||
* admin_label = @Translation("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 an 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.
|
||||
*/
|
||||
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(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\statistics\Plugin\migrate\destination;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\migrate\Plugin\migrate\destination\DestinationBase;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* 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(MigrationInterface $migration = NULL) {
|
||||
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])
|
||||
->expression('timestamp', 'CASE WHEN timestamp > :timestamp THEN timestamp ELSE :timestamp END', [':timestamp' => $timestamp])
|
||||
->execute();
|
||||
|
||||
return [$row->getDestinationProperty('nid')];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\statistics\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Node counter source from database.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
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.
|
||||
*
|
||||
* @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');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\statistics\Plugin\views\field;
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
}
|
||||
102
2017/web/core/modules/statistics/src/StatisticsSettingsForm.php
Normal file
102
2017/web/core/modules/statistics/src/StatisticsSettingsForm.php
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\statistics;
|
||||
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Configure statistics settings for this site.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class StatisticsSettingsForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Constructs a \Drupal\statistics\StatisticsSettingsForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The factory for configuration objects.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler) {
|
||||
parent::__construct($config_factory);
|
||||
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('module_handler')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'statistics_settings_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['statistics.settings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$config = $this->config('statistics.settings');
|
||||
|
||||
// Content counter settings.
|
||||
$form['content'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => t('Content viewing counter settings'),
|
||||
'#open' => TRUE,
|
||||
];
|
||||
$form['content']['statistics_count_content_views'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Count content views'),
|
||||
'#default_value' => $config->get('count_content_views'),
|
||||
'#description' => t('Increment a counter each time content is viewed.'),
|
||||
];
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->config('statistics.settings')
|
||||
->set('count_content_views', $form_state->getValue('statistics_count_content_views'))
|
||||
->save();
|
||||
|
||||
// The popular statistics block is dependent on these settings, so clear the
|
||||
// block plugin definitions cache.
|
||||
if ($this->moduleHandler->moduleExists('block')) {
|
||||
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
|
||||
}
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\statistics;
|
||||
|
||||
/**
|
||||
* Provides an interface defining Statistics Storage.
|
||||
*
|
||||
* Stores the views per day, total views and timestamp of last view
|
||||
* for entities.
|
||||
*/
|
||||
interface StatisticsStorageInterface {
|
||||
|
||||
/**
|
||||
* Count a entity view.
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Returns the number of times a single entity has been viewed.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public function fetchView($id);
|
||||
|
||||
/**
|
||||
* Returns the number of times a entity has been viewed.
|
||||
*
|
||||
* @param string $order
|
||||
* The counter name to order by:
|
||||
* - 'totalcount' The total number of views.
|
||||
* - 'daycount' The number of views today.
|
||||
* - 'timestamp' The unix timestamp of the last view.
|
||||
*
|
||||
* @param int $limit
|
||||
* The number of entity IDs to return.
|
||||
*
|
||||
* @return array
|
||||
* An ordered array of entity IDs.
|
||||
*/
|
||||
public function fetchAll($order = 'totalcount', $limit = 5);
|
||||
|
||||
/**
|
||||
* Delete counts for a specific entity.
|
||||
*
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* Returns the highest 'totalcount' value.
|
||||
*
|
||||
* @return int
|
||||
* The highest 'totalcount' value.
|
||||
*/
|
||||
public function maxTotalCount();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\statistics;
|
||||
|
||||
/**
|
||||
* Value object for passing statistic results.
|
||||
*/
|
||||
class StatisticsViewsResult {
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $totalCount;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $dayCount;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $timestamp;
|
||||
|
||||
public function __construct($total_count, $day_count, $timestamp) {
|
||||
$this->totalCount = (int) $total_count;
|
||||
$this->dayCount = (int) $day_count;
|
||||
$this->timestamp = (int) $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Total number of times the entity has been viewed.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalCount() {
|
||||
return $this->totalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Total number of times the entity has been viewed "today".
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDayCount() {
|
||||
return $this->dayCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamp of when the entity was last viewed.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTimestamp() {
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\statistics\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Defines a base class for testing the Statistics module.
|
||||
*
|
||||
* @deprecated Scheduled for removal in Drupal 9.0.0.
|
||||
* Use \Drupal\Tests\statistics\Functional\StatisticsTestBase instead.
|
||||
*/
|
||||
abstract class StatisticsTestBase extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'block', 'ban', 'statistics'];
|
||||
|
||||
/**
|
||||
* User with permissions to ban IP's.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $blockingUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create Basic page node type.
|
||||
if ($this->profile != 'standard') {
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
}
|
||||
|
||||
// Create user.
|
||||
$this->blockingUser = $this->drupalCreateUser([
|
||||
'access administration pages',
|
||||
'access site reports',
|
||||
'ban IP addresses',
|
||||
'administer blocks',
|
||||
'administer statistics',
|
||||
'administer users',
|
||||
]);
|
||||
$this->drupalLogin($this->blockingUser);
|
||||
|
||||
// Enable logging.
|
||||
$this->config('statistics.settings')
|
||||
->set('count_content_views', 1)
|
||||
->save();
|
||||
}
|
||||
|
||||
}
|
||||
15
2017/web/core/modules/statistics/statistics.es6.js
Normal file
15
2017/web/core/modules/statistics/statistics.es6.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* @file
|
||||
* Statistics functionality.
|
||||
*/
|
||||
|
||||
(function($, Drupal, drupalSettings) {
|
||||
$(document).ready(() => {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
url: drupalSettings.statistics.url,
|
||||
data: drupalSettings.statistics.data,
|
||||
});
|
||||
});
|
||||
})(jQuery, Drupal, drupalSettings);
|
||||
9
2017/web/core/modules/statistics/statistics.info.yml
Normal file
9
2017/web/core/modules/statistics/statistics.info.yml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
name: Statistics
|
||||
type: module
|
||||
description: 'Logs content statistics for your site.'
|
||||
package: Core
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
configure: statistics.settings
|
||||
dependencies:
|
||||
- drupal:node
|
||||
88
2017/web/core/modules/statistics/statistics.install
Normal file
88
2017/web/core/modules/statistics/statistics.install
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install and update functions for the Statistics module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function statistics_uninstall() {
|
||||
// Remove states.
|
||||
\Drupal::state()->delete('statistics.node_counter_scale');
|
||||
\Drupal::state()->delete('statistics.day_timestamp');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the Statistics module if the node module is not enabled.
|
||||
*/
|
||||
function statistics_update_8001() {
|
||||
if (!\Drupal::moduleHandler()->moduleExists('node')) {
|
||||
if (\Drupal::service('module_installer')->uninstall(['statistics'], TRUE)) {
|
||||
return 'The statistics module depends on the node module and has therefore been uninstalled.';
|
||||
}
|
||||
else {
|
||||
return 'There was an error uninstalling the statistcs module.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the Statistics module if the node module is not enabled.
|
||||
*/
|
||||
function statistics_update_8002() {
|
||||
// Set the new configuration setting for max age to the initial value.
|
||||
\Drupal::configFactory()->getEditable('statistics.settings')->set('display_max_age', 3600)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove access_log settings.
|
||||
*/
|
||||
function statistics_update_8300() {
|
||||
\Drupal::configFactory()->getEditable('statistics.settings')->clear('access_log')->save();
|
||||
}
|
||||
17
2017/web/core/modules/statistics/statistics.js
Normal file
17
2017/web/core/modules/statistics/statistics.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
$(document).ready(function () {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
url: drupalSettings.statistics.url,
|
||||
data: drupalSettings.statistics.data
|
||||
});
|
||||
});
|
||||
})(jQuery, Drupal, drupalSettings);
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
drupal.statistics:
|
||||
version: VERSION
|
||||
js:
|
||||
statistics.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/drupalSettings
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
statistics.settings:
|
||||
title: Statistics
|
||||
description: 'Configure the logging of content statistics.'
|
||||
route_name: statistics.settings
|
||||
parent: system.admin_config_system
|
||||
weight: -15
|
||||
199
2017/web/core/modules/statistics/statistics.module
Normal file
199
2017/web/core/modules/statistics/statistics.module
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Logs and displays content statistics for a site.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\statistics\StatisticsViewsResult;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function statistics_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.statistics':
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Statistics module shows you how often content is viewed. This is useful in determining which pages of your site are most popular. For more information, see the <a href=":statistics_do">online documentation for the Statistics module</a>.', [':statistics_do' => 'https://www.drupal.org/documentation/modules/statistics/']) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Displaying popular content') . '</dt>';
|
||||
$output .= '<dd>' . t('The module includes a <em>Popular content</em> block that displays the most viewed pages today and for all time, and the last content viewed. To use the block, enable <em>Count content views</em> on the <a href=":statistics-settings">Statistics page</a>, and then you can enable and configure the block on the <a href=":blocks">Block layout page</a>.', [':statistics-settings' => \Drupal::url('statistics.settings'), ':blocks' => (\Drupal::moduleHandler()->moduleExists('block')) ? \Drupal::url('block.admin_display') : '#']) . '</dd>';
|
||||
$output .= '<dt>' . t('Page view counter') . '</dt>';
|
||||
$output .= '<dd>' . t('The Statistics module includes a counter for each page that increases whenever the page is viewed. To use the counter, enable <em>Count content views</em> on the <a href=":statistics-settings">Statistics page</a>, and set the necessary <a href=":permissions">permissions</a> (<em>View content hits</em>) so that the counter is visible to the users.', [':statistics-settings' => \Drupal::url('statistics.settings'), ':permissions' => \Drupal::url('user.admin_permissions', [], ['fragment' => 'module-statistics'])]) . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
|
||||
case 'statistics.settings':
|
||||
return '<p>' . t('Settings for the statistical information that Drupal will keep about the site.') . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)) {
|
||||
$build['#attached']['library'][] = 'statistics/drupal.statistics';
|
||||
$settings = ['data' => ['nid' => $node->id()], 'url' => \Drupal::request()->getBasePath() . '/' . drupal_get_path('module', 'statistics') . '/statistics.php'];
|
||||
$build['#attached']['drupalSettings']['statistics'] = $settings;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_links_alter().
|
||||
*/
|
||||
function statistics_node_links_alter(array &$links, NodeInterface $entity, array &$context) {
|
||||
if ($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());
|
||||
if ($statistics) {
|
||||
$statistics_links['statistics_counter']['title'] = \Drupal::translation()->formatPlural($statistics->getTotalCount(), '1 view', '@count views');
|
||||
$links['statistics'] = [
|
||||
'#theme' => 'links__node__statistics',
|
||||
'#links' => $statistics_links,
|
||||
'#attributes' => ['class' => ['links', 'inline']],
|
||||
];
|
||||
}
|
||||
$links['#cache']['max-age'] = \Drupal::config('statistics.settings')->get('display_max_age');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most viewed content of all time, today, or the last-viewed node.
|
||||
*
|
||||
* @param string $dbfield
|
||||
* The database field to use, one of:
|
||||
* - 'totalcount': Integer that shows the top viewed content of all time.
|
||||
* - 'daycount': Integer that shows the top viewed content for today.
|
||||
* - 'timestamp': Integer that shows only the last viewed node.
|
||||
* @param int $dbrows
|
||||
* The number of rows to be returned.
|
||||
*
|
||||
* @return SelectQuery|false
|
||||
* A query result containing the node ID, title, user ID that owns the node,
|
||||
* and the username for the selected node(s), or FALSE if the query could not
|
||||
* be executed correctly.
|
||||
*/
|
||||
function statistics_title_list($dbfield, $dbrows) {
|
||||
if (in_array($dbfield, ['totalcount', 'daycount', 'timestamp'])) {
|
||||
$query = db_select('node_field_data', 'n');
|
||||
$query->addTag('node_access');
|
||||
$query->join('node_counter', 's', 'n.nid = s.nid');
|
||||
$query->join('users_field_data', 'u', 'n.uid = u.uid');
|
||||
|
||||
return $query
|
||||
->fields('n', ['nid', 'title'])
|
||||
->fields('u', ['uid', 'name'])
|
||||
->condition($dbfield, 0, '<>')
|
||||
->condition('n.status', 1)
|
||||
// @todo This should be actually filtering on the desired node status
|
||||
// field language and just fall back to the default language.
|
||||
->condition('n.default_langcode', 1)
|
||||
->condition('u.default_langcode', 1)
|
||||
->orderBy($dbfield, 'DESC')
|
||||
->range(0, $dbrows)
|
||||
->execute();
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a node's "view statistics".
|
||||
*
|
||||
* @deprecated in Drupal 8.2.x, will be removed before Drupal 9.0.0.
|
||||
* Use \Drupal::service('statistics.storage.node')->fetchView($id).
|
||||
*/
|
||||
function statistics_get($id) {
|
||||
if ($id > 0) {
|
||||
/** @var \Drupal\statistics\StatisticsViewsResult $statistics */
|
||||
$statistics = \Drupal::service('statistics.storage.node')->fetchView($id);
|
||||
|
||||
// For backwards compatibility, return FALSE if an invalid node ID was
|
||||
// passed in.
|
||||
if (!($statistics instanceof StatisticsViewsResult)) {
|
||||
return FALSE;
|
||||
}
|
||||
return [
|
||||
'totalcount' => $statistics->getTotalCount(),
|
||||
'daycount' => $statistics->getDayCount(),
|
||||
'timestamp' => $statistics->getTimestamp(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ranking().
|
||||
*/
|
||||
function statistics_ranking() {
|
||||
if (\Drupal::config('statistics.settings')->get('count_content_views')) {
|
||||
return [
|
||||
'views' => [
|
||||
'title' => t('Number of views'),
|
||||
'join' => [
|
||||
'type' => 'LEFT',
|
||||
'table' => 'node_counter',
|
||||
'alias' => 'node_counter',
|
||||
'on' => 'node_counter.nid = i.sid',
|
||||
],
|
||||
// Inverse law that maps the highest view count on the site to 1 and 0
|
||||
// to 0. Note that the ROUND here is necessary for PostgreSQL and SQLite
|
||||
// in order to ensure that the :statistics_scale argument is treated as
|
||||
// a numeric type, because the PostgreSQL PDO driver sometimes puts
|
||||
// values in as strings instead of numbers in complex expressions like
|
||||
// this.
|
||||
'score' => '2.0 - 2.0 / (1.0 + node_counter.totalcount * (ROUND(:statistics_scale, 4)))',
|
||||
'arguments' => [':statistics_scale' => \Drupal::state()->get('statistics.node_counter_scale') ?: 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_HOOK() for block templates.
|
||||
*/
|
||||
function statistics_preprocess_block(&$variables) {
|
||||
if ($variables['configuration']['provider'] == 'statistics') {
|
||||
$variables['attributes']['role'] = 'navigation';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_block_alter().
|
||||
*
|
||||
* Removes the "popular" block from display if the module is not configured
|
||||
* 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)) {
|
||||
unset($definitions['statistics_popular_block']);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
administer statistics:
|
||||
title: 'Administer statistics'
|
||||
view post access counter:
|
||||
title: 'View content hits'
|
||||
30
2017/web/core/modules/statistics/statistics.php
Normal file
30
2017/web/core/modules/statistics/statistics.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Handles counts of node views via AJAX with minimal bootstrap.
|
||||
*/
|
||||
|
||||
use Drupal\Core\DrupalKernel;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
chdir('../../..');
|
||||
|
||||
$autoloader = require_once 'autoload.php';
|
||||
|
||||
$kernel = DrupalKernel::createFromRequest(Request::createFromGlobals(), $autoloader, 'prod');
|
||||
$kernel->boot();
|
||||
$container = $kernel->getContainer();
|
||||
|
||||
$views = $container
|
||||
->get('config.factory')
|
||||
->get('statistics.settings')
|
||||
->get('count_content_views');
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
7
2017/web/core/modules/statistics/statistics.routing.yml
Normal file
7
2017/web/core/modules/statistics/statistics.routing.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
statistics.settings:
|
||||
path: '/admin/config/system/statistics'
|
||||
defaults:
|
||||
_form: 'Drupal\statistics\StatisticsSettingsForm'
|
||||
_title: 'Statistics'
|
||||
requirements:
|
||||
_permission: 'administer statistics'
|
||||
6
2017/web/core/modules/statistics/statistics.services.yml
Normal file
6
2017/web/core/modules/statistics/statistics.services.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
services:
|
||||
statistics.storage.node:
|
||||
class: Drupal\statistics\NodeStatisticsDatabaseStorage
|
||||
arguments: ['@database', '@state', '@request_stack']
|
||||
tags:
|
||||
- { name: backend_overridable }
|
||||
66
2017/web/core/modules/statistics/statistics.tokens.inc
Normal file
66
2017/web/core/modules/statistics/statistics.tokens.inc
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Builds placeholder replacement tokens for node visitor statistics.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function statistics_token_info() {
|
||||
$node['total-count'] = [
|
||||
'name' => t("Number of views"),
|
||||
'description' => t("The number of visitors who have read the node."),
|
||||
];
|
||||
$node['day-count'] = [
|
||||
'name' => t("Views today"),
|
||||
'description' => t("The number of visitors who have read the node today."),
|
||||
];
|
||||
$node['last-view'] = [
|
||||
'name' => t("Last view"),
|
||||
'description' => t("The date on which a visitor last read the node."),
|
||||
'type' => 'date',
|
||||
];
|
||||
|
||||
return [
|
||||
'tokens' => ['node' => $node],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function statistics_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
|
||||
$token_service = \Drupal::token();
|
||||
|
||||
$replacements = [];
|
||||
|
||||
if ($type == 'node' & !empty($data['node'])) {
|
||||
$node = $data['node'];
|
||||
|
||||
foreach ($tokens as $name => $original) {
|
||||
if ($name == 'total-count') {
|
||||
$statistics = statistics_get($node->id());
|
||||
$replacements[$original] = $statistics['totalcount'];
|
||||
}
|
||||
elseif ($name == 'day-count') {
|
||||
$statistics = statistics_get($node->id());
|
||||
$replacements[$original] = $statistics['daycount'];
|
||||
}
|
||||
elseif ($name == 'last-view') {
|
||||
$statistics = statistics_get($node->id());
|
||||
$replacements[$original] = format_date($statistics['timestamp']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($created_tokens = $token_service->findWithPrefix($tokens, 'last-view')) {
|
||||
$statistics = statistics_get($node->id());
|
||||
$replacements += $token_service->generate('date', $created_tokens, ['date' => $statistics['timestamp']], $options, $bubbleable_metadata);
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
||||
76
2017/web/core/modules/statistics/statistics.views.inc
Normal file
76
2017/web/core/modules/statistics/statistics.views.inc
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide views data for statistics.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_data().
|
||||
*/
|
||||
function statistics_views_data() {
|
||||
$data['node_counter']['table']['group'] = t('Content statistics');
|
||||
|
||||
$data['node_counter']['table']['join'] = [
|
||||
'node_field_data' => [
|
||||
'left_field' => 'nid',
|
||||
'field' => 'nid',
|
||||
],
|
||||
];
|
||||
|
||||
$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['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['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',
|
||||
],
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
name: 'Statistics test views'
|
||||
type: module
|
||||
description: 'Provides default views for views statistics tests.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:statistics
|
||||
- drupal:views
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: test_statistics_integration
|
||||
label: 'Test statistics integration'
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: null
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
cache:
|
||||
type: tag
|
||||
query:
|
||||
type: views_query
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: none
|
||||
options:
|
||||
offset: 0
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
title:
|
||||
id: title
|
||||
table: node_field_data
|
||||
field: title
|
||||
label: ''
|
||||
alter:
|
||||
alter_text: false
|
||||
make_link: false
|
||||
absolute: false
|
||||
trim: false
|
||||
word_boundary: false
|
||||
ellipsis: false
|
||||
strip_tags: false
|
||||
html: false
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
plugin_id: field
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
timestamp:
|
||||
id: timestamp
|
||||
table: node_counter
|
||||
field: timestamp
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: 'Most recent view'
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
date_format: html_year
|
||||
custom_date_format: ''
|
||||
timezone: ''
|
||||
plugin_id: date
|
||||
totalcount:
|
||||
id: totalcount
|
||||
table: node_counter
|
||||
field: totalcount
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: 'Total views'
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
set_precision: false
|
||||
precision: 0
|
||||
decimal: .
|
||||
separator: ''
|
||||
format_plural: false
|
||||
format_plural_string: "1\x03@count"
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
plugin_id: numeric
|
||||
daycount:
|
||||
id: daycount
|
||||
table: node_counter
|
||||
field: daycount
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: 'Views today'
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
set_precision: false
|
||||
precision: 0
|
||||
decimal: .
|
||||
separator: ''
|
||||
format_plural: false
|
||||
format_plural_string: "1\x03@count"
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
plugin_id: numeric
|
||||
filters:
|
||||
status:
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
expose:
|
||||
operator: ''
|
||||
group: 1
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: status
|
||||
sorts:
|
||||
created:
|
||||
id: created
|
||||
table: node_field_data
|
||||
field: created
|
||||
order: DESC
|
||||
entity_type: node
|
||||
entity_field: created
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: null
|
||||
display_options:
|
||||
path: test_statistics_integration
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\Traits\Core\CronRunTrait;
|
||||
|
||||
/**
|
||||
* Tests the statistics admin.
|
||||
*
|
||||
* @group statistics
|
||||
*/
|
||||
class StatisticsAdminTest extends BrowserTestBase {
|
||||
|
||||
use CronRunTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'statistics'];
|
||||
|
||||
/**
|
||||
* A user that has permission to administer statistics.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $privilegedUser;
|
||||
|
||||
/**
|
||||
* A page node for which to check content statistics.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $testNode;
|
||||
|
||||
/**
|
||||
* The Guzzle HTTP client.
|
||||
*
|
||||
* @var \GuzzleHttp\Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Set the max age to 0 to simplify testing.
|
||||
$this->config('statistics.settings')->set('display_max_age', 0)->save();
|
||||
|
||||
// Create Basic page node type.
|
||||
if ($this->profile != 'standard') {
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
}
|
||||
$this->privilegedUser = $this->drupalCreateUser(['administer statistics', 'view post access counter', 'create page content']);
|
||||
$this->drupalLogin($this->privilegedUser);
|
||||
$this->testNode = $this->drupalCreateNode(['type' => 'page', 'uid' => $this->privilegedUser->id()]);
|
||||
$this->client = \Drupal::httpClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the statistics settings page works.
|
||||
*/
|
||||
public function testStatisticsSettings() {
|
||||
$config = $this->config('statistics.settings');
|
||||
$this->assertFalse($config->get('count_content_views'), 'Count content view log is disabled by default.');
|
||||
|
||||
// Enable counter on content view.
|
||||
$edit['statistics_count_content_views'] = 1;
|
||||
$this->drupalPostForm('admin/config/system/statistics', $edit, t('Save configuration'));
|
||||
$config = $this->config('statistics.settings');
|
||||
$this->assertTrue($config->get('count_content_views'), 'Count content view log 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];
|
||||
global $base_url;
|
||||
$stats_path = $base_url . '/' . drupal_get_path('module', 'statistics') . '/statistics.php';
|
||||
$this->client->post($stats_path, ['form_params' => $post]);
|
||||
|
||||
// Hit the node again (the counter is incremented after the hit, so
|
||||
// "1 view" will actually be shown when the node is hit the second time).
|
||||
$this->drupalGet('node/' . $this->testNode->id());
|
||||
$this->client->post($stats_path, ['form_params' => $post]);
|
||||
$this->assertText('1 view', 'Node is viewed once.');
|
||||
|
||||
$this->drupalGet('node/' . $this->testNode->id());
|
||||
$this->client->post($stats_path, ['form_params' => $post]);
|
||||
$this->assertText('2 views', 'Node is viewed 2 times.');
|
||||
|
||||
// Increase the max age to test that nodes are no longer immediately
|
||||
// updated, visit the node once more to populate the cache.
|
||||
$this->config('statistics.settings')->set('display_max_age', 3600)->save();
|
||||
$this->drupalGet('node/' . $this->testNode->id());
|
||||
$this->assertText('3 views', 'Node is viewed 3 times.');
|
||||
|
||||
$this->client->post($stats_path, ['form_params' => $post]);
|
||||
$this->drupalGet('node/' . $this->testNode->id());
|
||||
$this->assertText('3 views', 'Views counter was not updated.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->drupalGet('node/' . $this->testNode->id());
|
||||
// Manually calling statistics.php, simulating ajax behavior.
|
||||
$nid = $this->testNode->id();
|
||||
$post = ['nid' => $nid];
|
||||
global $base_url;
|
||||
$stats_path = $base_url . '/' . drupal_get_path('module', 'statistics') . '/statistics.php';
|
||||
$this->client->post($stats_path, ['form_params' => $post]);
|
||||
|
||||
$result = db_select('node_counter', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->condition('n.nid', $this->testNode->id())
|
||||
->execute()
|
||||
->fetchAssoc();
|
||||
$this->assertEqual($result['nid'], $this->testNode->id(), 'Verifying that the node counter is incremented.');
|
||||
|
||||
$this->testNode->delete();
|
||||
|
||||
$result = db_select('node_counter', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->condition('n.nid', $this->testNode->id())
|
||||
->execute()
|
||||
->fetchAssoc();
|
||||
$this->assertFalse($result, 'Verifying that the node counter is deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that cron clears day counts and expired access logs.
|
||||
*/
|
||||
public function testExpiredLogs() {
|
||||
$this->config('statistics.settings')
|
||||
->set('count_content_views', 1)
|
||||
->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];
|
||||
global $base_url;
|
||||
$stats_path = $base_url . '/' . drupal_get_path('module', 'statistics') . '/statistics.php';
|
||||
$this->client->post($stats_path, ['form_params' => $post]);
|
||||
$this->drupalGet('node/' . $this->testNode->id());
|
||||
$this->client->post($stats_path, ['form_params' => $post]);
|
||||
$this->assertText('1 view', 'Node is viewed once.');
|
||||
|
||||
// statistics_cron() will subtract
|
||||
// statistics.settings:accesslog.max_lifetime config from REQUEST_TIME in
|
||||
// the delete query, so wait two secs here to make sure the access log will
|
||||
// be flushed for the node just hit.
|
||||
sleep(2);
|
||||
$this->cronRun();
|
||||
|
||||
$this->drupalGet('admin/reports/pages');
|
||||
$this->assertNoText('node/' . $this->testNode->id(), 'No hit URL found.');
|
||||
|
||||
$result = db_select('node_counter', 'nc')
|
||||
->fields('nc', ['daycount'])
|
||||
->condition('nid', $this->testNode->id(), '=')
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertFalse($result, 'Daycounter is zero.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Tests if statistics.js is loaded when content is not printed.
|
||||
*
|
||||
* @group statistics
|
||||
*/
|
||||
class StatisticsAttachedTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'statistics'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalCreateContentType(['type' => 'page']);
|
||||
|
||||
// Install "statistics_test_attached" and set it as the default theme.
|
||||
$theme = 'statistics_test_attached';
|
||||
\Drupal::service('theme_handler')->install([$theme]);
|
||||
$this->config('system.theme')
|
||||
->set('default', $theme)
|
||||
->save();
|
||||
// Installing a theme will cause the kernel terminate event to rebuild the
|
||||
// router. Simulate that here.
|
||||
\Drupal::service('router.builder')->rebuildIfNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if statistics.js is loaded when content is not printed.
|
||||
*/
|
||||
public function testAttached() {
|
||||
|
||||
$node = Node::create([
|
||||
'type' => 'page',
|
||||
'title' => 'Page node',
|
||||
'body' => 'body text',
|
||||
]);
|
||||
$node->save();
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
|
||||
$this->assertRaw('core/modules/statistics/statistics.js', 'Statistics library is available');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Tests request logging for cached and uncached pages.
|
||||
*
|
||||
* We subclass WebTestBase rather than StatisticsTestBase, because we
|
||||
* want to test requests from an anonymous user.
|
||||
*
|
||||
* @group statistics
|
||||
*/
|
||||
class StatisticsLoggingTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'statistics', 'block', 'locale'];
|
||||
|
||||
/**
|
||||
* User with permissions to create and edit pages.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $authUser;
|
||||
|
||||
/**
|
||||
* Associative array representing a hypothetical Drupal language.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* The Guzzle HTTP client.
|
||||
*
|
||||
* @var \GuzzleHttp\Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create Basic page node type.
|
||||
if ($this->profile != 'standard') {
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
}
|
||||
|
||||
$this->authUser = $this->drupalCreateUser([
|
||||
// For node creation.
|
||||
'access content',
|
||||
'create page content',
|
||||
'edit own page content',
|
||||
// For language negotiation administration.
|
||||
'administer languages',
|
||||
'access administration pages',
|
||||
]);
|
||||
|
||||
// Ensure we have a node page to access.
|
||||
$this->node = $this->drupalCreateNode(['title' => $this->randomMachineName(255), 'uid' => $this->authUser->id()]);
|
||||
|
||||
// Add a custom language and enable path-based language negotiation.
|
||||
$this->drupalLogin($this->authUser);
|
||||
$this->language = [
|
||||
'predefined_langcode' => 'custom',
|
||||
'langcode' => 'xx',
|
||||
'label' => $this->randomMachineName(16),
|
||||
'direction' => 'ltr',
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $this->language, t('Add custom language'));
|
||||
$this->drupalPostForm('admin/config/regional/language/detection', ['language_interface[enabled][language-url]' => 1], t('Save settings'));
|
||||
$this->drupalLogout();
|
||||
|
||||
// Enable access logging.
|
||||
$this->config('statistics.settings')
|
||||
->set('count_content_views', 1)
|
||||
->save();
|
||||
|
||||
// Clear the logs.
|
||||
db_truncate('node_counter');
|
||||
$this->client = \Drupal::httpClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies node hit counter logging and script placement.
|
||||
*/
|
||||
public function testLogging() {
|
||||
$path = 'node/' . $this->node->id();
|
||||
$module_path = drupal_get_path('module', 'statistics');
|
||||
$stats_path = base_path() . $module_path . '/statistics.php';
|
||||
$lib_path = base_path() . $module_path . '/statistics.js';
|
||||
$expected_library = '/<script src=".*?' . preg_quote($lib_path, '/.') . '.*?">/is';
|
||||
|
||||
// Verify that logging scripts are not found on a non-node page.
|
||||
$this->drupalGet('node');
|
||||
$settings = $this->getDrupalSettings();
|
||||
$this->assertNoPattern($expected_library, 'Statistics library JS not found on node page.');
|
||||
$this->assertFalse(isset($settings['statistics']), 'Statistics settings not found on node page.');
|
||||
|
||||
// Verify that logging scripts are not found on a non-existent node page.
|
||||
$this->drupalGet('node/9999');
|
||||
$settings = $this->getDrupalSettings();
|
||||
$this->assertNoPattern($expected_library, 'Statistics library JS not found on non-existent node page.');
|
||||
$this->assertFalse(isset($settings['statistics']), 'Statistics settings not found on node page.');
|
||||
|
||||
// Verify that logging scripts are found on a valid node page.
|
||||
$this->drupalGet($path);
|
||||
$settings = $this->getDrupalSettings();
|
||||
$this->assertPattern($expected_library, 'Found statistics library JS on node page.');
|
||||
$this->assertIdentical($this->node->id(), $settings['statistics']['data']['nid'], '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->assertPattern($expected_library, 'Found statistics library JS on a valid node page in a non-default language.');
|
||||
$this->assertIdentical($this->node->id(), $settings['statistics']['data']['nid'], '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()];
|
||||
$this->client->post($base_root . $stats_path, ['form_params' => $post]);
|
||||
$node_counter = statistics_get($this->node->id());
|
||||
$this->assertIdentical($node_counter['totalcount'], 1);
|
||||
|
||||
// Try fetching statistics for an invalid node ID and verify it returns
|
||||
// FALSE.
|
||||
$node_id = 1000000;
|
||||
$node = Node::load($node_id);
|
||||
$this->assertNull($node);
|
||||
|
||||
// This is a test specifically for the deprecated statistics_get() function
|
||||
// and so should remain unconverted until that function is removed.
|
||||
$result = statistics_get($node_id);
|
||||
$this->assertIdentical($result, FALSE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Functional;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* Tests display of statistics report blocks.
|
||||
*
|
||||
* @group statistics
|
||||
*/
|
||||
class StatisticsReportsTest extends StatisticsTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* Tests the "popular content" block.
|
||||
*/
|
||||
public function testPopularContentBlock() {
|
||||
// Clear the block cache to load the Statistics module's block definitions.
|
||||
$this->container->get('plugin.manager.block')->clearCachedDefinitions();
|
||||
|
||||
// Visit a node to have something show up in the block.
|
||||
$node = $this->drupalCreateNode(['type' => 'page', 'uid' => $this->blockingUser->id()]);
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
// Manually calling statistics.php, simulating ajax behavior.
|
||||
$nid = $node->id();
|
||||
$post = http_build_query(['nid' => $nid]);
|
||||
$headers = ['Content-Type' => 'application/x-www-form-urlencoded'];
|
||||
global $base_url;
|
||||
$stats_path = $base_url . '/' . drupal_get_path('module', 'statistics') . '/statistics.php';
|
||||
$client = \Drupal::httpClient();
|
||||
$client->post($stats_path, ['headers' => $headers, 'body' => $post]);
|
||||
|
||||
// Configure and save the block.
|
||||
$block = $this->drupalPlaceBlock('statistics_popular_block', [
|
||||
'label' => 'Popular content',
|
||||
'top_day_num' => 3,
|
||||
'top_all_num' => 3,
|
||||
'top_last_num' => 3,
|
||||
]);
|
||||
|
||||
// Get some page and check if the block is displayed.
|
||||
$this->drupalGet('user');
|
||||
$this->assertText('Popular content', 'Found the popular content block.');
|
||||
$this->assertText("Today's", "Found today's popular content.");
|
||||
$this->assertText('All time', 'Found the all time popular content.');
|
||||
$this->assertText('Last viewed', 'Found the last viewed popular content.');
|
||||
|
||||
$tags = Cache::mergeTags($node->getCacheTags(), $block->getCacheTags());
|
||||
$tags = Cache::mergeTags($tags, $this->blockingUser->getCacheTags());
|
||||
$tags = Cache::mergeTags($tags, ['block_view', 'config:block_list', 'node_list', 'rendered', 'user_view']);
|
||||
$this->assertCacheTags($tags);
|
||||
$contexts = Cache::mergeContexts($node->getCacheContexts(), $block->getCacheContexts());
|
||||
$contexts = Cache::mergeContexts($contexts, ['url.query_args:_wrapper_format']);
|
||||
$this->assertCacheContexts($contexts);
|
||||
|
||||
// Check if the node link is displayed.
|
||||
$this->assertRaw(\Drupal::l($node->label(), $node->urlInfo('canonical')), 'Found link to visited node.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Defines a base class for testing the Statistics module.
|
||||
*/
|
||||
abstract class StatisticsTestBase extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'block', 'ban', 'statistics'];
|
||||
|
||||
/**
|
||||
* User with permissions to ban IP's.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $blockingUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create Basic page node type.
|
||||
if ($this->profile != 'standard') {
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
}
|
||||
|
||||
// Create user.
|
||||
$this->blockingUser = $this->drupalCreateUser([
|
||||
'access administration pages',
|
||||
'access site reports',
|
||||
'ban IP addresses',
|
||||
'administer blocks',
|
||||
'administer statistics',
|
||||
'administer users',
|
||||
]);
|
||||
$this->drupalLogin($this->blockingUser);
|
||||
|
||||
// Enable logging.
|
||||
$this->config('statistics.settings')
|
||||
->set('count_content_views', 1)
|
||||
->save();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Functional;
|
||||
|
||||
/**
|
||||
* Generates text using placeholders for dummy content to check statistics token
|
||||
* replacement.
|
||||
*
|
||||
* @group statistics
|
||||
*/
|
||||
class StatisticsTokenReplaceTest extends StatisticsTestBase {
|
||||
|
||||
/**
|
||||
* Creates a node, then tests the statistics tokens generated from it.
|
||||
*/
|
||||
public function testStatisticsTokenReplacement() {
|
||||
$language_interface = \Drupal::languageManager()->getCurrentLanguage();
|
||||
|
||||
// Create user and node.
|
||||
$user = $this->drupalCreateUser(['create page content']);
|
||||
$this->drupalLogin($user);
|
||||
$node = $this->drupalCreateNode(['type' => 'page', 'uid' => $user->id()]);
|
||||
|
||||
// Hit the node.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
// Manually calling statistics.php, simulating ajax behavior.
|
||||
$nid = $node->id();
|
||||
$post = http_build_query(['nid' => $nid]);
|
||||
$headers = ['Content-Type' => 'application/x-www-form-urlencoded'];
|
||||
global $base_url;
|
||||
$stats_path = $base_url . '/' . drupal_get_path('module', 'statistics') . '/statistics.php';
|
||||
$client = \Drupal::httpClient();
|
||||
$client->post($stats_path, ['headers' => $headers, 'body' => $post]);
|
||||
$statistics = statistics_get($node->id());
|
||||
|
||||
// Generate and test tokens.
|
||||
$tests = [];
|
||||
$tests['[node:total-count]'] = 1;
|
||||
$tests['[node:day-count]'] = 1;
|
||||
$tests['[node:last-view]'] = format_date($statistics['timestamp']);
|
||||
$tests['[node:last-view:short]'] = format_date($statistics['timestamp'], 'short');
|
||||
|
||||
// Test to make sure that we generated something for each token.
|
||||
$this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = \Drupal::token()->replace($input, ['node' => $node], ['langcode' => $language_interface->getId()]);
|
||||
$this->assertEqual($output, $expected, format_string('Statistics token %token replaced.', ['%token' => $input]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Functional\Views;
|
||||
|
||||
use Drupal\Tests\views\Functional\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Tests basic integration of views data from the statistics module.
|
||||
*
|
||||
* @group statistics
|
||||
* @see
|
||||
*/
|
||||
class IntegrationTest extends ViewTestBase {
|
||||
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['statistics', 'statistics_test_views', 'node'];
|
||||
|
||||
/**
|
||||
* Stores the user object that accesses the page.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* Stores the node object which is used by the test.
|
||||
*
|
||||
* @var \Drupal\node\Entity\Node
|
||||
*/
|
||||
protected $node;
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_statistics_integration'];
|
||||
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['statistics_test_views']);
|
||||
|
||||
// Create a new user for viewing nodes and statistics.
|
||||
$this->webUser = $this->drupalCreateUser(['access content', 'view post access counter']);
|
||||
|
||||
// Create a new user for viewing nodes only.
|
||||
$this->deniedUser = $this->drupalCreateUser(['access content']);
|
||||
|
||||
$this->drupalCreateContentType(['type' => 'page']);
|
||||
$this->node = $this->drupalCreateNode(['type' => 'page']);
|
||||
|
||||
// Enable counting of content views.
|
||||
$this->config('statistics.settings')
|
||||
->set('count_content_views', 1)
|
||||
->save();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the integration of the {node_counter} table in views.
|
||||
*/
|
||||
public function testNodeCounterIntegration() {
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
$this->drupalGet('node/' . $this->node->id());
|
||||
// Manually calling statistics.php, simulating ajax behavior.
|
||||
// @see \Drupal\statistics\Tests\StatisticsLoggingTest::testLogging().
|
||||
global $base_url;
|
||||
$stats_path = $base_url . '/' . drupal_get_path('module', 'statistics') . '/statistics.php';
|
||||
$client = $this->getHttpClient();
|
||||
$client->post($stats_path, ['form_params' => ['nid' => $this->node->id()]]);
|
||||
$this->drupalGet('test_statistics_integration');
|
||||
|
||||
$expected = statistics_get($this->node->id());
|
||||
// Convert the timestamp to year, to match the expected output of the date
|
||||
// handler.
|
||||
$expected['timestamp'] = date('Y', $expected['timestamp']);
|
||||
|
||||
foreach ($expected as $field => $value) {
|
||||
$xpath = "//div[contains(@class, views-field-$field)]/span[@class = 'field-content']";
|
||||
$this->assertFieldByXpath($xpath, $value, "The $field output matches the expected.");
|
||||
}
|
||||
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->deniedUser);
|
||||
$this->drupalGet('test_statistics_integration');
|
||||
$this->assertResponse(200);
|
||||
|
||||
foreach ($expected as $field => $value) {
|
||||
$xpath = "//div[contains(@class, views-field-$field)]/span[@class = 'field-content']";
|
||||
$this->assertNoFieldByXpath($xpath, $value, "The $field output is not displayed.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\FunctionalJavascript;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Tests that statistics works.
|
||||
*
|
||||
* @group system
|
||||
*/
|
||||
class StatisticsLoggingTest extends WebDriverTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node', 'statistics', 'language'];
|
||||
|
||||
/**
|
||||
* Node for tests.
|
||||
*
|
||||
* @var \Drupal\node\Entity\Node
|
||||
*/
|
||||
protected $node;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->config('statistics.settings')
|
||||
->set('count_content_views', 1)
|
||||
->save();
|
||||
|
||||
Role::load(AccountInterface::ANONYMOUS_ROLE)
|
||||
->grantPermission('view post access counter')
|
||||
->save();
|
||||
|
||||
// Add another language to enable multilingual path processor.
|
||||
ConfigurableLanguage::create(['id' => 'xx'])->save();
|
||||
$this->config('language.negotiation')->set('url.prefixes.en', 'en')->save();
|
||||
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
$this->node = $this->drupalCreateNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that statistics works with different addressing variants.
|
||||
*/
|
||||
public function testLoggingPage() {
|
||||
// At the first request, the page does not contain statistics counter.
|
||||
$this->assertNull($this->getStatisticsCounter('node/1'));
|
||||
$this->assertSame(1, $this->getStatisticsCounter('node/1'));
|
||||
$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'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets counter of views by path.
|
||||
*
|
||||
* @param string $path
|
||||
* A path to node.
|
||||
*
|
||||
* @return int|null
|
||||
* A counter of views. Returns NULL if the page does not contain statistics.
|
||||
*/
|
||||
protected function getStatisticsCounter($path) {
|
||||
$this->drupalGet($path);
|
||||
// Wait while statistics module send ajax request.
|
||||
$this->assertSession()->assertWaitOnAjaxRequest();
|
||||
// Resaving the node to call the hook_node_links_alter(), which is used to
|
||||
// update information on the page. See statistics_node_links_alter().
|
||||
$this->node->save();
|
||||
|
||||
$field_counter = $this->getSession()->getPage()->find('css', '.statistics-counter');
|
||||
return $field_counter ? (int) explode(' ', $field_counter->getText())[0] : NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Kernel\Migrate\d6;
|
||||
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
|
||||
/**
|
||||
* Tests the migration of node counter data to Drupal 8.
|
||||
*
|
||||
* @group statistics
|
||||
*/
|
||||
class MigrateNodeCounterTest extends MigrateDrupal6TestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_translation',
|
||||
'language',
|
||||
'menu_ui',
|
||||
// Required for translation migrations.
|
||||
'migrate_drupal_multilingual',
|
||||
'node',
|
||||
'statistics',
|
||||
'text',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('node');
|
||||
$this->installConfig('node');
|
||||
$this->installSchema('node', ['node_access']);
|
||||
$this->installSchema('statistics', ['node_counter']);
|
||||
|
||||
$this->executeMigrations([
|
||||
'language',
|
||||
'd6_filter_format',
|
||||
'd6_user_role',
|
||||
'd6_node_settings',
|
||||
'd6_user',
|
||||
'd6_node_type',
|
||||
'd6_language_content_settings',
|
||||
'd6_node',
|
||||
'd6_node_translation',
|
||||
'statistics_node_counter',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of node counter.
|
||||
*/
|
||||
public function testStatisticsSettings() {
|
||||
$this->assertNodeCounter(1, 2, 0, 1421727536);
|
||||
$this->assertNodeCounter(2, 1, 0, 1471428059);
|
||||
$this->assertNodeCounter(3, 1, 0, 1471428153);
|
||||
$this->assertNodeCounter(4, 1, 1, 1478755275);
|
||||
$this->assertNodeCounter(5, 1, 1, 1478755314);
|
||||
$this->assertNodeCounter(10, 5, 1, 1521137459);
|
||||
$this->assertNodeCounter(12, 3, 0, 1521137469);
|
||||
|
||||
// Tests that translated node counts include all translation counts.
|
||||
$this->executeMigration('statistics_node_translation_counter');
|
||||
$this->assertNodeCounter(10, 8, 2, 1521137463);
|
||||
$this->assertNodeCounter(12, 5, 1, 1521137470);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts various aspects of a node counter.
|
||||
*
|
||||
* @param int $nid
|
||||
* The node ID.
|
||||
* @param int $total_count
|
||||
* The expected total count.
|
||||
* @param int $day_count
|
||||
* The expected day count.
|
||||
* @param int $timestamp
|
||||
* The expected timestamp.
|
||||
*/
|
||||
protected function assertNodeCounter($nid, $total_count, $day_count, $timestamp) {
|
||||
/** @var \Drupal\statistics\StatisticsViewsResult $statistics */
|
||||
$statistics = $this->container->get('statistics.storage.node')->fetchView($nid);
|
||||
// @todo Remove casting after https://www.drupal.org/node/2926069 lands.
|
||||
$this->assertSame($total_count, (int) $statistics->getTotalCount());
|
||||
$this->assertSame($day_count, (int) $statistics->getDayCount());
|
||||
$this->assertSame($timestamp, (int) $statistics->getTimestamp());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Kernel\Migrate\d6;
|
||||
|
||||
use Drupal\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
|
||||
/**
|
||||
* Upgrade variables to statistics.settings.yml.
|
||||
*
|
||||
* @group migrate_drupal_6
|
||||
*/
|
||||
class MigrateStatisticsConfigsTest extends MigrateDrupal6TestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['statistics'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->executeMigration('statistics_settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of statistics variables to statistics.settings.yml.
|
||||
*/
|
||||
public function testStatisticsSettings() {
|
||||
$config = $this->config('statistics.settings');
|
||||
$this->assertSame(1, $config->get('count_content_views'));
|
||||
$this->assertConfigSchema(\Drupal::service('config.typed'), 'statistics.settings', $config->get());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Kernel\Migrate\d7;
|
||||
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
|
||||
/**
|
||||
* Tests the migration of node counter data to Drupal 8.
|
||||
*
|
||||
* @group statistics
|
||||
*/
|
||||
class MigrateNodeCounterTest extends MigrateDrupal7TestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_translation',
|
||||
'language',
|
||||
'menu_ui',
|
||||
// Required for translation migrations.
|
||||
'migrate_drupal_multilingual',
|
||||
'node',
|
||||
'statistics',
|
||||
'text',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('node');
|
||||
$this->installConfig('node');
|
||||
$this->installSchema('node', ['node_access']);
|
||||
$this->installSchema('statistics', ['node_counter']);
|
||||
|
||||
$this->executeMigrations([
|
||||
'language',
|
||||
'd7_user_role',
|
||||
'd7_user',
|
||||
'd7_node_type',
|
||||
'd7_language_content_settings',
|
||||
'd7_node',
|
||||
'd7_node_translation',
|
||||
'statistics_node_counter',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of node counter.
|
||||
*/
|
||||
public function testStatisticsSettings() {
|
||||
$this->assertNodeCounter(1, 2, 0, 1421727536);
|
||||
$this->assertNodeCounter(2, 1, 0, 1471428059);
|
||||
$this->assertNodeCounter(4, 1, 1, 1478755275);
|
||||
|
||||
// Tests that translated node counts include all translation counts.
|
||||
$this->executeMigration('statistics_node_translation_counter');
|
||||
$this->assertNodeCounter(2, 2, 0, 1471428153);
|
||||
$this->assertNodeCounter(4, 2, 2, 1478755314);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts various aspects of a node counter.
|
||||
*
|
||||
* @param int $nid
|
||||
* The node ID.
|
||||
* @param int $total_count
|
||||
* The expected total count.
|
||||
* @param int $day_count
|
||||
* The expected day count.
|
||||
* @param int $timestamp
|
||||
* The expected timestamp.
|
||||
*/
|
||||
protected function assertNodeCounter($nid, $total_count, $day_count, $timestamp) {
|
||||
/** @var \Drupal\statistics\StatisticsViewsResult $statistics */
|
||||
$statistics = $this->container->get('statistics.storage.node')->fetchView($nid);
|
||||
// @todo Remove casting after https://www.drupal.org/node/2926069 lands.
|
||||
$this->assertSame($total_count, (int) $statistics->getTotalCount());
|
||||
$this->assertSame($day_count, (int) $statistics->getDayCount());
|
||||
$this->assertSame($timestamp, (int) $statistics->getTimestamp());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Kernel\Migrate\d7;
|
||||
|
||||
use Drupal\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
|
||||
/**
|
||||
* Upgrade variables to statistics.settings.yml.
|
||||
*
|
||||
* @group migrate_drupal_7
|
||||
*/
|
||||
class MigrateStatisticsConfigsTest extends MigrateDrupal7TestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['statistics'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->executeMigration('statistics_settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of statistics variables to statistics.settings.yml.
|
||||
*/
|
||||
public function testStatisticsSettings() {
|
||||
$config = $this->config('statistics.settings');
|
||||
$this->assertIdentical(1, $config->get('count_content_views'));
|
||||
$this->assertConfigSchema(\Drupal::service('config.typed'), 'statistics.settings', $config->get());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Kernel\Plugin\migrate\source;
|
||||
|
||||
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
|
||||
|
||||
/**
|
||||
* Tests the node_counter source plugin.
|
||||
*
|
||||
* @covers \Drupal\statistics\Plugin\migrate\source\NodeCounter
|
||||
*
|
||||
* @group statistics
|
||||
*/
|
||||
class NodeCounterTest extends MigrateSqlSourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['migrate_drupal', 'statistics'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function providerSource() {
|
||||
$tests = [];
|
||||
|
||||
// The source data.
|
||||
$tests[0]['source_data']['node_counter'] = [
|
||||
[
|
||||
'nid' => 1,
|
||||
'totalcount' => 2,
|
||||
'daycount' => 0,
|
||||
'timestamp' => 1421727536,
|
||||
],
|
||||
[
|
||||
'nid' => 2,
|
||||
'totalcount' => 1,
|
||||
'daycount' => 0,
|
||||
'timestamp' => 1471428059,
|
||||
],
|
||||
[
|
||||
'nid' => 3,
|
||||
'totalcount' => 1,
|
||||
'daycount' => 0,
|
||||
'timestamp' => 1471428153,
|
||||
],
|
||||
[
|
||||
'nid' => 4,
|
||||
'totalcount' => 1,
|
||||
'daycount' => 1,
|
||||
'timestamp' => 1478755275,
|
||||
],
|
||||
[
|
||||
'nid' => 5,
|
||||
'totalcount' => 1,
|
||||
'daycount' => 1,
|
||||
'timestamp' => 1478755314,
|
||||
],
|
||||
];
|
||||
|
||||
// The expected results.
|
||||
$tests[0]['expected_data'] = $tests[0]['source_data']['node_counter'];
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\statistics\Unit;
|
||||
|
||||
use Drupal\statistics\StatisticsViewsResult;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\statistics\StatisticsViewsResult
|
||||
* @group statistics
|
||||
*/
|
||||
class StatisticsViewsResultTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests migration of node counter.
|
||||
*
|
||||
* @covers ::__construct
|
||||
*
|
||||
* @dataProvider providerTestStatisticsCount
|
||||
*/
|
||||
public function testStatisticsCount($total_count, $day_count, $timestamp) {
|
||||
$statistics = new StatisticsViewsResult($total_count, $day_count, $timestamp);
|
||||
$this->assertSame((int) $total_count, $statistics->getTotalCount());
|
||||
$this->assertSame((int) $day_count, $statistics->getDayCount());
|
||||
$this->assertSame((int) $timestamp, $statistics->getTimestamp());
|
||||
}
|
||||
|
||||
public function providerTestStatisticsCount() {
|
||||
return [
|
||||
[2, 0, 1421727536],
|
||||
[1, 0, 1471428059],
|
||||
[1, 1, 1478755275],
|
||||
['1', '1', '1478755275'],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<article{{ attributes }}>
|
||||
|
||||
{{ title_prefix }}
|
||||
{% if not page %}
|
||||
<h2{{ title_attributes }}>
|
||||
<a href="{{ url }}" rel="bookmark">{{ label }}</a>
|
||||
</h2>
|
||||
{% endif %}
|
||||
{{ title_suffix }}
|
||||
|
||||
{% if display_submitted %}
|
||||
<footer>
|
||||
{{ author_picture }}
|
||||
<div{{ author_attributes }}>
|
||||
{% trans %}Submitted by {{ author_name }} on {{ date }}{% endtrans %}
|
||||
{{ metadata }}
|
||||
</div>
|
||||
</footer>
|
||||
{% endif %}
|
||||
|
||||
<div{{ content_attributes }}>
|
||||
{{ content.body }}
|
||||
</div>
|
||||
</article>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
name: 'Statistics test attached theme'
|
||||
type: theme
|
||||
description: 'Theme for testing attached library'
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
Reference in a new issue