/**
  * Update the table which links Differential revisions to paths they affect,
  * so Diffusion can efficiently find pending revisions for a given file.
  */
 private function updateAffectedPathTable(DifferentialRevision $revision, DifferentialDiff $diff)
 {
     $repository = $revision->getRepository();
     if (!$repository) {
         // The repository where the code lives is untracked.
         return;
     }
     $path_prefix = null;
     $local_root = $diff->getSourceControlPath();
     if ($local_root) {
         // We're in a working copy which supports subdirectory checkouts (e.g.,
         // SVN) so we need to figure out what prefix we should add to each path
         // (e.g., trunk/projects/example/) to get the absolute path from the
         // root of the repository. DVCS systems like Git and Mercurial are not
         // affected.
         // Normalize both paths and check if the repository root is a prefix of
         // the local root. If so, throw it away. Note that this correctly handles
         // the case where the remote path is "/".
         $local_root = id(new PhutilURI($local_root))->getPath();
         $local_root = rtrim($local_root, '/');
         $repo_root = id(new PhutilURI($repository->getRemoteURI()))->getPath();
         $repo_root = rtrim($repo_root, '/');
         if (!strncmp($repo_root, $local_root, strlen($repo_root))) {
             $path_prefix = substr($local_root, strlen($repo_root));
         }
     }
     $changesets = $diff->getChangesets();
     $paths = array();
     foreach ($changesets as $changeset) {
         $paths[] = $path_prefix . '/' . $changeset->getFilename();
     }
     // Mark this as also touching all parent paths, so you can see all pending
     // changes to any file within a directory.
     $all_paths = array();
     foreach ($paths as $local) {
         foreach (DiffusionPathIDQuery::expandPathToRoot($local) as $path) {
             $all_paths[$path] = true;
         }
     }
     $all_paths = array_keys($all_paths);
     $path_ids = PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths($all_paths);
     $table = new DifferentialAffectedPath();
     $conn_w = $table->establishConnection('w');
     $sql = array();
     foreach ($path_ids as $path_id) {
         $sql[] = qsprintf($conn_w, '(%d, %d, %d, %d)', $repository->getID(), $path_id, time(), $revision->getID());
     }
     queryfx($conn_w, 'DELETE FROM %T WHERE revisionID = %d', $table->getTableName(), $revision->getID());
     foreach (array_chunk($sql, 256) as $chunk) {
         queryfx($conn_w, 'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %Q', $table->getTableName(), implode(', ', $chunk));
     }
 }
 private function buildOperationsBox(DifferentialRevision $revision)
 {
     $viewer = $this->getViewer();
     // Save a query if we can't possibly have pending operations.
     $repository = $revision->getRepository();
     if (!$repository || !$repository->canPerformAutomation()) {
         return null;
     }
     $operations = id(new DrydockRepositoryOperationQuery())->setViewer($viewer)->withObjectPHIDs(array($revision->getPHID()))->withOperationStates(array(DrydockRepositoryOperation::STATE_WAIT, DrydockRepositoryOperation::STATE_WORK, DrydockRepositoryOperation::STATE_FAIL))->execute();
     if (!$operations) {
         return null;
     }
     $operation = head(msort($operations, 'getID'));
     $box_view = id(new PHUIObjectBoxView())->setHeaderText(pht('Active Operations'));
     return id(new DrydockRepositoryOperationStatusView())->setUser($viewer)->setBoxView($box_view)->setOperation($operation);
 }
 public function getBarrierToLanding(PhabricatorUser $viewer, DifferentialRevision $revision)
 {
     $repository = $revision->getRepository();
     if (!$repository) {
         return array('title' => pht('No Repository'), 'body' => pht('This revision is not associated with a known repository. Only ' . 'revisions associated with a tracked repository can be landed ' . 'automatically.'));
     }
     if (!$repository->canPerformAutomation()) {
         return array('title' => pht('No Repository Automation'), 'body' => pht('The repository this revision is associated with ("%s") is not ' . 'configured to support automation. Configure automation for the ' . 'repository to enable revisions to be landed automatically.', $repository->getMonogram()));
     }
     // TODO: At some point we should allow installs to give "land reviewed
     // code" permission to more users than "push any commit", because it is
     // a much less powerful operation. For now, just require push so this
     // doesn't do anything users can't do on their own.
     $can_push = PhabricatorPolicyFilter::hasCapability($viewer, $repository, DiffusionPushCapability::CAPABILITY);
     if (!$can_push) {
         return array('title' => pht('Unable to Push'), 'body' => pht('You do not have permission to push to the repository this ' . 'revision is associated with ("%s"), so you can not land it.', $repository->getMonogram()));
     }
     $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED;
     if ($revision->getStatus() != $status_accepted) {
         switch ($revision->getStatus()) {
             case ArcanistDifferentialRevisionStatus::CLOSED:
                 return array('title' => pht('Revision Closed'), 'body' => pht('This revision has already been closed. Only open, accepted ' . 'revisions may land.'));
             case ArcanistDifferentialRevisionStatus::ABANDONED:
                 return array('title' => pht('Revision Abandoned'), 'body' => pht('This revision has been abandoned. Only accepted revisions ' . 'may land.'));
             default:
                 return array('title' => pht('Revision Not Accepted'), 'body' => pht('This revision is still under review. Only revisions which ' . 'have been accepted may land.'));
         }
     }
     // Check for other operations. Eventually this should probably be more
     // general (e.g., it's OK to land to multiple different branches
     // simultaneously) but just put this in as a sanity check for now.
     $other_operations = id(new DrydockRepositoryOperationQuery())->setViewer($viewer)->withObjectPHIDs(array($revision->getPHID()))->withOperationTypes(array($this->getOperationConstant()))->withOperationStates(array(DrydockRepositoryOperation::STATE_WAIT, DrydockRepositoryOperation::STATE_WORK, DrydockRepositoryOperation::STATE_DONE))->execute();
     if ($other_operations) {
         $any_done = false;
         foreach ($other_operations as $operation) {
             if ($operation->isDone()) {
                 $any_done = true;
                 break;
             }
         }
         if ($any_done) {
             return array('title' => pht('Already Complete'), 'body' => pht('This revision has already landed.'));
         } else {
             return array('title' => pht('Already In Flight'), 'body' => pht('This revision is already landing.'));
         }
     }
     return null;
 }
 private function buildOperationsBox(DifferentialRevision $revision)
 {
     $viewer = $this->getViewer();
     // Save a query if we can't possibly have pending operations.
     $repository = $revision->getRepository();
     if (!$repository || !$repository->canPerformAutomation()) {
         return null;
     }
     $operations = id(new DrydockRepositoryOperationQuery())->setViewer($viewer)->withObjectPHIDs(array($revision->getPHID()))->withIsDismissed(false)->withOperationTypes(array(DrydockLandRepositoryOperation::OPCONST))->execute();
     if (!$operations) {
         return null;
     }
     $state_fail = DrydockRepositoryOperation::STATE_FAIL;
     // We're going to show the oldest operation which hasn't failed, or the
     // most recent failure if they're all failures.
     $operations = msort($operations, 'getID');
     foreach ($operations as $operation) {
         if ($operation->getOperationState() != $state_fail) {
             break;
         }
     }
     // If we found a completed operation, don't render anything. We don't want
     // to show an older error after the thing worked properly.
     if ($operation->isDone()) {
         return null;
     }
     $box_view = id(new PHUIObjectBoxView())->setHeaderText(pht('Active Operations'));
     return id(new DrydockRepositoryOperationStatusView())->setUser($viewer)->setBoxView($box_view)->setOperation($operation);
 }
 private function buildOperationsBox(DifferentialRevision $revision)
 {
     $viewer = $this->getViewer();
     // Save a query if we can't possibly have pending operations.
     $repository = $revision->getRepository();
     if (!$repository || !$repository->canPerformAutomation()) {
         return null;
     }
     $operations = id(new DrydockRepositoryOperationQuery())->setViewer($viewer)->withObjectPHIDs(array($revision->getPHID()))->withOperationStates(array(DrydockRepositoryOperation::STATE_WAIT, DrydockRepositoryOperation::STATE_WORK, DrydockRepositoryOperation::STATE_FAIL))->execute();
     if (!$operations) {
         return null;
     }
     $operation = head(msort($operations, 'getID'));
     // TODO: This is completely made up for now, give it useful information and
     // a sweet progress bar.
     switch ($operation->getOperationState()) {
         case DrydockRepositoryOperation::STATE_WAIT:
         case DrydockRepositoryOperation::STATE_WORK:
             $severity = PHUIInfoView::SEVERITY_NOTICE;
             $text = pht('Some sort of repository operation is currently running.');
             break;
         default:
             $severity = PHUIInfoView::SEVERITY_ERROR;
             $text = pht('Some sort of repository operation failed.');
             break;
     }
     $info_view = id(new PHUIInfoView())->setSeverity($severity)->appendChild($text);
     return id(new PHUIObjectBoxView())->setHeaderText(pht('Active Operations (EXPERIMENTAL!)'))->setInfoView($info_view);
 }