Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2894063
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
37 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php
index 727a4ca184..bc93de2db2 100644
--- a/src/applications/differential/query/DifferentialRevisionQuery.php
+++ b/src/applications/differential/query/DifferentialRevisionQuery.php
@@ -1,992 +1,1014 @@
<?php
/**
* @task config Query Configuration
* @task exec Query Execution
* @task internal Internals
*/
final class DifferentialRevisionQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $pathIDs = array();
private $authors = array();
private $draftAuthors = array();
private $ccs = array();
private $reviewers = array();
private $revIDs = array();
private $commitHashes = array();
private $commitPHIDs = array();
private $phids = array();
private $responsibles = array();
private $branches = array();
private $repositoryPHIDs;
private $updatedEpochMin;
private $updatedEpochMax;
private $statuses;
private $isOpen;
+ private $createdEpochMin;
+ private $createdEpochMax;
const ORDER_MODIFIED = 'order-modified';
const ORDER_CREATED = 'order-created';
private $needActiveDiffs = false;
private $needDiffIDs = false;
private $needCommitPHIDs = false;
private $needHashes = false;
private $needReviewers = false;
private $needReviewerAuthority;
private $needDrafts;
private $needFlags;
/* -( Query Configuration )------------------------------------------------ */
/**
* Filter results to revisions which affect a Diffusion path ID in a given
* repository. You can call this multiple times to select revisions for
* several paths.
*
* @param int Diffusion repository ID.
* @param int Diffusion path ID.
* @return this
* @task config
*/
public function withPath($repository_id, $path_id) {
$this->pathIDs[] = array(
'repositoryID' => $repository_id,
'pathID' => $path_id,
);
return $this;
}
/**
* Filter results to revisions authored by one of the given PHIDs. Calling
* this function will clear anything set by previous calls to
* @{method:withAuthors}.
*
* @param array List of PHIDs of authors
* @return this
* @task config
*/
public function withAuthors(array $author_phids) {
$this->authors = $author_phids;
return $this;
}
/**
* Filter results to revisions which CC one of the listed people. Calling this
* function will clear anything set by previous calls to @{method:withCCs}.
*
* @param array List of PHIDs of subscribers.
* @return this
* @task config
*/
public function withCCs(array $cc_phids) {
$this->ccs = $cc_phids;
return $this;
}
/**
* Filter results to revisions that have one of the provided PHIDs as
* reviewers. Calling this function will clear anything set by previous calls
* to @{method:withReviewers}.
*
* @param array List of PHIDs of reviewers
* @return this
* @task config
*/
public function withReviewers(array $reviewer_phids) {
$this->reviewers = $reviewer_phids;
return $this;
}
/**
* Filter results to revisions that have one of the provided commit hashes.
* Calling this function will clear anything set by previous calls to
* @{method:withCommitHashes}.
*
* @param array List of pairs <Class
* ArcanistDifferentialRevisionHash::HASH_$type constant,
* hash>
* @return this
* @task config
*/
public function withCommitHashes(array $commit_hashes) {
$this->commitHashes = $commit_hashes;
return $this;
}
/**
* Filter results to revisions that have one of the provided PHIDs as
* commits. Calling this function will clear anything set by previous calls
* to @{method:withCommitPHIDs}.
*
* @param array List of PHIDs of commits
* @return this
* @task config
*/
public function withCommitPHIDs(array $commit_phids) {
$this->commitPHIDs = $commit_phids;
return $this;
}
public function withStatuses(array $statuses) {
$this->statuses = $statuses;
return $this;
}
public function withIsOpen($is_open) {
$this->isOpen = $is_open;
return $this;
}
/**
* Filter results to revisions on given branches.
*
* @param list List of branch names.
* @return this
* @task config
*/
public function withBranches(array $branches) {
$this->branches = $branches;
return $this;
}
/**
* Filter results to only return revisions whose ids are in the given set.
*
* @param array List of revision ids
* @return this
* @task config
*/
public function withIDs(array $ids) {
$this->revIDs = $ids;
return $this;
}
/**
* Filter results to only return revisions whose PHIDs are in the given set.
*
* @param array List of revision PHIDs
* @return this
* @task config
*/
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
/**
* Given a set of users, filter results to return only revisions they are
* responsible for (i.e., they are either authors or reviewers).
*
* @param array List of user PHIDs.
* @return this
* @task config
*/
public function withResponsibleUsers(array $responsible_phids) {
$this->responsibles = $responsible_phids;
return $this;
}
public function withRepositoryPHIDs(array $repository_phids) {
$this->repositoryPHIDs = $repository_phids;
return $this;
}
public function withUpdatedEpochBetween($min, $max) {
$this->updatedEpochMin = $min;
$this->updatedEpochMax = $max;
return $this;
}
+ public function withCreatedEpochBetween($min, $max) {
+ $this->createdEpochMin = $min;
+ $this->createdEpochMax = $max;
+ return $this;
+ }
+
/**
* Set whether or not the query should load the active diff for each
* revision.
*
* @param bool True to load and attach diffs.
* @return this
* @task config
*/
public function needActiveDiffs($need_active_diffs) {
$this->needActiveDiffs = $need_active_diffs;
return $this;
}
/**
* Set whether or not the query should load the associated commit PHIDs for
* each revision.
*
* @param bool True to load and attach diffs.
* @return this
* @task config
*/
public function needCommitPHIDs($need_commit_phids) {
$this->needCommitPHIDs = $need_commit_phids;
return $this;
}
/**
* Set whether or not the query should load associated diff IDs for each
* revision.
*
* @param bool True to load and attach diff IDs.
* @return this
* @task config
*/
public function needDiffIDs($need_diff_ids) {
$this->needDiffIDs = $need_diff_ids;
return $this;
}
/**
* Set whether or not the query should load associated commit hashes for each
* revision.
*
* @param bool True to load and attach commit hashes.
* @return this
* @task config
*/
public function needHashes($need_hashes) {
$this->needHashes = $need_hashes;
return $this;
}
/**
* Set whether or not the query should load associated reviewers.
*
* @param bool True to load and attach reviewers.
* @return this
* @task config
*/
public function needReviewers($need_reviewers) {
$this->needReviewers = $need_reviewers;
return $this;
}
/**
* Request information about the viewer's authority to act on behalf of each
* reviewer. In particular, they have authority to act on behalf of projects
* they are a member of.
*
* @param bool True to load and attach authority.
* @return this
* @task config
*/
public function needReviewerAuthority($need_reviewer_authority) {
$this->needReviewerAuthority = $need_reviewer_authority;
return $this;
}
public function needFlags($need_flags) {
$this->needFlags = $need_flags;
return $this;
}
public function needDrafts($need_drafts) {
$this->needDrafts = $need_drafts;
return $this;
}
/* -( Query Execution )---------------------------------------------------- */
public function newResultObject() {
return new DifferentialRevision();
}
/**
* Execute the query as configured, returning matching
* @{class:DifferentialRevision} objects.
*
* @return list List of matching DifferentialRevision objects.
* @task exec
*/
protected function loadPage() {
$data = $this->loadData();
$data = $this->didLoadRawRows($data);
$table = $this->newResultObject();
return $table->loadAllFromArray($data);
}
protected function willFilterPage(array $revisions) {
$viewer = $this->getViewer();
$repository_phids = mpull($revisions, 'getRepositoryPHID');
$repository_phids = array_filter($repository_phids);
$repositories = array();
if ($repository_phids) {
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->withPHIDs($repository_phids)
->execute();
$repositories = mpull($repositories, null, 'getPHID');
}
// If a revision is associated with a repository:
//
// - the viewer must be able to see the repository; or
// - the viewer must have an automatic view capability.
//
// In the latter case, we'll load the revision but not load the repository.
$can_view = PhabricatorPolicyCapability::CAN_VIEW;
foreach ($revisions as $key => $revision) {
$repo_phid = $revision->getRepositoryPHID();
if (!$repo_phid) {
// The revision has no associated repository. Attach `null` and move on.
$revision->attachRepository(null);
continue;
}
$repository = idx($repositories, $repo_phid);
if ($repository) {
// The revision has an associated repository, and the viewer can see
// it. Attach it and move on.
$revision->attachRepository($repository);
continue;
}
if ($revision->hasAutomaticCapability($can_view, $viewer)) {
// The revision has an associated repository which the viewer can not
// see, but the viewer has an automatic capability on this revision.
// Load the revision without attaching a repository.
$revision->attachRepository(null);
continue;
}
if ($this->getViewer()->isOmnipotent()) {
// The viewer is omnipotent. Allow the revision to load even without
// a repository.
$revision->attachRepository(null);
continue;
}
// The revision has an associated repository, and the viewer can't see
// it, and the viewer has no special capabilities. Filter out this
// revision.
$this->didRejectResult($revision);
unset($revisions[$key]);
}
if (!$revisions) {
return array();
}
$table = new DifferentialRevision();
$conn_r = $table->establishConnection('r');
if ($this->needCommitPHIDs) {
$this->loadCommitPHIDs($conn_r, $revisions);
}
$need_active = $this->needActiveDiffs;
$need_ids = $need_active || $this->needDiffIDs;
if ($need_ids) {
$this->loadDiffIDs($conn_r, $revisions);
}
if ($need_active) {
$this->loadActiveDiffs($conn_r, $revisions);
}
if ($this->needHashes) {
$this->loadHashes($conn_r, $revisions);
}
if ($this->needReviewers || $this->needReviewerAuthority) {
$this->loadReviewers($conn_r, $revisions);
}
return $revisions;
}
protected function didFilterPage(array $revisions) {
$viewer = $this->getViewer();
if ($this->needFlags) {
$flags = id(new PhabricatorFlagQuery())
->setViewer($viewer)
->withOwnerPHIDs(array($viewer->getPHID()))
->withObjectPHIDs(mpull($revisions, 'getPHID'))
->execute();
$flags = mpull($flags, null, 'getObjectPHID');
foreach ($revisions as $revision) {
$revision->attachFlag(
$viewer,
idx($flags, $revision->getPHID()));
}
}
if ($this->needDrafts) {
PhabricatorDraftEngine::attachDrafts(
$viewer,
$revisions);
}
return $revisions;
}
private function loadData() {
$table = $this->newResultObject();
$conn_r = $table->establishConnection('r');
$selects = array();
// NOTE: If the query includes "responsiblePHIDs", we execute it as a
// UNION of revisions they own and revisions they're reviewing. This has
// much better performance than doing it with JOIN/WHERE.
if ($this->responsibles) {
$basic_authors = $this->authors;
$basic_reviewers = $this->reviewers;
try {
// Build the query where the responsible users are authors.
$this->authors = array_merge($basic_authors, $this->responsibles);
$this->reviewers = $basic_reviewers;
$selects[] = $this->buildSelectStatement($conn_r);
// Build the query where the responsible users are reviewers, or
// projects they are members of are reviewers.
$this->authors = $basic_authors;
$this->reviewers = array_merge($basic_reviewers, $this->responsibles);
$selects[] = $this->buildSelectStatement($conn_r);
// Put everything back like it was.
$this->authors = $basic_authors;
$this->reviewers = $basic_reviewers;
} catch (Exception $ex) {
$this->authors = $basic_authors;
$this->reviewers = $basic_reviewers;
throw $ex;
}
} else {
$selects[] = $this->buildSelectStatement($conn_r);
}
if (count($selects) > 1) {
$query = qsprintf(
$conn_r,
'%Q %Q %Q',
implode(' UNION DISTINCT ', $selects),
$this->buildOrderClause($conn_r, true),
$this->buildLimitClause($conn_r));
} else {
$query = head($selects);
}
return queryfx_all($conn_r, '%Q', $query);
}
private function buildSelectStatement(AphrontDatabaseConnection $conn_r) {
$table = new DifferentialRevision();
$select = $this->buildSelectClause($conn_r);
$from = qsprintf(
$conn_r,
'FROM %T r',
$table->getTableName());
$joins = $this->buildJoinsClause($conn_r);
$where = $this->buildWhereClause($conn_r);
$group_by = $this->buildGroupClause($conn_r);
$having = $this->buildHavingClause($conn_r);
$order_by = $this->buildOrderClause($conn_r);
$limit = $this->buildLimitClause($conn_r);
return qsprintf(
$conn_r,
'(%Q %Q %Q %Q %Q %Q %Q %Q)',
$select,
$from,
$joins,
$where,
$group_by,
$having,
$order_by,
$limit);
}
/* -( Internals )---------------------------------------------------------- */
/**
* @task internal
*/
private function buildJoinsClause($conn_r) {
$joins = array();
if ($this->pathIDs) {
$path_table = new DifferentialAffectedPath();
$joins[] = qsprintf(
$conn_r,
'JOIN %T p ON p.revisionID = r.id',
$path_table->getTableName());
}
if ($this->commitHashes) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T hash_rel ON hash_rel.revisionID = r.id',
ArcanistDifferentialRevisionHash::TABLE_NAME);
}
if ($this->ccs) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T e_ccs ON e_ccs.src = r.phid '.
'AND e_ccs.type = %s '.
'AND e_ccs.dst in (%Ls)',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorObjectHasSubscriberEdgeType::EDGECONST,
$this->ccs);
}
if ($this->reviewers) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T reviewer ON reviewer.revisionPHID = r.phid
AND reviewer.reviewerStatus != %s
AND reviewer.reviewerPHID in (%Ls)',
id(new DifferentialReviewer())->getTableName(),
DifferentialReviewerStatus::STATUS_RESIGNED,
$this->reviewers);
}
if ($this->draftAuthors) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T has_draft ON has_draft.srcPHID = r.phid
AND has_draft.type = %s
AND has_draft.dstPHID IN (%Ls)',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorObjectHasDraftEdgeType::EDGECONST,
$this->draftAuthors);
}
if ($this->commitPHIDs) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T commits ON commits.revisionID = r.id',
DifferentialRevision::TABLE_COMMIT);
}
$joins[] = $this->buildJoinClauseParts($conn_r);
return $this->formatJoinClause($joins);
}
/**
* @task internal
*/
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->pathIDs) {
$path_clauses = array();
$repo_info = igroup($this->pathIDs, 'repositoryID');
foreach ($repo_info as $repository_id => $paths) {
$path_clauses[] = qsprintf(
$conn_r,
'(p.repositoryID = %d AND p.pathID IN (%Ld))',
$repository_id,
ipull($paths, 'pathID'));
}
$path_clauses = '('.implode(' OR ', $path_clauses).')';
$where[] = $path_clauses;
}
if ($this->authors) {
$where[] = qsprintf(
$conn_r,
'r.authorPHID IN (%Ls)',
$this->authors);
}
if ($this->revIDs) {
$where[] = qsprintf(
$conn_r,
'r.id IN (%Ld)',
$this->revIDs);
}
if ($this->repositoryPHIDs) {
$where[] = qsprintf(
$conn_r,
'r.repositoryPHID IN (%Ls)',
$this->repositoryPHIDs);
}
if ($this->commitHashes) {
$hash_clauses = array();
foreach ($this->commitHashes as $info) {
list($type, $hash) = $info;
$hash_clauses[] = qsprintf(
$conn_r,
'(hash_rel.type = %s AND hash_rel.hash = %s)',
$type,
$hash);
}
$hash_clauses = '('.implode(' OR ', $hash_clauses).')';
$where[] = $hash_clauses;
}
if ($this->commitPHIDs) {
$where[] = qsprintf(
$conn_r,
'commits.commitPHID IN (%Ls)',
$this->commitPHIDs);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'r.phid IN (%Ls)',
$this->phids);
}
if ($this->branches) {
$where[] = qsprintf(
$conn_r,
'r.branchName in (%Ls)',
$this->branches);
}
if ($this->updatedEpochMin !== null) {
$where[] = qsprintf(
$conn_r,
'r.dateModified >= %d',
$this->updatedEpochMin);
}
if ($this->updatedEpochMax !== null) {
$where[] = qsprintf(
$conn_r,
'r.dateModified <= %d',
$this->updatedEpochMax);
}
+ if ($this->createdEpochMin !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'r.dateCreated >= %d',
+ $this->createdEpochMin);
+ }
+
+ if ($this->createdEpochMax !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'r.dateCreated <= %d',
+ $this->createdEpochMax);
+ }
+
if ($this->statuses !== null) {
$where[] = qsprintf(
$conn_r,
'r.status in (%Ls)',
$this->statuses);
}
if ($this->isOpen !== null) {
if ($this->isOpen) {
$statuses = DifferentialLegacyQuery::getModernValues(
DifferentialLegacyQuery::STATUS_OPEN);
} else {
$statuses = DifferentialLegacyQuery::getModernValues(
DifferentialLegacyQuery::STATUS_CLOSED);
}
$where[] = qsprintf(
$conn_r,
'r.status in (%Ls)',
$statuses);
}
$where[] = $this->buildWhereClauseParts($conn_r);
return $this->formatWhereClause($where);
}
/**
* @task internal
*/
protected function shouldGroupQueryResultRows() {
$join_triggers = array_merge(
$this->pathIDs,
$this->ccs,
$this->reviewers);
if (count($join_triggers) > 1) {
return true;
}
return parent::shouldGroupQueryResultRows();
}
public function getBuiltinOrders() {
$orders = parent::getBuiltinOrders() + array(
'updated' => array(
'vector' => array('updated', 'id'),
'name' => pht('Date Updated (Latest First)'),
'aliases' => array(self::ORDER_MODIFIED),
),
'outdated' => array(
'vector' => array('-updated', '-id'),
'name' => pht('Date Updated (Oldest First)'),
),
);
// Alias the "newest" builtin to the historical key for it.
$orders['newest']['aliases'][] = self::ORDER_CREATED;
return $orders;
}
protected function getDefaultOrderVector() {
return array('updated', 'id');
}
public function getOrderableColumns() {
return array(
'updated' => array(
'table' => $this->getPrimaryTableAlias(),
'column' => 'dateModified',
'type' => 'int',
),
) + parent::getOrderableColumns();
}
protected function getPagingValueMap($cursor, array $keys) {
$revision = $this->loadCursorObject($cursor);
return array(
'id' => $revision->getID(),
'updated' => $revision->getDateModified(),
);
}
private function loadCommitPHIDs($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$commit_phids = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE revisionID IN (%Ld)',
DifferentialRevision::TABLE_COMMIT,
mpull($revisions, 'getID'));
$commit_phids = igroup($commit_phids, 'revisionID');
foreach ($revisions as $revision) {
$phids = idx($commit_phids, $revision->getID(), array());
$phids = ipull($phids, 'commitPHID');
$revision->attachCommitPHIDs($phids);
}
}
private function loadDiffIDs($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$diff_table = new DifferentialDiff();
$diff_ids = queryfx_all(
$conn_r,
'SELECT revisionID, id FROM %T WHERE revisionID IN (%Ld)
ORDER BY id DESC',
$diff_table->getTableName(),
mpull($revisions, 'getID'));
$diff_ids = igroup($diff_ids, 'revisionID');
foreach ($revisions as $revision) {
$ids = idx($diff_ids, $revision->getID(), array());
$ids = ipull($ids, 'id');
$revision->attachDiffIDs($ids);
}
}
private function loadActiveDiffs($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$diff_table = new DifferentialDiff();
$load_ids = array();
foreach ($revisions as $revision) {
$diffs = $revision->getDiffIDs();
if ($diffs) {
$load_ids[] = max($diffs);
}
}
$active_diffs = array();
if ($load_ids) {
$active_diffs = $diff_table->loadAllWhere(
'id IN (%Ld)',
$load_ids);
}
$active_diffs = mpull($active_diffs, null, 'getRevisionID');
foreach ($revisions as $revision) {
$revision->attachActiveDiff(idx($active_diffs, $revision->getID()));
}
}
private function loadHashes(
AphrontDatabaseConnection $conn_r,
array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE revisionID IN (%Ld)',
'differential_revisionhash',
mpull($revisions, 'getID'));
$data = igroup($data, 'revisionID');
foreach ($revisions as $revision) {
$hashes = idx($data, $revision->getID(), array());
$list = array();
foreach ($hashes as $hash) {
$list[] = array($hash['type'], $hash['hash']);
}
$revision->attachHashes($list);
}
}
private function loadReviewers(
AphrontDatabaseConnection $conn,
array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$reviewer_table = new DifferentialReviewer();
$reviewer_rows = queryfx_all(
$conn,
'SELECT * FROM %T WHERE revisionPHID IN (%Ls)
ORDER BY id ASC',
$reviewer_table->getTableName(),
mpull($revisions, 'getPHID'));
$reviewer_list = $reviewer_table->loadAllFromArray($reviewer_rows);
$reviewer_map = mgroup($reviewer_list, 'getRevisionPHID');
foreach ($reviewer_map as $key => $reviewers) {
$reviewer_map[$key] = mpull($reviewers, null, 'getReviewerPHID');
}
$viewer = $this->getViewer();
$viewer_phid = $viewer->getPHID();
$allow_key = 'differential.allow-self-accept';
$allow_self = PhabricatorEnv::getEnvConfig($allow_key);
// Figure out which of these reviewers the viewer has authority to act as.
if ($this->needReviewerAuthority && $viewer_phid) {
$authority = $this->loadReviewerAuthority(
$revisions,
$reviewer_map,
$allow_self);
}
foreach ($revisions as $revision) {
$reviewers = idx($reviewer_map, $revision->getPHID(), array());
foreach ($reviewers as $reviewer_phid => $reviewer) {
if ($this->needReviewerAuthority) {
if (!$viewer_phid) {
// Logged-out users never have authority.
$has_authority = false;
} else if ((!$allow_self) &&
($revision->getAuthorPHID() == $viewer_phid)) {
// The author can never have authority unless we allow self-accept.
$has_authority = false;
} else {
// Otherwise, look up whether the viewer has authority.
$has_authority = isset($authority[$reviewer_phid]);
}
$reviewer->attachAuthority($viewer, $has_authority);
}
$reviewers[$reviewer_phid] = $reviewer;
}
$revision->attachReviewers($reviewers);
}
}
private function loadReviewerAuthority(
array $revisions,
array $reviewers,
$allow_self) {
$revision_map = mpull($revisions, null, 'getPHID');
$viewer_phid = $this->getViewer()->getPHID();
// Find all the project/package reviewers which the user may have authority
// over.
$project_phids = array();
$package_phids = array();
$project_type = PhabricatorProjectProjectPHIDType::TYPECONST;
$package_type = PhabricatorOwnersPackagePHIDType::TYPECONST;
foreach ($reviewers as $revision_phid => $reviewer_list) {
if (!$allow_self) {
if ($revision_map[$revision_phid]->getAuthorPHID() == $viewer_phid) {
// If self-review isn't permitted, the user will never have
// authority over projects on revisions they authored because you
// can't accept your own revisions, so we don't need to load any
// data about these reviewers.
continue;
}
}
foreach ($reviewer_list as $reviewer_phid => $reviewer) {
$phid_type = phid_get_type($reviewer_phid);
if ($phid_type == $project_type) {
$project_phids[] = $reviewer_phid;
}
if ($phid_type == $package_type) {
$package_phids[] = $reviewer_phid;
}
}
}
// The viewer has authority over themselves.
$user_authority = array_fuse(array($viewer_phid));
// And over any projects they are a member of.
$project_authority = array();
if ($project_phids) {
$project_authority = id(new PhabricatorProjectQuery())
->setViewer($this->getViewer())
->withPHIDs($project_phids)
->withMemberPHIDs(array($viewer_phid))
->execute();
$project_authority = mpull($project_authority, 'getPHID');
$project_authority = array_fuse($project_authority);
}
// And over any packages they own.
$package_authority = array();
if ($package_phids) {
$package_authority = id(new PhabricatorOwnersPackageQuery())
->setViewer($this->getViewer())
->withPHIDs($package_phids)
->withAuthorityPHIDs(array($viewer_phid))
->execute();
$package_authority = mpull($package_authority, 'getPHID');
$package_authority = array_fuse($package_authority);
}
return $user_authority + $project_authority + $package_authority;
}
public function getQueryApplicationClass() {
return 'PhabricatorDifferentialApplication';
}
protected function getPrimaryTableAlias() {
return 'r';
}
}
diff --git a/src/applications/differential/query/DifferentialRevisionSearchEngine.php b/src/applications/differential/query/DifferentialRevisionSearchEngine.php
index 835291fba8..557ca23078 100644
--- a/src/applications/differential/query/DifferentialRevisionSearchEngine.php
+++ b/src/applications/differential/query/DifferentialRevisionSearchEngine.php
@@ -1,276 +1,292 @@
<?php
final class DifferentialRevisionSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Differential Revisions');
}
public function getApplicationClassName() {
return 'PhabricatorDifferentialApplication';
}
protected function newResultBuckets() {
return DifferentialRevisionResultBucket::getAllResultBuckets();
}
public function newQuery() {
return id(new DifferentialRevisionQuery())
->needFlags(true)
->needDrafts(true)
->needReviewers(true);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['responsiblePHIDs']) {
$query->withResponsibleUsers($map['responsiblePHIDs']);
}
if ($map['authorPHIDs']) {
$query->withAuthors($map['authorPHIDs']);
}
if ($map['reviewerPHIDs']) {
$query->withReviewers($map['reviewerPHIDs']);
}
if ($map['repositoryPHIDs']) {
$query->withRepositoryPHIDs($map['repositoryPHIDs']);
}
if ($map['statuses']) {
$query->withStatuses($map['statuses']);
}
+ if ($map['createdStart'] || $map['createdEnd']) {
+ $query->withCreatedEpochBetween(
+ $map['createdStart'],
+ $map['createdEnd']);
+ }
+
return $query;
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Responsible Users'))
->setKey('responsiblePHIDs')
->setAliases(array('responsiblePHID', 'responsibles', 'responsible'))
->setDatasource(new DifferentialResponsibleDatasource())
->setDescription(
pht('Find revisions that a given user is responsible for.')),
id(new PhabricatorUsersSearchField())
->setLabel(pht('Authors'))
->setKey('authorPHIDs')
->setAliases(array('author', 'authors', 'authorPHID'))
->setDescription(
pht('Find revisions with specific authors.')),
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Reviewers'))
->setKey('reviewerPHIDs')
->setAliases(array('reviewer', 'reviewers', 'reviewerPHID'))
->setDatasource(new DiffusionAuditorFunctionDatasource())
->setDescription(
pht('Find revisions with specific reviewers.')),
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Repositories'))
->setKey('repositoryPHIDs')
->setAliases(array('repository', 'repositories', 'repositoryPHID'))
->setDatasource(new DiffusionRepositoryFunctionDatasource())
->setDescription(
pht('Find revisions from specific repositories.')),
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Statuses'))
->setKey('statuses')
->setAliases(array('status'))
->setDatasource(new DifferentialRevisionStatusFunctionDatasource())
->setDescription(
pht('Find revisions with particular statuses.')),
+ id(new PhabricatorSearchDateField())
+ ->setLabel(pht('Created After'))
+ ->setKey('createdStart')
+ ->setDescription(
+ pht('Find revisions created at or after a particular time.')),
+ id(new PhabricatorSearchDateField())
+ ->setLabel(pht('Created Before'))
+ ->setKey('createdEnd')
+ ->setDescription(
+ pht('Find revisions created at or before a particular time.')),
);
}
protected function getURI($path) {
return '/differential/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
if ($this->requireViewer()->isLoggedIn()) {
$names['active'] = pht('Active Revisions');
$names['authored'] = pht('Authored');
}
$names['all'] = pht('All Revisions');
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
$viewer = $this->requireViewer();
switch ($query_key) {
case 'active':
$bucket_key = DifferentialRevisionRequiredActionResultBucket::BUCKETKEY;
return $query
->setParameter('responsiblePHIDs', array($viewer->getPHID()))
->setParameter('statuses', array('open()'))
->setParameter('bucket', $bucket_key);
case 'authored':
return $query
->setParameter('authorPHIDs', array($viewer->getPHID()));
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
private function getStatusOptions() {
return array(
DifferentialLegacyQuery::STATUS_ANY => pht('All'),
DifferentialLegacyQuery::STATUS_OPEN => pht('Open'),
DifferentialLegacyQuery::STATUS_ACCEPTED => pht('Accepted'),
DifferentialLegacyQuery::STATUS_NEEDS_REVIEW => pht('Needs Review'),
DifferentialLegacyQuery::STATUS_NEEDS_REVISION => pht('Needs Revision'),
DifferentialLegacyQuery::STATUS_CLOSED => pht('Closed'),
DifferentialLegacyQuery::STATUS_ABANDONED => pht('Abandoned'),
);
}
protected function renderResultList(
array $revisions,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($revisions, 'DifferentialRevision');
$viewer = $this->requireViewer();
$template = id(new DifferentialRevisionListView())
->setViewer($viewer)
->setNoBox($this->isPanelContext());
$bucket = $this->getResultBucket($query);
$unlanded = $this->loadUnlandedDependencies($revisions);
$views = array();
if ($bucket) {
$bucket->setViewer($viewer);
try {
$groups = $bucket->newResultGroups($query, $revisions);
foreach ($groups as $group) {
// Don't show groups in Dashboard Panels
if ($group->getObjects() || !$this->isPanelContext()) {
$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);
}
if (!$views) {
$views[] = id(new DifferentialRevisionListView())
->setViewer($viewer)
->setNoDataString(pht('No revisions found.'));
}
foreach ($views as $view) {
$view->setUnlandedDependencies($unlanded);
}
if (count($views) == 1) {
// Reduce this to a PHUIObjectItemListView so we can get the free
// support from ApplicationSearch.
$list = head($views)->render();
} else {
$list = $views;
}
$result = new PhabricatorApplicationSearchResultView();
$result->setContent($list);
return $result;
}
protected function getNewUserBody() {
$create_button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Create a Diff'))
->setHref('/differential/diff/create/')
->setColor(PHUIButtonView::GREEN);
$icon = $this->getApplication()->getIcon();
$app_name = $this->getApplication()->getName();
$view = id(new PHUIBigInfoView())
->setIcon($icon)
->setTitle(pht('Welcome to %s', $app_name))
->setDescription(
pht('Pre-commit code review. Revisions that are waiting on your input '.
'will appear here.'))
->addAction($create_button);
return $view;
}
private function loadUnlandedDependencies(array $revisions) {
$phids = array();
foreach ($revisions as $revision) {
if (!$revision->isAccepted()) {
continue;
}
$phids[] = $revision->getPHID();
}
if (!$phids) {
return array();
}
$query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($phids)
->withEdgeTypes(
array(
DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST,
));
$query->execute();
$revision_phids = $query->getDestinationPHIDs();
if (!$revision_phids) {
return array();
}
$viewer = $this->requireViewer();
$blocking_revisions = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withPHIDs($revision_phids)
->withIsOpen(true)
->execute();
$blocking_revisions = mpull($blocking_revisions, null, 'getPHID');
$result = array();
foreach ($revisions as $revision) {
$revision_phid = $revision->getPHID();
$blocking_phids = $query->getDestinationPHIDs(array($revision_phid));
$blocking = array_select_keys($blocking_revisions, $blocking_phids);
if ($blocking) {
$result[$revision_phid] = $blocking;
}
}
return $result;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 19:19 (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1127904
Default Alt Text
(37 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment