Path: blob/master/src/applications/diffusion/controller/DiffusionLintController.php
12242 views
<?php12final class DiffusionLintController extends DiffusionController {34public function shouldAllowPublic() {5return true;6}78public function handleRequest(AphrontRequest $request) {9$viewer = $this->getViewer();1011if ($this->getRepositoryIdentifierFromRequest($request)) {12$response = $this->loadDiffusionContext();13if ($response) {14return $response;15}1617$drequest = $this->getDiffusionRequest();18} else {19$drequest = null;20}2122$code = $request->getStr('lint');23if (strlen($code)) {24return $this->buildDetailsResponse();25}2627$owners = array();28if (!$drequest) {29if (!$request->getArr('owner')) {30$owners = array($viewer->getPHID());31} else {32$owners = array(head($request->getArr('owner')));33}34}3536$codes = $this->loadLintCodes($drequest, $owners);3738if ($codes) {39$branches = id(new PhabricatorRepositoryBranch())->loadAllWhere(40'id IN (%Ld)',41array_unique(ipull($codes, 'branchID')));42$branches = mpull($branches, null, 'getID');43} else {44$branches = array();45}4647if ($branches) {48$repositories = id(new PhabricatorRepositoryQuery())49->setViewer($viewer)50->withIDs(mpull($branches, 'getRepositoryID'))51->execute();52$repositories = mpull($repositories, null, 'getID');53} else {54$repositories = array();55}565758$rows = array();59$total = 0;60foreach ($codes as $code) {61$branch = idx($branches, $code['branchID']);62if (!$branch) {63continue;64}6566$repository = idx($repositories, $branch->getRepositoryID());67if (!$repository) {68continue;69}7071$total += $code['n'];7273if ($drequest) {74$href_lint = $drequest->generateURI(75array(76'action' => 'lint',77'lint' => $code['code'],78));7980$href_browse = $drequest->generateURI(81array(82'action' => 'browse',83'lint' => $code['code'],84));8586$href_repo = $drequest->generateURI(87array(88'action' => 'lint',89));90} else {91$href_lint = $repository->generateURI(92array(93'action' => 'lint',94'lint' => $code['code'],95));9697$href_browse = $repository->generateURI(98array(99'action' => 'browse',100'lint' => $code['code'],101));102103$href_repo = $repository->generateURI(104array(105'action' => 'lint',106));107}108109$rows[] = array(110phutil_tag('a', array('href' => $href_lint), $code['n']),111phutil_tag('a', array('href' => $href_browse), $code['files']),112phutil_tag(113'a',114array(115'href' => $href_repo,116),117$repository->getDisplayName()),118ArcanistLintSeverity::getStringForSeverity($code['maxSeverity']),119$code['code'],120$code['maxName'],121$code['maxDescription'],122);123}124125$table = id(new AphrontTableView($rows))126->setHeaders(array(127pht('Problems'),128pht('Files'),129pht('Repository'),130pht('Severity'),131pht('Code'),132pht('Name'),133pht('Example'),134))135->setColumnVisibility(array(true, true, !$drequest))136->setColumnClasses(array('n', 'n', '', '', 'pri', '', ''));137138$content = array();139140if (!$drequest) {141$form = id(new AphrontFormView())142->setUser($viewer)143->setMethod('GET')144->appendControl(145id(new AphrontFormTokenizerControl())146->setDatasource(new PhabricatorPeopleDatasource())147->setLimit(1)148->setName('owner')149->setLabel(pht('Owner'))150->setValue($owners))151->appendChild(152id(new AphrontFormSubmitControl())153->setValue(pht('Filter')));154$content[] = id(new AphrontListFilterView())->appendChild($form);155}156157$content[] = id(new PHUIObjectBoxView())158->setHeaderText(pht('Lint'))159->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)160->setTable($table);161162$title = array('Lint');163$crumbs = $this->buildCrumbs(164array(165'branch' => true,166'path' => true,167'view' => 'lint',168));169$crumbs->setBorder(true);170171if ($drequest) {172$title[] = $drequest->getRepository()->getDisplayName();173} else {174$crumbs->addTextCrumb(pht('All Lint'));175}176177if ($drequest) {178$branch = $drequest->loadBranch();179180$header = id(new PHUIHeaderView())181->setHeader(pht('Lint: %s', $this->renderPathLinks($drequest, 'lint')))182->setUser($viewer)183->setHeaderIcon('fa-code');184$actions = $this->buildActionView($drequest);185$properties = $this->buildPropertyView(186$drequest,187$branch,188$total,189$actions);190191$object_box = id(new PHUIObjectBoxView())192->setHeader($header)193->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)194->addPropertyList($properties);195} else {196$object_box = null;197$header = id(new PHUIHeaderView())198->setHeader(pht('All Lint'))199->setHeaderIcon('fa-code');200}201202$view = id(new PHUITwoColumnView())203->setHeader($header)204->setFooter(array(205$object_box,206$content,207));208209return $this->newPage()210->setTitle($title)211->setCrumbs($crumbs)212->appendChild(213array(214$view,215));216}217218private function loadLintCodes($drequest, array $owner_phids) {219$conn = id(new PhabricatorRepository())->establishConnection('r');220$where = array('1 = 1');221222if ($drequest) {223$branch = $drequest->loadBranch();224if (!$branch) {225return array();226}227228$where[] = qsprintf($conn, 'branchID = %d', $branch->getID());229230if ($drequest->getPath() != '') {231$path = '/'.$drequest->getPath();232$is_dir = (substr($path, -1) == '/');233$where[] = ($is_dir234? qsprintf($conn, 'path LIKE %>', $path)235: qsprintf($conn, 'path = %s', $path));236}237}238239if ($owner_phids) {240$or = array();241$or[] = qsprintf($conn, 'authorPHID IN (%Ls)', $owner_phids);242243$paths = array();244$packages = id(new PhabricatorOwnersOwner())245->loadAllWhere('userPHID IN (%Ls)', $owner_phids);246if ($packages) {247$paths = id(new PhabricatorOwnersPath())->loadAllWhere(248'packageID IN (%Ld)',249mpull($packages, 'getPackageID'));250}251252if ($paths) {253$repositories = id(new PhabricatorRepositoryQuery())254->setViewer($this->getRequest()->getUser())255->withPHIDs(mpull($paths, 'getRepositoryPHID'))256->execute();257$repositories = mpull($repositories, 'getID', 'getPHID');258259$branches = id(new PhabricatorRepositoryBranch())->loadAllWhere(260'repositoryID IN (%Ld)',261$repositories);262$branches = mgroup($branches, 'getRepositoryID');263}264265foreach ($paths as $path) {266$branch = idx(267$branches,268idx(269$repositories,270$path->getRepositoryPHID()));271if ($branch) {272$condition = qsprintf(273$conn,274'(branchID IN (%Ld) AND path LIKE %>)',275array_keys($branch),276$path->getPath());277if ($path->getExcluded()) {278$where[] = qsprintf($conn, 'NOT %Q', $condition);279} else {280$or[] = $condition;281}282}283}284$where[] = qsprintf($conn, '%LO', $or);285}286287return queryfx_all(288$conn,289'SELECT290branchID,291code,292MAX(severity) AS maxSeverity,293MAX(name) AS maxName,294MAX(description) AS maxDescription,295COUNT(DISTINCT path) AS files,296COUNT(*) AS n297FROM %T298WHERE %LA299GROUP BY branchID, code300ORDER BY n DESC',301PhabricatorRepository::TABLE_LINTMESSAGE,302$where);303}304305protected function buildActionView(DiffusionRequest $drequest) {306$viewer = $this->getRequest()->getUser();307308$view = id(new PhabricatorActionListView())309->setUser($viewer);310311$list_uri = $drequest->generateURI(312array(313'action' => 'lint',314'lint' => '',315));316317$view->addAction(318id(new PhabricatorActionView())319->setName(pht('View As List'))320->setHref($list_uri)321->setIcon('fa-list'));322323$history_uri = $drequest->generateURI(324array(325'action' => 'history',326));327328$view->addAction(329id(new PhabricatorActionView())330->setName(pht('View History'))331->setHref($history_uri)332->setIcon('fa-clock-o'));333334$browse_uri = $drequest->generateURI(335array(336'action' => 'browse',337));338339$view->addAction(340id(new PhabricatorActionView())341->setName(pht('Browse Content'))342->setHref($browse_uri)343->setIcon('fa-files-o'));344345return $view;346}347348protected function buildPropertyView(349DiffusionRequest $drequest,350PhabricatorRepositoryBranch $branch,351$total,352PhabricatorActionListView $actions) {353354$viewer = $this->getRequest()->getUser();355356$view = id(new PHUIPropertyListView())357->setUser($viewer)358->setActionList($actions);359360$lint_commit = $branch->getLintCommit();361362$view->addProperty(363pht('Lint Commit'),364phutil_tag(365'a',366array(367'href' => $drequest->generateURI(368array(369'action' => 'commit',370'commit' => $lint_commit,371)),372),373$drequest->getRepository()->formatCommitName($lint_commit)));374375$view->addProperty(376pht('Total Messages'),377pht('%s', new PhutilNumber($total)));378379return $view;380}381382383private function buildDetailsResponse() {384$request = $this->getRequest();385386$limit = 500;387388$pager = id(new PHUIPagerView())389->readFromRequest($request)390->setPageSize($limit);391392$offset = $pager->getOffset();393394$drequest = $this->getDiffusionRequest();395$branch = $drequest->loadBranch();396$messages = $this->loadLintMessages($branch, $limit, $offset);397$is_dir = (substr('/'.$drequest->getPath(), -1) == '/');398399$pager->setHasMorePages(count($messages) >= $limit);400401$authors = $this->loadViewerHandles(ipull($messages, 'authorPHID'));402403$rows = array();404foreach ($messages as $message) {405$path = phutil_tag(406'a',407array(408'href' => $drequest->generateURI(array(409'action' => 'lint',410'path' => $message['path'],411)),412),413substr($message['path'], strlen($drequest->getPath()) + 1));414415$line = phutil_tag(416'a',417array(418'href' => $drequest->generateURI(array(419'action' => 'browse',420'path' => $message['path'],421'line' => $message['line'],422'commit' => $branch->getLintCommit(),423)),424),425$message['line']);426427$author = $message['authorPHID'];428if ($author && $authors[$author]) {429$author = $authors[$author]->renderLink();430}431432$rows[] = array(433$path,434$line,435$author,436ArcanistLintSeverity::getStringForSeverity($message['severity']),437$message['name'],438$message['description'],439);440}441442$table = id(new AphrontTableView($rows))443->setHeaders(array(444pht('Path'),445pht('Line'),446pht('Author'),447pht('Severity'),448pht('Name'),449pht('Description'),450))451->setColumnClasses(array('', 'n'))452->setColumnVisibility(array($is_dir));453454$content = array();455456$content[] = id(new PHUIObjectBoxView())457->setHeaderText(pht('Lint Details'))458->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)459->setTable($table)460->setPager($pager);461462$crumbs = $this->buildCrumbs(463array(464'branch' => true,465'path' => true,466'view' => 'lint',467));468$crumbs->setBorder(true);469470$header = id(new PHUIHeaderView())471->setHeader(pht('Lint: %s', $drequest->getRepository()->getDisplayName()))472->setHeaderIcon('fa-code');473474$view = id(new PHUITwoColumnView())475->setHeader($header)476->setFooter(array(477$content,478));479480return $this->newPage()481->setTitle(482array(483pht('Lint'),484$drequest->getRepository()->getDisplayName(),485))486->setCrumbs($crumbs)487->appendChild(488array(489$view,490));491}492493private function loadLintMessages(494PhabricatorRepositoryBranch $branch,495$limit,496$offset) {497498$drequest = $this->getDiffusionRequest();499if (!$branch) {500return array();501}502503$conn = $branch->establishConnection('r');504505$where = array(506qsprintf($conn, 'branchID = %d', $branch->getID()),507);508509if ($drequest->getPath() != '') {510$path = '/'.$drequest->getPath();511$is_dir = (substr($path, -1) == '/');512$where[] = ($is_dir513? qsprintf($conn, 'path LIKE %>', $path)514: qsprintf($conn, 'path = %s', $path));515}516517if ($drequest->getLint() != '') {518$where[] = qsprintf(519$conn,520'code = %s',521$drequest->getLint());522}523524return queryfx_all(525$conn,526'SELECT *527FROM %T528WHERE %LA529ORDER BY path, code, line LIMIT %d OFFSET %d',530PhabricatorRepository::TABLE_LINTMESSAGE,531$where,532$limit,533$offset);534}535}536537538