DbLogController.php 11.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
<?php

/**
 * @file
 * Contains \Drupal\dblog\Controller\DbLogController.
 */

namespace Drupal\dblog\Controller;

use Drupal\Component\Utility\Unicode;
11
12
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Xss;
13
use Drupal\Core\Controller\ControllerBase;
14
use Drupal\Core\Database\Connection;
15
use Drupal\Core\Datetime\DateFormatter;
16
use Drupal\Core\Extension\ModuleHandlerInterface;
17
use Drupal\Core\Form\FormBuilderInterface;
18
19
20
21
22
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Returns responses for dblog routes.
 */
23
class DbLogController extends ControllerBase {
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

  /**
   * The database service.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $database;

  /**
   * The module handler service.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

39
  /**
40
   * The date formatter service.
41
   *
42
   * @var \Drupal\Core\Datetime\DateFormatter
43
   */
44
  protected $dateFormatter;
45

46
  /**
47
48
49
   * The form builder service.
   *
   * @var \Drupal\Core\Form\FormBuilderInterface
50
   */
51
  protected $formBuilder;
52

53
54
55
56
57
58
  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('database'),
59
      $container->get('module_handler'),
60
      $container->get('date.formatter'),
61
      $container->get('form_builder')
62
63
64
65
66
67
    );
  }

  /**
   * Constructs a DbLogController object.
   *
68
   * @param \Drupal\Core\Database\Connection $database
69
   *   A database connection.
70
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
71
   *   A module handler.
72
   * @param \Drupal\Core\Datetime\DateFormatter $date_formatter
73
   *   The date formatter service.
74
75
   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
   *   The form builder service.
76
   */
