mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-20 20:40:56 +01:00
Move determination of reviewer authority into DifferentialRevisionQuery
Summary: Ref T1279. We currently determine reviewers at display time, but this is bad for several reasons: - It puts queries very close to the display layer. - We have to query for each revision if we want to figure out authority for several. - We need to figure it out in several places, so we'll end up with copies of this logic. - The logic isn't trivial (exceptions for the viewer, exceptions to that rule for install configuration). - We already do this "figure it out when we need it" stuff in Diffusion for audits and it's really bad: we have half-working copies of the logic spread all over the place. Instead, put it in the Query. Callers query for it and get the data attached to the reviewer objects. Test Plan: - Looked at some revisions, verified the correct lines were highlighted. - Looked at a revision I created and verified that projects I was a member of were not highlighted. - With self-accept enabled, these //are// highlighted. - Looked at a revision I did not create and verified that projects I was a member of were highlighted. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T1279 Differential Revision: https://secure.phabricator.com/D7241
This commit is contained in:
parent
515f9a36ab
commit
3d3d3b6d80
6 changed files with 130 additions and 42 deletions
|
@ -30,6 +30,7 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
||||||
->setViewer($request->getUser())
|
->setViewer($request->getUser())
|
||||||
->needRelationships(true)
|
->needRelationships(true)
|
||||||
->needReviewerStatus(true)
|
->needReviewerStatus(true)
|
||||||
|
->needReviewerAuthority(true)
|
||||||
->executeOne();
|
->executeOne();
|
||||||
|
|
||||||
if (!$revision) {
|
if (!$revision) {
|
||||||
|
|
|
@ -27,24 +27,10 @@ final class DifferentialProjectReviewersFieldSpecification
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$highlight = array();
|
|
||||||
if ($this->getUser()->getPHID() != $this->getRevision()->getAuthorPHID()) {
|
|
||||||
// Determine which of these projects the viewer is a member of, so we can
|
|
||||||
// highlight them. (If the viewer is the author, skip this since they
|
|
||||||
// can't review.)
|
|
||||||
$phids = mpull($reviewers, 'getReviewerPHID');
|
|
||||||
$projects = id(new PhabricatorProjectQuery())
|
|
||||||
->setViewer($this->getUser())
|
|
||||||
->withPHIDs($phids)
|
|
||||||
->withMemberPHIDs(array($this->getUser()->getPHID()))
|
|
||||||
->execute();
|
|
||||||
$highlight = mpull($projects, 'getPHID');
|
|
||||||
}
|
|
||||||
|
|
||||||
$view = id(new DifferentialReviewersView())
|
$view = id(new DifferentialReviewersView())
|
||||||
|
->setUser($this->getUser())
|
||||||
->setReviewers($reviewers)
|
->setReviewers($reviewers)
|
||||||
->setHandles($this->getLoadedHandles())
|
->setHandles($this->getLoadedHandles());
|
||||||
->setHighlightPHIDs($highlight);
|
|
||||||
|
|
||||||
$diff = $this->getRevision()->loadActiveDiff();
|
$diff = $this->getRevision()->loadActiveDiff();
|
||||||
if ($diff) {
|
if ($diff) {
|
||||||
|
|
|
@ -32,8 +32,8 @@ final class DifferentialReviewersFieldSpecification
|
||||||
}
|
}
|
||||||
|
|
||||||
$view = id(new DifferentialReviewersView())
|
$view = id(new DifferentialReviewersView())
|
||||||
|
->setUser($this->getUser())
|
||||||
->setReviewers($reviewers)
|
->setReviewers($reviewers)
|
||||||
->setHighlightPHIDs(array($this->getUser()->getPHID()))
|
|
||||||
->setHandles($this->getLoadedHandles());
|
->setHandles($this->getLoadedHandles());
|
||||||
|
|
||||||
$diff = $this->getRevision()->loadActiveDiff();
|
$diff = $this->getRevision()->loadActiveDiff();
|
||||||
|
|
|
@ -59,6 +59,7 @@ final class DifferentialRevisionQuery
|
||||||
private $needCommitPHIDs = false;
|
private $needCommitPHIDs = false;
|
||||||
private $needHashes = false;
|
private $needHashes = false;
|
||||||
private $needReviewerStatus = false;
|
private $needReviewerStatus = false;
|
||||||
|
private $needReviewerAuthority;
|
||||||
|
|
||||||
private $buildingGlobalOrder;
|
private $buildingGlobalOrder;
|
||||||
|
|
||||||
|
@ -347,6 +348,21 @@ final class DifferentialRevisionQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Query Execution )---------------------------------------------------- */
|
/* -( Query Execution )---------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
@ -450,7 +466,7 @@ final class DifferentialRevisionQuery
|
||||||
$this->loadHashes($conn_r, $revisions);
|
$this->loadHashes($conn_r, $revisions);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->needReviewerStatus) {
|
if ($this->needReviewerStatus || $this->needReviewerAuthority) {
|
||||||
$this->loadReviewers($conn_r, $revisions);
|
$this->loadReviewers($conn_r, $revisions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1040,15 +1056,42 @@ final class DifferentialRevisionQuery
|
||||||
->setOrder(PhabricatorEdgeQuery::ORDER_OLDEST_FIRST)
|
->setOrder(PhabricatorEdgeQuery::ORDER_OLDEST_FIRST)
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$viewer_phid = $viewer->getPHID();
|
||||||
|
|
||||||
|
// Figure out which of these reviewers the viewer has authority to act as.
|
||||||
|
if ($this->needReviewerAuthority && $viewer_phid) {
|
||||||
|
$allow_key = 'differential.allow-self-accept';
|
||||||
|
$allow_self = PhabricatorEnv::getEnvConfig($allow_key);
|
||||||
|
$authority = $this->loadReviewerAuthority(
|
||||||
|
$revisions,
|
||||||
|
$edges,
|
||||||
|
$allow_self);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($revisions as $revision) {
|
foreach ($revisions as $revision) {
|
||||||
$revision_edges = $edges[$revision->getPHID()][$edge_type];
|
$revision_edges = $edges[$revision->getPHID()][$edge_type];
|
||||||
$reviewers = array();
|
$reviewers = array();
|
||||||
foreach ($revision_edges as $user_phid => $edge) {
|
foreach ($revision_edges as $reviewer_phid => $edge) {
|
||||||
$data = $edge['data'];
|
$reviewer = new DifferentialReviewer($reviewer_phid, $edge['data']);
|
||||||
$reviewers[] = new DifferentialReviewer(
|
|
||||||
$user_phid,
|
if ($this->needReviewerAuthority) {
|
||||||
idx($data, 'status'),
|
if (!$viewer_phid) {
|
||||||
idx($data, 'diff'));
|
// 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 th viewer has authority.
|
||||||
|
$has_authority = isset($authority[$reviewer_phid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$reviewer->attachAuthority($viewer, $has_authority);
|
||||||
|
}
|
||||||
|
|
||||||
|
$reviewers[$reviewer_phid] = $reviewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
$revision->attachReviewerStatus($reviewers);
|
$revision->attachReviewerStatus($reviewers);
|
||||||
|
@ -1091,5 +1134,55 @@ final class DifferentialRevisionQuery
|
||||||
return array($blocking, $active, $waiting);
|
return array($blocking, $active, $waiting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function loadReviewerAuthority(
|
||||||
|
array $revisions,
|
||||||
|
array $edges,
|
||||||
|
$allow_self) {
|
||||||
|
|
||||||
|
$revision_map = mpull($revisions, null, 'getPHID');
|
||||||
|
$viewer_phid = $this->getViewer()->getPHID();
|
||||||
|
|
||||||
|
// Find all the project reviewers which the user may have authority over.
|
||||||
|
$project_phids = array();
|
||||||
|
$project_type = PhabricatorProjectPHIDTypeProject::TYPECONST;
|
||||||
|
$edge_type = PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER;
|
||||||
|
foreach ($edges as $src => $types) {
|
||||||
|
if (!$allow_self) {
|
||||||
|
if ($revision_map[$src]->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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$edge_data = idx($types, $edge_type, array());
|
||||||
|
foreach ($edge_data as $dst => $data) {
|
||||||
|
if (phid_get_type($dst) == $project_type) {
|
||||||
|
$project_phids[] = $dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, figure out which of these projects the viewer is actually 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, the viewer has authority over themselves.
|
||||||
|
return array(
|
||||||
|
$viewer_phid => true,
|
||||||
|
) + array_fuse($project_authority);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
|
|
||||||
final class DifferentialReviewer {
|
final class DifferentialReviewer {
|
||||||
|
|
||||||
protected $reviewerPHID;
|
private $reviewerPHID;
|
||||||
protected $status;
|
private $status;
|
||||||
protected $diffID;
|
private $diffID;
|
||||||
|
private $authority = array();
|
||||||
|
|
||||||
public function __construct($reviewer_phid, $status, $diff_id = null) {
|
public function __construct($reviewer_phid, array $edge_data) {
|
||||||
$this->reviewerPHID = $reviewer_phid;
|
$this->reviewerPHID = $reviewer_phid;
|
||||||
$this->status = $status;
|
$this->status = idx($edge_data, 'status');
|
||||||
$this->diffID = $diff_id;
|
$this->diffID = idx($edge_data, 'diff');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getReviewerPHID() {
|
public function getReviewerPHID() {
|
||||||
|
@ -29,4 +30,20 @@ final class DifferentialReviewer {
|
||||||
return (phid_get_type($this->getReviewerPHID()) == $user_type);
|
return (phid_get_type($this->getReviewerPHID()) == $user_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function attachAuthority(PhabricatorUser $user, $has_authority) {
|
||||||
|
$this->authority[$user->getPHID()] = $has_authority;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasAuthority(PhabricatorUser $viewer) {
|
||||||
|
// It would be nice to use assertAttachedKey() here, but we don't extend
|
||||||
|
// PhabricatorLiskDAO, and faking that seems sketchy.
|
||||||
|
|
||||||
|
$viewer_phid = $viewer->getPHID();
|
||||||
|
if (!array_key_exists($viewer_phid, $this->authority)) {
|
||||||
|
throw new Exception("You must attachAuthority() first!");
|
||||||
|
}
|
||||||
|
return $this->authority[$viewer_phid];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ final class DifferentialReviewersView extends AphrontView {
|
||||||
private $reviewers;
|
private $reviewers;
|
||||||
private $handles;
|
private $handles;
|
||||||
private $diff;
|
private $diff;
|
||||||
private $highlightPHIDs = array();
|
|
||||||
|
|
||||||
public function setReviewers(array $reviewers) {
|
public function setReviewers(array $reviewers) {
|
||||||
assert_instances_of($reviewers, 'DifferentialReviewer');
|
assert_instances_of($reviewers, 'DifferentialReviewer');
|
||||||
|
@ -24,16 +23,10 @@ final class DifferentialReviewersView extends AphrontView {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setHighlightPHIDs(array $phids) {
|
|
||||||
$this->highlightPHIDs = $phids;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
|
$viewer = $this->getUser();
|
||||||
|
|
||||||
$view = new PHUIStatusListView();
|
$view = new PHUIStatusListView();
|
||||||
|
|
||||||
$highlighted = array_fuse($this->highlightPHIDs);
|
|
||||||
|
|
||||||
foreach ($this->reviewers as $reviewer) {
|
foreach ($this->reviewers as $reviewer) {
|
||||||
$phid = $reviewer->getReviewerPHID();
|
$phid = $reviewer->getReviewerPHID();
|
||||||
$handle = $this->handles[$phid];
|
$handle = $this->handles[$phid];
|
||||||
|
@ -46,9 +39,7 @@ final class DifferentialReviewersView extends AphrontView {
|
||||||
|
|
||||||
$item = new PHUIStatusItemView();
|
$item = new PHUIStatusItemView();
|
||||||
|
|
||||||
if (isset($highlighted[$phid])) {
|
$item->setHighlighted($reviewer->hasAuthority($viewer));
|
||||||
$item->setHighlighted(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($reviewer->getStatus()) {
|
switch ($reviewer->getStatus()) {
|
||||||
case DifferentialReviewerStatus::STATUS_ADDED:
|
case DifferentialReviewerStatus::STATUS_ADDED:
|
||||||
|
|
Loading…
Reference in a new issue