diff --git a/resources/sql/autopatches/20150924.drydock.disable.1.sql b/resources/sql/autopatches/20150924.drydock.disable.1.sql new file mode 100644 index 0000000000..6e5dafe5ec --- /dev/null +++ b/resources/sql/autopatches/20150924.drydock.disable.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_drydock.drydock_blueprint + ADD isDisabled BOOL NOT NULL; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 11e5134628..5cc4d58561 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -805,6 +805,7 @@ phutil_register_library_map(array( 'DrydockBlueprintCreateController' => 'applications/drydock/controller/DrydockBlueprintCreateController.php', 'DrydockBlueprintCustomField' => 'applications/drydock/customfield/DrydockBlueprintCustomField.php', 'DrydockBlueprintDatasource' => 'applications/drydock/typeahead/DrydockBlueprintDatasource.php', + 'DrydockBlueprintDisableController' => 'applications/drydock/controller/DrydockBlueprintDisableController.php', 'DrydockBlueprintEditController' => 'applications/drydock/controller/DrydockBlueprintEditController.php', 'DrydockBlueprintEditor' => 'applications/drydock/editor/DrydockBlueprintEditor.php', 'DrydockBlueprintImplementation' => 'applications/drydock/blueprint/DrydockBlueprintImplementation.php', @@ -4530,6 +4531,7 @@ phutil_register_library_map(array( 'DrydockBlueprintCreateController' => 'DrydockBlueprintController', 'DrydockBlueprintCustomField' => 'PhabricatorCustomField', 'DrydockBlueprintDatasource' => 'PhabricatorTypeaheadDatasource', + 'DrydockBlueprintDisableController' => 'DrydockBlueprintController', 'DrydockBlueprintEditController' => 'DrydockBlueprintController', 'DrydockBlueprintEditor' => 'PhabricatorApplicationTransactionEditor', 'DrydockBlueprintImplementation' => 'Phobject', diff --git a/src/applications/drydock/application/PhabricatorDrydockApplication.php b/src/applications/drydock/application/PhabricatorDrydockApplication.php index d3a543a4f0..afc4219d1f 100644 --- a/src/applications/drydock/application/PhabricatorDrydockApplication.php +++ b/src/applications/drydock/application/PhabricatorDrydockApplication.php @@ -49,7 +49,11 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication { '' => 'DrydockConsoleController', 'blueprint/' => array( '(?:query/(?P[^/]+)/)?' => 'DrydockBlueprintListController', - '(?P[1-9]\d*)/' => 'DrydockBlueprintViewController', + '(?P[1-9]\d*)/' => array( + '' => 'DrydockBlueprintViewController', + '(?Pdisable|enable)/' => + 'DrydockBlueprintDisableController', + ), 'create/' => 'DrydockBlueprintCreateController', 'edit/(?:(?P[1-9]\d*)/)?' => 'DrydockBlueprintEditController', ), @@ -62,8 +66,10 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication { ), 'lease/' => array( '(?:query/(?P[^/]+)/)?' => 'DrydockLeaseListController', - '(?P[1-9]\d*)/' => 'DrydockLeaseViewController', - '(?P[1-9]\d*)/release/' => 'DrydockLeaseReleaseController', + '(?P[1-9]\d*)/' => array( + '' => 'DrydockLeaseViewController', + 'release/' => 'DrydockLeaseReleaseController', + ), ), 'log/' => array( '(?:query/(?P[^/]+)/)?' => 'DrydockLogListController', diff --git a/src/applications/drydock/controller/DrydockBlueprintDisableController.php b/src/applications/drydock/controller/DrydockBlueprintDisableController.php new file mode 100644 index 0000000000..525e55228b --- /dev/null +++ b/src/applications/drydock/controller/DrydockBlueprintDisableController.php @@ -0,0 +1,64 @@ +getViewer(); + $id = $request->getURIData('id'); + + $blueprint = id(new DrydockBlueprintQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$blueprint) { + return new Aphront404Response(); + } + + $is_disable = ($request->getURIData('action') == 'disable'); + $id = $blueprint->getID(); + $cancel_uri = $this->getApplicationURI("blueprint/{$id}/"); + + if ($request->isFormPost()) { + $xactions = array(); + + $xactions[] = id(new DrydockBlueprintTransaction()) + ->setTransactionType(DrydockBlueprintTransaction::TYPE_DISABLED) + ->setNewValue($is_disable ? 1 : 0); + + $editor = id(new DrydockBlueprintEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($blueprint, $xactions); + + return id(new AphrontRedirectResponse())->setURI($cancel_uri); + } + + if ($is_disable) { + $title = pht('Disable Blueprint'); + $body = pht( + 'If you disable this blueprint, Drydock will no longer use it to '. + 'allocate new resources. Existing resources will not be affected.'); + $button = pht('Disable Blueprint'); + } else { + $title = pht('Enable Blueprint'); + $body = pht( + 'If you enable this blueprint, Drydock will start using it to '. + 'allocate new resources.'); + $button = pht('Enable Blueprint'); + } + + return $this->newDialog() + ->setTitle($title) + ->appendParagraph($body) + ->addCancelButton($cancel_uri) + ->addSubmitButton($button); + } +} diff --git a/src/applications/drydock/controller/DrydockBlueprintEditController.php b/src/applications/drydock/controller/DrydockBlueprintEditController.php index 1c75bf708d..2e61ec868c 100644 --- a/src/applications/drydock/controller/DrydockBlueprintEditController.php +++ b/src/applications/drydock/controller/DrydockBlueprintEditController.php @@ -33,8 +33,10 @@ final class DrydockBlueprintEditController extends DrydockBlueprintController { return new Aphront400Response(); } - $blueprint = DrydockBlueprint::initializeNewBlueprint($viewer); - $blueprint->setClassName($class); + $blueprint = DrydockBlueprint::initializeNewBlueprint($viewer) + ->setClassName($class) + ->attachImplementation($impl); + $cancel_uri = $this->getApplicationURI('blueprint/'); } diff --git a/src/applications/drydock/controller/DrydockBlueprintViewController.php b/src/applications/drydock/controller/DrydockBlueprintViewController.php index 5da92539cf..815ace7026 100644 --- a/src/applications/drydock/controller/DrydockBlueprintViewController.php +++ b/src/applications/drydock/controller/DrydockBlueprintViewController.php @@ -21,6 +21,12 @@ final class DrydockBlueprintViewController extends DrydockBlueprintController { ->setUser($viewer) ->setPolicyObject($blueprint); + if ($blueprint->getIsDisabled()) { + $header->setStatus('fa-ban', 'red', pht('Disabled')); + } else { + $header->setStatus('fa-check', 'bluegrey', pht('Active')); + } + $actions = $this->buildActionListView($blueprint); $properties = $this->buildPropertyListView($blueprint, $actions); @@ -84,15 +90,15 @@ final class DrydockBlueprintViewController extends DrydockBlueprintController { } private function buildActionListView(DrydockBlueprint $blueprint) { - $viewer = $this->getRequest()->getUser(); + $viewer = $this->getViewer(); + $id = $blueprint->getID(); $view = id(new PhabricatorActionListView()) ->setUser($viewer) ->setObjectURI($this->getRequest()->getRequestURI()) ->setObject($blueprint); - $uri = '/blueprint/edit/'.$blueprint->getID().'/'; - $uri = $this->getApplicationURI($uri); + $edit_uri = $this->getApplicationURI("blueprint/edit/{$id}/"); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, @@ -101,12 +107,30 @@ final class DrydockBlueprintViewController extends DrydockBlueprintController { $view->addAction( id(new PhabricatorActionView()) - ->setHref($uri) + ->setHref($edit_uri) ->setName(pht('Edit Blueprint')) ->setIcon('fa-pencil') ->setWorkflow(!$can_edit) ->setDisabled(!$can_edit)); + if (!$blueprint->getIsDisabled()) { + $disable_name = pht('Disable Blueprint'); + $disable_icon = 'fa-ban'; + $disable_uri = $this->getApplicationURI("blueprint/{$id}/disable/"); + } else { + $disable_name = pht('Enable Blueprint'); + $disable_icon = 'fa-check'; + $disable_uri = $this->getApplicationURI("blueprint/{$id}/enable/"); + } + + $view->addAction( + id(new PhabricatorActionView()) + ->setHref($disable_uri) + ->setName($disable_name) + ->setIcon($disable_icon) + ->setWorkflow(true) + ->setDisabled(!$can_edit)); + return $view; } diff --git a/src/applications/drydock/editor/DrydockBlueprintEditor.php b/src/applications/drydock/editor/DrydockBlueprintEditor.php index b211e25aa8..b5fd584945 100644 --- a/src/applications/drydock/editor/DrydockBlueprintEditor.php +++ b/src/applications/drydock/editor/DrydockBlueprintEditor.php @@ -16,7 +16,9 @@ final class DrydockBlueprintEditor $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; + $types[] = DrydockBlueprintTransaction::TYPE_NAME; + $types[] = DrydockBlueprintTransaction::TYPE_DISABLED; return $types; } @@ -28,7 +30,11 @@ final class DrydockBlueprintEditor switch ($xaction->getTransactionType()) { case DrydockBlueprintTransaction::TYPE_NAME: return $object->getBlueprintName(); + case DrydockBlueprintTransaction::TYPE_DISABLED: + return (int)$object->getIsDisabled(); } + + return parent::getCustomTransactionOldValue($object, $xaction); } protected function getCustomTransactionNewValue( @@ -38,7 +44,11 @@ final class DrydockBlueprintEditor switch ($xaction->getTransactionType()) { case DrydockBlueprintTransaction::TYPE_NAME: return $xaction->getNewValue(); + case DrydockBlueprintTransaction::TYPE_DISABLED: + return (int)$xaction->getNewValue(); } + + return parent::getCustomTransactionNewValue($object, $xaction); } protected function applyCustomInternalTransaction( @@ -48,26 +58,26 @@ final class DrydockBlueprintEditor switch ($xaction->getTransactionType()) { case DrydockBlueprintTransaction::TYPE_NAME: $object->setBlueprintName($xaction->getNewValue()); - break; + return; + case DrydockBlueprintTransaction::TYPE_DISABLED: + $object->setIsDisabled((int)$xaction->getNewValue()); + return; } + + return parent::applyCustomInternalTransaction($object, $xaction); } protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { - return; - } - protected function extractFilePHIDsFromCustomTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - return array(); - } + switch ($xaction->getTransactionType()) { + case DrydockBlueprintTransaction::TYPE_NAME: + case DrydockBlueprintTransaction::TYPE_DISABLED: + return; + } - protected function shouldSendMail( - PhabricatorLiskDAO $object, - array $xactions) { - return false; + return parent::applyCustomExternalTransaction($object, $xaction); } } diff --git a/src/applications/drydock/query/DrydockBlueprintQuery.php b/src/applications/drydock/query/DrydockBlueprintQuery.php index 7404c9457a..7ce5dcbe5b 100644 --- a/src/applications/drydock/query/DrydockBlueprintQuery.php +++ b/src/applications/drydock/query/DrydockBlueprintQuery.php @@ -6,6 +6,7 @@ final class DrydockBlueprintQuery extends DrydockQuery { private $phids; private $blueprintClasses; private $datasourceQuery; + private $disabled; public function withIDs(array $ids) { $this->ids = $ids; @@ -27,6 +28,11 @@ final class DrydockBlueprintQuery extends DrydockQuery { return $this; } + public function withDisabled($disabled) { + $this->disabled = $disabled; + return $this; + } + public function newResultObject() { return new DrydockBlueprint(); } @@ -82,6 +88,13 @@ final class DrydockBlueprintQuery extends DrydockQuery { $this->blueprintClasses); } + if ($this->disabled !== null) { + $where[] = qsprintf( + $conn, + 'isDisabled = %d', + (int)$this->disabled); + } + return $where; } diff --git a/src/applications/drydock/query/DrydockBlueprintSearchEngine.php b/src/applications/drydock/query/DrydockBlueprintSearchEngine.php index a0420c610c..64859649df 100644 --- a/src/applications/drydock/query/DrydockBlueprintSearchEngine.php +++ b/src/applications/drydock/query/DrydockBlueprintSearchEngine.php @@ -18,11 +18,23 @@ final class DrydockBlueprintSearchEngine protected function buildQueryFromParameters(array $map) { $query = $this->newQuery(); + if ($map['isDisabled'] !== null) { + $query->withDisabled($map['isDisabled']); + } + return $query; } protected function buildCustomSearchFields() { - return array(); + return array( + id(new PhabricatorSearchThreeStateField()) + ->setLabel(pht('Disabled')) + ->setKey('isDisabled') + ->setOptions( + pht('(Show All)'), + pht('Show Only Disabled Blueprints'), + pht('Hide Disabled Blueprints')), + ); } protected function getURI($path) { @@ -31,6 +43,7 @@ final class DrydockBlueprintSearchEngine protected function getBuiltinQueryNames() { return array( + 'active' => pht('Active Blueprints'), 'all' => pht('All Blueprints'), ); } @@ -40,6 +53,8 @@ final class DrydockBlueprintSearchEngine $query->setQueryKey($query_key); switch ($query_key) { + case 'active': + return $query->setParameter('isDisabled', false); case 'all': return $query; } @@ -64,6 +79,12 @@ final class DrydockBlueprintSearchEngine if (!$blueprint->getImplementation()->isEnabled()) { $item->setDisabled(true); + $item->addIcon('fa-chain-broken grey', pht('Implementation')); + } + + if ($blueprint->getIsDisabled()) { + $item->setDisabled(true); + $item->addIcon('fa-ban grey', pht('Disabled')); } $item->addAttribute($blueprint->getImplementation()->getBlueprintName()); diff --git a/src/applications/drydock/storage/DrydockBlueprint.php b/src/applications/drydock/storage/DrydockBlueprint.php index c687c996a9..429e5c2971 100644 --- a/src/applications/drydock/storage/DrydockBlueprint.php +++ b/src/applications/drydock/storage/DrydockBlueprint.php @@ -15,6 +15,7 @@ final class DrydockBlueprint extends DrydockDAO protected $viewPolicy; protected $editPolicy; protected $details = array(); + protected $isDisabled; private $implementation = self::ATTACHABLE; private $customFields = self::ATTACHABLE; @@ -34,7 +35,8 @@ final class DrydockBlueprint extends DrydockDAO return id(new DrydockBlueprint()) ->setViewPolicy($view_policy) ->setEditPolicy($edit_policy) - ->setBlueprintName(''); + ->setBlueprintName('') + ->setIsDisabled(0); } protected function getConfiguration() { @@ -46,6 +48,7 @@ final class DrydockBlueprint extends DrydockDAO self::CONFIG_COLUMN_SCHEMA => array( 'className' => 'text255', 'blueprintName' => 'sort255', + 'isDisabled' => 'bool', ), ) + parent::getConfiguration(); } diff --git a/src/applications/drydock/storage/DrydockBlueprintTransaction.php b/src/applications/drydock/storage/DrydockBlueprintTransaction.php index 0e3b2e2e3e..0856f01fdb 100644 --- a/src/applications/drydock/storage/DrydockBlueprintTransaction.php +++ b/src/applications/drydock/storage/DrydockBlueprintTransaction.php @@ -3,7 +3,8 @@ final class DrydockBlueprintTransaction extends PhabricatorApplicationTransaction { - const TYPE_NAME = 'drydock:blueprint:name'; + const TYPE_NAME = 'drydock:blueprint:name'; + const TYPE_DISABLED = 'drydock:blueprint:disabled'; public function getApplicationName() { return 'drydock'; @@ -31,6 +32,16 @@ final class DrydockBlueprintTransaction $old, $new); } + case self::TYPE_DISABLED: + if ($new) { + return pht( + '%s disabled this blueprint.', + $author_handle); + } else { + return pht( + '%s enabled this blueprint.', + $author_handle); + } } return parent::getTitle(); diff --git a/src/applications/drydock/worker/DrydockAllocatorWorker.php b/src/applications/drydock/worker/DrydockAllocatorWorker.php index ec676bf2fb..a67f242c01 100644 --- a/src/applications/drydock/worker/DrydockAllocatorWorker.php +++ b/src/applications/drydock/worker/DrydockAllocatorWorker.php @@ -184,12 +184,10 @@ final class DrydockAllocatorWorker extends DrydockWorker { return array(); } - // TODO: When blueprints can be disabled, this query should ignore disabled - // blueprints. - $blueprints = id(new DrydockBlueprintQuery()) ->setViewer($viewer) ->withBlueprintClasses(array_keys($impls)) + ->withDisabled(false) ->execute(); $keep = array();