@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at recaptime-dev/main 536 lines 14 kB view raw
1<?php 2 3final 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}