mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Improve UX and messaging for certain errors when landing revisions
Summary: Ref T9994. - Allow errors to be dismissed. - Tailor messaging for closed/abandoned revisions. - Reduce scare messaging on land dialog, since it's not really that scary anymore. Test Plan: - Dismissed errors. - Hit new warnings. - Wasn't as scared when landing. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9994 Differential Revision: https://secure.phabricator.com/D14886
This commit is contained in:
parent
55373030df
commit
367955f3fd
10 changed files with 137 additions and 17 deletions
2
resources/sql/autopatches/20151226.reop.1.sql
Normal file
2
resources/sql/autopatches/20151226.reop.1.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_drydock.drydock_repositoryoperation
|
||||||
|
ADD isDismissed BOOL NOT NULL;
|
|
@ -911,6 +911,7 @@ phutil_register_library_map(array(
|
||||||
'DrydockObjectAuthorizationView' => 'applications/drydock/view/DrydockObjectAuthorizationView.php',
|
'DrydockObjectAuthorizationView' => 'applications/drydock/view/DrydockObjectAuthorizationView.php',
|
||||||
'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php',
|
'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php',
|
||||||
'DrydockRepositoryOperation' => 'applications/drydock/storage/DrydockRepositoryOperation.php',
|
'DrydockRepositoryOperation' => 'applications/drydock/storage/DrydockRepositoryOperation.php',
|
||||||
|
'DrydockRepositoryOperationDismissController' => 'applications/drydock/controller/DrydockRepositoryOperationDismissController.php',
|
||||||
'DrydockRepositoryOperationListController' => 'applications/drydock/controller/DrydockRepositoryOperationListController.php',
|
'DrydockRepositoryOperationListController' => 'applications/drydock/controller/DrydockRepositoryOperationListController.php',
|
||||||
'DrydockRepositoryOperationPHIDType' => 'applications/drydock/phid/DrydockRepositoryOperationPHIDType.php',
|
'DrydockRepositoryOperationPHIDType' => 'applications/drydock/phid/DrydockRepositoryOperationPHIDType.php',
|
||||||
'DrydockRepositoryOperationQuery' => 'applications/drydock/query/DrydockRepositoryOperationQuery.php',
|
'DrydockRepositoryOperationQuery' => 'applications/drydock/query/DrydockRepositoryOperationQuery.php',
|
||||||
|
@ -4900,6 +4901,7 @@ phutil_register_library_map(array(
|
||||||
'DrydockDAO',
|
'DrydockDAO',
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
),
|
),
|
||||||
|
'DrydockRepositoryOperationDismissController' => 'DrydockController',
|
||||||
'DrydockRepositoryOperationListController' => 'DrydockController',
|
'DrydockRepositoryOperationListController' => 'DrydockController',
|
||||||
'DrydockRepositoryOperationPHIDType' => 'PhabricatorPHIDType',
|
'DrydockRepositoryOperationPHIDType' => 'PhabricatorPHIDType',
|
||||||
'DrydockRepositoryOperationQuery' => 'DrydockQuery',
|
'DrydockRepositoryOperationQuery' => 'DrydockQuery',
|
||||||
|
|
|
@ -94,8 +94,7 @@ final class DifferentialRevisionOperationController
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->appendRemarkupInstructions(
|
->appendRemarkupInstructions(
|
||||||
pht(
|
pht(
|
||||||
'In theory, this will do approximately what `arc land` would do. '.
|
'(NOTE) This feature is new and experimental.'))
|
||||||
'In practice, you will have a riveting adventure instead.'))
|
|
||||||
->appendControl(
|
->appendControl(
|
||||||
id(new AphrontFormTokenizerControl())
|
id(new AphrontFormTokenizerControl())
|
||||||
->setLabel(pht('Onto Branch'))
|
->setLabel(pht('Onto Branch'))
|
||||||
|
@ -103,11 +102,7 @@ final class DifferentialRevisionOperationController
|
||||||
->setLimit(1)
|
->setLimit(1)
|
||||||
->setError($e_ref)
|
->setError($e_ref)
|
||||||
->setValue($v_ref)
|
->setValue($v_ref)
|
||||||
->setDatasource($ref_datasource))
|
->setDatasource($ref_datasource));
|
||||||
->appendRemarkupInstructions(
|
|
||||||
pht(
|
|
||||||
'(WARNING) THIS FEATURE IS EXPERIMENTAL AND DANGEROUS! USE IT AT '.
|
|
||||||
'YOUR OWN RISK!'));
|
|
||||||
|
|
||||||
return $this->newDialog()
|
return $this->newDialog()
|
||||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||||
|
@ -115,7 +110,7 @@ final class DifferentialRevisionOperationController
|
||||||
->setErrors($errors)
|
->setErrors($errors)
|
||||||
->appendForm($form)
|
->appendForm($form)
|
||||||
->addCancelButton($detail_uri)
|
->addCancelButton($detail_uri)
|
||||||
->addSubmitButton(pht('Mutate Repository Unpredictably'));
|
->addSubmitButton(pht('Land Revision'));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function newRefQuery(PhabricatorRepository $repository) {
|
private function newRefQuery(PhabricatorRepository $repository) {
|
||||||
|
|
|
@ -1047,6 +1047,7 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
||||||
$operations = id(new DrydockRepositoryOperationQuery())
|
$operations = id(new DrydockRepositoryOperationQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withObjectPHIDs(array($revision->getPHID()))
|
->withObjectPHIDs(array($revision->getPHID()))
|
||||||
|
->withIsDismissed(false)
|
||||||
->withOperationTypes(
|
->withOperationTypes(
|
||||||
array(
|
array(
|
||||||
DrydockLandRepositoryOperation::OPCONST,
|
DrydockLandRepositoryOperation::OPCONST,
|
||||||
|
|
|
@ -96,6 +96,7 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication {
|
||||||
'(?P<id>[1-9]\d*)/' => array(
|
'(?P<id>[1-9]\d*)/' => array(
|
||||||
'' => 'DrydockRepositoryOperationViewController',
|
'' => 'DrydockRepositoryOperationViewController',
|
||||||
'status/' => 'DrydockRepositoryOperationStatusController',
|
'status/' => 'DrydockRepositoryOperationStatusController',
|
||||||
|
'dismiss/' => 'DrydockRepositoryOperationDismissController',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DrydockRepositoryOperationDismissController
|
||||||
|
extends DrydockController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
$id = $request->getURIData('id');
|
||||||
|
|
||||||
|
$operation = id(new DrydockRepositoryOperationQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
if (!$operation) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$object_phid = $operation->getObjectPHID();
|
||||||
|
$handles = $viewer->loadHandles(array($object_phid));
|
||||||
|
$done_uri = $handles[$object_phid]->getURI();
|
||||||
|
|
||||||
|
if ($operation->getIsDismissed()) {
|
||||||
|
return $this->newDialog()
|
||||||
|
->setTitle(pht('Already Dismissed'))
|
||||||
|
->appendParagraph(
|
||||||
|
pht(
|
||||||
|
'This operation has already been dismissed, and can not be '.
|
||||||
|
'dismissed any further.'))
|
||||||
|
->addCancelButton($done_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
$operation
|
||||||
|
->setIsDismissed(1)
|
||||||
|
->save();
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())->setURI($done_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->newDialog()
|
||||||
|
->setTitle(pht('Dismiss Operation'))
|
||||||
|
->appendParagraph(
|
||||||
|
pht(
|
||||||
|
'Dismiss this operation? It will no longer be shown, but logs '.
|
||||||
|
'can be found in Drydock.'))
|
||||||
|
->addSubmitButton(pht('Dismiss'))
|
||||||
|
->addCancelButton($done_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -235,12 +235,29 @@ final class DrydockLandRepositoryOperation
|
||||||
|
|
||||||
$status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED;
|
$status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED;
|
||||||
if ($revision->getStatus() != $status_accepted) {
|
if ($revision->getStatus() != $status_accepted) {
|
||||||
return array(
|
switch ($revision->getStatus()) {
|
||||||
'title' => pht('Revision Not Accepted'),
|
case ArcanistDifferentialRevisionStatus::CLOSED:
|
||||||
'body' => pht(
|
return array(
|
||||||
'This revision is still under review. Only revisions which have '.
|
'title' => pht('Revision Closed'),
|
||||||
'been accepted may land.'),
|
'body' => pht(
|
||||||
);
|
'This revision has already been closed. Only open, accepted '.
|
||||||
|
'revisions may land.'),
|
||||||
|
);
|
||||||
|
case ArcanistDifferentialRevisionStatus::ABANDONED:
|
||||||
|
return array(
|
||||||
|
'title' => pht('Revision Abandoned'),
|
||||||
|
'body' => pht(
|
||||||
|
'This revision has been abandoned. Only accepted revisions '.
|
||||||
|
'may land.'),
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return array(
|
||||||
|
'title' => pht('Revision Not Accepted'),
|
||||||
|
'body' => pht(
|
||||||
|
'This revision is still under review. Only revisions which '.
|
||||||
|
'have been accepted may land.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for other operations. Eventually this should probably be more
|
// Check for other operations. Eventually this should probably be more
|
||||||
|
|
|
@ -8,6 +8,7 @@ final class DrydockRepositoryOperationQuery extends DrydockQuery {
|
||||||
private $repositoryPHIDs;
|
private $repositoryPHIDs;
|
||||||
private $operationStates;
|
private $operationStates;
|
||||||
private $operationTypes;
|
private $operationTypes;
|
||||||
|
private $isDismissed;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
|
@ -39,6 +40,11 @@ final class DrydockRepositoryOperationQuery extends DrydockQuery {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withIsDismissed($dismissed) {
|
||||||
|
$this->isDismissed = $dismissed;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function newResultObject() {
|
public function newResultObject() {
|
||||||
return new DrydockRepositoryOperation();
|
return new DrydockRepositoryOperation();
|
||||||
}
|
}
|
||||||
|
@ -152,6 +158,13 @@ final class DrydockRepositoryOperationQuery extends DrydockQuery {
|
||||||
$this->operationTypes);
|
$this->operationTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->isDismissed !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'isDismissed = %d',
|
||||||
|
(int)$this->isDismissed);
|
||||||
|
}
|
||||||
|
|
||||||
return $where;
|
return $where;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ final class DrydockRepositoryOperation extends DrydockDAO
|
||||||
protected $operationType;
|
protected $operationType;
|
||||||
protected $operationState;
|
protected $operationState;
|
||||||
protected $properties = array();
|
protected $properties = array();
|
||||||
|
protected $isDismissed;
|
||||||
|
|
||||||
private $repository = self::ATTACHABLE;
|
private $repository = self::ATTACHABLE;
|
||||||
private $object = self::ATTACHABLE;
|
private $object = self::ATTACHABLE;
|
||||||
|
@ -30,7 +31,8 @@ final class DrydockRepositoryOperation extends DrydockDAO
|
||||||
|
|
||||||
return id(new DrydockRepositoryOperation())
|
return id(new DrydockRepositoryOperation())
|
||||||
->setOperationState(self::STATE_WAIT)
|
->setOperationState(self::STATE_WAIT)
|
||||||
->setOperationType($op->getOperationConstant());
|
->setOperationType($op->getOperationConstant())
|
||||||
|
->setIsDismissed(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getConfiguration() {
|
protected function getConfiguration() {
|
||||||
|
@ -43,6 +45,7 @@ final class DrydockRepositoryOperation extends DrydockDAO
|
||||||
'repositoryTarget' => 'bytes',
|
'repositoryTarget' => 'bytes',
|
||||||
'operationType' => 'text32',
|
'operationType' => 'text32',
|
||||||
'operationState' => 'text32',
|
'operationState' => 'text32',
|
||||||
|
'isDismissed' => 'bool',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'key_object' => array(
|
'key_object' => array(
|
||||||
|
@ -196,11 +199,17 @@ final class DrydockRepositoryOperation extends DrydockDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPolicy($capability) {
|
public function getPolicy($capability) {
|
||||||
return $this->getRepository()->getPolicy($capability);
|
$need_capability = $this->getRequiredRepositoryCapability($capability);
|
||||||
|
|
||||||
|
return $this->getRepository()
|
||||||
|
->getPolicy($need_capability);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||||
return $this->getRepository()->hasAutomaticCapability($capability, $viewer);
|
$need_capability = $this->getRequiredRepositoryCapability($capability);
|
||||||
|
|
||||||
|
return $this->getRepository()
|
||||||
|
->hasAutomaticCapability($need_capability, $viewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function describeAutomaticCapability($capability) {
|
public function describeAutomaticCapability($capability) {
|
||||||
|
@ -209,4 +218,17 @@ final class DrydockRepositoryOperation extends DrydockDAO
|
||||||
'affects.');
|
'affects.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getRequiredRepositoryCapability($capability) {
|
||||||
|
// To edit a RepositoryOperation, require that the user be able to push
|
||||||
|
// to the repository.
|
||||||
|
|
||||||
|
$map = array(
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT =>
|
||||||
|
DiffusionPushCapability::CAPABILITY,
|
||||||
|
);
|
||||||
|
|
||||||
|
return idx($map, $capability, $capability);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ final class DrydockRepositoryOperationStatusView
|
||||||
|
|
||||||
$item = id(new PHUIObjectItemView())
|
$item = id(new PHUIObjectItemView())
|
||||||
->setHref("/drydock/operation/{$id}/")
|
->setHref("/drydock/operation/{$id}/")
|
||||||
|
->setObjectName(pht('Operation %d', $id))
|
||||||
->setHeader($operation->getOperationDescription($viewer))
|
->setHeader($operation->getOperationDescription($viewer))
|
||||||
->setStatusIcon($icon, $name);
|
->setStatusIcon($icon, $name);
|
||||||
|
|
||||||
|
@ -98,6 +99,16 @@ final class DrydockRepositoryOperationStatusView
|
||||||
} else {
|
} else {
|
||||||
$item->addAttribute(pht('Operation encountered an error.'));
|
$item->addAttribute(pht('Operation encountered an error.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$is_dismissed = $operation->getIsDismissed();
|
||||||
|
|
||||||
|
$item->addAction(
|
||||||
|
id(new PHUIListItemView())
|
||||||
|
->setName('Dismiss')
|
||||||
|
->setIcon('fa-times')
|
||||||
|
->setDisabled($is_dismissed)
|
||||||
|
->setWorkflow(true)
|
||||||
|
->setHref("/drydock/operation/{$id}/dismiss/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return id(new PHUIObjectItemListView())
|
return id(new PHUIObjectItemListView())
|
||||||
|
|
Loading…
Reference in a new issue