Commit 3260c6c4 authored by alexpott's avatar alexpott

Issue #2851293 by dagmar, alexpott, Munavijayalakshmi, jibran, Lendude,...

Issue #2851293 by dagmar, alexpott, Munavijayalakshmi, jibran, Lendude, dawehner, catch: dblog is using the wrong views field, filter and relationships definitions
parent f156c3d1
......@@ -11,3 +11,7 @@ views.field.dblog_message:
views.field.dblog_operations:
type: views_field
label: 'Operation link markup'
views.filter.dblog_types:
type: 'views.filter.in_operator'
label: 'Types'
......@@ -90,3 +90,69 @@ function dblog_schema() {
return $schema;
}
/**
* Use standard plugin for wid and uid fields. Use dblog_types for type filter.
*/
function dblog_update_8400() {
$config_factory = \Drupal::configFactory();
foreach ($config_factory->listAll('views.view.') as $view_config_name) {
$view = $config_factory->getEditable($view_config_name);
if ($view->get('base_table') != 'watchdog') {
continue;
}
$save = FALSE;
foreach ($view->get('display') as $display_name => $display) {
// Iterate through all the fields of watchdog views based tables.
if (isset($display['display_options']['fields'])) {
foreach ($display['display_options']['fields'] as $field_name => $field) {
// We are only interested in wid and uid fields from the watchdog
// table that still use the numeric id.
if (isset($field['table']) &&
$field['table'] === 'watchdog' &&
$field['plugin_id'] == 'numeric' &&
in_array($field['field'], ['wid', 'uid'])) {
$save = TRUE;
$new_value = $field;
$new_value['plugin_id'] = 'standard';
// Delete all the attributes related to numeric fields.
unset(
$new_value['set_precision'],
$new_value['precision'],
$new_value['decimal'],
$new_value['separator'],
$new_value['format_plural'],
$new_value['format_plural_string'],
$new_value['prefix'],
$new_value['suffix']
);
$view->set("display.$display_name.display_options.fields.$field_name", $new_value);
}
}
}
// Iterate all filters looking for type filters to update.
if (isset($display['display_options']['filters'])) {
foreach ($display['display_options']['filters'] as $filter_name => $filter) {
if (isset($filter['table']) &&
$filter['table'] === 'watchdog' &&
$filter['plugin_id'] == 'in_operator' &&
$filter['field'] == 'type') {
$save = TRUE;
$filter['plugin_id'] = 'dblog_types';
$view->set("display.$display_name.display_options.filters.$filter_name", $filter);
}
}
}
}
if ($save) {
$view->save();
}
}
}
......@@ -12,6 +12,7 @@
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\views\ViewEntityInterface;
/**
* Implements hook_help().
......@@ -115,3 +116,74 @@ function dblog_form_system_logging_settings_alter(&$form, FormStateInterface $fo
function dblog_logging_settings_submit($form, FormStateInterface $form_state) {
\Drupal::configFactory()->getEditable('dblog.settings')->set('row_limit', $form_state->getValue('dblog_row_limit'))->save();
}
/**
* Implements hook_ENTITY_TYPE_presave() for views entities.
*
* This hook ensures there are no views based that are using a wrong plugin for
* wid and uid fields on views that use watchdog as base table.
*
* @deprecated in Drupal 8.4.x and will be removed before 9.0.0.
*
* @see https://www.drupal.org/node/2876378
*/
function dblog_view_presave(ViewEntityInterface $view) {
// Only interested in watchdog based views.
if ($view->get('base_table') != 'watchdog') {
return;
}
$displays = $view->get('display');
$update = FALSE;
foreach ($displays as $display_name => $display) {
// Iterate through all the fields of watchdog views based tables.
if (isset($display['display_options']['fields'])) {
foreach ($display['display_options']['fields'] as $field_name => $field) {
// We are only interested in wid and uid fields from the watchdog table
// that still use the numeric id.
if (isset($field['table']) &&
$field['table'] === 'watchdog' &&
$field['plugin_id'] == 'numeric' &&
in_array($field['field'], ['wid', 'uid'], TRUE)) {
$displays[$display_name]['display_options']['fields'][$field_name]['plugin_id'] = 'standard';
// Delete all the attributes related to numeric fields.
unset(
$displays[$display_name]['display_options']['fields'][$field_name]['set_precision'],
$displays[$display_name]['display_options']['fields'][$field_name]['precision'],
$displays[$display_name]['display_options']['fields'][$field_name]['decimal'],
$displays[$display_name]['display_options']['fields'][$field_name]['separator'],
$displays[$display_name]['display_options']['fields'][$field_name]['format_plural'],
$displays[$display_name]['display_options']['fields'][$field_name]['format_plural_string'],
$displays[$display_name]['display_options']['fields'][$field_name]['prefix'],
$displays[$display_name]['display_options']['fields'][$field_name]['suffix']
);
$update = TRUE;
@trigger_error("The numeric plugin for watchdog.$field_name field is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Must use standard plugin instead. See https://www.drupal.org/node/2876378.", E_USER_DEPRECATED);
}
}
}
// Iterate all filters looking for type filters to update.
if (isset($display['display_options']['filters'])) {
foreach ($display['display_options']['filters'] as $filter_name => $filter) {
if (isset($filter['table']) &&
$filter['table'] === 'watchdog' &&
$filter['plugin_id'] == 'in_operator' &&
$filter['field'] == 'type') {
$displays[$display_name]['display_options']['filters'][$filter_name]['plugin_id'] = 'dblog_types';
$update = TRUE;
@trigger_error("The in_operator plugin for watchdog.type filter is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Must use dblog_types plugin instead. See https://www.drupal.org/node/2876378.", E_USER_DEPRECATED);
}
}
}
}
if ($update) {
$view->set('display', $displays);
}
}
......@@ -24,7 +24,7 @@ function dblog_views_data() {
'title' => t('WID'),
'help' => t('Unique watchdog event ID.'),
'field' => [
'id' => 'numeric',
'id' => 'standard',
],
'filter' => [
'id' => 'numeric',
......@@ -39,9 +39,9 @@ function dblog_views_data() {
$data['watchdog']['uid'] = [
'title' => t('UID'),
'help' => t('The user ID of the user on which the log entry was written..'),
'help' => t('The user ID of the user on which the log entry was written.'),
'field' => [
'id' => 'numeric',
'id' => 'standard',
],
'filter' => [
'id' => 'numeric',
......@@ -52,7 +52,7 @@ function dblog_views_data() {
'relationship' => [
'title' => t('User'),
'help' => t('The user on which the log entry as written.'),
'base' => 'users',
'base' => 'users_field_data',
'base field' => 'uid',
'id' => 'standard',
],
......@@ -68,8 +68,7 @@ function dblog_views_data() {
'id' => 'string',
],
'filter' => [
'id' => 'in_operator',
'options callback' => '_dblog_get_message_types',
'id' => 'dblog_types',
],
'sort' => [
'id' => 'standard',
......@@ -120,7 +119,7 @@ function dblog_views_data() {
],
'filter' => [
'id' => 'in_operator',
'options callback' => 'Drupal\dblog\Controller\DbLogController::getLogLevelClassMap',
'options callback' => 'Drupal\Core\Logger\RfcLogLevel::getLevels',
],
'sort' => [
'id' => 'standard',
......
<?php
namespace Drupal\dblog\Plugin\views\filter;
use Drupal\views\Plugin\views\filter\InOperator;
/**
* Exposes log types to views module.
*
* @ViewsFilter("dblog_types")
*/
class DblogTypes extends InOperator {
/**
* {@inheritdoc}
*/
public function getValueOptions() {
if (!isset($this->valueOptions)) {
$this->valueOptions = _dblog_get_message_types();
}
return $this->valueOptions;
}
}
<?php
namespace Drupal\dblog\Tests\Update;
use Drupal\system\Tests\Update\UpdatePathTestBase;
use Drupal\views\Views;
use Drupal\Core\Serialization\Yaml;
/**
* Tests the upgrade path for views field and filter handlers.
*
* @see dblog_update_8400()
*
* @group Update
*/
class DblogFiltersAndFieldsUpgradeTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../tests/fixtures/update/dblog-2851293.php',
];
}
/**
* Tests that field and filter handlers are updated properly.
*/
public function testDblogUpgradePath() {
$this->runUpdates();
$view = Views::getView('dblog_2851293');
$data = $view->storage->toArray();
$fields = $data['display']['default']['display_options']['fields'];
// The 'wid' and 'uid' fields should use the standard plugin now.
$this->assertEqual('standard', $fields['wid']['plugin_id']);
$this->assertEqual('standard', $fields['uid']['plugin_id']);
$filters = $data['display']['default']['display_options']['filters'];
// The 'type' filter should use the dblog_types plugin now.
$this->assertEqual('dblog_types', $filters['type']['plugin_id']);
// Now that the view had been converted, try the same approach but using
// dblog_view_presave()
$config_factory = \Drupal::configFactory();
$config_view = $config_factory->getEditable('views.view.dblog_2851293');
$config_view->setData(Yaml::decode(file_get_contents('core/modules/dblog/tests/modules/dblog_test_views/test_views/views.view.dblog_2851293.yml')));
$config_view->save();
// Make sure we have a not upgraded view.
$view = Views::getView('dblog_2851293');
$data = $view->storage->toArray();
$fields = $data['display']['default']['display_options']['fields'];
$filters = $data['display']['default']['display_options']['filters'];
$this->assertEqual('numeric', $fields['wid']['plugin_id']);
$this->assertEqual('numeric', $fields['uid']['plugin_id']);
$this->assertEqual('in_operator', $filters['type']['plugin_id']);
// Now save the view. This trigger dblog_view_presave().
$view->save();
// Finally check the same convertion proccess ran.
$data = $view->storage->toArray();
$fields = $data['display']['default']['display_options']['fields'];
$filters = $data['display']['default']['display_options']['filters'];
$this->assertEqual('standard', $fields['wid']['plugin_id']);
$this->assertEqual('standard', $fields['uid']['plugin_id']);
$this->assertEqual('dblog_types', $filters['type']['plugin_id']);
}
}
<?php
/**
* @file
* Test fixture.
*/
use Drupal\Core\Database\Database;
use Drupal\Core\Serialization\Yaml;
$connection = Database::getConnection();
$connection->insert('config')
->fields([
'collection' => '',
'name' => 'views.view.dblog_2851293',
'data' => serialize(Yaml::decode(file_get_contents('core/modules/dblog/tests/modules/dblog_test_views/test_views/views.view.dblog_2851293.yml'))),
])
->execute();
langcode: en
status: true
dependencies:
module:
- user
id: dblog_2851293
label: dblog_2851293
module: views
description: 'Base view to test upgrade from issue #2851293'
tag: ''
base_table: watchdog
base_field: wid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'access site reports'
cache:
type: none
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Filter
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: some
options:
items_per_page: 10
offset: 0
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
wid:
table: watchdog
field: wid
id: wid
entity_type: null
entity_field: null
plugin_id: numeric
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
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: ''
uid:
id: uid
table: watchdog
field: uid
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
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:
type:
id: type
table: watchdog
field: type
relationship: none
group_type: group
admin_label: ''
operator: in
value: { }
group: 1
exposed: true
expose:
operator_id: type_op
label: Type
description: ''
use_operator: false
operator: type_op
identifier: type
required: false
remember: false
multiple: true
reduce: false
plugin_id: in_operator
sorts: { }
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_interface'
- user.permissions
tags: { }
......@@ -18,41 +18,161 @@
class ViewsIntegrationTest extends ViewsKernelTestBase {
/**
* Views used by this test.
*
* @var array
* {@inheritdoc}
*/
public static $testViews = ['test_dblog'];
public static $testViews = ['test_dblog', 'dblog_integration_test'];
/**
* Modules to enable.
*
* @var array
* {@inheritdoc}
*/
public static $modules = ['dblog', 'dblog_test_views', 'user'];
/**
* {@inheritdoc}
*/
protected $columnMap = ['watchdog_message' => 'message'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp();
// Rebuild the router, otherwise we can't generate links.
$this->container->get('router.builder')->rebuild();
$this->installEntitySchema('user');
$this->installSchema('dblog', ['watchdog']);
ViewTestData::createTestViews(get_class($this), ['dblog_test_views']);
}
/**
* Tests the integration.
* Tests the messages escaping functionality.
*/
public function testMessages() {
// Remove the watchdog entries added by the potential batch process.
$this->container->get('database')->truncate('watchdog')->execute();
$entries = $this->createLogEntries();
$view = Views::getView('test_dblog');
$this->executeView($view);
$view->initStyle();
foreach ($entries as $index => $entry) {
if (!isset($entry['variables'])) {
continue;
}
$this->assertEqual($view->style_plugin->getField($index, 'message'), SafeMarkup::format($entry['message'], $entry['variables']));
$link_field = $view->style_plugin->getField($index, 'link');
// The 3rd entry contains some unsafe markup that needs to get filtered.
if ($index == 2) {
// Make sure that unsafe link differs from the rendered link, so we know
// that some filtering actually happened.
$this->assertNotEqual($link_field, $entry['variables']['link']);
}
$this->assertEqual($link_field, Xss::filterAdmin($entry['variables']['link']));
}
// Disable replacing variables and check that the tokens aren't replaced.
$view->destroy();
$view->storage->invalidateCaches();
$view->initHandlers();
$this->executeView($view);
$view->initStyle();
$view->field['message']->options['replace_variables'] = FALSE;
foreach ($entries as $index => $entry) {
$this->assertEqual($view->style_plugin->getField($index, 'message'), $entry['message']);
}
}
/**
* Tests the relationship with the users_field_data table.
*/
public function testIntegration() {
public function testRelationship() {
$view = Views::getView('dblog_integration_test');
$view->setDisplay('page_1');
// The uid relationship should now join to the {users_field_data} table.
$tables = array_keys($view->getBaseTables());
$this->assertTrue(in_array('users_field_data', $tables));
$this->assertFalse(in_array('users', $tables));
$this->assertTrue(in_array('watchdog', $tables));
}
/**
* Test views can be filtered by severity and log type.
*/
public function testFiltering() {
// Remove the watchdog entries added by the potential batch process.
$this->container->get('database')->truncate('watchdog')->execute();
$this->createLogEntries();
$view = Views::getView('dblog_integration_test');
$filters = [
'severity' => [
'id' => 'severity',
'table' => 'watchdog',
'field' => 'severity',
'relationship' => 'none',
'group_type' => 'group',
'admin_label' => '',
'operator' => 'in',
'value' => [
RfcLogLevel::WARNING,
],
'group' => 1,
'exposed' => FALSE,
'plugin_id' => 'in_operator',
],
];
$view->setDisplay('page_1');
$view->displayHandlers->get('page_1')->overrideOption('filters', $filters);
$view->save();
$this->executeView($view);
$resultset = [['message' => 'Warning message']];
$this->assertIdenticalResultset($view, $resultset, $this->columnMap);
$view = Views::getView('dblog_integration_test');
$filters = [
'type' => [
'id' => 'type',
'table' => 'watchdog',
'field' => 'type',
'relationship' => 'none',
'group_type' => 'group',
'admin_label' => '',
'operator' => 'in',
'value' => [
'my-module' => 'my-module',
],
'group' => '1',
'exposed' => FALSE,
'is_grouped' => FALSE,
'plugin_id' => 'dblog_types',
],
];
$view->setDisplay('page_1');
$view->displayHandlers->get('page_1')->overrideOption('filters', $filters);
$view->save();
$this->executeView($view);
$resultset = [['message' => 'My module message']];
$this->assertIdenticalResultset($view, $resultset, $this->columnMap);
}
/**
* Create a set of log entries.
*
* @return array
* An array of data used to create the log entries.
*/
protected function createLogEntries() {
$entries = [];
// Setup a watchdog entry without tokens.
$entries[] = [
......@@ -76,41 +196,28 @@ public function testIntegration() {
'link' => '<a href="' . \Drupal::url('<front>') . '"><object>Link</object></a>',
],
];
// Setup a watchdog entry with severity WARNING.
$entries[] = [
'message' => 'Warning message',
'severity' => RfcLogLevel::WARNING,
];
// Setup a watchdog entry with a different module.
$entries[] = [
'message' => 'My module message',
'severity' => RfcLogLevel::INFO,
'type' => 'my-module',
];
$logger_factory = $this->container->get('logger.factory');
foreach ($entries as $entry) {
$entry += [
'type' => 'test-views',
'severity' => RfcLogLevel::NOTICE,
'variables' => [],
];
$logger_factory->get($entry['type'])->log($entry['severity'], $entry['message'], $entry['variables']);
}
$view = Views::getView('test_dblog');
$this->executeView($view);
$view->initStyle();
foreach ($entries as $index => $entry) {
$this->assertEqual($view->style_plugin->getField($index, 'message'), SafeMarkup::format($entry['message'], $entry['variables']));
$link_field = $view->style_plugin->getField($index, 'link');
// The 3rd entry contains some unsafe markup that needs to get filtered.
if ($index == 2) {
// Make sure that unsafe link differs from the rendered link, so we know
// that some filtering actually happened.
$this->assertNotEqual($link_field, $entry['variables']['link']);
}
$this->assertEqual($link_field, Xss::filterAdmin($entry['variables']['link']));
}
// Disable replacing variables and check that the tokens aren't replaced.
$view->destroy();
$view->storage->invalidateCaches();