mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-20 20:40:56 +01:00
Rough cut of "Blueprint Authorizations"
Summary: Ref T9519. This is like 80% of the way there and doesn't fully work yet, but roughly shows the shape of things to come. Here's how it works: First, there's a new custom field type for blueprints which works like a normal typeahead but has some extra logic. It's implemented this way to make it easy to add to Blueprints in Drydock and Build Plans in Harbormaster. Here, I've added a "Use Blueprints" field to the "WorkingCopy" blueprint, so you can control which hosts the working copies are permitted to allocate on: {F869865} This control has a bit of custom rendering logic. Instead of rendering a normal list of PHIDs, it renders an annotated list with icons: {F869866} These icons show whether the blueprint on the other size of the authorization has approved this object. Once you have a green checkmark, you're good to go. On the blueprint side, things look like this: {F869867} This table shows all the objects which have asked for access to this blueprint. In this case it's showing that one object is approved to use the blueprint since I already approved it, but by default new requests come in here as "Authorization Requested" and someone has to go approve them. You approve them from within the authorization detail screen: {F869868} You can use the "Approve" or "Decline" buttons to allow or prevent use of the blueprint. This doesn't actually do anything yet -- objects don't need to be authorized in order to use blueprints quite yet. That will come in the next diff, I just wanted to get the UI in reasonable shape first. The authorization also has a second piece of state, which is whether the request from the object is active or inactive. We use this to keep track of the authorization if the blueprint is (maybe temporarily) deleted. For example, you might have a Build Plan that uses Blueprints A and B. For a couple days, you only want to use A, so you remove B from the "Use Blueprints: ..." field. Later, you can add B back and it will connect to its old authorization again, so you don't need to go re-approve things (and if you're declined, you stay declined instead of being able to request authorization over and over again). This should make working with authorizations a little easier and less labor intensive. Stuff not in this diff: - Actually preventing any allocations (next diff). - Probably should have transactions for approve/decline, at least, at some point, so there's a log of who did approvals and when. - Maybe should have a more clear/loud error state when no blueprints are approved? - Should probably restrict the typeahead to specific blueprint types. Test Plan: - Added the field. - Typed some stuff into it. - Saw the UI update properly. - Approved an authorization. - Declined an authorization. - Saw active authorizations on a blueprint page. - Didn't see any inactive authroizations there. - Clicked "View All Authorizations", saw all authorizations. Reviewers: chad, hach-que Reviewed By: chad Maniphest Tasks: T9519 Differential Revision: https://secure.phabricator.com/D14251
This commit is contained in:
parent
32d4ae8cb2
commit
2f6d3119f5
19 changed files with 1059 additions and 11 deletions
resources/sql/autopatches
src
__phutil_library_map__.php
applications/drydock
application
blueprint
controller
DrydockAuthorizationAuthorizeController.phpDrydockAuthorizationListController.phpDrydockAuthorizationViewController.phpDrydockBlueprintViewController.phpDrydockController.phpDrydockResourceViewController.php
customfield
phid
query
storage
view
infrastructure/customfield/standard
14
resources/sql/autopatches/20151009.drydock.auth.1.sql
Normal file
14
resources/sql/autopatches/20151009.drydock.auth.1.sql
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_drydock.drydock_authorization (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
phid VARBINARY(64) NOT NULL,
|
||||||
|
blueprintPHID VARBINARY(64) NOT NULL,
|
||||||
|
blueprintAuthorizationState VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||||
|
objectPHID VARBINARY(64) NOT NULL,
|
||||||
|
objectAuthorizationState VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||||
|
dateCreated INT UNSIGNED NOT NULL,
|
||||||
|
dateModified INT UNSIGNED NOT NULL,
|
||||||
|
UNIQUE KEY `key_phid` (phid),
|
||||||
|
UNIQUE KEY `key_unique` (objectPHID, blueprintPHID),
|
||||||
|
KEY `key_blueprint` (blueprintPHID, blueprintAuthorizationState),
|
||||||
|
KEY `key_object` (objectPHID, objectAuthorizationState)
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -799,6 +799,14 @@ phutil_register_library_map(array(
|
||||||
'DoorkeeperTagsController' => 'applications/doorkeeper/controller/DoorkeeperTagsController.php',
|
'DoorkeeperTagsController' => 'applications/doorkeeper/controller/DoorkeeperTagsController.php',
|
||||||
'DrydockAlmanacServiceHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php',
|
'DrydockAlmanacServiceHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php',
|
||||||
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php',
|
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php',
|
||||||
|
'DrydockAuthorization' => 'applications/drydock/storage/DrydockAuthorization.php',
|
||||||
|
'DrydockAuthorizationAuthorizeController' => 'applications/drydock/controller/DrydockAuthorizationAuthorizeController.php',
|
||||||
|
'DrydockAuthorizationListController' => 'applications/drydock/controller/DrydockAuthorizationListController.php',
|
||||||
|
'DrydockAuthorizationListView' => 'applications/drydock/view/DrydockAuthorizationListView.php',
|
||||||
|
'DrydockAuthorizationPHIDType' => 'applications/drydock/phid/DrydockAuthorizationPHIDType.php',
|
||||||
|
'DrydockAuthorizationQuery' => 'applications/drydock/query/DrydockAuthorizationQuery.php',
|
||||||
|
'DrydockAuthorizationSearchEngine' => 'applications/drydock/query/DrydockAuthorizationSearchEngine.php',
|
||||||
|
'DrydockAuthorizationViewController' => 'applications/drydock/controller/DrydockAuthorizationViewController.php',
|
||||||
'DrydockBlueprint' => 'applications/drydock/storage/DrydockBlueprint.php',
|
'DrydockBlueprint' => 'applications/drydock/storage/DrydockBlueprint.php',
|
||||||
'DrydockBlueprintController' => 'applications/drydock/controller/DrydockBlueprintController.php',
|
'DrydockBlueprintController' => 'applications/drydock/controller/DrydockBlueprintController.php',
|
||||||
'DrydockBlueprintCoreCustomField' => 'applications/drydock/customfield/DrydockBlueprintCoreCustomField.php',
|
'DrydockBlueprintCoreCustomField' => 'applications/drydock/customfield/DrydockBlueprintCoreCustomField.php',
|
||||||
|
@ -2946,6 +2954,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorSpacesTestCase' => 'applications/spaces/__tests__/PhabricatorSpacesTestCase.php',
|
'PhabricatorSpacesTestCase' => 'applications/spaces/__tests__/PhabricatorSpacesTestCase.php',
|
||||||
'PhabricatorSpacesViewController' => 'applications/spaces/controller/PhabricatorSpacesViewController.php',
|
'PhabricatorSpacesViewController' => 'applications/spaces/controller/PhabricatorSpacesViewController.php',
|
||||||
'PhabricatorStandardCustomField' => 'infrastructure/customfield/standard/PhabricatorStandardCustomField.php',
|
'PhabricatorStandardCustomField' => 'infrastructure/customfield/standard/PhabricatorStandardCustomField.php',
|
||||||
|
'PhabricatorStandardCustomFieldBlueprints' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php',
|
||||||
'PhabricatorStandardCustomFieldBool' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php',
|
'PhabricatorStandardCustomFieldBool' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php',
|
||||||
'PhabricatorStandardCustomFieldCredential' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php',
|
'PhabricatorStandardCustomFieldCredential' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php',
|
||||||
'PhabricatorStandardCustomFieldDatasource' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldDatasource.php',
|
'PhabricatorStandardCustomFieldDatasource' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldDatasource.php',
|
||||||
|
@ -4537,6 +4546,17 @@ phutil_register_library_map(array(
|
||||||
'DoorkeeperTagsController' => 'PhabricatorController',
|
'DoorkeeperTagsController' => 'PhabricatorController',
|
||||||
'DrydockAlmanacServiceHostBlueprintImplementation' => 'DrydockBlueprintImplementation',
|
'DrydockAlmanacServiceHostBlueprintImplementation' => 'DrydockBlueprintImplementation',
|
||||||
'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
|
'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
|
||||||
|
'DrydockAuthorization' => array(
|
||||||
|
'DrydockDAO',
|
||||||
|
'PhabricatorPolicyInterface',
|
||||||
|
),
|
||||||
|
'DrydockAuthorizationAuthorizeController' => 'DrydockController',
|
||||||
|
'DrydockAuthorizationListController' => 'DrydockController',
|
||||||
|
'DrydockAuthorizationListView' => 'AphrontView',
|
||||||
|
'DrydockAuthorizationPHIDType' => 'PhabricatorPHIDType',
|
||||||
|
'DrydockAuthorizationQuery' => 'DrydockQuery',
|
||||||
|
'DrydockAuthorizationSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
|
'DrydockAuthorizationViewController' => 'DrydockController',
|
||||||
'DrydockBlueprint' => array(
|
'DrydockBlueprint' => array(
|
||||||
'DrydockDAO',
|
'DrydockDAO',
|
||||||
'PhabricatorApplicationTransactionInterface',
|
'PhabricatorApplicationTransactionInterface',
|
||||||
|
@ -7091,6 +7111,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorSpacesTestCase' => 'PhabricatorTestCase',
|
'PhabricatorSpacesTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorSpacesViewController' => 'PhabricatorSpacesController',
|
'PhabricatorSpacesViewController' => 'PhabricatorSpacesController',
|
||||||
'PhabricatorStandardCustomField' => 'PhabricatorCustomField',
|
'PhabricatorStandardCustomField' => 'PhabricatorCustomField',
|
||||||
|
'PhabricatorStandardCustomFieldBlueprints' => 'PhabricatorStandardCustomFieldTokenizer',
|
||||||
'PhabricatorStandardCustomFieldBool' => 'PhabricatorStandardCustomField',
|
'PhabricatorStandardCustomFieldBool' => 'PhabricatorStandardCustomField',
|
||||||
'PhabricatorStandardCustomFieldCredential' => 'PhabricatorStandardCustomField',
|
'PhabricatorStandardCustomFieldCredential' => 'PhabricatorStandardCustomField',
|
||||||
'PhabricatorStandardCustomFieldDatasource' => 'PhabricatorStandardCustomFieldTokenizer',
|
'PhabricatorStandardCustomFieldDatasource' => 'PhabricatorStandardCustomFieldTokenizer',
|
||||||
|
|
|
@ -57,6 +57,8 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication {
|
||||||
'DrydockResourceListController',
|
'DrydockResourceListController',
|
||||||
'logs/(?:query/(?P<queryKey>[^/]+)/)?' =>
|
'logs/(?:query/(?P<queryKey>[^/]+)/)?' =>
|
||||||
'DrydockLogListController',
|
'DrydockLogListController',
|
||||||
|
'authorizations/(?:query/(?P<queryKey>[^/]+)/)?' =>
|
||||||
|
'DrydockAuthorizationListController',
|
||||||
),
|
),
|
||||||
'create/' => 'DrydockBlueprintCreateController',
|
'create/' => 'DrydockBlueprintCreateController',
|
||||||
'edit/(?:(?P<id>[1-9]\d*)/)?' => 'DrydockBlueprintEditController',
|
'edit/(?:(?P<id>[1-9]\d*)/)?' => 'DrydockBlueprintEditController',
|
||||||
|
@ -81,6 +83,13 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication {
|
||||||
'DrydockLogListController',
|
'DrydockLogListController',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
'(?P<type>authorization)/' => array(
|
||||||
|
'(?P<id>[1-9]\d*)/' => array(
|
||||||
|
'' => 'DrydockAuthorizationViewController',
|
||||||
|
'(?P<action>authorize|decline)/' =>
|
||||||
|
'DrydockAuthorizationAuthorizeController',
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -390,5 +390,15 @@ final class DrydockWorkingCopyBlueprintImplementation
|
||||||
return $lease;
|
return $lease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFieldSpecifications() {
|
||||||
|
return array(
|
||||||
|
'blueprintPHIDs' => array(
|
||||||
|
'name' => pht('Use Blueprints'),
|
||||||
|
'type' => 'blueprints',
|
||||||
|
'required' => true,
|
||||||
|
),
|
||||||
|
) + parent::getFieldSpecifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DrydockAuthorizationAuthorizeController
|
||||||
|
extends DrydockController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
$id = $request->getURIData('id');
|
||||||
|
|
||||||
|
$authorization = id(new DrydockAuthorizationQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
if (!$authorization) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$authorization_uri = $this->getApplicationURI("authorization/{$id}/");
|
||||||
|
$is_authorize = ($request->getURIData('action') == 'authorize');
|
||||||
|
|
||||||
|
$state_authorized = DrydockAuthorization::BLUEPRINTAUTH_AUTHORIZED;
|
||||||
|
$state_declined = DrydockAuthorization::BLUEPRINTAUTH_DECLINED;
|
||||||
|
|
||||||
|
$state = $authorization->getBlueprintAuthorizationState();
|
||||||
|
$can_authorize = ($state != $state_authorized);
|
||||||
|
$can_decline = ($state != $state_declined);
|
||||||
|
|
||||||
|
if ($is_authorize && !$can_authorize) {
|
||||||
|
return $this->newDialog()
|
||||||
|
->setTitle(pht('Already Authorized'))
|
||||||
|
->appendParagraph(
|
||||||
|
pht(
|
||||||
|
'This authorization has already been approved.'))
|
||||||
|
->addCancelButton($authorization_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$is_authorize && !$can_decline) {
|
||||||
|
return $this->newDialog()
|
||||||
|
->setTitle(pht('Already Declined'))
|
||||||
|
->appendParagraph(
|
||||||
|
pht('This authorization has already been declined.'))
|
||||||
|
->addCancelButton($authorization_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
if ($is_authorize) {
|
||||||
|
$new_state = $state_authorized;
|
||||||
|
} else {
|
||||||
|
$new_state = $state_declined;
|
||||||
|
}
|
||||||
|
|
||||||
|
$authorization
|
||||||
|
->setBlueprintAuthorizationState($new_state)
|
||||||
|
->save();
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())->setURI($authorization_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($is_authorize) {
|
||||||
|
$title = pht('Approve Authorization');
|
||||||
|
$body = pht(
|
||||||
|
'Approve this authorization? The object will be able to lease and '.
|
||||||
|
'allocate resources created by this blueprint.');
|
||||||
|
$button = pht('Approve Authorization');
|
||||||
|
} else {
|
||||||
|
$title = pht('Decline Authorization');
|
||||||
|
$body = pht(
|
||||||
|
'Decline this authorization? The object will not be able to lease '.
|
||||||
|
'or allocate resources created by this blueprint.');
|
||||||
|
$button = pht('Decline Authorization');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->newDialog()
|
||||||
|
->setTitle($title)
|
||||||
|
->appendParagraph($body)
|
||||||
|
->addSubmitButton($button)
|
||||||
|
->addCancelButton($authorization_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildSideNavView() {
|
||||||
|
// TODO: Get rid of this, but it's currently required by DrydockController.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildApplicationMenu() {
|
||||||
|
// TODO: As above.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DrydockAuthorizationListController
|
||||||
|
extends DrydockController {
|
||||||
|
|
||||||
|
private $blueprint;
|
||||||
|
|
||||||
|
public function setBlueprint(DrydockBlueprint $blueprint) {
|
||||||
|
$this->blueprint = $blueprint;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBlueprint() {
|
||||||
|
return $this->blueprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldAllowPublic() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$engine = new DrydockAuthorizationSearchEngine();
|
||||||
|
|
||||||
|
$id = $request->getURIData('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($engine)
|
||||||
|
->setNavigation($this->buildSideNavView());
|
||||||
|
|
||||||
|
return $this->delegateToController($controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildSideNavView() {
|
||||||
|
$nav = new AphrontSideNavFilterView();
|
||||||
|
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
|
||||||
|
|
||||||
|
$engine = id(new DrydockAuthorizationSearchEngine())
|
||||||
|
->setViewer($this->getViewer());
|
||||||
|
|
||||||
|
$engine->setBlueprint($this->getBlueprint());
|
||||||
|
$engine->addNavigationItems($nav->getMenu());
|
||||||
|
|
||||||
|
$nav->selectFilter(null);
|
||||||
|
|
||||||
|
return $nav;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildApplicationCrumbs() {
|
||||||
|
$crumbs = parent::buildApplicationCrumbs();
|
||||||
|
|
||||||
|
$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('Authorizations'),
|
||||||
|
$this->getApplicationURI("blueprint/{$id}/authorizations/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $crumbs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DrydockAuthorizationViewController
|
||||||
|
extends DrydockController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
$id = $request->getURIData('id');
|
||||||
|
|
||||||
|
$authorization = id(new DrydockAuthorizationQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->executeOne();
|
||||||
|
if (!$authorization) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $authorization->getID();
|
||||||
|
$title = pht('Authorization %d', $id);
|
||||||
|
|
||||||
|
$blueprint = $authorization->getBlueprint();
|
||||||
|
$blueprint_id = $blueprint->getID();
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader($title)
|
||||||
|
->setUser($viewer)
|
||||||
|
->setPolicyObject($authorization);
|
||||||
|
|
||||||
|
|
||||||
|
$state = $authorization->getBlueprintAuthorizationState();
|
||||||
|
$icon = DrydockAuthorization::getBlueprintStateIcon($state);
|
||||||
|
$name = DrydockAuthorization::getBlueprintStateName($state);
|
||||||
|
|
||||||
|
$header->setStatus($icon, null, $name);
|
||||||
|
|
||||||
|
$actions = $this->buildActionListView($authorization);
|
||||||
|
$properties = $this->buildPropertyListView($authorization);
|
||||||
|
$properties->setActionList($actions);
|
||||||
|
|
||||||
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
|
$crumbs->addTextCrumb(
|
||||||
|
pht('Blueprints'),
|
||||||
|
$this->getApplicationURI('blueprint/'));
|
||||||
|
$crumbs->addTextCrumb(
|
||||||
|
$blueprint->getBlueprintName(),
|
||||||
|
$this->getApplicationURI("blueprint/{$blueprint_id}/"));
|
||||||
|
$crumbs->addTextCrumb($title);
|
||||||
|
|
||||||
|
$object_box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($header)
|
||||||
|
->addPropertyList($properties);
|
||||||
|
|
||||||
|
return $this->buildApplicationPage(
|
||||||
|
array(
|
||||||
|
$crumbs,
|
||||||
|
$object_box,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => $title,
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildActionListView(DrydockAuthorization $authorization) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$id = $authorization->getID();
|
||||||
|
|
||||||
|
$view = id(new PhabricatorActionListView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setObjectURI($this->getRequest()->getRequestURI())
|
||||||
|
->setObject($authorization);
|
||||||
|
|
||||||
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$viewer,
|
||||||
|
$authorization,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
|
||||||
|
$authorize_uri = $this->getApplicationURI("authorization/{$id}/authorize/");
|
||||||
|
$decline_uri = $this->getApplicationURI("authorization/{$id}/decline/");
|
||||||
|
|
||||||
|
$state_authorized = DrydockAuthorization::BLUEPRINTAUTH_AUTHORIZED;
|
||||||
|
$state_declined = DrydockAuthorization::BLUEPRINTAUTH_DECLINED;
|
||||||
|
|
||||||
|
$state = $authorization->getBlueprintAuthorizationState();
|
||||||
|
$can_authorize = $can_edit && ($state != $state_authorized);
|
||||||
|
$can_decline = $can_edit && ($state != $state_declined);
|
||||||
|
|
||||||
|
$view->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setHref($authorize_uri)
|
||||||
|
->setName(pht('Approve Authorization'))
|
||||||
|
->setIcon('fa-check')
|
||||||
|
->setWorkflow(true)
|
||||||
|
->setDisabled(!$can_authorize));
|
||||||
|
|
||||||
|
$view->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setHref($decline_uri)
|
||||||
|
->setName(pht('Decline Authorization'))
|
||||||
|
->setIcon('fa-times')
|
||||||
|
->setWorkflow(true)
|
||||||
|
->setDisabled(!$can_decline));
|
||||||
|
|
||||||
|
return $view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildPropertyListView(DrydockAuthorization $authorization) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$object_phid = $authorization->getObjectPHID();
|
||||||
|
$handles = $viewer->loadHandles(array($object_phid));
|
||||||
|
$handle = $handles[$object_phid];
|
||||||
|
|
||||||
|
$view = new PHUIPropertyListView();
|
||||||
|
|
||||||
|
$view->addProperty(
|
||||||
|
pht('Authorized Object'),
|
||||||
|
$handle->renderLink($handle->getFullName()));
|
||||||
|
|
||||||
|
$view->addProperty(pht('Object Type'), $handle->getTypeName());
|
||||||
|
|
||||||
|
$object_state = $authorization->getObjectAuthorizationState();
|
||||||
|
|
||||||
|
$view->addProperty(
|
||||||
|
pht('Authorization State'),
|
||||||
|
DrydockAuthorization::getObjectStateName($object_state));
|
||||||
|
|
||||||
|
return $view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildSideNavView() {
|
||||||
|
// TODO: Get rid of this, but it's currently required by DrydockController.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildApplicationMenu() {
|
||||||
|
// TODO: As above.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -51,6 +51,8 @@ final class DrydockBlueprintViewController extends DrydockBlueprintController {
|
||||||
|
|
||||||
$resource_box = $this->buildResourceBox($blueprint);
|
$resource_box = $this->buildResourceBox($blueprint);
|
||||||
|
|
||||||
|
$authorizations_box = $this->buildAuthorizationsBox($blueprint);
|
||||||
|
|
||||||
$timeline = $this->buildTransactionTimeline(
|
$timeline = $this->buildTransactionTimeline(
|
||||||
$blueprint,
|
$blueprint,
|
||||||
new DrydockBlueprintTransactionQuery());
|
new DrydockBlueprintTransactionQuery());
|
||||||
|
@ -68,6 +70,7 @@ final class DrydockBlueprintViewController extends DrydockBlueprintController {
|
||||||
$crumbs,
|
$crumbs,
|
||||||
$object_box,
|
$object_box,
|
||||||
$resource_box,
|
$resource_box,
|
||||||
|
$authorizations_box,
|
||||||
$log_box,
|
$log_box,
|
||||||
$timeline,
|
$timeline,
|
||||||
),
|
),
|
||||||
|
@ -167,12 +170,78 @@ final class DrydockBlueprintViewController extends DrydockBlueprintController {
|
||||||
->setTag('a')
|
->setTag('a')
|
||||||
->setHref($resources_uri)
|
->setHref($resources_uri)
|
||||||
->setIconFont('fa-search')
|
->setIconFont('fa-search')
|
||||||
->setText(pht('View All Resources')));
|
->setText(pht('View All')));
|
||||||
|
|
||||||
return id(new PHUIObjectBoxView())
|
return id(new PHUIObjectBoxView())
|
||||||
->setHeader($resource_header)
|
->setHeader($resource_header)
|
||||||
->setObjectList($resource_list);
|
->setObjectList($resource_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildAuthorizationsBox(DrydockBlueprint $blueprint) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$limit = 25;
|
||||||
|
|
||||||
|
// If there are pending authorizations against this blueprint, make sure
|
||||||
|
// we show them first.
|
||||||
|
|
||||||
|
$pending_authorizations = id(new DrydockAuthorizationQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withBlueprintPHIDs(array($blueprint->getPHID()))
|
||||||
|
->withObjectStates(
|
||||||
|
array(
|
||||||
|
DrydockAuthorization::OBJECTAUTH_ACTIVE,
|
||||||
|
))
|
||||||
|
->withBlueprintStates(
|
||||||
|
array(
|
||||||
|
DrydockAuthorization::BLUEPRINTAUTH_REQUESTED,
|
||||||
|
))
|
||||||
|
->setLimit($limit)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$all_authorizations = id(new DrydockAuthorizationQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withBlueprintPHIDs(array($blueprint->getPHID()))
|
||||||
|
->withObjectStates(
|
||||||
|
array(
|
||||||
|
DrydockAuthorization::OBJECTAUTH_ACTIVE,
|
||||||
|
))
|
||||||
|
->withBlueprintStates(
|
||||||
|
array(
|
||||||
|
DrydockAuthorization::BLUEPRINTAUTH_REQUESTED,
|
||||||
|
DrydockAuthorization::BLUEPRINTAUTH_AUTHORIZED,
|
||||||
|
))
|
||||||
|
->setLimit($limit)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$authorizations =
|
||||||
|
mpull($pending_authorizations, null, 'getPHID') +
|
||||||
|
mpull($all_authorizations, null, 'getPHID');
|
||||||
|
|
||||||
|
$authorization_list = id(new DrydockAuthorizationListView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setAuthorizations($authorizations)
|
||||||
|
->setNoDataString(
|
||||||
|
pht('No objects have active authorizations to use this blueprint.'));
|
||||||
|
|
||||||
|
$id = $blueprint->getID();
|
||||||
|
$authorizations_uri = "blueprint/{$id}/authorizations/query/all/";
|
||||||
|
$authorizations_uri = $this->getApplicationURI($authorizations_uri);
|
||||||
|
|
||||||
|
$authorizations_header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Active Authorizations'))
|
||||||
|
->addActionLink(
|
||||||
|
id(new PHUIButtonView())
|
||||||
|
->setTag('a')
|
||||||
|
->setHref($authorizations_uri)
|
||||||
|
->setIconFont('fa-search')
|
||||||
|
->setText(pht('View All')));
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($authorizations_header)
|
||||||
|
->setObjectList($authorization_list);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ abstract class DrydockController extends PhabricatorController {
|
||||||
->setTag('a')
|
->setTag('a')
|
||||||
->setHref($all_uri)
|
->setHref($all_uri)
|
||||||
->setIconFont('fa-search')
|
->setIconFont('fa-search')
|
||||||
->setText(pht('View All Logs')));
|
->setText(pht('View All')));
|
||||||
|
|
||||||
return id(new PHUIObjectBoxView())
|
return id(new PHUIObjectBoxView())
|
||||||
->setHeader($log_header)
|
->setHeader($log_header)
|
||||||
|
|
|
@ -170,7 +170,7 @@ final class DrydockResourceViewController extends DrydockResourceController {
|
||||||
->setTag('a')
|
->setTag('a')
|
||||||
->setHref($leases_uri)
|
->setHref($leases_uri)
|
||||||
->setIconFont('fa-search')
|
->setIconFont('fa-search')
|
||||||
->setText(pht('View All Leases')));
|
->setText(pht('View All')));
|
||||||
|
|
||||||
$lease_list = id(new DrydockLeaseListView())
|
$lease_list = id(new DrydockLeaseListView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
|
|
|
@ -41,11 +41,6 @@ final class DrydockBlueprintCoreCustomField
|
||||||
$object->setDetail($key, $value);
|
$object->setDetail($key, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function applyApplicationTransactionExternalEffects(
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBlueprintFieldValue() {
|
public function getBlueprintFieldValue() {
|
||||||
return $this->getProxy()->getFieldValue();
|
return $this->getProxy()->getFieldValue();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DrydockAuthorizationPHIDType extends PhabricatorPHIDType {
|
||||||
|
|
||||||
|
const TYPECONST = 'DRYA';
|
||||||
|
|
||||||
|
public function getTypeName() {
|
||||||
|
return pht('Drydock Authorization');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newObject() {
|
||||||
|
return new DrydockAuthorization();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildQueryForObjects(
|
||||||
|
PhabricatorObjectQuery $query,
|
||||||
|
array $phids) {
|
||||||
|
|
||||||
|
return id(new DrydockAuthorizationQuery())
|
||||||
|
->withPHIDs($phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadHandles(
|
||||||
|
PhabricatorHandleQuery $query,
|
||||||
|
array $handles,
|
||||||
|
array $objects) {
|
||||||
|
|
||||||
|
foreach ($handles as $phid => $handle) {
|
||||||
|
$authorization = $objects[$phid];
|
||||||
|
$id = $authorization->getID();
|
||||||
|
|
||||||
|
$handle->setName(pht('Drydock Authorization %d', $id));
|
||||||
|
$handle->setURI("/drydock/authorization/{$id}/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,9 +28,12 @@ final class DrydockBlueprintPHIDType extends PhabricatorPHIDType {
|
||||||
foreach ($handles as $phid => $handle) {
|
foreach ($handles as $phid => $handle) {
|
||||||
$blueprint = $objects[$phid];
|
$blueprint = $objects[$phid];
|
||||||
$id = $blueprint->getID();
|
$id = $blueprint->getID();
|
||||||
|
$name = $blueprint->getBlueprintName();
|
||||||
|
|
||||||
$handle->setName($blueprint->getBlueprintName());
|
$handle
|
||||||
$handle->setURI("/drydock/blueprint/{$id}/");
|
->setName($name)
|
||||||
|
->setFullName(pht('Blueprint %d: %s', $id, $name))
|
||||||
|
->setURI("/drydock/blueprint/{$id}/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
146
src/applications/drydock/query/DrydockAuthorizationQuery.php
Normal file
146
src/applications/drydock/query/DrydockAuthorizationQuery.php
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DrydockAuthorizationQuery extends DrydockQuery {
|
||||||
|
|
||||||
|
private $ids;
|
||||||
|
private $phids;
|
||||||
|
private $blueprintPHIDs;
|
||||||
|
private $objectPHIDs;
|
||||||
|
private $blueprintStates;
|
||||||
|
private $objectStates;
|
||||||
|
|
||||||
|
public function withIDs(array $ids) {
|
||||||
|
$this->ids = $ids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withPHIDs(array $phids) {
|
||||||
|
$this->phids = $phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withBlueprintPHIDs(array $phids) {
|
||||||
|
$this->blueprintPHIDs = $phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withObjectPHIDs(array $phids) {
|
||||||
|
$this->objectPHIDs = $phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withBlueprintStates(array $states) {
|
||||||
|
$this->blueprintStates = $states;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withObjectStates(array $states) {
|
||||||
|
$this->objectStates = $states;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newResultObject() {
|
||||||
|
return new DrydockAuthorization();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadPage() {
|
||||||
|
return $this->loadStandardPage($this->newResultObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function willFilterPage(array $authorizations) {
|
||||||
|
$blueprint_phids = mpull($authorizations, 'getBlueprintPHID');
|
||||||
|
if ($blueprint_phids) {
|
||||||
|
$blueprints = id(new DrydockBlueprintQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->setParentQuery($this)
|
||||||
|
->withPHIDs($blueprint_phids)
|
||||||
|
->execute();
|
||||||
|
$blueprints = mpull($blueprints, null, 'getPHID');
|
||||||
|
} else {
|
||||||
|
$blueprints = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($authorizations as $key => $authorization) {
|
||||||
|
$blueprint = idx($blueprints, $authorization->getBlueprintPHID());
|
||||||
|
if (!$blueprint) {
|
||||||
|
$this->didRejectResult($authorization);
|
||||||
|
unset($authorizations[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$authorization->attachBlueprint($blueprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
$object_phids = mpull($authorizations, 'getObjectPHID');
|
||||||
|
if ($object_phids) {
|
||||||
|
$objects = id(new PhabricatorObjectQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->setParentQuery($this)
|
||||||
|
->withPHIDs($object_phids)
|
||||||
|
->execute();
|
||||||
|
$objects = mpull($objects, null, 'getPHID');
|
||||||
|
} else {
|
||||||
|
$objects = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($authorizations as $key => $authorization) {
|
||||||
|
$object = idx($objects, $authorization->getObjectPHID());
|
||||||
|
if (!$object) {
|
||||||
|
$this->didRejectResult($authorization);
|
||||||
|
unset($authorizations[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$authorization->attachObject($object);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $authorizations;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||||
|
$where = parent::buildWhereClauseParts($conn);
|
||||||
|
|
||||||
|
if ($this->ids !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'id IN (%Ld)',
|
||||||
|
$this->ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->phids !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'phid IN (%Ls)',
|
||||||
|
$this->phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->blueprintPHIDs !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'blueprintPHID IN (%Ls)',
|
||||||
|
$this->blueprintPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->objectPHIDs !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'objectPHID IN (%Ls)',
|
||||||
|
$this->objectPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->blueprintStates !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'blueprintAuthorizationState IN (%Ls)',
|
||||||
|
$this->blueprintStates);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->objectStates !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'objectAuthorizationState IN (%Ls)',
|
||||||
|
$this->objectStates);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $where;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DrydockAuthorizationSearchEngine
|
||||||
|
extends PhabricatorApplicationSearchEngine {
|
||||||
|
|
||||||
|
private $blueprint;
|
||||||
|
|
||||||
|
public function setBlueprint(DrydockBlueprint $blueprint) {
|
||||||
|
$this->blueprint = $blueprint;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBlueprint() {
|
||||||
|
return $this->blueprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResultTypeDescription() {
|
||||||
|
return pht('Drydock Authorizations');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationClassName() {
|
||||||
|
return 'PhabricatorDrydockApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canUseInPanelContext() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newQuery() {
|
||||||
|
$query = new DrydockAuthorizationQuery();
|
||||||
|
|
||||||
|
$blueprint = $this->getBlueprint();
|
||||||
|
$query->withBlueprintPHIDs(array($blueprint->getPHID()));
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildQueryFromParameters(array $map) {
|
||||||
|
$query = $this->newQuery();
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildCustomSearchFields() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getURI($path) {
|
||||||
|
$blueprint = $this->getBlueprint();
|
||||||
|
$id = $blueprint->getID();
|
||||||
|
return "/drydock/blueprint/{$id}/authorizations/".$path;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getBuiltinQueryNames() {
|
||||||
|
return array(
|
||||||
|
'all' => pht('All Authorizations'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildSavedQueryFromBuiltin($query_key) {
|
||||||
|
$query = $this->newSavedQuery();
|
||||||
|
$query->setQueryKey($query_key);
|
||||||
|
|
||||||
|
switch ($query_key) {
|
||||||
|
case 'all':
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderResultList(
|
||||||
|
array $authorizations,
|
||||||
|
PhabricatorSavedQuery $query,
|
||||||
|
array $handles) {
|
||||||
|
|
||||||
|
$list = id(new DrydockAuthorizationListView())
|
||||||
|
->setUser($this->requireViewer())
|
||||||
|
->setAuthorizations($authorizations);
|
||||||
|
|
||||||
|
$result = new PhabricatorApplicationSearchResultView();
|
||||||
|
$result->setTable($list);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
122
src/applications/drydock/storage/DrydockAuthorization.php
Normal file
122
src/applications/drydock/storage/DrydockAuthorization.php
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DrydockAuthorization extends DrydockDAO
|
||||||
|
implements
|
||||||
|
PhabricatorPolicyInterface {
|
||||||
|
|
||||||
|
const OBJECTAUTH_ACTIVE = 'active';
|
||||||
|
const OBJECTAUTH_INACTIVE = 'inactive';
|
||||||
|
|
||||||
|
const BLUEPRINTAUTH_REQUESTED = 'requested';
|
||||||
|
const BLUEPRINTAUTH_AUTHORIZED = 'authorized';
|
||||||
|
const BLUEPRINTAUTH_DECLINED = 'declined';
|
||||||
|
|
||||||
|
protected $blueprintPHID;
|
||||||
|
protected $blueprintAuthorizationState;
|
||||||
|
protected $objectPHID;
|
||||||
|
protected $objectAuthorizationState;
|
||||||
|
|
||||||
|
private $blueprint = self::ATTACHABLE;
|
||||||
|
private $object = self::ATTACHABLE;
|
||||||
|
|
||||||
|
protected function getConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::CONFIG_AUX_PHID => true,
|
||||||
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
|
'blueprintAuthorizationState' => 'text32',
|
||||||
|
'objectAuthorizationState' => 'text32',
|
||||||
|
),
|
||||||
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
|
'key_unique' => array(
|
||||||
|
'columns' => array('objectPHID', 'blueprintPHID'),
|
||||||
|
'unique' => true,
|
||||||
|
),
|
||||||
|
'key_blueprint' => array(
|
||||||
|
'columns' => array('blueprintPHID', 'blueprintAuthorizationState'),
|
||||||
|
),
|
||||||
|
'key_object' => array(
|
||||||
|
'columns' => array('objectPHID', 'objectAuthorizationState'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) + parent::getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generatePHID() {
|
||||||
|
return PhabricatorPHID::generateNewPHID(
|
||||||
|
DrydockAuthorizationPHIDType::TYPECONST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachBlueprint(DrydockBlueprint $blueprint) {
|
||||||
|
$this->blueprint = $blueprint;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBlueprint() {
|
||||||
|
return $this->assertAttached($this->blueprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachObject($object) {
|
||||||
|
$this->object = $object;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getObject() {
|
||||||
|
return $this->assertAttached($this->object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getBlueprintStateIcon($state) {
|
||||||
|
$map = array(
|
||||||
|
self::BLUEPRINTAUTH_REQUESTED => 'fa-exclamation-circle indigo',
|
||||||
|
self::BLUEPRINTAUTH_AUTHORIZED => 'fa-check-circle green',
|
||||||
|
self::BLUEPRINTAUTH_DECLINED => 'fa-times red',
|
||||||
|
);
|
||||||
|
|
||||||
|
return idx($map, $state, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getBlueprintStateName($state) {
|
||||||
|
$map = array(
|
||||||
|
self::BLUEPRINTAUTH_REQUESTED => pht('Requested'),
|
||||||
|
self::BLUEPRINTAUTH_AUTHORIZED => pht('Authorized'),
|
||||||
|
self::BLUEPRINTAUTH_DECLINED => pht('Declined'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return idx($map, $state, pht('<Unknown: %s>', $state));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getObjectStateName($state) {
|
||||||
|
$map = array(
|
||||||
|
self::OBJECTAUTH_ACTIVE => pht('Active'),
|
||||||
|
self::OBJECTAUTH_INACTIVE => pht('Inactive'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return idx($map, $state, pht('<Unknown: %s>', $state));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function getCapabilities() {
|
||||||
|
return array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPolicy($capability) {
|
||||||
|
return $this->getBlueprint()->getPolicy($capability);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||||
|
return $this->getBlueprint()->hasAutomaticCapability($capability, $viewer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAutomaticCapability($capability) {
|
||||||
|
return pht(
|
||||||
|
'An authorization inherits the policies of the blueprint it '.
|
||||||
|
'authorizes access to.');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DrydockAuthorizationListView extends AphrontView {
|
||||||
|
|
||||||
|
private $authorizations;
|
||||||
|
private $noDataString;
|
||||||
|
|
||||||
|
public function setAuthorizations(array $authorizations) {
|
||||||
|
assert_instances_of($authorizations, 'DrydockAuthorization');
|
||||||
|
$this->authorizations = $authorizations;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setNoDataString($string) {
|
||||||
|
$this->noDataString = $string;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNoDataString() {
|
||||||
|
return $this->noDataString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
$viewer = $this->getUser();
|
||||||
|
|
||||||
|
$authorizations = $this->authorizations;
|
||||||
|
|
||||||
|
$view = new PHUIObjectItemListView();
|
||||||
|
|
||||||
|
$nodata = $this->getNoDataString();
|
||||||
|
if ($nodata) {
|
||||||
|
$view->setNoDataString($nodata);
|
||||||
|
}
|
||||||
|
|
||||||
|
$handles = $viewer->loadHandles(mpull($authorizations, 'getObjectPHID'));
|
||||||
|
|
||||||
|
foreach ($authorizations as $authorization) {
|
||||||
|
$id = $authorization->getID();
|
||||||
|
$object_phid = $authorization->getObjectPHID();
|
||||||
|
$handle = $handles[$object_phid];
|
||||||
|
|
||||||
|
$item = id(new PHUIObjectItemView())
|
||||||
|
->setHref("/drydock/authorization/{$id}/")
|
||||||
|
->setObjectName(pht('Authorization %d', $id))
|
||||||
|
->setHeader($handle->getFullName());
|
||||||
|
|
||||||
|
$item->addAttribute($handle->getTypeName());
|
||||||
|
|
||||||
|
$object_state = $authorization->getObjectAuthorizationState();
|
||||||
|
$item->addAttribute(
|
||||||
|
DrydockAuthorization::getObjectStateName($object_state));
|
||||||
|
|
||||||
|
$state = $authorization->getBlueprintAuthorizationState();
|
||||||
|
$icon = DrydockAuthorization::getBlueprintStateIcon($state);
|
||||||
|
$name = DrydockAuthorization::getBlueprintStateName($state);
|
||||||
|
|
||||||
|
$item->setStatusIcon($icon, $name);
|
||||||
|
|
||||||
|
$view->addItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $view;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorStandardCustomFieldBlueprints
|
||||||
|
extends PhabricatorStandardCustomFieldTokenizer {
|
||||||
|
|
||||||
|
public function getFieldType() {
|
||||||
|
return 'blueprints';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasource() {
|
||||||
|
return new DrydockBlueprintDatasource();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyApplicationTransactionExternalEffects(
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
|
||||||
|
$object_phid = $xaction->getObjectPHID();
|
||||||
|
|
||||||
|
$old = $this->decodeValue($xaction->getOldValue());
|
||||||
|
$new = $this->decodeValue($xaction->getNewValue());
|
||||||
|
|
||||||
|
$old_phids = array_fuse($old);
|
||||||
|
$new_phids = array_fuse($new);
|
||||||
|
|
||||||
|
$rem_phids = array_diff_key($old_phids, $new_phids);
|
||||||
|
$add_phids = array_diff_key($new_phids, $old_phids);
|
||||||
|
|
||||||
|
$altered_phids = $rem_phids + $add_phids;
|
||||||
|
|
||||||
|
if (!$altered_phids) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$authorizations = id(new DrydockAuthorizationQuery())
|
||||||
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
|
->withObjectPHIDs(array($object_phid))
|
||||||
|
->withBlueprintPHIDs($altered_phids)
|
||||||
|
->execute();
|
||||||
|
$authorizations = mpull($authorizations, null, 'getBlueprintPHID');
|
||||||
|
|
||||||
|
$state_active = DrydockAuthorization::OBJECTAUTH_ACTIVE;
|
||||||
|
$state_inactive = DrydockAuthorization::OBJECTAUTH_INACTIVE;
|
||||||
|
|
||||||
|
$state_requested = DrydockAuthorization::BLUEPRINTAUTH_REQUESTED;
|
||||||
|
|
||||||
|
// Disable the object side of the authorization for any existing
|
||||||
|
// authorizations.
|
||||||
|
foreach ($rem_phids as $rem_phid) {
|
||||||
|
$authorization = idx($authorizations, $rem_phid);
|
||||||
|
if (!$authorization) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$authorization
|
||||||
|
->setObjectAuthorizationState($state_inactive)
|
||||||
|
->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For new authorizations, either add them or reactivate them depending
|
||||||
|
// on the current state.
|
||||||
|
foreach ($add_phids as $add_phid) {
|
||||||
|
$needs_update = false;
|
||||||
|
|
||||||
|
$authorization = idx($authorizations, $add_phid);
|
||||||
|
if (!$authorization) {
|
||||||
|
$authorization = id(new DrydockAuthorization())
|
||||||
|
->setObjectPHID($object_phid)
|
||||||
|
->setObjectAuthorizationState($state_active)
|
||||||
|
->setBlueprintPHID($add_phid)
|
||||||
|
->setBlueprintAuthorizationState($state_requested);
|
||||||
|
|
||||||
|
$needs_update = true;
|
||||||
|
} else {
|
||||||
|
$current_state = $authorization->getObjectAuthorizationState();
|
||||||
|
if ($current_state != $state_active) {
|
||||||
|
$authorization->setObjectAuthorizationState($state_active);
|
||||||
|
$needs_update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($needs_update) {
|
||||||
|
$authorization->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderPropertyViewValue(array $handles) {
|
||||||
|
$value = $this->getFieldValue();
|
||||||
|
if (!$value) {
|
||||||
|
return phutil_tag('em', array(), pht('No authorized blueprints.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$object = $this->getObject();
|
||||||
|
$object_phid = $object->getPHID();
|
||||||
|
|
||||||
|
// NOTE: We're intentionally letting you see the authorization state on
|
||||||
|
// blueprints you can't see because this has a tremendous potential to
|
||||||
|
// be extremely confusing otherwise. You still can't see the blueprints
|
||||||
|
// themselves, but you can know if the object is authorized on something.
|
||||||
|
|
||||||
|
if ($value) {
|
||||||
|
$handles = $this->getViewer()->loadHandles($value);
|
||||||
|
|
||||||
|
$authorizations = id(new DrydockAuthorizationQuery())
|
||||||
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
|
->withObjectPHIDs(array($object_phid))
|
||||||
|
->withBlueprintPHIDs($value)
|
||||||
|
->execute();
|
||||||
|
$authorizations = mpull($authorizations, null, 'getBlueprintPHID');
|
||||||
|
} else {
|
||||||
|
$handles = array();
|
||||||
|
$authorizations = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = array();
|
||||||
|
foreach ($value as $phid) {
|
||||||
|
$authorization = idx($authorizations, $phid);
|
||||||
|
if (!$authorization) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$handle = $handles[$phid];
|
||||||
|
|
||||||
|
$item = id(new PHUIStatusItemView())
|
||||||
|
->setTarget($handle->renderLink());
|
||||||
|
|
||||||
|
$state = $authorization->getBlueprintAuthorizationState();
|
||||||
|
$item->setIcon(
|
||||||
|
DrydockAuthorization::getBlueprintStateIcon($state),
|
||||||
|
null,
|
||||||
|
DrydockAuthorization::getBlueprintStateName($state));
|
||||||
|
|
||||||
|
$items[] = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = new PHUIStatusListView();
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$status->addItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -217,7 +217,7 @@ abstract class PhabricatorStandardCustomFieldPHIDs
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function decodeValue($value) {
|
protected function decodeValue($value) {
|
||||||
$value = json_decode($value);
|
$value = json_decode($value);
|
||||||
if (!is_array($value)) {
|
if (!is_array($value)) {
|
||||||
$value = array();
|
$value = array();
|
||||||
|
|
Loading…
Reference in a new issue