1
0
Fork 0
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:
epriestley 2015-12-26 12:20:21 -08:00
parent 55373030df
commit 367955f3fd
10 changed files with 137 additions and 17 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_drydock.drydock_repositoryoperation
ADD isDismissed BOOL NOT NULL;

View file

@ -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',

View file

@ -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) {

View file

@ -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,

View file

@ -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',
), ),
), ),
), ),

View file

@ -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);
}
}

View file

@ -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

View file

@ -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;
} }

View file

@ -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);
}
} }

View file

@ -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())