2015-08-17 17:00:26 -07:00
< ? php
namespace Drupal\dblog\Controller ;
use Drupal\Component\Utility\Html ;
use Drupal\Component\Utility\Unicode ;
2016-06-02 15:56:09 -07:00
use Drupal\Component\Utility\Xss ;
2015-08-17 17:00:26 -07:00
use Drupal\Core\Controller\ControllerBase ;
use Drupal\Core\Database\Connection ;
2015-10-08 11:40:12 -07:00
use Drupal\Core\Datetime\DateFormatterInterface ;
2015-08-17 17:00:26 -07:00
use Drupal\Core\Extension\ModuleHandlerInterface ;
use Drupal\Core\Form\FormBuilderInterface ;
use Drupal\Core\Logger\RfcLogLevel ;
use Drupal\Core\Url ;
use Drupal\user\Entity\User ;
use Symfony\Component\DependencyInjection\ContainerInterface ;
/**
* Returns responses for dblog routes .
*/
class DbLogController extends ControllerBase {
/**
* 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 .
*
2015-10-08 11:40:12 -07:00
* @ var \Drupal\Core\Datetime\DateFormatterInterface
2015-08-17 17:00:26 -07:00
*/
protected $dateFormatter ;
/**
* The form builder service .
*
* @ var \Drupal\Core\Form\FormBuilderInterface
*/
protected $formBuilder ;
/**
* The user storage .
*
* @ var \Drupal\user\UserStorageInterface
*/
protected $userStorage ;
/**
* { @ inheritdoc }
*/
public static function create ( ContainerInterface $container ) {
return new static (
$container -> get ( 'database' ),
$container -> get ( 'module_handler' ),
$container -> get ( 'date.formatter' ),
$container -> get ( 'form_builder' )
);
}
/**
* Constructs a DbLogController object .
*
* @ param \Drupal\Core\Database\Connection $database
* A database connection .
* @ param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* A module handler .
2015-10-08 11:40:12 -07:00
* @ param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
2015-08-17 17:00:26 -07:00
* The date formatter service .
* @ param \Drupal\Core\Form\FormBuilderInterface $form_builder
* The form builder service .
*/
2015-10-08 11:40:12 -07:00
public function __construct ( Connection $database , ModuleHandlerInterface $module_handler , DateFormatterInterface $date_formatter , FormBuilderInterface $form_builder ) {
2015-08-17 17:00:26 -07:00
$this -> database = $database ;
$this -> moduleHandler = $module_handler ;
$this -> dateFormatter = $date_formatter ;
$this -> formBuilder = $form_builder ;
$this -> userStorage = $this -> entityManager () -> getStorage ( 'user' );
}
/**
* 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' ,
);
}
/**
* 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' );
$header = array (
// Icon column.
'' ,
array (
'data' => $this -> t ( 'Type' ),
'field' => 'w.type' ,
'class' => array ( RESPONSIVE_PRIORITY_MEDIUM )),
array (
'data' => $this -> t ( 'Date' ),
'field' => 'w.wid' ,
'sort' => 'desc' ,
'class' => array ( RESPONSIVE_PRIORITY_LOW )),
$this -> t ( 'Message' ),
array (
'data' => $this -> t ( 'User' ),
'field' => 'ufd.name' ,
'class' => array ( RESPONSIVE_PRIORITY_MEDIUM )),
array (
'data' => $this -> t ( 'Operations' ),
'class' => array ( RESPONSIVE_PRIORITY_LOW )),
);
$query = $this -> database -> select ( 'watchdog' , 'w' )
-> extend ( '\Drupal\Core\Database\Query\PagerSelectExtender' )
-> extend ( '\Drupal\Core\Database\Query\TableSortExtender' );
$query -> fields ( 'w' , array (
'wid' ,
'uid' ,
'severity' ,
'type' ,
'timestamp' ,
'message' ,
'variables' ,
'link' ,
));
$query -> leftJoin ( 'users_field_data' , 'ufd' , 'w.uid = ufd.uid' );
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 )) {
2015-09-04 13:20:09 -07:00
$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.
2015-08-17 17:00:26 -07:00
$message = $this -> l ( $log_text , new Url ( 'dblog.event' , array ( 'event_id' => $dblog -> wid ), array (
'attributes' => array (
2015-09-04 13:20:09 -07:00
// 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 ,
2015-08-17 17:00:26 -07:00
),
)));
}
$username = array (
'#theme' => 'username' ,
'#account' => $this -> userStorage -> load ( $dblog -> uid ),
);
$rows [] = array (
'data' => array (
// Cells.
array ( 'class' => array ( 'icon' )),
$this -> t ( $dblog -> type ),
$this -> dateFormatter -> format ( $dblog -> timestamp , 'short' ),
$message ,
array ( 'data' => $username ),
2015-09-04 13:20:09 -07:00
array ( 'data' => array ( '#markup' => $dblog -> link )),
2015-08-17 17:00:26 -07:00
),
// Attributes for table row.
'class' => array ( Html :: getClass ( 'dblog-' . $dblog -> type ), $classes [ $dblog -> severity ]),
);
}
$build [ 'dblog_table' ] = array (
'#type' => 'table' ,
'#header' => $header ,
'#rows' => $rows ,
'#attributes' => array ( 'id' => 'admin-dblog' , 'class' => array ( 'admin-dblog' )),
'#empty' => $this -> t ( 'No log messages available.' ),
'#attached' => array (
'library' => array ( 'dblog/drupal.dblog' ),
),
);
$build [ 'dblog_pager' ] = array ( '#type' => 'pager' );
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 (
array ( 'data' => $this -> t ( 'Type' ), 'header' => TRUE ),
$this -> t ( $dblog -> type ),
),
array (
array ( 'data' => $this -> t ( 'Date' ), 'header' => TRUE ),
$this -> dateFormatter -> format ( $dblog -> timestamp , 'long' ),
),
array (
array ( 'data' => $this -> t ( 'User' ), 'header' => TRUE ),
array ( 'data' => $username ),
),
array (
array ( 'data' => $this -> t ( 'Location' ), 'header' => TRUE ),
$this -> l ( $dblog -> location , $dblog -> location ? Url :: fromUri ( $dblog -> location ) : Url :: fromRoute ( '<none>' )),
),
array (
array ( 'data' => $this -> t ( 'Referrer' ), 'header' => TRUE ),
$this -> l ( $dblog -> referer , $dblog -> referer ? Url :: fromUri ( $dblog -> referer ) : Url :: fromRoute ( '<none>' )),
),
array (
array ( 'data' => $this -> t ( 'Message' ), 'header' => TRUE ),
$message ,
),
array (
array ( 'data' => $this -> t ( 'Severity' ), 'header' => TRUE ),
$severity [ $dblog -> severity ],
),
array (
array ( 'data' => $this -> t ( 'Hostname' ), 'header' => TRUE ),
2015-09-04 13:20:09 -07:00
$dblog -> hostname ,
2015-08-17 17:00:26 -07:00
),
array (
array ( 'data' => $this -> t ( 'Operations' ), 'header' => TRUE ),
2015-08-27 12:03:05 -07:00
array ( 'data' => array ( '#markup' => $dblog -> link )),
2015-08-17 17:00:26 -07:00
),
);
$build [ 'dblog_table' ] = array (
'#type' => 'table' ,
'#rows' => $rows ,
'#attributes' => array ( 'class' => array ( 'dblog-event' )),
'#attached' => array (
'library' => array ( 'dblog/drupal.dblog' ),
),
);
}
return $build ;
}
/**
* 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 .
*
* @ param object $row
* The record from the watchdog table . The object properties are : wid , uid ,
* severity , type , timestamp , message , variables , link , name .
*
2016-06-02 15:56:09 -07:00
* @ return string | \Drupal\Core\StringTranslation\TranslatableMarkup | false
2015-08-17 17:00:26 -07:00
* The formatted log message or FALSE if the message or variables properties
* are not set .
*/
public function formatMessage ( $row ) {
// Check for required properties.
2016-10-06 15:16:20 -07:00
if ( isset ( $row -> message , $row -> variables )) {
$variables = @ unserialize ( $row -> variables );
2015-08-17 17:00:26 -07:00
// Messages without variables or user specified text.
2016-10-06 15:16:20 -07:00
if ( $variables === NULL ) {
2016-06-02 15:56:09 -07:00
$message = Xss :: filterAdmin ( $row -> message );
2015-08-17 17:00:26 -07:00
}
2016-10-06 15:16:20 -07:00
elseif ( ! is_array ( $variables )) {
$message = $this -> t ( 'Log data is corrupted and cannot be unserialized: @message' , [ '@message' => Xss :: filterAdmin ( $row -> message )]);
}
2015-08-17 17:00:26 -07:00
// Message to translate with injected variables.
else {
2016-10-06 15:16:20 -07:00
$message = $this -> t ( Xss :: filterAdmin ( $row -> message ), $variables );
2015-08-17 17:00:26 -07:00
}
}
else {
$message = FALSE ;
}
return $message ;
}
/**
* 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 () .
*/
public function topLogMessages ( $type ) {
$header = array (
array ( 'data' => $this -> t ( 'Count' ), 'field' => 'count' , 'sort' => 'desc' ),
array ( 'data' => $this -> t ( 'Message' ), 'field' => 'message' ),
);
$count_query = $this -> database -> select ( 'watchdog' );
$count_query -> addExpression ( 'COUNT(DISTINCT(message))' );
$count_query -> condition ( 'type' , $type );
$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 );
}
}
$build [ 'dblog_top_table' ] = array (
'#type' => 'table' ,
'#header' => $header ,
'#rows' => $rows ,
'#empty' => $this -> t ( 'No log messages available.' ),
'#attached' => array (
'library' => array ( 'dblog/drupal.dblog' ),
),
);
$build [ 'dblog_top_pager' ] = array ( '#type' => 'pager' );
return $build ;
}
}