Completed
Push — 2761013-watchdog_pager ( d635f7...2f2a35 )
by Frédéric G.
03:11
created

OverviewController::create()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 9
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 22
rs 9.2
1
<?php
2
3
namespace Drupal\mongodb_watchdog\Controller;
4
5
use Drupal\Component\Utility\SafeMarkup;
6
use Drupal\Component\Utility\Unicode;
7
use Drupal\Core\Controller\ControllerBase;
8
use Drupal\Core\Datetime\DateFormatterInterface;
9
use Drupal\Core\Extension\ModuleHandlerInterface;
10
use Drupal\Core\Form\FormBuilderInterface;
11
use Drupal\Core\Link;
12
use Drupal\Core\Logger\RfcLogLevel;
13
use Drupal\mongodb_watchdog\Event;
14
use Drupal\mongodb_watchdog\EventTemplate;
15
use Drupal\mongodb_watchdog\Form\OverviewFilterForm;
16
use Drupal\mongodb_watchdog\Logger;
17
use Psr\Log\LogLevel;
18
use Psr\Log\LoggerInterface;
19
use Symfony\Component\DependencyInjection\ContainerInterface;
20
use Symfony\Component\HttpFoundation\Request;
21
22
/**
23
 * Class OverviewController provides the main MongoDB Watchdog report page.
24
 */