77
  public function __construct(Connection $database, ModuleHandlerInterface $module_handler, DateFormatter $date_formatter, FormBuilderInterface $form_builder) {
78
79
    $this->database = $database;
    $this->moduleHandler = $module_handler;
80
    $this->dateFormatter = $date_formatter;
81
    $this->formBuilder = $form_builder;
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  }

  /**
   * Gets an array of log level classes.
   *
   * @return array
   *   An array of log level classes.
   */
  public static function getLogLevelClassMap() {
    return array(
      WATCHDOG_DEBUG => 'dblog-debug',
      WATCHDOG_INFO => 'dblog-info',
      WATCHDOG_NOTICE => 'dblog-notice',
      WATCHDOG_WARNING => 'dblog-warning',
      WATCHDOG_ERROR => 'dblog-error',
      WATCHDOG_CRITICAL => 'dblog-critical',
      WATCHDOG_ALERT => 'dblog-alert',
      WATCHDOG_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');

124
125
    $build['dblog_filter_form'] = $this->formBuilder->getForm('Drupal\dblog\Form\DblogFilterForm');
    $build['dblog_clear_log_form'] = $this->formBuilder->getForm('Drupal\dblog\Form\DblogClearLogForm');
126
127
128
129
130

    $header = array(
      // Icon column.
      '',
      array(
131
        'data' => $this->t('Type'),
132
133
134
        'field' => 'w.type',
        'class' => array(RESPONSIVE_PRIORITY_MEDIUM)),
      array(
135
        'data' => $this->t('Date'),
136
137
138
        'field' => 'w.wid',
        'sort' => 'desc',
        'class' => array(RESPONSIVE_PRIORITY_LOW)),
139
      $this->t('Message'),
140
      array(
141
        'data' => $this->t('User'),
142
143
144
        'field' => 'u.name',
        'class' => array(RESPONSIVE_PRIORITY_MEDIUM)),
      array(
145
        'data' => $this->t('Operations'),
146
147
148
149
        'class' => array(RESPONSIVE_PRIORITY_LOW)),
    );

    $query = $this->database->select('watchdog', 'w')
150
151
      ->extend('\Drupal\Core\Database\Query\PagerSelectExtender')
      ->extend('\Drupal\Core\Database\Query\TableSortExtender');
152
153
154
155
156
157
158
159
160
    $query->fields('w', array(
      'wid',
      'uid',
      'severity',
      'type',
      'timestamp',
      'message',
      'variables',
      'link',
161
    ));
162
163
164
165
166
167
168
169
170
171

    if (!empty($filter['where'])) {
      $query->where($filter['where'], $filter['args']);
    }
    $result = $query
      ->limit(50)
      ->orderByHeader($header)
      ->execute();

    foreach ($result as $dblog) {
172
173
174
175
      $message = $this->formatMessage($dblog);
      if ($message && isset($dblog->wid)) {
        // Truncate link_text to 56 chars of message.
        $log_text = Unicode::truncate(Xss::filter($message, array()), 56, TRUE, TRUE);
176
177
178
179
180
181
182
        $message = $this->l($log_text, 'dblog.event',  array('event_id' => $dblog->wid), array(
          'attributes' => array(
            // Provide a title for the link for useful hover hints.
            'title' => Unicode::truncate(strip_tags($message), 256, TRUE, TRUE),
          ),
          'html' => TRUE,
        ));
183
      }
184
185
      $username = array(
        '#theme' => 'username',
186
        '#account' => user_load($dblog->uid),
187
      );
188
189
190
191
      $rows[] = array(
        'data' => array(
          // Cells.
          array('class' => array('icon')),
192
          $this->t($dblog->type),
193
          $this->dateFormatter->format($dblog->timestamp, 'short'),
194
          $message,
195
          array('data' => $username),
196
          Xss::filter($dblog->link),
197
198
199
200
201
202
203
        ),
        // Attributes for table row.
        'class' => array(drupal_html_class('dblog-' . $dblog->type), $classes[$dblog->severity]),
      );
    }

    $build['dblog_table'] = array(
204
      '#type' => 'table',
205
206
207
      '#header' => $header,
      '#rows' => $rows,
      '#attributes' => array('id' => 'admin-dblog', 'class' => array('admin-dblog')),
208
      '#empty' => $this->t('No log messages available.'),
209
210
211
      '#attached' => array(
        'library' => array('dblog/drupal.dblog'),
      ),
212
213
214
215
216
217
218
    );
    $build['dblog_pager'] = array('#theme' => 'pager');

    return $build;

  }

219
220
221
222
223
224
225
226
227
228
229
230
231
  /**
   * 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();
232
    if ($dblog = $this->database->query('SELECT w.*, u.name, u.uid FROM {watchdog} w INNER JOIN {users_field_data} u ON w.uid = u.uid WHERE w.wid = :id AND u.default_langcode = 1', array(':id' => $event_id))->fetchObject()) {
233
      $severity = watchdog_severity_levels();
234
      $message = $this->formatMessage($dblog);
235
236
237
238
239
240
      $username = array(
        '#theme' => 'username',
        '#account' => user_load($dblog->uid),
      );
      $rows = array(
        array(
241
242
          array('data' => $this->t('Type'), 'header' => TRUE),
          $this->t($dblog->type),
243
244
        ),
        array(
245
          array('data' => $this->t('Date'), 'header' => TRUE),
246
          $this->dateFormatter->format($dblog->timestamp, 'long'),
247
248
        ),
        array(
249
          array('data' => $this->t('User'), 'header' => TRUE),
250
251
252
          array('data' => $username),
        ),
        array(
253
          array('data' => $this->t('Location'), 'header' => TRUE),
254
255
256
          l($dblog->location, $dblog->location),
        ),
        array(
257
          array('data' => $this->t('Referrer'), 'header' => TRUE),
258
259
260
          l($dblog->referer, $dblog->referer),
        ),
        array(
261
          array('data' => $this->t('Message'), 'header' => TRUE),
262
263
264
          $message,
        ),
        array(
265
          array('data' => $this->t('Severity'), 'header' => TRUE),
266
267
268
          $severity[$dblog->severity],
        ),
        array(
269
          array('data' => $this->t('Hostname'), 'header' => TRUE),
270
271
272
          String::checkPlain($dblog->hostname),
        ),
        array(
273
          array('data' => $this->t('Operations'), 'header' => TRUE),
274
275
276
277
          $dblog->link,
        ),
      );
      $build['dblog_table'] = array(
278
        '#type' => 'table',
279
280
        '#rows' => $rows,
        '#attributes' => array('class' => array('dblog-event')),
281
282
283
        '#attached' => array(
          'library' => array('dblog/drupal.dblog'),
        ),
284
285
286
287
288
289
      );
    }

    return $build;
  }

290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
  /**
   * 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,
    );
  }

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
  /**
   * Formats a database log message.
   *
   * @param stdClass $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;
  }

354
  /**
355
356
357
358
359
360
361
362
363
364
365
366
   * 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().
367
   */
368
369
370
371
372
  public function topLogMessages($type) {
    $header = array(
      array('data' => $this->t('Count'), 'field' => 'count', 'sort' => 'desc'),
      array('data' => $this->t('Message'), 'field' => 'message'),
    );
373

374
375
376
    $count_query = $this->database->select('watchdog');
    $count_query->addExpression('COUNT(DISTINCT(message))');
    $count_query->condition('type', $type);
377

378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
    $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) {
394
395
      if ($message = $this->formatMessage($dblog)) {
        $rows[] = array($dblog->count, $message);
396
397
398
399
      }
    }

    $build['dblog_top_table']  = array(
400
      '#type' => 'table',
401
402
403
      '#header' => $header,
      '#rows' => $rows,
      '#empty' => $this->t('No log messages available.'),
404
405
406
      '#attached' => array(
        'library' => array('dblog/drupal.dblog'),
      ),
407
408
409
410
    );
    $build['dblog_top_pager'] = array('#theme' => 'pager');

    return $build;
411
412
  }

413
}