Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/diffusion/controller/DiffusionLintController.php
12242 views
1
<?php
2
3
final class DiffusionLintController extends DiffusionController {
4
5
public function shouldAllowPublic() {
6
return true;
7
}
8
9
public function handleRequest(AphrontRequest $request) {
10
$viewer = $this->getViewer();
11
12
if ($this->getRepositoryIdentifierFromRequest($request)) {
13
$response = $this->loadDiffusionContext();
14
if ($response) {
15
return $response;
16
}
17
18
$drequest = $this->getDiffusionRequest();
19
} else {
20
$drequest = null;
21
}
22
23
$code = $request->getStr('lint');
24
if (strlen($code)) {
25
return $this->buildDetailsResponse();
26
}
27
28
$owners = array();
29
if (!$drequest) {
30
if (!$request->getArr('owner')) {
31
$owners = array($viewer->getPHID());
32
} else {
33
$owners = array(head($request->getArr('owner')));
34
}
35
}
36
37
$codes = $this->loadLintCodes($drequest, $owners);
38
39
if ($codes) {
40
$branches = id(new PhabricatorRepositoryBranch())->loadAllWhere(
41
'id IN (%Ld)',
42
array_unique(ipull($codes, 'branchID')));
43
$branches = mpull($branches, null, 'getID');
44
} else {
45
$branches = array();
46
}
47
48
if ($branches) {
49
$repositories = id(new PhabricatorRepositoryQuery())
50
->setViewer($viewer)
51
->withIDs(mpull($branches, 'getRepositoryID'))
52
->execute();
53
$repositories = mpull($repositories, null, 'getID');
54
} else {
55
$repositories = array();
56
}
57
58
59
$rows = array();
60
$total = 0;
61
foreach ($codes as $code) {
62
$branch = idx($branches, $code['branchID']);
63
if (!$branch) {
64
continue;
65
}
66
67
$repository = idx($repositories, $branch->getRepositoryID());
68
if (!$repository) {
69
continue;
70
}
71
72
$total += $code['n'];
73
74
if ($drequest) {
75
$href_lint = $drequest->generateURI(
76
array(
77
'action' => 'lint',
78
'lint' => $code['code'],
79
));
80
81
$href_browse = $drequest->generateURI(
82
array(
83
'action' => 'browse',
84
'lint' => $code['code'],
85
));
86
87
$href_repo = $drequest->generateURI(
88
array(
89
'action' => 'lint',
90
));
91
} else {
92
$href_lint = $repository->generateURI(
93
array(
94
'action' => 'lint',
95
'lint' => $code['code'],
96
));
97
98
$href_browse = $repository->generateURI(
99
array(
100
'action' => 'browse',
101
'lint' => $code['code'],
102
));
103
104
$href_repo = $repository->generateURI(
105
array(
106
'action' => 'lint',
107
));
108
}
109
110
$rows[] = array(
111
phutil_tag('a', array('href' => $href_lint), $code['n']),
112
phutil_tag('a', array('href' => $href_browse), $code['files']),
113
phutil_tag(
114
'a',
115
array(
116
'href' => $href_repo,
117
),
118
$repository->getDisplayName()),
119
ArcanistLintSeverity::getStringForSeverity($code['maxSeverity']),
120
$code['code'],
121
$code['maxName'],
122
$code['maxDescription'],
123
);
124
}
125
126
$table = id(new AphrontTableView($rows))
127
->setHeaders(array(
128
pht('Problems'),
129
pht('Files'),
130
pht('Repository'),
131
pht('Severity'),
132
pht('Code'),
133
pht('Name'),
134
pht('Example'),
135
))
136
->setColumnVisibility(array(true, true, !$drequest))
137
->setColumnClasses(array('n', 'n', '', '', 'pri', '', ''));
138
139
$content = array();
140
141
if (!$drequest) {
142
$form = id(new AphrontFormView())
143
->setUser($viewer)
144
->setMethod('GET')
145
->appendControl(
146
id(new AphrontFormTokenizerControl())
147
->setDatasource(new PhabricatorPeopleDatasource())
148
->setLimit(1)
149
->setName('owner')
150
->setLabel(pht('Owner'))
151
->setValue($owners))
152
->appendChild(
153
id(new AphrontFormSubmitControl())
154
->setValue(pht('Filter')));
155
$content[] = id(new AphrontListFilterView())->appendChild($form);
156
}
157
158
$content[] = id(new PHUIObjectBoxView())
159
->setHeaderText(pht('Lint'))
160
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
161
->setTable($table);
162
163
$title = array('Lint');
164
$crumbs = $this->buildCrumbs(
165
array(
166
'branch' => true,
167
'path' => true,
168
'view' => 'lint',
169
));
170
$crumbs->setBorder(true);
171
172
if ($drequest) {
173
$title[] = $drequest->getRepository()->getDisplayName();
174
} else {
175
$crumbs->addTextCrumb(pht('All Lint'));
176
}
177
178
if ($drequest) {
179
$branch = $drequest->loadBranch();
180
181
$header = id(new PHUIHeaderView())
182
->setHeader(pht('Lint: %s', $this->renderPathLinks($drequest, 'lint')))
183
->setUser($viewer)
184
->setHeaderIcon('fa-code');
185
$actions = $this->buildActionView($drequest);
186
$properties = $this->buildPropertyView(
187
$drequest,
188
$branch,
189
$total,
190
$actions);
191
192
$object_box = id(new PHUIObjectBoxView())
193
->setHeader($header)
194
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
195
->addPropertyList($properties);
196
} else {
197
$object_box = null;
198
$header = id(new PHUIHeaderView())
199
->setHeader(pht('All Lint'))
200
->setHeaderIcon('fa-code');
201
}
202
203
$view = id(new PHUITwoColumnView())
204
->setHeader($header)
205
->setFooter(array(
206
$object_box,
207
$content,
208
));
209
210
return $this->newPage()
211
->setTitle($title)
212
->setCrumbs($crumbs)
213
->appendChild(
214
array(
215
$view,
216
));
217
}
218
219
private function loadLintCodes($drequest, array $owner_phids) {
220
$conn = id(new PhabricatorRepository())->establishConnection('r');
221
$where = array('1 = 1');
222
223
if ($drequest) {
224
$branch = $drequest->loadBranch();
225
if (!$branch) {
226
return array();
227
}
228
229
$where[] = qsprintf($conn, 'branchID = %d', $branch->getID());
230
231
if ($drequest->getPath() != '') {
232
$path = '/'.$drequest->getPath();
233
$is_dir = (substr($path, -1) == '/');
234
$where[] = ($is_dir
235
? qsprintf($conn, 'path LIKE %>', $path)
236
: qsprintf($conn, 'path = %s', $path));
237
}
238
}
239
240
if ($owner_phids) {
241
$or = array();
242
$or[] = qsprintf($conn, 'authorPHID IN (%Ls)', $owner_phids);
243
244
$paths = array();
245
$packages = id(new PhabricatorOwnersOwner())
246
->loadAllWhere('userPHID IN (%Ls)', $owner_phids);
247
if ($packages) {
248
$paths = id(new PhabricatorOwnersPath())->loadAllWhere(
249
'packageID IN (%Ld)',
250
mpull($packages, 'getPackageID'));
251
}
252
253
if ($paths) {
254
$repositories = id(new PhabricatorRepositoryQuery())
255
->setViewer($this->getRequest()->getUser())
256
->withPHIDs(mpull($paths, 'getRepositoryPHID'))
257
->execute();
258
$repositories = mpull($repositories, 'getID', 'getPHID');
259
260
$branches = id(new PhabricatorRepositoryBranch())->loadAllWhere(
261
'repositoryID IN (%Ld)',
262
$repositories);
263
$branches = mgroup($branches, 'getRepositoryID');
264
}
265
266
foreach ($paths as $path) {
267
$branch = idx(
268
$branches,
269
idx(
270
$repositories,
271
$path->getRepositoryPHID()));
272
if ($branch) {
273
$condition = qsprintf(
274
$conn,
275
'(branchID IN (%Ld) AND path LIKE %>)',
276
array_keys($branch),
277
$path->getPath());
278
if ($path->getExcluded()) {
279
$where[] = qsprintf($conn, 'NOT %Q', $condition);
280
} else {
281
$or[] = $condition;
282
}
283
}
284
}
285
$where[] = qsprintf($conn, '%LO', $or);
286
}
287
288
return queryfx_all(
289
$conn,
290
'SELECT
291
branchID,
292
code,
293
MAX(severity) AS maxSeverity,
294
MAX(name) AS maxName,
295
MAX(description) AS maxDescription,
296
COUNT(DISTINCT path) AS files,
297
COUNT(*) AS n
298
FROM %T
299
WHERE %LA
300
GROUP BY branchID, code
301
ORDER BY n DESC',
302
PhabricatorRepository::TABLE_LINTMESSAGE,
303
$where);
304
}
305
306
protected function buildActionView(DiffusionRequest $drequest) {
307
$viewer = $this->getRequest()->getUser();
308
309
$view = id(new PhabricatorActionListView())
310
->setUser($viewer);
311
312
$list_uri = $drequest->generateURI(
313
array(
314
'action' => 'lint',
315
'lint' => '',
316
));
317
318
$view->addAction(
319
id(new PhabricatorActionView())
320
->setName(pht('View As List'))
321
->setHref($list_uri)
322
->setIcon('fa-list'));
323
324
$history_uri = $drequest->generateURI(
325
array(
326
'action' => 'history',
327
));
328
329
$view->addAction(
330
id(new PhabricatorActionView())
331
->setName(pht('View History'))
332
->setHref($history_uri)
333
->setIcon('fa-clock-o'));
334
335
$browse_uri = $drequest->generateURI(
336
array(
337
'action' => 'browse',
338
));
339
340
$view->addAction(
341
id(new PhabricatorActionView())
342
->setName(pht('Browse Content'))
343
->setHref($browse_uri)
344
->setIcon('fa-files-o'));
345
346
return $view;
347
}
348
349
protected function buildPropertyView(
350
DiffusionRequest $drequest,
351
PhabricatorRepositoryBranch $branch,
352
$total,
353
PhabricatorActionListView $actions) {
354
355
$viewer = $this->getRequest()->getUser();
356
357
$view = id(new PHUIPropertyListView())
358
->setUser($viewer)
359
->setActionList($actions);
360
361
$lint_commit = $branch->getLintCommit();
362
363
$view->addProperty(
364
pht('Lint Commit'),
365
phutil_tag(
366
'a',
367
array(
368
'href' => $drequest->generateURI(
369
array(
370
'action' => 'commit',
371
'commit' => $lint_commit,
372
)),
373
),
374
$drequest->getRepository()->formatCommitName($lint_commit)));
375
376
$view->addProperty(
377
pht('Total Messages'),
378
pht('%s', new PhutilNumber($total)));
379
380
return $view;
381
}
382
383
384
private function buildDetailsResponse() {
385
$request = $this->getRequest();
386
387
$limit = 500;
388
389
$pager = id(new PHUIPagerView())
390
->readFromRequest($request)
391
->setPageSize($limit);
392
393
$offset = $pager->getOffset();
394
395
$drequest = $this->getDiffusionRequest();
396
$branch = $drequest->loadBranch();
397
$messages = $this->loadLintMessages($branch, $limit, $offset);
398
$is_dir = (substr('/'.$drequest->getPath(), -1) == '/');
399
400
$pager->setHasMorePages(count($messages) >= $limit);
401
402
$authors = $this->loadViewerHandles(ipull($messages, 'authorPHID'));
403
404
$rows = array();
405
foreach ($messages as $message) {
406
$path = phutil_tag(
407
'a',
408
array(
409
'href' => $drequest->generateURI(array(
410
'action' => 'lint',
411
'path' => $message['path'],
412
)),
413
),
414
substr($message['path'], strlen($drequest->getPath()) + 1));
415
416
$line = phutil_tag(
417
'a',
418
array(
419
'href' => $drequest->generateURI(array(
420
'action' => 'browse',
421
'path' => $message['path'],
422
'line' => $message['line'],
423
'commit' => $branch->getLintCommit(),
424
)),
425
),
426
$message['line']);
427
428
$author = $message['authorPHID'];
429
if ($author && $authors[$author]) {
430
$author = $authors[$author]->renderLink();
431
}
432
433
$rows[] = array(
434
$path,
435
$line,
436
$author,
437
ArcanistLintSeverity::getStringForSeverity($message['severity']),
438
$message['name'],
439
$message['description'],
440
);
441
}
442
443
$table = id(new AphrontTableView($rows))
444
->setHeaders(array(
445
pht('Path'),
446
pht('Line'),
447
pht('Author'),
448
pht('Severity'),
449
pht('Name'),
450
pht('Description'),
451
))
452
->setColumnClasses(array('', 'n'))
453
->setColumnVisibility(array($is_dir));
454
455
$content = array();
456
457
$content[] = id(new PHUIObjectBoxView())
458
->setHeaderText(pht('Lint Details'))
459
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
460
->setTable($table)
461
->setPager($pager);
462
463
$crumbs = $this->buildCrumbs(
464
array(
465
'branch' => true,
466
'path' => true,
467
'view' => 'lint',
468
));
469
$crumbs->setBorder(true);
470
471
$header = id(new PHUIHeaderView())
472
->setHeader(pht('Lint: %s', $drequest->getRepository()->getDisplayName()))
473
->setHeaderIcon('fa-code');
474
475
$view = id(new PHUITwoColumnView())
476
->setHeader($header)
477
->setFooter(array(
478
$content,
479
));
480
481
return $this->newPage()
482
->setTitle(
483
array(
484
pht('Lint'),
485
$drequest->getRepository()->getDisplayName(),
486
))
487
->setCrumbs($crumbs)
488
->appendChild(
489
array(
490
$view,
491
));
492
}
493
494
private function loadLintMessages(
495
PhabricatorRepositoryBranch $branch,
496
$limit,
497
$offset) {
498
499
$drequest = $this->getDiffusionRequest();
500
if (!$branch) {
501
return array();
502
}
503
504
$conn = $branch->establishConnection('r');
505
506
$where = array(
507
qsprintf($conn, 'branchID = %d', $branch->getID()),
508
);
509
510
if ($drequest->getPath() != '') {
511
$path = '/'.$drequest->getPath();
512
$is_dir = (substr($path, -1) == '/');
513
$where[] = ($is_dir
514
? qsprintf($conn, 'path LIKE %>', $path)
515
: qsprintf($conn, 'path = %s', $path));
516
}
517
518
if ($drequest->getLint() != '') {
519
$where[] = qsprintf(
520
$conn,
521
'code = %s',
522
$drequest->getLint());
523
}
524
525
return queryfx_all(
526
$conn,
527
'SELECT *
528
FROM %T
529
WHERE %LA
530
ORDER BY path, code, line LIMIT %d OFFSET %d',
531
PhabricatorRepository::TABLE_LINTMESSAGE,
532
$where,
533
$limit,
534
$offset);
535
}
536
}
537
538