25
class OverviewController extends ControllerBase {
26
  const EVENT_TYPE_MAP = [
27
    'typeMap' => [
28
      'array' => 'array',
29
      'document' => 'array',
30
      'root' => 'Drupal\mongodb_watchdog\Event',
31
    ],
32
  ];
33
  const SEVERITY_PREFIX = 'mongodb_watchdog__severity_';
34
  const SEVERITY_CLASSES = [
35
    RfcLogLevel::DEBUG => self::SEVERITY_PREFIX . LogLevel::DEBUG,
36
    RfcLogLevel::INFO => self::SEVERITY_PREFIX . LogLevel::INFO,
37
    RfcLogLevel::NOTICE => self::SEVERITY_PREFIX . LogLevel::NOTICE,
38
    RfcLogLevel::WARNING => self::SEVERITY_PREFIX . LogLevel::WARNING,
39
    RfcLogLevel::ERROR => self::SEVERITY_PREFIX . LogLevel::ERROR,
40
    RfcLogLevel::CRITICAL => self::SEVERITY_PREFIX . LogLevel::CRITICAL,
41
    RfcLogLevel::ALERT => self::SEVERITY_PREFIX . LogLevel::ALERT,
42
    RfcLogLevel::EMERGENCY => self::SEVERITY_PREFIX . LogLevel::EMERGENCY,
43
  ];
44
45
  /**
46
   * The core date.formatter service.
47
   *
48
   * @var \Drupal\Core\Datetime\DateFormatterInterface
49
   */
50
  protected $dateFormatter;
51
52
  /**
53
   * The form builder service.
54
   *
55
   * @var \Drupal\Core\Form\FormBuilderInterface
56
   */
57
  protected $formBuilder;
58
59
  /**
60
   * The items_per_page configuration value.
61
   *
62
   * @var int
63
   */
64
  protected $itemsPerPage;
65
66
  /**
67
   * The core logger channel, to log intervening events.
68
   *
69
   * @var \Psr\Log\LoggerInterface
70
   */
71
  protected $logger;
72
73
  /**
74
   * The module handler service.
75
   *
76
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
77
   */
78
  protected $moduleHandler;
79
80
  /**
81
   * The length of the disk path to DRUPAL_ROOT.
82
   *
83
   * @var int
84
   *
85
   * @see \Drupal\mongodb_watchdog\Controller\OverviewController::getEventSource()
86
   */
87
  protected $rootLength;
88
89
  /**
90
   * The MongoDB logger, to load events.
91
   *
92
   * @var \Drupal\mongodb_watchdog\Logger
93
   */
94
  protected $watchdog;
95
96
  /**
97
   * Constructor.
98
   *
99
   * @param \Psr\Log\LoggerInterface $logger
100
   *   The logger service, to log intervening events.
101
   * @param \Drupal\mongodb_watchdog\Logger $watchdog
102
   *   The MongoDB logger, to load stored events.
103
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
104
   *   A module handler.
105
   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
106
   *   The form builder service.
107
   * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
108
   *   The core date_formatter service.
109
   * @param int $items_per_page
110
   *   The items_per_page configuration value.
111
   */
112
  public function __construct(
113
    LoggerInterface $logger,
114
    Logger $watchdog,
115
    ModuleHandlerInterface $module_handler,
116
    FormBuilderInterface $form_builder,
117
    DateFormatterInterface $date_formatter,
118
    $items_per_page) {
119
    $this->dateFormatter = $date_formatter;
120
    $this->formBuilder = $form_builder;
121
    $this->logger = $logger;
122
    $this->moduleHandler = $module_handler;
123
    $this->watchdog = $watchdog;
124
125
    $this->itemsPerPage = $items_per_page;
126
127
    // Add terminal "/".
128
    $this->rootLength = Unicode::strlen(DRUPAL_ROOT);
129
130
  }
131
132
  /**
133
   * {@inheritdoc}
134
   */
135
  public static function create(ContainerInterface $container) {
136
    /** @var \Drupal\Core\Datetime\DateFormatterInterface $date_formatter */
137
    $date_formatter = $container->get('date.formatter');
138
139
    /** @var \Drupal\Core\Form\FormBuilderInterface $form_builder */
140
    $form_builder = $container->get('form_builder');
141
142
    /** @var \Psr\Log\LoggerInterface $logger */
143
    $logger = $container->get('logger.channel.mongodb_watchdog');
144
145
    /** @var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler */
146
    $module_handler = $container->get('module_handler');
147
148
    /** @var \Drupal\mongodb_watchdog\Logger $logger */
149
    $watchdog = $container->get('mongodb.logger');
150
151
    /** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
152
    $config_factory = $container->get('config.factory');
153
    $items_per_page = $config_factory->get('mongodb_watchdog.settings')->get('items_per_page');
154
155
    return new static($logger, $watchdog, $module_handler, $form_builder, $date_formatter, $items_per_page);
156
  }
157
158
  /**
159
   * Build the link to the event or top report for the event template.
160
   *
161
   * @param \Drupal\mongodb_watchdog\EventTemplate $template
162
   *   The event template for which to buildl the link.
163
   *
164
   * @return string
165
   *   An internal link in string form.
166
   */
167
  protected function getEventLink(EventTemplate $template) {
168
    switch ($template->type) {
169
      case 'page not found':
170
        $cell = Link::createFromRoute(t('( Top 404 )'), 'mongodb_watchdog.reports.top404');
171
        break;
172
173
      case 'access denied':
174
        $cell = Link::createFromRoute(t('( Top 403 )'), 'mongodb_watchdog.reports.top403');
175
        break;
176
177
      default:
178
        // Limited-length message.
179
        $message = Unicode::truncate(strip_tags(SafeMarkup::format($template->message, [])), 56, TRUE, TRUE);
180
        $cell = Link::createFromRoute($message, 'mongodb_watchdog.reports.detail', [
181
          'event_template' => $template->_id,
182
        ]);
183
        break;
184
    }
185
186
    return $cell;
187
  }
188
189
  /**
190
   * Get the location in source code where the event was logged.
191
   *
192
   * @param \Drupal\mongodb_watchdog\EventTemplate $template
193
   *   The template for which to find a source location.
194
   *
195
   * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be string|array<string,string|array>?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
196
   *   A render array for the source location, possibly empty or wrong.
197
   */
198
  protected function getEventSource(EventTemplate $template) {
199
    if (in_array($template->type, TopController::TYPES)) {
200
      return '';
201
    }
202
203
    $event_collection = $this->watchdog->eventCollection($template->_id);
204
    $event = $event_collection->findOne([], static::EVENT_TYPE_MAP);
205
    if (!($event instanceof Event)) {
206
      return '';
207
    }
208
209
    $file = $event->variables['%file'] ?? '';
1 ignored issue
show
introduced by
Expected 1 space after "?"; 0 found
Loading history...
210
    if ($file && strncmp($file, DRUPAL_ROOT, $this->rootLength) === 0) {
211
      $hover = Unicode::substr($file, $this->rootLength + 1);
212
      $file = Unicode::truncate(basename($file), 30);
213
    }
214
    else {
215
      $hover = NULL;
216
    }
217
218
    $line = $event->variables['%line'] ?? NULL;
1 ignored issue
show
introduced by
Expected 1 space after "?"; 0 found
Loading history...
219
    $cell = [
220
      '#type' => 'html_tag',
221
      '#tag' => 'span',
222
      '#value' => "${file}#${line}",
223
    ];
224
225
    if ($hover) {
226
      $cell['#attributes'] = [
227
        'class' => 'mongodb_watchdog__code_path',
228
        'title' => $hover,
229
      ];
230
    }
231
232
    return $cell;
233
  }
234
235
  /**
236
   * Controller for mongodb_watchdog.overview.
237
   *
238
   * @return array
239
   *   A render array.
240
   */
241
  public function overview(Request $request) {
242
    $page = $this->setupPager($request);
243
244
    $ret = [
245
      'filter_form' => $this->formBuilder->getForm('Drupal\mongodb_watchdog\Form\OverviewFilterForm'),
246
      'rows' => $this->overviewRows($page),
247
      'pager' => [
248
        '#type' => 'pager',
249
      ],
250
      '#attached' => [
251
        'library' => ['mongodb_watchdog/styling'],
252
      ],
253
    ];
254
255
    return $ret;
256
  }
257
258
  /**
259
   * Build a table from the event rows.
260
   *
261
   * @param int $page
262
   *   The number of the page to display.
263
   *
264
   * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string|array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
265
   *   A render array.
266
   */
267
  public function overviewRows($page) {
268
    $header = [
269
      t('#'),
270
      t('Latest'),
271
      t('Severity'),
272
      t('Type'),
273
      t('Message'),
274
      t('Source'),
275
    ];
276
    $rows = [];
277
    $levels = RfcLogLevel::getLevels();
278
    $filters = $_SESSION[OverviewFilterForm::SESSION_KEY] ?? NULL;
1 ignored issue
show
introduced by
Expected 1 space after "?"; 0 found
Loading history...
279
    $skip = $page * $this->itemsPerPage;
280
    $limit = $this->itemsPerPage;
281
    $cursor = $this->watchdog->templates($filters['type'] ?? [], $filters['severity'] ?? [], $skip, $limit);
1 ignored issue
show
introduced by
Expected 1 space after "?"; 0 found
Loading history...
282
283
    /** @var \Drupal\mongodb_watchdog\EventTemplate $template */
284
    foreach ($cursor as $template) {
285
      $row = [];
286
      $row[] = $template->count;
287
      $row[] = $this->dateFormatter->format($template->changed, 'short');
288
      $row[] = [
289
        'class' => static::SEVERITY_CLASSES[$template->severity],
290
        'data' => $levels[$template->severity],
291
      ];
292
      $row[] = $template->type;
293
      $row[] = $this->getEventLink($template);
294
      $row[] = [
295
        'data' => $this->getEventSource($template, $row),
296
      ];
297
298
      $rows[] = $row;
299
    }
300
301
    return [
302
      '#type' => 'table',
303
      '#header' => $header,
304
      '#rows' => $rows,
305
    ];
306
  }
307
308
  /**
309
   * Set up the templates pager.
310
   *
311
   * @param \Symfony\Component\HttpFoundation\Request $request
312
   *   The current request.
313
   *
314
   * @return int
315
   *   The number of the page to display, starting at 0.
316
   */
317 View Code Duplication
  public function setupPager(Request $request) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
318
    $count = $this->watchdog->templatesCount();
319
    $height = $this->itemsPerPage;
320
    pager_default_initialize($count, $height);
321
322
    $page = intval($request->query->get('page'));
323
    if ($page < 0) {
324
      $page = 0;
325
    }
326
    else {
327
      $page_max = intval(min(ceil($count / $height), PHP_INT_MAX) - 1);
328
      if ($page > $page_max) {
329
        $page = $page_max;
330
      }
331
    }
332
333
    return $page;
334
  }
335
336
}
0 ignored issues
show
Coding Style introduced by
As per coding style, files should not end with a newline character.

This check marks files that end in a newline character, i.e. an empy line.

Loading history...
337