diff --git a/src/applications/drydock/application/PhabricatorDrydockApplication.php b/src/applications/drydock/application/PhabricatorDrydockApplication.php index afc4219d1f..bf95a57009 100644 --- a/src/applications/drydock/application/PhabricatorDrydockApplication.php +++ b/src/applications/drydock/application/PhabricatorDrydockApplication.php @@ -1,98 +1,100 @@ pht('Drydock User Guide'), 'href' => PhabricatorEnv::getDoclink('Drydock User Guide'), ), ); } public function getRoutes() { return array( '/drydock/' => array( '' => 'DrydockConsoleController', 'blueprint/' => array( '(?:query/(?P[^/]+)/)?' => 'DrydockBlueprintListController', '(?P[1-9]\d*)/' => array( '' => 'DrydockBlueprintViewController', '(?Pdisable|enable)/' => 'DrydockBlueprintDisableController', + 'resources/(?:query/(?P[^/]+)/)?' => + 'DrydockResourceListController', ), 'create/' => 'DrydockBlueprintCreateController', 'edit/(?:(?P[1-9]\d*)/)?' => 'DrydockBlueprintEditController', ), 'resource/' => array( '(?:query/(?P[^/]+)/)?' => 'DrydockResourceListController', '(?P[1-9]\d*)/' => array( '' => 'DrydockResourceViewController', 'release/' => 'DrydockResourceReleaseController', ), ), 'lease/' => array( '(?:query/(?P[^/]+)/)?' => 'DrydockLeaseListController', '(?P[1-9]\d*)/' => array( '' => 'DrydockLeaseViewController', 'release/' => 'DrydockLeaseReleaseController', ), ), 'log/' => array( '(?:query/(?P[^/]+)/)?' => 'DrydockLogListController', ), ), ); } protected function getCustomCapabilities() { return array( DrydockDefaultViewCapability::CAPABILITY => array( 'template' => DrydockBlueprintPHIDType::TYPECONST, 'capability' => PhabricatorPolicyCapability::CAN_VIEW, ), DrydockDefaultEditCapability::CAPABILITY => array( 'default' => PhabricatorPolicies::POLICY_ADMIN, 'template' => DrydockBlueprintPHIDType::TYPECONST, 'capability' => PhabricatorPolicyCapability::CAN_EDIT, ), DrydockCreateBlueprintsCapability::CAPABILITY => array( 'default' => PhabricatorPolicies::POLICY_ADMIN, ), ); } } diff --git a/src/applications/drydock/controller/DrydockBlueprintViewController.php b/src/applications/drydock/controller/DrydockBlueprintViewController.php index 815ace7026..6991e18fa2 100644 --- a/src/applications/drydock/controller/DrydockBlueprintViewController.php +++ b/src/applications/drydock/controller/DrydockBlueprintViewController.php @@ -1,151 +1,170 @@ getViewer(); $id = $request->getURIData('id'); $blueprint = id(new DrydockBlueprintQuery()) ->setViewer($viewer) ->withIDs(array($id)) ->executeOne(); if (!$blueprint) { return new Aphront404Response(); } $title = $blueprint->getBlueprintName(); $header = id(new PHUIHeaderView()) ->setHeader($title) ->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); - $blueprint_uri = 'blueprint/'.$blueprint->getID().'/'; - $blueprint_uri = $this->getApplicationURI($blueprint_uri); - - $resources = id(new DrydockResourceQuery()) - ->withBlueprintPHIDs(array($blueprint->getPHID())) - ->setViewer($viewer) - ->execute(); - - $resource_list = id(new DrydockResourceListView()) - ->setUser($viewer) - ->setResources($resources) - ->render(); - $resource_list->setNoDataString(pht('This blueprint has no resources.')); - - $pager = new PHUIPagerView(); - $pager->setURI(new PhutilURI($blueprint_uri), 'offset'); - $pager->setOffset($request->getInt('offset')); - $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Blueprint %d', $blueprint->getID())); $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->addPropertyList($properties); $field_list = PhabricatorCustomField::getObjectFields( $blueprint, PhabricatorCustomField::ROLE_VIEW); $field_list ->setViewer($viewer) ->readFieldsFromStorage($blueprint); $field_list->appendFieldsToPropertyList( $blueprint, $viewer, $properties); - $resource_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Resources')) - ->setObjectList($resource_list); + $resource_box = $this->buildResourceBox($blueprint); $timeline = $this->buildTransactionTimeline( $blueprint, new DrydockBlueprintTransactionQuery()); $timeline->setShouldTerminate(true); return $this->buildApplicationPage( array( $crumbs, $object_box, $resource_box, $timeline, ), array( 'title' => $title, )); } private function buildActionListView(DrydockBlueprint $blueprint) { $viewer = $this->getViewer(); $id = $blueprint->getID(); $view = id(new PhabricatorActionListView()) ->setUser($viewer) ->setObjectURI($this->getRequest()->getRequestURI()) ->setObject($blueprint); $edit_uri = $this->getApplicationURI("blueprint/edit/{$id}/"); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $blueprint, PhabricatorPolicyCapability::CAN_EDIT); $view->addAction( id(new PhabricatorActionView()) ->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; } private function buildPropertyListView( DrydockBlueprint $blueprint, PhabricatorActionListView $actions) { $view = new PHUIPropertyListView(); $view->setActionList($actions); $view->addProperty( pht('Type'), $blueprint->getImplementation()->getBlueprintName()); return $view; } + private function buildResourceBox(DrydockBlueprint $blueprint) { + $viewer = $this->getViewer(); + + $resources = id(new DrydockResourceQuery()) + ->setViewer($viewer) + ->withBlueprintPHIDs(array($blueprint->getPHID())) + ->withStatuses( + array( + DrydockResourceStatus::STATUS_PENDING, + DrydockResourceStatus::STATUS_ACTIVE, + )) + ->setLimit(100) + ->execute(); + + $resource_list = id(new DrydockResourceListView()) + ->setUser($viewer) + ->setResources($resources) + ->render() + ->setNoDataString(pht('This blueprint has no active resources.')); + + $id = $blueprint->getID(); + $resources_uri = "blueprint/{$id}/resources/query/all/"; + $resources_uri = $this->getApplicationURI($resources_uri); + + $resource_header = id(new PHUIHeaderView()) + ->setHeader(pht('Active Resources')) + ->addActionLink( + id(new PHUIButtonView()) + ->setTag('a') + ->setHref($resources_uri) + ->setIconFont('fa-search') + ->setText(pht('View All Resources'))); + + return id(new PHUIObjectBoxView()) + ->setHeader($resource_header) + ->setObjectList($resource_list); + } + + } diff --git a/src/applications/drydock/controller/DrydockResourceController.php b/src/applications/drydock/controller/DrydockResourceController.php index e29b13e2c2..8675b5bf59 100644 --- a/src/applications/drydock/controller/DrydockResourceController.php +++ b/src/applications/drydock/controller/DrydockResourceController.php @@ -1,27 +1,55 @@ blueprint = $blueprint; + return $this; + } + + public function getBlueprint() { + return $this->blueprint; + } + public function buildSideNavView() { $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); id(new DrydockResourceSearchEngine()) - ->setViewer($this->getRequest()->getUser()) + ->setViewer($this->getViewer()) ->addNavigationItems($nav->getMenu()); $nav->selectFilter(null); return $nav; } protected function buildApplicationCrumbs() { $crumbs = parent::buildApplicationCrumbs(); - $crumbs->addTextCrumb( - pht('Resources'), - $this->getApplicationURI('resource/')); + + $blueprint = $this->getBlueprint(); + if ($blueprint) { + $id = $blueprint->getID(); + $crumbs->addTextCrumb( + pht('Blueprints'), + $this->getApplicationURI('blueprint/')); + + $crumbs->addTextCrumb( + $blueprint->getBlueprintName(), + $this->getApplicationURI("blueprint/{$id}/")); + + $crumbs->addTextCrumb( + pht('Resources'), + $this->getApplicationURI("blueprint/{$id}/resources/")); + } else { + $crumbs->addTextCrumb( + pht('Resources'), + $this->getApplicationURI('resource/')); + } return $crumbs; } } diff --git a/src/applications/drydock/controller/DrydockResourceListController.php b/src/applications/drydock/controller/DrydockResourceListController.php index d2f34ec25b..6e1f80cbd6 100644 --- a/src/applications/drydock/controller/DrydockResourceListController.php +++ b/src/applications/drydock/controller/DrydockResourceListController.php @@ -1,21 +1,37 @@ getViewer(); + $viewer = $this->getViewer(); + + $engine = new DrydockResourceSearchEngine(); + + $id = $request->getURIData('id'); + if ($id) { + $blueprint = id(new DrydockBlueprintQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$blueprint) { + return new Aphront404Response(); + } + $this->setBlueprint($blueprint); + $engine->setBlueprint($blueprint); + } + $querykey = $request->getURIData('queryKey'); $controller = id(new PhabricatorApplicationSearchController()) ->setQueryKey($querykey) - ->setSearchEngine(new DrydockResourceSearchEngine()) + ->setSearchEngine($engine) ->setNavigation($this->buildSideNavView()); return $this->delegateToController($controller); } } diff --git a/src/applications/drydock/query/DrydockResourceSearchEngine.php b/src/applications/drydock/query/DrydockResourceSearchEngine.php index 7b0bca0324..8f72fdf217 100644 --- a/src/applications/drydock/query/DrydockResourceSearchEngine.php +++ b/src/applications/drydock/query/DrydockResourceSearchEngine.php @@ -1,82 +1,106 @@ blueprint = $blueprint; + return $this; + } + + public function getBlueprint() { + return $this->blueprint; + } + public function getResultTypeDescription() { return pht('Drydock Resources'); } public function getApplicationClassName() { return 'PhabricatorDrydockApplication'; } public function newQuery() { - return new DrydockResourceQuery(); + $query = new DrydockResourceQuery(); + + $blueprint = $this->getBlueprint(); + if ($blueprint) { + $query->withBlueprintPHIDs(array($blueprint->getPHID())); + } + + return $query; } protected function buildQueryFromParameters(array $map) { $query = $this->newQuery(); if ($map['statuses']) { $query->withStatuses($map['statuses']); } return $query; } protected function buildCustomSearchFields() { return array( id(new PhabricatorSearchCheckboxesField()) ->setLabel(pht('Statuses')) ->setKey('statuses') ->setOptions(DrydockResourceStatus::getStatusMap()), ); } protected function getURI($path) { - return '/drydock/resource/'.$path; + $blueprint = $this->getBlueprint(); + if ($blueprint) { + $id = $blueprint->getID(); + return "/drydock/blueprint/{$id}/resources/".$path; + } else { + return '/drydock/resource/'.$path; + } } protected function getBuiltinQueryNames() { return array( 'active' => pht('Active Resources'), 'all' => pht('All Resources'), ); } public function buildSavedQueryFromBuiltin($query_key) { $query = $this->newSavedQuery(); $query->setQueryKey($query_key); switch ($query_key) { case 'active': return $query->setParameter( 'statuses', array( DrydockResourceStatus::STATUS_PENDING, DrydockResourceStatus::STATUS_ACTIVE, )); case 'all': return $query; } return parent::buildSavedQueryFromBuiltin($query_key); } protected function renderResultList( array $resources, PhabricatorSavedQuery $query, array $handles) { $list = id(new DrydockResourceListView()) ->setUser($this->requireViewer()) ->setResources($resources); $result = new PhabricatorApplicationSearchResultView(); $result->setTable($list); return $result; } }