Newer
Older
Alex Pott
committed
<?php
/**
* @file
* Contains \Drupal\dblog\Controller\DbLogController.
*/
namespace Drupal\dblog\Controller;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Controller\ControllerBase;
Alex Pott
committed
use Drupal\Core\Database\Connection;
Alex Pott
committed
use Drupal\Core\Datetime\DateFormatter;
Alex Pott
committed
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\Url;
use Drupal\user\Entity\User;
Alex Pott
committed
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Returns responses for dblog routes.
*/
Angie Byron
committed
class DbLogController extends ControllerBase {
Alex Pott
committed
/**
* The database service.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The date formatter service.
Alex Pott
committed
* @var \Drupal\Core\Datetime\DateFormatter
*/
protected $dateFormatter;
/**
* The form builder service.
*
* @var \Drupal\Core\Form\FormBuilderInterface
*/
protected $formBuilder;
/**
* The user storage.
*
* @var \Drupal\user\UserStorageInterface
*/
protected $userStorage;
Alex Pott
committed
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('database'),
$container->get('module_handler'),
Alex Pott
committed
$container->get('date.formatter'),
$container->get('form_builder')
Alex Pott
committed
);
}
/**
* Constructs a DbLogController object.
*
* @param \Drupal\Core\Database\Connection $database
Alex Pott
committed
* A database connection.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
Alex Pott
committed
* A module handler.
Alex Pott
committed
* @param \Drupal\Core\Datetime\DateFormatter $date_formatter
* The date formatter service.
* @param \Drupal\Core\Form\FormBuilderInterface $form_builder
* The form builder service.
Alex Pott
committed
*/
public function __construct(Connection $database, ModuleHandlerInterface $module_handler, DateFormatter $date_formatter, FormBuilderInterface $form_builder) {
Alex Pott
committed
$this->database = $database;
$this->moduleHandler = $module_handler;
$this->dateFormatter = $date_formatter;
$this->formBuilder = $form_builder;
$this->userStorage = $this->entityManager()->getStorage('user');
Alex Pott
committed
}
/**
* Gets an array of log level classes.
*
* @return array
* An array of log level classes.
*/
public static function getLogLevelClassMap() {
return array(
RfcLogLevel::DEBUG => 'dblog-debug',
RfcLogLevel::INFO => 'dblog-info',
RfcLogLevel::NOTICE => 'dblog-notice',
RfcLogLevel::WARNING => 'dblog-warning',
RfcLogLevel::ERROR => 'dblog-error',
RfcLogLevel::CRITICAL => 'dblog-critical',
RfcLogLevel::ALERT => 'dblog-alert',
RfcLogLevel::EMERGENCY => 'dblog-emergency',
Alex Pott
committed
);
}
/**
* Displays a listing of database log messages.
*
* Messages are truncated at 56 chars.
* Full-length messages can be viewed on the message details page.
*
* @return array
* A render array as expected by drupal_render().
*
* @see dblog_clear_log_form()
* @see dblog_event()
*/
public function overview() {
$filter = $this->buildFilterQuery();
$rows = array();
$classes = static::getLogLevelClassMap();
$this->moduleHandler->loadInclude('dblog', 'admin.inc');
$build['dblog_filter_form'] = $this->formBuilder->getForm('Drupal\dblog\Form\DblogFilterForm');
$build['dblog_clear_log_form'] = $this->formBuilder->getForm('Drupal\dblog\Form\DblogClearLogForm');
Alex Pott
committed
$header = array(
// Icon column.
'',
array(
Angie Byron
committed
'data' => $this->t('Type'),
Alex Pott
committed
'field' => 'w.type',
'class' => array(RESPONSIVE_PRIORITY_MEDIUM)),
array(
Angie Byron
committed
'data' => $this->t('Date'),
Alex Pott
committed
'field' => 'w.wid',
'sort' => 'desc',
'class' => array(RESPONSIVE_PRIORITY_LOW)),
Angie Byron
committed
$this->t('Message'),
Alex Pott
committed
array(
Angie Byron
committed
'data' => $this->t('User'),
'field' => 'ufd.name',
Alex Pott
committed
'class' => array(RESPONSIVE_PRIORITY_MEDIUM)),
array(
Angie Byron
committed
'data' => $this->t('Operations'),
Alex Pott
committed
'class' => array(RESPONSIVE_PRIORITY_LOW)),
);
$query = $this->database->select('watchdog', 'w')
Angie Byron
committed
->extend('\Drupal\Core\Database\Query\PagerSelectExtender')
->extend('\Drupal\Core\Database\Query\TableSortExtender');
Alex Pott
committed
$query->fields('w', array(
'wid',
'uid',
'severity',
'type',
'timestamp',
'message',
'variables',
'link',
));
$query->leftJoin('users_field_data', 'ufd', 'w.uid = ufd.uid');
Alex Pott
committed
if (!empty($filter['where'])) {
$query->where($filter['where'], $filter['args']);
}
$result = $query
->limit(50)
->orderByHeader($header)
->execute();
foreach ($result as $dblog) {
$message = $this->formatMessage($dblog);
if ($message && isset($dblog->wid)) {
catch
committed
$title = Unicode::truncate(Html::decodeEntities(strip_tags($message)), 256, TRUE, TRUE);
$log_text = Unicode::truncate($title, 56, TRUE, TRUE);
// The link generator will escape any unsafe HTML entities in the final
// text.
$message = $this->l($log_text, new Url('dblog.event', array('event_id' => $dblog->wid), array(
Alex Pott
committed
'attributes' => array(
catch
committed
// Provide a title for the link for useful hover hints. The
// Attribute object will escape any unsafe HTML entities in the
// final text.
'title' => $title,
Alex Pott
committed
),
)));
Alex Pott
committed
}
$username = array(
'#theme' => 'username',
'#account' => $this->userStorage->load($dblog->uid),
Alex Pott
committed
$rows[] = array(
'data' => array(
// Cells.
array('class' => array('icon')),
Angie Byron
committed
$this->t($dblog->type),
$this->dateFormatter->format($dblog->timestamp, 'short'),
Alex Pott
committed
$message,
array('data' => $username),
array('data' => array('#markup' => $dblog->link)),
Alex Pott
committed
),
// Attributes for table row.
'class' => array(Html::getClass('dblog-' . $dblog->type), $classes[$dblog->severity]),
Alex Pott
committed
);
}
$build['dblog_table'] = array(
Angie Byron
committed
'#type' => 'table',
Alex Pott
committed
'#header' => $header,
'#rows' => $rows,
'#attributes' => array('id' => 'admin-dblog', 'class' => array('admin-dblog')),
Angie Byron
committed
'#empty' => $this->t('No log messages available.'),
'#attached' => array(
'library' => array('dblog/drupal.dblog'),
),
Alex Pott
committed
);
$build['dblog_pager'] = array('#type' => 'pager');
Alex Pott
committed
return $build;
}
/**
* Displays details about a specific database log message.
*
* @param int $event_id
* Unique ID of the database log message.
*
* @return array
* If the ID is located in the Database Logging table, a build array in the
* format expected by drupal_render();
*
*/
public function eventDetails($event_id) {
$build = array();
if ($dblog = $this->database->query('SELECT w.*, u.uid FROM {watchdog} w LEFT JOIN {users} u ON u.uid = w.uid WHERE w.wid = :id', array(':id' => $event_id))->fetchObject()) {
$severity = RfcLogLevel::getLevels();
$message = $this->formatMessage($dblog);
$username = array(
'#theme' => 'username',
'#account' => $dblog->uid ? $this->userStorage->load($dblog->uid) : User::getAnonymousUser(),
);
$rows = array(
array(
Angie Byron
committed
array('data' => $this->t('Type'), 'header' => TRUE),
$this->t($dblog->type),
),
array(
Angie Byron
committed
array('data' => $this->t('Date'), 'header' => TRUE),
$this->dateFormatter->format($dblog->timestamp, 'long'),
),
array(
Angie Byron
committed
array('data' => $this->t('User'), 'header' => TRUE),
array('data' => $username),
),
array(
Angie Byron
committed
array('data' => $this->t('Location'), 'header' => TRUE),
$this->l($dblog->location, $dblog->location ? Url::fromUri($dblog->location) : Url::fromRoute('<none>')),
),
array(
Angie Byron
committed
array('data' => $this->t('Referrer'), 'header' => TRUE),
$this->l($dblog->referer, $dblog->referer ? Url::fromUri($dblog->referer) : Url::fromRoute('<none>')),
),
array(
Angie Byron
committed
array('data' => $this->t('Message'), 'header' => TRUE),
$message,
),
array(
Angie Byron
committed
array('data' => $this->t('Severity'), 'header' => TRUE),
$severity[$dblog->severity],
),
array(
Angie Byron
committed
array('data' => $this->t('Hostname'), 'header' => TRUE),
$dblog->hostname,
),
array(
Angie Byron
committed
array('data' => $this->t('Operations'), 'header' => TRUE),
array('data' => array('#markup' => $dblog->link)),
),
);
$build['dblog_table'] = array(
Angie Byron
committed
'#type' => 'table',
'#rows' => $rows,
'#attributes' => array('class' => array('dblog-event')),
'#attached' => array(
'library' => array('dblog/drupal.dblog'),
),
);
}
return $build;
}
Alex Pott
committed
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
/**
* Builds a query for database log administration filters based on session.
*
* @return array
* An associative array with keys 'where' and 'args'.
*/
protected function buildFilterQuery() {
if (empty($_SESSION['dblog_overview_filter'])) {
return;
}
$this->moduleHandler->loadInclude('dblog', 'admin.inc');
$filters = dblog_filters();
// Build query.
$where = $args = array();
foreach ($_SESSION['dblog_overview_filter'] as $key => $filter) {
$filter_where = array();
foreach ($filter as $value) {
$filter_where[] = $filters[$key]['where'];
$args[] = $value;
}
if (!empty($filter_where)) {
$where[] = '(' . implode(' OR ', $filter_where) . ')';
}
}
$where = !empty($where) ? implode(' AND ', $where) : '';
return array(
'where' => $where,
'args' => $args,
);
}
/**
* Formats a database log message.
*
Alex Pott
committed
* @param object $row
* The record from the watchdog table. The object properties are: wid, uid,
* severity, type, timestamp, message, variables, link, name.
*
* @return string|false
* The formatted log message or FALSE if the message or variables properties
* are not set.
*/
public function formatMessage($row) {
// Check for required properties.
if (isset($row->message) && isset($row->variables)) {
// Messages without variables or user specified text.
if ($row->variables === 'N;') {
$message = $row->message;
}
// Message to translate with injected variables.
else {
$message = $this->t($row->message, unserialize($row->variables));
}
}
else {
$message = FALSE;
}
return $message;
}
Angie Byron
committed
/**
Angie Byron
committed
* Shows the most frequent log messages of a given event type.
*
* Messages are not truncated on this page because events detailed herein do
* not have links to a detailed view.
*
* Use one of the above *Report() methods.
*
* @param string $type
* Type of database log events to display (e.g., 'search').
*
* @return array
* A build array in the format expected by drupal_render().
Angie Byron
committed
*/
Angie Byron
committed
public function topLogMessages($type) {
$header = array(
array('data' => $this->t('Count'), 'field' => 'count', 'sort' => 'desc'),
array('data' => $this->t('Message'), 'field' => 'message'),
);
Angie Byron
committed
Angie Byron
committed
$count_query = $this->database->select('watchdog');
$count_query->addExpression('COUNT(DISTINCT(message))');
$count_query->condition('type', $type);
Angie Byron
committed
Angie Byron
committed
$query = $this->database->select('watchdog', 'w')
->extend('\Drupal\Core\Database\Query\PagerSelectExtender')
->extend('\Drupal\Core\Database\Query\TableSortExtender');
$query->addExpression('COUNT(wid)', 'count');
$query = $query
->fields('w', array('message', 'variables'))
->condition('w.type', $type)
->groupBy('message')
->groupBy('variables')
->limit(30)
->orderByHeader($header);
$query->setCountQuery($count_query);
$result = $query->execute();
$rows = array();
foreach ($result as $dblog) {
if ($message = $this->formatMessage($dblog)) {
$rows[] = array($dblog->count, $message);
Angie Byron
committed
}
}
$build['dblog_top_table'] = array(
Angie Byron
committed
'#type' => 'table',
Angie Byron
committed
'#header' => $header,
'#rows' => $rows,
'#empty' => $this->t('No log messages available.'),
'#attached' => array(
'library' => array('dblog/drupal.dblog'),
),
Angie Byron
committed
);
$build['dblog_top_pager'] = array('#type' => 'pager');
Angie Byron
committed
return $build;
Angie Byron
committed
}