mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-01 19:22:42 +01:00
Implement "Resign" action against ApplicationTransactions
Summary: Ref T2222. This introduces two small new concepts: - `expandTransactions()`: allows a transaction to expand into several transactions. For example, "resign" adds a "remove reviewers" transaction. - We have some other cases which could use this, but currently hard-code things outside of the `Editor`. - One example is that in Maniphest, closing a task implies claiming it if it is unowned. - `setIgnoreOnNoEffect()`: The whole Editor can be set to continue or stop if any transactions have no effect, but this allows the behavior to be refined at the individual transaction level. This is primarily to make the UX less confusing, so the user gets only a single relevant error instead of one for each expanded transaction. Otherwise, this is pretty straightforward. Test Plan: Rigged comment form to use SavePro controller, enabled resign action, then tried to resign from a bunch of stuff. {F117743} Reviewers: btrahan Reviewed By: btrahan CC: chad, aran Maniphest Tasks: T2222 Differential Revision: https://secure.phabricator.com/D8328
This commit is contained in:
parent
72a062d802
commit
9292433dae
9 changed files with 151 additions and 11 deletions
|
@ -7,7 +7,7 @@
|
||||||
return array(
|
return array(
|
||||||
'names' =>
|
'names' =>
|
||||||
array(
|
array(
|
||||||
'core.pkg.css' => '8c8b76a8',
|
'core.pkg.css' => '76aa3fcd',
|
||||||
'core.pkg.js' => '8f7aa2c3',
|
'core.pkg.js' => '8f7aa2c3',
|
||||||
'darkconsole.pkg.js' => 'ca8671ce',
|
'darkconsole.pkg.js' => 'ca8671ce',
|
||||||
'differential.pkg.css' => '6aef439e',
|
'differential.pkg.css' => '6aef439e',
|
||||||
|
@ -21,7 +21,7 @@ return array(
|
||||||
'rsrc/css/aphront/aphront-notes.css' => '6acadd3f',
|
'rsrc/css/aphront/aphront-notes.css' => '6acadd3f',
|
||||||
'rsrc/css/aphront/context-bar.css' => '1c3b0529',
|
'rsrc/css/aphront/context-bar.css' => '1c3b0529',
|
||||||
'rsrc/css/aphront/dark-console.css' => '6378ef3d',
|
'rsrc/css/aphront/dark-console.css' => '6378ef3d',
|
||||||
'rsrc/css/aphront/dialog-view.css' => 'dd9db96c',
|
'rsrc/css/aphront/dialog-view.css' => 'c01d24b4',
|
||||||
'rsrc/css/aphront/error-view.css' => '16cd9949',
|
'rsrc/css/aphront/error-view.css' => '16cd9949',
|
||||||
'rsrc/css/aphront/lightbox-attachment.css' => '686f8885',
|
'rsrc/css/aphront/lightbox-attachment.css' => '686f8885',
|
||||||
'rsrc/css/aphront/list-filter-view.css' => 'ef989c67',
|
'rsrc/css/aphront/list-filter-view.css' => 'ef989c67',
|
||||||
|
@ -483,7 +483,7 @@ return array(
|
||||||
'aphront-bars' => '231ac33c',
|
'aphront-bars' => '231ac33c',
|
||||||
'aphront-contextbar-view-css' => '1c3b0529',
|
'aphront-contextbar-view-css' => '1c3b0529',
|
||||||
'aphront-dark-console-css' => '6378ef3d',
|
'aphront-dark-console-css' => '6378ef3d',
|
||||||
'aphront-dialog-view-css' => 'dd9db96c',
|
'aphront-dialog-view-css' => 'c01d24b4',
|
||||||
'aphront-error-view-css' => '16cd9949',
|
'aphront-error-view-css' => '16cd9949',
|
||||||
'aphront-list-filter-view-css' => 'ef989c67',
|
'aphront-list-filter-view-css' => 'ef989c67',
|
||||||
'aphront-multi-column-view-css' => '12f65921',
|
'aphront-multi-column-view-css' => '12f65921',
|
||||||
|
|
|
@ -94,7 +94,7 @@ final class DifferentialCommentSaveControllerPro
|
||||||
->withPHIDs($inline_phids)
|
->withPHIDs($inline_phids)
|
||||||
->execute();
|
->execute();
|
||||||
} else {
|
} else {
|
||||||
$inlines = null;
|
$inlines = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($inlines as $inline) {
|
foreach ($inlines as $inline) {
|
||||||
|
|
|
@ -82,6 +82,14 @@ final class DifferentialTransactionEditor
|
||||||
return ($object->getStatus() != $status_revision);
|
return ($object->getStatus() != $status_revision);
|
||||||
case DifferentialAction::ACTION_REQUEST:
|
case DifferentialAction::ACTION_REQUEST:
|
||||||
return ($object->getStatus() != $status_review);
|
return ($object->getStatus() != $status_review);
|
||||||
|
case DifferentialAction::ACTION_RESIGN:
|
||||||
|
$actor_phid = $this->getActor()->getPHID();
|
||||||
|
foreach ($object->getReviewerStatus() as $reviewer) {
|
||||||
|
if ($reviewer->getReviewerPHID() == $actor_phid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +121,9 @@ final class DifferentialTransactionEditor
|
||||||
$status_revision = ArcanistDifferentialRevisionStatus::NEEDS_REVISION;
|
$status_revision = ArcanistDifferentialRevisionStatus::NEEDS_REVISION;
|
||||||
|
|
||||||
switch ($xaction->getNewValue()) {
|
switch ($xaction->getNewValue()) {
|
||||||
|
case DifferentialAction::ACTION_RESIGN:
|
||||||
|
// TODO: Update review status?
|
||||||
|
break;
|
||||||
case DifferentialAction::ACTION_ABANDON:
|
case DifferentialAction::ACTION_ABANDON:
|
||||||
$object->setStatus(ArcanistDifferentialRevisionStatus::ABANDONED);
|
$object->setStatus(ArcanistDifferentialRevisionStatus::ABANDONED);
|
||||||
break;
|
break;
|
||||||
|
@ -150,6 +161,41 @@ final class DifferentialTransactionEditor
|
||||||
return parent::applyCustomInternalTransaction($object, $xaction);
|
return parent::applyCustomInternalTransaction($object, $xaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function expandTransaction(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
|
||||||
|
$results = parent::expandTransaction($object, $xaction);
|
||||||
|
switch ($xaction->getTransactionType()) {
|
||||||
|
case DifferentialTransaction::TYPE_ACTION:
|
||||||
|
switch ($xaction->getNewValue()) {
|
||||||
|
case DifferentialAction::ACTION_RESIGN:
|
||||||
|
// If the user is resigning, add a separate reviewer edit
|
||||||
|
// transaction which removes them as a reviewer.
|
||||||
|
|
||||||
|
$actor_phid = $this->getActor()->getPHID();
|
||||||
|
$type_edge = PhabricatorTransactions::TYPE_EDGE;
|
||||||
|
$edge_reviewer = PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER;
|
||||||
|
|
||||||
|
$results[] = id(new DifferentialTransaction())
|
||||||
|
->setTransactionType($type_edge)
|
||||||
|
->setMetadataValue('edge:type', $edge_reviewer)
|
||||||
|
->setIgnoreOnNoEffect(true)
|
||||||
|
->setNewValue(
|
||||||
|
array(
|
||||||
|
'-' => array(
|
||||||
|
$actor_phid => $actor_phid,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
protected function applyCustomExternalTransaction(
|
protected function applyCustomExternalTransaction(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
@ -221,6 +267,11 @@ final class DifferentialTransactionEditor
|
||||||
$status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
|
$status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
|
||||||
|
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
|
case DifferentialAction::ACTION_RESIGN:
|
||||||
|
// You can always resign from a revision if you're a reviewer. If you
|
||||||
|
// aren't, this is a no-op rather than invalid.
|
||||||
|
break;
|
||||||
|
|
||||||
case DifferentialAction::ACTION_ABANDON:
|
case DifferentialAction::ACTION_ABANDON:
|
||||||
if (!$actor_is_author) {
|
if (!$actor_is_author) {
|
||||||
return pht(
|
return pht(
|
||||||
|
|
|
@ -121,7 +121,8 @@ final class DifferentialTransaction extends PhabricatorApplicationTransaction {
|
||||||
switch ($this->getMetadataValue('edge:type')) {
|
switch ($this->getMetadataValue('edge:type')) {
|
||||||
case PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER:
|
case PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER:
|
||||||
return pht(
|
return pht(
|
||||||
'Those reviewers are already reviewing this revision.');
|
'The reviewers you are trying to add are already reviewing '.
|
||||||
|
'this revision.');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DifferentialTransaction::TYPE_ACTION:
|
case DifferentialTransaction::TYPE_ACTION:
|
||||||
|
@ -142,6 +143,10 @@ final class DifferentialTransaction extends PhabricatorApplicationTransaction {
|
||||||
return pht('This revision already requires changes.');
|
return pht('This revision already requires changes.');
|
||||||
case DifferentialAction::ACTION_REQUEST:
|
case DifferentialAction::ACTION_REQUEST:
|
||||||
return pht('Review is already requested for this revision.');
|
return pht('Review is already requested for this revision.');
|
||||||
|
case DifferentialAction::ACTION_RESIGN:
|
||||||
|
return pht(
|
||||||
|
'You can not resign from this revision because you are not '.
|
||||||
|
'a reviewer.');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -417,6 +417,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
$xactions[] = $mention_xaction;
|
$xactions[] = $mention_xaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$xactions = $this->expandTransactions($object, $xactions);
|
||||||
$xactions = $this->combineTransactions($xactions);
|
$xactions = $this->combineTransactions($xactions);
|
||||||
|
|
||||||
foreach ($xactions as $xaction) {
|
foreach ($xactions as $xaction) {
|
||||||
|
@ -853,6 +854,30 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionally expand transactions which imply other effects. For example,
|
||||||
|
* resigning from a revision in Differential implies removing yourself as
|
||||||
|
* a reviewer.
|
||||||
|
*/
|
||||||
|
private function expandTransactions(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
array $xactions) {
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
foreach ($xactions as $xaction) {
|
||||||
|
foreach ($this->expandTransaction($object, $xaction) as $expanded) {
|
||||||
|
$results[] = $expanded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function expandTransaction(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
return array($xaction);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to combine similar transactions into a smaller number of total
|
* Attempt to combine similar transactions into a smaller number of total
|
||||||
|
@ -912,6 +937,12 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
}
|
}
|
||||||
$u->setNewValue($result);
|
$u->setNewValue($result);
|
||||||
|
|
||||||
|
// When combining an "ignore" transaction with a normal transaction, make
|
||||||
|
// sure we don't propagate the "ignore" flag.
|
||||||
|
if (!$v->getIgnoreOnNoEffect()) {
|
||||||
|
$u->setIgnoreOnNoEffect(false);
|
||||||
|
}
|
||||||
|
|
||||||
return $u;
|
return $u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1128,6 +1159,8 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
if ($xaction->getTransactionType() != $type_comment) {
|
if ($xaction->getTransactionType() != $type_comment) {
|
||||||
$any_effect = true;
|
$any_effect = true;
|
||||||
}
|
}
|
||||||
|
} else if ($xaction->getIgnoreOnNoEffect()) {
|
||||||
|
unset($xactions[$key]);
|
||||||
} else {
|
} else {
|
||||||
$no_effect[$key] = $xaction;
|
$no_effect[$key] = $xaction;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,22 +32,28 @@ final class PhabricatorApplicationTransactionNoEffectResponse
|
||||||
$only_empty_comment = (count($xactions) == 1) &&
|
$only_empty_comment = (count($xactions) == 1) &&
|
||||||
(head($xactions)->getTransactionType() == $type_comment);
|
(head($xactions)->getTransactionType() == $type_comment);
|
||||||
|
|
||||||
|
$count = new PhutilNumber(count($xactions));
|
||||||
|
|
||||||
if ($ex->hasAnyEffect()) {
|
if ($ex->hasAnyEffect()) {
|
||||||
$title = pht('%d Action(s) With No Effect', count($xactions));
|
$title = pht('%d Action(s) With No Effect', $count);
|
||||||
|
$head = pht('Some of your %d action(s) have no effect:', $count);
|
||||||
$tail = pht('Apply remaining actions?');
|
$tail = pht('Apply remaining actions?');
|
||||||
$continue = pht('Apply Other Actions');
|
$continue = pht('Apply Remaining Actions');
|
||||||
} else if ($ex->hasComment()) {
|
} else if ($ex->hasComment()) {
|
||||||
$title = pht('Post as Comment');
|
$title = pht('Post as Comment');
|
||||||
|
$head = pht('The %d action(s) you are taking have no effect:', $count);
|
||||||
$tail = pht('Do you want to post your comment anyway?');
|
$tail = pht('Do you want to post your comment anyway?');
|
||||||
$continue = pht('Post Comment');
|
$continue = pht('Post Comment');
|
||||||
} else if ($only_empty_comment) {
|
} else if ($only_empty_comment) {
|
||||||
// Special case this since it's common and we can give the user a nicer
|
// Special case this since it's common and we can give the user a nicer
|
||||||
// dialog than "Action Has No Effect".
|
// dialog than "Action Has No Effect".
|
||||||
$title = pht('Empty Comment');
|
$title = pht('Empty Comment');
|
||||||
|
$head = null;
|
||||||
$tail = null;
|
$tail = null;
|
||||||
$continue = null;
|
$continue = null;
|
||||||
} else {
|
} else {
|
||||||
$title = pht('%d Action(s) Have No Effect', count($xactions));
|
$title = pht('%d Action(s) Have No Effect', $count);
|
||||||
|
$head = pht('The %d action(s) you are taking have no effect:', $count);
|
||||||
$tail = null;
|
$tail = null;
|
||||||
$continue = null;
|
$continue = null;
|
||||||
}
|
}
|
||||||
|
@ -56,10 +62,17 @@ final class PhabricatorApplicationTransactionNoEffectResponse
|
||||||
->setUser($request->getUser())
|
->setUser($request->getUser())
|
||||||
->setTitle($title);
|
->setTitle($title);
|
||||||
|
|
||||||
|
$dialog->appendChild($head);
|
||||||
|
|
||||||
|
$list = array();
|
||||||
foreach ($xactions as $xaction) {
|
foreach ($xactions as $xaction) {
|
||||||
$dialog->appendChild(
|
$list[] = phutil_tag(
|
||||||
phutil_tag('p', array(), $xaction->getNoEffectDescription()));
|
'li',
|
||||||
|
array(),
|
||||||
|
$xaction->getNoEffectDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$dialog->appendChild(phutil_tag('ul', array(), $list));
|
||||||
$dialog->appendChild($tail);
|
$dialog->appendChild($tail);
|
||||||
|
|
||||||
if ($continue) {
|
if ($continue) {
|
||||||
|
|
|
@ -31,6 +31,24 @@ abstract class PhabricatorApplicationTransaction
|
||||||
private $viewer = self::ATTACHABLE;
|
private $viewer = self::ATTACHABLE;
|
||||||
private $object = self::ATTACHABLE;
|
private $object = self::ATTACHABLE;
|
||||||
|
|
||||||
|
private $ignoreOnNoEffect;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag this transaction as a pure side-effect which should be ignored when
|
||||||
|
* applying transactions if it has no effect, even if transaction application
|
||||||
|
* would normally fail. This both provides users with better error messages
|
||||||
|
* and allows transactions to perform optional side effects.
|
||||||
|
*/
|
||||||
|
public function setIgnoreOnNoEffect($ignore) {
|
||||||
|
$this->ignoreOnNoEffect = $ignore;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIgnoreOnNoEffect() {
|
||||||
|
return $this->ignoreOnNoEffect;
|
||||||
|
}
|
||||||
|
|
||||||
abstract public function getApplicationTransactionType();
|
abstract public function getApplicationTransactionType();
|
||||||
|
|
||||||
private function getApplicationObjectTypeName() {
|
private function getApplicationObjectTypeName() {
|
||||||
|
|
|
@ -215,6 +215,26 @@ abstract class PhabricatorBaseEnglishTranslation
|
||||||
'Actions With No Effect',
|
'Actions With No Effect',
|
||||||
),
|
),
|
||||||
|
|
||||||
|
'Some of your %d action(s) have no effect:' => array(
|
||||||
|
'One of your actions has no effect:',
|
||||||
|
'Some of your actions have no effect:',
|
||||||
|
),
|
||||||
|
|
||||||
|
'Apply remaining %d action(s)?' => array(
|
||||||
|
'Apply remaining action?',
|
||||||
|
'Apply remaining actions?',
|
||||||
|
),
|
||||||
|
|
||||||
|
'Apply %d Other Action(s)' => array(
|
||||||
|
'Apply Remaining Action',
|
||||||
|
'Apply Remaining Actions',
|
||||||
|
),
|
||||||
|
|
||||||
|
'The %d action(s) you are taking have no effect:' => array(
|
||||||
|
'The action you are taking has no effect:',
|
||||||
|
'The actions you are taking have no effect:',
|
||||||
|
),
|
||||||
|
|
||||||
'%s edited post(s), added %d: %s; removed %d: %s.' =>
|
'%s edited post(s), added %d: %s; removed %d: %s.' =>
|
||||||
'%s edited posts, added: %3$s; removed: %5$s',
|
'%s edited posts, added: %3$s; removed: %5$s',
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aphront-access-dialog ul {
|
.aphront-dialog-view ul {
|
||||||
margin: 12px 24px;
|
margin: 12px 24px;
|
||||||
list-style: circle;
|
list-style: circle;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue