Page MenuHomePhabricator

D15924.id38350.diff
No OneTemporary

D15924.id38350.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -3320,6 +3320,7 @@
'PhabricatorSearchPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorSearchPreferencesSettingsPanel.php',
'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php',
'PhabricatorSearchResultBucket' => 'applications/search/buckets/PhabricatorSearchResultBucket.php',
+ 'PhabricatorSearchResultBucketGroup' => 'applications/search/buckets/PhabricatorSearchResultBucketGroup.php',
'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php',
'PhabricatorSearchSchemaSpec' => 'applications/search/storage/PhabricatorSearchSchemaSpec.php',
'PhabricatorSearchSelectController' => 'applications/search/controller/PhabricatorSearchSelectController.php',
@@ -8015,6 +8016,7 @@
'PhabricatorSearchPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorSearchRelationship' => 'Phobject',
'PhabricatorSearchResultBucket' => 'Phobject',
+ 'PhabricatorSearchResultBucketGroup' => 'Phobject',
'PhabricatorSearchResultView' => 'AphrontView',
'PhabricatorSearchSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PhabricatorSearchSelectController' => 'PhabricatorSearchBaseController',
diff --git a/src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php b/src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php
--- a/src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php
+++ b/src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php
@@ -5,8 +5,196 @@
const BUCKETKEY = 'action';
+ private $objects;
+
public function getResultBucketName() {
return pht('Bucket by Required Action');
}
+ protected function buildResultGroups(
+ PhabricatorSavedQuery $query,
+ array $objects) {
+
+ $this->objects = $objects;
+
+ $phids = $query->getParameter('responsiblePHIDs', array());
+ if (!$phids) {
+ throw new Exception(
+ pht(
+ 'You can not bucket results by required action without '.
+ 'specifying "Responsible Users".'));
+ }
+ $phids = array_fuse($phids);
+
+ $groups = array();
+
+ $groups[] = $this->newGroup()
+ ->setName(pht('Must Review'))
+ ->setNoDataString(pht('No revisions are blocked on your review.'))
+ ->setObjects($this->filterMustReview($phids));
+
+ $groups[] = $this->newGroup()
+ ->setName(pht('Ready to Review'))
+ ->setNoDataString(pht('No revisions are waiting on you to review them.'))
+ ->setObjects($this->filterShouldReview($phids));
+
+ $groups[] = $this->newGroup()
+ ->setName(pht('Ready to Land'))
+ ->setNoDataString(pht('No revisions are ready to land.'))
+ ->setObjects($this->filterShouldLand($phids));
+
+ $groups[] = $this->newGroup()
+ ->setName(pht('Ready to Update'))
+ ->setNoDataString(pht('No revisions are waiting for updates.'))
+ ->setObjects($this->filterShouldUpdate($phids));
+
+ $groups[] = $this->newGroup()
+ ->setName(pht('Waiting on Review'))
+ ->setNoDataString(pht('None of your revisions are waiting on review.'))
+ ->setObjects($this->filterWaitingForReview($phids));
+
+ $groups[] = $this->newGroup()
+ ->setName(pht('Waiting on Authors'))
+ ->setNoDataString(pht('No revisions are waiting on author action.'))
+ ->setObjects($this->filterWaitingOnAuthors($phids));
+
+ // Because you can apply these buckets to queries which include revisions
+ // that have been closed, add an "Other" bucket if we still have stuff
+ // that didn't get filtered into any of the previous buckets.
+ if ($this->objects) {
+ $groups[] = $this->newGroup()
+ ->setName(pht('Other Revisions'))
+ ->setObjects($this->objects);
+ }
+
+ return $groups;
+ }
+
+ private function filterMustReview(array $phids) {
+ $blocking = array(
+ DifferentialReviewerStatus::STATUS_BLOCKING,
+ DifferentialReviewerStatus::STATUS_REJECTED,
+ DifferentialReviewerStatus::STATUS_REJECTED_OLDER,
+ );
+ $blocking = array_fuse($blocking);
+
+ $objects = $this->getRevisionsUnderReview($this->objects, $phids);
+
+ $results = array();
+ foreach ($objects as $key => $object) {
+ if (!$this->hasReviewersWithStatus($object, $phids, $blocking)) {
+ continue;
+ }
+
+ $results[$key] = $object;
+ unset($this->objects[$key]);
+ }
+
+ return $results;
+ }
+
+ private function filterShouldReview(array $phids) {
+ $reviewing = array(
+ DifferentialReviewerStatus::STATUS_ADDED,
+ );
+ $reviewing = array_fuse($reviewing);
+
+ $objects = $this->getRevisionsUnderReview($this->objects, $phids);
+
+ $results = array();
+ foreach ($objects as $key => $object) {
+ if (!$this->hasReviewersWithStatus($object, $phids, $reviewing)) {
+ continue;
+ }
+
+ $results[$key] = $object;
+ unset($this->objects[$key]);
+ }
+
+ return $results;
+ }
+
+ private function filterShouldLand(array $phids) {
+ $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED;
+
+ $objects = $this->getRevisionsAuthored($this->objects, $phids);
+
+ $results = array();
+ foreach ($objects as $key => $object) {
+ if ($object->getStatus() != $status_accepted) {
+ continue;
+ }
+
+ $results[$key] = $object;
+ unset($this->objects[$key]);
+ }
+
+ return $results;
+ }
+
+ private function filterShouldUpdate(array $phids) {
+ $statuses = array(
+ ArcanistDifferentialRevisionStatus::NEEDS_REVISION,
+ ArcanistDifferentialRevisionStatus::CHANGES_PLANNED,
+ ArcanistDifferentialRevisionStatus::IN_PREPARATION,
+ );
+ $statuses = array_fuse($statuses);
+
+ $objects = $this->getRevisionsAuthored($this->objects, $phids);
+
+ $results = array();
+ foreach ($objects as $key => $object) {
+ if (empty($statuses[$object->getStatus()])) {
+ continue;
+ }
+
+ $results[$key] = $object;
+ unset($this->objects[$key]);
+ }
+
+ return $results;
+ }
+
+ private function filterWaitingForReview(array $phids) {
+ $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
+
+ $objects = $this->getRevisionsAuthored($this->objects, $phids);
+
+ $results = array();
+ foreach ($objects as $key => $object) {
+ if ($object->getStatus() != $status_review) {
+ continue;
+ }
+
+ $results[$key] = $object;
+ unset($this->objects[$key]);
+ }
+
+ return $results;
+ }
+
+ private function filterWaitingOnAuthors(array $phids) {
+ $statuses = array(
+ ArcanistDifferentialRevisionStatus::ACCEPTED,
+ ArcanistDifferentialRevisionStatus::NEEDS_REVISION,
+ ArcanistDifferentialRevisionStatus::CHANGES_PLANNED,
+ ArcanistDifferentialRevisionStatus::IN_PREPARATION,
+ );
+ $statuses = array_fuse($statuses);
+
+ $objects = $this->getRevisionsNotAuthored($this->objects, $phids);
+
+ $results = array();
+ foreach ($objects as $key => $object) {
+ if (empty($statuses[$object->getStatus()])) {
+ continue;
+ }
+
+ $results[$key] = $object;
+ unset($this->objects[$key]);
+ }
+
+ return $results;
+ }
+
}
diff --git a/src/applications/differential/query/DifferentialRevisionResultBucket.php b/src/applications/differential/query/DifferentialRevisionResultBucket.php
--- a/src/applications/differential/query/DifferentialRevisionResultBucket.php
+++ b/src/applications/differential/query/DifferentialRevisionResultBucket.php
@@ -10,4 +10,68 @@
->execute();
}
+ protected function getRevisionsUnderReview(array $objects, array $phids) {
+ $results = array();
+
+ $objects = $this->getRevisionsNotAuthored($objects, $phids);
+
+ $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
+ foreach ($objects as $key => $object) {
+ if ($object->getStatus() !== $status_review) {
+ continue;
+ }
+
+ $results[$key] = $object;
+ }
+
+ return $results;
+ }
+
+ protected function getRevisionsAuthored(array $objects, array $phids) {
+ $results = array();
+
+ foreach ($objects as $key => $object) {
+ if (isset($phids[$object->getAuthorPHID()])) {
+ $results[$key] = $object;
+ }
+ }
+
+ return $results;
+ }
+
+ protected function getRevisionsNotAuthored(array $objects, array $phids) {
+ $results = array();
+
+ foreach ($objects as $key => $object) {
+ if (empty($phids[$object->getAuthorPHID()])) {
+ $results[$key] = $object;
+ }
+ }
+
+ return $results;
+ }
+
+ protected function hasReviewersWithStatus(
+ DifferentialRevision $revision,
+ array $phids,
+ array $statuses) {
+
+ foreach ($revision->getReviewerStatus() as $reviewer) {
+ $reviewer_phid = $reviewer->getReviewerPHID();
+ if (empty($phids[$reviewer_phid])) {
+ continue;
+ }
+
+ $status = $reviewer->getStatus();
+ if (empty($statuses[$status])) {
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+
}
diff --git a/src/applications/differential/query/DifferentialRevisionSearchEngine.php b/src/applications/differential/query/DifferentialRevisionSearchEngine.php
--- a/src/applications/differential/query/DifferentialRevisionSearchEngine.php
+++ b/src/applications/differential/query/DifferentialRevisionSearchEngine.php
@@ -19,7 +19,8 @@
return id(new DifferentialRevisionQuery())
->needFlags(true)
->needDrafts(true)
- ->needRelationships(true);
+ ->needRelationships(true)
+ ->needReviewerStatus(true);
}
protected function buildQueryFromParameters(array $map) {
@@ -153,33 +154,20 @@
$views = array();
if ($bucket) {
- $split = DifferentialRevisionQuery::splitResponsible(
- $revisions,
- $query->getParameter('responsiblePHIDs'));
- list($blocking, $active, $waiting) = $split;
-
- $views[] = id(clone $template)
- ->setHeader(pht('Blocking Others'))
- ->setNoDataString(
- pht('No revisions are blocked on your action.'))
- ->setHighlightAge(true)
- ->setRevisions($blocking)
- ->setHandles(array());
-
- $views[] = id(clone $template)
- ->setHeader(pht('Action Required'))
- ->setNoDataString(
- pht('No revisions require your action.'))
- ->setHighlightAge(true)
- ->setRevisions($active)
- ->setHandles(array());
-
- $views[] = id(clone $template)
- ->setHeader(pht('Waiting on Others'))
- ->setNoDataString(
- pht('You have no revisions waiting on others.'))
- ->setRevisions($waiting)
- ->setHandles(array());
+ $bucket->setViewer($viewer);
+
+ try {
+ $groups = $bucket->newResultGroups($query, $revisions);
+
+ foreach ($groups as $group) {
+ $views[] = id(clone $template)
+ ->setHeader($group->getName())
+ ->setNoDataString($group->getNoDataString())
+ ->setRevisions($group->getObjects());
+ }
+ } catch (Exception $ex) {
+ $this->addError($ex->getMessage());
+ }
} else {
$views[] = id(clone $template)
->setRevisions($revisions)
diff --git a/src/applications/search/buckets/PhabricatorSearchResultBucket.php b/src/applications/search/buckets/PhabricatorSearchResultBucket.php
--- a/src/applications/search/buckets/PhabricatorSearchResultBucket.php
+++ b/src/applications/search/buckets/PhabricatorSearchResultBucket.php
@@ -3,6 +3,7 @@
abstract class PhabricatorSearchResultBucket
extends Phobject {
+ private $viewer;
private $pageSize;
final public function setPageSize($page_size) {
@@ -18,14 +19,36 @@
return $this->pageSize;
}
+ public function setViewer(PhabricatorUser $viewer) {
+ $this->viewer = $viewer;
+ return $this;
+ }
+
+ public function getViewer() {
+ return $this->viewer;
+ }
+
protected function getDefaultPageSize() {
return 1000;
}
abstract public function getResultBucketName();
+ abstract protected function buildResultGroups(
+ PhabricatorSavedQuery $query,
+ array $objects);
+
+ final public function newResultGroups(
+ PhabricatorSavedQuery $query,
+ array $objects) {
+ return $this->buildResultGroups($query, $objects);
+ }
final public function getResultBucketKey() {
return $this->getPhobjectClassConstant('BUCKETKEY');
}
+ final protected function newGroup() {
+ return new PhabricatorSearchResultBucketGroup();
+ }
+
}
diff --git a/src/applications/search/buckets/PhabricatorSearchResultBucketGroup.php b/src/applications/search/buckets/PhabricatorSearchResultBucketGroup.php
new file mode 100644
--- /dev/null
+++ b/src/applications/search/buckets/PhabricatorSearchResultBucketGroup.php
@@ -0,0 +1,37 @@
+<?php
+
+final class PhabricatorSearchResultBucketGroup
+ extends Phobject {
+
+ private $name;
+ private $noDataString;
+ private $objects;
+
+ public function setNoDataString($no_data_string) {
+ $this->noDataString = $no_data_string;
+ return $this;
+ }
+
+ public function getNoDataString() {
+ return $this->noDataString;
+ }
+
+ public function setName($name) {
+ $this->name = $name;
+ return $this;
+ }
+
+ public function getName() {
+ return $this->name;
+ }
+
+ public function setObjects(array $objects) {
+ $this->objects = $objects;
+ return $this;
+ }
+
+ public function getObjects() {
+ return $this->objects;
+ }
+
+}
diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php
--- a/src/applications/search/controller/PhabricatorApplicationSearchController.php
+++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php
@@ -213,6 +213,8 @@
if ($run_query) {
+ $exec_errors = array();
+
$box->setAnchor(
id(new PhabricatorAnchorView())
->setAnchorName('R'));
@@ -280,10 +282,18 @@
}
}
} catch (PhabricatorTypeaheadInvalidTokenException $ex) {
- $errors[] = pht(
+ $exec_errors[] = pht(
'This query specifies an invalid parameter. Review the '.
'query parameters and correct errors.');
}
+
+ // The engine may have encountered additional errors during rendering;
+ // merge them in and show everything.
+ foreach ($engine->getErrors() as $error) {
+ $exec_errors[] = $error;
+ }
+
+ $errors = $exec_errors;
}
if ($errors) {

File Metadata

Mime Type
text/plain
Expires
Mar 5 2025, 10:14 AM (5 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7224992
Default Alt Text
D15924.id38350.diff (14 KB)

Event Timeline