1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-16 11:52:40 +01:00
phorge-phorge/src/applications/differential/customfield/DifferentialReviewersField.php

327 lines
8.6 KiB
PHP
Raw Normal View History

<?php
final class DifferentialReviewersField
extends DifferentialCoreCustomField {
public function getFieldKey() {
return 'differential:reviewers';
}
public function getFieldKeyForConduit() {
return 'reviewerPHIDs';
}
public function getFieldName() {
return pht('Reviewers');
}
public function getFieldDescription() {
return pht('Manage reviewers.');
}
protected function readValueFromRevision(
DifferentialRevision $revision) {
return $revision->getReviewerStatus();
}
public function getNewValueForApplicationTransactions() {
$specs = array();
foreach ($this->getValue() as $reviewer) {
$specs[$reviewer->getReviewerPHID()] = array(
'data' => $reviewer->getEdgeData(),
);
}
return array('=' => $specs);
}
public function readValueFromRequest(AphrontRequest $request) {
$datasource = id(new DifferentialBlockingReviewerDatasource())
->setViewer($request->getViewer());
$new_phids = $request->getArr($this->getFieldKey());
$new_phids = $datasource->evaluateTokens($new_phids);
$reviewers = array();
foreach ($new_phids as $spec) {
if (!is_array($spec)) {
$reviewers[$spec] = DifferentialReviewerStatus::STATUS_ADDED;
} else {
$reviewers[$spec['phid']] = $spec['type'];
}
}
$this->updateReviewers($this->getValue(), $reviewers);
}
private function updateReviewers(array $old_reviewers, array $new_map) {
// Compute a new set of reviewer objects. We're going to respect the new
// reviewer order, add or remove any new or missing reviewers, and respect
// any blocking or unblocking changes. For reviewers who were there before
// and are still there, we're going to keep the old value because it
// may be something like "Accept", "Reject", etc.
$old_map = mpull($old_reviewers, 'getStatus', 'getReviewerPHID');
$status_blocking = DifferentialReviewerStatus::STATUS_BLOCKING;
$new_reviewers = array();
foreach ($new_map as $phid => $new) {
$old = idx($old_map, $phid);
// If we have an old status and this didn't make the reviewer blocking
// or nonblocking, just retain the old status. This makes sure we don't
// throw away rejects, accepts, etc.
if ($old) {
$is_block = ($old !== $status_blocking && $new === $status_blocking);
$is_unblock = ($old === $status_blocking && $new !== $status_blocking);
if (!$is_block && !$is_unblock) {
$new_reviewers[$phid] = $old;
continue;
}
}
$new_reviewers[$phid] = $new;
}
foreach ($new_reviewers as $phid => $status) {
$new_reviewers[$phid] = new DifferentialReviewer(
$phid,
array(
'status' => $status,
));
}
$this->setValue($new_reviewers);
}
Convert all tokenizers to take token/scalar inputs Summary: Ref T7689. Ref T4100. This advances the goals of removing `loadViewerHandles()` (only 67 callsites remain!) and letting tokenizers some day take token functions like `viewer()` and `members(differential)`. Test Plan: - Sent a new message; used "To". - I simplified the cancel URI construction slightly because it's moot in all normal cases. - Edited a thread; used "Add Participants". - Searched rooms; used "Participants". - Searched countdowns; used "Authors". - Created a diff; used "Repository". - Edited a revision; edited "Projects"; edited "Reveiwers"; edited "Subscribers". - Searched for revisions; edited "responsible users"; "authors"; "reviwers"; "subscribers"; "repositories". - Added revision comments; edited "Add Reveiwers"; "Add Subscribers". - Commented on a commit; edited "Add Auditors"; "Add subscribers". - Edited a commit; edited "Projects". - Edited a repository; edited "Projects". - Searched feed, used "include Users"; "include Proejcts". - Searched files, used "authors". - Edited initiative; edited "Projects". - Searched backers; used "Backers". - Searched initiatives; used "Owners". - Edited build plans; edited "Run Command". - Searched Herald; used "Authors". - Added signature exemption in Legalpad. - Searhced legalpad; used "creators"; used "contributors". - Searched signatures; used "documents"; used "signers". - Created meme. - Searched macros; used "Authors". - Used "Projects" in Maniphest reports. - Used Maniphest comment actions. - Edited Maniphest tasks; edited "Assigned To"; edited "CC"; edited "projects". - Used "parent" in Maniphest task creation workflow. - Searched for projects; used "assigned to"; "in any projec"; "in all projects"; "not in projects"; "in users' projects"; "authors"; "subscribers". - Edited Maniphest bug filing domains, used "Default Author". - Searched for OAuth applications, used "Creators". - Edited Owners pacakge; edited "Primary Owner"; edited "Owners". - Searched for Owners packages; used "Owner". - OMG this UI is OLD - Edited a paste; edited "Projects". - Searched for paste; used "Authors". - Searched user activity log; used "Actors"; used "Users". - Edited a mock; edited "Projects"; edited "CC". - Searched for mocks; used "Authors". - Edited Phortune account; edited "Members". - Edited Phortune merchant account; edited "Members". - Searched Phrequent; used "Users". - Edited Ponder question; sued "projects". - Searched Ponder; used "Authors"; used "Answered By". - Added project members. - Searched for projects; used "Members". - Edited a Releeph product; edited "Pushers". - Searched pull requests; searched "Requestors". - Edited an arcanist project; used "Uses Symbols From". - Searhced push logs; used "Repositories"; used "Pushers". - Searched repositories; used "In nay project". - Used global search; used Authors/owners/Subscribers/In Any Project. - Edited a slowvote; used "Projects". - Searched slovotes; used "Authors". - Created a custom "Users" field; edited and searched for it. - Made a whole lot of typos in this list. ^^^^^^ Did not test: - Lint is nontrivial to test locally, I'll test it in production. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T4100, T7689 Differential Revision: https://secure.phabricator.com/D12224
2015-03-31 23:10:55 +02:00
public function renderEditControl(array $handles) {
$status_blocking = DifferentialReviewerStatus::STATUS_BLOCKING;
$value = array();
foreach ($this->getValue() as $reviewer) {
$phid = $reviewer->getReviewerPHID();
if ($reviewer->getStatus() == $status_blocking) {
$value[] = 'blocking('.$phid.')';
} else {
$value[] = $phid;
}
}
return id(new AphrontFormTokenizerControl())
Convert all tokenizers to take token/scalar inputs Summary: Ref T7689. Ref T4100. This advances the goals of removing `loadViewerHandles()` (only 67 callsites remain!) and letting tokenizers some day take token functions like `viewer()` and `members(differential)`. Test Plan: - Sent a new message; used "To". - I simplified the cancel URI construction slightly because it's moot in all normal cases. - Edited a thread; used "Add Participants". - Searched rooms; used "Participants". - Searched countdowns; used "Authors". - Created a diff; used "Repository". - Edited a revision; edited "Projects"; edited "Reveiwers"; edited "Subscribers". - Searched for revisions; edited "responsible users"; "authors"; "reviwers"; "subscribers"; "repositories". - Added revision comments; edited "Add Reveiwers"; "Add Subscribers". - Commented on a commit; edited "Add Auditors"; "Add subscribers". - Edited a commit; edited "Projects". - Edited a repository; edited "Projects". - Searched feed, used "include Users"; "include Proejcts". - Searched files, used "authors". - Edited initiative; edited "Projects". - Searched backers; used "Backers". - Searched initiatives; used "Owners". - Edited build plans; edited "Run Command". - Searched Herald; used "Authors". - Added signature exemption in Legalpad. - Searhced legalpad; used "creators"; used "contributors". - Searched signatures; used "documents"; used "signers". - Created meme. - Searched macros; used "Authors". - Used "Projects" in Maniphest reports. - Used Maniphest comment actions. - Edited Maniphest tasks; edited "Assigned To"; edited "CC"; edited "projects". - Used "parent" in Maniphest task creation workflow. - Searched for projects; used "assigned to"; "in any projec"; "in all projects"; "not in projects"; "in users' projects"; "authors"; "subscribers". - Edited Maniphest bug filing domains, used "Default Author". - Searched for OAuth applications, used "Creators". - Edited Owners pacakge; edited "Primary Owner"; edited "Owners". - Searched for Owners packages; used "Owner". - OMG this UI is OLD - Edited a paste; edited "Projects". - Searched for paste; used "Authors". - Searched user activity log; used "Actors"; used "Users". - Edited a mock; edited "Projects"; edited "CC". - Searched for mocks; used "Authors". - Edited Phortune account; edited "Members". - Edited Phortune merchant account; edited "Members". - Searched Phrequent; used "Users". - Edited Ponder question; sued "projects". - Searched Ponder; used "Authors"; used "Answered By". - Added project members. - Searched for projects; used "Members". - Edited a Releeph product; edited "Pushers". - Searched pull requests; searched "Requestors". - Edited an arcanist project; used "Uses Symbols From". - Searhced push logs; used "Repositories"; used "Pushers". - Searched repositories; used "In nay project". - Used global search; used Authors/owners/Subscribers/In Any Project. - Edited a slowvote; used "Projects". - Searched slovotes; used "Authors". - Created a custom "Users" field; edited and searched for it. - Made a whole lot of typos in this list. ^^^^^^ Did not test: - Lint is nontrivial to test locally, I'll test it in production. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T4100, T7689 Differential Revision: https://secure.phabricator.com/D12224
2015-03-31 23:10:55 +02:00
->setUser($this->getViewer())
->setName($this->getFieldKey())
->setDatasource(new DifferentialReviewerDatasource())
->setValue($value)
->setError($this->getFieldError())
->setLabel($this->getFieldName());
}
public function getApplicationTransactionType() {
return PhabricatorTransactions::TYPE_EDGE;
}
public function getApplicationTransactionMetadata() {
return array(
'edge:type' => DifferentialRevisionHasReviewerEdgeType::EDGECONST,
);
}
public function shouldAppearInPropertyView() {
return true;
}
public function renderPropertyViewLabel() {
return $this->getFieldName();
}
public function getRequiredHandlePHIDsForPropertyView() {
return mpull($this->getUserReviewers(), 'getReviewerPHID');
}
public function renderPropertyViewValue(array $handles) {
$reviewers = $this->getUserReviewers();
if (!$reviewers) {
return phutil_tag('em', array(), pht('None'));
}
$view = id(new DifferentialReviewersView())
->setUser($this->getViewer())
->setReviewers($reviewers)
->setHandles($handles);
// TODO: Active diff stuff.
return $view;
}
private function getUserReviewers() {
$reviewers = array();
foreach ($this->getObject()->getReviewerStatus() as $reviewer) {
if ($reviewer->isUser()) {
$reviewers[] = $reviewer;
}
}
return $reviewers;
}
public function shouldAppearInCommitMessage() {
return true;
}
public function shouldAppearInCommitMessageTemplate() {
return true;
}
public function getCommitMessageLabels() {
return array(
'Reviewer',
'Reviewers',
);
}
public function parseValueFromCommitMessage($value) {
$results = $this->parseObjectList(
$value,
array(
PhabricatorPeopleUserPHIDType::TYPECONST,
PhabricatorProjectProjectPHIDType::TYPECONST,
PhabricatorOwnersPackagePHIDType::TYPECONST,
),
false,
array('!'));
return $this->flattenReviewers($results);
}
public function getRequiredHandlePHIDsForCommitMessage() {
return mpull($this->getValue(), 'getReviewerPHID');
}
public function readValueFromCommitMessage($value) {
$value = $this->inflateReviewers($value);
$reviewers = array();
foreach ($value as $spec) {
$phid = $spec['phid'];
$is_blocking = isset($spec['suffixes']['!']);
if ($is_blocking) {
$status = DifferentialReviewerStatus::STATUS_BLOCKING;
} else {
$status = DifferentialReviewerStatus::STATUS_ADDED;
}
$reviewers[$phid] = $status;
}
$this->updateReviewers(
$this->getObject()->getReviewerStatus(),
$reviewers);
return $this;
}
public function renderCommitMessageValue(array $handles) {
$suffixes = array();
$status_blocking = DifferentialReviewerStatus::STATUS_BLOCKING;
foreach ($this->getValue() as $reviewer) {
if ($reviewer->getStatus() == $status_blocking) {
$phid = $reviewer->getReviewerPHID();
$suffixes[$phid] = '!';
}
}
return $this->renderObjectList($handles, $suffixes);
}
public function validateCommitMessageValue($value) {
$author_phid = $this->getObject()->getAuthorPHID();
$config_self_accept_key = 'differential.allow-self-accept';
$allow_self_accept = PhabricatorEnv::getEnvConfig($config_self_accept_key);
foreach ($value as $spec) {
$phid = $spec['phid'];
if (($phid == $author_phid) && !$allow_self_accept) {
throw new DifferentialFieldValidationException(
pht('The author of a revision can not be a reviewer.'));
}
}
}
public function getRequiredHandlePHIDsForRevisionHeaderWarnings() {
return mpull($this->getValue(), 'getReviewerPHID');
}
public function getWarningsForRevisionHeader(array $handles) {
$revision = $this->getObject();
$status_needs_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
if ($revision->getStatus() != $status_needs_review) {
return array();
}
foreach ($this->getValue() as $reviewer) {
if (!$handles[$reviewer->getReviewerPHID()]->isDisabled()) {
return array();
}
}
$warnings = array();
if ($this->getValue()) {
$warnings[] = pht(
'This revision needs review, but all specified reviewers are '.
'disabled or inactive.');
} else {
$warnings[] = pht(
'This revision needs review, but there are no reviewers specified.');
}
return $warnings;
}
public function getProTips() {
return array(
pht(
'You can mark a reviewer as blocking by adding an exclamation '.
'mark ("!") after their name.'),
);
}
private function flattenReviewers(array $values) {
// NOTE: For now, `arc` relies on this field returning only scalars, so we
// need to reduce the results into scalars. See T10981.
$result = array();
foreach ($values as $value) {
$result[] = $value['phid'].implode('', array_keys($value['suffixes']));
}
return $result;
}
private function inflateReviewers(array $values) {
$result = array();
foreach ($values as $value) {
if (substr($value, -1) == '!') {
$value = substr($value, 0, -1);
$suffixes = array('!' => '!');
} else {
$suffixes = array();
}
$result[] = array(
'phid' => $value,
'suffixes' => $suffixes,
);
}
return $result;
}
}