diff --git a/src/applications/config/controller/PhabricatorConfigClusterRepositoriesController.php b/src/applications/config/controller/PhabricatorConfigClusterRepositoriesController.php --- a/src/applications/config/controller/PhabricatorConfigClusterRepositoriesController.php +++ b/src/applications/config/controller/PhabricatorConfigClusterRepositoriesController.php @@ -27,10 +27,15 @@ ->setBorder(true); $repository_status = $this->buildClusterRepositoryStatus(); + $repository_errors = $this->buildClusterRepositoryErrors(); $content = id(new PhabricatorConfigPageView()) ->setHeader($header) - ->setContent($repository_status); + ->setContent( + array( + $repository_status, + $repository_errors, + )); return $this->newPage() ->setTitle($title) @@ -338,4 +343,70 @@ } + private function buildClusterRepositoryErrors() { + $viewer = $this->getViewer(); + + $messages = id(new PhabricatorRepositoryStatusMessage())->loadAllWhere( + 'statusCode IN (%Ls)', + array( + PhabricatorRepositoryStatusMessage::CODE_ERROR, + )); + + $repository_ids = mpull($messages, 'getRepositoryID'); + if ($repository_ids) { + // NOTE: We're bypassing policies when loading repositories because we + // want to show errors exist even if the viewer can't see the repository. + // We use handles to describe the repository below, so the viewer won't + // actually be able to see any particulars if they can't see the + // repository. + $repositories = id(new PhabricatorRepositoryQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withIDs($repository_ids) + ->execute(); + $repositories = mpull($repositories, null, 'getID'); + } + + $rows = array(); + foreach ($messages as $message) { + $repository = idx($repositories, $message->getRepositoryID()); + if (!$repository) { + continue; + } + + if (!$repository->isTracked()) { + continue; + } + + $icon = id(new PHUIIconView()) + ->setIcon('fa-exclamation-triangle red'); + + $rows[] = array( + $icon, + $viewer->renderHandle($repository->getPHID()), + phutil_tag( + 'a', + array( + 'href' => $repository->getPathURI('manage/status/'), + ), + $message->getStatusTypeName()), + ); + } + + return id(new AphrontTableView($rows)) + ->setNoDataString( + pht('No active repositories have outstanding errors.')) + ->setHeaders( + array( + null, + pht('Repository'), + pht('Error'), + )) + ->setColumnClasses( + array( + null, + 'pri', + 'wide', + )); + } + } diff --git a/src/applications/repository/storage/PhabricatorRepositoryStatusMessage.php b/src/applications/repository/storage/PhabricatorRepositoryStatusMessage.php --- a/src/applications/repository/storage/PhabricatorRepositoryStatusMessage.php +++ b/src/applications/repository/storage/PhabricatorRepositoryStatusMessage.php @@ -40,4 +40,15 @@ return idx($this->parameters, $key, $default); } + public function getStatusTypeName() { + $names = array( + self::TYPE_INIT => pht('Error While Initializing Repository'), + self::TYPE_FETCH => pht('Error While Fetching Changes'), + self::TYPE_NEEDS_UPDATE => pht('Repository Needs Update'), + ); + + $type = $this->getStatusType(); + return idx($names, $type, $type); + } + }